diff --git a/Week3/assignment/answers.md b/Week3/assignment/answers.md
new file mode 100644
index 000000000..f0cf62eec
--- /dev/null
+++ b/Week3/assignment/answers.md
@@ -0,0 +1,44 @@
+1. **Which columns break 1NF?**
+- Each cell must have one single value (atomic).
+- No lists or repeated values in one cell.
+
**These columns break 1NF:**
+
`food_code → example: C1, C2`
+
`food_description → example: Curry, Cake`
+
+
+2. **What entities can we extract?**
+ We can create 5 entities (tables):
+
+- Members → `people who join the dinner.`
+
+- Dinners → `dinner events.`
+
+- Venues → `places where dinners happen.`
+
+- Foods → `food items served.`
+
+Connections → who ate what, where, and when.
+
+3. **Name all the tables and columns that would make a 3NF compliant solution.**
+ - Members
+ `member_id Primary Key`
+ `member_name Text`
+ `member_address Text`
+
+ - Dinners
+ `dinner_id Primary Key`
+ `dinner_date Date`
+ `venue_code Foreign Key → Venues.venue_code`
+
+ - Venues
+ `venue_code Primary Key`
+ `venue_description Text`
+
+ - Foods
+ `food_code Primary Key`
+ `food_description Text`
+
+ - Member_Dinner
+ `member_id Foreign Key → Members.member_id`
+ `dinner_id Foreign Key → Dinners.dinner_id`
+
diff --git a/Week3/assignment/injection.js b/Week3/assignment/injection.js
new file mode 100644
index 000000000..28a47f997
--- /dev/null
+++ b/Week3/assignment/injection.js
@@ -0,0 +1,29 @@
+// Give an example of a value that can be passed as name and code that would take advantage of SQL-injection and ( fetch all the records in the database)
+function getPopulation(Country, name, code, cb) {
+ // assuming that connection to the database is established and stored as conn
+ conn.query(
+ `SELECT Population FROM ${Country} WHERE Name = '${name}' and code = '${code}'`,
+ function (err, result) {
+ if (err) cb(err);
+ if (result.length == 0) cb(new Error("Not found"));
+ cb(null, result[0].name);
+ }
+ );
+}
+// we can pass name as ('' OR '1'='1), or code as ('' OR '1'='1)
+// SELECT Population FROM Countries WHERE Name = '' OR '1'='1' and code = '' OR '1'='1';
+
+
+// Rewrite the function so that it is no longer vulnerable to SQL injection
+
+function getPopulation(Country, name, code, cb) {
+ conn.query(
+ `SELECT Population FROM ?? WHERE Name = ? AND code = ?`,
+ [Country, name, code],
+ function (err, result) {
+ if (err) return cb(err);
+ if (result.length === 0) return cb(new Error("Not found"));
+ cb(null, result[0].Population); // исправлено: result[0].name → result[0].Population
+ }
+ );
+}
\ No newline at end of file
diff --git a/Week3/assignment/package-lock.json b/Week3/assignment/package-lock.json
new file mode 100644
index 000000000..cd5647e44
--- /dev/null
+++ b/Week3/assignment/package-lock.json
@@ -0,0 +1,143 @@
+{
+ "name": "assignment",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "assignment",
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "mysql2": "^3.14.1"
+ }
+ },
+ "node_modules/aws-ssl-profiles": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz",
+ "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/denque": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
+ "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/generate-function": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
+ "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-property": "^1.0.2"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-property": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+ "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==",
+ "license": "MIT"
+ },
+ "node_modules/long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/lru-cache": {
+ "version": "7.18.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
+ "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/lru.min": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.2.tgz",
+ "integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==",
+ "license": "MIT",
+ "engines": {
+ "bun": ">=1.0.0",
+ "deno": ">=1.30.0",
+ "node": ">=8.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wellwelwel"
+ }
+ },
+ "node_modules/mysql2": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.14.1.tgz",
+ "integrity": "sha512-7ytuPQJjQB8TNAYX/H2yhL+iQOnIBjAMam361R7UAL0lOVXWjtdrmoL9HYKqKoLp/8UUTRcvo1QPvK9KL7wA8w==",
+ "license": "MIT",
+ "dependencies": {
+ "aws-ssl-profiles": "^1.1.1",
+ "denque": "^2.1.0",
+ "generate-function": "^2.3.1",
+ "iconv-lite": "^0.6.3",
+ "long": "^5.2.1",
+ "lru.min": "^1.0.0",
+ "named-placeholders": "^1.1.3",
+ "seq-queue": "^0.0.5",
+ "sqlstring": "^2.3.2"
+ },
+ "engines": {
+ "node": ">= 8.0"
+ }
+ },
+ "node_modules/named-placeholders": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
+ "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==",
+ "license": "MIT",
+ "dependencies": {
+ "lru-cache": "^7.14.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/seq-queue": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
+ "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
+ },
+ "node_modules/sqlstring": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
+ "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ }
+ }
+}
diff --git a/Week3/assignment/package.json b/Week3/assignment/package.json
new file mode 100644
index 000000000..8a78d83a7
--- /dev/null
+++ b/Week3/assignment/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "assignment",
+ "version": "1.0.0",
+ "type": "module",
+ "main": "transactions-create-tables.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "ISC",
+ "description": "",
+ "dependencies": {
+ "mysql2": "^3.14.1"
+ }
+}
diff --git a/Week3/assignment/transaction.js b/Week3/assignment/transaction.js
new file mode 100644
index 000000000..918f7253d
--- /dev/null
+++ b/Week3/assignment/transaction.js
@@ -0,0 +1,26 @@
+import mysql from 'mysql2/promise';
+
+const connection = await mysql.createConnection({
+ host: 'localhost',
+ user: 'hyfuser',
+ password : 'hyfpassword',
+});
+
+try {
+ await connection.query(`USE Transactions`)
+ await connection.query(`START TRANSACTION`);
+ await connection.query(`UPDATE account SET balance = balance - 1000 WHERE account_number = 1001`);
+ await connection.query(`UPDATE account SET balance = balance + 1000 WHERE account_number = 1002`);
+ await connection.query(`
+ INSERT INTO account_changes(account_number, amount, changed_date, remark)
+ VALUES
+ (1001, -1000, '2020-01-01', 'transaction'),
+ (1002, 1000, '2020-01-02', 'deposit')
+ `);
+ await connection.query(`COMMIT`);
+} catch (err) {
+ console.error("Transaction failed, rolling back:", err);
+ await connection.query(`ROLLBACK`);
+} finally {
+ connection.end();
+}
\ No newline at end of file
diff --git a/Week3/assignment/transactions-create-tables.js b/Week3/assignment/transactions-create-tables.js
new file mode 100644
index 000000000..7a6655df0
--- /dev/null
+++ b/Week3/assignment/transactions-create-tables.js
@@ -0,0 +1,30 @@
+import mysql from 'mysql2/promise';
+
+const connection = await mysql.createConnection({
+ host: 'localhost',
+ user: 'hyfuser',
+ password : 'hyfpassword',
+});
+
+try {
+ await connection.query(`DROP DATABASE IF EXISTS Transactions`);
+ await connection.query(`CREATE DATABASE IF NOT EXISTS Transactions`);
+ await connection.query(`USE Transactions`)
+ await connection.query(`CREATE TABLE IF NOT EXISTS account(
+ account_number INT AUTO_INCREMENT PRIMARY KEY,
+ balance INT NOT NULL DEFAULT 0)`
+ );
+
+ await connection.query(`CREATE TABLE IF NOT EXISTS account_changes(
+ change_number INT AUTO_INCREMENT PRIMARY KEY,
+ account_number INT,
+ amount INT,
+ changed_date DATETIME,
+ remark TEXT,
+ FOREIGN KEY (account_number) REFERENCES account(account_number))`
+ );
+} catch (err) {
+ console.log(err);
+} finally {
+ connection.end();
+}
\ No newline at end of file
diff --git a/Week3/assignment/transactions-insert-values.js b/Week3/assignment/transactions-insert-values.js
new file mode 100644
index 000000000..209bd3410
--- /dev/null
+++ b/Week3/assignment/transactions-insert-values.js
@@ -0,0 +1,25 @@
+import mysql from 'mysql2/promise';
+
+const connection = await mysql.createConnection({
+ host: 'localhost',
+ user: 'hyfuser',
+ password : 'hyfpassword',
+});
+
+try {
+ await connection.query(`USE Transactions`);
+ await connection.query(`INSERT INTO account(account_number, balance)
+ VALUES (1001, 5000),
+ (1002, 3000),
+ (1003, 7000)`
+ );
+ await connection.query(`INSERT INTO account_changes(account_number, amount, changed_date, remark)
+ VALUES (1001, 1000, '2020-01-01', 'Initial deposit'),
+ (1002, -1000, '2020-01-02', 'Initial deposit'),
+ (1003, 1000, '2020-01-03', 'Initial deposit')`
+ );
+} catch (err) {
+ console.log(err);
+} finally {
+ connection.end();
+}
\ No newline at end of file
diff --git a/Week3/homework/mongodb/index.js b/Week3/homework/mongodb/index.js
index 41ee8b618..ebce54062 100644
--- a/Week3/homework/mongodb/index.js
+++ b/Week3/homework/mongodb/index.js
@@ -1,6 +1,6 @@
-const { MongoClient, ServerApiVersion } = require("mongodb");
-
-const { seedDatabase } = require("./seedDatabase.js");
+import { MongoClient, ServerApiVersion } from 'mongodb';
+import 'dotenv/config';
+import { seedDatabase } from './seedDatabase.js';
async function createEpisodeExercise(client) {
/**
@@ -11,41 +11,52 @@ async function createEpisodeExercise(client) {
* elements: ["CIRRUS", "CLOUDS", "CONIFER", "DECIDIOUS", "GRASS", "MOUNTAIN", "MOUNTAINS", "RIVER", "SNOWY_MOUNTAIN", "TREE", "TREES"]
*/
- // Write code that will add this to the collection!
+ const dataBaseName = 'databaseWeek3';
+ const collectionName = 'bob_ross_episodes';
+ // Write code that will add this to the collection!
+ const result = await client.db(dataBaseName).collection(collectionName).insertOne({
+ episode: "S09E13",
+ title: "MOUNTAIN HIDE-AWAY",
+ elements: ["CIRRUS", "CLOUDS", "CONIFER", "DECIDIOUS", "GRASS", "MOUNTAIN", "MOUNTAINS", "RIVER", "SNOWY_MOUNTAIN", "TREE", "TREES"]
+ });
console.log(
- `Created season 9 episode 13 and the document got the id ${"TODO: fill in variable here"}`
+ `Created season 9 episode 13 and the document got the id ${result.insertedId}`
);
}
async function findEpisodesExercises(client) {
- /**
- * Complete the following exercises.
- * The comments indicate what to do and what the result should be!
- */
+ const doc = {episode: "S02E02"};
- // Find the title of episode 2 in season 2 [Should be: WINTER SUN]
+ const result = await client.db(dataBaseName).collection(collectionName).findOne(doc);
console.log(
- `The title of episode 2 in season 2 is ${"TODO: fill in variable here"}`
+ `The title of episode 2 in season 2 is ${result.title}`
);
// Find the season and episode number of the episode called "BLACK RIVER" [Should be: S02E06]
-
+ const result1 = await client.db(dataBaseName).collection(collectionName).findOne({ title: "BLACK RIVER"});
console.log(
- `The season and episode number of the "BLACK RIVER" episode is ${"TODO: fill in variable here"}`
+ `The season and episode number of the "BLACK RIVER" episode is ${result1.episode}`
);
// Find all of the episode titles where Bob Ross painted a CLIFF [Should be: NIGHT LIGHT, EVENING SEASCAPE, SURF'S UP, CLIFFSIDE, BY THE SEA, DEEP WILDERNESS HOME, CRIMSON TIDE, GRACEFUL WATERFALL]
-
+ const result2 = await client.db(dataBaseName).collection(collectionName).find({ elements: "CLIFF"});
+ const allCLIFFEpisodes = [];
+ for await (const doc of result2) {
+ allCLIFFEpisodes.push(doc.title);
+ }
console.log(
- `The episodes that Bob Ross painted a CLIFF are ${"TODO: fill in variable here"}`
+ `The episodes that Bob Ross painted a CLIFF are ${allCLIFFEpisodes.join(', ')}`
);
// Find all of the episode titles where Bob Ross painted a CLIFF and a LIGHTHOUSE [Should be: NIGHT LIGHT]
+ const result3 = await client.db(dataBaseName).collection(collectionName).find({ elements: { $all: ["CLIFF", "LIGHTHOUSE"] } }.toArray());
+
+ const allCLIFFandLIGHTHOUSEEpisodes = result3.map(doc => doc.title);
console.log(
- `The episodes that Bob Ross painted a CLIFF and a LIGHTHOUSE are ${"TODO: fill in variable here"}`
+ `The episodes that Bob Ross painted a CLIFF and a LIGHTHOUSE are ${allCLIFFandLIGHTHOUSEEpisodes.join(', ')}`
);
}
@@ -57,18 +68,32 @@ async function updateEpisodeExercises(client) {
* Note: do NOT change the data.json file
*/
- // Episode 13 in season 30 should be called BLUE RIDGE FALLS, yet it is called BLUE RIDGE FALLERS now. Fix that
+ // Episode 13 in season 30 should be called BLUE RIDGE FALLS, yet it is called BLUE RIDGE FALLERS now. Fix that
+ const updateDoc = {
+ $set: {
+ title: "BLUE RIDGE FALLS",
+ },
+ };
+
+ const result = await client.db(dataBaseName).collection(collectionName).updateOne({episode: "S30E13"}, updateDoc);
console.log(
- `Ran a command to update episode 13 in season 30 and it updated ${"TODO: fill in variable here"} episodes`
+ `Ran a command to update episode 13 in season 30 and it updated ${result.modifiedCount} episodes`
);
// Unfortunately we made a mistake in the arrays and the element type called 'BUSHES' should actually be 'BUSH' as sometimes only one bush was painted.
// Update all of the documents in the collection that have `BUSHES` in the elements array to now have `BUSH`
// It should update 120 episodes!
+ const result2 = await client
+ .db(dataBaseName)
+ .collection(collectionName)
+ .updateMany(
+ { elements: "BUSHES" }, [{$set: {elements: {$map: {input: "$elements", as: "el", in: {$cond: [{ $eq: ["$$el", "BUSHES"] }, "BUSH", "$$el"]}}}}}]
+ );
+
console.log(
- `Ran a command to update all the BUSHES to BUSH and it updated ${"TODO: fill in variable here"} episodes`
+ `Ran a command to update all the BUSHES to BUSH and it updated ${result2.modifiedCount} episodes`
);
}
@@ -78,8 +103,12 @@ async function deleteEpisodeExercise(client) {
* This is episode 14 in season 31. Please remove it and verify that it has been removed!
*/
+ const doc = {episode: "S31E14"};
+
+ const result = await client.db(dataBaseName).collection(collectionName).deleteOne(doc);
+
console.log(
- `Ran a command to delete episode and it deleted ${"TODO: fill in variable here"} episodes`
+ `Ran a command to delete episode and it deleted ${result.deletedCount} episodes`
);
}
diff --git a/Week3/homework/mongodb/package-lock.json b/Week3/homework/mongodb/package-lock.json
new file mode 100644
index 000000000..aaa95e343
--- /dev/null
+++ b/Week3/homework/mongodb/package-lock.json
@@ -0,0 +1,28 @@
+{
+ "name": "mongodb",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "mongodb",
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "dotenv": "^16.5.0"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.5.0",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
+ "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ }
+ }
+}
diff --git a/Week3/homework/mongodb/package.json b/Week3/homework/mongodb/package.json
new file mode 100644
index 000000000..1f2b72f1a
--- /dev/null
+++ b/Week3/homework/mongodb/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "mongodb",
+ "version": "1.0.0",
+ "main": "index.js",
+ "type": "module",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "ISC",
+ "description": "",
+ "dependencies": {
+ "dotenv": "^16.5.0"
+ }
+}
diff --git a/Week3/homework/mongodb/seedDatabase.js b/Week3/homework/mongodb/seedDatabase.js
index 99be6b3d8..19cfb6f08 100644
--- a/Week3/homework/mongodb/seedDatabase.js
+++ b/Week3/homework/mongodb/seedDatabase.js
@@ -1,12 +1,11 @@
-const data = require("./data.json");
-
-/**
+import data from "./data.json" with {type : 'json'};
+ /**
* This function will drop and recreate the collection of sample data in our csv file.
* By doing this we ensure that your functions are working on the same data, very similar to how you would set up a test environment.
*
* @param {MongoClient} client - The client that is connected to your database
*/
-const seedDatabase = async (client) => {
+export const seedDatabase = async (client) => {
const hasCollection = await client
.db("databaseWeek3")
.listCollections({ name: "bob_ross_episodes" })
@@ -45,7 +44,3 @@ const seedDatabase = async (client) => {
throw Error("The collection `bob_ross_episodes` does not exist!");
}
};
-
-module.exports = {
- seedDatabase,
-};
diff --git a/Week3/input-demo.js b/Week3/input-demo.js
index 65855ba40..ee5d93a16 100644
--- a/Week3/input-demo.js
+++ b/Week3/input-demo.js
@@ -11,3 +11,4 @@ prompt.get(['username', 'email'], function (err, result) {
console.log(' email: ' + result.email);
}
);
+