diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..b512c09d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/cyf_ecommerce b/cyf_ecommerce new file mode 100644 index 00000000..3ae971df --- /dev/null +++ b/cyf_ecommerce @@ -0,0 +1,72 @@ + order_id | order_reference | order_date | total +----------+-----------------+------------+------- + 1 | ORD001 | 2019-06-01 | 18 + 1 | ORD001 | 2019-06-01 | 25 + 2 | ORD002 | 2019-07-15 | 32 + 2 | ORD002 | 2019-07-15 | 10 + 3 | ORD003 | 2019-07-11 | 40 + 3 | ORD003 | 2019-07-11 | 40 +(6 rows) + + order_id | order_reference | order_date | total +----------+-----------------+------------+------- + 1 | ORD001 | 2019-06-01 | 18 + 1 | ORD001 | 2019-06-01 | 25 + 2 | ORD002 | 2019-07-15 | 32 + 2 | ORD002 | 2019-07-15 | 10 + 3 | ORD003 | 2019-07-11 | 40 + 3 | ORD003 | 2019-07-11 | 40 +(6 rows) + + order_id | order_reference | order_date | total +----------+-----------------+------------+------- + 1 | ORD001 | 2019-06-01 | 18 + 1 | ORD001 | 2019-06-01 | 25 + 2 | ORD002 | 2019-07-15 | 32 + 2 | ORD002 | 2019-07-15 | 10 + 3 | ORD003 | 2019-07-11 | 40 + 3 | ORD003 | 2019-07-11 | 40 +(6 rows) + + order_id | product_name | order_reference | order_date | total +----------+-------------------------+-----------------+------------+------- + 1 | Tee Shirt Olympic Games | ORD001 | 2019-06-01 | 18 + 1 | Super warm socks | ORD001 | 2019-06-01 | 25 + 2 | Super warm socks | ORD002 | 2019-07-15 | 32 + 2 | Le Petit Prince | ORD002 | 2019-07-15 | 10 + 3 | Coffee Cup | ORD003 | 2019-07-11 | 40 + 3 | Ball | ORD003 | 2019-07-11 | 40 +(6 rows) + + order_id | order_reference | order_date | total +----------+-----------------+------------+------- + 1 | ORD001 | 2019-06-01 | 18 + 1 | ORD001 | 2019-06-01 | 25 + 2 | ORD002 | 2019-07-15 | 32 + 2 | ORD002 | 2019-07-15 | 10 + 3 | ORD003 | 2019-07-11 | 40 + 3 | ORD003 | 2019-07-11 | 40 +(6 rows) + + List of relations + Schema | Name | Type | Owner +--------+----------------------+-------+------- + public | customers | table | admin + public | order_items | table | admin + public | orders | table | admin + public | product_availability | table | admin + public | products | table | admin + public | suppliers | table | admin +(6 rows) + + List of relations + Schema | Name | Type | Owner +--------+----------------------+-------+------- + public | customers | table | admin + public | order_items | table | admin + public | orders | table | admin + public | product_availability | table | admin + public | products | table | admin + public | suppliers | table | admin +(6 rows) + diff --git a/week-1/mandatory/2-classes-db/task.md b/week-1/mandatory/2-classes-db/task.md index 735de6ae..93b8479d 100644 --- a/week-1/mandatory/2-classes-db/task.md +++ b/week-1/mandatory/2-classes-db/task.md @@ -2,13 +2,21 @@ ## Submission -Below you will find a set of tasks for you to complete to consolidate and extend your learning from this week. You will find it beneficial to complete the reading tasks before attempting some of these. +Below you will find a set of tasks for you to complete to consolidate and extend your learning from this week. You will find it beneficial to complete the reading tasks before attempting some of these. To submit this homework write the correct commands for each question here: ```sql - - +1. select * from rooms where rate > 100; +2. select * from reservations where (checkin_date >= '2020-09-01' and checkin_date <='2020-09-30') and checkout_date - checkin_date < 3; +3. select * from customers where city like 'M%'; +4. insert into room_types values('PENTHOUSE','185.00'); +5. insert into rooms (room_no, rate, room_type) values(501,(select def_rate from room_types where room_type = 'PENTHOUSE'),'PENTHOUSE'),(502,(select def_rate from room_types where room_type = 'PENTHOUSE'),'PENTHOUSE'); +6. insert into rooms (room_no, rate, room_type) values(503,143.00,'PREMIER PLUS'); +7. select all from reservations where checkin_date >= '2020-08-01' and checkin_date <= '2020-08-31'; / select count(id) from reservations where checkin_date >= '2020-08-01' and checkin_date <= '2020-08-31'; +8. select sum(checkout_date - checkin_date) from reservations where room_no between 200 and 299; +9. select sum(total),avg(total) from invoices where total > 300; +10. select sum(case when room_no between 100 and 199 then checkout_date - checkin_date end) as first_floor, sum(case when room_no between 200 and 299 then checkout_date - checkin_date end) as second_floor, sum(case when room_no between 300 and 399 then checkout_date - checkin_date end) as third_floor, sum(case when room_no between 400 and 499 then checkout_date - checkin_date end) as fourth_floor from reservations; ``` When you have finished all of the questions - open a pull request with your answers to the `Databases-Homework` repository. @@ -18,6 +26,7 @@ When you have finished all of the questions - open a pull request with your answ If you haven't completed all the exercises from this lesson then do that first. ### Tasks + 1. Which rooms have a rate of more than 100.00? 2. List the reservations that have a checkin date this month and are for more than three nights. 3. List all customers from cities that begin with the letter 'M'. @@ -33,4 +42,4 @@ Using what you can learn about aggregate functions in the w3schools SQL classes 7. The hotel manager wishes to know how many rooms were occupied any time during the previous month - find that information. 8. Get the total number of nights that customers stayed in rooms on the second floor (rooms 201 - 299). 9. How many invoices are for more than £300.00 and what is their grand total and average amount? -10. Bonus Question: list the number of nights stay for each floor of the hotel (floor no is the hundreds part of room number, e.g. room **3**12 is on floor **3**) +10. Bonus Question: list the number of nights stay for each floor of the hotel (floor no is the hundreds part of room number, e.g. room **3**12 is on floor **3**) diff --git a/week-2/mandatory/2-ecommerce-db/ERD.png b/week-2/mandatory/2-ecommerce-db/ERD.png new file mode 100644 index 00000000..b0d7ef56 Binary files /dev/null and b/week-2/mandatory/2-ecommerce-db/ERD.png differ diff --git a/week-2/mandatory/2-ecommerce-db/task.md b/week-2/mandatory/2-ecommerce-db/task.md index c48d2286..79286713 100644 --- a/week-2/mandatory/2-ecommerce-db/task.md +++ b/week-2/mandatory/2-ecommerce-db/task.md @@ -7,8 +7,24 @@ In this homework, you are going to work with an ecommerce database. In this data Below you will find a set of tasks for you to complete to set up a database for an e-commerce app. To submit this homework write the correct commands for each question here: + ```sql +1. select * from customers where country = 'United States'; +2. select * from customers order by name; +3. select * from products where product_name like '%socks%'; +4. select a.prod_id, p.product_name,a.supp_id,a.unit_price from product_availability a join products p on p.id = a.prod_id where a.unit_price > 100; +5. select * from product_availability order by unit_price DESC limit 5; +6. select p.product_name,a.unit_price,s.supplier_name from product_availability a join products p on p.id = a.prod_id join suppliers s on a.supp_id = s.id; +7. select p.product_name,s.supplier_name from product_availability a join products p on p.id = a.prod_id join suppliers s on a.supp_id = s.id where s.country = 'United Kingdom'; +8-a. select o.id as Order_ID,o.order_reference, o.order_date, a.unit_price * i.quantity as total from order_items i join orders o on o.id=i.order_id join product_availability a on (i.product_id = a.prod_id and i.supplier_id = a.supp_id) where o.customer_id = 1; +8-b. select o.id as Order_ID,p.product_name, o.order_reference, o.order_date, a.unit_price * i.quantity as total from order_items i join orders o on o.id=i.order_id join product_availability a on (i.product_id = a.prod_id and i.supplier_id = a.supp_id) join products p on p.id = a.prod_id where o.customer_id = 1; +8-c. select o.id as Order_ID, o.order_reference, o.order_date, sum(a.unit_price * i.quantity) as total from order_items i join orders o on o.id=i.order_id join product_availability a on (i.product_id = a.prod_id and i.supplier_id = a.supp_id) where o.customer_id = 1 group by o.id; (after mentor explanation) +9. select o.id as Order_ID, p.product_name from orders o join order_items i on o.id = i.order_id join customers c on c.id = o.customer_id join products p on p.id = i.product_id where c.name = 'Hope Crosby'; +10. select p.product_name, a.unit_price, i.quantity from order_items i join orders o on o.id=i.order_id join product_availability a on (i.product_id = a.prod_id and i.supplier_id = a.supp_id) join products p on p.id = a.prod_id where o.order_reference = 'ORD006'; +11. select c.name, o.order_reference, o.order_date, p.product_name, s.supplier_name, i.quantity from order_items i join product_availability a on (i.product_id = a.prod_id and i.supplier_id = a.supp_id) join orders o on o.id = i.order_id join customers c on o.customer_id = c.id join products p on a.prod_id = p.id join suppliers s on a.supp_id = s.id; +12. select distinct c.name from order_items i join orders o on i.order_id = o.id join customers c on c.id = o.customer_id join suppliers s on i.supplier_id = s.id where s.country = 'China'; +13. select c.name, o.order_reference, o.order_date, sum(i.quantity * a.unit_price) as total from orders o join customers c on o.customer_id = c.id join order_items i on i.order_id = o.id join product_availability a on (i.product_id = a.prod_id and i.supplier_id = a.supp_id) group by c.name, o.order_reference, o.order_date order by total DESC; ``` @@ -41,10 +57,9 @@ Once you understand the database that you are going to work with, solve the foll 5. Retrieve the 5 most expensive products 6. Retrieve all the products with their corresponding suppliers. The result should only contain the columns `product_name`, `unit_price` and `supplier_name` 7. Retrieve all the products sold by suppliers based in the United Kingdom. The result should only contain the columns `product_name` and `supplier_name`. -8. Retrieve all orders, including order items, from customer ID `1`. Include order id, reference, date and total cost (calculated as quantity * unit price). +8. Retrieve all orders, including order items, from customer ID `1`. Include order id, reference, date and total cost (calculated as quantity \* unit price). 9. Retrieve all orders, including order items, from customer named `Hope Crosby` 10. Retrieve all the products in the order `ORD006`. The result should only contain the columns `product_name`, `unit_price` and `quantity`. 11. Retrieve all the products with their supplier for all orders of all customers. The result should only contain the columns `name` (from customer), `order_reference`, `order_date`, `product_name`, `supplier_name` and `quantity`. 12. Retrieve the names of all customers who bought a product from a supplier based in China. -13. List all orders giving customer name, order reference, order date and order total amount (quantity * unit price) in descending order of total. - +13. List all orders giving customer name, order reference, order date and order total amount (quantity \* unit price) in descending order of total. diff --git a/week-2/mandatory/3-api/package-lock.json b/week-2/mandatory/3-api/package-lock.json new file mode 100644 index 00000000..ac376746 --- /dev/null +++ b/week-2/mandatory/3-api/package-lock.json @@ -0,0 +1,485 @@ +{ + "name": "cyf-ecommerce-api", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "pg": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.3.3.tgz", + "integrity": "sha512-wmUyoQM/Xzmo62wgOdQAn5tl7u+IA1ZYK7qbuppi+3E+Gj4hlUxVHjInulieWrd0SfHi/ADriTb5ILJ/lsJrSg==", + "requires": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.3.0", + "pg-pool": "^3.2.1", + "pg-protocol": "^1.2.5", + "pg-types": "^2.1.0", + "pgpass": "1.x", + "semver": "4.3.2" + } + }, + "pg-connection-string": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.3.0.tgz", + "integrity": "sha512-ukMTJXLI7/hZIwTW7hGMZJ0Lj0S2XQBCJ4Shv4y1zgQ/vqVea+FLhzywvPj0ujSuofu+yA4MYHGZPTsgjBgJ+w==" + }, + "pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" + }, + "pg-pool": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.1.tgz", + "integrity": "sha512-BQDPWUeKenVrMMDN9opfns/kZo4lxmSWhIqo+cSAF7+lfi9ZclQbr9vfnlNaPr8wYF3UYjm5X0yPAhbcgqNOdA==" + }, + "pg-protocol": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.2.5.tgz", + "integrity": "sha512-1uYCckkuTfzz/FCefvavRywkowa6M5FohNMF5OjKrqo9PSR8gYc8poVmwwYQaBxhmQdBjhtP514eXy9/Us2xKg==" + }, + "pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "requires": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + } + }, + "pgpass": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.2.tgz", + "integrity": "sha1-Knu0G2BltnkH6R2hsHwYR8h3swY=", + "requires": { + "split": "^1.0.0" + } + }, + "postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" + }, + "postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" + }, + "postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==" + }, + "postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "requires": { + "xtend": "^4.0.0" + } + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz", + "integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "requires": { + "through": "2" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + } + } +} diff --git a/week-2/mandatory/3-api/package.json b/week-2/mandatory/3-api/package.json new file mode 100644 index 00000000..6ed18b79 --- /dev/null +++ b/week-2/mandatory/3-api/package.json @@ -0,0 +1,15 @@ +{ + "name": "cyf-ecommerce-api", + "version": "1.0.0", + "description": "Node App to that connects to a simple PostgreSQL database.", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Osman Elsahib", + "license": "ISC", + "dependencies": { + "express": "^4.17.1", + "pg": "^8.3.3" + } +} diff --git a/week-2/mandatory/3-api/server.js b/week-2/mandatory/3-api/server.js new file mode 100644 index 00000000..fef80cbf --- /dev/null +++ b/week-2/mandatory/3-api/server.js @@ -0,0 +1,382 @@ +const express = require("express"); +const app = express(); + +const { Pool } = require("pg"); + +const db = new Pool({ + user: "admin", // replace with you username + host: "localhost", + database: "cyf_ecommerce", + password: "password", + port: 5432, +}); +app.use(express.json()); + +let serverError = "We have a server error, please contact site admin."; + +app.get("/customers", (req, res) => { + db.query("SELECT * FROM customers", (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + throw error; + } else { + res.status(200).json(result.rows); + } + }); +}); + +app.get("/customers/:customerID", (req, res) => { + let id = parseInt(req.params.customerID); + db.query("SELECT * FROM customers where id = $1", [id], (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + throw error; + } else { + if (result.rows.length > 0) { + res.status(200).json(result.rows[0]); + } else { + res.status(404).json(`There is no customer with this ID ${id}`); + } + } + }); +}); + +app.get("/customers/by-name/:name", (req, res) => { + let name = req.params.name.toLowerCase(); + + db.query( + "SELECT * FROM customers where Lower(name) LIKE $1 || '%'", + [name], + (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + throw error; + } else { + res.status(200).json(result.rows); + } + } + ); +}); + +app.get("/suppliers", (req, res) => { + db.query("SELECT * FROM suppliers", (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + } else { + res.status(200).json(result.rows); + } + }); +}); + +app.get("/products", (req, res) => { + let name = req.query.name; + if (name) { + name.toLowerCase(); + db.query( + "SELECT p.product_name, a.unit_price , s.supplier_name from products p join product_availability a on p.id = a.prod_id join suppliers s on s.id = a.supp_id where Lower(p.product_name) like '%'||$1||'%'", + [name], + (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + } else { + res.status(200).json(result.rows); + } + } + ); + } else { + db.query( + "SELECT p.product_name, a.unit_price , s.supplier_name from products p join product_availability a on p.id = a.prod_id join suppliers s on s.id = a.supp_id", + (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + } else { + res.status(200).json(result.rows); + } + } + ); + } +}); + +app.post("/customers", (req, res) => { + let name = req.body.name, + address = req.body.address, + city = req.body.city, + country = req.body.country; + let sql = + "INSERT INTO customers (name, address, city, country) " + + "VALUES ($1, $2, $3, $4) returning id"; + let attributes = [name, address, city, country]; + + db.query(sql, attributes, (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + } else { + res + .status(201) + .json(`A new customer is created with the ID ${result.rows[0].id}`); + } + }); +}); + +app.post("/products", (req, res) => { + let product = req.body.productName; + let sql = "insert into products (product_name)" + "values ($1) returning id"; + db.query(sql, [product], (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + } else { + res.status(201).json(`Product added with the ID ${result.rows[0].id}`); + } + }); +}); + +app.post("/availability", (req, res) => { + let price = parseInt(req.body.price), + product = parseInt(req.body.product_id), + supplier = parseInt(req.body.supplier_id); + let sql_prod = "select id from products where id = $1 ", + sql_supp = " select id from suppliers where id = $1 ", + sql_insert = + "insert into product_availability (prod_id,supp_id,unit_price)" + + "values ($1,$2,$3)"; + if (price > 0) { + db.query(sql_supp, [supplier], (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + } else if (result.rows.length > 0) { + db.query(sql_prod, [product], (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + } else if (result.rows.length > 0) { + db.query(sql_insert, [product, supplier, price], (error) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + } else { + res.status(201).json("Product price added."); + } + }); + } else { + res.status(404).json("Can't find a product with this ID"); + } + }); + } else { + res.status(404).json("Can't find a supplier with this ID"); + } + }); + } else { + res.status(400).json("The price needs to be larger than 0"); + } +}); + +app.post("/customers/:customerId/orders", (req, res) => { + let id = parseInt(req.params.customerId), + sql_orders = + "insert into orders (order_date, order_reference, customer_id)" + + "values (current_date,$1,$2) returning order_reference", + sql_customers = "select * from customers where id = $1", + sql_orderNumber = + "select order_reference from orders order by id DESC limit 1"; + + db.query(sql_customers, [id], (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + } else if (result.rows.length > 0) { + db.query(sql_orderNumber, (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + } else { + let lastOrder = result.rows[0].order_reference; + //orderRef functions are borrowed from https://electrictoolbox.com/pad-number-zeroes-javascript/ and SOF site + let orderRef = lastOrder.replace(/(\d+)+/g, function (match, number) { + function pad(number, length) { + let str = "" + number; + while (str.length < length) { + str = "0" + str; + } + return str; + } + return pad(parseInt(number) + 1, 3); + }); + db.query(sql_orders, [orderRef, id], (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + } else { + res + .status(201) + .json( + `Order placed with reference ${result.rows[0].order_reference}` + ); + } + }); + } + }); + } + }); +}); + +app.put("/customers/:customerId", (req, res) => { + const id = parseInt(req.params.customerId), + name = req.body.name, + address = req.body.address, + city = req.body.city, + country = req.body.country; + db.query("SELECT * FROM customers where id = $1", [id], (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + throw error; + } else { + if (result.rows.length > 0) { + db.query( + "update customers set name = $1, address = $2, city = $3,country = $4 where id = $5", + [name, address, city, country, id], + (error) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + throw error; + } else { + res.status(200).json(`Customer with ID ${id} data updated.`); + } + } + ); + } else { + res.status(404).json(`There is no customer with this ID ${id}`); + } + } + }); +}); + +app.delete("/orders/:orderId", (req, res) => { + let id = req.params.orderId; + db.query("SELECT * FROM orders where id = $1", [id], (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + throw error; + } else { + if (result.rows.length > 0) { + db.query( + "delete from order_items where order_id = $1", + [id], + (error) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + throw error; + } else { + db.query("delete from orders where id = $1", [id], (error) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + throw error; + } else { + res + .status(200) + .json(`Order ${id} and all it's Items are deleted`); + } + }); + } + } + ); + } else { + res.status(404).json(`There is no order with this ID ${id}`); + } + } + }); +}); +app.delete("/customers/:customerId", (req, res) => { + let id = parseInt(req.params.customerId); + db.query("SELECT * FROM customers where id = $1", [id], (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + throw error; + } else { + if (result.rows.length > 0) { + db.query( + "select * from orders where customer_id = $1", + [id], + (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + throw error; + } else if (result.rows.length > 0) { + res + .status(200) + .json(`Can't delete customer with id ${id}. They have orders`); + } else { + db.query("delete from customers where id = $1", [id], (error) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + throw error; + } else { + res.status(200).json(`Customer with ID ${id} is deleted`); + } + }); + } + } + ); + } else { + res.status(404).json(`There is no customer with this ID ${id}`); + } + } + }); +}); + +app.get("/customers/:customerId/orders", (req, res) => { + let id = parseInt(req.params.customerId); + db.query("SELECT * FROM customers where id = $1", [id], (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + throw error; + } else { + if (result.rows.length > 0) { + db.query( + "select o.order_reference as OrderRef, o.order_date as OrderDate," + + "p.product_name ProductNames, a.unit_price as UnitPrice,s.supplier_name as Supplier," + + "i.quantity as Quantity from order_items i join orders o on o.id=i.order_id " + + "join product_availability a on (i.product_id = a.prod_id and i.supplier_id = a.supp_id)" + + "join products p on p.id = a.prod_id join suppliers s on s.id = a.supp_id where o.customer_id = $1", + [id], + (error, result) => { + if (error) { + console.log("pg error code:", error.code); + res.status(500).json(serverError); + throw error; + } else { + res.status(200).json(result.rows); + } + } + ); + } else { + res + .status(404) + .json( + `There is no customer with this ID ${id} or they haven't ordered yet.` + ); + } + } + }); +}); + +app.listen(3000, function () { + console.log("Server is listening on port 3000."); +});