-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16 from PinoutLTD/v.0.1-beta
new structure of the project. new description
- Loading branch information
Showing
32 changed files
with
835 additions
and
679 deletions.
There are no files selected for viewing
File renamed without changes.
File renamed without changes.
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,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 |
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
File renamed without changes.
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,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.
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,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" |
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,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}") |
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,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.
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,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.
Oops, something went wrong.