Skip to content

Commit

Permalink
Add price endpoint (#22)
Browse files Browse the repository at this point in the history
* Add price endpoint
* Add dockerfile
* Configure github actions
  • Loading branch information
Uxio0 authored Nov 28, 2023
1 parent f7718d6 commit 3f07ea5
Show file tree
Hide file tree
Showing 48 changed files with 2,746 additions and 166 deletions.
7 changes: 7 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Test project outside docker
PYTHONPATH=/app/
DEBUG=0
DJANGO_SETTINGS_MODULE=config.settings.test
DJANGO_SECRET_KEY=t3st-s3cr3t#-!k3y
ETHEREUM_NODES_URLS=https://ethereum.publicnode.com,https://polygon-rpc.com
ETH_HASH_BACKEND=pysha3
8 changes: 8 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Test project outside docker
PYTHONPATH=/app/
DEBUG=0
DJANGO_SETTINGS_MODULE=config.settings.test
DJANGO_SECRET_KEY=t3st-s3cr3t#-!k3y
ETHEREUM_MAINNET_NODE=https://ethereum.publicnode.com
ETHEREUM_NODES_URLS=http://localhost:8545
ETH_HASH_BACKEND=pysha3
57 changes: 10 additions & 47 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Python CI
on:
push:
branches:
- master
- main
- develop
pull_request:
release:
Expand All @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10"]
python-version: ["3.11"]

steps:
- uses: actions/checkout@v4
Expand All @@ -30,38 +30,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10"]
services:
redis:
image: redis
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
postgres:
image: postgres:14
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
rabbitmq:
image: rabbitmq:alpine
options: >-
--health-cmd "rabbitmqctl await_startup"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- "5672:5672"
python-version: ["3.11"]
steps:
- name: Setup and run ganache
run: |
Expand All @@ -82,28 +51,22 @@ jobs:
- name: Run tests and coverage
run: |
python manage.py check
python manage.py makemigrations --check --dry-run
# python manage.py makemigrations --check --dry-run
coverage run --source=$SOURCE_FOLDER -m pytest -rxXs --reruns 3
env:
SOURCE_FOLDER: safe_price_service
CELERY_BROKER_URL: redis://localhost:6379/0
COINMARKETCAP_API_TOKEN: ${{ secrets.COINMARKETCAP_API_TOKEN }}
DATABASE_URL: psql://postgres:postgres@localhost/postgres
DJANGO_SETTINGS_MODULE: config.settings.test
ETHEREUM_MAINNET_NODE: ${{ secrets.ETHEREUM_MAINNET_NODE }}
ETHEREUM_NODE_URL: http://localhost:8545
ETHEREUM_TRACING_NODE_URL: http://localhost:8545
ETHEREUM_NODES_URLS: http://localhost:8545
ETH_HASH_BACKEND: pysha3
REDIS_URL: redis://localhost:6379/0
EVENTS_QUEUE_URL: amqp://guest:guest@localhost:5672/
- name: Coveralls
uses: coverallsapp/github-action@v2
docker-deploy:
runs-on: ubuntu-latest
needs:
- linting
- test-app
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' || (github.event_name == 'release' && github.event.action == 'released')
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' || (github.event_name == 'release' && github.event.action == 'released')
steps:
- uses: actions/checkout@v4
- uses: docker/setup-qemu-action@v3
Expand All @@ -115,8 +78,8 @@ jobs:
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Deploy Master
if: github.ref == 'refs/heads/master'
- name: Deploy main
if: github.ref == 'refs/heads/main'
uses: docker/build-push-action@v5
with:
context: .
Expand Down Expand Up @@ -159,11 +122,11 @@ jobs:
autodeploy:
runs-on: ubuntu-latest
needs: [docker-deploy]
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop'
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'
steps:
- uses: actions/checkout@v4
- name: Deploy Staging
if: github.ref == 'refs/heads/master'
if: github.ref == 'refs/heads/main'
run: bash scripts/autodeploy.sh
env:
AUTODEPLOY_URL: ${{ secrets.AUTODEPLOY_URL }}
Expand Down
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ venv.bak/

# Django
staticfiles/
safe_transaction_service/media
safe_price_service/media

### VisualStudioCode template
.vscode/*
Expand All @@ -117,7 +117,7 @@ safe_transaction_service/media
# Provided default Pycharm Run/Debug Configurations should be tracked by git
# In case of local modifications made by Pycharm, use update-index command
# for each changed file, like this:
# git update-index --assume-unchanged .idea/safe_transaction_service.iml
# git update-index --assume-unchanged .idea/safe_price_service.iml
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
Expand Down
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ repos:
hooks:
- id: isort
- repo: https://github.com/psf/black
rev: 22.12.0
rev: 23.11.0
hooks:
- id: black
- repo: https://github.com/PyCQA/flake8
rev: 6.0.0
rev: 6.1.0
hooks:
- id: flake8
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.5.0
hooks:
- id: check-docstring-first
- id: check-merge-conflict
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
![Build Status](https://github.com/safe-global/safe-price-service/workflows/Python%20CI/badge.svg?branch=master)
[![Coverage Status](https://coveralls.io/repos/github/safe-global/safe-price-service/badge.svg?branch=master)](https://coveralls.io/github/safe-global/safe-price-service?branch=master)
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit)
![Python 3.10](https://img.shields.io/badge/Python-3.10-blue.svg)
![Python 3.11](https://img.shields.io/badge/Python-3.11-blue.svg)
![Django 4](https://img.shields.io/badge/Django-4-blue.svg)
[![Docker Image Version (latest semver)](https://img.shields.io/docker/v/safeglobal/safe-price-service?label=Docker&sort=semver)](https://hub.docker.com/r/safeglobal/safe-price-service)

# Safe Price Service
Returns fiat prices for base currencies and ERC20 tokens.

## Configuration

Environment variables:
- `ETHEREUM_NODES_URLS`: Comma separated list of the node RPCS for the chains supported for fetching prices.
- `PRICES_CACHE_TTL_MINUTES`: Minutes to keep a price in cache.

## Contributors
[See contributors](https://github.com/safe-global/safe-price-service/graphs/contributors)
5 changes: 0 additions & 5 deletions config/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +0,0 @@
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery_app import app as celery_app

__all__ = ("celery_app",)
34 changes: 13 additions & 21 deletions config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,11 @@
SITE_ID = 1
# https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n
USE_I18N = True
# https://docs.djangoproject.com/en/dev/ref/settings/#use-l10n
USE_L10N = True
# https://docs.djangoproject.com/en/dev/ref/settings/#use-tz
USE_TZ = True
# https://docs.djangoproject.com/en/3.2/ref/settings/#force-script-name
FORCE_SCRIPT_NAME = env("FORCE_SCRIPT_NAME", default=None)

# Enable analytics endpoints
ENABLE_ANALYTICS = env("ENABLE_ANALYTICS", default=False)

# GUNICORN
GUNICORN_REQUEST_TIMEOUT = gunicorn_request_timeout
GUNICORN_WORKER_CONNECTIONS = gunicorn_worker_connections
Expand All @@ -58,23 +53,14 @@
# DATABASES
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#databases
DB_STATEMENT_TIMEOUT = env.int("DB_STATEMENT_TIMEOUT", 60_000)
# Project does not need a database for now
DATABASES = {
"default": env.db("DATABASE_URL"),
}
DATABASES["default"]["ATOMIC_REQUESTS"] = False
DATABASES["default"]["ENGINE"] = "django_db_geventpool.backends.postgresql_psycopg2"
DATABASES["default"]["CONN_MAX_AGE"] = 0
DB_MAX_CONNS = env.int("DB_MAX_CONNS", default=50)
DATABASES["default"]["OPTIONS"] = {
# https://github.com/jneight/django-db-geventpool#settings
"MAX_CONNS": DB_MAX_CONNS,
"REUSE_CONNS": env.int("DB_REUSE_CONNS", default=DB_MAX_CONNS),
"options": f"-c statement_timeout={DB_STATEMENT_TIMEOUT}",
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": ":memory:",
}
}

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

# URLS
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf
Expand Down Expand Up @@ -297,10 +283,16 @@
},
}

REDIS_URL = env("REDIS_URL", default="redis://localhost:6379/0")

SWAGGER_SETTINGS = {
"SECURITY_DEFINITIONS": {
"api_key": {"type": "apiKey", "in": "header", "name": "Authorization"}
},
}

# Ethereum
ETHEREUM_NODE_URL = env("ETHEREUM_NODE_URL", default=None)
ETHEREUM_NODES_URLS = env.list("ETHEREUM_NODES_URLS", default=[])


# Prices
PRICES_CACHE_TTL_MINUTES = env.int("PRICES_CACHE_TTL_MINUTES", default=60)
11 changes: 1 addition & 10 deletions config/settings/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,12 @@
# https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts
ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=["*"])

REDIS_URL = env.str("REDIS_URL")

# CACHES
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#caches
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": REDIS_URL,
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
# Mimicing memcache behavior.
# http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior
"IGNORE_EXCEPTIONS": True,
},
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
}
}

Expand Down
17 changes: 0 additions & 17 deletions config/settings/production.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,6 @@
# DATABASES['default'] = env.db('DATABASE_URL') # noqa F405
DATABASES["default"]["ATOMIC_REQUESTS"] = False # noqa F405

REDIS_URL = env.str("REDIS_URL")

# CACHES
# ------------------------------------------------------------------------------
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": REDIS_URL,
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
# Mimicing memcache behavior.
# http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior
"IGNORE_EXCEPTIONS": True,
},
}
}

# SECURITY
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/middleware/#x-content-type-options-nosniff
Expand Down
6 changes: 4 additions & 2 deletions config/settings/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@
# https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers
PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]

# Fix error with `task_id` when running celery in eager mode
LOGGING["formatters"]["celery_verbose"] = LOGGING["formatters"]["verbose"] # noqa F405
LOGGING["loggers"] = { # noqa F405
"safe_price_service": {
"level": "DEBUG",
}
}

ETHEREUM_TEST_PRIVATE_KEY = (
"6370fd033278c143179d81c5526140625662b8daa446c22ee2d73db3707e620c"
)
15 changes: 0 additions & 15 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,6 @@ services:
depends_on:
- web

redis:
image: redis:alpine
ports:
- "6379:6379"
command:
- --appendonly yes

db:
image: postgres:14-alpine
ports:
- "5432:5432"
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres

web:
build:
context: .
Expand Down
14 changes: 12 additions & 2 deletions docker/web/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.10-slim
FROM python:3.11-slim

ARG APP_HOME=/app
WORKDIR ${APP_HOME}
Expand Down Expand Up @@ -27,5 +27,15 @@ RUN set -ex \
-o \( -type f -a -name '*.pyc' -o -name '*.pyo' \) \
-exec rm -rf '{}' +

COPY . .
# /nginx mount point must be created before so it doesn't have root permissions
# ${APP_HOME} root folder will not be updated by COPY --chown, so permissions need to be adjusted
RUN groupadd -g 999 python && \
useradd -u 999 -r -g python python && \
mkdir -p /nginx && \
chown -R python:python /nginx ${APP_HOME}
COPY --chown=python:python . .

# Use numeric ids so kubernetes identifies the user correctly
USER 999:999

RUN DJANGO_SETTINGS_MODULE=config.settings.production DJANGO_DOT_ENV_FILE=.env.tracing.sample python manage.py collectstatic --noinput
22 changes: 0 additions & 22 deletions docker/web/Dockerfile_alpine

This file was deleted.

1 change: 0 additions & 1 deletion requirements-test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ factory-boy==3.3.0
faker==19.11.0
mypy==1.6.1
pytest==7.4.2
pytest-celery==0.0.0
pytest-django==4.5.2
pytest-env==1.0.1
pytest-rerunfailures==12.0
Expand Down
Loading

0 comments on commit 3f07ea5

Please sign in to comment.