Commit 62e5907c by Javier

Documentación

parent 15b848e5
...@@ -13,7 +13,32 @@ from pydantic import BaseModel ...@@ -13,7 +13,32 @@ from pydantic import BaseModel
import mbilling.functions import mbilling.functions
app = FastAPI() description = """
Servicio API para la interacción con el Tarficador de Telefonía ☎️
## Account
Endpoints para la consulta, creación y modificación de cuentas SIP y sus atributos.
"""
tags_metadata = [
{
"name": "Account",
"description": "Consulta, creación y modificación de cuentas SIP y sus atributos.",
},
]
app = FastAPI(
title='API Magnus Billing',
description=description,
version='0.0.1 Beta',
contact={
'name': 'Javier Ramírez',
'email': 'javier@rentelwifi.com'
},
openapi_tags=tags_metadata
)
class Rate(str, Enum): class Rate(str, Enum):
...@@ -22,10 +47,12 @@ class Rate(str, Enum): ...@@ -22,10 +47,12 @@ class Rate(str, Enum):
PLANA_1000 = "Tarifa Plana 1000 Minutos" PLANA_1000 = "Tarifa Plana 1000 Minutos"
PLANA_2000 = "Tarifa Plana 2000 Minutos" PLANA_2000 = "Tarifa Plana 2000 Minutos"
class Tax(float, Enum): class Tax(float, Enum):
GENERAL = 0.21 GENERAL = 0.21
LIBRE = 0.0 LIBRE = 0.0
class Refill(str, Enum): class Refill(str, Enum):
AUTOMATICA = 'Recarga Automática' AUTOMATICA = 'Recarga Automática'
LIQUIDACION = 'Liquidación Mensual de consumos Postpago' LIQUIDACION = 'Liquidación Mensual de consumos Postpago'
...@@ -33,6 +60,7 @@ class Refill(str, Enum): ...@@ -33,6 +60,7 @@ class Refill(str, Enum):
TRASPASO = 'Traspaso de Fondos desde A2Billing' TRASPASO = 'Traspaso de Fondos desde A2Billing'
INICIAL = 'Recarga Inicial' INICIAL = 'Recarga Inicial'
class Account(BaseModel): class Account(BaseModel):
"""_summary_ """_summary_
...@@ -59,21 +87,22 @@ class Account(BaseModel): ...@@ -59,21 +87,22 @@ class Account(BaseModel):
default=None, example='192.168.2.71', title='Centralita', description='Dirección IP de la Centralita.' default=None, example='192.168.2.71', title='Centralita', description='Dirección IP de la Centralita.'
) )
class AccountDetails(BaseModel): class AccountDetails(BaseModel):
number: str = Body( number: str = Body(
..., regex=r'[89][1-9]\d{7}', example='856135158', title='Número', description='Identificador de entrada y salidas de Llamadas' ..., regex=r'[89][1-9]\d{7}', example='856135158', title='Número', description='Identificador de entrada y salidas de Llamadas'
) )
password : str = Body( password: str = Body(
..., title='Contraseña', description='Credencial Cuenta SIP para host dinámico.' ..., title='Contraseña', description='Credencial Cuenta SIP para host dinámico.'
) )
host : str = Body( host: str = Body(
..., title='Host', description='Dirección desde la que se conecta el usuario.' ..., title='Host', description='Dirección desde la que se conecta el usuario.'
) )
credit : float = Body( credit: float = Body(
..., title='Saldo Disponible', description='Adeudo o activo que tiene el cliente.' ..., title='Saldo Disponible', description='Adeudo o activo que tiene el cliente.'
) )
...@@ -83,12 +112,12 @@ class AccountDetails(BaseModel): ...@@ -83,12 +112,12 @@ class AccountDetails(BaseModel):
class Config: class Config:
schema_extra = { schema_extra = {
"example" : { "example": {
"number" : "856135158", "number": "856135158",
"password" : "rentel58", "password": "rentel58",
"host": "192.168.2.71", "host": "192.168.2.71",
"credit": 32.45, "credit": 32.45,
"rate" : Rate.PLANA_2000 "rate": Rate.PLANA_2000
} }
} }
...@@ -98,34 +127,34 @@ class ConnectionData(BaseModel): ...@@ -98,34 +127,34 @@ class ConnectionData(BaseModel):
""" """
account : str = Body( account: str = Body(
..., title='Cuenta', description='Usuario/Número de cuenta para Autentificación SIP.' ..., title='Cuenta', description='Usuario/Número de cuenta para Autentificación SIP.'
) )
password : str = Body( password: str = Body(
..., title='Contraseña', description='Credencial para la Autentificación.' ..., title='Contraseña', description='Credencial para la Autentificación.'
) )
peer_details : str = Body( peer_details: str = Body(
default=None, title='Peer Details', description='Credenciales para la salida por troncal en Centralitas.' default=None, title='Peer Details', description='Credenciales para la salida por troncal en Centralitas.'
) )
user_details : str = Body( user_details: str = Body(
default=None, title='User Context', description='Credenciales para la entrada por troncal en Centralitas.' default=None, title='User Context', description='Credenciales para la entrada por troncal en Centralitas.'
) )
class Config: class Config:
schema_extra = { schema_extra = {
"example" : { "example": {
"account" : "856135158", "account": "856135158",
"password" : "rentel58", "password": "rentel58",
"peer_details": "type = friend username = 951203503 host = 192.168.2.84 nat = force_rport, comedia qualify = yes insecure = invite, port", "peer_details": "type = friend username = 951203503 host = 192.168.2.84 nat = force_rport, comedia qualify = yes insecure = invite, port",
"user_details": "type = friend host = 192.168.2.84 nat = force_rport, comedia qualify = yes insecure = invite, port" "user_details": "type = friend host = 192.168.2.84 nat = force_rport, comedia qualify = yes insecure = invite, port"
} }
} }
def encode_rate(rate : Rate = Rate.ESTANDAR) -> Tuple[int,int]:
def encode_rate(rate: Rate = Rate.ESTANDAR) -> Tuple[int, int]:
# Tarifa por Defecto # Tarifa por Defecto
plan_id = 1 plan_id = 1
offer_id = 0 offer_id = 0
...@@ -142,7 +171,8 @@ def encode_rate(rate : Rate = Rate.ESTANDAR) -> Tuple[int,int]: ...@@ -142,7 +171,8 @@ def encode_rate(rate : Rate = Rate.ESTANDAR) -> Tuple[int,int]:
return plan_id, offer_id return plan_id, offer_id
def decode_rate(plan_id : int, offer_id : int = 0) -> Rate:
def decode_rate(plan_id: int, offer_id: int = 0) -> Rate:
if plan_id == 1: if plan_id == 1:
return Rate.ESTANDAR return Rate.ESTANDAR
elif plan_id == 2: elif plan_id == 2:
...@@ -150,15 +180,15 @@ def decode_rate(plan_id : int, offer_id : int = 0) -> Rate: ...@@ -150,15 +180,15 @@ def decode_rate(plan_id : int, offer_id : int = 0) -> Rate:
elif plan_id == 4: elif plan_id == 4:
return Rate.PLANA_1000 if offer_id == 4 else Rate.PLANA_2000 return Rate.PLANA_1000 if offer_id == 4 else Rate.PLANA_2000
@app.post('/account', response_model=ConnectionData, name='Añadir Cuenta SIP', description='Genera una nueva cuenta en el Tarificador')
@app.post('/account', response_model=ConnectionData, name='Añadir Cuenta SIP', description='Genera una nueva cuenta en el Tarificador', tags=['Account'])
def create_account(account: Account = Depends()): def create_account(account: Account = Depends()):
"""Genera una nueva cuenta SIP con los datos proporcionados. """Genera una nueva cuenta SIP con los datos proporcionados.
Args: Args:
account (Account, optional): Modelo de Cuenta. Defaults to Depends(). account (Account, optional): Modelo de Cuenta. Defaults to Depends().
"""
"""
plan_id, offer_id = encode_rate(account.rate) plan_id, offer_id = encode_rate(account.rate)
...@@ -174,7 +204,8 @@ def create_account(account: Account = Depends()): ...@@ -174,7 +204,8 @@ def create_account(account: Account = Depends()):
) )
if not account.pbx_address: if not account.pbx_address:
response = ConnectionData(account=account.number, password=password) response = ConnectionData(
account=account.number, password=password)
else: else:
response = ConnectionData( response = ConnectionData(
account=account.number, account=account.number,
...@@ -186,8 +217,8 @@ def create_account(account: Account = Depends()): ...@@ -186,8 +217,8 @@ def create_account(account: Account = Depends()):
except mbilling.functions.MagnusCommandError as e: except mbilling.functions.MagnusCommandError as e:
response = JSONResponse( response = JSONResponse(
content={ content={
'error' : 'Se ha producido un error en Magnus durante la ejecución del comando.', 'error': 'Se ha producido un error en Magnus durante la ejecución del comando.',
'description' : str(e), 'description': str(e),
}, },
status_code=400 status_code=400
) )
...@@ -195,7 +226,7 @@ def create_account(account: Account = Depends()): ...@@ -195,7 +226,7 @@ def create_account(account: Account = Depends()):
return response return response
@app.get('/account/{account_name}', response_model=AccountDetails) @app.get('/account/{account_name}', response_model=AccountDetails, tags=['Account'])
def get_account(account_name: str = Query(..., regex=r'[89][1-9]\d{7}', name='Número', description='Cuenta SIP a recargar')): def get_account(account_name: str = Query(..., regex=r'[89][1-9]\d{7}', name='Número', description='Cuenta SIP a recargar')):
user_data = mbilling.functions.get_user(account_name) user_data = mbilling.functions.get_user(account_name)
...@@ -208,32 +239,34 @@ def get_account(account_name: str = Query(..., regex=r'[89][1-9]\d{7}', name='N ...@@ -208,32 +239,34 @@ def get_account(account_name: str = Query(..., regex=r'[89][1-9]\d{7}', name='N
credit=user_data['credit'], credit=user_data['credit'],
rate=decode_rate(user_data['plan'], user_data['offer']) rate=decode_rate(user_data['plan'], user_data['offer'])
) )
else: else:
response = JSONResponse( response = JSONResponse(
content={ content={
'result' : 'ERROR', 'result': 'ERROR',
'description' : 'No existe ningún usuario con la identificación solicitada.' 'description': 'No existe ningún usuario con la identificación solicitada.'
}, status_code=404 }, status_code=404
) )
return response return response
@app.post('/account/{account_name}/balance', name='Añade saldo a una cuenta SIP', description='Realiza una recarga en nombre del cliente.') @app.post('/account/{account_name}/balance', name='Añade saldo a una cuenta SIP', description='Realiza una recarga en nombre del cliente.', tags=['Account'])
def add_balance(account_name: str = Query(..., regex=r'[89][1-9]\d{7}', name='Número', description='Cuenta SIP a recargar'), def add_balance(account_name: str = Query(..., regex=r'[89][1-9]\d{7}', name='Número', description='Cuenta SIP a recargar'),
quantity: float=Query(default=10.00, name='Cantidad', description='Total a ingresar incluyendo impuestos'), quantity: float = Query(
tax : Tax = Query(default=Tax.GENERAL, name='Impuesto', description='Tasa impositiva a reducir del monto.'), description : Refill = default=10.00, name='Cantidad', description='Total a ingresar incluyendo impuestos'),
Query(default=Refill.AUTOMATICA, name='Tipo', description='Clase de Recarga') ): tax: Tax = Query(default=Tax.GENERAL, name='Impuesto', description='Tasa impositiva a reducir del monto.'), description: Refill =
Query(default=Refill.AUTOMATICA, name='Tipo', description='Clase de Recarga')):
if quantity >= 0 and tax >= 0: if quantity >= 0 and tax >= 0:
try: try:
mbilling.functions.add_balance(account_name, quantity, tax if description != Refill.TRASPASO else 0.0, description) mbilling.functions.add_balance(
account_name, quantity, tax if description != Refill.TRASPASO else 0.0, description)
response = JSONResponse( response = JSONResponse(
content={ content={
'result' : 'OK', 'result': 'OK',
'description' : 'Se ha realizado una recarga de {quantity} al usuario {account}.'.format( 'description': 'Se ha realizado una recarga de {quantity} al usuario {account}.'.format(
quantity=quantity, account=account_name quantity=quantity, account=account_name
) )
}, status_code=201 }, status_code=201
...@@ -241,18 +274,17 @@ def add_balance(account_name: str = Query(..., regex=r'[89][1-9]\d{7}', name='N ...@@ -241,18 +274,17 @@ def add_balance(account_name: str = Query(..., regex=r'[89][1-9]\d{7}', name='N
except mbilling.functions.MagnusCommandError as e: except mbilling.functions.MagnusCommandError as e:
response = JSONResponse( response = JSONResponse(
content={ content={
'result' : 'ERROR', 'result': 'ERROR',
'description' : f'El usuario no ha sido localizado, {str(e)}' 'description': f'El usuario no ha sido localizado, {str(e)}'
}, status_code=500 }, status_code=500
) )
else: else:
response = JSONResponse( response = JSONResponse(
content={ content={
'result' : 'ERROR', 'result': 'ERROR',
'description' : 'La cantidad y tasa impositiva han de ser mayores que 0.' 'description': 'La cantidad y tasa impositiva han de ser mayores que 0.'
}, status_code=400 }, status_code=400
) )
return response return response
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment