Skip to content

Commit

Permalink
Merge pull request #16 from PinoutLTD/v.0.1-beta
Browse files Browse the repository at this point in the history
new structure of the project. new description
  • Loading branch information
tubleronchik authored Jun 24, 2024
2 parents efdaad2 + e083cb8 commit 1239b20
Show file tree
Hide file tree
Showing 32 changed files with 835 additions and 679 deletions.
File renamed without changes.
File renamed without changes.
104 changes: 104 additions & 0 deletions helpers/odoo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from dotenv import load_dotenv
import os
import xmlrpc.client
import typing as tp
from helpers.logger import Logger

load_dotenv()

ODOO_URL = os.getenv("ODOO_URL")
ODOO_DB = os.getenv("ODOO_DB")
ODOO_USER = os.getenv("ODOO_USER")
ODOO_PASSWORD = os.getenv("ODOO_PASSWORD")


class OdooHelper:
def __init__(self, name_of_the_user: str) -> None:
self._logger = Logger(f"odoo-helper-{name_of_the_user}")
self._connection, self._uid = self._connect_to_db()

def _connect_to_db(self):
"""Connect to Odoo db
:return: Proxy to the object endpoint to call methods of the odoo models.
"""
try:
common = xmlrpc.client.ServerProxy("{}/xmlrpc/2/common".format(ODOO_URL), allow_none=1)
uid = common.authenticate(ODOO_DB, ODOO_USER, ODOO_PASSWORD, {})
if uid == 0:
raise Exception("Credentials are wrong for remote system access")
else:
self._logger.debug("Connection Stablished Successfully")
connection = xmlrpc.client.ServerProxy("{}/xmlrpc/2/object".format(ODOO_URL))
return connection, uid
except Exception as e:
self._logger.error(f"Couldn't connect to the db: {e}")

def create(self, model: str, data: dict) -> tp.Optional[int]:
"""Method to create a new record in any Odoo table
:param model: Name of the model in Odoo
:param data: Data to create record with
:return: Id of the new record
"""
try:
record_id = self._connection.execute_kw(
ODOO_DB,
self._uid,
ODOO_PASSWORD,
model,
"create",
[data],
)
return record_id
except Exception as e:
self._logger.error(f"Couldn't create a new record in {model}: {e}")
return None

def update(self, model: str, record_id: int, data: dict) -> bool:
"""Method to update the exhisting record. with the new data
:param model: Name of the model in Odoo
:param record_id: Id of the record to be updated.
:param data: Data to write
:return: True if updated successfuly, False otherwise
"""
return self._connection.execute_kw(
ODOO_DB,
self._uid,
ODOO_PASSWORD,
model,
"write",
[[record_id], data],
)

def search(self, model: str, search_domains: list = []) -> list:
"""Looking for a record in the model with the specified domain.
:param model: Name of the model in Odoo
:param search_domains: Optional: A list of tuples that define the search criteria.
Retrievs all records of the model if is empty.
:return: List of record ids. If there are no records matching the domain, returns an empty list.
"""
ids = self._connection.execute_kw(ODOO_DB, self._uid, ODOO_PASSWORD, model, "search", [search_domains])
return ids

def read(self, model: str, record_ids: list, fields: list = []) -> list:
"""Method to fetch details of the records corresponding to the ids.
:param model: Name of the model in Odoo
:param record_ids: Ids of the records to fetch details for
:param fields: Optional: Read only the fields. If emtpy, returns all fields
:return: List of the records
"""

data = self._connection.execute_kw(
ODOO_DB,
self._uid,
ODOO_PASSWORD,
model,
"read",
record_ids,
{"fields": fields},
)
return data
26 changes: 5 additions & 21 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,10 @@
from flask import Flask
import threading
import os
from dotenv import load_dotenv
from src.odoo import OdooProxy
from src.websocket import WSClient
from src.http_server import OdooFlaskView, BaseView

load_dotenv()

FLASK_PORT = os.getenv("FLASK_PORT")

from registar.registar import Registar
from rrs_operator.rrs_operator import Operator

def main() -> None:
odoo = OdooProxy()
app = Flask(__name__)
ws = WSClient(odoo)
BaseView.initialize(odoo, ws)
OdooFlaskView.register(app, route_base="/")
flask_thread = threading.Thread(target=lambda: app.run(host="127.0.0.1", port=FLASK_PORT))
flask_thread.start()
ws.run()
os._exit(0)
operator = Operator()
add_user_callback = operator.get_robonomics_add_user_callback()
registar = Registar(add_user_callback)


if __name__ == "__main__":
Expand Down
File renamed without changes.
22 changes: 22 additions & 0 deletions registar/registar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from registar.src.odoo import Odoo
from registar.src.http_server import OdooFlaskView, BaseView
from registar.src.websocket import WSClient
from flask import Flask
import threading
import os
from dotenv import load_dotenv

load_dotenv()
FLASK_PORT = os.getenv("FLASK_PORT")

class Registar:
def __init__(self, add_user_callback) -> None:
self.odoo = Odoo()
self.app = Flask(__name__)
self.ws = WSClient(self.odoo)
BaseView.initialize(add_user_callback)
OdooFlaskView.register(self.app, route_base="/")
flask_thread = threading.Thread(target=lambda: self.app.run(host="127.0.0.1", port=FLASK_PORT))
flask_thread.start()
self.ws.run()
os._exit(0)
Empty file added registar/src/__init__.py
Empty file.
43 changes: 43 additions & 0 deletions registar/src/http_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from flask import request
from flask_classful import FlaskView, route
from dotenv import load_dotenv
import os

from helpers.logger import Logger

load_dotenv()

PINATA_API_KEY = os.getenv("PINATA_API_KEY")
PINATA_API_SECRET = os.getenv("PINATA_API_SECRET")
STATUS_PAID_ID = os.getenv("ODOO_RRS_STATUS_PAID_ID")
STATUS_NOTPAID_ID = os.getenv("ODOO_RRS_STATUS_NOTPAID_ID")
ADMIN_SEED = os.getenv("ADMIN_SEED")
DONE_SATGE_ID = os.getenv("ODOO_HELPDESK_DONE_STAGE_ID")


class BaseView(FlaskView):
odoo = None
ws = None
_logger = None

@classmethod
def initialize(cls, add_user_callback):
cls.set_logger()
cls.add_user_callback = add_user_callback

@classmethod
def set_logger(cls):
cls._logger = Logger("flask")


class OdooFlaskView(BaseView):
def index(self):
return "<h1>Welcome from Flask</h1>"

@route("/rrs/new-user", methods=["POST"])
def new_user_handler(self):
request_data = request.get_json()
self._logger.debug(f"Data from new-user request: {request_data}")
address = request_data["address"]
self.add_user_callback(address)
return "ok"
84 changes: 84 additions & 0 deletions registar/src/odoo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from helpers.logger import Logger
from helpers.odoo import OdooHelper
import typing as tp
from tenacity import *

class Odoo:
def __init__(self) -> None:
self.helper = OdooHelper("registar")
self._logger = Logger("odoo-registar")

@retry(wait=wait_fixed(5))
def create_rrs_user(self, email: str, sender_address: str) -> tp.Optional[int]:
"""Creates user in Robonomics Report Service module and returns its id.
:param email: Customer's email address
:param sender_address: Customer's address in Robonomics parachain
:return: User id
"""
try:
user_id = self.helper.create(
"rrs.register",
{
"address": sender_address,
"customer_email": email,
},
)
return user_id
except Exception as e:
self._logger.error(f"Couldn't create user: {e}")
raise Exception("Failed to create rrs user")

@retry(wait=wait_fixed(5))
def check_if_rrs_user_exists(self, sender_address: str) -> tp.Union[int, bool]:
"""Looking for a rrs user id by the controller address.
:param sender_address: Customer's address in Robonomics parachain.
:return: The user id or false.
"""
id = self.helper.search("rrs.register", [("address", "=", sender_address)])
self._logger.debug(f"Find RRS user with id: {id}")
if id:
return id[0]
return False

@retry(wait=wait_fixed(5))
def update_rrs_user_with_pinata_creds(self, user_id: int, pinata_key: str, pinata_api_secret: str) -> bool:
"""Update the customer profile with pinata credentials in RRS module.
:param customer_id: User id
:param pinata_key: Pinata API key
:param pinata_api_secret: Pinata API secret key
:return: bool
"""
try:
return self.helper.update(
"rrs.register",
user_id,
{
"pinata_key": pinata_key,
"pinata_secret": pinata_api_secret,
},
)
except Exception as e:
self._logger.error(f"Couldn't update user {user_id} with pinata creds {e}")
raise Exception("Failed to update the user")

@retry(wait=wait_fixed(5))
def retrieve_pinata_creds(self, sender_address: str, rrs_user_id: int) -> tuple:
"""Retrieve pinata creds.
:param sender_address: Customer's address in Robonomics parachain
:return: The Pinata creds or None.
"""
try:
rrs_user_data = self.helper.read("rrs.register", [rrs_user_id], ["pinata_key", "pinata_secret"])
if rrs_user_data:
pinata_key = rrs_user_data[0]["pinata_key"]
pinata_secret = rrs_user_data[0]["pinata_secret"]
self._logger.debug(f"Found pinata creds for address: {sender_address}, pinata key: {pinata_key}")
return pinata_key, pinata_secret
else:
self._logger.error(f"Couldn't find pinata creds for {sender_address}")
except Exception as e:
self._logger.error(f"Couldn't get pinata creds for user: {sender_address}")
raise Exception(f"Couldn't retrieve pinata creds for {sender_address}")
84 changes: 84 additions & 0 deletions registar/src/websocket.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import websocket
import os
from dotenv import load_dotenv
import json
import rel
from helpers.logger import Logger
from utils.decryption import decrypt_message
from registar.utils.messages import message_with_pinata_creds, message_for_subscribing
from registar.utils.robonomics import add_device_to_subscription
from registar.utils.pinata import generate_pinata_keys

load_dotenv()

LIBP2P_WS_SERVER = os.getenv("LIBP2P_WS_SERVER")
ADMIN_SEED = os.getenv("ADMIN_SEED")
PINATA_API_KEY = os.getenv("PINATA_API_KEY")
PINATA_API_SECRET = os.getenv("PINATA_API_SECRET")


class WSClient:
def __init__(self, odoo) -> None:
self.odoo = odoo
self._logger = Logger("websocket")
self._connect2server()

def _connect2server(self):
# websocket.enableTrace(True)
self.ws = websocket.WebSocketApp(
url=LIBP2P_WS_SERVER,
on_open=self._on_connection,
on_message=self._on_message,
on_error=self._on_error,
on_close=self._on_close,
)

def run(self) -> None:
self.ws.run_forever(dispatcher=rel, reconnect=5)
rel.signal(2, rel.abort) # Keyboard Interrupt
rel.dispatch()

def _on_connection(self, ws):
self._logger.debug(f"Connected to {LIBP2P_WS_SERVER}")
msg = message_for_subscribing()
self._logger.debug(f"Connection msg: {msg}")
self.ws.send(msg)

def _on_message(self, ws, message):
json_message = json.loads(message)
self._logger.debug(f"Got msg: {json_message}")
if "peerId" in json_message:
return
message_data = json_message["data"]
if "email" in message_data:
encrypted_email = message_data["email"]
sender_address = message_data["sender_address"]
decrypted_email = decrypt_message(encrypted_email, sender_address, self._logger)
rrs_user_id = self.odoo.check_if_rrs_user_exists(sender_address)
if rrs_user_id:
pinata_key, pinata_secret = self.odoo.retrieve_pinata_creds(sender_address, rrs_user_id)
if pinata_key:
msg = message_with_pinata_creds(pinata_key, pinata_secret, sender_address, self._logger)
self.ws.send(msg)
return
user_id = self.odoo.create_rrs_user(decrypted_email, sender_address)
hash = add_device_to_subscription(sender_address)
if hash:
self._logger.debug(f"Add {sender_address} to subscription")
pinata_keys = generate_pinata_keys(PINATA_API_KEY, PINATA_API_SECRET, sender_address)
pinata_key = pinata_keys["pinata_api_key"]
pinata_secret = pinata_keys["pinata_api_secret"]
self.odoo.update_rrs_user_with_pinata_creds(user_id, pinata_key, pinata_secret)
msg = message_with_pinata_creds(pinata_key, pinata_secret, sender_address, self._logger)
self.ws.send(msg)

else:
self._logger.error(f"Couldn't add {sender_address} to subscription: {hash}")

def _on_error(self, ws, error):
self._logger.error(f"{error}")

def _on_close(self, ws, close_status_code, close_msg):
self._logger.debug(f"Connection closed with status code {close_status_code} and message {close_msg}")


Empty file added registar/utils/__init__.py
Empty file.
20 changes: 20 additions & 0 deletions registar/utils/messages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import os
import json
from utils.encryption import encrypt_for_users

ADMIN_SEED = os.getenv("ADMIN_SEED")

def message_with_pinata_creds(pinata_key: str, pinata_secret: str, sender_address: str, logger) -> str:
pinata_key_encrypted = encrypt_for_users(pinata_key, [sender_address], logger)
pinata_secret_encrypted = encrypt_for_users(pinata_secret, [sender_address], logger)
msg = {
"protocol": f"/pinataCreds/{sender_address}",
"serverPeerId": "",
"save_data": False,
"data": {"data": {"public": pinata_key_encrypted, "private": pinata_secret_encrypted}},
}
return json.dumps(msg)

def message_for_subscribing() -> str:
msg = {"protocols_to_listen": ["/initialization"]}
return json.dumps(msg)
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 1239b20

Please sign in to comment.