Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
58e31fa
chore: upgrade to Django 6 and PyJWT 2.x, fix strawberry extension cr…
AswinJ1 May 16, 2026
1415ab0
chore: upgrade to Django 6 and PyJWT 2.x, fix strawberry extension cr…
AswinJ1 Jun 9, 2026
28f2524
Delete screencapture-github-traboda-chowkidar-compare-main-traboda-ch…
AswinJ1 Jun 10, 2026
a72e9db
bump(requirements): bumped up the version of strawberry-graphql to 0.…
seshagiriprabhu Aug 5, 2026
9f6c37e
fix: update JWTAuthExtension for newer strawberry-graphql hook pattern
seshagiriprabhu Aug 5, 2026
932e636
chore(tooling): add ruff, pre-commit config and dev extras
seshagiriprabhu Aug 5, 2026
d6f51fd
style: apply ruff formatting and fix whitespace across project
seshagiriprabhu Aug 5, 2026
9079bc0
docs: add numpy-style docstrings across all modules
seshagiriprabhu Aug 5, 2026
ef5be96
refactor(utils): extract context and validation into separate modules
seshagiriprabhu Aug 5, 2026
afdd8a8
fix: use sufficiently long HMAC key and add type hints to auth functions
seshagiriprabhu Aug 5, 2026
66e69f8
chore(tooling): add pytest-cov and coverage config to setup.cfg
seshagiriprabhu Aug 5, 2026
beda86e
test: add comprehensive test suite with 100% coverage and security ed…
seshagiriprabhu Aug 5, 2026
7bae8ae
chore: Added vscode, ai-prompts, and coverage reports to gitignore
seshagiriprabhu Aug 5, 2026
c4adf25
feat(ci): Added lint and unit testing workflows
seshagiriprabhu Aug 5, 2026
e787149
feat(ci): Added lint and unit testing workflows
seshagiriprabhu Aug 5, 2026
dc5d770
fix(setup): move install_requires from [metadata] to [options]
seshagiriprabhu Aug 5, 2026
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
35 changes: 35 additions & 0 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: RUFF Check

on:
pull_request:
workflow_dispatch:
push:
branches:
- "main"

jobs:
ruff:
name: Code quality check using RUFF (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
timeout-minutes: 5
strategy:
matrix:
python-version: ["3.13", "3.14"]

steps:
- name: Check out source repository
uses: actions/checkout@v7

- name: Set up Python environment
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}

- uses: astral-sh/ruff-action@v4.1.0
name: Set up RUFF

- name: Analyze code using RUFF
run: ruff check

- name: Check RUFF formatter needs to be run on any files
run: ruff format --check
68 changes: 68 additions & 0 deletions .github/workflows/unit-testing.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Django Unit Testing

on:
pull_request:
workflow_dispatch:
push:
branches:
- "main"

env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
POSTGRES_HOST: postgres
POSTGRES_PORT: 5432
DEBUG: False

jobs:
django-unit-tests:
name: Run Tests and Measure Coverage (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
timeout-minutes: 5
strategy:
matrix:
python-version: ["3.13", "3.14"]
permissions:
contents: write
checks: write
pull-requests: write

steps:
- name: Check out source repository
uses: actions/checkout@v7

- name: Set up Python environment
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
set -e
python -m pip install --upgrade pip
pip install -e .[dev]

- name: Run tests
id: run_tests
run: pytest --cov=chowkidar --cov-report=xml --cov-report=term-missing

- name: Create code coverage comment
uses: orgoro/coverage@v3.2
id: code_coverage_comment
if: always()
with:
coverageFile: coverage.xml
token: ${{ secrets.GITHUB_TOKEN }}
thresholdAll: 0.0
thresholdNew: 0.0
thresholdModified: 0.0
sourceDir: .

- name: Check test results
if: always()
run: |
if [[ "${{ steps.run_tests.outcome }}" == "failure" ]]; then
echo "Tests failed!"
exit 1
fi
21 changes: 20 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
.idea
chowkidar_strawberry.egg-info
dist
dist
# Virtual Environment
.venv/
venv/

# Python Cache
__pycache__/
*.pyc

# Testing
.pytest_cache/

# Environment Variables
.coverage
.coverage.*
coverage.xml
report.xml
htmlcov/
.vscode/
ai-prompts/
39 changes: 39 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
default_install_hook_types: [pre-commit, pre-push]
repos:
- repo: https://github.com/commitizen-tools/commitizen
rev: v4.15.0
hooks:
- id: commitizen
stages: [commit-msg]

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: check-yaml
- id: check-added-large-files
- id: check-merge-conflict
- id: end-of-file-fixer
exclude: "^(migrations/)"
- id: trailing-whitespace
exclude: "^(migrations/)"
- id: no-commit-to-branch
args: ["--branch", "main"]
- id: check-toml

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.12
hooks:
- id: ruff
args: [--fix]
- id: ruff-format

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.12
hooks:
- id: ruff
name: Run ruff linter
args: [--fix]
stages: [pre-commit]
- id: ruff-format
name: Run ruff formatter
stages: [pre-commit]
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -670,4 +670,4 @@ into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
<https://www.gnu.org/licenses/why-not-lgpl.html>.
30 changes: 15 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ class AuthMutations:
```

All your resolvers will now get the following parameters from `info.context` -
- `info.context.userID` - ID of the requesting user, None if not logged-in
- `info.context.userID` - ID of the requesting user, None if not logged-in
- `info.context.refreshToken`- Refresh token string of the requesting user, None if not logged-in

Chowkidar comes with 3 authentication methods (importable from `chowkidar.authentication`), which you may use -
Expand All @@ -109,13 +109,13 @@ Chowkidar comes with 3 authentication methods (importable from `chowkidar.authen
You can use these decorators

1. `@login_required` - wrap your resolver with this decorator to ensure the resolver is called only for logged-in users.

```python
from chowkidar.decorators import login_required

@strawberry.type
class Query:

@strawberry.field
@login_required
def movies(self, info) -> List[MovieType]:
Expand All @@ -129,15 +129,15 @@ from chowkidar.decorators import resolve_user

@strawberry.type
class Mutation:

@strawberry.mutation
@resolve_user
def create_movie(self, name: str, info) -> List[MovieType]:
if not info.context.user.is_superuser:
raise Exception("Only superusers can create movies")

# Note: Like you see here, for most queryset operations you can use - user_id=info.context.userID, without needing any decorator or hitting the DB.
return Movie.objects.create(name=name, user_id=info.context.userID)
return Movie.objects.create(name=name, user_id=info.context.userID)

```

Expand All @@ -147,13 +147,13 @@ class Mutation:
class RefreshToken(AbstractRefreshToken, models.Model):
ip = models.GenericIPAddressField(null=True, blank=True)
userAgent = models.CharField(max_length=255, null=True, blank=True)

def process_request_before_save(self, request: HttpRequest):
# set IP from the request
from ipware import get_client_ip
ip, is_routable = get_client_ip(request)
self.ip = ip

# set user agent from the request
agent = None
if "User-Agent" in request.headers:
Expand Down Expand Up @@ -200,24 +200,24 @@ JWT_ISSUER: str = None

## How it Works?

- Uses short-lived stateless JWT Access Token set as cookie to authenticate users. An additional, long-running stateful
JWT Refresh Token, that is recorded in RefreshToken model, is also generated to automatically to allow refreshing /
- Uses short-lived stateless JWT Access Token set as cookie to authenticate users. An additional, long-running stateful
JWT Refresh Token, that is recorded in RefreshToken model, is also generated to automatically to allow refreshing /
generating new access token when expired. This process is fully managed automatically at the backend. For issuing
new access token using a existing refresh token, the refresh token is validated against the DB. For all other requests,
the DB is not hit, but access key is simply validated against its key.
- `settings.py` enlists various configuration options for this plugin. The default values are set to work out of the box
with minimal configuration. You can override these values in your project's `settings.py` to customize the behavior.
- Uses a [custom Strawberry Extension](https://strawberry.rocks/docs/guides/extensions) to read JWT cookies from the
request, for validation, and auto issuing new access token using refresh token when available. Also sets up
- Uses a [custom Strawberry Extension](https://strawberry.rocks/docs/guides/extensions) to read JWT cookies from the
request, for validation, and auto issuing new access token using refresh token when available. Also sets up
`info.context.userID` for easy access to the authenticated user's ID in resolvers. This extension is valid throughout
the resolving period of the GraphQL request, although auth is processed before actual query execution. This is defined
the resolving period of the GraphQL request, although auth is processed before actual query execution. This is defined
in `extensions.py`.
- Uses a wrapper function that wraps the GraphQLView to manage cookies. Data for the cookies is sent to this function
via setting custom attribute in `request` object from `extensions.py`. This function executes after GraphQL has been
fully processed and http response is ready. This is defined in `view.py`.
- Consumer applications can custom write login/logout mutations, by wrapping those with `@issue_tokens_on_login` and
- Consumer applications can custom write login/logout mutations, by wrapping those with `@issue_tokens_on_login` and
`@revoke_tokens_on_logout` decorators. These are defined in `wrappers.py`
- Consumer APIs can decorate auth requiring resolvers with `@login_required` (or `@resolve_user`), as well as get
- Consumer APIs can decorate auth requiring resolvers with `@login_required` (or `@resolve_user`), as well as get
get the ID of the requesting user from `info.context.userID`. The decorators are defined in `decorators.py`.


Expand Down
Loading
Loading