20240926

I got a weird error running a workflow in GitHub Actions

Error response from daemon: Head "https://registry-1.docker.io/v2/library/postgres/manifests/16-alpine": unauthorized: incorrect username or password

I googled it, but could not find any relevant web pages. Somehow, retrying after 20 minutes worked. Very Strange.

The other day, Dependabot would not perform actios such as rebasing.

Those kinds of anomalies seem to happen from time to time.

Repository Design Pattern

Unfortunately, I left GoF at my parents' house and cannot check it. Instead, I browsed some web pages.

I'm forced to decode Java code and had some doubts on the design pattern. There are so many Repository classes that some of my brain cells were killed instantly.

As a Python programmer, it's kind of rare to encounter this pattern.

Repository Design Pattern - GeeksforGeeks

The Repository Design Pattern is a software design pattern that acts as an intermediary layer between an application's business logic and data storage.

Its primary purpose is to provide a structured and standardized way to access, manage, and manipulate data while abstracting the underlying details of data storage technologies.

This pattern promotes a clear separation of concerns, making software more maintainable, testable, and adaptable to changes in data sources, without entangling the core application logic with data access intricacies.

In essence, the Repository Design Pattern is a blueprint for organizing and simplifying data access, enhancing the efficiency and flexibility of software systems.

Advantages of Repository Design Pattern

It sounds familiar… I feel that Dependency Injection's advantage is the same.

Disadvantages of Repository Design Pattern

Use Cases for Repository Design Pattern

It just makes sense. I now remember all the patterns with Python.

Example with FastAPI

In FastAPI, it is a very common pattern to have intermediate functions whose responsibility is to read data from database.

fastapi/full-stack-fastapi-template

For example, functions to CRUD user records are defined in crud.py.

def create_user(*, session: Session, user_create: UserCreate) -> User:
    db_obj = User.model_validate(
        user_create, update={"hashed_password": get_password_hash(user_create.password)}
    )
    session.add(db_obj)
    session.commit()
    session.refresh(db_obj)
    return db_obj


def get_user_by_email(*, session: Session, email: str) -> User | None:
    statement = select(User).where(User.email == email)
    session_user = session.exec(statement).first()
    return session_user

Now, the functions are used in creating API endpoints

@router.post(
    "/", dependencies=[Depends(get_current_active_superuser)], response_model=UserPublic
)
def create_user(*, session: SessionDep, user_in: UserCreate) -> Any:
    """
    Create new user.
    """
    user = crud.get_user_by_email(session=session, email=user_in.email)
    if user:
        raise HTTPException(
            status_code=400,
            detail="The user with this email already exists in the system.",
        )

    user = crud.create_user(session=session, user_create=user_in)
    if settings.emails_enabled and user_in.email:
        email_data = generate_new_account_email(
            email_to=user_in.email, username=user_in.email, password=user_in.password
        )
        send_email(
            email_to=user_in.email,
            subject=email_data.subject,
            html_content=email_data.html_content,
        )
    return user

By doing this, it's easier to test the API endpoint because we only need to mock crud.get_user_by_email(). If we write db logic in the API endpint, it would be a nightmare to mock.

Also, we can improve portability; now we only need to update the database operations in repositories when migrating to a new database. I don't think it often happens tho.


Puchao 200 Chicken wings 600 Sushi bowl 800 Protein shake 200

Total 1800 kcal


MUST:

TODO:


index 20240925 20240927