From 488116b532e9a58ad49f8c35e1dff1fdab16bc73 Mon Sep 17 00:00:00 2001 From: Giacomo Licari Date: Wed, 28 Feb 2024 14:21:12 +0100 Subject: [PATCH 1/3] Add migrations --- api/api/api.py | 6 ++--- api/api/services/database.py | 6 ++--- api/migrations/versions/71441c34724e_.py | 32 ++++++++++++++++++++++++ api/scripts/run_migrations.sh | 14 +++++++++++ api/scripts/run_test_env.sh | 23 ----------------- api/tests/conftest.py | 2 ++ api/tests/temp_env_var.py | 2 +- api/tests/test_database.py | 8 +++--- 8 files changed, 57 insertions(+), 36 deletions(-) create mode 100644 api/migrations/versions/71441c34724e_.py create mode 100644 api/scripts/run_migrations.sh delete mode 100644 api/scripts/run_test_env.sh diff --git a/api/api/api.py b/api/api/api.py index bff7e1d..b1c36b6 100644 --- a/api/api/api.py +++ b/api/api/api.py @@ -2,10 +2,11 @@ from flask import Flask from flask_cors import CORS +from flask_migrate import Migrate from .routes import apiv1 from .services import Cache, Web3Singleton -from .services.database import db, migrate +from .services.database import db def setup_logger(log_level): @@ -39,8 +40,7 @@ def create_app(): with app.app_context(): db.init_app(app) - migrate.init_app(app, db) - db.create_all() # Create database tables for our data models + Migrate(app, db) # Initialize Web3 class w3 = Web3Singleton(app.config['FAUCET_RPC_URL'], app.config['FAUCET_PRIVATE_KEY']) diff --git a/api/api/services/database.py b/api/api/services/database.py index 17a93d2..88d53df 100644 --- a/api/api/services/database.py +++ b/api/api/services/database.py @@ -1,10 +1,8 @@ import sqlite3 -from flask_migrate import Migrate from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() -migrate = Migrate() class Database: @@ -68,8 +66,8 @@ def delete(self, commit=True): class AccessKey(BaseModel): __tablename__ = "access_keys" access_key_id = db.Column(db.String(16), primary_key=True) - secret_access_key = db.Column(db.String(32)) - enabled = db.Column(db.Boolean(), default=True) + secret_access_key = db.Column(db.String(32), nullable=False) + enabled = db.Column(db.Boolean(), default=True, nullable=False) def __repr__(self): return f"" diff --git a/api/migrations/versions/71441c34724e_.py b/api/migrations/versions/71441c34724e_.py new file mode 100644 index 0000000..5360806 --- /dev/null +++ b/api/migrations/versions/71441c34724e_.py @@ -0,0 +1,32 @@ +"""empty message + +Revision ID: 71441c34724e +Revises: +Create Date: 2024-02-28 14:11:13.601403 + +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = '71441c34724e' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('access_keys', + sa.Column('access_key_id', sa.String(length=16), nullable=False), + sa.Column('secret_access_key', sa.String(length=32), nullable=False), + sa.Column('enabled', sa.Boolean(), nullable=False), + sa.PrimaryKeyConstraint('access_key_id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('access_keys') + # ### end Alembic commands ### diff --git a/api/scripts/run_migrations.sh b/api/scripts/run_migrations.sh new file mode 100644 index 0000000..6f1ab68 --- /dev/null +++ b/api/scripts/run_migrations.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +set -x + +# DB MIGRATIONS: +FLASK_APP=api FAUCET_DATABASE_URI=sqlite:///:memory python3 -m flask db init # only the first time we initialize the DB +FLASK_APP=api FAUCET_DATABASE_URI=sqlite:///:memory python3 -m flask db migrate +# Reflect migrations onto the database: +# FLASK_APP=api python3 -m flask db upgrade + +# Valid SQLite URL forms are: +# sqlite:///:memory: (or, sqlite://) +# sqlite:///relative/path/to/file.db +# sqlite:////absolute/path/to/file.db \ No newline at end of file diff --git a/api/scripts/run_test_env.sh b/api/scripts/run_test_env.sh deleted file mode 100644 index 9ca35df..0000000 --- a/api/scripts/run_test_env.sh +++ /dev/null @@ -1,23 +0,0 @@ -touch /tmp/faucet_test.db - -# !! PRIVATE KEY FOR TEST PURPOSE ONLY !! -# 0x21b1ae205147d4e2fcdee7b3fc762aa21f955f3dace8a185306ac104be797081 -# !! DO NOT USE IN ANY OTHER CONTEXTS !! - -FAUCET_RPC_URL=https://rpc.chiadochain.net \ -FAUCET_PRIVATE_KEY="0x21b1ae205147d4e2fcdee7b3fc762aa21f955f3dace8a185306ac104be797081" \ -FAUCET_CHAIN_ID=10200 \ -FAUCET_DATABASE_URI=sqlite:///tmp/faucet_test.db \ -CAPTCHA_VERIFY_ENDPOINT=localhost \ -CAPTCHA_SECRET_KEY=testkey \ -python3 -m flask --app api run --port 8000 - -# DB MIGRATIONS: -## Generate migrations -### ENV_VARIABLES python3 -m flask --app api db init -### ENV_VARIABLES python3 -m flask --app api db migrate - -# Valid SQLite URL forms are: -# sqlite:///:memory: (or, sqlite://) -# sqlite:///relative/path/to/file.db -# sqlite:////absolute/path/to/file.db \ No newline at end of file diff --git a/api/tests/conftest.py b/api/tests/conftest.py index 22420b4..68082ed 100644 --- a/api/tests/conftest.py +++ b/api/tests/conftest.py @@ -1,6 +1,7 @@ import os import pytest +from flask_migrate import upgrade from temp_env_var import (CAPTCHA_TEST_RESPONSE_TOKEN, ERC20_TOKEN_ADDRESS, ERC20_TOKEN_AMOUNT, NATIVE_TOKEN_ADDRESS, NATIVE_TOKEN_AMOUNT, NATIVE_TRANSFER_TX_HASH, @@ -30,6 +31,7 @@ def app(self, mocker): mocker = self._mock(mocker, TEMP_ENV_VARS) app = self._create_app() with app.app_context(): + upgrade() yield app @pytest.fixture diff --git a/api/tests/temp_env_var.py b/api/tests/temp_env_var.py index ca6e29c..4122565 100644 --- a/api/tests/temp_env_var.py +++ b/api/tests/temp_env_var.py @@ -23,7 +23,7 @@ 'FAUCET_PRIVATE_KEY': token_bytes(32).hex(), 'FAUCET_RATE_LIMIT_TIME_LIMIT_SECONDS': '10', 'FAUCET_ENABLED_TOKENS': json.dumps(FAUCET_ENABLED_TOKENS), - 'FAUCET_DATABASE_URI': 'sqlite:///', # run in-memory + 'FAUCET_DATABASE_URI': 'sqlite:///:memory', # run in-memory 'CAPTCHA_SECRET_KEY': CAPTCHA_TEST_SECRET_KEY } diff --git a/api/tests/test_database.py b/api/tests/test_database.py index b70ff5e..3e9ad4f 100644 --- a/api/tests/test_database.py +++ b/api/tests/test_database.py @@ -1,15 +1,13 @@ from conftest import BaseTest -# from mock import patch -from temp_env_var import (CAPTCHA_TEST_RESPONSE_TOKEN, ERC20_TOKEN_ADDRESS, - ERC20_TOKEN_AMOUNT, NATIVE_TOKEN_ADDRESS, - NATIVE_TOKEN_AMOUNT, NATIVE_TRANSFER_TX_HASH, - TEMP_ENV_VARS, TOKEN_TRANSFER_TX_HASH, ZERO_ADDRESS) from api.services.database import AccessKey from api.utils import generate_access_key class TestDatabase(BaseTest): + + # db.create_all() # Create database tables for our data models + def test_models(self, client): access_key_id, secret_access_key = generate_access_key() assert len(access_key_id) == 16 From f2972e6da01166ad1b91c1291677d6145825e192 Mon Sep 17 00:00:00 2001 From: Giacomo Licari Date: Wed, 28 Feb 2024 16:46:20 +0100 Subject: [PATCH 2/3] API: improve Dockerfile, add migrations, add command to create access keys --- README.md | 29 +++++++++++-------- api/.env.example | 1 + api/Dockerfile | 2 +- api/api/api.py | 5 +++- api/api/manage.py | 19 ++++++++++++ api/api/routes.py | 10 ++++--- ..._migrations.sh => local_run_migrations.sh} | 2 +- api/scripts/production_run_api.sh | 10 +++++++ api/scripts/run.sh | 1 - api/tests/test_api.py | 4 +-- api/tests/test_database.py | 2 -- 11 files changed, 61 insertions(+), 24 deletions(-) create mode 100644 api/api/manage.py rename api/scripts/{run_migrations.sh => local_run_migrations.sh} (91%) create mode 100644 api/scripts/production_run_api.sh delete mode 100644 api/scripts/run.sh diff --git a/README.md b/README.md index 4071a55..7cd86e9 100644 --- a/README.md +++ b/README.md @@ -2,39 +2,38 @@ A simple python implementation of an EVM compatible faucet. -## API +## Python API ### Requirements -Python +3.x, NodeJS v18.x +Python +3.x -### Python API +### Installation ``` cd api python3 -m venv .venv . .venv/bin/activate -pip3 install -r requirements.txt - -python3 -m flask --app api run --port 8000 +pip3 install -r requirements-dev.txt ``` -#### Run application +### Run application + +Check .env.example for reference. ``` cd api python3 -m flask --app api run --port 8000 ``` - -#### Run tests +### Run tests ``` cd api python3 -m pytest -s ``` -#### Run Flake8 and isort +### Run Flake8 and isort ``` cd api @@ -43,7 +42,13 @@ isort **/*.py --atomic python3 -m flake8 ``` -### ReactJS Frontend +## ReactJS Frontend + +### Requirements + +NodeJS v18.x + +### Installation ``` nvm use @@ -52,7 +57,7 @@ cd app yarn ``` -#### Run application +### Run application ``` cd app diff --git a/api/.env.example b/api/.env.example index a444063..8c59633 100644 --- a/api/.env.example +++ b/api/.env.example @@ -4,5 +4,6 @@ FAUCET_RPC_URL=https://rpc.chiadochain.net FAUCET_CHAIN_ID=10200 FAUCET_ENABLED_TOKENS="[{\"address\": \"0x19C653Da7c37c66208fbfbE8908A5051B57b4C70\", \"name\":\"GNO\", \"maximumAmount\": 0.5}]" # FAUCET_ENABLED_TOKENS= +FAUCET_DATABASE_URI=sqlite:///:memory CAPTCHA_VERIFY_ENDPOINT=https://api.hcaptcha.com/siteverify CAPTCHA_SECRET_KEY=0x0000000000000000000000000000000000000000 \ No newline at end of file diff --git a/api/Dockerfile b/api/Dockerfile index 2ae5386..236b1c5 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -6,4 +6,4 @@ RUN pip install --no-cache-dir -r /tmp/requirements.txt COPY . /api WORKDIR /api -CMD ["gunicorn", "--bind", "0.0.0.0:8000", "api:create_app()"] \ No newline at end of file +ENTRYPOINT ["/sbin/tini", "--"] \ No newline at end of file diff --git a/api/api/api.py b/api/api/api.py index b1c36b6..2a53fb0 100644 --- a/api/api/api.py +++ b/api/api/api.py @@ -4,6 +4,7 @@ from flask_cors import CORS from flask_migrate import Migrate +from .manage import create_access_keys_cmd from .routes import apiv1 from .services import Cache, Web3Singleton from .services.database import db @@ -37,12 +38,14 @@ def create_app(): app.config['FAUCET_CACHE'] = Cache(app.config['FAUCET_RATE_LIMIT_TIME_LIMIT_SECONDS']) # Initialize API Routes app.register_blueprint(apiv1, url_prefix="/api/v1") + # Add cli commands + app.cli.add_command(create_access_keys_cmd) with app.app_context(): db.init_app(app) Migrate(app, db) - # Initialize Web3 class + # Initialize Web3 class for latter usage w3 = Web3Singleton(app.config['FAUCET_RPC_URL'], app.config['FAUCET_PRIVATE_KEY']) setup_cors(app) diff --git a/api/api/manage.py b/api/api/manage.py new file mode 100644 index 0000000..2d46961 --- /dev/null +++ b/api/api/manage.py @@ -0,0 +1,19 @@ +import logging + +import click +from flask.cli import with_appcontext + +from .services.database import AccessKey +from .utils import generate_access_key + + +@click.command(name='create_access_keys') +@with_appcontext +def create_access_keys_cmd(): + access_key_id, secret_access_key = generate_access_key() + access_key = AccessKey() + access_key.access_key_id = access_key_id + access_key.secret_access_key = secret_access_key + access_key.save() + logging.info(f'Access Key ID : ${access_key_id}') + logging.info(f'Secret access key: ${secret_access_key}') diff --git a/api/api/routes.py b/api/api/routes.py index c7d444d..c81c9bb 100644 --- a/api/api/routes.py +++ b/api/api/routes.py @@ -106,13 +106,14 @@ def _ask(request_data, validate_captcha): @apiv1.route("/ask", methods=["POST"]) def ask(): - return _ask(request.get_json(), validate_captcha=True) + data, status_code = _ask(request.get_json(), validate_captcha=True) + return data, status_code @apiv1.route("/cli/ask", methods=["POST"]) def cli_ask(): - access_key_id = request.headers.get('FAUCET_ACCESS_KEY_ID', None) - secret_access_key = request.headers.get('FAUCET_SECRET_ACCESS_KEY', None) + access_key_id = request.headers.get('X-faucet-access-key-id', None) + secret_access_key = request.headers.get('X-faucet-secret-access-key', None) validation_errors = [] @@ -127,4 +128,5 @@ def cli_ask(): validation_errors.append('Access denied') return jsonify(errors=validation_errors), 403 - return _ask(request.get_json(), validate_captcha=False) + data, status_code = _ask(request.get_json(), validate_captcha=False) + return data, status_code diff --git a/api/scripts/run_migrations.sh b/api/scripts/local_run_migrations.sh similarity index 91% rename from api/scripts/run_migrations.sh rename to api/scripts/local_run_migrations.sh index 6f1ab68..daa87d0 100644 --- a/api/scripts/run_migrations.sh +++ b/api/scripts/local_run_migrations.sh @@ -5,7 +5,7 @@ set -x # DB MIGRATIONS: FLASK_APP=api FAUCET_DATABASE_URI=sqlite:///:memory python3 -m flask db init # only the first time we initialize the DB FLASK_APP=api FAUCET_DATABASE_URI=sqlite:///:memory python3 -m flask db migrate -# Reflect migrations onto the database: +# Reflect migrations into the database: # FLASK_APP=api python3 -m flask db upgrade # Valid SQLite URL forms are: diff --git a/api/scripts/production_run_api.sh b/api/scripts/production_run_api.sh new file mode 100644 index 0000000..3a749b6 --- /dev/null +++ b/api/scripts/production_run_api.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +set -euo pipefail + + +echo "==> $(date +%H:%M:%S) ==> Migrating DB models... " +FLASK_APP=api python -m flask db upgrade + +echo "==> $(date +%H:%M:%S) ==> Running Gunicorn... " +exec gunicorn --bind 0.0.0.0:8000 "api:create_app()" \ No newline at end of file diff --git a/api/scripts/run.sh b/api/scripts/run.sh deleted file mode 100644 index 26db72c..0000000 --- a/api/scripts/run.sh +++ /dev/null @@ -1 +0,0 @@ -python3 -m flask --app api run --port 8000 \ No newline at end of file diff --git a/api/tests/test_api.py b/api/tests/test_api.py index bb2502f..3da6796 100644 --- a/api/tests/test_api.py +++ b/api/tests/test_api.py @@ -127,8 +127,8 @@ class TestCliAPI(BaseTest): def test_ask_route_parameters(self, client): access_key_id, secret_access_key = generate_access_key() http_headers = { - 'FAUCET_ACCESS_KEY_ID': access_key_id, - 'FAUCET_SECRET_ACCESS_KEY': secret_access_key + 'X-faucet-access-key-id': access_key_id, + 'X-faucet-secret-access-key': secret_access_key } response = client.post(api_prefix + '/cli/ask', json={}) diff --git a/api/tests/test_database.py b/api/tests/test_database.py index 3e9ad4f..5d3f645 100644 --- a/api/tests/test_database.py +++ b/api/tests/test_database.py @@ -6,8 +6,6 @@ class TestDatabase(BaseTest): - # db.create_all() # Create database tables for our data models - def test_models(self, client): access_key_id, secret_access_key = generate_access_key() assert len(access_key_id) == 16 From f201b916ecf726fd5a9cca36e9f27a2b24c106ba Mon Sep 17 00:00:00 2001 From: Giacomo Licari Date: Tue, 5 Mar 2024 11:40:33 +0100 Subject: [PATCH 3/3] Revert "Merge pull request #10 from gnosischain/improvements" This reverts commit 2ef35b65f88aa2cdc75cd61ccf6fbeb3f9e23f9e, reversing changes made to 5091cc7f907794071ba178686f98a0d54e35a530. --- api/.env.example | 1 - api/{tox.ini => .tox.ini} | 0 api/Dockerfile | 2 +- api/api/api.py | 11 ++-- api/api/const.py | 2 +- api/api/manage.py | 19 ------ api/api/routes.py | 10 ++- api/api/services/cache.py | 2 +- api/api/services/database.py | 6 +- api/api/settings.py | 2 +- api/api/utils.py | 2 +- api/migrations/README | 1 - api/migrations/alembic.ini | 50 --------------- api/migrations/env.py | 112 ---------------------------------- api/migrations/script.py.mako | 24 -------- api/tests/conftest.py | 2 - api/tests/temp_env_var.py | 2 +- api/tests/test_api.py | 4 +- api/tests/test_database.py | 6 +- 19 files changed, 25 insertions(+), 233 deletions(-) rename api/{tox.ini => .tox.ini} (100%) delete mode 100644 api/api/manage.py delete mode 100644 api/migrations/README delete mode 100644 api/migrations/alembic.ini delete mode 100644 api/migrations/env.py delete mode 100644 api/migrations/script.py.mako diff --git a/api/.env.example b/api/.env.example index 8c59633..a444063 100644 --- a/api/.env.example +++ b/api/.env.example @@ -4,6 +4,5 @@ FAUCET_RPC_URL=https://rpc.chiadochain.net FAUCET_CHAIN_ID=10200 FAUCET_ENABLED_TOKENS="[{\"address\": \"0x19C653Da7c37c66208fbfbE8908A5051B57b4C70\", \"name\":\"GNO\", \"maximumAmount\": 0.5}]" # FAUCET_ENABLED_TOKENS= -FAUCET_DATABASE_URI=sqlite:///:memory CAPTCHA_VERIFY_ENDPOINT=https://api.hcaptcha.com/siteverify CAPTCHA_SECRET_KEY=0x0000000000000000000000000000000000000000 \ No newline at end of file diff --git a/api/tox.ini b/api/.tox.ini similarity index 100% rename from api/tox.ini rename to api/.tox.ini diff --git a/api/Dockerfile b/api/Dockerfile index 236b1c5..2ae5386 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -6,4 +6,4 @@ RUN pip install --no-cache-dir -r /tmp/requirements.txt COPY . /api WORKDIR /api -ENTRYPOINT ["/sbin/tini", "--"] \ No newline at end of file +CMD ["gunicorn", "--bind", "0.0.0.0:8000", "api:create_app()"] \ No newline at end of file diff --git a/api/api/api.py b/api/api/api.py index 2a53fb0..bff7e1d 100644 --- a/api/api/api.py +++ b/api/api/api.py @@ -2,12 +2,10 @@ from flask import Flask from flask_cors import CORS -from flask_migrate import Migrate -from .manage import create_access_keys_cmd from .routes import apiv1 from .services import Cache, Web3Singleton -from .services.database import db +from .services.database import db, migrate def setup_logger(log_level): @@ -38,14 +36,13 @@ def create_app(): app.config['FAUCET_CACHE'] = Cache(app.config['FAUCET_RATE_LIMIT_TIME_LIMIT_SECONDS']) # Initialize API Routes app.register_blueprint(apiv1, url_prefix="/api/v1") - # Add cli commands - app.cli.add_command(create_access_keys_cmd) with app.app_context(): db.init_app(app) - Migrate(app, db) + migrate.init_app(app, db) + db.create_all() # Create database tables for our data models - # Initialize Web3 class for latter usage + # Initialize Web3 class w3 = Web3Singleton(app.config['FAUCET_RPC_URL'], app.config['FAUCET_PRIVATE_KEY']) setup_cors(app) diff --git a/api/api/const.py b/api/api/const.py index ec46c65..5c6a67e 100644 --- a/api/api/const.py +++ b/api/api/const.py @@ -1 +1 @@ -NATIVE_TOKEN_ADDRESS = 'native' +NATIVE_TOKEN_ADDRESS='native' diff --git a/api/api/manage.py b/api/api/manage.py deleted file mode 100644 index 2d46961..0000000 --- a/api/api/manage.py +++ /dev/null @@ -1,19 +0,0 @@ -import logging - -import click -from flask.cli import with_appcontext - -from .services.database import AccessKey -from .utils import generate_access_key - - -@click.command(name='create_access_keys') -@with_appcontext -def create_access_keys_cmd(): - access_key_id, secret_access_key = generate_access_key() - access_key = AccessKey() - access_key.access_key_id = access_key_id - access_key.secret_access_key = secret_access_key - access_key.save() - logging.info(f'Access Key ID : ${access_key_id}') - logging.info(f'Secret access key: ${secret_access_key}') diff --git a/api/api/routes.py b/api/api/routes.py index c81c9bb..157eb37 100644 --- a/api/api/routes.py +++ b/api/api/routes.py @@ -106,14 +106,13 @@ def _ask(request_data, validate_captcha): @apiv1.route("/ask", methods=["POST"]) def ask(): - data, status_code = _ask(request.get_json(), validate_captcha=True) - return data, status_code + return _ask(request.get_json(), validate_captcha=True) @apiv1.route("/cli/ask", methods=["POST"]) def cli_ask(): - access_key_id = request.headers.get('X-faucet-access-key-id', None) - secret_access_key = request.headers.get('X-faucet-secret-access-key', None) + access_key_id = request.headers.get('FAUCET_ACCESS_KEY_ID', None) + secret_access_key = request.headers.get('FAUCET_SECRET_ACCESS_KEY', None) validation_errors = [] @@ -128,5 +127,4 @@ def cli_ask(): validation_errors.append('Access denied') return jsonify(errors=validation_errors), 403 - data, status_code = _ask(request.get_json(), validate_captcha=False) - return data, status_code + return _ask(request.get_json(), validate_captcha=False) \ No newline at end of file diff --git a/api/api/services/cache.py b/api/api/services/cache.py index aff61b9..00a3317 100644 --- a/api/api/services/cache.py +++ b/api/api/services/cache.py @@ -30,4 +30,4 @@ def ttl(self, hours=False): if hours: # 3600 seconds = 1h return self._ttl // 3600 - return self._ttl + return self._ttl \ No newline at end of file diff --git a/api/api/services/database.py b/api/api/services/database.py index 88d53df..17a93d2 100644 --- a/api/api/services/database.py +++ b/api/api/services/database.py @@ -1,8 +1,10 @@ import sqlite3 +from flask_migrate import Migrate from flask_sqlalchemy import SQLAlchemy db = SQLAlchemy() +migrate = Migrate() class Database: @@ -66,8 +68,8 @@ def delete(self, commit=True): class AccessKey(BaseModel): __tablename__ = "access_keys" access_key_id = db.Column(db.String(16), primary_key=True) - secret_access_key = db.Column(db.String(32), nullable=False) - enabled = db.Column(db.Boolean(), default=True, nullable=False) + secret_access_key = db.Column(db.String(32)) + enabled = db.Column(db.Boolean(), default=True) def __repr__(self): return f"" diff --git a/api/api/settings.py b/api/api/settings.py index bbcdf82..97e6210 100644 --- a/api/api/settings.py +++ b/api/api/settings.py @@ -37,4 +37,4 @@ CORS_ALLOWED_ORIGINS = os.getenv('CORS_ALLOWED_ORIGINS', '*') CAPTCHA_VERIFY_ENDPOINT = os.getenv('CAPTCHA_VERIFY_ENDPOINT') -CAPTCHA_SECRET_KEY = os.getenv('CAPTCHA_SECRET_KEY') +CAPTCHA_SECRET_KEY = os.getenv('CAPTCHA_SECRET_KEY') \ No newline at end of file diff --git a/api/api/utils.py b/api/api/utils.py index 1827d19..2440279 100644 --- a/api/api/utils.py +++ b/api/api/utils.py @@ -63,4 +63,4 @@ def is_amount_valid(amount, token_address, tokens_list): def generate_access_key(): access_key_id = secrets.token_hex(8) # returns a 16 chars long string secret_access_key = secrets.token_hex(16) # returns a 32 chars long string - return access_key_id, secret_access_key + return access_key_id, secret_access_key \ No newline at end of file diff --git a/api/migrations/README b/api/migrations/README deleted file mode 100644 index 0e04844..0000000 --- a/api/migrations/README +++ /dev/null @@ -1 +0,0 @@ -Single-database configuration for Flask. diff --git a/api/migrations/alembic.ini b/api/migrations/alembic.ini deleted file mode 100644 index ec9d45c..0000000 --- a/api/migrations/alembic.ini +++ /dev/null @@ -1,50 +0,0 @@ -# A generic, single database configuration. - -[alembic] -# template used to generate migration files -# file_template = %%(rev)s_%%(slug)s - -# set to 'true' to run the environment during -# the 'revision' command, regardless of autogenerate -# revision_environment = false - - -# Logging configuration -[loggers] -keys = root,sqlalchemy,alembic,flask_migrate - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = WARN -handlers = console -qualname = - -[logger_sqlalchemy] -level = WARN -handlers = -qualname = sqlalchemy.engine - -[logger_alembic] -level = INFO -handlers = -qualname = alembic - -[logger_flask_migrate] -level = INFO -handlers = -qualname = flask_migrate - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(levelname)-5.5s [%(name)s] %(message)s -datefmt = %H:%M:%S diff --git a/api/migrations/env.py b/api/migrations/env.py deleted file mode 100644 index 0749ebf..0000000 --- a/api/migrations/env.py +++ /dev/null @@ -1,112 +0,0 @@ -import logging -from logging.config import fileConfig - -from alembic import context -from flask import current_app - -# this is the Alembic Config object, which provides -# access to the values within the .ini file in use. -config = context.config - -# Interpret the config file for Python logging. -# This line sets up loggers basically. -fileConfig(config.config_file_name) -logger = logging.getLogger('alembic.env') - - -def get_engine(): - try: - # this works with Flask-SQLAlchemy<3 and Alchemical - return current_app.extensions['migrate'].db.get_engine() - except (TypeError, AttributeError): - # this works with Flask-SQLAlchemy>=3 - return current_app.extensions['migrate'].db.engine - - -def get_engine_url(): - try: - return get_engine().url.render_as_string(hide_password=False).replace( - '%', '%%') - except AttributeError: - return str(get_engine().url).replace('%', '%%') - - -# add your model's MetaData object here -# for 'autogenerate' support -# from myapp import mymodel -# target_metadata = mymodel.Base.metadata -config.set_main_option('sqlalchemy.url', get_engine_url()) -target_db = current_app.extensions['migrate'].db - -# other values from the config, defined by the needs of env.py, -# can be acquired: -# my_important_option = config.get_main_option("my_important_option") -# ... etc. - - -def get_metadata(): - if hasattr(target_db, 'metadatas'): - return target_db.metadatas[None] - return target_db.metadata - - -def run_migrations_offline(): - """Run migrations in 'offline' mode. - - This configures the context with just a URL - and not an Engine, though an Engine is acceptable - here as well. By skipping the Engine creation - we don't even need a DBAPI to be available. - - Calls to context.execute() here emit the given string to the - script output. - - """ - url = config.get_main_option("sqlalchemy.url") - context.configure( - url=url, target_metadata=get_metadata(), literal_binds=True - ) - - with context.begin_transaction(): - context.run_migrations() - - -def run_migrations_online(): - """Run migrations in 'online' mode. - - In this scenario we need to create an Engine - and associate a connection with the context. - - """ - - # this callback is used to prevent an auto-migration from being generated - # when there are no changes to the schema - # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html - def process_revision_directives(context, revision, directives): - if getattr(config.cmd_opts, 'autogenerate', False): - script = directives[0] - if script.upgrade_ops.is_empty(): - directives[:] = [] - logger.info('No changes in schema detected.') - - conf_args = current_app.extensions['migrate'].configure_args - if conf_args.get("process_revision_directives") is None: - conf_args["process_revision_directives"] = process_revision_directives - - connectable = get_engine() - - with connectable.connect() as connection: - context.configure( - connection=connection, - target_metadata=get_metadata(), - **conf_args - ) - - with context.begin_transaction(): - context.run_migrations() - - -if context.is_offline_mode(): - run_migrations_offline() -else: - run_migrations_online() diff --git a/api/migrations/script.py.mako b/api/migrations/script.py.mako deleted file mode 100644 index 2c01563..0000000 --- a/api/migrations/script.py.mako +++ /dev/null @@ -1,24 +0,0 @@ -"""${message} - -Revision ID: ${up_revision} -Revises: ${down_revision | comma,n} -Create Date: ${create_date} - -""" -from alembic import op -import sqlalchemy as sa -${imports if imports else ""} - -# revision identifiers, used by Alembic. -revision = ${repr(up_revision)} -down_revision = ${repr(down_revision)} -branch_labels = ${repr(branch_labels)} -depends_on = ${repr(depends_on)} - - -def upgrade(): - ${upgrades if upgrades else "pass"} - - -def downgrade(): - ${downgrades if downgrades else "pass"} diff --git a/api/tests/conftest.py b/api/tests/conftest.py index 68082ed..22420b4 100644 --- a/api/tests/conftest.py +++ b/api/tests/conftest.py @@ -1,7 +1,6 @@ import os import pytest -from flask_migrate import upgrade from temp_env_var import (CAPTCHA_TEST_RESPONSE_TOKEN, ERC20_TOKEN_ADDRESS, ERC20_TOKEN_AMOUNT, NATIVE_TOKEN_ADDRESS, NATIVE_TOKEN_AMOUNT, NATIVE_TRANSFER_TX_HASH, @@ -31,7 +30,6 @@ def app(self, mocker): mocker = self._mock(mocker, TEMP_ENV_VARS) app = self._create_app() with app.app_context(): - upgrade() yield app @pytest.fixture diff --git a/api/tests/temp_env_var.py b/api/tests/temp_env_var.py index 4122565..ca6e29c 100644 --- a/api/tests/temp_env_var.py +++ b/api/tests/temp_env_var.py @@ -23,7 +23,7 @@ 'FAUCET_PRIVATE_KEY': token_bytes(32).hex(), 'FAUCET_RATE_LIMIT_TIME_LIMIT_SECONDS': '10', 'FAUCET_ENABLED_TOKENS': json.dumps(FAUCET_ENABLED_TOKENS), - 'FAUCET_DATABASE_URI': 'sqlite:///:memory', # run in-memory + 'FAUCET_DATABASE_URI': 'sqlite:///', # run in-memory 'CAPTCHA_SECRET_KEY': CAPTCHA_TEST_SECRET_KEY } diff --git a/api/tests/test_api.py b/api/tests/test_api.py index 3da6796..bb2502f 100644 --- a/api/tests/test_api.py +++ b/api/tests/test_api.py @@ -127,8 +127,8 @@ class TestCliAPI(BaseTest): def test_ask_route_parameters(self, client): access_key_id, secret_access_key = generate_access_key() http_headers = { - 'X-faucet-access-key-id': access_key_id, - 'X-faucet-secret-access-key': secret_access_key + 'FAUCET_ACCESS_KEY_ID': access_key_id, + 'FAUCET_SECRET_ACCESS_KEY': secret_access_key } response = client.post(api_prefix + '/cli/ask', json={}) diff --git a/api/tests/test_database.py b/api/tests/test_database.py index 5d3f645..b70ff5e 100644 --- a/api/tests/test_database.py +++ b/api/tests/test_database.py @@ -1,11 +1,15 @@ from conftest import BaseTest +# from mock import patch +from temp_env_var import (CAPTCHA_TEST_RESPONSE_TOKEN, ERC20_TOKEN_ADDRESS, + ERC20_TOKEN_AMOUNT, NATIVE_TOKEN_ADDRESS, + NATIVE_TOKEN_AMOUNT, NATIVE_TRANSFER_TX_HASH, + TEMP_ENV_VARS, TOKEN_TRANSFER_TX_HASH, ZERO_ADDRESS) from api.services.database import AccessKey from api.utils import generate_access_key class TestDatabase(BaseTest): - def test_models(self, client): access_key_id, secret_access_key = generate_access_key() assert len(access_key_id) == 16