From 77c742ee7eeaec6a95e560b780a85bde6fe3c22d Mon Sep 17 00:00:00 2001 From: Michelle_Anguiano Date: Tue, 2 Aug 2022 19:57:38 -0500 Subject: [PATCH 01/12] Add gitignore --- .gitignore | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..281aea5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# package directories +../node_modules +../jspm_packages +../.idea + +../package-lock.json + +.env +# Serverless directories +.serverless + +../.env + +.gitignore + From 118ee34db8e180564d525bad21971431b15d841f Mon Sep 17 00:00:00 2001 From: Michelle_Anguiano Date: Tue, 2 Aug 2022 20:04:48 -0500 Subject: [PATCH 02/12] Corrected lambda.product.test.js Corrected get-product.js --- product-service/get-product.js | 3 +++ product-service/test/lambda.product.test.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/product-service/get-product.js b/product-service/get-product.js index 764b8b4..9326561 100644 --- a/product-service/get-product.js +++ b/product-service/get-product.js @@ -12,6 +12,9 @@ module.exports.getProductsById = async (event) => { product = getById(id); if(product === undefined) { status = 404 + product = { + message: 'Product not found' + } } } return { diff --git a/product-service/test/lambda.product.test.js b/product-service/test/lambda.product.test.js index c2c0fb1..5e19260 100644 --- a/product-service/test/lambda.product.test.js +++ b/product-service/test/lambda.product.test.js @@ -35,7 +35,7 @@ describe('product-service', () => { .expectResult(response => response); expect(result.statusCode).equals(404); - expect(result.body).equals(undefined); + expect(JSON.parse(result.body).message).equals('Product not found'); }); it('Should return 200 with productId', async () => { From c97b1f2728dd31a945f5600d8208f96e240e92f9 Mon Sep 17 00:00:00 2001 From: Michelle_Anguiano Date: Tue, 2 Aug 2022 20:46:08 -0500 Subject: [PATCH 03/12] Corrected serverless.yml Added console.log() added webpack.config.js --- package.json | 4 +- .../controller/product.controller.js | 1 + product-service/get-product.js | 3 +- product-service/get-products.js | 3 ++ product-service/path.json | 1 + product-service/serverless.yml | 41 +++++++++++-------- product-service/webpack.config.js | 11 +++++ 7 files changed, 46 insertions(+), 18 deletions(-) create mode 100644 product-service/path.json create mode 100644 product-service/webpack.config.js diff --git a/package.json b/package.json index 8341761..6994632 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,8 @@ "dependencies": { "chai": "^4.3.6", "lambda-tester": "^4.0.1", - "mocha": "^10.0.0" + "mocha": "^10.0.0", + "serverless-webpack": "^5.8.0", + "webpack-node-externals": "^3.0.0" } } diff --git a/product-service/controller/product.controller.js b/product-service/controller/product.controller.js index 0fd534e..fa932e1 100644 --- a/product-service/controller/product.controller.js +++ b/product-service/controller/product.controller.js @@ -2,6 +2,7 @@ const model = require('../model/product.model').products; const selectAll = function () { + console.log(`${model}`) return model; } diff --git a/product-service/get-product.js b/product-service/get-product.js index 9326561..9461213 100644 --- a/product-service/get-product.js +++ b/product-service/get-product.js @@ -2,7 +2,7 @@ const getById = require('./controller/product.controller').selectById; -module.exports.getProductsById = async (event) => { +module.exports.getProductById = async (event) => { let status = 200; let product = {} if (event[`pathParameters`].productId === '' || !event[`pathParameters`].productId) { @@ -17,6 +17,7 @@ module.exports.getProductsById = async (event) => { } } } + console.log(product); return { statusCode: status, headers: { diff --git a/product-service/get-products.js b/product-service/get-products.js index 66477b1..19a12a5 100644 --- a/product-service/get-products.js +++ b/product-service/get-products.js @@ -3,7 +3,10 @@ const selectAll = require('./controller/product.controller').selectAll; module.exports.getProductsList = async () => { + console.log('getProductsList Started') const products = selectAll(); + console.log('getProductsList executed') + console.log(JSON.stringify(products)); return { statusCode: 200, headers: { diff --git a/product-service/path.json b/product-service/path.json new file mode 100644 index 0000000..cdef318 --- /dev/null +++ b/product-service/path.json @@ -0,0 +1 @@ +{"pathParameters":{"productId":"ABCDE102030"}} \ No newline at end of file diff --git a/product-service/serverless.yml b/product-service/serverless.yml index b8493cc..6e94a57 100644 --- a/product-service/serverless.yml +++ b/product-service/serverless.yml @@ -26,12 +26,21 @@ provider: stage: dev region: us-east-1 +plugins: + - serverless-webpack + +custom: + webpack: + webpackConfig: 'webpack.config.js' # Name of webpack configuration file + includeModules: false + packager: 'npm' + # you can overwrite defaults here # stage: dev # region: us-east-1 functions: - getProductsList: + getProductsListTest: handler: get-products.getProductsList events: - http: @@ -47,23 +56,23 @@ functions: - X-Amz-Security-Token - X-Amz-User-Agent allowCredentials: false - getProductsById: + getProductByIdTest: handler: get-product.getProductById events: - http: path: /products/{productId} method: get - cors: - origins: '*' - headers: - - Content-Type - - X-Amz-Date - - Authorization - - X-Api-Key - - X-Amz-Security-Token - - X-Amz-User-Agent - allowCredentials: false - request: - parameters: - paths: - productId: true \ No newline at end of file + cors: + origins: '*' + headers: + - Content-Type + - X-Amz-Date + - Authorization + - X-Api-Key + - X-Amz-Security-Token + - X-Amz-User-Agent + allowCredentials: false + request: + parameters: + paths: + productId: true \ No newline at end of file diff --git a/product-service/webpack.config.js b/product-service/webpack.config.js new file mode 100644 index 0000000..0266cb6 --- /dev/null +++ b/product-service/webpack.config.js @@ -0,0 +1,11 @@ +const slsw = require('serverless-webpack'); +const nodeExternals = require('webpack-node-externals'); +module.exports = { + context: __dirname, + mode: slsw.lib.webpack.isLocal ? 'development' : 'production', + entry: slsw.lib.entries, + target: 'node', + /*target: 'node', + mode: 'none',*/ + externals: [nodeExternals(), 'pg-native'], +}; From 02a0d604f6b1ef478266f2713e4bb4bf6e80d643 Mon Sep 17 00:00:00 2001 From: Michelle_Anguiano Date: Tue, 2 Aug 2022 20:49:33 -0500 Subject: [PATCH 04/12] returned lambdas names --- product-service/serverless.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/product-service/serverless.yml b/product-service/serverless.yml index 6e94a57..efa19d2 100644 --- a/product-service/serverless.yml +++ b/product-service/serverless.yml @@ -40,7 +40,7 @@ custom: # region: us-east-1 functions: - getProductsListTest: + getProductsList: handler: get-products.getProductsList events: - http: @@ -56,7 +56,7 @@ functions: - X-Amz-Security-Token - X-Amz-User-Agent allowCredentials: false - getProductByIdTest: + getProductById: handler: get-product.getProductById events: - http: From 09cf2ff7c20c1800989c2db38258965c8c2bfecd Mon Sep 17 00:00:00 2001 From: Michelle_Anguiano Date: Wed, 3 Aug 2022 00:01:36 -0500 Subject: [PATCH 05/12] added async/await --- product-service/controller/product.controller.js | 4 ++-- product-service/get-product.js | 2 +- product-service/get-products.js | 2 +- product-service/test/lambda.product.test.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/product-service/controller/product.controller.js b/product-service/controller/product.controller.js index fa932e1..eed09a5 100644 --- a/product-service/controller/product.controller.js +++ b/product-service/controller/product.controller.js @@ -1,12 +1,12 @@ 'use strict'; const model = require('../model/product.model').products; -const selectAll = function () { +const selectAll = async () => { console.log(`${model}`) return model; } -const selectById = function (product_id) { +const selectById = async (product_id) => { return model.find(product => product.id === product_id); } diff --git a/product-service/get-product.js b/product-service/get-product.js index 9461213..205d036 100644 --- a/product-service/get-product.js +++ b/product-service/get-product.js @@ -9,7 +9,7 @@ module.exports.getProductById = async (event) => { status = 500; } else { const id = event[`pathParameters`].productId; - product = getById(id); + product = await getById(id); if(product === undefined) { status = 404 product = { diff --git a/product-service/get-products.js b/product-service/get-products.js index 19a12a5..d194d9d 100644 --- a/product-service/get-products.js +++ b/product-service/get-products.js @@ -4,7 +4,7 @@ const selectAll = require('./controller/product.controller').selectAll; module.exports.getProductsList = async () => { console.log('getProductsList Started') - const products = selectAll(); + const products = await selectAll(); console.log('getProductsList executed') console.log(JSON.stringify(products)); return { diff --git a/product-service/test/lambda.product.test.js b/product-service/test/lambda.product.test.js index 5e19260..4b42435 100644 --- a/product-service/test/lambda.product.test.js +++ b/product-service/test/lambda.product.test.js @@ -3,7 +3,7 @@ const expect = require('chai').expect; const lambda = require('lambda-tester'); const getProductList = require('../get-products').getProductsList; -const getProductById = require('../get-product').getProductsById; +const getProductById = require('../get-product').getProductById; describe('product-service', () => { From 5c58dcddc17302e197c108c50a1c4fd6ae88eb98 Mon Sep 17 00:00:00 2001 From: Michelle_Anguiano Date: Wed, 3 Aug 2022 11:18:43 -0500 Subject: [PATCH 06/12] - added create-product.js - added .env - added db connection --- package.json | 11 +- product-service/config.js | 11 + .../controller/product.controller.js | 68 ++- .../controller/stock.controller.js | 29 + product-service/create-product.js | 74 +++ product-service/serverless.yml | 26 +- product-service/test/lambda.product.test.js | 517 +++++++++++++++++- product-service/utils/util.connection.js | 16 + product-service/utils/utils.functions.js | 7 + 9 files changed, 743 insertions(+), 16 deletions(-) create mode 100644 product-service/config.js create mode 100644 product-service/controller/stock.controller.js create mode 100644 product-service/create-product.js create mode 100644 product-service/utils/util.connection.js create mode 100644 product-service/utils/utils.functions.js diff --git a/package.json b/package.json index 6994632..f447bb9 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,19 @@ "author": "", "license": "ISC", "dependencies": { + "@supercharge/strings": "^1.28.0", + "aws-sdk": "^2.1181.0", "chai": "^4.3.6", + "dotenv": "^16.0.1", "lambda-tester": "^4.0.1", "mocha": "^10.0.0", - "serverless-webpack": "^5.8.0", + "pg": "^8.7.3", + "uuid": "^8.3.2", + "webpack": "^5.73.0", "webpack-node-externals": "^3.0.0" + }, + "devDependencies": { + "serverless-dotenv-plugin": "^4.0.1", + "serverless-webpack": "^5.7.1" } } diff --git a/product-service/config.js b/product-service/config.js new file mode 100644 index 0000000..cb2dc21 --- /dev/null +++ b/product-service/config.js @@ -0,0 +1,11 @@ +const dotenv = require('dotenv'); + +module.exports = async ({ options, resolveConfigurationProperty }) => { + // Load env vars into Serverless environment + // You can do more complicated env var resolution with dotenv here + const envVars = dotenv.config({ path: '.env' }).parsed; + return Object.assign( + {}, + envVars, // `dotenv` environment variables + ); +}; \ No newline at end of file diff --git a/product-service/controller/product.controller.js b/product-service/controller/product.controller.js index eed09a5..46f5fe8 100644 --- a/product-service/controller/product.controller.js +++ b/product-service/controller/product.controller.js @@ -1,13 +1,69 @@ 'use strict'; -const model = require('../model/product.model').products; +const {db} = require('../utils/util.connection'); + const selectAll = async () => { - console.log(`${model}`) - return model; + try { + console.log(`selectedAll executing`); + console.log(`DB Connection`); + const client = await db.connect(); + console.log(`Selecting from Product Table`); + const select = await client.query('SELECT p.*, s.count FROM product p INNER JOIN stock s ON p.id = s.product_id'); + client.release(); + console.log(`Products: ${JSON.stringify(select.rows)}`); + return select.rows; + } catch (error) { + console.log(`Error occurred: ${error}`); + return undefined; + } } -const selectById = async (product_id) => { - return model.find(product => product.id === product_id); +const selectById = async (id) => { + try { + console.log(`selectById executing`); + console.log(`DB Connection`); + const client = await db.connect(); + console.log(`Selecting from Product Table by ${id}`); + const select = await client.query( + `SELECT p.*, s.count FROM product p INNER JOIN stock s ON p.id = s.product_id WHERE p.id = $1`, + [id] + ); + client.release(); + console.log(`Product: ${JSON.stringify(select.rows)}`); + return select.rows; + } catch (error) { + console.log(`Error occurred: ${error}`); + return undefined; + } } -module.exports = {selectAll, selectById}; \ No newline at end of file +const create = async (product) => { + console.log(`create product executing`); + console.log(`DB Connection`); + const client = await db.connect(); + let isCreated = true; + try { + console.log(`BEGIN TRANSACTION`); + await client.query('BEGIN'); + + console.log(`INSERT product`); + await client.query( + 'INSERT INTO public.product (id, title, description, price) VALUES ($1, $2, $3, $4)', + [product.id, product.title, product.description, product.price] + ); + console.log(`COMMIT TRANSACTION`); + await client.query('COMMIT'); + + } catch (error) { + console.log(`ROLLBACK TRANSACTION`); + await client.query('ROLLBACK'); + console.log(`An error occurred ${error}`); + isCreated = false; + } finally { + client.release(); + } + + return isCreated; +}; + +module.exports = {selectAll, selectById, create}; \ No newline at end of file diff --git a/product-service/controller/stock.controller.js b/product-service/controller/stock.controller.js new file mode 100644 index 0000000..7bc9c9c --- /dev/null +++ b/product-service/controller/stock.controller.js @@ -0,0 +1,29 @@ +'use strict'; +const {db} = require('../utils/util.connection'); + +const create = async (stock) => { + console.log(`create stock executing`); + console.log(`DB Connection`); + const client = await db.connect(); + let isCreated = true; + try { + console.log(`BEGIN TRANSACTION`); + await client.query('BEGIN'); + + console.log(`INSERT stock`); + await client.query('INSERT INTO public.stock (product_id, count) VALUES ($1, $2)', [stock.productId, stock.count]); + + console.log(`COMMIT TRANSACTION`); + await client.query('COMMIT'); + } catch (error) { + console.log(`ROLLBACK TRANSACTION`); + await client.query('ROLLBACK'); + console.log(`An error occurred ${error}`); + isCreated = false; + } finally { + client.release(); + } + return isCreated; +}; + +module.exports = {create}; \ No newline at end of file diff --git a/product-service/create-product.js b/product-service/create-product.js new file mode 100644 index 0000000..d7f60fc --- /dev/null +++ b/product-service/create-product.js @@ -0,0 +1,74 @@ +'use strict'; +const {v4: uuidv4} = require('uuid'); + +const product = require('./controller/product.controller').create; +const stock = require('./controller/stock.controller').create; + +const utils = require('./utils/utils.functions'); + +module.exports.createProduct = async (event) => { + let data; + let status = 201; + + try { + if (!event.body) { + status = 501; + data = 'Data not provided' + } else { + let body = JSON.parse(event.body); + + if (body.title === undefined + || body.description === undefined + || body.price === undefined + || body.count === undefined) { + status = 502; + data = 'Missing Data'; + } else { + + if (!utils.validateProductData(body)) { + status = 400; + data = 'Data is Invalid'; + } else { + const p = {} + p.id = uuidv4(); + p.title = body.title; + p.price = body.price; + + let isCreated = await product(p); + + if(isCreated) { + console.log(`Create Stock from Product`); + const s = {}; + s.productId = p.id; + s.count = body.count; + + isCreated = await stock(s); + + if(isCreated) { + data = `Product created ID: ${p.id}`; + } else { + status = 504; + data = `An error occurred on Stock creation`; + } + } else { + status = 503; + data = `Can't created product, an error occurred`; + } + } + } + } + } catch (error) { + console.log(`Error: ${error}`); + status = 500; + data = `An error occurred` + } + + return { + statusCode: status, + headers: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Credentials': true, + }, + body: JSON.stringify(data) + }; +} \ No newline at end of file diff --git a/product-service/serverless.yml b/product-service/serverless.yml index efa19d2..ddae572 100644 --- a/product-service/serverless.yml +++ b/product-service/serverless.yml @@ -25,11 +25,19 @@ provider: runtime: nodejs12.x stage: dev region: us-east-1 + environment: + USER_DB: ${self:custom.dotenvVars.USER_DB, env:USER_DB, ''} + HOST_DB: ${self:custom.dotenvVars.HOST_DB, env:HOST_DB, ''} + PWD_DB: ${self:custom.dotenvVars.PWD_DB, env:PWD_DB, ''} + PORT_DB: ${self:custom.dotenvVars.PORT_DB, env:PORT_DB, ''} + DB_NAME: ${self:custom.dotenvVars.DB_NAME, env:DB_NAME, ''} plugins: - serverless-webpack + - serverless-dotenv-plugin custom: + dotenvVars: ${./configs.js)} webpack: webpackConfig: 'webpack.config.js' # Name of webpack configuration file includeModules: false @@ -75,4 +83,20 @@ functions: request: parameters: paths: - productId: true \ No newline at end of file + productId: true + createProduct: + handler: create-product.createProduct + events: + - http: + path: products + method: post + cors: + origin: '*' # <-- Specify allowed origin + headers: # <-- Specify allowed headers + - Content-Type + - X-Amz-Date + - Authorization + - X-Api-Key + - X-Amz-Security-Token + - X-Amz-User-Agent + allowCredentials: false \ No newline at end of file diff --git a/product-service/test/lambda.product.test.js b/product-service/test/lambda.product.test.js index 4b42435..f3a0c4d 100644 --- a/product-service/test/lambda.product.test.js +++ b/product-service/test/lambda.product.test.js @@ -4,19 +4,20 @@ const lambda = require('lambda-tester'); const getProductList = require('../get-products').getProductsList; const getProductById = require('../get-product').getProductById; +const createProduct = require('../create-product').createProduct; describe('product-service', () => { describe('lambdas-aws', () => { describe('get-products', () => { - it('Should return 200 - all products', async () => { - const result = await lambda(getProductList) - .event() - .expectResult(response => response); - - expect(result.statusCode).equals(200); - expect(JSON.parse(result.body).length).greaterThan(1); - }); + it('Should return 200 - all products', async () => { + const result = await lambda(getProductList) + .event() + .expectResult(response => response); + + expect(result.statusCode).equals(200); + expect(JSON.parse(result.body).length).greaterThan(1); + }); }); describe('get-product', () => { @@ -47,6 +48,506 @@ describe('product-service', () => { expect(JSON.parse(result.body).id).equals('ABCDE102030'); }); }); + + describe('create-product', () => { + it('Should return 500 when event is wrong' , async () => { + const result = await lambda(createProduct) + .event( { + resource: '/products', + path: '/products', + httpMethod: 'POST', + headers: { + Accept: '*/*', + 'Accept-Encoding': 'gzip, deflate, br', + 'CloudFront-Forwarded-Proto': 'https', + 'CloudFront-Is-Desktop-Viewer': 'true', + 'CloudFront-Is-Mobile-Viewer': 'false', + 'CloudFront-Is-SmartTV-Viewer': 'false', + 'CloudFront-Is-Tablet-Viewer': 'false', + 'CloudFront-Viewer-ASN': '28509', + 'CloudFront-Viewer-Country': 'MX', + 'Content-Type': 'application/json', + Host: 'o3lc79zr1i.execute-api.us-east-1.amazonaws.com', + 'Postman-Token': 'da51eb72-aa2e-41ea-a120-6f14e21b1d94', + 'User-Agent': 'PostmanRuntime/7.29.2', + Via: '1.1 e453cfec7ab7b0f50057381607edb486.cloudfront.net (CloudFront)', + 'X-Amz-Cf-Id': 'AL_XrwZDLODp365r4ALZNt8piYvuuKet9zEQGxBGUrTB68EID6Tr2w==', + 'X-Amzn-Trace-Id': 'Root=1-62db01d2-5bca1ccb57b50f957f73a1df', + 'X-Forwarded-For': '187.253.120.36, 130.176.179.78', + 'X-Forwarded-Port': '443', + 'X-Forwarded-Proto': 'https' + }, + multiValueHeaders: { + Accept: [ '*/*' ], + 'Accept-Encoding': [ 'gzip, deflate, br' ], + 'CloudFront-Forwarded-Proto': [ 'https' ], + 'CloudFront-Is-Desktop-Viewer': [ 'true' ], + 'CloudFront-Is-Mobile-Viewer': [ 'false' ], + 'CloudFront-Is-SmartTV-Viewer': [ 'false' ], + 'CloudFront-Is-Tablet-Viewer': [ 'false' ], + 'CloudFront-Viewer-ASN': [ '28509' ], + 'CloudFront-Viewer-Country': [ 'MX' ], + 'Content-Type': [ 'application/json' ], + Host: [ 'o3lc79zr1i.execute-api.us-east-1.amazonaws.com' ], + 'Postman-Token': [ 'da51eb72-aa2e-41ea-a120-6f14e21b1d94' ], + 'User-Agent': [ 'PostmanRuntime/7.29.2' ], + Via: [ + '1.1 e453cfec7ab7b0f50057381607edb486.cloudfront.net (CloudFront)' + ], + 'X-Amz-Cf-Id': [ 'AL_XrwZDLODp365r4ALZNt8piYvuuKet9zEQGxBGUrTB68EID6Tr2w==' ], + 'X-Amzn-Trace-Id': [ 'Root=1-62db01d2-5bca1ccb57b50f957f73a1df' ], + 'X-Forwarded-For': [ '187.253.120.36, 130.176.179.78' ], + 'X-Forwarded-Port': [ '443' ], + 'X-Forwarded-Proto': [ 'https' ] + }, + queryStringParameters: null, + multiValueQueryStringParameters: null, + pathParameters: null, + stageVariables: null, + requestContext: { + resourceId: 'cic4y4', + resourcePath: '/products', + httpMethod: 'POST', + extendedRequestId: 'Vr046GH6oAMF93A=', + requestTime: '22/Jul/2022:20:00:18 +0000', + path: '/dev/products', + accountId: '436988374415', + protocol: 'HTTP/1.1', + stage: 'dev', + domainPrefix: 'o3lc79zr1i', + requestTimeEpoch: 1658520018467, + requestId: 'be81d25d-203b-4d50-9908-b173622b9b35', + identity: { + cognitoIdentityPoolId: null, + accountId: null, + cognitoIdentityId: null, + caller: null, + sourceIp: '187.253.120.36', + principalOrgId: null, + accessKey: null, + cognitoAuthenticationType: null, + cognitoAuthenticationProvider: null, + userArn: null, + userAgent: 'PostmanRuntime/7.29.2', + user: null + }, + domainName: 'o3lc79zr1i.execute-api.us-east-1.amazonaws.com', + apiId: 'o3lc79zr1i' + }, + body: '{\r\n' + + ' "title": "Test product :D",\r\n' + + ' "description": "Test description",\r\n' + + ' "price": 10,\r\n' + + '}', + isBase64Encoded: false + }) + .expectResult(response => response); + + expect(result.statusCode).equals(500); + expect(JSON.parse(result.body)).equals('An error occurred'); + }); + + it('Should return 501 with not event data', async () => { + const result = await lambda(createProduct) + .event({}) + .expectResult(response => response); + + expect(result.statusCode).equals(501); + expect(JSON.parse(result.body)).equals('Data not provided'); + }); + + it('Should return 502 whit missing data', async () => { + const result = await lambda(createProduct) + .event( { + resource: '/products', + path: '/products', + httpMethod: 'POST', + headers: { + Accept: '*/*', + 'Accept-Encoding': 'gzip, deflate, br', + 'CloudFront-Forwarded-Proto': 'https', + 'CloudFront-Is-Desktop-Viewer': 'true', + 'CloudFront-Is-Mobile-Viewer': 'false', + 'CloudFront-Is-SmartTV-Viewer': 'false', + 'CloudFront-Is-Tablet-Viewer': 'false', + 'CloudFront-Viewer-ASN': '28509', + 'CloudFront-Viewer-Country': 'MX', + 'Content-Type': 'application/json', + Host: 'o3lc79zr1i.execute-api.us-east-1.amazonaws.com', + 'Postman-Token': 'da51eb72-aa2e-41ea-a120-6f14e21b1d94', + 'User-Agent': 'PostmanRuntime/7.29.2', + Via: '1.1 e453cfec7ab7b0f50057381607edb486.cloudfront.net (CloudFront)', + 'X-Amz-Cf-Id': 'AL_XrwZDLODp365r4ALZNt8piYvuuKet9zEQGxBGUrTB68EID6Tr2w==', + 'X-Amzn-Trace-Id': 'Root=1-62db01d2-5bca1ccb57b50f957f73a1df', + 'X-Forwarded-For': '187.253.120.36, 130.176.179.78', + 'X-Forwarded-Port': '443', + 'X-Forwarded-Proto': 'https' + }, + multiValueHeaders: { + Accept: [ '*/*' ], + 'Accept-Encoding': [ 'gzip, deflate, br' ], + 'CloudFront-Forwarded-Proto': [ 'https' ], + 'CloudFront-Is-Desktop-Viewer': [ 'true' ], + 'CloudFront-Is-Mobile-Viewer': [ 'false' ], + 'CloudFront-Is-SmartTV-Viewer': [ 'false' ], + 'CloudFront-Is-Tablet-Viewer': [ 'false' ], + 'CloudFront-Viewer-ASN': [ '28509' ], + 'CloudFront-Viewer-Country': [ 'MX' ], + 'Content-Type': [ 'application/json' ], + Host: [ 'o3lc79zr1i.execute-api.us-east-1.amazonaws.com' ], + 'Postman-Token': [ 'da51eb72-aa2e-41ea-a120-6f14e21b1d94' ], + 'User-Agent': [ 'PostmanRuntime/7.29.2' ], + Via: [ + '1.1 e453cfec7ab7b0f50057381607edb486.cloudfront.net (CloudFront)' + ], + 'X-Amz-Cf-Id': [ 'AL_XrwZDLODp365r4ALZNt8piYvuuKet9zEQGxBGUrTB68EID6Tr2w==' ], + 'X-Amzn-Trace-Id': [ 'Root=1-62db01d2-5bca1ccb57b50f957f73a1df' ], + 'X-Forwarded-For': [ '187.253.120.36, 130.176.179.78' ], + 'X-Forwarded-Port': [ '443' ], + 'X-Forwarded-Proto': [ 'https' ] + }, + queryStringParameters: null, + multiValueQueryStringParameters: null, + pathParameters: null, + stageVariables: null, + requestContext: { + resourceId: 'cic4y4', + resourcePath: '/products', + httpMethod: 'POST', + extendedRequestId: 'Vr046GH6oAMF93A=', + requestTime: '22/Jul/2022:20:00:18 +0000', + path: '/dev/products', + accountId: '436988374415', + protocol: 'HTTP/1.1', + stage: 'dev', + domainPrefix: 'o3lc79zr1i', + requestTimeEpoch: 1658520018467, + requestId: 'be81d25d-203b-4d50-9908-b173622b9b35', + identity: { + cognitoIdentityPoolId: null, + accountId: null, + cognitoIdentityId: null, + caller: null, + sourceIp: '187.253.120.36', + principalOrgId: null, + accessKey: null, + cognitoAuthenticationType: null, + cognitoAuthenticationProvider: null, + userArn: null, + userAgent: 'PostmanRuntime/7.29.2', + user: null + }, + domainName: 'o3lc79zr1i.execute-api.us-east-1.amazonaws.com', + apiId: 'o3lc79zr1i' + }, + body: '{\r\n' + + ' "title": "Test product :D",\r\n' + + ' "description": "Test description",\r\n' + + ' "price": 10\r\n' + + '}', + isBase64Encoded: false + }) + .expectResult(response => response); + + expect(result.statusCode).equals(502); + expect(JSON.parse(result.body)).equals('Missing Data'); + }); + + it('Should return 400 whit wrong data typeof', async () => { + const result = await lambda(createProduct) + .event( { + resource: '/products', + path: '/products', + httpMethod: 'POST', + headers: { + Accept: '*/*', + 'Accept-Encoding': 'gzip, deflate, br', + 'CloudFront-Forwarded-Proto': 'https', + 'CloudFront-Is-Desktop-Viewer': 'true', + 'CloudFront-Is-Mobile-Viewer': 'false', + 'CloudFront-Is-SmartTV-Viewer': 'false', + 'CloudFront-Is-Tablet-Viewer': 'false', + 'CloudFront-Viewer-ASN': '28509', + 'CloudFront-Viewer-Country': 'MX', + 'Content-Type': 'application/json', + Host: 'o3lc79zr1i.execute-api.us-east-1.amazonaws.com', + 'Postman-Token': 'da51eb72-aa2e-41ea-a120-6f14e21b1d94', + 'User-Agent': 'PostmanRuntime/7.29.2', + Via: '1.1 e453cfec7ab7b0f50057381607edb486.cloudfront.net (CloudFront)', + 'X-Amz-Cf-Id': 'AL_XrwZDLODp365r4ALZNt8piYvuuKet9zEQGxBGUrTB68EID6Tr2w==', + 'X-Amzn-Trace-Id': 'Root=1-62db01d2-5bca1ccb57b50f957f73a1df', + 'X-Forwarded-For': '187.253.120.36, 130.176.179.78', + 'X-Forwarded-Port': '443', + 'X-Forwarded-Proto': 'https' + }, + multiValueHeaders: { + Accept: [ '*/*' ], + 'Accept-Encoding': [ 'gzip, deflate, br' ], + 'CloudFront-Forwarded-Proto': [ 'https' ], + 'CloudFront-Is-Desktop-Viewer': [ 'true' ], + 'CloudFront-Is-Mobile-Viewer': [ 'false' ], + 'CloudFront-Is-SmartTV-Viewer': [ 'false' ], + 'CloudFront-Is-Tablet-Viewer': [ 'false' ], + 'CloudFront-Viewer-ASN': [ '28509' ], + 'CloudFront-Viewer-Country': [ 'MX' ], + 'Content-Type': [ 'application/json' ], + Host: [ 'o3lc79zr1i.execute-api.us-east-1.amazonaws.com' ], + 'Postman-Token': [ 'da51eb72-aa2e-41ea-a120-6f14e21b1d94' ], + 'User-Agent': [ 'PostmanRuntime/7.29.2' ], + Via: [ + '1.1 e453cfec7ab7b0f50057381607edb486.cloudfront.net (CloudFront)' + ], + 'X-Amz-Cf-Id': [ 'AL_XrwZDLODp365r4ALZNt8piYvuuKet9zEQGxBGUrTB68EID6Tr2w==' ], + 'X-Amzn-Trace-Id': [ 'Root=1-62db01d2-5bca1ccb57b50f957f73a1df' ], + 'X-Forwarded-For': [ '187.253.120.36, 130.176.179.78' ], + 'X-Forwarded-Port': [ '443' ], + 'X-Forwarded-Proto': [ 'https' ] + }, + queryStringParameters: null, + multiValueQueryStringParameters: null, + pathParameters: null, + stageVariables: null, + requestContext: { + resourceId: 'cic4y4', + resourcePath: '/products', + httpMethod: 'POST', + extendedRequestId: 'Vr046GH6oAMF93A=', + requestTime: '22/Jul/2022:20:00:18 +0000', + path: '/dev/products', + accountId: '436988374415', + protocol: 'HTTP/1.1', + stage: 'dev', + domainPrefix: 'o3lc79zr1i', + requestTimeEpoch: 1658520018467, + requestId: 'be81d25d-203b-4d50-9908-b173622b9b35', + identity: { + cognitoIdentityPoolId: null, + accountId: null, + cognitoIdentityId: null, + caller: null, + sourceIp: '187.253.120.36', + principalOrgId: null, + accessKey: null, + cognitoAuthenticationType: null, + cognitoAuthenticationProvider: null, + userArn: null, + userAgent: 'PostmanRuntime/7.29.2', + user: null + }, + domainName: 'o3lc79zr1i.execute-api.us-east-1.amazonaws.com', + apiId: 'o3lc79zr1i' + }, + body: '{\r\n' + + ' "title": false,\r\n' + + ' "description": "Test description",\r\n' + + ' "price": "10",\r\n' + + ' "count": false\r\n' + + '}', + isBase64Encoded: false + }) + .expectResult(response => response); + + expect(result.statusCode).equals(400); + expect(JSON.parse(result.body)).equals('Data is Invalid'); + }); + + it('Should return 504 whit wrong data typeof', async () => { + const result = await lambda(createProduct) + .event( { + resource: '/products', + path: '/products', + httpMethod: 'POST', + headers: { + Accept: '*/*', + 'Accept-Encoding': 'gzip, deflate, br', + 'CloudFront-Forwarded-Proto': 'https', + 'CloudFront-Is-Desktop-Viewer': 'true', + 'CloudFront-Is-Mobile-Viewer': 'false', + 'CloudFront-Is-SmartTV-Viewer': 'false', + 'CloudFront-Is-Tablet-Viewer': 'false', + 'CloudFront-Viewer-ASN': '28509', + 'CloudFront-Viewer-Country': 'MX', + 'Content-Type': 'application/json', + Host: 'o3lc79zr1i.execute-api.us-east-1.amazonaws.com', + 'Postman-Token': 'da51eb72-aa2e-41ea-a120-6f14e21b1d94', + 'User-Agent': 'PostmanRuntime/7.29.2', + Via: '1.1 e453cfec7ab7b0f50057381607edb486.cloudfront.net (CloudFront)', + 'X-Amz-Cf-Id': 'AL_XrwZDLODp365r4ALZNt8piYvuuKet9zEQGxBGUrTB68EID6Tr2w==', + 'X-Amzn-Trace-Id': 'Root=1-62db01d2-5bca1ccb57b50f957f73a1df', + 'X-Forwarded-For': '187.253.120.36, 130.176.179.78', + 'X-Forwarded-Port': '443', + 'X-Forwarded-Proto': 'https' + }, + multiValueHeaders: { + Accept: [ '*/*' ], + 'Accept-Encoding': [ 'gzip, deflate, br' ], + 'CloudFront-Forwarded-Proto': [ 'https' ], + 'CloudFront-Is-Desktop-Viewer': [ 'true' ], + 'CloudFront-Is-Mobile-Viewer': [ 'false' ], + 'CloudFront-Is-SmartTV-Viewer': [ 'false' ], + 'CloudFront-Is-Tablet-Viewer': [ 'false' ], + 'CloudFront-Viewer-ASN': [ '28509' ], + 'CloudFront-Viewer-Country': [ 'MX' ], + 'Content-Type': [ 'application/json' ], + Host: [ 'o3lc79zr1i.execute-api.us-east-1.amazonaws.com' ], + 'Postman-Token': [ 'da51eb72-aa2e-41ea-a120-6f14e21b1d94' ], + 'User-Agent': [ 'PostmanRuntime/7.29.2' ], + Via: [ + '1.1 e453cfec7ab7b0f50057381607edb486.cloudfront.net (CloudFront)' + ], + 'X-Amz-Cf-Id': [ 'AL_XrwZDLODp365r4ALZNt8piYvuuKet9zEQGxBGUrTB68EID6Tr2w==' ], + 'X-Amzn-Trace-Id': [ 'Root=1-62db01d2-5bca1ccb57b50f957f73a1df' ], + 'X-Forwarded-For': [ '187.253.120.36, 130.176.179.78' ], + 'X-Forwarded-Port': [ '443' ], + 'X-Forwarded-Proto': [ 'https' ] + }, + queryStringParameters: null, + multiValueQueryStringParameters: null, + pathParameters: null, + stageVariables: null, + requestContext: { + resourceId: 'cic4y4', + resourcePath: '/products', + httpMethod: 'POST', + extendedRequestId: 'Vr046GH6oAMF93A=', + requestTime: '22/Jul/2022:20:00:18 +0000', + path: '/dev/products', + accountId: '436988374415', + protocol: 'HTTP/1.1', + stage: 'dev', + domainPrefix: 'o3lc79zr1i', + requestTimeEpoch: 1658520018467, + requestId: 'be81d25d-203b-4d50-9908-b173622b9b35', + identity: { + cognitoIdentityPoolId: null, + accountId: null, + cognitoIdentityId: null, + caller: null, + sourceIp: '187.253.120.36', + principalOrgId: null, + accessKey: null, + cognitoAuthenticationType: null, + cognitoAuthenticationProvider: null, + userArn: null, + userAgent: 'PostmanRuntime/7.29.2', + user: null + }, + domainName: 'o3lc79zr1i.execute-api.us-east-1.amazonaws.com', + apiId: 'o3lc79zr1i' + }, + body: '{\r\n' + + ' "title": "Test product :D",\r\n' + + ' "description": "Test description",\r\n' + + ' "price": "10",\r\n' + + ' "count": false\r\n' + + '}', + isBase64Encoded: false + }) + .expectResult(response => response); + + expect(result.statusCode).equals(504); + expect(JSON.parse(result.body)).equals('An error occurred on Stock creation'); + }); + + it('Should return 200 with correct json', async () => { + const result = await lambda(createProduct) + .event( + { + resource: '/products', + path: '/products', + httpMethod: 'POST', + headers: { + Accept: '*/*', + 'Accept-Encoding': 'gzip, deflate, br', + 'CloudFront-Forwarded-Proto': 'https', + 'CloudFront-Is-Desktop-Viewer': 'true', + 'CloudFront-Is-Mobile-Viewer': 'false', + 'CloudFront-Is-SmartTV-Viewer': 'false', + 'CloudFront-Is-Tablet-Viewer': 'false', + 'CloudFront-Viewer-ASN': '28509', + 'CloudFront-Viewer-Country': 'MX', + 'Content-Type': 'application/json', + Host: 'o3lc79zr1i.execute-api.us-east-1.amazonaws.com', + 'Postman-Token': 'da51eb72-aa2e-41ea-a120-6f14e21b1d94', + 'User-Agent': 'PostmanRuntime/7.29.2', + Via: '1.1 e453cfec7ab7b0f50057381607edb486.cloudfront.net (CloudFront)', + 'X-Amz-Cf-Id': 'AL_XrwZDLODp365r4ALZNt8piYvuuKet9zEQGxBGUrTB68EID6Tr2w==', + 'X-Amzn-Trace-Id': 'Root=1-62db01d2-5bca1ccb57b50f957f73a1df', + 'X-Forwarded-For': '187.253.120.36, 130.176.179.78', + 'X-Forwarded-Port': '443', + 'X-Forwarded-Proto': 'https' + }, + multiValueHeaders: { + Accept: ['*/*'], + 'Accept-Encoding': ['gzip, deflate, br'], + 'CloudFront-Forwarded-Proto': ['https'], + 'CloudFront-Is-Desktop-Viewer': ['true'], + 'CloudFront-Is-Mobile-Viewer': ['false'], + 'CloudFront-Is-SmartTV-Viewer': ['false'], + 'CloudFront-Is-Tablet-Viewer': ['false'], + 'CloudFront-Viewer-ASN': ['28509'], + 'CloudFront-Viewer-Country': ['MX'], + 'Content-Type': ['application/json'], + Host: ['o3lc79zr1i.execute-api.us-east-1.amazonaws.com'], + 'Postman-Token': ['da51eb72-aa2e-41ea-a120-6f14e21b1d94'], + 'User-Agent': ['PostmanRuntime/7.29.2'], + Via: [ + '1.1 e453cfec7ab7b0f50057381607edb486.cloudfront.net (CloudFront)' + ], + 'X-Amz-Cf-Id': ['AL_XrwZDLODp365r4ALZNt8piYvuuKet9zEQGxBGUrTB68EID6Tr2w=='], + 'X-Amzn-Trace-Id': ['Root=1-62db01d2-5bca1ccb57b50f957f73a1df'], + 'X-Forwarded-For': ['187.253.120.36, 130.176.179.78'], + 'X-Forwarded-Port': ['443'], + 'X-Forwarded-Proto': ['https'] + }, + queryStringParameters: null, + multiValueQueryStringParameters: null, + pathParameters: null, + stageVariables: null, + requestContext: { + resourceId: 'cic4y4', + resourcePath: '/products', + httpMethod: 'POST', + extendedRequestId: 'Vr046GH6oAMF93A=', + requestTime: '22/Jul/2022:20:00:18 +0000', + path: '/dev/products', + accountId: '436988374415', + protocol: 'HTTP/1.1', + stage: 'dev', + domainPrefix: 'o3lc79zr1i', + requestTimeEpoch: 1658520018467, + requestId: 'be81d25d-203b-4d50-9908-b173622b9b35', + identity: { + cognitoIdentityPoolId: null, + accountId: null, + cognitoIdentityId: null, + caller: null, + sourceIp: '187.253.120.36', + principalOrgId: null, + accessKey: null, + cognitoAuthenticationType: null, + cognitoAuthenticationProvider: null, + userArn: null, + userAgent: 'PostmanRuntime/7.29.2', + user: null + }, + domainName: 'o3lc79zr1i.execute-api.us-east-1.amazonaws.com', + apiId: 'o3lc79zr1i' + }, + body: '{\r\n' + + ' "title": "Test product :D",\r\n' + + ' "description": "Test description",\r\n' + + ' "price": 10,\r\n' + + ' "count": 5\r\n' + + '}', + isBase64Encoded: false + } + ) + .expectResult(result => result); + + expect(result.statusCode).equals(201); + }); + }); }); }); \ No newline at end of file diff --git a/product-service/utils/util.connection.js b/product-service/utils/util.connection.js new file mode 100644 index 0000000..07ed936 --- /dev/null +++ b/product-service/utils/util.connection.js @@ -0,0 +1,16 @@ +'use strict'; +const {Pool} = require('pg'); +const path = require('path') +require('dotenv').config({ path: path.resolve(__dirname, '../.env') }) + +const config = { + user: process.env.USER_DB, + host: process.env.HOST_DB, + password: process.env.PWD_DB, + database: process.env.DB_NAME, + port: process.env.PORT_DB +} + +const db = new Pool(config); + +module.exports = {db}; \ No newline at end of file diff --git a/product-service/utils/utils.functions.js b/product-service/utils/utils.functions.js new file mode 100644 index 0000000..ec2e81a --- /dev/null +++ b/product-service/utils/utils.functions.js @@ -0,0 +1,7 @@ +const Str = require('@supercharge/strings'); + +const validateProductData = (body) => { + return !!(Str.isString(body.title) && Str.isString(body.description) && !isNaN(body.price) && !isNaN(body.count)); +} + +module.exports = {validateProductData} \ No newline at end of file From 75d937d3dc0aec4ed28907d17f44c2b12f3d089b Mon Sep 17 00:00:00 2001 From: Michelle_Anguiano Date: Wed, 3 Aug 2022 11:19:41 -0500 Subject: [PATCH 07/12] - added create-product.js - added .env - added db connection --- product-service/test/lambda.product.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/product-service/test/lambda.product.test.js b/product-service/test/lambda.product.test.js index f3a0c4d..a6009e1 100644 --- a/product-service/test/lambda.product.test.js +++ b/product-service/test/lambda.product.test.js @@ -39,6 +39,7 @@ describe('product-service', () => { expect(JSON.parse(result.body).message).equals('Product not found'); }); + it('Should return 200 with productId', async () => { const result = await lambda(getProductById) .event({'pathParameters': {'productId': 'ABCDE102030'}}) From c565888545779eb05f24609e2e1d91e1e05b2a04 Mon Sep 17 00:00:00 2001 From: Michelle_Anguiano Date: Thu, 4 Aug 2022 01:17:49 -0500 Subject: [PATCH 08/12] - created impor-service - added images.js - added image.js - added test for lambda - added webpack.config.js - added serverless.yml configuration --- .gitignore | 2 - README.md | 19 ++++- import-service/config.js | 11 +++ import-service/image.js | 15 ++++ import-service/images.js | 27 +++++++ import-service/test/image.test.js | 23 ++++++ import-service/utils/utils.aws.functions.js | 89 +++++++++++++++++++++ import-service/webpack.config.js | 11 +++ package.json | 1 + 9 files changed, 195 insertions(+), 3 deletions(-) create mode 100644 import-service/config.js create mode 100644 import-service/image.js create mode 100644 import-service/images.js create mode 100644 import-service/test/image.test.js create mode 100644 import-service/utils/utils.aws.functions.js create mode 100644 import-service/webpack.config.js diff --git a/.gitignore b/.gitignore index 281aea5..f46f82b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,5 @@ # Serverless directories .serverless -../.env - .gitignore diff --git a/README.md b/README.md index ae62362..d02b3f6 100644 --- a/README.md +++ b/README.md @@ -11,4 +11,21 @@ Endpoint backend: https://o3lc79zr1i.execute-api.us-east-1.amazonaws.com/dev #### Description: Get a product by ID - productId is required - productId is String -- curl --location --request GET 'https://o3lc79zr1i.execute-api.us-east-1.amazonaws.com/dev/products/ABCDE102030' +- curl --location --request GET 'https://o3lc79zr1i.execute-api.us-east-1.amazonaws.com/dev/products/ABC001' + +### POST - Create product - /product +#### Description: Create a product + +- title: String - is required +- description: String - is requires +- price: Number - is required +- count": Number - is required + +## Import + +### Get - Image +#### Description: Get image from bucket +- curl --location --request GET 'https://guouzuadzd.execute-api.us-east-1.amazonaws.com/dev/import/Alexa' + +## Frontend +### URL: http://epam-shopcart.s3-website-us-east-1.amazonaws.com/ \ No newline at end of file diff --git a/import-service/config.js b/import-service/config.js new file mode 100644 index 0000000..cb2dc21 --- /dev/null +++ b/import-service/config.js @@ -0,0 +1,11 @@ +const dotenv = require('dotenv'); + +module.exports = async ({ options, resolveConfigurationProperty }) => { + // Load env vars into Serverless environment + // You can do more complicated env var resolution with dotenv here + const envVars = dotenv.config({ path: '.env' }).parsed; + return Object.assign( + {}, + envVars, // `dotenv` environment variables + ); +}; \ No newline at end of file diff --git a/import-service/image.js b/import-service/image.js new file mode 100644 index 0000000..5498111 --- /dev/null +++ b/import-service/image.js @@ -0,0 +1,15 @@ +'use strict'; + +const utils = require('./utils/utils.aws.functions'); + +module.exports.importFileParser = async (event) => { + console.log(`Event: ${JSON.stringify(event)}`); + for (const record of event.Records) { + await utils.moveImage(record.s3.object.key); + } + + return { + statusCode: 200, + body: `importFileParser executed` + }; +} \ No newline at end of file diff --git a/import-service/images.js b/import-service/images.js new file mode 100644 index 0000000..159dcb3 --- /dev/null +++ b/import-service/images.js @@ -0,0 +1,27 @@ +'use strict'; +const utils = require('./utils/utils.aws.functions'); + +module.exports.importProductsFile = async (event) => { + const body = {}; + let status = 200 + + if (!event[`pathParameters`].nameImage) { + status = 500; + body.message = 'Name image not provided' + } else { + const image = event[`pathParameters`].nameImage; + const result = await utils.getSignedImage(image).then((data) => data).catch(error => error); + + body.message = result.message; + body.url = result.status === 200 ? result.url : ''; + } + + return { + statusCode: status, + headers: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Credentials': true, + }, + body: JSON.stringify(body) + }; +} \ No newline at end of file diff --git a/import-service/test/image.test.js b/import-service/test/image.test.js new file mode 100644 index 0000000..e091291 --- /dev/null +++ b/import-service/test/image.test.js @@ -0,0 +1,23 @@ +const expect = require('chai').expect; +const tester = require('lambda-tester'); +const getImage = require('../images').importProductsFile; + +describe('import-service', () => { + describe('lambdas', () => { + it('Should return 500', async () => { + const result = await tester(getImage) + .event({'pathParameters': {'nameImage': ''}}) + .expectResult(data => data); + expect(JSON.parse(result.statusCode)).equals(500); + expect(JSON.parse(result.body).message).equals('Name image not provided') + }); + + it('Should return 200', async () => { + const result = await tester(getImage) + .event({'pathParameters': {'nameImage': 'Alexa'}}) + .expectResult(data => data); + expect(JSON.parse(result.statusCode)).equals(200); + expect(JSON.parse(result.body).message).equals('Success'); + }); + }); +}); \ No newline at end of file diff --git a/import-service/utils/utils.aws.functions.js b/import-service/utils/utils.aws.functions.js new file mode 100644 index 0000000..63d60b8 --- /dev/null +++ b/import-service/utils/utils.aws.functions.js @@ -0,0 +1,89 @@ +'use strict'; + +const AWS = require('aws-sdk'); +const path = require('path'); +const csv = require('csv-parser') +require('dotenv').config({path: path.resolve(__dirname, '../.env')}) + +const s3 = new AWS.S3({region: 'us-east-1'}); + +const bucket = process.env.BUCKET; +const uploaded = process.env.PATH_AWS; + +const getSignedImage = (image) => new Promise(async (resolve, reject) => { + try { + console.log(`getSignedImage executing`); + console.log(`Image: ${image}`); + const params = { + Bucket: bucket, + Key: `${uploaded}/${image}.jpg` + }; + + console.log(JSON.stringify(params)); + + console.log(`Start getSignedUrl function`); + + await s3.getSignedUrl('getObject', params, (error, url) => { + if (error) { + console.log(error); + reject({status: 500, error, message: 'An error occurred getting signed url'}); + } + resolve({status: 200, url, message: 'Success'}); + }); + } catch (error) { + console.log(`Error on getSignedImage: ${error}`); + reject({status: 500, message: `Error on getSignedImage: ${error}`}) + } +}); + +const moveImage = async (image) => { + try { + const params = { + Bucket: bucket, + Key: image + }; + + /*console.log(`Params: ${JSON.stringify(params)}`); + + const s3Stream = await s3.getObject(params).createReadStream(); + + console.log(`s3Stream: ${JSON.stringify(s3Stream)}`); + + await s3Stream.pipe(csv()) + .on('data', data => { + console.log(data); + }) + .on('end', async () => { + console.log(`${bucket}/${image}`); + await s3.copyObject({ + Bucket: bucket, + CopySource: `${bucket}/${image}`, + Key: image.replace('images', path) + }).promise(); + + await s3.deleteObject({ + Bucket: bucket, + Key: image + }).promise(); + + console.log(`Copied into ${bucket}/${image.replace('images', path)}`); + });*/ + + console.log(`${bucket}/${image}`); + await s3.copyObject({ + Bucket: bucket, + CopySource: `${bucket}/${image}`, + Key: image.replace('images', path) + }).promise(); + + await s3.deleteObject(params).promise(); + + console.log(`Copied into ${bucket}/${JSON.stringify(image.replace('images', path))}`); + return true; + } catch (error) { + console.log(`Error on moveImage: ${error}`); + return false; + } +} + +module.exports = {getSignedImage, moveImage} \ No newline at end of file diff --git a/import-service/webpack.config.js b/import-service/webpack.config.js new file mode 100644 index 0000000..0266cb6 --- /dev/null +++ b/import-service/webpack.config.js @@ -0,0 +1,11 @@ +const slsw = require('serverless-webpack'); +const nodeExternals = require('webpack-node-externals'); +module.exports = { + context: __dirname, + mode: slsw.lib.webpack.isLocal ? 'development' : 'production', + entry: slsw.lib.entries, + target: 'node', + /*target: 'node', + mode: 'none',*/ + externals: [nodeExternals(), 'pg-native'], +}; diff --git a/package.json b/package.json index f447bb9..2a16f57 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@supercharge/strings": "^1.28.0", "aws-sdk": "^2.1181.0", "chai": "^4.3.6", + "csv-parser": "^3.0.0", "dotenv": "^16.0.1", "lambda-tester": "^4.0.1", "mocha": "^10.0.0", From 37f87450b5aaea9c54f1a9b5b50c1ce8dcbf2093 Mon Sep 17 00:00:00 2001 From: Michelle_Anguiano Date: Thu, 4 Aug 2022 23:53:59 -0500 Subject: [PATCH 09/12] - modified README.md --- import-service/handler.js | 18 ++++++++ import-service/serverless.yml | 86 +++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 import-service/handler.js create mode 100644 import-service/serverless.yml diff --git a/import-service/handler.js b/import-service/handler.js new file mode 100644 index 0000000..7f8b0cc --- /dev/null +++ b/import-service/handler.js @@ -0,0 +1,18 @@ +'use strict'; + +module.exports.hello = async (event) => { + return { + statusCode: 200, + body: JSON.stringify( + { + message: 'Go Serverless v1.0! Your function executed successfully!', + input: event, + }, + null, + 2 + ), + }; + + // Use this code if you don't use the http event with the LAMBDA-PROXY integration + // return { message: 'Go Serverless v1.0! Your function executed successfully!', event }; +}; diff --git a/import-service/serverless.yml b/import-service/serverless.yml new file mode 100644 index 0000000..34807ba --- /dev/null +++ b/import-service/serverless.yml @@ -0,0 +1,86 @@ +# Welcome to Serverless! +# +# This file is the main config file for your service. +# It's very minimal at this point and uses default values. +# You can always add more config options for more control. +# We've included some commented out config examples here. +# Just uncomment any of them to get that config option. +# +# For full config options, check the docs: +# docs.serverless.com +# +# Happy Coding! + +service: import-service +# app and org for use with dashboard.serverless.com +#app: your-app-name +#org: your-org-name + +# You can pin your service to only deploy with a specific Serverless version +# Check out our docs for more details +frameworkVersion: '3' + +provider: + name: aws + runtime: nodejs12.x + stage: dev + region: us-east-1 + profile: default + environment: + REGION: ${self:custom.dotenvVars.REGION, env:REGION, ''} + BUCKET: ${self:custom.dotenvVars.BUCKET, env:BUCKET, ''} + PATH_AWS: ${self:custom.dotenvVars.PATH_AWS, env:PATH_AWS, ''} + iamRoleStatements: + - Effect: Allow + Action: 's3:ListBucket' + Resource: + - 'arn:aws:s3:::src-image-shop' + - Effect: Allow + Action: + - 's3:*' + Resource: + - 'arn:aws:s3:::src-image-shop/*' + +plugins: + - serverless-webpack + - serverless-dotenv-plugin + +custom: + webpack: + webpackConfig: 'webpack.config.js' # Name of webpack configuration file + includeModules: false + packager: 'npm' + dotenvVars: ${./configs.js)} + +functions: + hello: + handler: handler.hello + getSignedImage: + handler: images.importProductsFile + events: + - http: + path: /import/{nameImage} + method: get + cors: + origins: '*' + headers: + - Content-Type + - X-Amz-Date + - Authorization + - X-Api-Key + - X-Amz-Security-Token + - X-Amz-User-Agent + allowCredentials: false + request: + parameters: + paths: + nameImage: true + importFileParser: + handler: image.importFileParser + events: + - s3: + bucket: src-image-shop + event: s3:ObjectCreated:* + rules: + - prefix: images/ + existing: true \ No newline at end of file From dfbb9fb35d5d26356fd22488e116d19cecca70a8 Mon Sep 17 00:00:00 2001 From: Michelle_Anguiano Date: Fri, 5 Aug 2022 13:49:36 -0500 Subject: [PATCH 10/12] - added batch-process.js - modified image.js - added sns - added sqs --- import-service/serverless.yml | 4 ++ import-service/utils/utils.aws.functions.js | 31 ++++++++++++++- product-service/batch-process.js | 40 ++++++++++++++++++++ product-service/serverless.yml | 19 +++++++++- product-service/utils/utils.aws.functions.js | 29 ++++++++++++++ 5 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 product-service/batch-process.js create mode 100644 product-service/utils/utils.aws.functions.js diff --git a/import-service/serverless.yml b/import-service/serverless.yml index 34807ba..fe1f60e 100644 --- a/import-service/serverless.yml +++ b/import-service/serverless.yml @@ -30,6 +30,7 @@ provider: REGION: ${self:custom.dotenvVars.REGION, env:REGION, ''} BUCKET: ${self:custom.dotenvVars.BUCKET, env:BUCKET, ''} PATH_AWS: ${self:custom.dotenvVars.PATH_AWS, env:PATH_AWS, ''} + SQS_URL: ${self:custom.dotenvVars.SQS_URL, env:SQS_URL, ''} iamRoleStatements: - Effect: Allow Action: 's3:ListBucket' @@ -40,6 +41,9 @@ provider: - 's3:*' Resource: - 'arn:aws:s3:::src-image-shop/*' + - Effect: Allow + Action: 'sqs:SendMessage' + Resource: 'arn:aws:sqs:us-east-1:436988374415:catalogItemsQueue.fifo' plugins: - serverless-webpack diff --git a/import-service/utils/utils.aws.functions.js b/import-service/utils/utils.aws.functions.js index 63d60b8..fc11089 100644 --- a/import-service/utils/utils.aws.functions.js +++ b/import-service/utils/utils.aws.functions.js @@ -3,12 +3,14 @@ const AWS = require('aws-sdk'); const path = require('path'); const csv = require('csv-parser') -require('dotenv').config({path: path.resolve(__dirname, '../.env')}) +require('dotenv').config({path: path.resolve(__dirname, '../.env')}); const s3 = new AWS.S3({region: 'us-east-1'}); +const sqs = new AWS.SQS({region: 'us-east-1'}); const bucket = process.env.BUCKET; const uploaded = process.env.PATH_AWS; +const SQS_URL = process.env.SQS_URL; const getSignedImage = (image) => new Promise(async (resolve, reject) => { try { @@ -78,6 +80,15 @@ const moveImage = async (image) => { await s3.deleteObject(params).promise(); + const data = {} + + data.title = 'Test SQS'; + data.description = 'Description From SQS'; + data.price = 0; + data.count = 0; + + sender(data); + console.log(`Copied into ${bucket}/${JSON.stringify(image.replace('images', path))}`); return true; } catch (error) { @@ -86,4 +97,22 @@ const moveImage = async (image) => { } } +const sender = async (data) => { + console.log(`Sender SQS executing`); + const params = { + MessageBody: data, + QueueUrl: SQS_URL + }; + + console.log(`Params SQS ${params}`) + + await sqs.sendMessage(params, (error, data) => { + console.log(`sqs.sendMessage`); + if(error) { + console.log(`Error on sqs send message ${error}`); + } + console.log(`Data: ${data}`); + }) +} + module.exports = {getSignedImage, moveImage} \ No newline at end of file diff --git a/product-service/batch-process.js b/product-service/batch-process.js new file mode 100644 index 0000000..4b3b3f9 --- /dev/null +++ b/product-service/batch-process.js @@ -0,0 +1,40 @@ +'use strict'; + +const {v4: uuidv4} = require('uuid'); + +const util = require('./utils/utils.aws.functions'); + +const product = require('./controller/product.controller').create; +const stock = require('./controller/stock.controller').create; + +module.exports.catalogBatchProcess = async (event) => { + //revived sqs event + for (const record of event.Records) { + //create product + const body = JSON.parse(record.body); + + const p = {}; + p.id = uuidv4(); + p.title = body.title; + p.price = body.price; + + let isCreated = await product(p); + + const s = {} + s.productId = p.id; + s.count = body.count; + + if(isCreated) { + isCreated = await stock(s); + + if (isCreated) { + util.sendEmail(p); + } + } + } + + return { + statusCode: 200, + body: `catalogBatchProcess executed` + }; +}; \ No newline at end of file diff --git a/product-service/serverless.yml b/product-service/serverless.yml index ddae572..3b69839 100644 --- a/product-service/serverless.yml +++ b/product-service/serverless.yml @@ -31,6 +31,14 @@ provider: PWD_DB: ${self:custom.dotenvVars.PWD_DB, env:PWD_DB, ''} PORT_DB: ${self:custom.dotenvVars.PORT_DB, env:PORT_DB, ''} DB_NAME: ${self:custom.dotenvVars.DB_NAME, env:DB_NAME, ''} + SNS_TOPIC: ${self:custom.dotenvVars.SNS_TOPIC, env:SNS_TOPIC, ''} + iamRoleStatements: + - Effect: Allow + Action: 'sqs:GetQueueAttributes' + Resource: '*' + - Effect: Allow + Action: 'sns:Publish' + Resource: 'arn:aws:sns:us-east-1:436988374415:createProductTopic' plugins: - serverless-webpack @@ -99,4 +107,13 @@ functions: - X-Api-Key - X-Amz-Security-Token - X-Amz-User-Agent - allowCredentials: false \ No newline at end of file + allowCredentials: false + catalogBatchProcess: + handler: batch-process.catalogBatchProcess + events: + - sqs: + arn: 'arn:aws:sqs:us-east-1:436988374415:catalogItemsQueue.fifo' + batchSize: 5 + - sns: + arn: 'arn:aws:sns:us-east-1:436988374415:createProductTopic' + topicName: createProductTopic \ No newline at end of file diff --git a/product-service/utils/utils.aws.functions.js b/product-service/utils/utils.aws.functions.js new file mode 100644 index 0000000..0c92a69 --- /dev/null +++ b/product-service/utils/utils.aws.functions.js @@ -0,0 +1,29 @@ +const AWS = require('aws-sdk'); +const path = require('path'); +require('dotenv').config({path: path.resolve(__dirname, '../.env')}); + +const sns = new AWS.SNS({region: 'us-east-1'}); +const topic = process.env.SNS_TOPIC; + +const sendEmail = async (product) => { + console.log(`sendEmail executing`); + try { + const params = { + Message: `The product ${product.time} with ID: ${product.id} has been created`, + TopicARN: topic + } + + console.log(`Params SNS: ${params}`); + + sns.publish(params, (error, data) => { + if(error) { + console.log(`Error on sns.publish: ${error}`); + } + console.log(`Data: ${data}`); + }); + } catch (error) { + console.log(`Error on sendEmail: ${error}`); + } +}; + +module.exports = {sendEmail}; \ No newline at end of file From d9ef3241c5cc92a1d5ae616c94265f7574d31995 Mon Sep 17 00:00:00 2001 From: Michelle_Anguiano Date: Sat, 6 Aug 2022 10:17:43 -0500 Subject: [PATCH 11/12] - fixed sqs received - sns fixed --- import-service/utils/utils.aws.functions.js | 18 ++++++----- product-service/batch-process.js | 10 ++++-- product-service/serverless.yml | 6 ++-- product-service/test/lambda.product.test.js | 33 ++++++++++++++++++++ product-service/utils/utils.aws.functions.js | 9 +++--- 5 files changed, 58 insertions(+), 18 deletions(-) diff --git a/import-service/utils/utils.aws.functions.js b/import-service/utils/utils.aws.functions.js index fc11089..a2e8589 100644 --- a/import-service/utils/utils.aws.functions.js +++ b/import-service/utils/utils.aws.functions.js @@ -72,10 +72,12 @@ const moveImage = async (image) => { });*/ console.log(`${bucket}/${image}`); + console.log(`path`) + await s3.copyObject({ Bucket: bucket, CopySource: `${bucket}/${image}`, - Key: image.replace('images', path) + Key: image.replace('images', uploaded) }).promise(); await s3.deleteObject(params).promise(); @@ -87,9 +89,9 @@ const moveImage = async (image) => { data.price = 0; data.count = 0; - sender(data); + await sender(data); - console.log(`Copied into ${bucket}/${JSON.stringify(image.replace('images', path))}`); + console.log(`Copied into ${bucket}/${image.replace('images', uploaded)}`); return true; } catch (error) { console.log(`Error on moveImage: ${error}`); @@ -97,21 +99,21 @@ const moveImage = async (image) => { } } -const sender = async (data) => { +const sender = async (dataPorduct) => { console.log(`Sender SQS executing`); const params = { - MessageBody: data, + MessageBody: JSON.stringify(dataPorduct), QueueUrl: SQS_URL }; - console.log(`Params SQS ${params}`) + console.log(`Params SQS ${JSON.stringify(params)}`) await sqs.sendMessage(params, (error, data) => { console.log(`sqs.sendMessage`); if(error) { - console.log(`Error on sqs send message ${error}`); + console.log(`Error on sqs send message ${JSON.stringify(error)}`); } - console.log(`Data: ${data}`); + console.log(`Data: ${JSON.stringify(data)}`); }) } diff --git a/product-service/batch-process.js b/product-service/batch-process.js index 4b3b3f9..ba58e98 100644 --- a/product-service/batch-process.js +++ b/product-service/batch-process.js @@ -8,15 +8,19 @@ const product = require('./controller/product.controller').create; const stock = require('./controller/stock.controller').create; module.exports.catalogBatchProcess = async (event) => { + console.log(JSON.stringify(event)); //revived sqs event for (const record of event.Records) { //create product - const body = JSON.parse(record.body); - + console.log(JSON.stringify(record)); + const body = JSON.parse(JSON.parse(record.body)); const p = {}; p.id = uuidv4(); p.title = body.title; p.price = body.price; + p.description = body.description; + + console.log(JSON.stringify(p)); let isCreated = await product(p); @@ -28,7 +32,7 @@ module.exports.catalogBatchProcess = async (event) => { isCreated = await stock(s); if (isCreated) { - util.sendEmail(p); + await util.sendEmail(p); } } } diff --git a/product-service/serverless.yml b/product-service/serverless.yml index 3b69839..7d4f287 100644 --- a/product-service/serverless.yml +++ b/product-service/serverless.yml @@ -114,6 +114,6 @@ functions: - sqs: arn: 'arn:aws:sqs:us-east-1:436988374415:catalogItemsQueue.fifo' batchSize: 5 - - sns: - arn: 'arn:aws:sns:us-east-1:436988374415:createProductTopic' - topicName: createProductTopic \ No newline at end of file +# - sns: +# arn: 'arn:aws:sns:us-east-1:436988374415:createProductTopic' +# topicName: createProductTopic \ No newline at end of file diff --git a/product-service/test/lambda.product.test.js b/product-service/test/lambda.product.test.js index a6009e1..f3f3bf8 100644 --- a/product-service/test/lambda.product.test.js +++ b/product-service/test/lambda.product.test.js @@ -5,6 +5,7 @@ const lambda = require('lambda-tester'); const getProductList = require('../get-products').getProductsList; const getProductById = require('../get-product').getProductById; const createProduct = require('../create-product').createProduct; +const batch = require('../batch-process').catalogBatchProcess describe('product-service', () => { @@ -549,6 +550,38 @@ describe('product-service', () => { expect(result.statusCode).equals(201); }); }); + + describe('batch-lambda', () => { + it('Getting SQS Message', async () => { + await lambda(batch) + .event( + { + "Records": [ + { + "messageId": "48e3ff89-33cc-451f-b564-4036a0094d65", + "receiptHandle": "AQEBteTrXlEu1TBfJzaJMyipS+McKYkf8UrBKn/jzFaRfNbq8edSXg25eZ1nl1gomZzmqdKqOpaHRgxoQklgj/g4shF41b2uLGCk8BezFMKD1Wwmns+U7pgk6zcNotVBmmtn2WLOOEIOek3fpNALG8s6DRJ87xyMuy5izE0Q/YUkIcb9knXRfdWgLBLHlm1P/cJ/3v3jIlOZKcMX4zB/DSQIFIqaXHH+3PLXq9T9rdAMa7g1ScCUQ+g49dP5wWCJ80H8+attjoc1PkW/i/UT0lMxiTy0BLqGe5WljJG6s0ghMzc=", + "body": "\"{\\\"title\\\":\\\"Test SQS\\\",\\\"description\\\":\\\"Description From SQS\\\",\\\"price\\\":0,\\\"count\\\":0}\"", + "attributes": { + "ApproximateReceiveCount": "1", + "SentTimestamp": "1659737693375", + "SequenceNumber": "18871636923213551616", + "MessageGroupId": "test", + "SenderId": "AIDAWLPUJNWH4HTC6FCEQ", + "MessageDeduplicationId": "test", + "ApproximateFirstReceiveTimestamp": "1659737693375" + }, + "messageAttributes": {}, + "md5OfBody": "5552aaf6126dd9668cdf421186622f2d", + "eventSource": "aws:sqs", + "eventSourceARN": "arn:aws:sqs:us-east-1:436988374415:catalogItemsQueue.fifo", + "awsRegion": "us-east-1" + } + ] + } + ) + .expectResult(result => result); + }) + }); }); }); \ No newline at end of file diff --git a/product-service/utils/utils.aws.functions.js b/product-service/utils/utils.aws.functions.js index 0c92a69..acd0fb0 100644 --- a/product-service/utils/utils.aws.functions.js +++ b/product-service/utils/utils.aws.functions.js @@ -7,19 +7,20 @@ const topic = process.env.SNS_TOPIC; const sendEmail = async (product) => { console.log(`sendEmail executing`); + console.log(topic) try { const params = { - Message: `The product ${product.time} with ID: ${product.id} has been created`, - TopicARN: topic + TopicArn: topic, + Message: `The product ${product.time} with ID: ${product.id} has been created` } console.log(`Params SNS: ${params}`); - sns.publish(params, (error, data) => { + await sns.publish(params, (error, data) => { if(error) { console.log(`Error on sns.publish: ${error}`); } - console.log(`Data: ${data}`); + console.log(`Data: ${JSON.stringify(data)}`); }); } catch (error) { console.log(`Error on sendEmail: ${error}`); From 17d8b4e055adba723e9c6727ea73ba124c7bbd3f Mon Sep 17 00:00:00 2001 From: Michelle_Anguiano Date: Sat, 6 Aug 2022 15:58:23 -0500 Subject: [PATCH 12/12] - added test for image.test.js - added test for batch-process.js - modified process to create a product - added config to send message to sqs --- import-service/test/image.test.js | 79 +++++++++++++++++---- import-service/utils/utils.aws.functions.js | 57 ++++++++------- product-service/batch-process.js | 6 +- product-service/test/lambda.product.test.js | 6 +- 4 files changed, 104 insertions(+), 44 deletions(-) diff --git a/import-service/test/image.test.js b/import-service/test/image.test.js index e091291..184a522 100644 --- a/import-service/test/image.test.js +++ b/import-service/test/image.test.js @@ -1,23 +1,76 @@ const expect = require('chai').expect; -const tester = require('lambda-tester'); +const lambda = require('lambda-tester'); const getImage = require('../images').importProductsFile; +const moveImage = require('../image').importFileParser; describe('import-service', () => { describe('lambdas', () => { - it('Should return 500', async () => { - const result = await tester(getImage) - .event({'pathParameters': {'nameImage': ''}}) - .expectResult(data => data); - expect(JSON.parse(result.statusCode)).equals(500); - expect(JSON.parse(result.body).message).equals('Name image not provided') + describe('get-image', () => { + it('Should return 500', async () => { + const result = await lambda(getImage) + .event({'pathParameters': {'nameImage': ''}}) + .expectResult(data => data); + expect(JSON.parse(result.statusCode)).equals(500); + expect(JSON.parse(result.body).message).equals('Name image not provided') + }); + + it('Should return 200', async () => { + const result = await lambda(getImage) + .event({'pathParameters': {'nameImage': 'Alexa'}}) + .expectResult(data => data); + expect(JSON.parse(result.statusCode)).equals(200); + expect(JSON.parse(result.body).message).equals('Success'); + }); }); - it('Should return 200', async () => { - const result = await tester(getImage) - .event({'pathParameters': {'nameImage': 'Alexa'}}) - .expectResult(data => data); - expect(JSON.parse(result.statusCode)).equals(200); - expect(JSON.parse(result.body).message).equals('Success'); + describe('move-image', () => { + it('Should return', async () => { + const result = await lambda(moveImage) + .event( + { + "Records": [ + { + "eventVersion": "2.1", + "eventSource": "aws:s3", + "awsRegion": "us-east-1", + "eventTime": "2022-08-06T14:29:57.320Z", + "eventName": "ObjectCreated:Put", + "userIdentity": { + "principalId": "AWS:AIDAWLPUJNWH4HTC6FCEQ" + }, + "requestParameters": { + "sourceIPAddress": "187.193.146.100" + }, + "responseElements": { + "x-amz-request-id": "398JW4JQ4K36K77H", + "x-amz-id-2": "fNnnzzT9SbH+TsRwnOyqL3VpRskNgK7bF8TlstrSHTeQDk8Y70RkwrroUuN44gWxb5chRgfIERPFGtJLEzbd513QkiXFMEuf" + }, + "s3": { + "s3SchemaVersion": "1.0", + "configurationId": "import-service-dev-importFileParser-057a87b0c6d7a62748d8b7e8958d9482", + "bucket": { + "name": "src-image-shop", + "ownerIdentity": { + "principalId": "A62BSUDDNWO6V" + }, + "arn": "arn:aws:s3:::src-image-shop" + }, + "object": { + "key": "images/Alexa.jpg", + "size": 31761, + "eTag": "607177f4d4599df93e5528f477d8c315", + "sequencer": "0062EE7AE54AF819E8" + } + } + } + ] + } + ) + .expectResult(data => data); + + expect(result.statusCode).equals(200); + }); }); + }); }); \ No newline at end of file diff --git a/import-service/utils/utils.aws.functions.js b/import-service/utils/utils.aws.functions.js index a2e8589..06ea3d9 100644 --- a/import-service/utils/utils.aws.functions.js +++ b/import-service/utils/utils.aws.functions.js @@ -3,6 +3,8 @@ const AWS = require('aws-sdk'); const path = require('path'); const csv = require('csv-parser') +const {v4: uuidv4} = require('uuid'); + require('dotenv').config({path: path.resolve(__dirname, '../.env')}); const s3 = new AWS.S3({region: 'us-east-1'}); @@ -71,9 +73,6 @@ const moveImage = async (image) => { console.log(`Copied into ${bucket}/${image.replace('images', path)}`); });*/ - console.log(`${bucket}/${image}`); - console.log(`path`) - await s3.copyObject({ Bucket: bucket, CopySource: `${bucket}/${image}`, @@ -82,14 +81,15 @@ const moveImage = async (image) => { await s3.deleteObject(params).promise(); - const data = {} + const product = {} - data.title = 'Test SQS'; - data.description = 'Description From SQS'; - data.price = 0; - data.count = 0; + product.id = uuidv4(); + product.title = image.substring(7, image.length - 4); + product.description = image.substring(7, image.length - 4); + product.price = 0; + product.count = 0; - await sender(data); + await sender(product); console.log(`Copied into ${bucket}/${image.replace('images', uploaded)}`); return true; @@ -99,22 +99,29 @@ const moveImage = async (image) => { } } -const sender = async (dataPorduct) => { - console.log(`Sender SQS executing`); - const params = { - MessageBody: JSON.stringify(dataPorduct), - QueueUrl: SQS_URL - }; - - console.log(`Params SQS ${JSON.stringify(params)}`) - - await sqs.sendMessage(params, (error, data) => { - console.log(`sqs.sendMessage`); - if(error) { - console.log(`Error on sqs send message ${JSON.stringify(error)}`); - } - console.log(`Data: ${JSON.stringify(data)}`); - }) +const sender = async (product) => { + try { + console.log(`Sender SQS executing`); + const params = { + MessageBody: JSON.stringify(product), + MessageDeduplicationId: product.id, + MessageGroupId: product.id, + QueueUrl: SQS_URL + }; + + console.log(`Params SQS ${JSON.stringify(params)}`) + + await sqs.sendMessage(params, (error, data) => { + console.log(`sqs.sendMessage`); + if (error) { + console.log(`Error on sqs send message ${JSON.stringify(error)}`); + } + console.log(`Data: ${JSON.stringify(data)}`); + }) + } catch (error) { + console.log(error); + } + } module.exports = {getSignedImage, moveImage} \ No newline at end of file diff --git a/product-service/batch-process.js b/product-service/batch-process.js index ba58e98..d4299a2 100644 --- a/product-service/batch-process.js +++ b/product-service/batch-process.js @@ -1,7 +1,5 @@ 'use strict'; -const {v4: uuidv4} = require('uuid'); - const util = require('./utils/utils.aws.functions'); const product = require('./controller/product.controller').create; @@ -13,9 +11,9 @@ module.exports.catalogBatchProcess = async (event) => { for (const record of event.Records) { //create product console.log(JSON.stringify(record)); - const body = JSON.parse(JSON.parse(record.body)); + const body = JSON.parse(record.body); const p = {}; - p.id = uuidv4(); + p.id = body.id; p.title = body.title; p.price = body.price; p.description = body.description; diff --git a/product-service/test/lambda.product.test.js b/product-service/test/lambda.product.test.js index f3f3bf8..6492e0c 100644 --- a/product-service/test/lambda.product.test.js +++ b/product-service/test/lambda.product.test.js @@ -553,14 +553,14 @@ describe('product-service', () => { describe('batch-lambda', () => { it('Getting SQS Message', async () => { - await lambda(batch) + const result = await lambda(batch) .event( { "Records": [ { "messageId": "48e3ff89-33cc-451f-b564-4036a0094d65", "receiptHandle": "AQEBteTrXlEu1TBfJzaJMyipS+McKYkf8UrBKn/jzFaRfNbq8edSXg25eZ1nl1gomZzmqdKqOpaHRgxoQklgj/g4shF41b2uLGCk8BezFMKD1Wwmns+U7pgk6zcNotVBmmtn2WLOOEIOek3fpNALG8s6DRJ87xyMuy5izE0Q/YUkIcb9knXRfdWgLBLHlm1P/cJ/3v3jIlOZKcMX4zB/DSQIFIqaXHH+3PLXq9T9rdAMa7g1ScCUQ+g49dP5wWCJ80H8+attjoc1PkW/i/UT0lMxiTy0BLqGe5WljJG6s0ghMzc=", - "body": "\"{\\\"title\\\":\\\"Test SQS\\\",\\\"description\\\":\\\"Description From SQS\\\",\\\"price\\\":0,\\\"count\\\":0}\"", + "body": "{\"id\":\"c835d671-a3f6-49be-8142-9c7dc3bb2c31\",\"title\":\"Test SQS\",\"description\":\"Description From SQS\",\"price\":0,\"count\":0}", "attributes": { "ApproximateReceiveCount": "1", "SentTimestamp": "1659737693375", @@ -580,6 +580,8 @@ describe('product-service', () => { } ) .expectResult(result => result); + + expect(result.statusCode).equals(200); }) }); });