Skip to content

Commit

Permalink
Merge HAN-54: Creacion dummy handoff gateway #1
Browse files Browse the repository at this point in the history
  • Loading branch information
violetaperezandrade authored Apr 7, 2024
2 parents 272c232 + 7b37c34 commit 0ffd21b
Show file tree
Hide file tree
Showing 12 changed files with 378 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .env.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
LOGGING_LEVEL=
USERS_HOST=
MEASUREMENTS_HOST=
PLANTS_HOST=
23 changes: 23 additions & 0 deletions .github/workflows/linters.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Flake8 and Black

on: [push]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 black
- name: Checking code style with Flake8
run: |
flake8 .
- name: Formatting code with Black
run: |
black .
10 changes: 10 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM python:3.11-slim-buster
WORKDIR /src

COPY . /src

RUN pip install -r requirements.txt

EXPOSE 5000

CMD ["gunicorn", "wsgi:app", "-b", "0.0.0.0:5000"]
26 changes: 26 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
default: docker-compose-up

all:

create-network:
@if ! docker network inspect common_network >/dev/null 2>&1; then \
docker network create common_network; \
fi
.PHONY: create-network

docker-image: create-network
docker build -f ./Dockerfile -t "gateway:latest" .
.PHONY: docker-image

docker-compose-up: docker-image
docker-compose -f docker-compose.yaml up -d --build
.PHONY: docker-compose-up

docker-compose-down:
docker-compose -f docker-compose.yaml stop -t 20
docker-compose -f docker-compose.yaml down --remove-orphans
.PHONY: docker-compose-down

docker-compose-logs:
docker-compose -f docker-compose.yaml logs -f
.PHONY: docker-compose-logs
44 changes: 44 additions & 0 deletions app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import os
import logging
from flask import Flask
from flask_restful import Api
from werkzeug.routing import BaseConverter
from src.resource import Gateway


def initialize_log(logging_level):
"""
Python custom logging initialization
Current timestamp is added to be able to identify in docker
compose logs the date when the log has arrived
"""
logging.basicConfig(
format='%(asctime)s %(levelname)-8s %(message)s',
level=logging_level,
datefmt='%Y-%m-%d %H:%M:%S',
)


logging_level = os.getenv("LOGGING_LEVEL", "INFO")
initialize_log(logging_level)
app = Flask(__name__)


class RegexConverter(BaseConverter):
def __init__(self, url_map, *items):
super(RegexConverter, self).__init__(url_map)
self.regex = items[0]


@app.after_request
def _build_cors_post_response(response):
response.headers.add("Access-Control-Allow-Origin", "*")
response.headers.add("Access-Control-Allow-Headers", "*")
response.headers.add("Access-Control-Allow-Methods", "*")
return response


app.url_map.converters['regex'] = RegexConverter
api = Api(app)
api.add_resource(Gateway, '/<path:url>')
18 changes: 18 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: '3.9'

services:

gateway:
build:
context: ./
container_name: gateway
env_file:
- .env
ports:
- "5000:5000"
networks:
- common_network

networks:
common_network:
external: true
6 changes: 6 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
requests
Werkzeug
Flask
flask_restful
gunicorn
PyJWT
57 changes: 57 additions & 0 deletions src/apps/measurements.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import os
import logging

import requests
from flask import make_response


def get_query_params(queryParam) -> str:
if not queryParam:
return ""
return f"?{str(queryParam, 'utf-8')}"


class Measurements:
def __init__(self):
self.host = os.getenv("MEASUREMENTS_HOST")

def getResponseJson(self, response):
if response.status_code == 503 or not response.text:
return {"message": "measurements service is currently unavailable,"
" please try again later", "status": 503}
return response.json()

def get(self, url, body, headers, query_params):
url = f"{self.host}{url}{get_query_params(query_params)}"
logging.info(f"MEASUREMENTS | GET | {url}")
response = requests.get(url, json=body, headers=headers)
return make_response(self.getResponseJson(response),
response.status_code)

def post(self, url, body, headers, query_params):
response = requests.post(f"{self.host}{url}"
f"{get_query_params(query_params)}",
json=body,
headers=headers)
logging.info(f"MEASUREMENTS | POST | {url}")
logging.debug(f"BODY: {body}")
return make_response(self.getResponseJson(response),
response.status_code)

def patch(self, url, body, headers, query_params):
response = requests.patch(f"{self.host}{url}"
f"{get_query_params(query_params)}",
json=body,
headers=headers)
logging.info(f"MEASUREMENTS | PATCH | {url}")
logging.debug(f"BODY: {body}")
return make_response(self.getResponseJson(response),
response.status_code)

def delete(self, url, body, headers, query_params):
response = requests.delete(f"{self.host}{url}"
f"{get_query_params(query_params)}",
headers=headers)
logging.info(f"MEASUREMENTS | DELETE | {url}")
return make_response(self.getResponseJson(response),
response.status_code)
57 changes: 57 additions & 0 deletions src/apps/plants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import os
import logging

import requests
from flask import make_response


def get_query_params(queryParam) -> str:
if not queryParam:
return ""
return f"?{str(queryParam, 'utf-8')}"


class Plants:
def __init__(self):
self.host = os.getenv("PLANTS_HOST")

def getResponseJson(self, response):
if response.status_code == 503 or not response.text:
return {"message": "plants service is currently unavailable,"
"please try again later", "status": 503}
return response.json()

def get(self, url, body, headers, query_params):
url = f"{self.host}{url}{get_query_params(query_params)}"
response = requests.get(url, json=body, headers=headers)
logging.info(f"PLANTS | GET | {url}")
return make_response(self.getResponseJson(response),
response.status_code)

def post(self, url, body, headers, query_params):
response = requests.post(f"{self.host}{url}"
f"{get_query_params(query_params)}",
json=body,
headers=headers)
logging.info(f"PLANTS | POST | {url}")
logging.debug(f"BODY: {body}")
return make_response(self.getResponseJson(response),
response.status_code)

def patch(self, url, body, headers, query_params):
response = requests.patch(f"{self.host}{url}"
f"{get_query_params(query_params)}",
json=body,
headers=headers)
logging.info(f"PLANTS | PATCH | {url}")
logging.debug(f"BODY: {body}")
return make_response(self.getResponseJson(response),
response.status_code)

def delete(self, url, body, headers, query_params):
response = requests.delete(f"{self.host}{url}"
f"{get_query_params(query_params)}",
headers=headers)
logging.info(f"PLANTS | DELETE | {url}")
return make_response(self.getResponseJson(response),
response.status_code)
57 changes: 57 additions & 0 deletions src/apps/users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import os
import logging

import requests
from flask import make_response


def get_query_params(queryParam) -> str:
if not queryParam:
return ""
return f"?{str(queryParam, 'utf-8')}"


class Users:
def __init__(self):
self.host = os.getenv("USERS_HOST")

def getResponseJson(self, response):
if response.status_code == 503 or not response.text:
return {"message": "users service is currently unavailable, please"
"try again later", "status": 503}
return response.json()

def get(self, url, body, headers, query_params):
url = f"{self.host}{url}{get_query_params(query_params)}"
response = requests.get(url, json=body, headers=headers)
logging.info(f"USERS | GET | {url}")
return make_response(self.getResponseJson(response),
response.status_code)

def post(self, url, body, headers, query_params):
response = requests.post(f"{self.host}{url}"
f"{get_query_params(query_params)}",
json=body,
headers=headers)
logging.info(f"USERS | POST | {url}")
logging.debug(f"BODY: {body}")
return make_response(self.getResponseJson(response),
response.status_code)

def patch(self, url, body, headers, query_params):
response = requests.patch(f"{self.host}{url}"
f"{get_query_params(query_params)}",
json=body,
headers=headers)
logging.info(f"USERS | PATCH | {url}")
logging.debug(f"BODY: {body}")
return make_response(self.getResponseJson(response),
response.status_code)

def delete(self, url, body, headers, query_params):
response = requests.delete(f"{self.host}{url}"
f"{get_query_params(query_params)}",
headers=headers)
logging.info(f"USERS | DELETE | {url}")
return make_response(self.getResponseJson(response),
response.status_code)
72 changes: 72 additions & 0 deletions src/resource.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import logging
from flask import request, make_response # type: ignore
from flask_restful import Resource

from src.apps.users import Users
from src.apps.measurements import Measurements
from src.apps.plants import Plants


def getExtraData():
if request.is_json:
body = request.json
else:
body = {}
headers = dict(request.headers)
if 'Host' in headers:
headers.pop('Host') # Invalid header
query_params = request.query_string
return body, headers, query_params


SERVICE_MAP = {
"users": Users(),
"measurements": Measurements(),
"device-plant": Measurements(),
"plants": Plants(),
"plant-type": Plants(),
"logs": Plants(),
"login": Users()
}


def getCorrectEndpoint(url: str):
values = url.split("/")
return SERVICE_MAP.get(values[0]) if len(values) else None


class Gateway(Resource):
def get(self, url):
resource = getCorrectEndpoint(url)
if not resource:
logging.error(f"Resource not found for url {url}")
return make_response({"message": "not found"}, 404)
return resource.get(url, *getExtraData())

def post(self, url):
resource = getCorrectEndpoint(url)
if not resource:
logging.error(f"Resource not found for url {url}")
return make_response({"message": "not found"}, 404)
return resource.post(url, *getExtraData())

def patch(self, url):
resource = getCorrectEndpoint(url)
if not resource:
logging.error(f"Resource not found for url {url}")
return make_response({"message": "not found"}, 404)
return resource.patch(url, *getExtraData())

def delete(self, url):
resource = getCorrectEndpoint(url)
if not resource:
logging.error(f"Resource not found for url {url}")
return make_response({"message": "not found"}, 404)
return resource.delete(url, *getExtraData())

def put(self, url):
resource = getCorrectEndpoint(url)
if not resource:
logging.error(f"Resource not found for url {url}")
return make_response({"message": "not found"}, 404)
return resource.put(url, *getExtraData())
4 changes: 4 additions & 0 deletions wsgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from app import app

if __name__ == "__main__":
app.run()

0 comments on commit 0ffd21b

Please sign in to comment.