Felix database skeleton and registrtion view stubbing

pull/812/head
Kieran Prasch 2019-03-05 13:50:14 -08:00
parent ce39f92555
commit 3fbb5ef426
No known key found for this signature in database
GPG Key ID: 199AB839D4125A62
5 changed files with 101 additions and 58 deletions

View File

@ -3,6 +3,7 @@ import os
from os.path import dirname, abspath
import click
import maya
from flask import Flask, render_template, Response
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
@ -111,35 +112,41 @@ class Felix(Character):
_default_crypto_powerups = [SigningPower]
def __init__(self, db_filepath, rest_host, rest_port, *args, **kwargs):
def __init__(self,
db_filepath: str,
rest_host: str,
rest_port: int,
*args, **kwargs):
# Character
super().__init__(*args, **kwargs)
#
# Felix
#
# Network
self.rest_port = rest_port
self.rest_host = rest_host
self.db_filepath = db_filepath
self.rest_app = None
# Database
self.db_filepath = db_filepath
self.db = None
self.engine = create_engine(f'sqlite://{self.db_filepath}', convert_unicode=True)
# Banner
self.log.info(FELIX_BANNER.format(bytes(self.stamp).hex()))
def init_db(self):
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=self.engine))
Base = declarative_base()
Base.query = db_session.query_property()
Base.metadata.create_all(bind=self.engine)
def make_web_app(self):
from flask import request
from flask_sqlalchemy import SQLAlchemy
# WSGI Service
self.rest_app = Flask("faucet", template_folder=TEMPLATES_DIR)
# Flask Settings
self.rest_app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite://{self.db_filepath}'
self.rest_app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{self.db_filepath}'
self.rest_app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
self.rest_app.secret_key = "flask rocks!" # FIXME: NO!!!
@ -152,19 +159,31 @@ class Felix(Character):
id = self.db.Column(self.db.Integer, primary_key=True)
address = self.db.Column(self.db.String)
joined = self.db.Column(self.db.String)
amount_received = self.db.Column(self.db.Integer, default=0)
last_airdrop = self.db.Column(self.db.String, nullable=True)
rest_app = self.rest_app
@rest_app.route("/")
@rest_app.route("/", methods=['GET'])
def home():
return render_template('felix.html')
@rest_app.route("/register")
@rest_app.route("/register", methods=['POST'])
def register():
try:
recipient = Recipient(address=request.form['address'], joined=str(maya.now()))
self.db.session.add(recipient)
self.db.session.commit()
except Exception as e:
self.log.critical(str(e))
return Response(status=200)
return rest_app
def create_tables(self) -> None:
self.db.create_all()
return
def start(self, host: str, port: int, dry_run: bool = False):
# Server

View File

@ -43,64 +43,77 @@ def felix(click_config,
registry_filepath):
if action == "init":
"""Create a brand-new persistent Ursula"""
"""Create a brand-new Felix"""
# Validate "Init" Input
if not network:
raise click.BadArgumentUsage('--network is required to initialize a new configuration.')
# Acquire Keyring Password
if not config_root: # Flag
config_root = click_config.config_file # Envvar
new_password = click_config._get_password(confirm=True)
ursula_config = FelixConfiguration.generate(password=click_config._get_password(confirm=True),
config_root=config_root,
rest_host=host,
rest_port=discovery_port,
db_filepath=db_filepath,
domains={network} if network else None,
checksum_public_address=checksum_address,
no_registry=no_registry,
registry_filepath=registry_filepath,
provider_uri=provider_uri,
poa=poa)
new_felix_config = FelixConfiguration.generate(password=new_password,
config_root=config_root,
rest_host=host,
rest_port=discovery_port,
db_filepath=db_filepath,
domains={network} if network else None,
checksum_public_address=checksum_address,
no_registry=no_registry,
registry_filepath=registry_filepath,
provider_uri=provider_uri,
poa=poa)
painting.paint_new_installation_help(new_configuration=ursula_config,
# Paint Help
painting.paint_new_installation_help(new_configuration=new_felix_config,
config_root=config_root,
config_file=config_file)
return
elif action == 'run':
return # <-- do not remove (conditional flow control)
# Domains -> bytes | or default
domains = [bytes(network, encoding='utf-8')] if network else None
#
# Authentication Configurations
#
# Load Ursula from Configuration File
try:
felix_config = FelixConfiguration.from_configuration_file(filepath=config_file,
domains=domains,
registry_filepath=registry_filepath,
provider_uri=provider_uri,
rest_host=host,
rest_port=port,
db_filepath=db_filepath,
poa=poa)
except FileNotFoundError:
click.secho("No Felix Configuration File Found.")
raise click.Abort
# Domains -> bytes | or default
domains = [bytes(network, encoding='utf-8')] if network else None
# Teacher Ursula
# Load Ursula from Configuration File with overrides
try:
felix_config = FelixConfiguration.from_configuration_file(filepath=config_file,
domains=domains,
registry_filepath=registry_filepath,
provider_uri=provider_uri,
rest_host=host,
rest_port=port,
db_filepath=db_filepath,
poa=poa)
except FileNotFoundError:
click.secho(f"No Felix configuration file found at {config_file}. "
f"Check the filepath or run 'nucypher felix init' to create a new system configuration.")
raise click.Abort
else:
# Produce Teacher Ursulas
teacher_uris = [teacher_uri] if teacher_uri else list()
teacher_nodes = actions.load_seednodes(teacher_uris=teacher_uris,
min_stake=min_stake,
federated_only=False,
network_middleware=click_config.middleware)
# Felix
# Produce Felix
click_config.unlock_keyring(character_configuration=felix_config)
FELIX = felix_config.produce(domains=network, known_nodes=teacher_nodes)
FELIX.make_web_app() # attach web application, but dont start service
# Start web services
FELIX.make_web_app()
if action == "createdb": # Initialize Database
FELIX.create_tables()
elif action == 'run': # Start web services
FELIX.start(host=host, port=port, dry_run=dry_run)
else:
else: # Error
raise click.BadArgumentUsage("No such argument {}".format(action))

View File

@ -21,6 +21,10 @@ import os
from constant_sorrow.constants import (
UNINITIALIZED_CONFIGURATION
)
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, scoped_session
from nucypher.config.constants import DEFAULT_CONFIG_ROOT
from nucypher.config.keyring import NucypherKeyring
from nucypher.config.node import NodeConfiguration
@ -133,9 +137,13 @@ class FelixConfiguration(NodeConfiguration):
from nucypher.characters.chaotic import Felix
def __init__(self, db_filepath: str = None, *args, **kwargs) -> None:
self.db_filepath = db_filepath or self.DEFAULT_DB_FILEPATH
# Character
super().__init__(*args, **kwargs)
# Felix
self.db_filepath = db_filepath or os.path.join(self.config_root, self.DEFAULT_DB_NAME)
# Character
_CHARACTER_CLASS = Felix
_NAME = _CHARACTER_CLASS.__name__.lower()

View File

@ -16,14 +16,14 @@ from nucypher.utilities.sandbox.constants import (
@pytest_twisted.inlineCallbacks
def test_run_felix(click_runner, federated_ursulas):
args = ('felix', 'init',
'--config-root', MOCK_CUSTOM_INSTALLATION_PATH_2,
'--network', TEMPORARY_DOMAIN,
'--no-registry',
'--provider-uri', TEST_PROVIDER_URI)
init_args = ('felix', 'init',
'--config-root', MOCK_CUSTOM_INSTALLATION_PATH_2,
'--network', TEMPORARY_DOMAIN,
'--no-registry',
'--provider-uri', TEST_PROVIDER_URI)
user_input = f'{INSECURE_DEVELOPMENT_PASSWORD}\n{INSECURE_DEVELOPMENT_PASSWORD}'
result = click_runner.invoke(nucypher_cli, args, input=user_input, catch_exceptions=False)
result = click_runner.invoke(nucypher_cli, init_args, input=user_input, catch_exceptions=False)
assert result.exit_code == 0
configuration_file_location = os.path.join(MOCK_CUSTOM_INSTALLATION_PATH_2, 'felix.config')
@ -52,7 +52,10 @@ def test_run_felix(click_runner, federated_ursulas):
test_client = web_app.test_client()
response = test_client.get('/')
assert response == 200
assert response.status_code == 200
response = test_client.post('/register', data={'address': '0xdeadbeef'})
assert response.status_code == 200
d = threads.deferToThread(run_felix)
d.addCallback(request_felix_landing_page)

View File

@ -30,7 +30,7 @@ globalLogPublisher.removeObserver(logToSentry)
# Disable click sentry and file logging
NucypherClickConfig.log_to_sentry = False
NucypherClickConfig.log_to_file = False
NucypherClickConfig.log_to_file = True
# Crash on server error by default
WebEmitter._crash_on_error_default = False