-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
-separated services into Books/Customers service -added BFFs for each service -added deployment assets for everything
- Loading branch information
Showing
17 changed files
with
879 additions
and
1 deletion.
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 |
---|---|---|
@@ -1,3 +1,5 @@ | ||
.vagrant/* | ||
*/node_modules/* | ||
A1.pdf | ||
TestScript.json | ||
TestScript.json | ||
*.zip |
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,8 @@ | ||
FROM python:3.6-slim-buster AS PYTHON_BUILD | ||
MAINTAINER Max Kornyev | ||
COPY . /app/ | ||
COPY requirements.txt /app | ||
WORKDIR /app | ||
RUN pip install -r requirements.txt | ||
EXPOSE 80 | ||
ENTRYPOINT ["python", "app.py"] |
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,120 @@ | ||
|
||
# IMPORTS | ||
|
||
from flask import Flask, request, json | ||
import requests | ||
app = Flask(__name__) | ||
|
||
|
||
# CONSTANTS | ||
|
||
SERVER_HOST = '0.0.0.0' | ||
SERVER_PORT = 80 | ||
|
||
BOOK_SERVICE_HOST = 'http://localhost:3001/books' | ||
VALID_AUTH_TOKEN = 'Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJLUkFNRVJTIiwibHVscyI6IktSQU1FUlMiLCJjcmVhdGVkIjoxNjE3MjMwNzUxM zIwLCJyb2xlcyI6W10sImlzcyI6InRjdS5nb3YuYnIiLCJlaW8iOiIxMC4xMDAuMTkyLjUxIiwibnVzIjoiSk9BTyBBTkR PTklPUyBTUFlSSURBS0lTIiwibG90IjoiU2VnZWMiLCJhdWQiOiJPUklHRU1fUkVRVUVTVF9CUk9XU0VSIiwidHVzIjoiV ENVIiwiY3VscyI6MjI1LCJjb2QiOjIyNSwiZXhwIjoxNjE3MjczOTUxMzIwLCJudWxzIjoiSk9BTyBBTkRPTklPUyBTUFl SSURBS0lTIn0.qtJ0Sf2Agqd_JmxGKfqiLw8SldOiP9e21OT4pKC8BqdXrJ0plqOWHf0hHbwQWp-foEBZzAUWX0J-QHtLy Q7SRw' | ||
|
||
|
||
# VALIDATIONS | ||
|
||
def responseIfInvalidRequest(req): | ||
agentHeader = req.headers.get('User-Agent', None) | ||
if agentHeader == None: | ||
response = app.response_class( | ||
response=json.dumps({'message': 'User-Agent header required.'}), | ||
status=400, | ||
mimetype='application/json' | ||
) | ||
return response | ||
|
||
auth = req.headers.get('Authorization', None) | ||
if auth == None or auth != VALID_AUTH_TOKEN: | ||
response = app.response_class( | ||
response=json.dumps({'message': 'Valid Authorization Token required.'}), | ||
status=401, | ||
mimetype='application/json' | ||
) | ||
return response | ||
|
||
return None | ||
|
||
def isMobileAgent(req): | ||
agentHeader = req.headers.get('User-Agent', None) | ||
|
||
if 'Mobile' in agentHeader: | ||
return True | ||
|
||
return False | ||
|
||
|
||
# ROUTES | ||
|
||
@app.route('/books', methods=['POST']) | ||
def addBook(): | ||
response = responseIfInvalidRequest(request) | ||
if response: | ||
return response | ||
|
||
serviceRes = requests.post(BOOK_SERVICE_HOST, data=request.get_json()) | ||
|
||
return getResponseFor(serviceRes) | ||
|
||
|
||
@app.route('/books/<isbn>', methods=['PUT']) | ||
def putBook(isbn=None): | ||
response = responseIfInvalidRequest(request) | ||
if response: | ||
return response | ||
|
||
path = '/{}'.format(isbn) | ||
serviceRes = requests.put(BOOK_SERVICE_HOST+path, data=request.get_json()) | ||
|
||
return getResponseFor(serviceRes) | ||
|
||
|
||
@app.route('/books/isbn/<isbn>', methods=['GET']) | ||
def getBook(isbn=None): | ||
response = responseIfInvalidRequest(request) | ||
if response: | ||
return response | ||
|
||
path = '/isbn/{}'.format(isbn) | ||
serviceRes = requests.get(BOOK_SERVICE_HOST+path) | ||
|
||
if isMobileAgent(request): | ||
returnedCode = serviceRes.status_code | ||
returnedBody = serviceRes.json() | ||
|
||
if 'genre' in returnedBody and 'non-fiction' == returnedBody['genre']: | ||
returnedBody['genre'] = 3 | ||
|
||
response = app.response_class( | ||
response=json.dumps(returnedBody), | ||
status=returnedCode, | ||
mimetype='application/json' | ||
) | ||
return response | ||
|
||
return getResponseFor(serviceRes) | ||
|
||
|
||
|
||
# HELPERS | ||
|
||
def getResponseFor(serviceRes): | ||
returnedCode = serviceRes.status_code | ||
returnedBody = serviceRes.json() | ||
|
||
response = app.response_class( | ||
response=json.dumps(returnedBody), | ||
status=returnedCode, | ||
mimetype='application/json' | ||
) | ||
|
||
return response | ||
|
||
|
||
# RUN APP | ||
|
||
if __name__ == '__main__': | ||
app.run(host=SERVER_HOST, port=SERVER_PORT) |
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,11 @@ | ||
certifi==2020.12.5 | ||
chardet==4.0.0 | ||
click==7.1.2 | ||
Flask==1.1.2 | ||
idna==2.10 | ||
itsdangerous==1.1.0 | ||
Jinja2==2.11.3 | ||
MarkupSafe==1.1.1 | ||
requests==2.25.1 | ||
urllib3==1.26.4 | ||
Werkzeug==1.0.1 |
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,7 @@ | ||
FROM node:14-alpine AS NODE_BUILD | ||
MAINTAINER Max Kornyev | ||
COPY . /app/ | ||
WORKDIR /app | ||
RUN npm install | ||
EXPOSE 3000 | ||
ENTRYPOINT ["node", "index.js"] |
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 @@ | ||
|
||
//################ CONNECTION SETUP ################ | ||
|
||
var MYSQL = require('mysql') | ||
var CONNECTION | ||
|
||
|
||
//################ QUERY TEMPLATES ################ | ||
|
||
var INSERT_BOOK_SQL = 'INSERT INTO books (ISBN, title, Author, description, genre, price, quantity) VALUES (?, ?, ?, ?, ?, ?, ?);' | ||
var UPDATE_BOOK_SQL = 'UPDATE books SET ISBN = ?, title = ?, Author = ?, description = ?, genre = ?, price = ?, quantity = ? WHERE ISBN = ?;' | ||
var GET_BOOK_SQL = 'SELECT * FROM books WHERE ISBN = ? LIMIT 1;' | ||
|
||
|
||
//################ QUERY HELPERS ################ | ||
|
||
function testConnection() { | ||
CONNECTION.connect(function(err) { | ||
if (err) { | ||
console.error('Database connection failed: ' + err.stack) | ||
CONNECTION.end() | ||
} else { | ||
console.log('Connection success') | ||
CONNECTION.end() | ||
} | ||
}) | ||
} | ||
|
||
function setRDSConnection(){ | ||
CONNECTION = MYSQL.createConnection({ | ||
host : '', | ||
user : '', | ||
password : '', | ||
port : '', | ||
database : '', | ||
}) | ||
|
||
CONNECTION.on('error', function(err) { | ||
if(err.code === 'PROTOCOL_CONNECTION_LOST') { | ||
console.log('Connection Disconnect... Retrying in 2 seconds', err); | ||
setTimeout(setRDSConnection, 2000); | ||
} else { | ||
throw err; | ||
} | ||
}); | ||
} | ||
|
||
|
||
//################ BOOK QUERIES ################ | ||
|
||
function createBook(ISBN, title, Author, description, genre, price, quantity, exists, success){ | ||
CONNECTION.query(INSERT_BOOK_SQL, [ISBN, title, Author, description, genre, price, quantity], function (err, result) { | ||
if (err) { exists() } | ||
else { success() } | ||
}) | ||
} | ||
|
||
function getBook(ISBN, notFound, success){ | ||
CONNECTION.query(GET_BOOK_SQL, [ISBN], function (err, result) { | ||
if (err || result.length == 0) { notFound() } | ||
else { success(result) } | ||
}) | ||
} | ||
|
||
function updateBook(oldISBN, ISBN, title, Author, description, genre, price, quantity, notFound, success){ | ||
CONNECTION.query(UPDATE_BOOK_SQL, [ISBN, title, Author, description, genre, price, quantity, oldISBN], function (err, result) { | ||
if (err || result.affectedRows == 0) { notFound() } | ||
else { success() } | ||
}) | ||
} | ||
|
||
|
||
//################ MODULE EXPORT ################ | ||
|
||
module.exports = { testConnection, setRDSConnection, createBook, updateBook, getBook } |
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,113 @@ | ||
//################ IMPORTS ################ | ||
|
||
const express = require('express') | ||
const bodyParser = require('body-parser') | ||
const port = 3000 | ||
|
||
const conn = require('./db-connector') | ||
const app = express() | ||
|
||
app.use(bodyParser.urlencoded({ extended: true })) | ||
app.use(bodyParser.json()) | ||
app.use(bodyParser.raw()) | ||
|
||
|
||
//################ CONSTANTS ################ | ||
|
||
var PRICE_REGEX = /^\d+(\.(\d{2}|0))?$/ // 10 | 10.0 | 10.01 are all valid | ||
|
||
|
||
//################ BOOK ROUTES ################ | ||
|
||
// Add Book: | ||
app.post('/books', (req, res) => { | ||
if(!validateBook(req, res)) return | ||
|
||
conn.createBook(req.body.ISBN, req.body.title, req.body.Author, req.body.description, req.body.genre, req.body.price, req.body.quantity, () => { | ||
// If Already Exists | ||
res.statusCode = 422 | ||
res.json({ message: 'This ISBN already exists in the system.' }) | ||
}, () => { | ||
// Success | ||
res.statusCode = 201 | ||
res.location(`${req.headers.host}/books/${req.body.ISBN}`) | ||
res.json(req.body) | ||
}) | ||
}) | ||
|
||
// Update Book: | ||
// !!! VALIDATE ADMIN | ||
app.put('/books/:ISBN', (req, res) => { | ||
if(!validateBook(req, res)) return | ||
var oldISBN = req.params.ISBN | ||
|
||
if(oldISBN == req.body.ISBN){ | ||
callToUpdate() | ||
} else { | ||
conn.getBook(req.body.ISBN, () => { | ||
// If Book not found (newISBN) | ||
callToUpdate() | ||
}, () => { | ||
res.statusCode = 422 | ||
res.json({ message: 'The new ISBN already exists in the system.' }) // The logic is inverted — checks whether the NEW isbn exists BEFORE checking the OLD one | ||
}) | ||
} | ||
|
||
function callToUpdate(){ | ||
conn.updateBook(oldISBN, req.body.ISBN, req.body.title, req.body.Author, req.body.description, req.body.genre, req.body.price, req.body.quantity, () => { | ||
// If Book not found (oldISBN) | ||
res.statusCode = 404 | ||
res.json({ message: 'No ISBN Found.' }) | ||
}, () => { | ||
res.statusCode = 200 | ||
res.json(req.body) | ||
}) | ||
} | ||
}) | ||
|
||
// Retrieve Book: | ||
app.get('/books/isbn/:ISBN', (req, res) => { | ||
var ISBN = req.params.ISBN | ||
|
||
conn.getBook(ISBN, () => { | ||
// If Book not found | ||
res.statusCode = 404 | ||
res.json({ message: 'No ISBN Found.' }) | ||
}, (book) => { | ||
res.statusCode = 200 | ||
res.json( book[0] ) | ||
}) | ||
}) | ||
|
||
|
||
//################ VIEW HELPERS ################ | ||
|
||
function validateBook(req, res) { | ||
|
||
if( | ||
!req.body.ISBN || | ||
!req.body.title || | ||
!req.body.Author || | ||
!req.body.description || | ||
!req.body.genre || | ||
!req.body.price || | ||
!req.body.quantity || | ||
!PRICE_REGEX.test(req.body.price) | ||
){ | ||
res.statusCode = 400 | ||
res.json({ message: 'Malformed input.' }) | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
|
||
//################ =+= ################ | ||
|
||
app.listen(port, () => { | ||
conn.setRDSConnection() | ||
console.log(`Example app listening at http://localhost:${port}`) | ||
}) | ||
|
||
|
||
|
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,15 @@ | ||
{ | ||
"name": "book_service", | ||
"version": "1.0.0", | ||
"description": "An application for 17647", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"author": "Maksym Kornyev", | ||
"license": "ISC", | ||
"dependencies": { | ||
"express": "^4.17.1", | ||
"mysql": "^2.18.1" | ||
} | ||
} |
Oops, something went wrong.