fastapi2postman 0.1.2__tar.gz → 0.3.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.
- {fastapi2postman-0.1.2 → fastapi2postman-0.3.0}/LICENSE +20 -20
- fastapi2postman-0.3.0/PKG-INFO +173 -0
- fastapi2postman-0.3.0/README.md +146 -0
- fastapi2postman-0.3.0/pyproject.toml +80 -0
- fastapi2postman-0.3.0/pyproject.toml.orig +65 -0
- fastapi2postman-0.3.0/src/fastapi2postman/__init__.py +327 -0
- fastapi2postman-0.3.0/src/fastapi2postman/cli.py +168 -0
- fastapi2postman-0.3.0/src/fastapi2postman/py.typed +0 -0
- fastapi2postman-0.3.0/src/fastapi2postman/routes.py +92 -0
- fastapi2postman-0.1.2/PKG-INFO +0 -105
- fastapi2postman-0.1.2/README.rst +0 -74
- fastapi2postman-0.1.2/fastapi2postman.egg-info/PKG-INFO +0 -105
- fastapi2postman-0.1.2/fastapi2postman.egg-info/SOURCES.txt +0 -10
- fastapi2postman-0.1.2/fastapi2postman.egg-info/dependency_links.txt +0 -1
- fastapi2postman-0.1.2/fastapi2postman.egg-info/entry_points.txt +0 -3
- fastapi2postman-0.1.2/fastapi2postman.egg-info/requires.txt +0 -4
- fastapi2postman-0.1.2/fastapi2postman.egg-info/top_level.txt +0 -1
- fastapi2postman-0.1.2/fastapi2postman.py +0 -72
- fastapi2postman-0.1.2/setup.cfg +0 -4
- fastapi2postman-0.1.2/setup.py +0 -45
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2023 ashhadahsan
|
|
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
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 ashhadahsan
|
|
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
21
|
SOFTWARE.
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastapi2postman
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Turn a FastAPI app into a Postman collection you can run straight away.
|
|
5
|
+
Keywords: fastapi,postman,newman,openapi,api,cli
|
|
6
|
+
Author: Ashhad Ahsan Rehman
|
|
7
|
+
Author-email: Ashhad Ahsan Rehman <ashhadahsan@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Framework :: FastAPI
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
20
|
+
Classifier: Topic :: Software Development :: Testing
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Dist: fastapi>=0.105
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Project-URL: Source, https://github.com/ashhadahsan/fastapi2postman
|
|
25
|
+
Project-URL: Changelog, https://github.com/ashhadahsan/fastapi2postman/blob/main/CHANGELOG.md
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# fastapi2postman
|
|
29
|
+
|
|
30
|
+
[](https://pypi.org/project/fastapi2postman/)
|
|
31
|
+
[](https://pypi.org/project/fastapi2postman/)
|
|
32
|
+
[](https://github.com/ashhadahsan/fastapi2postman/actions/workflows/ci.yml)
|
|
33
|
+
[](https://github.com/ashhadahsan/fastapi2postman/blob/main/LICENSE)
|
|
34
|
+
|
|
35
|
+
**Turn a FastAPI app into a Postman collection you can run straight away.**
|
|
36
|
+
|
|
37
|
+
```console
|
|
38
|
+
$ pip install fastapi2postman
|
|
39
|
+
$ fastapi2postman --app main.py
|
|
40
|
+
output.json: 25 requests
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Import `output.json` into Postman, fill in the collection's variables, and send the login request. The token is saved for every other request.
|
|
44
|
+
|
|
45
|
+
Install it in the same environment as your app, because it imports the app to read its schema. Everything comes from `app.openapi()`, so what shows in your `/docs` shows in the collection.
|
|
46
|
+
|
|
47
|
+
## What's in the collection
|
|
48
|
+
|
|
49
|
+
| In your app | In Postman |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| `include_router()` and mounted FastAPI apps | Every route with its full path. Routes with `include_in_schema=False` and mounted apps with `openapi_url=None` stay out |
|
|
52
|
+
| Tags | A folder per tag |
|
|
53
|
+
| A route with several methods | One request per method |
|
|
54
|
+
| Path parameters | Path variables such as `:item_id` with a sample value |
|
|
55
|
+
| Query and header parameters, including those inside `Depends()` | Filled in, with optional ones switched off |
|
|
56
|
+
| Pydantic body models | A sample JSON body that follows your `Field` limits, enums, formats, defaults and declared examples |
|
|
57
|
+
| `Form()` and `File()` | Form fields and file pickers |
|
|
58
|
+
| Docstrings | Request descriptions |
|
|
59
|
+
| Title, version and description | Collection info |
|
|
60
|
+
| `servers`, or `--base-url` | A `{{baseUrl}}` variable |
|
|
61
|
+
|
|
62
|
+
## Auth
|
|
63
|
+
|
|
64
|
+
Auth is found wherever you declared it: on the route, in a nested `Depends()`, in `Security()`, or in the `dependencies=` of a route, `APIRouter`, `include_router()` or `FastAPI`.
|
|
65
|
+
|
|
66
|
+
| In your app | In Postman |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| `HTTPBearer` and subclasses, any `OAuth2…` scheme, OpenID Connect | Bearer auth with `{{bearerToken}}` |
|
|
69
|
+
| A dependency with an `authorization` header parameter | Bearer auth with `{{bearerToken}}` |
|
|
70
|
+
| `HTTPBasic` | Basic auth with `{{username}}` and `{{password}}` |
|
|
71
|
+
| `APIKeyHeader`, `APIKeyQuery` | The header or query parameter, set to a variable with the same name |
|
|
72
|
+
| A query parameter, header or form field whose name contains `token`, `api_key`, `secret` or `password` | The same, so `x_token` is sent as `{{x-token}}` |
|
|
73
|
+
|
|
74
|
+
Secret variables start blank, so the file holds no credentials. You fill them in inside Postman.
|
|
75
|
+
|
|
76
|
+
**The token saves itself.** A `POST` route that is your `tokenUrl`, or that returns `access_token` or `accessToken`, gets a script that stores the token in `{{bearerToken}}`. Those requests come first in the collection, so running the whole collection works.
|
|
77
|
+
|
|
78
|
+
**Auth the schema can't show.** Middleware, or a dependency that reads `request.headers`, doesn't appear in OpenAPI. Use `--bearer` to send `{{bearerToken}}` on every request that has no auth of its own.
|
|
79
|
+
|
|
80
|
+
## Check your routes
|
|
81
|
+
|
|
82
|
+
Two more commands read the routes themselves, so they also see what the schema leaves out: routes hidden from the docs, websockets, and plain Starlette routes.
|
|
83
|
+
|
|
84
|
+
`routes` lists everything the app serves and what protects it, like `flask routes`:
|
|
85
|
+
|
|
86
|
+
```console
|
|
87
|
+
$ fastapi2postman routes --app main.py
|
|
88
|
+
METHOD PATH AUTH TAGS NOTE
|
|
89
|
+
GET /health - -
|
|
90
|
+
GET /internal - - hidden from the docs
|
|
91
|
+
GET /me HTTPBearer -
|
|
92
|
+
POST /orders - orders
|
|
93
|
+
* /static ? - can't read: StaticFiles
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`audit` fails when a route has no visible auth, so a route merged without its `Depends()` turns CI red:
|
|
97
|
+
|
|
98
|
+
```console
|
|
99
|
+
$ fastapi2postman audit --app main.py --allow "/health" --allow "/static*"
|
|
100
|
+
2 of 5 routes have no visible auth:
|
|
101
|
+
|
|
102
|
+
GET /internal no auth, hidden from the docs
|
|
103
|
+
POST /orders no auth
|
|
104
|
+
|
|
105
|
+
Protect them, or allow the ones meant to be open, for example --allow "/health" or --allow "GET /public/*".
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
It exits with 1 when it finds any, and 0 otherwise. `--allow` takes a path or a method and a path, and `*` matches anything, including `/`. An `--allow` that matches no route prints a warning, because it could let a route added later through unnoticed.
|
|
109
|
+
|
|
110
|
+
Auth counts when a route's dependencies declare it, at any depth: a security scheme such as `HTTPBearer`, or a required header, query parameter or cookie named like a credential, such as `x_token` or `session_id`.
|
|
111
|
+
|
|
112
|
+
**`audit` reads what routes declare and doesn't run them.** So:
|
|
113
|
+
|
|
114
|
+
- Auth in middleware, or in a dependency reading `request.headers`, looks like no auth. Allow those routes.
|
|
115
|
+
- A dependency that declares a token but never checks it still counts as auth. So does `auto_error=False`, which `routes` shows as `HTTPBearer (optional)`.
|
|
116
|
+
- Plain Starlette routes and mounted apps such as `StaticFiles` have nothing to read, so they're always flagged until you allow them.
|
|
117
|
+
|
|
118
|
+
## Pointing it at your app
|
|
119
|
+
|
|
120
|
+
Run it from your project root.
|
|
121
|
+
|
|
122
|
+
| `--app` | Loads |
|
|
123
|
+
| --- | --- |
|
|
124
|
+
| `main.py` | `app` from that file |
|
|
125
|
+
| `main.py:api` | `api` from that file |
|
|
126
|
+
| `src/shop/main.py` | `app` from that file. Relative imports work if the folder has an `__init__.py` |
|
|
127
|
+
| `shop.main` | `app` from that module |
|
|
128
|
+
| `shop.main:api` | `api` from that module |
|
|
129
|
+
| `shop.main:create_app` | The app that this factory function returns |
|
|
130
|
+
|
|
131
|
+
## Options
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
--app APP the app: main.py, main.py:api or package.main:api. The
|
|
135
|
+
name defaults to app, and may be a factory
|
|
136
|
+
--output OUTPUT output file (default: output.json)
|
|
137
|
+
--name NAME collection name (default: the app's title)
|
|
138
|
+
--base-url BASE_URL where the requests go (default: the app's first server,
|
|
139
|
+
else http://localhost:8000)
|
|
140
|
+
--host HOST shorthand for --base-url http://HOST:PORT
|
|
141
|
+
--port PORT shorthand for --base-url http://HOST:PORT
|
|
142
|
+
--bearer send {{bearerToken}} on every request without auth of
|
|
143
|
+
its own: for auth in middleware, or in a dependency
|
|
144
|
+
reading request.headers, which the app's schema can't
|
|
145
|
+
show
|
|
146
|
+
--version show program's version number and exit
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## From Python
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from fastapi2postman import to_postman
|
|
153
|
+
|
|
154
|
+
collection = to_postman(app, name="Shop", base_url="https://api.example.com")
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`to_postman` returns the collection as a dict.
|
|
158
|
+
|
|
159
|
+
## Limits
|
|
160
|
+
|
|
161
|
+
- **Samples are placeholders.** Any `pattern`, and validators that compare fields, can't be read from the schema. Declare `examples=` on the field where a sample gets rejected.
|
|
162
|
+
- **A request holds one auth.** If a route declares several, the first one Postman supports is used. API keys don't count, because they're sent as plain headers or query parameters.
|
|
163
|
+
- **Cookies are left to Postman's cookie jar**, which fills up when you log in. That covers `Cookie()` parameters and `APIKeyCookie`.
|
|
164
|
+
- **The token script reads the top level of a JSON response.** A login returning a bare `token` field or a nested token isn't detected, because a bare `token` could just as well be a reset or invite token.
|
|
165
|
+
- **Only FastAPI apps are followed into mounts.** Other mounted apps have no schema to read.
|
|
166
|
+
|
|
167
|
+
## Contributing
|
|
168
|
+
|
|
169
|
+
See [CONTRIBUTING.md](https://github.com/ashhadahsan/fastapi2postman/blob/main/CONTRIBUTING.md). Each change is checked by running a generated collection in Newman, Postman's command-line runner, against a live app.
|
|
170
|
+
|
|
171
|
+
## License
|
|
172
|
+
|
|
173
|
+
MIT. Inspired by [flask2postman](https://github.com/numberly/flask2postman).
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# fastapi2postman
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/fastapi2postman/)
|
|
4
|
+
[](https://pypi.org/project/fastapi2postman/)
|
|
5
|
+
[](https://github.com/ashhadahsan/fastapi2postman/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/ashhadahsan/fastapi2postman/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
**Turn a FastAPI app into a Postman collection you can run straight away.**
|
|
9
|
+
|
|
10
|
+
```console
|
|
11
|
+
$ pip install fastapi2postman
|
|
12
|
+
$ fastapi2postman --app main.py
|
|
13
|
+
output.json: 25 requests
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Import `output.json` into Postman, fill in the collection's variables, and send the login request. The token is saved for every other request.
|
|
17
|
+
|
|
18
|
+
Install it in the same environment as your app, because it imports the app to read its schema. Everything comes from `app.openapi()`, so what shows in your `/docs` shows in the collection.
|
|
19
|
+
|
|
20
|
+
## What's in the collection
|
|
21
|
+
|
|
22
|
+
| In your app | In Postman |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| `include_router()` and mounted FastAPI apps | Every route with its full path. Routes with `include_in_schema=False` and mounted apps with `openapi_url=None` stay out |
|
|
25
|
+
| Tags | A folder per tag |
|
|
26
|
+
| A route with several methods | One request per method |
|
|
27
|
+
| Path parameters | Path variables such as `:item_id` with a sample value |
|
|
28
|
+
| Query and header parameters, including those inside `Depends()` | Filled in, with optional ones switched off |
|
|
29
|
+
| Pydantic body models | A sample JSON body that follows your `Field` limits, enums, formats, defaults and declared examples |
|
|
30
|
+
| `Form()` and `File()` | Form fields and file pickers |
|
|
31
|
+
| Docstrings | Request descriptions |
|
|
32
|
+
| Title, version and description | Collection info |
|
|
33
|
+
| `servers`, or `--base-url` | A `{{baseUrl}}` variable |
|
|
34
|
+
|
|
35
|
+
## Auth
|
|
36
|
+
|
|
37
|
+
Auth is found wherever you declared it: on the route, in a nested `Depends()`, in `Security()`, or in the `dependencies=` of a route, `APIRouter`, `include_router()` or `FastAPI`.
|
|
38
|
+
|
|
39
|
+
| In your app | In Postman |
|
|
40
|
+
| --- | --- |
|
|
41
|
+
| `HTTPBearer` and subclasses, any `OAuth2…` scheme, OpenID Connect | Bearer auth with `{{bearerToken}}` |
|
|
42
|
+
| A dependency with an `authorization` header parameter | Bearer auth with `{{bearerToken}}` |
|
|
43
|
+
| `HTTPBasic` | Basic auth with `{{username}}` and `{{password}}` |
|
|
44
|
+
| `APIKeyHeader`, `APIKeyQuery` | The header or query parameter, set to a variable with the same name |
|
|
45
|
+
| A query parameter, header or form field whose name contains `token`, `api_key`, `secret` or `password` | The same, so `x_token` is sent as `{{x-token}}` |
|
|
46
|
+
|
|
47
|
+
Secret variables start blank, so the file holds no credentials. You fill them in inside Postman.
|
|
48
|
+
|
|
49
|
+
**The token saves itself.** A `POST` route that is your `tokenUrl`, or that returns `access_token` or `accessToken`, gets a script that stores the token in `{{bearerToken}}`. Those requests come first in the collection, so running the whole collection works.
|
|
50
|
+
|
|
51
|
+
**Auth the schema can't show.** Middleware, or a dependency that reads `request.headers`, doesn't appear in OpenAPI. Use `--bearer` to send `{{bearerToken}}` on every request that has no auth of its own.
|
|
52
|
+
|
|
53
|
+
## Check your routes
|
|
54
|
+
|
|
55
|
+
Two more commands read the routes themselves, so they also see what the schema leaves out: routes hidden from the docs, websockets, and plain Starlette routes.
|
|
56
|
+
|
|
57
|
+
`routes` lists everything the app serves and what protects it, like `flask routes`:
|
|
58
|
+
|
|
59
|
+
```console
|
|
60
|
+
$ fastapi2postman routes --app main.py
|
|
61
|
+
METHOD PATH AUTH TAGS NOTE
|
|
62
|
+
GET /health - -
|
|
63
|
+
GET /internal - - hidden from the docs
|
|
64
|
+
GET /me HTTPBearer -
|
|
65
|
+
POST /orders - orders
|
|
66
|
+
* /static ? - can't read: StaticFiles
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`audit` fails when a route has no visible auth, so a route merged without its `Depends()` turns CI red:
|
|
70
|
+
|
|
71
|
+
```console
|
|
72
|
+
$ fastapi2postman audit --app main.py --allow "/health" --allow "/static*"
|
|
73
|
+
2 of 5 routes have no visible auth:
|
|
74
|
+
|
|
75
|
+
GET /internal no auth, hidden from the docs
|
|
76
|
+
POST /orders no auth
|
|
77
|
+
|
|
78
|
+
Protect them, or allow the ones meant to be open, for example --allow "/health" or --allow "GET /public/*".
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
It exits with 1 when it finds any, and 0 otherwise. `--allow` takes a path or a method and a path, and `*` matches anything, including `/`. An `--allow` that matches no route prints a warning, because it could let a route added later through unnoticed.
|
|
82
|
+
|
|
83
|
+
Auth counts when a route's dependencies declare it, at any depth: a security scheme such as `HTTPBearer`, or a required header, query parameter or cookie named like a credential, such as `x_token` or `session_id`.
|
|
84
|
+
|
|
85
|
+
**`audit` reads what routes declare and doesn't run them.** So:
|
|
86
|
+
|
|
87
|
+
- Auth in middleware, or in a dependency reading `request.headers`, looks like no auth. Allow those routes.
|
|
88
|
+
- A dependency that declares a token but never checks it still counts as auth. So does `auto_error=False`, which `routes` shows as `HTTPBearer (optional)`.
|
|
89
|
+
- Plain Starlette routes and mounted apps such as `StaticFiles` have nothing to read, so they're always flagged until you allow them.
|
|
90
|
+
|
|
91
|
+
## Pointing it at your app
|
|
92
|
+
|
|
93
|
+
Run it from your project root.
|
|
94
|
+
|
|
95
|
+
| `--app` | Loads |
|
|
96
|
+
| --- | --- |
|
|
97
|
+
| `main.py` | `app` from that file |
|
|
98
|
+
| `main.py:api` | `api` from that file |
|
|
99
|
+
| `src/shop/main.py` | `app` from that file. Relative imports work if the folder has an `__init__.py` |
|
|
100
|
+
| `shop.main` | `app` from that module |
|
|
101
|
+
| `shop.main:api` | `api` from that module |
|
|
102
|
+
| `shop.main:create_app` | The app that this factory function returns |
|
|
103
|
+
|
|
104
|
+
## Options
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
--app APP the app: main.py, main.py:api or package.main:api. The
|
|
108
|
+
name defaults to app, and may be a factory
|
|
109
|
+
--output OUTPUT output file (default: output.json)
|
|
110
|
+
--name NAME collection name (default: the app's title)
|
|
111
|
+
--base-url BASE_URL where the requests go (default: the app's first server,
|
|
112
|
+
else http://localhost:8000)
|
|
113
|
+
--host HOST shorthand for --base-url http://HOST:PORT
|
|
114
|
+
--port PORT shorthand for --base-url http://HOST:PORT
|
|
115
|
+
--bearer send {{bearerToken}} on every request without auth of
|
|
116
|
+
its own: for auth in middleware, or in a dependency
|
|
117
|
+
reading request.headers, which the app's schema can't
|
|
118
|
+
show
|
|
119
|
+
--version show program's version number and exit
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## From Python
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from fastapi2postman import to_postman
|
|
126
|
+
|
|
127
|
+
collection = to_postman(app, name="Shop", base_url="https://api.example.com")
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`to_postman` returns the collection as a dict.
|
|
131
|
+
|
|
132
|
+
## Limits
|
|
133
|
+
|
|
134
|
+
- **Samples are placeholders.** Any `pattern`, and validators that compare fields, can't be read from the schema. Declare `examples=` on the field where a sample gets rejected.
|
|
135
|
+
- **A request holds one auth.** If a route declares several, the first one Postman supports is used. API keys don't count, because they're sent as plain headers or query parameters.
|
|
136
|
+
- **Cookies are left to Postman's cookie jar**, which fills up when you log in. That covers `Cookie()` parameters and `APIKeyCookie`.
|
|
137
|
+
- **The token script reads the top level of a JSON response.** A login returning a bare `token` field or a nested token isn't detected, because a bare `token` could just as well be a reset or invite token.
|
|
138
|
+
- **Only FastAPI apps are followed into mounts.** Other mounted apps have no schema to read.
|
|
139
|
+
|
|
140
|
+
## Contributing
|
|
141
|
+
|
|
142
|
+
See [CONTRIBUTING.md](https://github.com/ashhadahsan/fastapi2postman/blob/main/CONTRIBUTING.md). Each change is checked by running a generated collection in Newman, Postman's command-line runner, against a live app.
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
MIT. Inspired by [flask2postman](https://github.com/numberly/flask2postman).
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "fastapi2postman"
|
|
3
|
+
version = "0.3.0"
|
|
4
|
+
description = "Turn a FastAPI app into a Postman collection you can run straight away."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
requires-python = ">=3.11"
|
|
9
|
+
dependencies = ["fastapi>=0.105"]
|
|
10
|
+
keywords = [
|
|
11
|
+
"fastapi",
|
|
12
|
+
"postman",
|
|
13
|
+
"newman",
|
|
14
|
+
"openapi",
|
|
15
|
+
"api",
|
|
16
|
+
"cli",
|
|
17
|
+
]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 4 - Beta",
|
|
20
|
+
"Environment :: Console",
|
|
21
|
+
"Framework :: FastAPI",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Programming Language :: Python :: 3.14",
|
|
28
|
+
"Programming Language :: Python :: 3.15",
|
|
29
|
+
"Topic :: Software Development :: Testing",
|
|
30
|
+
"Typing :: Typed",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[[project.authors]]
|
|
34
|
+
name = "Ashhad Ahsan Rehman"
|
|
35
|
+
email = "ashhadahsan@gmail.com"
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Source = "https://github.com/ashhadahsan/fastapi2postman"
|
|
39
|
+
Changelog = "https://github.com/ashhadahsan/fastapi2postman/blob/main/CHANGELOG.md"
|
|
40
|
+
|
|
41
|
+
[project.scripts]
|
|
42
|
+
fastapi2postman = "fastapi2postman.cli:main"
|
|
43
|
+
|
|
44
|
+
[build-system]
|
|
45
|
+
requires = ["uv_build>=0.13.6,<0.14"]
|
|
46
|
+
build-backend = "uv_build"
|
|
47
|
+
|
|
48
|
+
[tool.ruff]
|
|
49
|
+
line-length = 120
|
|
50
|
+
|
|
51
|
+
[tool.ruff.lint]
|
|
52
|
+
select = [
|
|
53
|
+
"E",
|
|
54
|
+
"F",
|
|
55
|
+
"I",
|
|
56
|
+
"UP",
|
|
57
|
+
"B",
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
[tool.mypy]
|
|
61
|
+
strict = true
|
|
62
|
+
files = ["src"]
|
|
63
|
+
|
|
64
|
+
[tool.pytest.ini_options]
|
|
65
|
+
testpaths = ["tests"]
|
|
66
|
+
filterwarnings = ["ignore:Duplicate Operation ID:UserWarning"]
|
|
67
|
+
|
|
68
|
+
[tool.coverage.run]
|
|
69
|
+
source = ["fastapi2postman"]
|
|
70
|
+
|
|
71
|
+
[dependency-groups]
|
|
72
|
+
dev = [
|
|
73
|
+
"coverage>=7.17.2",
|
|
74
|
+
"jsonschema>=4.26.0",
|
|
75
|
+
"mypy>=2.4.1",
|
|
76
|
+
"pytest>=9.2.0",
|
|
77
|
+
"python-multipart>=0.0.33",
|
|
78
|
+
"ruff>=0.17.3",
|
|
79
|
+
"uvicorn>=0.54.0",
|
|
80
|
+
]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "fastapi2postman"
|
|
3
|
+
version = "0.3.0"
|
|
4
|
+
description = "Turn a FastAPI app into a Postman collection you can run straight away."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
authors = [{ name = "Ashhad Ahsan Rehman", email = "ashhadahsan@gmail.com" }]
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
dependencies = ["fastapi>=0.105"]
|
|
11
|
+
keywords = ["fastapi", "postman", "newman", "openapi", "api", "cli"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 4 - Beta",
|
|
14
|
+
"Environment :: Console",
|
|
15
|
+
"Framework :: FastAPI",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
"Programming Language :: Python :: 3.13",
|
|
21
|
+
"Programming Language :: Python :: 3.14",
|
|
22
|
+
"Programming Language :: Python :: 3.15",
|
|
23
|
+
"Topic :: Software Development :: Testing",
|
|
24
|
+
"Typing :: Typed",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Source = "https://github.com/ashhadahsan/fastapi2postman"
|
|
29
|
+
Changelog = "https://github.com/ashhadahsan/fastapi2postman/blob/main/CHANGELOG.md"
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
fastapi2postman = "fastapi2postman.cli:main"
|
|
33
|
+
|
|
34
|
+
[build-system]
|
|
35
|
+
requires = ["uv_build>=0.13.6,<0.14"]
|
|
36
|
+
build-backend = "uv_build"
|
|
37
|
+
|
|
38
|
+
[tool.ruff]
|
|
39
|
+
line-length = 120
|
|
40
|
+
|
|
41
|
+
[tool.ruff.lint]
|
|
42
|
+
select = ["E", "F", "I", "UP", "B"]
|
|
43
|
+
|
|
44
|
+
[tool.mypy]
|
|
45
|
+
strict = true
|
|
46
|
+
files = ["src"]
|
|
47
|
+
|
|
48
|
+
[tool.pytest.ini_options]
|
|
49
|
+
testpaths = ["tests"]
|
|
50
|
+
# FastAPI warns about every route with several methods; the collection doesn't use operation ids.
|
|
51
|
+
filterwarnings = ["ignore:Duplicate Operation ID:UserWarning"]
|
|
52
|
+
|
|
53
|
+
[tool.coverage.run]
|
|
54
|
+
source = ["fastapi2postman"]
|
|
55
|
+
|
|
56
|
+
[dependency-groups]
|
|
57
|
+
dev = [
|
|
58
|
+
"coverage>=7.17.2",
|
|
59
|
+
"jsonschema>=4.26.0",
|
|
60
|
+
"mypy>=2.4.1",
|
|
61
|
+
"pytest>=9.2.0",
|
|
62
|
+
"python-multipart>=0.0.33",
|
|
63
|
+
"ruff>=0.17.3",
|
|
64
|
+
"uvicorn>=0.54.0",
|
|
65
|
+
]
|