fastapi-audit-recorder 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. fastapi_audit_recorder-0.1.0/.gitignore +6 -0
  2. fastapi_audit_recorder-0.1.0/.gitlab-ci.yml +58 -0
  3. fastapi_audit_recorder-0.1.0/LICENSE +21 -0
  4. fastapi_audit_recorder-0.1.0/MANIFEST.in +5 -0
  5. fastapi_audit_recorder-0.1.0/PKG-INFO +248 -0
  6. fastapi_audit_recorder-0.1.0/README.md +208 -0
  7. fastapi_audit_recorder-0.1.0/dev_server.py +123 -0
  8. fastapi_audit_recorder-0.1.0/fastapi_audit_recorder/__init__.py +17 -0
  9. fastapi_audit_recorder-0.1.0/fastapi_audit_recorder/audit_decorator.py +207 -0
  10. fastapi_audit_recorder-0.1.0/fastapi_audit_recorder/context.py +59 -0
  11. fastapi_audit_recorder-0.1.0/fastapi_audit_recorder/models.py +57 -0
  12. fastapi_audit_recorder-0.1.0/fastapi_audit_recorder/resolver.py +32 -0
  13. fastapi_audit_recorder-0.1.0/fastapi_audit_recorder/result.py +25 -0
  14. fastapi_audit_recorder-0.1.0/fastapi_audit_recorder/service.py +113 -0
  15. fastapi_audit_recorder-0.1.0/fastapi_audit_recorder/transactional_decorator.py +37 -0
  16. fastapi_audit_recorder-0.1.0/fastapi_audit_recorder.egg-info/PKG-INFO +248 -0
  17. fastapi_audit_recorder-0.1.0/fastapi_audit_recorder.egg-info/SOURCES.txt +27 -0
  18. fastapi_audit_recorder-0.1.0/fastapi_audit_recorder.egg-info/dependency_links.txt +1 -0
  19. fastapi_audit_recorder-0.1.0/fastapi_audit_recorder.egg-info/requires.txt +8 -0
  20. fastapi_audit_recorder-0.1.0/fastapi_audit_recorder.egg-info/top_level.txt +1 -0
  21. fastapi_audit_recorder-0.1.0/pyproject.toml +60 -0
  22. fastapi_audit_recorder-0.1.0/setup.cfg +4 -0
  23. fastapi_audit_recorder-0.1.0/tests/__init__.py +0 -0
  24. fastapi_audit_recorder-0.1.0/tests/test_audit_decorator.py +257 -0
  25. fastapi_audit_recorder-0.1.0/tests/test_context.py +51 -0
  26. fastapi_audit_recorder-0.1.0/tests/test_resolver.py +78 -0
  27. fastapi_audit_recorder-0.1.0/tests/test_result.py +43 -0
  28. fastapi_audit_recorder-0.1.0/tests/test_service.py +209 -0
  29. fastapi_audit_recorder-0.1.0/tests/test_transactional_decorator.py +89 -0
@@ -0,0 +1,6 @@
1
+ .venv/
2
+ .tool-versions
3
+ __pycache__
4
+ *egg-info/
5
+ dist/
6
+ node_modules/
@@ -0,0 +1,58 @@
1
+ stages:
2
+ - lint
3
+ - test
4
+ - build
5
+ - publish
6
+
7
+ variables:
8
+ PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
9
+
10
+ default:
11
+ cache:
12
+ paths:
13
+ - .cache/pip
14
+
15
+ ruff:
16
+ stage: lint
17
+ image: python:3.11
18
+ before_script:
19
+ - pip install -e .[dev]
20
+ script:
21
+ - ruff check .
22
+ allow_failure: true
23
+
24
+ pytest:
25
+ stage: test
26
+ image: python:3.11
27
+ script:
28
+ - pip install -e .[dev]
29
+ - pytest
30
+
31
+ build:
32
+ stage: build
33
+ image: python:3.11
34
+ before_script:
35
+ - pip install build
36
+ script:
37
+ - export SETUPTOOLS_SCM_PRETEND_VERSION=${CI_COMMIT_TAG#v}
38
+ - python -m build
39
+ artifacts:
40
+ paths:
41
+ - dist/
42
+ rules:
43
+ - if: '$CI_COMMIT_TAG'
44
+
45
+ publish:
46
+ stage: publish
47
+ image: python:3.11
48
+ before_script:
49
+ - pip install twine
50
+ script:
51
+ - export TWINE_USERNAME="__token__"
52
+ - export TWINE_PASSWORD="$PYPI_TOKEN"
53
+ - twine upload dist/* --verbose
54
+ dependencies:
55
+ - build
56
+ rules:
57
+ - if: '$CI_COMMIT_TAG'
58
+ when: manual
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 fastapi-audit contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,5 @@
1
+ include LICENSE
2
+ include README.md
3
+ recursive-include fastapi_audit_recorder/static *
4
+ recursive-include fastapi_audit_recorder/static/components *
5
+ recursive-include fastapi_audit_recorder/static/vendor *
@@ -0,0 +1,248 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastapi-audit-recorder
3
+ Version: 0.1.0
4
+ Summary: Generic audit library for FastAPI applications
5
+ Author: fastapi-audit-recorder contributors
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 fastapi-audit contributors
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Keywords: fastapi,audit,sqlalchemy,logging,trail
29
+ Requires-Python: >=3.11
30
+ Description-Content-Type: text/markdown
31
+ License-File: LICENSE
32
+ Requires-Dist: sqlalchemy>=2.0
33
+ Requires-Dist: pydantic>=2.0
34
+ Provides-Extra: dev
35
+ Requires-Dist: ruff; extra == "dev"
36
+ Requires-Dist: pytest; extra == "dev"
37
+ Requires-Dist: pytest-anyio; extra == "dev"
38
+ Requires-Dist: anyio[trio]; extra == "dev"
39
+ Dynamic: license-file
40
+
41
+
42
+ Cette librairie permet d'auditer les actions sur un projet et mets également à disposition une UI intégrée.
43
+
44
+ # Structure complète du projet
45
+
46
+ ```text
47
+ project-audit/
48
+ ├── fastapi_audit_recorder/
49
+ │ ├── __init__.py
50
+ │ ├── context.py
51
+ │ ├── audit_decorator.py
52
+ │ ├── transactional_decorator.py
53
+ │ ├── service.py
54
+ │ ├── models.py
55
+ │ ├── resolver.py
56
+ │ └── result.py
57
+ ├── tests/
58
+ ├── LICENSE
59
+ ├── README.md
60
+ └── pyproject.toml
61
+ ```
62
+
63
+ # Formatting with ruff
64
+
65
+ ruff check . --fix
66
+ ruff format .
67
+
68
+ # Tests
69
+
70
+ pytest
71
+
72
+ # Builder la lib
73
+
74
+ ```bash
75
+ python -m pip install --upgrade build
76
+ python -m build
77
+ ```
78
+
79
+ Le répertoire dist contiendra un fichier .whl et un fichier .tar.gz.
80
+
81
+ # Installer la distribution sur un projet (en local)
82
+
83
+ ```bash
84
+ pip install /path/to/project-audit/dist/fastapi_audit_recorder-0.1.0-py3-none-any.whl
85
+ pip show fastapi-audit-recorder
86
+ ```
87
+
88
+ # Installer la distribution sur un projet (en mode dev)
89
+
90
+ ```bash
91
+ pip install -e /home/you/dev/project-audit
92
+ ```
93
+
94
+ # Executer les tests unitaires
95
+
96
+ ```bash
97
+ pytest -v
98
+ ```
99
+
100
+ ## Intégration de la base de données (sqlalchemy et alembic)
101
+
102
+ Importer et ajouter la metadata dans le fichier de configuration :
103
+
104
+ ```python
105
+ from fastapi_audit_recorder import models as fastapi_audit_models
106
+
107
+ target_metadata = [app.models.model.Base.metadata, fastapi_audit_models.Base.metadata]
108
+ ```
109
+
110
+ Générer et executer le script de mise à jour, sous Alembic :
111
+
112
+ ```bash
113
+ alembic revision --autogenerate -m "create_audit_logs_from_fastapi_audit_recorder"
114
+ alembic upgrade head
115
+ ```
116
+
117
+ ## Intégration du service
118
+
119
+ ### Configuration
120
+
121
+ Créer un fichier audit_setup.py.
122
+ Celui-ci condiendra les requêtes qui seront executées avant et après chaque action, afin d'historiser les données et les changements.
123
+ Les actions et les types de resources sont à la charge de chaque projet. TODO : héritage
124
+
125
+ Créer autant de query et de register que vous avez de type de resource. Exemple :
126
+
127
+ ```python
128
+ from fastapi_audit_recorder.resolver import resolver
129
+ from sqlalchemy.orm import Session
130
+ from app.models.model import Compte
131
+
132
+ class AuditResourceEnum(enum.Enum):
133
+ COMPTE = "COMPTE"
134
+
135
+ class AuditActionEnum(enum.Enum):
136
+ APPROVE = "APPROVE"
137
+ REFUSE = "REFUSE"
138
+
139
+ def get_compte(session: Session, id: int) -> Compte | None:
140
+ return session.query(Compte).get(id)
141
+
142
+ def audit_setup():
143
+ resolver.register(AuditResourceEnum.COMPTE.name, get_compte, Compte.id)
144
+ ```
145
+
146
+ La méthode `register` accepte trois paramètres :
147
+ - `resource_type` : le nom de la ressource
148
+ - `loader` : la fonction de chargement de la ressource, signature `(session: Session, resource_id: Any) -> Any`
149
+ - `identifier` : le champ SQLAlchemy utilisé comme identifiant de la ressource (ex. `Compte.id`, `Compte.uuid`)
150
+
151
+ L'`identifier` sert à extraire automatiquement l'id depuis l'objet retourné par la fonction décorée lorsque `id_param` n'est pas renseigné, notamment dans le cas d'une création.
152
+
153
+ Appeler la méthode audit_setup() dans le context manager :
154
+
155
+ ```python
156
+ from app.audit_setup import audit_setup
157
+
158
+ @asynccontextmanager
159
+ async def lifespan(_: FastAPI) -> AsyncIterator[None]:
160
+ ...
161
+ audit_setup()
162
+ yield
163
+ ```
164
+
165
+ ### Décorateur @audit
166
+
167
+ La lib mets à disposition un décorateur afin d'auditer une action.
168
+ Exemple d'utilisation :
169
+
170
+ ```python
171
+ from fastapi_audit_recorder.decorators.audit_decorator import audit
172
+
173
+ @audit(action="UPDATE", resource_type="COMPTE", id_param="compteDTO.id")
174
+ async def update_compte(compteDTO: CompteDTO, id_token: KeycloakIDToken, session: Session) -> CompteDTO:
175
+ ```
176
+
177
+ Si `id_param` n'est pas renseigné, l'identifiant sera extrait automatiquement depuis l'objet retourné par la fonction,
178
+ en utilisant l'`identifier` enregistré dans le resolver (via `resolver.register`). Exemple :
179
+
180
+ ```python
181
+ @audit(action="CREATE", resource_type="COMPTE") # pas d'id_param
182
+ async def create_compte(compteDTO: CompteDTO, id_token: KeycloakIDToken, session: Session) -> Compte:
183
+ ...
184
+ return db_compte # db_compte.id sera extrait via Compte.id déclaré dans le resolver
185
+ ```
186
+
187
+ Important:
188
+ - **Tous les paramètres du décorateur `@audit` doivent être passés en nommés** (`action=`, `resource_type=`, `id_param=`)
189
+ - **Tous les arguments de la fonction décorée doivent également être passés en nommés** lors de l'appel
190
+ - `id_token: KeycloakIDToken` est obligatoire afin de pouvoir récupérer les informations sur l'utilisateur
191
+ - `session: Session` est obligatoire afin que les logs d'audit soient ajoutées dans la même session
192
+
193
+ ### Gestion de la transaction - décorateur @transactional
194
+
195
+ Le décorateur @audit ne réalise aucun commit, c'est action à la charge de la méthode appelante.
196
+ Un décorateur @transactional est mis à disposition afin d'automatiser les commits, exemple :
197
+
198
+ ```python
199
+ from fastapi_audit_recorder.decorators.audit_decorator import audit
200
+ from fastapi_audit_recorder.decorators.transactional_decorator import transactional
201
+
202
+ @transactional
203
+ @audit(action=AuditActionEnum.CREATE.name, resource_type=AuditResourceEnum.COMPTE.name, id_param="db_compte.id")
204
+ def approve_demande_creation(...)
205
+ ```
206
+
207
+ A noter: l'ordre est important, le l'annotation @transactional doit précéder l'annotation @audit pour la persitance des données.
208
+
209
+ ### Contexte
210
+
211
+ Dans certain cas, on souhaite auditer une action en fonction de l'appelant.
212
+ Par exemple, auditer une approbation manuelle mais ne pas auditer une autoapprobation.
213
+
214
+ Pour cela il est possible de définir un contexte afin de désactiver un ou plusieurs audits, exemple :
215
+
216
+ ```python
217
+ from fastapi_audit_recorder.context import skip_audit
218
+
219
+ with skip_audit(AuditActionEnum.APPROVE, AuditActionEnum.REJECT)
220
+ with skip_audit():
221
+ service.approve(...)
222
+ ```
223
+
224
+ ### Ignorer l'audit
225
+
226
+ L'audit est automatiquement réalisée après l'execution de la méthode annotée, sauf si celle-ci lance une exception.
227
+ Il est possible d'ignorer explicitement l'audit. Exemple :
228
+
229
+ ```python
230
+ from fastapi_audit_recorder.result import AuditResult
231
+
232
+ def approve(...) {
233
+ if is_already_approve:
234
+ return AuditResult.skip(db_compte)
235
+ }
236
+
237
+ ```
238
+
239
+ ## Services
240
+
241
+ La lib mets à disposition des services afin de récupérer des données filtrées et paginées :
242
+
243
+ ```python
244
+ from fastapi_audit_recorder import service
245
+
246
+ service.query_logs(...)
247
+ service.query_log_by_id(...)
248
+ ```
@@ -0,0 +1,208 @@
1
+
2
+ Cette librairie permet d'auditer les actions sur un projet et mets également à disposition une UI intégrée.
3
+
4
+ # Structure complète du projet
5
+
6
+ ```text
7
+ project-audit/
8
+ ├── fastapi_audit_recorder/
9
+ │ ├── __init__.py
10
+ │ ├── context.py
11
+ │ ├── audit_decorator.py
12
+ │ ├── transactional_decorator.py
13
+ │ ├── service.py
14
+ │ ├── models.py
15
+ │ ├── resolver.py
16
+ │ └── result.py
17
+ ├── tests/
18
+ ├── LICENSE
19
+ ├── README.md
20
+ └── pyproject.toml
21
+ ```
22
+
23
+ # Formatting with ruff
24
+
25
+ ruff check . --fix
26
+ ruff format .
27
+
28
+ # Tests
29
+
30
+ pytest
31
+
32
+ # Builder la lib
33
+
34
+ ```bash
35
+ python -m pip install --upgrade build
36
+ python -m build
37
+ ```
38
+
39
+ Le répertoire dist contiendra un fichier .whl et un fichier .tar.gz.
40
+
41
+ # Installer la distribution sur un projet (en local)
42
+
43
+ ```bash
44
+ pip install /path/to/project-audit/dist/fastapi_audit_recorder-0.1.0-py3-none-any.whl
45
+ pip show fastapi-audit-recorder
46
+ ```
47
+
48
+ # Installer la distribution sur un projet (en mode dev)
49
+
50
+ ```bash
51
+ pip install -e /home/you/dev/project-audit
52
+ ```
53
+
54
+ # Executer les tests unitaires
55
+
56
+ ```bash
57
+ pytest -v
58
+ ```
59
+
60
+ ## Intégration de la base de données (sqlalchemy et alembic)
61
+
62
+ Importer et ajouter la metadata dans le fichier de configuration :
63
+
64
+ ```python
65
+ from fastapi_audit_recorder import models as fastapi_audit_models
66
+
67
+ target_metadata = [app.models.model.Base.metadata, fastapi_audit_models.Base.metadata]
68
+ ```
69
+
70
+ Générer et executer le script de mise à jour, sous Alembic :
71
+
72
+ ```bash
73
+ alembic revision --autogenerate -m "create_audit_logs_from_fastapi_audit_recorder"
74
+ alembic upgrade head
75
+ ```
76
+
77
+ ## Intégration du service
78
+
79
+ ### Configuration
80
+
81
+ Créer un fichier audit_setup.py.
82
+ Celui-ci condiendra les requêtes qui seront executées avant et après chaque action, afin d'historiser les données et les changements.
83
+ Les actions et les types de resources sont à la charge de chaque projet. TODO : héritage
84
+
85
+ Créer autant de query et de register que vous avez de type de resource. Exemple :
86
+
87
+ ```python
88
+ from fastapi_audit_recorder.resolver import resolver
89
+ from sqlalchemy.orm import Session
90
+ from app.models.model import Compte
91
+
92
+ class AuditResourceEnum(enum.Enum):
93
+ COMPTE = "COMPTE"
94
+
95
+ class AuditActionEnum(enum.Enum):
96
+ APPROVE = "APPROVE"
97
+ REFUSE = "REFUSE"
98
+
99
+ def get_compte(session: Session, id: int) -> Compte | None:
100
+ return session.query(Compte).get(id)
101
+
102
+ def audit_setup():
103
+ resolver.register(AuditResourceEnum.COMPTE.name, get_compte, Compte.id)
104
+ ```
105
+
106
+ La méthode `register` accepte trois paramètres :
107
+ - `resource_type` : le nom de la ressource
108
+ - `loader` : la fonction de chargement de la ressource, signature `(session: Session, resource_id: Any) -> Any`
109
+ - `identifier` : le champ SQLAlchemy utilisé comme identifiant de la ressource (ex. `Compte.id`, `Compte.uuid`)
110
+
111
+ L'`identifier` sert à extraire automatiquement l'id depuis l'objet retourné par la fonction décorée lorsque `id_param` n'est pas renseigné, notamment dans le cas d'une création.
112
+
113
+ Appeler la méthode audit_setup() dans le context manager :
114
+
115
+ ```python
116
+ from app.audit_setup import audit_setup
117
+
118
+ @asynccontextmanager
119
+ async def lifespan(_: FastAPI) -> AsyncIterator[None]:
120
+ ...
121
+ audit_setup()
122
+ yield
123
+ ```
124
+
125
+ ### Décorateur @audit
126
+
127
+ La lib mets à disposition un décorateur afin d'auditer une action.
128
+ Exemple d'utilisation :
129
+
130
+ ```python
131
+ from fastapi_audit_recorder.decorators.audit_decorator import audit
132
+
133
+ @audit(action="UPDATE", resource_type="COMPTE", id_param="compteDTO.id")
134
+ async def update_compte(compteDTO: CompteDTO, id_token: KeycloakIDToken, session: Session) -> CompteDTO:
135
+ ```
136
+
137
+ Si `id_param` n'est pas renseigné, l'identifiant sera extrait automatiquement depuis l'objet retourné par la fonction,
138
+ en utilisant l'`identifier` enregistré dans le resolver (via `resolver.register`). Exemple :
139
+
140
+ ```python
141
+ @audit(action="CREATE", resource_type="COMPTE") # pas d'id_param
142
+ async def create_compte(compteDTO: CompteDTO, id_token: KeycloakIDToken, session: Session) -> Compte:
143
+ ...
144
+ return db_compte # db_compte.id sera extrait via Compte.id déclaré dans le resolver
145
+ ```
146
+
147
+ Important:
148
+ - **Tous les paramètres du décorateur `@audit` doivent être passés en nommés** (`action=`, `resource_type=`, `id_param=`)
149
+ - **Tous les arguments de la fonction décorée doivent également être passés en nommés** lors de l'appel
150
+ - `id_token: KeycloakIDToken` est obligatoire afin de pouvoir récupérer les informations sur l'utilisateur
151
+ - `session: Session` est obligatoire afin que les logs d'audit soient ajoutées dans la même session
152
+
153
+ ### Gestion de la transaction - décorateur @transactional
154
+
155
+ Le décorateur @audit ne réalise aucun commit, c'est action à la charge de la méthode appelante.
156
+ Un décorateur @transactional est mis à disposition afin d'automatiser les commits, exemple :
157
+
158
+ ```python
159
+ from fastapi_audit_recorder.decorators.audit_decorator import audit
160
+ from fastapi_audit_recorder.decorators.transactional_decorator import transactional
161
+
162
+ @transactional
163
+ @audit(action=AuditActionEnum.CREATE.name, resource_type=AuditResourceEnum.COMPTE.name, id_param="db_compte.id")
164
+ def approve_demande_creation(...)
165
+ ```
166
+
167
+ A noter: l'ordre est important, le l'annotation @transactional doit précéder l'annotation @audit pour la persitance des données.
168
+
169
+ ### Contexte
170
+
171
+ Dans certain cas, on souhaite auditer une action en fonction de l'appelant.
172
+ Par exemple, auditer une approbation manuelle mais ne pas auditer une autoapprobation.
173
+
174
+ Pour cela il est possible de définir un contexte afin de désactiver un ou plusieurs audits, exemple :
175
+
176
+ ```python
177
+ from fastapi_audit_recorder.context import skip_audit
178
+
179
+ with skip_audit(AuditActionEnum.APPROVE, AuditActionEnum.REJECT)
180
+ with skip_audit():
181
+ service.approve(...)
182
+ ```
183
+
184
+ ### Ignorer l'audit
185
+
186
+ L'audit est automatiquement réalisée après l'execution de la méthode annotée, sauf si celle-ci lance une exception.
187
+ Il est possible d'ignorer explicitement l'audit. Exemple :
188
+
189
+ ```python
190
+ from fastapi_audit_recorder.result import AuditResult
191
+
192
+ def approve(...) {
193
+ if is_already_approve:
194
+ return AuditResult.skip(db_compte)
195
+ }
196
+
197
+ ```
198
+
199
+ ## Services
200
+
201
+ La lib mets à disposition des services afin de récupérer des données filtrées et paginées :
202
+
203
+ ```python
204
+ from fastapi_audit_recorder import service
205
+
206
+ service.query_logs(...)
207
+ service.query_log_by_id(...)
208
+ ```
@@ -0,0 +1,123 @@
1
+ """
2
+ Serveur de développement local avec données de test.
3
+ Accessible depuis : http://localhost:8000/audit/
4
+ """
5
+
6
+ from datetime import datetime, timezone
7
+
8
+ from fastapi import FastAPI, Request
9
+ from fastapi_audit.models import AuditLog, Base
10
+ from fastapi_audit.routers.display_router import get_display_router
11
+ from fastapi_audit.search_router import get_search_router
12
+ from sqlalchemy import create_engine
13
+ from sqlalchemy.orm import sessionmaker
14
+
15
+ # --- DB SQLite en mémoire ---
16
+ engine = create_engine('sqlite:///:memory:', connect_args={'check_same_thread': False})
17
+ SessionLocal = sessionmaker(bind=engine)
18
+
19
+ Base.metadata.create_all(bind=engine)
20
+
21
+
22
+ def get_db():
23
+ db = SessionLocal()
24
+ try:
25
+ yield db
26
+ finally:
27
+ db.close()
28
+
29
+
30
+ # --- Données de test ---
31
+ def seed(db):
32
+ entries = [
33
+ AuditLog(
34
+ id='1',
35
+ action='CREATE',
36
+ resource_type='User',
37
+ resource_id='101',
38
+ user_id='42',
39
+ user_email='alice@example.com',
40
+ old_values=None,
41
+ new_values={'name': 'Alice', 'email': 'alice@example.com'},
42
+ changes={'name': {'old': None, 'new': 'Alice'}},
43
+ created_at=datetime(2026, 3, 1, 9, 0, 0, tzinfo=timezone.utc),
44
+ ),
45
+ AuditLog(
46
+ id='2',
47
+ action='UPDATE',
48
+ resource_type='User',
49
+ resource_id='101',
50
+ user_id='42',
51
+ user_email='alice@example.com',
52
+ old_values={'name': 'Alice'},
53
+ new_values={'name': 'Alice Dupont'},
54
+ changes={'name': {'old': 'Alice', 'new': 'Alice Dupont'}},
55
+ created_at=datetime(2026, 3, 10, 14, 30, 0, tzinfo=timezone.utc),
56
+ ),
57
+ AuditLog(
58
+ id='3',
59
+ action='DELETE',
60
+ resource_type='Order',
61
+ resource_id='55',
62
+ user_id='7',
63
+ user_email='bob@example.com',
64
+ old_values={'status': 'pending', 'amount': 99.9},
65
+ new_values=None,
66
+ changes={'status': {'old': 'pending', 'new': None}},
67
+ created_at=datetime(2026, 3, 15, 8, 0, 0, tzinfo=timezone.utc),
68
+ ),
69
+ AuditLog(
70
+ id='4',
71
+ action='CREATE',
72
+ resource_type='Order',
73
+ resource_id='56',
74
+ user_id='7',
75
+ user_email='bob@example.com',
76
+ old_values=None,
77
+ new_values={'status': 'confirmed', 'amount': 149.0},
78
+ changes={'status': {'old': None, 'new': 'confirmed'}},
79
+ created_at=datetime(2026, 3, 18, 11, 0, 0, tzinfo=timezone.utc),
80
+ ),
81
+ AuditLog(
82
+ id='5',
83
+ action='APPROVE',
84
+ resource_type='Invoice',
85
+ resource_id='9',
86
+ user_id='42',
87
+ user_email='alice@example.com',
88
+ old_values={'approved': False},
89
+ new_values={'approved': True},
90
+ changes={'approved': {'old': False, 'new': True}},
91
+ created_at=datetime(2026, 3, 19, 10, 0, 0, tzinfo=timezone.utc),
92
+ ),
93
+ ]
94
+ db.add_all(entries)
95
+ db.commit()
96
+
97
+
98
+ with SessionLocal() as db:
99
+ seed(db)
100
+
101
+ # --- App ---
102
+ app = FastAPI(title='Audit Dev Server')
103
+
104
+
105
+ @app.middleware('http')
106
+ async def inject_dev_cookie(request: Request, call_next):
107
+ """Injecte un cookie kc_token factice sur toutes les requêtes en dev."""
108
+ headers = dict(request.scope['headers'])
109
+ cookie_header = headers.get(b'cookie', b'')
110
+ if b'kc_token' not in cookie_header:
111
+ sep = b'; ' if cookie_header else b''
112
+ headers[b'cookie'] = cookie_header + sep + b'kc_token=dev-token'
113
+ request.scope['headers'] = list(headers.items())
114
+ return await call_next(request)
115
+
116
+
117
+ app.include_router(get_search_router(get_db), prefix='/audit')
118
+ app.include_router(get_display_router(), prefix='/audit')
119
+
120
+ if __name__ == '__main__':
121
+ import uvicorn
122
+
123
+ uvicorn.run('dev_server:app', host='0.0.0.0', port=8000, reload=True)
@@ -0,0 +1,17 @@
1
+ from .audit_decorator import audit
2
+ from .context import skip_audit
3
+ from .models import AuditLog, AuditLogDTO, PaginatedAuditLogs
4
+ from .resolver import ResourceResolver
5
+ from .result import AuditResult
6
+ from .transactional_decorator import transactional
7
+
8
+ __all__ = [
9
+ 'AuditLog',
10
+ 'AuditLogDTO',
11
+ 'AuditResult',
12
+ 'PaginatedAuditLogs',
13
+ 'ResourceResolver',
14
+ 'audit',
15
+ 'transactional',
16
+ 'skip_audit',
17
+ ]