first commit

This commit is contained in:
2026-02-13 17:00:52 -06:00
commit 07a6d7069f
49 changed files with 763 additions and 0 deletions

0
README.md Normal file
View File

4
run.sh Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/bash
source /home/rodo/Documents/examen-mss/netflix/bin/activate
cd /home/rodo/Documents/examen-mss/src/
python3 main.py

34
script.sh Executable file
View File

@@ -0,0 +1,34 @@
#!/bin/bash
CURRENT_DIR=$(pwd)
mkdir $CURRENT_DIR/src
mkdir $CURRENT_DIR/src/{application,core,domain,infrastructure}
mkdir $CURRENT_DIR/src/application/{ports,services}
mkdir $CURRENT_DIR/src/infrastructure/{adapters,api}
mkdir $CURRENT_DIR/src/infrastructure/adapters/persistence
touch $CURRENT_DIR/src/main.py
echo "Here goes all repositories, each repo will implement the object they need and defines all methods as abstracts" > $CURRENT_DIR/src/application/ports/here.txt
echo "Here we will use all repositores and will define as classes all functions we will implement as execute method" > $CURRENT_DIR/src/application/services/here.txt
echo "Here we will define all the configurations for the multiple api's we're going to use" > $CURRENT_DIR/src/core/here.txt
echo "Here we will define the entities that we will operate with, normally as dataclasses because we won't define methods here" > $CURRENT_DIR/src/domain/here.txt
echo "Here we will define all the database parts, db will define the connection and session, models will define what is going to be in the database and the sql repository will define all the methods that will be executed in the database, these are called by the api." > $CURRENT_DIR/src/infrastructure/adapters/persistence/here.txt
echo "Here we will define each api application in each directory, we can use router to make things easier and scallable" > $CURRENT_DIR/src/infrastructure/api/here.txt
# == Architecture dependencies == #
python3 -m venv $CURRENT_DIR/netflix
source $CURRENT_DIR/netflix/bin/activate
pip install uvicorn fastapi pydantic pydantic-settings sqlalchemy
printf "#!/bin/bash \ncd $(pwd)/src/ \npython3 main.py\n" > $CURRENT_DIR/run.sh
chmod +x $CURRENT_DIR/run.sh

Binary file not shown.

View File

@@ -0,0 +1,2 @@
class MicroserviceAlreadyExists(Exception):
pass

View File

@@ -0,0 +1,19 @@
from abc import ABC, abstractmethod
from domain.MicroservicioA import MicroservicioA
class MicroservicioARepository(ABC):
@abstractmethod
def save(self, msa: MicroservicioA):
...
@abstractmethod
def viewAll(self) -> list[MicroservicioA]:
...
@abstractmethod
def viewById(self, msa_id: int):
...
@abstractmethod
def remove(self, msa_id: int):
...
@abstractmethod
def editById(self, msa_id: int, msa: MicroservicioA):
...

View File

@@ -0,0 +1,19 @@
from abc import ABC, abstractmethod
from domain.MicroservicioB import MicroservicioB
class MicroservicioBRepository(ABC):
@abstractmethod
def save(self, msb: MicroservicioB):
...
@abstractmethod
def viewAll(self) -> list[MicroservicioB]:
...
@abstractmethod
def viewById(self, msb_id: int):
...
@abstractmethod
def remove(self, msb_id: int):
...
@abstractmethod
def editById(self, msb_id:int, msb: MicroservicioB):
...

View File

@@ -0,0 +1,4 @@
from infrastructure.adapters.persistence.db import engine
from infrastructure.adapters.persistence.models import Base
Base.metadata.create_all(bind=engine)

View File

@@ -0,0 +1,57 @@
from application.ports.microservicio_a_repository import MicroservicioARepository
from domain.MicroservicioA import MicroservicioA
class Save:
def __init__(self, repo: MicroservicioARepository):
if not isinstance(repo, MicroservicioARepository):
raise TypeError("MicroservicioARepository must be a class instance")
self.repo = repo
def execute(self, perfiles: str, idioma: str, calidad: str):
msa = MicroservicioA(msa_id=0,perfiles=perfiles, idioma=idioma,calidad=calidad)
self.repo.save(msa)
return msa
class ViewAllMsa:
def __init__(self, repo: MicroservicioARepository):
if not isinstance(repo, MicroservicioARepository):
raise TypeError("MicroservicioARepository must be a class instance")
self.repo = repo
def execute(self):
msa_s = self.repo.viewAll()
return msa_s
class ViewById:
def __init__(self, repo: MicroservicioARepository):
if not isinstance(repo, MicroservicioARepository):
raise TypeError("MicroservicioARepository must be a class instance")
self.repo = repo
def execute(self, msa_id: int) -> MicroservicioA | None:
msa = self.repo.viewById(msa_id)
return msa
class Remove:
def __init__(self, repo: MicroservicioARepository):
if not isinstance(repo, MicroservicioARepository):
raise TypeError("MicroservicioARepository must be a class instance")
self.repo = repo
def execute(self, msa_id:int):
msa = self.repo.remove(msa_id)
return msa
class EditById:
def __init__(self, repo: MicroservicioARepository):
if not isinstance(repo, MicroservicioARepository):
raise TypeError("MicroservicioARepository must be a class instance")
self.repo = repo
def execute(self, msa_id: int, msa: MicroservicioA):
return self.repo.editById(msa_id=msa_id, msa=msa)

View File

@@ -0,0 +1,56 @@
from application.ports.microservicio_b_repository import MicroservicioBRepository
from domain.MicroservicioB import MicroservicioB
class Save:
def __init__(self, repo: MicroservicioBRepository):
if not isinstance(repo, MicroservicioBRepository):
raise TypeError("MicroservicioBRepository must be a class instance")
self.repo = repo
def execute(self, metodoPagoPredeterminado: str, historialFacturas: str, fechaProximoPago: str):
msb = MicroservicioB(msb_id=0, metodoPagoPredeterminado=metodoPagoPredeterminado, historialFacturas=historialFacturas, fechaProximoPago=fechaProximoPago)
self.repo.save(msb)
return msb
class ViewAllMsb:
def __init__(self, repo: MicroservicioBRepository):
if not isinstance(repo, MicroservicioBRepository):
raise TypeError("MicroservicioBRepository must be a class instance")
self.repo = repo
def execute(self):
msb_s = self.repo.viewAll()
return msb_s
class ViewById:
def __init__(self, repo: MicroservicioBRepository):
if not isinstance(repo, MicroservicioBRepository):
raise TypeError("MicroservicioBRepository must be a class instance")
self.repo = repo
def execute(self, msb_id: int) -> MicroservicioB | None:
msb = self.repo.viewById(msb_id)
return msb
class Remove:
def __init__(self, repo: MicroservicioBRepository):
if not isinstance(repo, MicroservicioBRepository):
raise TypeError("MicroservicioBRepository must be a class instance")
self.repo = repo
def execute(self, msb_id:int):
msb = self.repo.remove(msb_id)
return msb
class EditById:
def __init__(self, repo: MicroservicioBRepository):
if not isinstance(repo, MicroservicioBRepository):
raise TypeError("MicroservicioBRepository must be a class instance")
self.repo = repo
def execute(self, msb_id: int, msb: MicroservicioB):
return self.repo.editById(msb_id=msb_id, msb=msb)

Binary file not shown.

23
src/core/config.py Normal file
View File

@@ -0,0 +1,23 @@
from pydantic_settings import BaseSettings
from pydantic import Field
class Settings(BaseSettings):
reload: bool = True
host: str = "0.0.0.0"
log_level:str = "info"
database_url: str = Field(default="sqlite:///./database.db", description="database")
# API A
api_a_title: str = "Microservicio A API"
api_a_version:str= "1.0.0"
api_a_description:str = "Catálogo y reproducción de contenido"
api_a_port: int = 8001
# API B
api_b_title: str = "Microservicio B API"
api_b_version:str= "1.0.0"
api_b_description:str = "Encargado de recomendaciones y \"Mi Lista\""
api_b_port: int = 8002
ConfSettings = Settings()

BIN
src/database.db Normal file

Binary file not shown.

View File

@@ -0,0 +1,8 @@
from dataclasses import dataclass
@dataclass
class MicroservicioA:
msa_id: int
perfiles: str
idioma: str
calidad: str

View File

@@ -0,0 +1,8 @@
from dataclasses import dataclass
@dataclass
class MicroservicioB:
msb_id: int
metodoPagoPredeterminado: str
historialFacturas: str
fechaProximoPago: str

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,16 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
from core.config import ConfSettings
engine = create_engine(
ConfSettings.database_url,
connect_args={"check_same_thread": False} if "sqlite" in ConfSettings.database_url else {}
)
SessionLocal = sessionmaker(
bind=engine,
autocommit=False,
autoflush=False
)
Base = declarative_base()

View File

@@ -0,0 +1,99 @@
from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError
from domain.MicroservicioA import MicroservicioA
from application.ports.microservicio_a_repository import MicroservicioARepository
from infrastructure.adapters.persistence.db import SessionLocal
from infrastructure.adapters.persistence.models import MsaModel
from application.exceptions import MicroserviceAlreadyExists
class SqlMicroservicioARepository(MicroservicioARepository):
def __init__(self):
self.db: Session = SessionLocal()
def save(self, msa: MicroservicioA):
model = MsaModel(
perfiles=msa.perfiles,
idioma=msa.idioma,
calidad=msa.calidad
)
self.db.add(model)
try:
self.db.commit()
except IntegrityError:
self.db.rollback()
raise MicroserviceAlreadyExists("There's already one registry with that data.")
self.db.refresh(model)
return MicroservicioA(
msa_id = model.msa_id,
perfiles=model.perfiles,
idioma=model.idioma,
calidad=model.calidad
)
def viewAll(self) -> list[MicroservicioA]:
models = self.db.query(MsaModel).all()
return [
MicroservicioA(
msa_id=model.msa_id,
perfiles=model.perfiles,
idioma=model.idioma,
calidad=model.calidad
) for model in models
]
def viewById(self, msa_id: int) -> MicroservicioA | None:
model = self.db.query(MsaModel).filter(MsaModel.msa_id == msa_id).first()
if not model:
return None
return MicroservicioA(
msa_id=model.msa_id,
perfiles=model.perfiles,
idioma=model.idioma,
calidad=model.calidad
)
def remove(self, msa_id: int):
model = self.db.query(MsaModel).filter(MsaModel.msa_id == msa_id).first()
if not model:
return False
try:
self.db.delete(model)
self.db.commit()
return True
except IntegrityError:
self.db.rollback()
raise MicroserviceAlreadyExists("There's already one registry with that data.")
def editById(self, msa_id: int, msa: MicroservicioA) -> MicroservicioA | None:
model = self.db.query(MsaModel).filter(MsaModel.msa_id == msa_id).first()
if not model:
return None
try:
model.perfiles = msa.perfiles
model.idioma = msa.idioma
model.calidad = msa.calidad
self.db.commit()
self.db.refresh(model)
return MicroservicioA(
msa_id=model.msa_id,
perfiles=model.perfiles,
idioma=model.idioma,
calidad=model.calidad
)
except IntegrityError:
self.db.rollback()
raise MicroserviceAlreadyExists("There's already one registry with that data.")

View File

@@ -0,0 +1,99 @@
from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError
from domain.MicroservicioB import MicroservicioB
from application.ports.microservicio_b_repository import MicroservicioBRepository
from infrastructure.adapters.persistence.db import SessionLocal
from infrastructure.adapters.persistence.models import MsbModel
from application.exceptions import MicroserviceAlreadyExists
class SqlMicroservicioBRepository(MicroservicioBRepository):
def __init__(self):
self.db: Session = SessionLocal()
def save(self, msb: MicroservicioB):
model = MsbModel(
metodoPagoPredeterminado=msb.metodoPagoPredeterminado,
historialFacturas=msb.historialFacturas,
fechaProximoPago=msb.fechaProximoPago
)
self.db.add(model)
try:
self.db.commit()
except IntegrityError:
self.db.rollback()
raise MicroserviceAlreadyExists("There's already one registry with that data.")
self.db.refresh(model)
return MicroservicioB(
msb_id = model.msb_id,
metodoPagoPredeterminado=model.metodoPagoPredeterminado,
historialFacturas=model.historialFacturas,
fechaProximoPago=model.fechaProximoPago
)
def viewAll(self) -> list[MicroservicioB]:
models = self.db.query(MsbModel).all()
return [
MicroservicioB(
msb_id=model.msb_id,
metodoPagoPredeterminado=model.metodoPagoPredeterminado,
historialFacturas=model.historialFacturas,
fechaProximoPago=model.fechaProximoPago
) for model in models
]
def viewById(self, msb_id: int) -> MicroservicioB | None:
model = self.db.query(MsbModel).filter(MsbModel.msb_id == msb_id).first()
if not model:
return None
return MicroservicioB(
msb_id=model.msb_id,
metodoPagoPredeterminado=model.metodoPagoPredeterminado,
historialFacturas=model.historialFacturas,
fechaProximoPago=model.fechaProximoPago
)
def remove(self, msb_id: int):
model = self.db.query(MsbModel).filter(MsbModel.msb_id == msb_id).first()
if not model:
return False
try:
self.db.delete(model)
self.db.commit()
return True
except IntegrityError:
self.db.rollback()
raise MicroserviceAlreadyExists("There's already one registry with that data.")
def editById(self, msb_id: int, msb: MicroservicioB) -> MicroservicioB | None:
model = self.db.query(MsbModel).filter(MsbModel.msb_id == msb_id).first()
if not model:
return None
try:
model.metodoPagoPredeterminado = msb.metodoPagoPredeterminado
model.historialFacturas = msb.historialFacturas
model.fechaProximoPago = msb.fechaProximoPago
self.db.commit()
self.db.refresh(model)
return MicroservicioB(
msb_id=model.msb_id,
metodoPagoPredeterminado=model.metodoPagoPredeterminado,
historialFacturas=model.historialFacturas,
fechaProximoPago=model.fechaProximoPago
)
except IntegrityError:
self.db.rollback()
raise MicroserviceAlreadyExists("There's already one registry with that data.")

View File

@@ -0,0 +1,19 @@
from sqlalchemy import Column, Integer, String, Float, DateTime
from infrastructure.adapters.persistence.db import Base
class MsaModel(Base):
__tablename__ = "microservicioa"
msa_id = Column(Integer, primary_key=True, index=True)
perfiles = Column(String, unique=False, index=True)
idioma = Column(String, unique=True, index=True)
calidad = Column(String, unique=True, index=True)
class MsbModel(Base):
__tablename__ = "microserviciob"
msb_id = Column(Integer, primary_key=True, index=True)
metodoPagoPredeterminado = Column(String, unique=False, index=True)
historialFacturas = Column(String, unique=False, index=True)
fechaProximoPago = Column(String, unique=False, index=True)

View File

@@ -0,0 +1,10 @@
from fastapi import FastAPI
from core.config import ConfSettings
from infrastructure.api.microservicio_a.router import router
def create_app() -> FastAPI:
app = FastAPI(title=ConfSettings.api_a_title,
version=ConfSettings.api_a_version,
description=ConfSettings.api_a_description)
app.include_router(router)
return app

View File

@@ -0,0 +1,79 @@
from fastapi import APIRouter, HTTPException
from application.services.microservicio_a_service import Save, ViewAllMsa, ViewById, Remove, EditById
from infrastructure.adapters.persistence.microservicio_a_repository_sql import SqlMicroservicioARepository
from domain.MicroservicioA import MicroservicioA
router = APIRouter()
@router.post("/")
def create_msa(perfiles: str, idioma: str, calidad: str):
service = Save(SqlMicroservicioARepository())
try:
return service.execute(perfiles, idioma, calidad)
except Exception as e:
raise HTTPException(
status_code=400,
detail=str(e)
)
@router.get("/")
def view_all_msa():
service = ViewAllMsa(SqlMicroservicioARepository())
try:
return service.execute()
except Exception as e:
raise HTTPException(
status_code=500,
detail=str(e)
)
@router.get("/{msa_id}")
def view_msa_by_id(msa_id: int):
service = ViewById(SqlMicroservicioARepository())
result = service.execute(msa_id)
if result is None:
raise HTTPException(
status_code=404,
detail="Microservice not found"
)
return result
@router.delete("/{msa_id}")
def delete_msa_by_id(msa_id: int):
service = Remove(SqlMicroservicioARepository())
result = service.execute(msa_id)
if result is None:
raise HTTPException(
status_code=404,
detail="Microservice not found"
)
return result
@router.put("/{msa_id}")
def edit_msa_by_id(msa_id: int, perfiles: str, idioma: str, calidad: str):
service = EditById(SqlMicroservicioARepository())
try:
msa = MicroservicioA(msa_id=msa_id, perfiles=perfiles, idioma=idioma, calidad=calidad)
result = service.execute(msa_id=msa_id, msa=msa)
except Exception as e:
raise HTTPException(
status_code=500,
detail=str(e)
)
if result is None:
raise HTTPException(
status_code=404,
detail="Microservice not found"
)
return result

View File

@@ -0,0 +1,16 @@
from fastapi import APIRouter
import platform
router = APIRouter()
@router.get("/")
def root():
uname = platform.uname()
return {
"machine": {
"OS": f"{uname.system} {uname.release}",
"Arch": uname.machine
},
"Status": "Running",
"Docs": "/docs"
}

View File

@@ -0,0 +1,17 @@
from fastapi import APIRouter
from infrastructure.api.microservicio_a.microservices import router as orders_router
from infrastructure.api.microservicio_a.root import router as root_router
router = APIRouter()
router.include_router(
orders_router,
prefix="/microservicio_a",
tags=["microservicio a"]
)
router.include_router(
root_router,
prefix='',
tags=["root"]
)

View File

@@ -0,0 +1,10 @@
from fastapi import FastAPI
from core.config import ConfSettings
from infrastructure.api.microservicio_b.router import router
def create_app() -> FastAPI:
app = FastAPI(title=ConfSettings.api_b_title,
version=ConfSettings.api_b_version,
description=ConfSettings.api_b_description)
app.include_router(router)
return app

View File

@@ -0,0 +1,79 @@
from fastapi import APIRouter, HTTPException
from application.services.microservicio_b_service import Save, ViewAllMsb, ViewById, Remove, EditById
from infrastructure.adapters.persistence.microservicio_b_repository_sql import SqlMicroservicioBRepository
from domain.MicroservicioB import MicroservicioB
router = APIRouter()
@router.post("/")
def create_msb(metodoPagoPredeterminado: str, historialFacturas: str, fechaProximoPago: str):
service = Save(SqlMicroservicioBRepository())
try:
return service.execute(metodoPagoPredeterminado, historialFacturas, fechaProximoPago)
except Exception as e:
raise HTTPException(
status_code=400,
detail=str(e)
)
@router.get("/")
def view_all_msb():
service = ViewAllMsb(SqlMicroservicioBRepository())
try:
return service.execute()
except Exception as e:
raise HTTPException(
status_code=500,
detail=str(e)
)
@router.get("/{msb_id}")
def view_msb_by_id(msb_id: int):
service = ViewById(SqlMicroservicioBRepository())
result = service.execute(msb_id)
if result is None:
raise HTTPException(
status_code=404,
detail="Microservice not found"
)
return result
@router.delete("/{msb_id}")
def delete_msb_by_id(msb_id: int):
service = Remove(SqlMicroservicioBRepository())
result = service.execute(msb_id)
if result is None:
raise HTTPException(
status_code=404,
detail="Microservice not found"
)
return result
@router.put("/{msb_id}")
def edit_msb_by_id(msb_id: int, metodoPagoPredeterminado: str, historialFacturas: str, fechaProximoPago: str):
service = EditById(SqlMicroservicioBRepository())
try:
msb = MicroservicioB(msb_id=msb_id, metodoPagoPredeterminado=metodoPagoPredeterminado, historialFacturas=historialFacturas, fechaProximoPago=fechaProximoPago)
result = service.execute(msb_id=msb_id, msb=msb)
except Exception as e:
raise HTTPException(
status_code=500,
detail=str(e)
)
if result is None:
raise HTTPException(
status_code=404,
detail="Microservice not found"
)
return result

View File

@@ -0,0 +1,16 @@
from fastapi import APIRouter
import platform
router = APIRouter()
@router.get("/")
def root():
uname = platform.uname()
return {
"machine": {
"OS": f"{uname.system} {uname.release}",
"Arch": uname.machine
},
"Status": "Running",
"Docs": "/docs"
}

View File

@@ -0,0 +1,17 @@
from fastapi import APIRouter
from infrastructure.api.microservicio_b.microservices import router as microservices_router
from infrastructure.api.microservicio_b.root import router as root_router
router = APIRouter()
router.include_router(
microservices_router,
prefix="/microservicio_b",
tags=["microservicio b"]
)
router.include_router(
root_router,
prefix='',
tags=["root"]
)

52
src/main.py Normal file
View File

@@ -0,0 +1,52 @@
from infrastructure.api.microservicio_a.app import create_app as create_msa_app
from infrastructure.api.microservicio_b.app import create_app as create_msb_app
from core.config import ConfSettings
import threading
import uvicorn
def run_msa_api():
"""Ejecuta la API de usuarios en puerto 8000"""
app_msa_app = create_msa_app()
uvicorn.run(
app_msa_app,
host=ConfSettings.host,
port=ConfSettings.api_a_port,
reload=False,
log_level=ConfSettings.log_level,
)
def run_msb_api():
"""Ejecuta la API del microservicio B"""
app_msb_app = create_msb_app()
uvicorn.run(
app_msb_app,
host=ConfSettings.host,
port=ConfSettings.api_b_port,
reload=False,
log_level=ConfSettings.log_level,
)
def run():
"""Inicia ambas APIs en threads separados"""
print("Iniciando microservicios...")
msa_thread = threading.Thread(target=run_msa_api, daemon=True, name="Microservice-A-API")
msb_thread = threading.Thread(target=run_msb_api, daemon=True, name="Microservice-B-API")
msa_thread.start()
msb_thread.start()
print("API del microservicio A ejecutándose en http://0.0.0.0:" + str(ConfSettings.api_a_port))
print("API del microservicio B ejecutándose en http://0.0.0.0:" + str(ConfSettings.api_b_port))
print("\nDocumentación disponible en:")
print(" - Microservicio A: http://localhost:" + str(ConfSettings.api_a_port) + "/docs")
print(" - Microservicio B: http://localhost:" + str(ConfSettings.api_b_port) + "/docs")
try:
msa_thread.join()
msb_thread.join()
except KeyboardInterrupt:
print("\n\nAteniendo señal de salida...")
if __name__ == "__main__":
run()