Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,3 @@ jobs:
run: pip install -r ./gringotts/requirements.txt
- name: Install worker libs
run: pip install -r ./worker/requirements.txt
- name: Run tests
run: pytest ./tests/gringotts
env:
PYTHONDONTWRITEBYTECODE: 1
PYTHONUNBUFFERED: 1
POSTGRES_HOST: 127.0.0.1
RABBITMQ_HOST: 127.0.0.1
RABBITMQ_PASSWORD: admin
RABBITMQ_USER: admin
POSTGRES_USER: user
POSTGRES_PASSWORD: secret
POSTGRES_DB: testdb
POSTGRES_TEST_DB: testdb
APPRAISAL_QUEUE: 'test_appraisal_requests'
APPRAISAL_ROUTING_KEY: 'test_appraisal_requests'

4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"python.linting.enabled": true
"python.linting.enabled": true,
"digma.hideFramesOutsideWorkspace": true,
"digma.enableNotifications": true
}
7 changes: 7 additions & 0 deletions docker-compose.seed.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
version: '3'

services:
gt-rabbitmq:
container_name: gt-rabbitmq-internal

gt-vault-db:
container_name: gt-vault-db-internal
gt-seed-data:
build:
context: .
Expand All @@ -9,6 +14,8 @@ services:
- POSTGRES_HOST=gt-vault-db
- RABBITMQ_HOST=gt-rabbitmq
- OTLP_EXPORTER_URL=host.docker.internal:4317
depends_on:
- gt-vault-api
env_file:
- .env
command: bash -c "
Expand Down
6 changes: 3 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ services:

gt-rabbitmq:
image: rabbitmq:3-management-alpine
profiles: [ "develop","standalone","test" ]
profiles: [ "develop","standalone","test","seed" ]
container_name: gt-rabbitmq
volumes:
- rabbitmq_gt_data:/var/lib/rabbitmq/
Expand All @@ -21,7 +21,7 @@ services:
retries: 3

gt-vault-db:
profiles: [ "develop","standalone","test" ]
profiles: [ "develop","standalone","test","seed" ]
container_name: gt-vault-db
environment:
POSTGRES_USER: ${POSTGRES_USER}
Expand Down Expand Up @@ -68,7 +68,7 @@ services:
build:
context: .
dockerfile: ./gringotts/Dockerfile
profiles: ["standalone", "test"]
profiles: ["standalone", "test", "seed"]
container_name: gt-vault-api
depends_on:
- gt-vault-db
Expand Down
52 changes: 43 additions & 9 deletions gringotts/api/vault_service.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,40 @@
from typing import Optional
import requests

from fastapi import APIRouter, Depends, status, HTTPException
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from opentelemetry import trace
from sqlalchemy.ext.asyncio import AsyncSession
from starlette.responses import PlainTextResponse

from gringotts.domain.vault_appraisal import get_latest_vault_appraisal, LedgerExpiredException, request_vault_appraisal
from gringotts.authentication.keysmith import create_access_token
from gringotts.database import get_db
from gringotts.domain.vault_authorization import authenticate_vault_owner_and_key, CreatureNotAuthenticatedException, \
get_owner_access, VaultOwnerAccess, authorize_vault_owner_vault_access
from gringotts.schemas.authentication import AuthenticationRequest, TokenResponse, TokenData
from gringotts.schemas.vault_balance import VaultBalanceResponse, VaultAppraisalRequest
from gringotts.domain.vault_appraisal import (LedgerExpiredException,
get_latest_vault_appraisal,
request_vault_appraisal)
from gringotts.domain.vault_authorization import (
CreatureNotAuthenticatedException, VaultOwnerAccess,
authenticate_vault_owner_and_key, authorize_vault_owner_vault_access,
get_owner_access)
from gringotts.schemas.authentication import (AuthenticationRequest, TokenData,
TokenResponse)
from gringotts.schemas.vault_balance import (VaultAppraisalRequest,
VaultBalanceResponse)

router = APIRouter(prefix="/gringotts/vaults")
tracer = trace.get_tracer(__name__)


@router.get("/example1", status_code=status.HTTP_200_OK)
async def method1():
with tracer.start_as_current_span("span2"):
requests.get('https://xkcd.com/1906/')

with tracer.start_as_current_span("span3"):
requests.get('https://xkcd.com/1906/')



@router.post("/token", response_model=TokenResponse, status_code=status.HTTP_200_OK)
async def authenticate_form(form_data: OAuth2PasswordRequestForm = Depends(), db_session: AsyncSession = Depends(get_db)):
try:
Expand All @@ -37,16 +54,19 @@ async def authenticate_form(form_data: OAuth2PasswordRequestForm = Depends(), db


@router.post("/authenticate", response_model=TokenResponse, status_code=status.HTTP_200_OK)
async def authenticate_vault_key(payload: AuthenticationRequest, db_session: AsyncSession = Depends(get_db)):
async def authenticate_vault_key(payload: AuthenticationRequest,
db_session: AsyncSession = Depends(get_db)):
try:
await authenticate_vault_owner_and_key(db_session=db_session,
vault_owner=payload.vault_owner,
vault_key=payload.vault_key)
await authorize_vault_owner_vault_access(db_session, payload.vault_owner, payload.vault_number)
await authorize_vault_owner_vault_access(db_session, payload.vault_owner,
payload.vault_number)

except CreatureNotAuthenticatedException:
error_json = {"Unauthorized": f"User does not have permissions to access vault : {payload.vault_owner}"}
return PlainTextResponse(str(error_json), status_code=status.HTTP_401_UNAUTHORIZED)
return PlainTextResponse(str(error_json),
status_code=status.HTTP_401_UNAUTHORIZED)

token = create_access_token(TokenData(vault_owner=payload.vault_owner).__dict__)

Expand All @@ -55,6 +75,11 @@ async def authenticate_vault_key(payload: AuthenticationRequest, db_session: Asy
return token_response







@router.get("/appraisal", response_model=VaultBalanceResponse, status_code=status.HTTP_200_OK)
async def get_vault_appraisal(vault_id: str, muggle_currency_code: Optional[str] = None,
db_session: AsyncSession = Depends(get_db),
Expand All @@ -81,6 +106,15 @@ async def start_new_vault_appraisal(payload: VaultAppraisalRequest,
await request_vault_appraisal(payload.vault_id)











async def _ensure_authorized_to_vault(db_session, owner_access: VaultOwnerAccess, vault_id):
try:
await authorize_vault_owner_vault_access(db_session,owner_access.vault_owner,vault_id)
Expand Down
20 changes: 12 additions & 8 deletions gringotts/domain/vault_appraisal.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import asyncio
import datetime
import json
import logging
Expand All @@ -14,6 +15,7 @@
from gringotts.schemas.vault_balance import VaultBalanceResponse
logger = logging.getLogger(__name__)
tracer = trace.get_tracer(__name__)
lock = asyncio.Lock()

LEDGER_EXPIRATION_IN_DAYS = 10

Expand All @@ -33,14 +35,16 @@ class MuggleCurrencies(Enum):

async def get_muggle_exchange_rates(currency_code: str):
with tracer.start_as_current_span("Retrieving muggle exchange rate"):
async with AsyncClient(
headers={"Content-Type": "application/json"},
) as client:
response = await client.get(get_settings().muggle_exchange_api)
exchange_rates_list = json.loads(response.text)
muggle_exchange_rate = \
next((e for e in exchange_rates_list if e["currency_code"] == currency_code.upper()), None)
return muggle_exchange_rate
async with lock:
async with AsyncClient(
headers={"Content-Type": "application/json"},
) as client:
await asyncio.sleep(1)
response = await client.get(get_settings().muggle_exchange_api)
exchange_rates_list = json.loads(response.text)
muggle_exchange_rate = \
next((e for e in exchange_rates_list if e["currency_code"] == currency_code.upper()), None)
return muggle_exchange_rate


def ledger_expired(ledger: VaultLedger) -> bool:
Expand Down
37 changes: 28 additions & 9 deletions gringotts/domain/vault_authorization.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import asyncio
import logging
import threading

from fastapi import Depends
from opentelemetry import trace
Expand Down Expand Up @@ -43,9 +42,23 @@ async def authorize_vault_owner_vault_access(db_session: AsyncSession, vault_own
await _ensure_owns_requested_vault(owner, vault_id)













async def authenticate_vault_owner_and_key(db_session: AsyncSession, vault_owner: str, vault_key: str ):

with tracer.start_as_current_span("Authenticate vault owner and key"):
# Owner is known
# await asyncio.sleep(6)

owner = await _ensure_owner_exists(db_session, vault_owner)

await _ensure_owner_has_a_vault(owner)
Expand Down Expand Up @@ -77,17 +90,22 @@ async def _ensure_key_matches_records(db_session, vault_key: str):


async def _ensure_owner_has_a_vault(owner):
# Vault requested is the one that belongs to the requestor
vault_id = owner.vault_id
if not vault_id:
raise CreatureNotAuthenticatedException(f"Specified vault_id {vault_id} doesn't exist")
return vault_id
# Vault requested is the one that belongto the requestor
with tracer.start_as_current_span("Ensure_owner_has_a_vault"):
vault_id = owner.vault_id
if not vault_id:
raise CreatureNotAuthenticatedException(
f"Specified vault_id {vault_id} doesn't exist")
return vault_id


async def _ensure_owner_exists(db_session, vault_owner:str):
owner: VaultOwner = await VaultOwner.find(username=vault_owner, db_session=db_session)
owner: VaultOwner = await VaultOwner.find(username=vault_owner,
db_session=db_session)
print("owner" + owner.name)
if not owner:
raise CreatureNotAuthenticatedException(f"Creature {vault_owner} isn't a registered owner")
raise CreatureNotAuthenticatedException(
f"Creature {vault_owner} isn't a registered owner")
return owner


Expand All @@ -96,9 +114,10 @@ async def _search_for_key_record(db_session: AsyncSession, key: str):

with tracer.start_as_current_span("Retrieiving the key record"):
vaults = await Vault.all(db_session)
found_key = None
found_key = None
for vault in vaults:
vault: Vault
all_vaults = await Vault.all(db_session)
vault_key = await VaultKey.find(db_session, vault.vault_key_id)
if vault_key.key == key:
found_key = key
Expand Down
2 changes: 1 addition & 1 deletion gringotts/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

resource = Resource.create(attributes={SERVICE_NAME: 'vault_service'})
resource = DigmaConfiguration().trace_this_package()\
.set_environment("Dev").resource.merge(resource)
.set_environment("CI").resource.merge(resource)
exporter = OTLPSpanExporter(endpoint=get_settings().otlp_exporter_url, insecure=True)
provider = TracerProvider(resource=resource)
provider.add_span_processor(BatchSpanProcessor(exporter))
Expand Down
8 changes: 8 additions & 0 deletions gringotts/models/vault_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,11 @@ async def find(cls, db_session: AsyncSession, key: str):
instance = result.scalars().first()
return instance

with tracer.start_as_current_span('Test new span'):
stmt = select(cls).where(cls.key == key)
result = await db_session.execute(stmt)
instance = result.scalars().first()
return instance



2 changes: 1 addition & 1 deletion observability/tracing/docker-compose.trace.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ services:
depends_on:
- jaeger
environment:
- OTLP_EXPORTER_DIGMA_COLLECTOR_API=digma-collector-api:5050
- OTLP_EXPORTER_DIGMA_COLLECTOR_API=host.docker.internal:5050

networks:
default:
Expand Down
5 changes: 5 additions & 0 deletions requests.http
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ content-type: application/json

GET http://localhost:8283/gringotts/vaults/appraisal?vault_id=713
Authorization: Bearer token_here

###


GET http://localhost:8283/gringotts/vaults/example1 HTTP/1.1