-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16 from Guysnacho/24-sale-lambda
24 sale lambda
- Loading branch information
Showing
14 changed files
with
400 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{ "user_id": "insert_id_from_db_here", "quantity": 1, "sku": "0" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
-- Active: 1726216791298@@storefront-db.ct84ooq2shac.us-west-2.rds.amazonaws.com@5432@storefront | ||
CREATE TABLE public.stock ( | ||
sku SERIAL PRIMARY KEY, | ||
name TEXT NOT NULL, | ||
quantity SMALLINT NOT NULL DEFAULT 0, | ||
price INTEGER NOT NULL DEFAULT 0, | ||
item_url TEXT NOT NULL | ||
); | ||
|
||
-- Seed DB | ||
INSERT into public.stock (name, price, quantity, item_url) VALUES ('Bando Stone and The New World', 45, 3, 'https://t2.genius.com/unsafe/728x0/https%3A%2F%2Fimages.genius.com%2Ff19320aae82a75396d97def01ae89ff3.1000x1000x1.png'); | ||
INSERT into public.stock (name, price, quantity, item_url) VALUES ('alligator bites never heal - Doechii', 30, 2, 'https://shop.capitolmusic.com/cdn/shop/files/DoechiiABNHLPInsert.png?v=1724951711&width=800'); | ||
INSERT into public.stock (name, price, quantity, item_url) VALUES ('Nova - James Fauntleroy, Terrace Martin', 31, 4, 'https://images.squarespace-cdn.com/content/v1/5699291fa976afc919dbca7d/a701db3b-83c1-457b-a892-0c46b0e6749c/Nova+Artwork.jpg?format=500w'); | ||
|
||
CREATE TABLE public.order ( | ||
id UUID PRIMARY KEY DEFAULT gen_random_uuid (), | ||
user_id UUID NOT NULL REFERENCES member (id), | ||
sku SERIAL NOT NULL, | ||
quantity SMALLINT NOT NULL DEFAULT 0 | ||
); | ||
|
||
SELECT * from public.stock; | ||
SELECT * from public.order; | ||
|
||
CREATE or REPLACE function public.handle_sale () | ||
returns trigger as | ||
$$ | ||
declare results RECORD; | ||
begin | ||
SELECT sku, quantity INTO results FROM public.stock WHERE sku = new.sku; | ||
|
||
IF NOT FOUND THEN | ||
RAISE EXCEPTION 'item not found'; | ||
ELSEIF results.quantity - new.quantity < 1 THEN | ||
RAISE EXCEPTION 'not enough in stock'; | ||
END IF; | ||
UPDATE public.stock SET quantity = quantity - new.quantity WHERE sku = new.sku; | ||
return new; | ||
END; | ||
$$ language plpgsql; | ||
|
||
-- trigger the function every time a user is created | ||
create trigger on_sale_init | ||
after insert on public.order | ||
for each row execute procedure public.handle_sale(); | ||
|
||
-- ========================= ========================= ========================= ========================= | ||
-- Test your queries here before writing up production queries in the lambda | ||
-- Invalid row, FK not present | ||
INSERT into public.order (sku, quantity) VALUES (2, 1000); | ||
|
||
-- ========================= ========================= ========================= ========================= | ||
-- Add stock user | ||
-- INSERT into | ||
-- member (email, password, fname, lname) | ||
-- VALUES ( | ||
-- 'email', | ||
-- 'password', | ||
-- 'fname', | ||
-- 'lname' | ||
-- ); | ||
-- SELECT * from member; | ||
-- -- Init an invalid sale | ||
-- INSERT into public.order (user_id, sku, quantity) VALUES ('your_user_id_here', 2, 1000); | ||
-- -- Init a valid sale | ||
-- INSERT into public.order (user_id, sku, quantity) VALUES ('9b3b9b36-8fe5-4bbd-b655-c212704e4c79', 2, 1); | ||
|
||
-- delete FROM public.order; | ||
-- delete FROM public.stock; | ||
-- delete FROM public.member; | ||
|
||
-- -- Seed after manually running tests | ||
-- INSERT into public.stock (name, price, quantity, item_url) VALUES ('Bando Stone and The New World', 45, 3, 'https://t2.genius.com/unsafe/728x0/https%3A%2F%2Fimages.genius.com%2Ff19320aae82a75396d97def01ae89ff3.1000x1000x1.png'); | ||
-- INSERT into public.stock (name, price, quantity, item_url) VALUES ('alligator bites never heal - Doechii', 30, 2, 'https://shop.capitolmusic.com/cdn/shop/files/DoechiiABNHLPInsert.png?v=1724951711&width=800'); | ||
-- INSERT into public.stock (name, price, quantity, item_url) VALUES ('Nova - James Fauntleroy, Terrace Martin', 31, 4, 'https://images.squarespace-cdn.com/content/v1/5699291fa976afc919dbca7d/a701db3b-83c1-457b-a892-0c46b0e6749c/Nova+Artwork.jpg?format=500w'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
// All AWS SDK Clients are available under the @aws-sdk namespace. You can install them locally to see functions and types | ||
import { | ||
GetSecretValueCommand, | ||
ListSecretsCommand, | ||
SecretsManagerClient, | ||
} from "@aws-sdk/client-secrets-manager"; | ||
import postgres from "postgres"; | ||
// import { Handler } from "aws-lambda"; | ||
|
||
/** @type {Handler} */ | ||
const handler = async (event, context, callback) => { | ||
console.log(`Starting ${context?.functionName} invocation`); | ||
console.debug("Payload recieved"); | ||
console.debug(event); | ||
|
||
const payload = isValidPayload(event); | ||
|
||
// If bad request recieved | ||
if (!payload) throw new Error("bad request"); | ||
|
||
const secret = await fetchDBSecret(); | ||
|
||
if (!secret || secret == "") | ||
throw new Error("failed to fetch database creds"); | ||
|
||
/** Database Credentials @type {{username: string, password: string} | undefined} */ | ||
let creds; | ||
try { | ||
creds = JSON.parse(secret); | ||
} catch (error) { | ||
console.error(error); | ||
throw new Error("Failed to fetch db creds"); | ||
} | ||
|
||
if (creds == undefined || !creds.password || !creds.username) { | ||
console.error("Mission failed, we'll get em next time"); | ||
throw new Error("Invalid db creds"); | ||
} | ||
console.log("Successfully fetched DB creds ✨"); | ||
|
||
return await handleSale(payload, creds); | ||
}; | ||
|
||
/** | ||
* Request validation | ||
* @param {*} event | ||
* @returns {{ user_id: string; quantity: number; sku: string; } | undefined} | ||
*/ | ||
const isValidPayload = (event) => { | ||
if ( | ||
event?.user_id && | ||
event?.user_id !== "" && | ||
event?.sku && | ||
event?.sku !== "" && | ||
event?.quantity | ||
) { | ||
return event; | ||
} else return undefined; | ||
}; | ||
|
||
const fetchDBSecret = async () => { | ||
const client = new SecretsManagerClient({ region: process.env.AWS_REGION }); | ||
const listCommand = new ListSecretsCommand({ | ||
region: process.env.AWS_REGION, | ||
// For some reason plain text filtering isn't working. Need to fix this if we're gonna have multiple secrets | ||
// Filters: [{ Key: "name", Values: "rds" }], | ||
}); | ||
|
||
const res = await client | ||
.send(listCommand) | ||
.then((res) => res.SecretList) | ||
.catch((err) => { | ||
console.error(err); | ||
return new Error("Failed to fetch db secret"); | ||
}); | ||
if (typeof res == typeof Error || !res || res.length == 0) return undefined; | ||
|
||
const getSecretCommand = new GetSecretValueCommand({ | ||
SecretId: res[0].Name, | ||
}); | ||
const secret = await client | ||
.send(getSecretCommand) | ||
.then((res) => res.SecretString) | ||
.catch((err) => { | ||
console.error(err); | ||
return new Error("Failed to fetch db secret"); | ||
}); | ||
if (typeof res == typeof Error || !res || res.length == 0) return undefined; | ||
|
||
return secret; | ||
}; | ||
|
||
/** | ||
* Handle our sale | ||
* @param {{ user_id: string; quantity: number; sku: string; }} payload | ||
* @param {{username: string, password: string}} creds | ||
*/ | ||
const handleSale = async ({ user_id, sku, quantity }, creds) => { | ||
console.log("Handling sale"); | ||
|
||
// Build a client | ||
const sql = postgres({ | ||
database: "storefront", | ||
user: creds.username, | ||
pass: creds.password, | ||
host: process.env.db_host, | ||
connection: { | ||
application_name: process.env.AWS_LAMBDA_FUNCTION_NAME, | ||
}, | ||
}); | ||
|
||
// Perform our insert and select the price | ||
const res = await sql`INSERT into public.order | ||
(user_id, sku, quantity) VALUES | ||
(${user_id}, ${sku}, ${quantity}) | ||
returning * | ||
` | ||
.then((res) => { | ||
return { | ||
statusCode: 201, | ||
sku: res[0].id, | ||
statusDescription: "sale complete", | ||
}; | ||
}) | ||
.catch((err) => { | ||
console.error("Ran into an issue during the sale"); | ||
console.error(err); | ||
return { | ||
statusCode: 500, | ||
error: err.message, | ||
}; | ||
}); | ||
|
||
if (res.error) throw new Error(res.error); | ||
return res; | ||
}; | ||
|
||
export { handler }; | ||
|
Oops, something went wrong.