python-appie 0.1.0__tar.gz → 0.2.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 (53) hide show
  1. {python_appie-0.1.0 → python_appie-0.2.0}/PKG-INFO +82 -7
  2. {python_appie-0.1.0 → python_appie-0.2.0}/README.md +80 -6
  3. {python_appie-0.1.0 → python_appie-0.2.0}/docs/authentication.md +4 -0
  4. python_appie-0.2.0/docs/authentication.nl.md +25 -0
  5. python_appie-0.2.0/docs/cli.md +20 -0
  6. python_appie-0.2.0/docs/cli.nl.md +20 -0
  7. python_appie-0.2.0/docs/development.md +44 -0
  8. python_appie-0.2.0/docs/development.nl.md +44 -0
  9. {python_appie-0.1.0 → python_appie-0.2.0}/docs/getting-started.md +17 -0
  10. python_appie-0.2.0/docs/getting-started.nl.md +64 -0
  11. python_appie-0.2.0/docs/history.md +229 -0
  12. python_appie-0.2.0/docs/history.nl.md +13 -0
  13. {python_appie-0.1.0 → python_appie-0.2.0}/docs/index.md +7 -0
  14. python_appie-0.2.0/docs/index.nl.md +36 -0
  15. python_appie-0.2.0/docs/lists.md +80 -0
  16. python_appie-0.2.0/docs/lists.nl.md +80 -0
  17. python_appie-0.2.0/docs/mock-client.md +265 -0
  18. python_appie-0.2.0/docs/mock-client.nl.md +265 -0
  19. {python_appie-0.1.0 → python_appie-0.2.0}/docs/products.md +18 -0
  20. python_appie-0.2.0/docs/products.nl.md +54 -0
  21. {python_appie-0.1.0 → python_appie-0.2.0}/docs/receipts.md +18 -0
  22. python_appie-0.2.0/docs/receipts.nl.md +59 -0
  23. python_appie-0.2.0/mkdocs.yml +65 -0
  24. python_appie-0.2.0/project-plan.md +235 -0
  25. {python_appie-0.1.0 → python_appie-0.2.0}/pyproject.toml +2 -1
  26. {python_appie-0.1.0 → python_appie-0.2.0}/src/appie/__init__.py +16 -3
  27. python_appie-0.2.0/src/appie/lists.py +132 -0
  28. python_appie-0.2.0/src/appie/mock.py +405 -0
  29. python_appie-0.2.0/src/appie/pytest_plugin.py +42 -0
  30. {python_appie-0.1.0 → python_appie-0.2.0}/tests/test_client.py +158 -7
  31. python_appie-0.2.0/tests/test_mock.py +130 -0
  32. {python_appie-0.1.0 → python_appie-0.2.0}/tests/test_package.py +7 -0
  33. {python_appie-0.1.0 → python_appie-0.2.0}/uv.lock +15 -1
  34. python_appie-0.1.0/docs/cli.md +0 -11
  35. python_appie-0.1.0/docs/development.md +0 -28
  36. python_appie-0.1.0/docs/lists.md +0 -24
  37. python_appie-0.1.0/docs/mock-client.md +0 -76
  38. python_appie-0.1.0/mkdocs.yml +0 -29
  39. python_appie-0.1.0/src/appie/lists.py +0 -78
  40. python_appie-0.1.0/src/appie/mock.py +0 -211
  41. python_appie-0.1.0/tests/test_mock.py +0 -54
  42. {python_appie-0.1.0 → python_appie-0.2.0}/.github/workflows/ci.yml +0 -0
  43. {python_appie-0.1.0 → python_appie-0.2.0}/.github/workflows/docs.yml +0 -0
  44. {python_appie-0.1.0 → python_appie-0.2.0}/.github/workflows/pypi.yml +0 -0
  45. {python_appie-0.1.0 → python_appie-0.2.0}/.gitignore +0 -0
  46. {python_appie-0.1.0 → python_appie-0.2.0}/.pre-commit-config.yaml +0 -0
  47. {python_appie-0.1.0 → python_appie-0.2.0}/src/appie/auth.py +0 -0
  48. {python_appie-0.1.0 → python_appie-0.2.0}/src/appie/client.py +0 -0
  49. {python_appie-0.1.0 → python_appie-0.2.0}/src/appie/models.py +0 -0
  50. {python_appie-0.1.0 → python_appie-0.2.0}/src/appie/products.py +0 -0
  51. {python_appie-0.1.0 → python_appie-0.2.0}/src/appie/receipts.py +0 -0
  52. {python_appie-0.1.0 → python_appie-0.2.0}/tests/conftest.py +0 -0
  53. {python_appie-0.1.0 → python_appie-0.2.0}/tests/test_auth.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-appie
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Unofficial Python client for the Albert Heijn API.
5
5
  Project-URL: Homepage, https://github.com/tijnschouten/appie
6
6
  Project-URL: Documentation, https://tijnschouten.github.io/appie/
@@ -13,6 +13,7 @@ Requires-Dist: playwright>=1.52.0
13
13
  Requires-Dist: pydantic>=2
14
14
  Provides-Extra: dev
15
15
  Requires-Dist: mkdocs-material>=9.5; extra == 'dev'
16
+ Requires-Dist: mkdocs-static-i18n>=1.2; extra == 'dev'
16
17
  Requires-Dist: mkdocs>=1.6; extra == 'dev'
17
18
  Requires-Dist: pre-commit>=4.0; extra == 'dev'
18
19
  Requires-Dist: pyright>=1.1.390; extra == 'dev'
@@ -27,7 +28,7 @@ Description-Content-Type: text/markdown
27
28
 
28
29
  `python-appie` is an unofficial async Python client for the Albert Heijn API.
29
30
 
30
- Full documentation lives in [`docs/`](/Users/tijnschouten/repos/personal/appie/docs), is built from [`mkdocs.yml`](/Users/tijnschouten/repos/personal/appie/mkdocs.yml), and is intended to be published at [tijnschouten.github.io/appie](https://tijnschouten.github.io/appie/).
31
+ [Full documentation](https://tijnschouten.github.io/appie/) is available in English and Dutch. The source for the docs lives in [`docs/`](/Users/tijnschouten/repos/personal/appie/docs) and is built from [`mkdocs.yml`](/Users/tijnschouten/repos/personal/appie/mkdocs.yml).
31
32
 
32
33
  Releases are intended to publish to PyPI as `python-appie` from version tags via GitHub Actions.
33
34
 
@@ -141,6 +142,9 @@ asyncio.run(main())
141
142
  ### Shopping lists
142
143
 
143
144
  - add an item via `client.lists.add_item(description, quantity=1, product_id=None)`
145
+ - read the current shopping list via `client.lists.get_list()`
146
+ - remove one item via `client.lists.remove_item(item_id)`
147
+ - clear the entire list via `client.lists.clear()`
144
148
  - use `MockAHClient` for local development and tests without touching AH
145
149
 
146
150
  Example:
@@ -153,15 +157,74 @@ from appie import AHClient
153
157
 
154
158
  async def main() -> None:
155
159
  async with AHClient() as client:
156
- item = await client.lists.add_item("Halfvolle melk", quantity=2)
157
- print(item)
160
+ await client.lists.add_item("Halfvolle melk", quantity=2)
161
+ items = await client.lists.get_list()
162
+ print(items)
158
163
 
159
164
 
160
165
  asyncio.run(main())
161
166
  ```
162
167
 
163
- Current limitation:
164
- shopping-list add is implemented, but `get_list()`, `remove_item()`, and `clear()` still raise `NotImplementedError` until their live API shape is confirmed.
168
+ Note:
169
+ the `ShoppingListItem.id` returned by `get_list()` is an opaque removal key designed for `remove_item(item_id)`. It should be treated as an implementation detail rather than a stable AH server identifier.
170
+
171
+ ### Mocking and downstream tests
172
+
173
+ - `MockAHClient()` provides an in-memory drop-in client for local development
174
+ - `client.mock.calls` and `client.mock.last_call` capture what your code did
175
+ - `client.mock.next_response(operation, value)` seeds a one-shot result
176
+ - `client.mock.next_error(operation, exc)` seeds a one-shot failure
177
+ - `client.mock.set_scenario(operation, delay_ms=..., error=...)` applies persistent delay/error behavior
178
+ - `appie.pytest_plugin` provides pytest fixtures for downstream packages
179
+
180
+ Example:
181
+
182
+ ```python
183
+ import asyncio
184
+
185
+ from appie import MockAHClient
186
+
187
+
188
+ async def main() -> None:
189
+ async with MockAHClient() as client:
190
+ client.mock.next_response("products.search", [])
191
+ products = await client.products.search("melk")
192
+ print(products)
193
+ print(client.mock.last_call)
194
+
195
+
196
+ asyncio.run(main())
197
+ ```
198
+
199
+ Expected outcome:
200
+
201
+ ```text
202
+ []
203
+ AppieMockCall(operation='products.search', params={'query': 'melk', 'limit': 10}, result=[], error=None)
204
+ ```
205
+
206
+ Pytest plugin example:
207
+
208
+ ```python
209
+ # tests/conftest.py
210
+ pytest_plugins = ["appie.pytest_plugin"]
211
+ ```
212
+
213
+ ```python
214
+ import pytest
215
+
216
+
217
+ @pytest.mark.asyncio
218
+ async def test_checkout_uses_expected_query(appie_mock):
219
+ await appie_mock.products.search("melk", limit=3)
220
+
221
+ assert appie_mock.mock.last_call is not None
222
+ assert appie_mock.mock.last_call.params == {"query": "melk", "limit": 3}
223
+ ```
224
+
225
+ Expected outcome:
226
+ - the test runs without touching AH
227
+ - the recorded call proves what your code sent into `python-appie`
165
228
 
166
229
  ## API overview
167
230
 
@@ -169,6 +232,7 @@ shopping-list add is implemented, but `get_list()`, `remove_item()`, and `clear(
169
232
 
170
233
  - `AHClient()`
171
234
  - `MockAHClient()`
235
+ - `MockAHClient().mock`
172
236
  - `await client.login()`
173
237
  - `await client.graphql(query, variables=None)`
174
238
 
@@ -190,6 +254,17 @@ shopping-list add is implemented, but `get_list()`, `remove_item()`, and `clear(
190
254
  - `client.lists.remove_item(item_id)`
191
255
  - `client.lists.clear()`
192
256
 
257
+ ### Mock helpers
258
+
259
+ - `client.mock.calls`
260
+ - `client.mock.last_call`
261
+ - `client.mock.clear_calls()`
262
+ - `client.mock.next_response(operation, value)`
263
+ - `client.mock.next_error(operation, exc)`
264
+ - `client.mock.clear_seeded_responses()`
265
+ - `client.mock.set_scenario(operation, delay_ms=0, error=None)`
266
+ - `client.mock.clear_scenarios()`
267
+
193
268
  ## Development
194
269
 
195
270
  Run checks locally:
@@ -206,6 +281,6 @@ uv run --extra dev mkdocs build --strict
206
281
 
207
282
  - This client is unofficial and may break when Albert Heijn changes its backend.
208
283
  - Receipt support currently covers in-store POS receipts.
209
- - Shopping-list support only implements the verified add-item mutation; other operations raise explicit `NotImplementedError` until their GraphQL shape is confirmed.
284
+ - Shopping-list read, add, remove, and clear are implemented against the live main-list endpoint.
210
285
  - Receipt summaries do not include line items; call `get_pos_receipt()` for a detailed receipt.
211
286
  - Endpoint discovery for this package is inspired by [gwillem/appie-go](https://github.com/gwillem/appie-go).
@@ -2,7 +2,7 @@
2
2
 
3
3
  `python-appie` is an unofficial async Python client for the Albert Heijn API.
4
4
 
5
- Full documentation lives in [`docs/`](/Users/tijnschouten/repos/personal/appie/docs), is built from [`mkdocs.yml`](/Users/tijnschouten/repos/personal/appie/mkdocs.yml), and is intended to be published at [tijnschouten.github.io/appie](https://tijnschouten.github.io/appie/).
5
+ [Full documentation](https://tijnschouten.github.io/appie/) is available in English and Dutch. The source for the docs lives in [`docs/`](/Users/tijnschouten/repos/personal/appie/docs) and is built from [`mkdocs.yml`](/Users/tijnschouten/repos/personal/appie/mkdocs.yml).
6
6
 
7
7
  Releases are intended to publish to PyPI as `python-appie` from version tags via GitHub Actions.
8
8
 
@@ -116,6 +116,9 @@ asyncio.run(main())
116
116
  ### Shopping lists
117
117
 
118
118
  - add an item via `client.lists.add_item(description, quantity=1, product_id=None)`
119
+ - read the current shopping list via `client.lists.get_list()`
120
+ - remove one item via `client.lists.remove_item(item_id)`
121
+ - clear the entire list via `client.lists.clear()`
119
122
  - use `MockAHClient` for local development and tests without touching AH
120
123
 
121
124
  Example:
@@ -128,15 +131,74 @@ from appie import AHClient
128
131
 
129
132
  async def main() -> None:
130
133
  async with AHClient() as client:
131
- item = await client.lists.add_item("Halfvolle melk", quantity=2)
132
- print(item)
134
+ await client.lists.add_item("Halfvolle melk", quantity=2)
135
+ items = await client.lists.get_list()
136
+ print(items)
133
137
 
134
138
 
135
139
  asyncio.run(main())
136
140
  ```
137
141
 
138
- Current limitation:
139
- shopping-list add is implemented, but `get_list()`, `remove_item()`, and `clear()` still raise `NotImplementedError` until their live API shape is confirmed.
142
+ Note:
143
+ the `ShoppingListItem.id` returned by `get_list()` is an opaque removal key designed for `remove_item(item_id)`. It should be treated as an implementation detail rather than a stable AH server identifier.
144
+
145
+ ### Mocking and downstream tests
146
+
147
+ - `MockAHClient()` provides an in-memory drop-in client for local development
148
+ - `client.mock.calls` and `client.mock.last_call` capture what your code did
149
+ - `client.mock.next_response(operation, value)` seeds a one-shot result
150
+ - `client.mock.next_error(operation, exc)` seeds a one-shot failure
151
+ - `client.mock.set_scenario(operation, delay_ms=..., error=...)` applies persistent delay/error behavior
152
+ - `appie.pytest_plugin` provides pytest fixtures for downstream packages
153
+
154
+ Example:
155
+
156
+ ```python
157
+ import asyncio
158
+
159
+ from appie import MockAHClient
160
+
161
+
162
+ async def main() -> None:
163
+ async with MockAHClient() as client:
164
+ client.mock.next_response("products.search", [])
165
+ products = await client.products.search("melk")
166
+ print(products)
167
+ print(client.mock.last_call)
168
+
169
+
170
+ asyncio.run(main())
171
+ ```
172
+
173
+ Expected outcome:
174
+
175
+ ```text
176
+ []
177
+ AppieMockCall(operation='products.search', params={'query': 'melk', 'limit': 10}, result=[], error=None)
178
+ ```
179
+
180
+ Pytest plugin example:
181
+
182
+ ```python
183
+ # tests/conftest.py
184
+ pytest_plugins = ["appie.pytest_plugin"]
185
+ ```
186
+
187
+ ```python
188
+ import pytest
189
+
190
+
191
+ @pytest.mark.asyncio
192
+ async def test_checkout_uses_expected_query(appie_mock):
193
+ await appie_mock.products.search("melk", limit=3)
194
+
195
+ assert appie_mock.mock.last_call is not None
196
+ assert appie_mock.mock.last_call.params == {"query": "melk", "limit": 3}
197
+ ```
198
+
199
+ Expected outcome:
200
+ - the test runs without touching AH
201
+ - the recorded call proves what your code sent into `python-appie`
140
202
 
141
203
  ## API overview
142
204
 
@@ -144,6 +206,7 @@ shopping-list add is implemented, but `get_list()`, `remove_item()`, and `clear(
144
206
 
145
207
  - `AHClient()`
146
208
  - `MockAHClient()`
209
+ - `MockAHClient().mock`
147
210
  - `await client.login()`
148
211
  - `await client.graphql(query, variables=None)`
149
212
 
@@ -165,6 +228,17 @@ shopping-list add is implemented, but `get_list()`, `remove_item()`, and `clear(
165
228
  - `client.lists.remove_item(item_id)`
166
229
  - `client.lists.clear()`
167
230
 
231
+ ### Mock helpers
232
+
233
+ - `client.mock.calls`
234
+ - `client.mock.last_call`
235
+ - `client.mock.clear_calls()`
236
+ - `client.mock.next_response(operation, value)`
237
+ - `client.mock.next_error(operation, exc)`
238
+ - `client.mock.clear_seeded_responses()`
239
+ - `client.mock.set_scenario(operation, delay_ms=0, error=None)`
240
+ - `client.mock.clear_scenarios()`
241
+
168
242
  ## Development
169
243
 
170
244
  Run checks locally:
@@ -181,6 +255,6 @@ uv run --extra dev mkdocs build --strict
181
255
 
182
256
  - This client is unofficial and may break when Albert Heijn changes its backend.
183
257
  - Receipt support currently covers in-store POS receipts.
184
- - Shopping-list support only implements the verified add-item mutation; other operations raise explicit `NotImplementedError` until their GraphQL shape is confirmed.
258
+ - Shopping-list read, add, remove, and clear are implemented against the live main-list endpoint.
185
259
  - Receipt summaries do not include line items; call `get_pos_receipt()` for a detailed receipt.
186
260
  - Endpoint discovery for this package is inspired by [gwillem/appie-go](https://github.com/gwillem/appie-go).
@@ -1,5 +1,7 @@
1
1
  # Authentication
2
2
 
3
+ Language: **English** | [Nederlands](https://tijnschouten.github.io/appie/nl/authentication/)
4
+
3
5
  ## Login flow
4
6
 
5
7
  The package uses a browser-assisted login flow exposed through `appie-login`.
@@ -19,3 +21,5 @@ Access tokens are refreshed automatically when they are close to expiry. Under n
19
21
 
20
22
  - This is not an official AH integration.
21
23
  - AH may change login requirements at any time.
24
+
25
+ Read next: [Products](products.md) for product search and product detail examples.
@@ -0,0 +1,25 @@
1
+ # Authenticatie
2
+
3
+ Taal: [English](https://tijnschouten.github.io/appie/authentication/) | **Nederlands**
4
+
5
+ ## Loginflow
6
+
7
+ De package gebruikt een browser-ondersteunde loginflow via `appie-login`.
8
+
9
+ De CLI:
10
+
11
+ - opent een Chrome-venster
12
+ - wacht op de AH-redirect naar `appie://login-exit?code=...`
13
+ - wisselt die code om voor tokens
14
+ - slaat tokens op in `~/.config/appie/tokens.json`
15
+
16
+ ## Token-refresh
17
+
18
+ Access tokens worden automatisch ververst vlak voor ze verlopen. Normaal gesproken hoef je `appie-login` alleen opnieuw te draaien als de refresh token niet meer geldig is.
19
+
20
+ ## Opmerkingen
21
+
22
+ - Dit is geen officiële AH-integratie.
23
+ - AH kan de loginvereisten op elk moment aanpassen.
24
+
25
+ Lees verder: [Producten](products.md) voor productzoeken en productdetails.
@@ -0,0 +1,20 @@
1
+ # CLI
2
+
3
+ Language: **English** | [Nederlands](https://tijnschouten.github.io/appie/nl/cli/)
4
+
5
+ ## `appie-login`
6
+
7
+ Authenticate and store tokens locally:
8
+
9
+ ```bash
10
+ uv run appie-login
11
+ ```
12
+
13
+ The CLI is intended as a one-time or occasional setup step. Day-to-day usage should normally rely on stored tokens and automatic refresh.
14
+
15
+ Expected outcome:
16
+ - a Chrome window opens for login
17
+ - the CLI captures the redirect code automatically
18
+ - it prints a success message after storing tokens
19
+
20
+ Read next: [Project History](history.md) for the original project brief and how the implementation evolved.
@@ -0,0 +1,20 @@
1
+ # CLI
2
+
3
+ Taal: [English](https://tijnschouten.github.io/appie/cli/) | **Nederlands**
4
+
5
+ ## `appie-login`
6
+
7
+ Authenticeer en sla tokens lokaal op:
8
+
9
+ ```bash
10
+ uv run appie-login
11
+ ```
12
+
13
+ De CLI is bedoeld als een eenmalige of incidentele setupstap. Daarna hoort normaal gebruik op opgeslagen tokens en automatische refresh te leunen.
14
+
15
+ Verwacht resultaat:
16
+ - er opent een Chrome-venster voor login
17
+ - de CLI vangt automatisch de redirectcode af
18
+ - na het opslaan van tokens verschijnt een succesmelding
19
+
20
+ Lees verder: [Projectgeschiedenis](history.md) voor de oorspronkelijke projectbrief en hoe de implementatie is geëvolueerd.
@@ -0,0 +1,44 @@
1
+ # Development
2
+
3
+ Language: **English** | [Nederlands](https://tijnschouten.github.io/appie/nl/development/)
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ uv sync --extra dev
9
+ pre-commit install
10
+ ```
11
+
12
+ Expected outcome:
13
+ - a local virtual environment is created
14
+ - development dependencies are installed
15
+ - git hooks are installed for future commits
16
+
17
+ ## Quality checks
18
+
19
+ ```bash
20
+ uv run ruff format .
21
+ uv run ruff check .
22
+ uv run pyright
23
+ uv run pytest
24
+ uv run mkdocs build
25
+ ```
26
+
27
+ Expected outcome:
28
+ - formatting is applied cleanly
29
+ - linting passes
30
+ - type checking passes
31
+ - tests pass with coverage
32
+ - docs build successfully
33
+
34
+ ## Pre-commit
35
+
36
+ This repository uses pre-commit hooks for:
37
+
38
+ - `ruff format`
39
+ - `ruff check`
40
+ - `pyright`
41
+ - `pytest`
42
+ - `mkdocs build`
43
+
44
+ Read next: [Home](index.md) to jump back to the documentation overview.
@@ -0,0 +1,44 @@
1
+ # Ontwikkeling
2
+
3
+ Taal: [English](https://tijnschouten.github.io/appie/development/) | **Nederlands**
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ uv sync --extra dev
9
+ pre-commit install
10
+ ```
11
+
12
+ Verwacht resultaat:
13
+ - er wordt een lokale virtual environment gemaakt
14
+ - ontwikkeldependencies worden geïnstalleerd
15
+ - git-hooks worden geïnstalleerd voor volgende commits
16
+
17
+ ## Quality checks
18
+
19
+ ```bash
20
+ uv run ruff format .
21
+ uv run ruff check .
22
+ uv run pyright
23
+ uv run pytest
24
+ uv run mkdocs build
25
+ ```
26
+
27
+ Verwacht resultaat:
28
+ - formatting wordt schoon toegepast
29
+ - linting slaagt
30
+ - type checking slaagt
31
+ - tests slagen met coverage
32
+ - docs bouwen succesvol
33
+
34
+ ## Pre-commit
35
+
36
+ Deze repository gebruikt pre-commit hooks voor:
37
+
38
+ - `ruff format`
39
+ - `ruff check`
40
+ - `pyright`
41
+ - `pytest`
42
+ - `mkdocs build`
43
+
44
+ Lees verder: [Home](index.md) om terug te gaan naar het documentatieoverzicht.
@@ -1,5 +1,7 @@
1
1
  # Getting Started
2
2
 
3
+ Language: **English** | [Nederlands](https://tijnschouten.github.io/appie/nl/getting-started/)
4
+
3
5
  ## Install
4
6
 
5
7
  ```bash
@@ -28,6 +30,11 @@ uv run appie-login
28
30
 
29
31
  This opens Chrome and captures the AH login redirect code automatically. Tokens are stored in `~/.config/appie/tokens.json`.
30
32
 
33
+ Expected outcome:
34
+ - a browser window opens for AH login
35
+ - after a successful login, tokens are stored locally
36
+ - later package usage reuses those tokens automatically
37
+
31
38
  ## First request
32
39
 
33
40
  ```python
@@ -45,3 +52,13 @@ async def main() -> None:
45
52
 
46
53
  asyncio.run(main())
47
54
  ```
55
+
56
+ Expected outcome:
57
+
58
+ ```text
59
+ Product(id=...)
60
+ Product(id=...)
61
+ Product(id=...)
62
+ ```
63
+
64
+ Read next: [Authentication](authentication.md) for token storage, refresh behavior, and re-login expectations.
@@ -0,0 +1,64 @@
1
+ # Snelstart
2
+
3
+ Taal: [English](https://tijnschouten.github.io/appie/getting-started/) | **Nederlands**
4
+
5
+ ## Installeren
6
+
7
+ ```bash
8
+ uv add python-appie
9
+ ```
10
+
11
+ Of:
12
+
13
+ ```bash
14
+ pip install python-appie
15
+ ```
16
+
17
+ Voor lokale ontwikkeling in deze repository:
18
+
19
+ ```bash
20
+ uv sync --extra dev
21
+ ```
22
+
23
+ ## Inloggen
24
+
25
+ Voer uit:
26
+
27
+ ```bash
28
+ uv run appie-login
29
+ ```
30
+
31
+ Dit opent Chrome, vangt automatisch de AH-redirectcode af en slaat tokens op in `~/.config/appie/tokens.json`.
32
+
33
+ Verwacht resultaat:
34
+ - er opent een browservenster voor de AH-login
35
+ - na succesvolle login worden tokens lokaal opgeslagen
36
+ - later hergebruikt de package die tokens automatisch
37
+
38
+ ## Eerste request
39
+
40
+ ```python
41
+ import asyncio
42
+
43
+ from appie import AHClient
44
+
45
+
46
+ async def main() -> None:
47
+ async with AHClient() as client:
48
+ products = await client.products.search("melk", limit=3)
49
+ for product in products:
50
+ print(product)
51
+
52
+
53
+ asyncio.run(main())
54
+ ```
55
+
56
+ Verwacht resultaat:
57
+
58
+ ```text
59
+ Product(id=...)
60
+ Product(id=...)
61
+ Product(id=...)
62
+ ```
63
+
64
+ Lees verder: [Authenticatie](authentication.md) voor tokenopslag, refreshgedrag en opnieuw inloggen.