Skip to content

afdafczl/BeyondBoss

Repository files navigation

Beyond Boss💫

Copyright © 2024 Beyond


1. Introduction👨‍💻

The job hunter's workbench.


2. Git Guide 📖

2.1 Branch Description

2.1.1 Branch List

There are five types of branch:

  • main: Current stable version of the project.
  • release: Next version in development.
  • dev: Workspace for the next version.
  • ${name}: Each developer has his/her own branch.
  • ${tag}-${description}: For separate feature or bug-fix, e.g. feature--add-cache.

2.1.2 Push Order

  1. Developers should be on his/her own branch for daily development. You should remember to pull from dev regularly.

  2. Once a change is done, push from ${name} to $dev branch, must be sure that this update is runnable.

  3. When dev branch passed test, the leader of the team should push it to release branch.

  4. Finally, when release branch is ready to go, the leader should then push it to master branch.

All push and pull should follow the convention mentioned in the following section.

2.2 Commit Convention

To make commit more consistent, you MUST follow the commit convention. There should better be fewer changes in one commit.

'Fewer' doesn't mean fewer lines of code, but fewer functions.

Generally, commit should be lower-case, and no final period punctuation.

2.2.1 feat

To introduce a new feature.

feat: add User model

2.2.2 update

Not to introduce a new feature, but only made updates to an old one.

update: add extra-check for username

This can also be used when new files are added or removed. The 'add' or 'remove' action must be included.

update: add website favicon
update: remove register.py

2.2.3 fix

It indicates a bug is fixed.

fix: invalid login password ignored

2.2.4 refactor

It indicates that changes are made, but no affect to the function.

refactor: divide views.py to multiple files

2.2.5 trivial

It indicates this is a trivial change. Such commit should not include any change to the business logic.

trivial: correct spelling error

3. Environment🏞️

3.1 Basic Environment

Environment is based on Python 3.10 using conda.

For remote development, refer to Configure Remote Interpreter in PyCharm.

3.2 Troubleshoot

Some packages may not be easy to install, here are some hints.

3.2.1 django-cors-headers

conda install -c conda-forge django-cors-headers

4. Project Structure🎯

4.1 Applications

Applications have their own packages, and common infrastructures are in shared package.

4.2 Naming Convention

4.2.1 Views

See user app for example.

In any application, do not use the default views.py, instead, create a endpoints.py for all endpoints. Also, do use urls.py to wrap all APIs.

All functions in endpoints.py are request handlers, and should be named with _ suffix to avoid naming conflict. (I hate Django.)

You can directly write login in endpoints.py, but for complicated logics, create a services.py and move logics there.

4.2.2 Models

See user/models.py for example.

Model class should be in Camel Case.

Models must have a static create method as constructor to hide concrete construction.

Models must have a Meta class, with at least db_table field set. db_table should be in snake_case.

4.3.3 Request

See user app for example.

We have provided some graceful decorators to handle parameter problems. So please follow the best practice.

For more information, see Chapter 5 below.

4.3.4 Response

Make sure to wrap response with our unified classes.

You should return different status code for different errors, see Http Status Code. Use OkResponse, UnautorizedResponse or else to wrap them, and use OkDto, UnauthorizedDto, ErrorDto or else to wrap the payload.

For more information, see Chapter 5 below.


5. Best Practice⚒️

This section lists some of the best practices to follow when you contribute to this project.

Before you continue, make sure you understand Chapter 4.

5.1 Graceful Request Handling

See user app for example.

5.1.1 Data Transfer Object (DTO)

If there are too many request parameters, or the parameters need some more validation, use DTO.

Make sure your DTO class inherit this base class. You can overwrite these two methods if necessary. With this and @with_dto decorator, you can simply complete parameter validation, and get a well-formed request data.

class BaseRequestDto:
    def cleanup(self):
        return self

    def validate(self) -> bool:
        return True

5.2.2 Views

Use @api_view to specify the request method, these are in endpoints.py.

# e.g.
@api_view(['POST'])
def register_(request):
    pass

5.2.3 Request Parameters

DO NOT parse params like primitives! Use decorators provided in shared/decorators.

For complicated parameters:

# e.g.
@with_dto(RegisterDto)
def register_(request, dto: RegisterDto):
    pass

For flexible complicated parameters:

@with_dict_params()
def update_user_info_(request, params: dict):
    pass

For simple parameters without type:

Will only check existence, not type. It may be an int or a string, or None. Better use this for optional parameters.

#e.g.
@with_typed_params(["uid", "pid"])
def get_user_info_(request, uid, pid):
    pass

For simple parameters with type:

Will check type of parameter, and try to make casting, e.g. str to int.

# e.g.
@with_typed_params([("uid", int)])
def refresh_jwt_token_(request, uid: int):
    pass

For request that need authentication:

If not authorized, will return UnauthorizedResponse with the specified message prematurely.

# e.g.
@with_auth()
def update_user_info_(request, auth: AuthView):
    pass

For files in request:

This will extract files from request of form-data.

@with_file(["file"])
def update_user_avatar_(request, file):
    pass

For a single cookie value:

May not be used that often.

@with_cookie(key="refresh_token")
def refresh_jwt_token_(request, cookie: str):
    pass

Decorators can co-exists, so you can use multiple decorators at the same time.

5.2.4 Logging

Make sure to log important events in service. Use @with_logger decorator to inject logger to the service.

# e.g.
@api_view(['GET'])
@with_user("Login first")
@with_logger()
def get_logging_status_service(request, user, logger: Logger):
    logger.info(f"User {user.id} checked logging status")
    return OkResponse(OkDto("Congratulations, you are logged in!"))

Five levels for logging: debug, info, warning, error, critical.

Only warning and above will be logged in production.

5.2 Unified Response

Do not directly use response provided by Django it self. Use our well-defined response instead.

See shared/dtos/ordinary_response_dto.py for all base response DTOs, and shared/response/json_response.py for response wrappers.

By default, the response DTO only takes a message. If data is available, use data= to specify it. And if data is too complex, define a class for it.

# e.g.
data = GenerateTokenSuccessData(uid, jwt_token, refresh_token.expires)
return OkResponse(OkDto(data=data))

# e.g.
return BadRequestResponse(BadRequestDto("Bad requets", data={
    "expect": "uid",
    "got": "id"
}))

5.3 Comments

Write comments for each function and class you write.

For services, just description is acceptable, for example:

def register_service(request, dto):
    """
    Register a new user.
    """
    pass

For other functions, specify parameters and return value. It can be generated as you type three quotes. For example:

def generate_jwt_token_pair(uid: int):
    """
    Generate JWT token and refresh token. Will save the refresh token
    in cache.
    :param uid: The user id for which we will generate the token.
    :return: A tuple of (jwt_token, refresh_token).
    """
    pass

English comments are preferred. If use Chinese, make sure your file encoding is UTF-8.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages