Skip to content

Commit

Permalink
1.3.0 Release branch (#47)
Browse files Browse the repository at this point in the history
* Added authorisation header to BasicKeyEncoder. pre-commit chores. (#40)

Co-authored-by: Del Hyman-Jones <DelboyJay@users.noreply.github.com>

* CLE-1613 django42 python311 support (#46)

* Fix test cases

* Update tox to run python version from 3.7 to 3.11 (3.12 is not supported fully yet)
Add django testing for 4.1 and 4.2
Add djangorestframework testing for 3.14

* update poetry packages

* update github workflows and actions

* update pre-commit config to latest versions
Fix linting

* Update README.md

* formatted files

* Updated CHANGES.md

* version to 1.3.0
added readme for pypi description and homepage link

* Fix release file

* fix linting

---------

Co-authored-by: Artem <tyomo4ka@users.noreply.github.com>
Co-authored-by: Stuart Bradley <stuy.bradley@gmail.com>
  • Loading branch information
3 people committed May 2, 2023
1 parent 29e30a9 commit 9507c02
Show file tree
Hide file tree
Showing 23 changed files with 1,129 additions and 938 deletions.
6 changes: 1 addition & 5 deletions .bumpversion.cfg
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 1.2.0
current_version = 1.3.0
commit = False
tag = False

Expand All @@ -10,7 +10,3 @@ replace = version = "{new_version}"
[bumpversion:file:Makefile]
search = django-idempotency-key-{current_version}.tar.gz
replace = django-idempotency-key-{new_version}.tar.gz

[bumpversion:file:.github/workflows/release.yml]
search = uses: d1618033/gh-action-python-publish-using-poetry@{current_version}
replace = uses: d1618033/gh-action-python-publish-using-poetry@{new_version}
12 changes: 6 additions & 6 deletions .github/actions/lint/action.yml
@@ -1,13 +1,13 @@
name: "Django Idempotency Key Tests"
name: "Linting"
runs:
using: "composite"
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- name: Set up Python 3.10.0
uses: actions/setup-python@v2
- name: Set up Python 3.11
uses: actions/setup-python@v4
with:
python-version: "3.10"
python-version: "3.11"

- name: pre-commit
uses: pre-commit/action@v2.0.3
uses: pre-commit/action@v3.0.0
6 changes: 3 additions & 3 deletions .github/actions/tests/action.yml
Expand Up @@ -7,10 +7,10 @@ runs:
using: "composite"
steps:
- name: Check out repository
uses: actions/checkout@v2
uses: actions/checkout@v3

- name: Set up Python ${{ inputs.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: ${{ inputs.python-version }}

Expand All @@ -25,7 +25,7 @@ runs:
- name: tox tests for python 3.7
shell: bash
if: ${{inputs.python-version == '3.7'}}
run: tox -e django22-drf38,django22-drf39,django22-drf310,django22-drf311,django22-drf312,django22-drf313,django32-drf311,django32-drf312,django32-drf313
run: tox -e py37-django22-drf38,py37-django22-drf39,py37-django22-drf310,py37-django22-drf311,py37-django22-drf312,py37-django22-drf313,py37-django32-drf311,py37-django32-drf312,py37-django32-drf313

- name: tox tests for python >= 3.8
shell: bash
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/continuous_integration.yml
Expand Up @@ -6,19 +6,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v2
uses: actions/checkout@v3

- uses: ./.github/actions/lint

test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10"]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
fail-fast: false
steps:
- name: Check out repository
uses: actions/checkout@v2
uses: actions/checkout@v3

- uses: ./.github/actions/tests
with:
Expand Down
34 changes: 5 additions & 29 deletions .github/workflows/release.yml
Expand Up @@ -2,40 +2,16 @@ name: Release

on:
release:
types: [created]
types:
- created

jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v2

- uses: ./.github/actions/lint

test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10"]
fail-fast: false
steps:
- name: Check out repository
uses: actions/checkout@v2

- uses: ./.github/actions/tests
with:
python-version: ${{ matrix.python-version }}

publish:
needs: [test, lint]
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v2

uses: actions/checkout@v3
- name: Build and publish
uses: d1618033/gh-action-python-publish-using-poetry@0.1.1
uses: JRubics/poetry-publish@v1.16
with:
pypi_username: '__token__'
pypi_password: ${{ secrets.PYPI_TOKEN }}
pypi_token: ${{ secrets.PYPI_TOKEN }}
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Expand Up @@ -11,13 +11,13 @@ repos:
- id: black

- repo: https://github.com/PyCQA/flake8
rev: 5.0.4
rev: 6.0.0
hooks:
- id: flake8
additional_dependencies: [flake8-bugbear]

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.2.0
rev: v4.4.0
hooks:
- id: check-added-large-files
- id: check-ast
Expand Down
8 changes: 8 additions & 0 deletions CHANGES.md
Expand Up @@ -20,6 +20,14 @@ updating.
**[Breaking changes]** - There are changes that break existing compatibility.

---
# 1.3.0
**[Dropped support]**

**[Added support]**
- Added support for Python 3.11
- Added support for Django 4.1 and Django 4.2
- Added HTTP_AUTHORIZATION header when generating encoded key for extra security. (Submitted by Artem)

# 1.2.0
**[Dropped support]**

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -35,7 +35,7 @@ bundle: static_analysis coverage
poetry build

release-test:
poetry run twine upload --repository-url https://test.pypi.org/legacy/ dist/django-idempotency-key-1.2.0.tar.gz
poetry run twine upload --repository-url https://test.pypi.org/legacy/ dist/django_idempotency_key-1.3.0.tar.gz

release: static_analysis coverage
poetry run twine upload dist/*
Expand Down
26 changes: 13 additions & 13 deletions README.md
Expand Up @@ -21,20 +21,20 @@ nothing new is created.

Django idempotency key requires the following:

Python (3.7, 3.8, 3.9, 3.10)
Python (3.7 to 3.11)

Django (2.2, 3.2, 4.0)
Django (2.2, 3.2, 4.0, 4.1, 4.2)

NOTE: M1 Apple laptops are yet to be tested but are expected to work with this library.
The following is a list of Django versions and the respective
DjangoRestFramework and python versions that it supports.

The following is a list of DjangoRestFramework versions and the respective
Django and python versions that it supports.

Python | Django | DRF
--- | --- | ---
\>=3.7 | 2.2 | \>=3.8
\>=3.7 | 3.2 | \>=3.11
\>=3.8 | 4.0 | \>=3.13
Python | Django | DRF
--------|--------|--------
\>=3.7 | 2.2 | \>=3.8
\>=3.7 | 3.2 | \>=3.11
\>=3.8 | 4.0 | \>=3.13
\>=3.8 | 4.1 | \>=3.14
\>=3.8 | 4.2 | \>=3.14

## Installation

Expand All @@ -58,8 +58,8 @@ MIDDLEWARE = [
non-safe HTTP methods to supply an idempotency key specified in the request header
under HTTP_IDEMPOTENCY_KEY. If this is missing then a 400 BAD REQUEST is returned.

However, if you prefer that all view functions are exempt by default and you will
out-in on a per view function basis then use the following:
However, if you prefer that all view functions are exempt by default, and you will
opt in on a per view function basis then use the following:

```
MIDDLEWARE = [
Expand Down
24 changes: 14 additions & 10 deletions idempotency_key/decorators.py
Expand Up @@ -3,8 +3,8 @@
from idempotency_key import utils

# NOTE:
# The following decorators must be specified BEFORE the @api_view decorator or the function will not be marked
# correctly.
# The following decorators must be specified BEFORE the @api_view decorator or the
# function will not be marked correctly.
#
# i.e:
#
Expand All @@ -16,17 +16,20 @@

def idempotency_key(*args, optional=False, cache_name=None):
"""
Allows an optional cache name to be specified so that different cache settings can be used on a per-view function
basis.
:param args: optional arguments. This can contain the view function object if cache_name is not specified
Allows an optional cache name to be specified so that different cache settings can
be used on a per-view function basis.
:param args: optional arguments. This can contain the view function object if
cache_name is not specified
:param optional: Mark idempotency key header as optional
:param cache_name: The name of the cache to use from the settings file under CACHES={...}
:param cache_name: The name of the cache to use from the settings file under
CACHES={...}
:return: wrapped function
"""

def _idempotency_key(view_func):
"""
Mark a view function as requiring idempotency key protection but the view should control the response.
Mark a view function as requiring idempotency key protection but the view
should control the response.
"""

@wraps(view_func)
Expand All @@ -42,8 +45,8 @@ def wrapped_view(*args, **kwargs):

return wrapped_view

# if there is an argument passed and it is a callable then this will be the view function object so pass it to
# the wrapper
# if there is an argument passed and it is a callable then this will be the view
# function object so pass it to the wrapper
if len(args) > 0 and callable(args[0]):
return _idempotency_key(args[0])

Expand All @@ -65,7 +68,8 @@ def wrapped_view(*args, **kwargs):

def idempotency_key_manual(view_func):
"""
Mark a view function as requiring idempotency key protection but the view should control the response.
Mark a view function as requiring idempotency key protection but the view should
control the response.
"""

def wrapped_view(*args, **kwargs):
Expand Down
7 changes: 6 additions & 1 deletion idempotency_key/encoders.py
@@ -1,6 +1,8 @@
import abc
import hashlib

from django.http.request import HttpRequest

from idempotency_key.exceptions import MissingIdempotencyKeyError


Expand All @@ -11,7 +13,7 @@ def encode_key(self, request, key):


class BasicKeyEncoder(IdempotencyKeyEncoder):
def encode_key(self, request, key):
def encode_key(self, request: HttpRequest, key):
if key is None:
raise MissingIdempotencyKeyError()
# Basic method for generating an encoded key
Expand All @@ -20,4 +22,7 @@ def encode_key(self, request, key):
m.update(request.path_info.encode("UTF-8"))
m.update(request.method.encode("UTF-8"))
m.update(request.body)
if request.META.get("HTTP_AUTHORIZATION"):
m.update(request.META.get("HTTP_AUTHORIZATION").encode("UTF-8"))

return m.hexdigest()
10 changes: 6 additions & 4 deletions idempotency_key/storage.py
Expand Up @@ -32,8 +32,9 @@ def retrieve_data(self, cache_name: str, encoded_key: str) -> Tuple[bool, object
@abc.abstractmethod
def validate_storage(name: str):
"""
Validate that the storage name exists. If the class is using django `CACHES` setting then this function ensures
that the cache is setup correctly in the settings file and will cause a failure at startup if it is not.
Validate that the storage name exists. If the class is using django `CACHES`
setting then this function ensures that the cache is setup correctly in the
settings file and will cause a failure at startup if it is not.
This function should raise an exception if the storage name cannot be validated.
:param name: The name of the storage.
"""
Expand Down Expand Up @@ -73,6 +74,7 @@ def retrieve_data(self, cache_name: str, encoded_key: str) -> Tuple[bool, object

@staticmethod
def validate_storage(name: str):
# Check that the cache exists. If the cache is not found then an InvalidCacheBackendError is raised.
# Not that there is no get function on the caches object so we cannot perform a normal check.
# Check that the cache exists. If the cache is not found then an
# InvalidCacheBackendError is raised. Note that there is no get function on the
# caches object, so we cannot perform a normal check.
caches[name]
2 changes: 1 addition & 1 deletion idempotency_key/utils.py
Expand Up @@ -71,7 +71,7 @@ def get_lock_class():


def get_lock_location():
return get_lock_settings().get("LOCATION", "Redis://localhost:6379/1")
return get_lock_settings().get("LOCATION", "redis://localhost:6379/1")


def get_lock_timeout():
Expand Down

0 comments on commit 9507c02

Please sign in to comment.