python-postman 0.3.0__tar.gz → 0.6.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.
- python_postman-0.6.0/PKG-INFO +52 -0
- python_postman-0.6.0/README.md +28 -0
- python_postman-0.6.0/pyproject.toml +59 -0
- python_postman-0.6.0/src/python_postman/body.py +33 -0
- python_postman-0.6.0/src/python_postman/config.py +110 -0
- {python-postman-0.3.0/pypostman → python_postman-0.6.0/src/python_postman}/environment.py +1 -1
- {python-postman-0.3.0/pypostman → python_postman-0.6.0/src/python_postman}/modules/http.py +78 -18
- {python-postman-0.3.0/pypostman → python_postman-0.6.0/src/python_postman}/utils/cli.py +23 -3
- python_postman-0.6.0/src/python_postman/utils/load_dotenvc.py +101 -0
- python-postman-0.3.0/.gitignore +0 -131
- python-postman-0.3.0/PKG-INFO +0 -73
- python-postman-0.3.0/README.md +0 -60
- python-postman-0.3.0/examples/collections/Coinmarketcap.postman_collection.json +0 -180
- python-postman-0.3.0/examples/collections/PokeAPI.postman_collection.json +0 -66
- python-postman-0.3.0/examples/models/coinmarketcap_example.py +0 -63
- python-postman-0.3.0/examples/models/pokeapi_example.py +0 -66
- python-postman-0.3.0/pypostman/body.py +0 -8
- python-postman-0.3.0/pypostman/config.py +0 -108
- python-postman-0.3.0/pypostman/utils/load_dotenvc.py +0 -99
- python-postman-0.3.0/pyproject.toml +0 -23
- python-postman-0.3.0/python_postman.egg-info/PKG-INFO +0 -73
- python-postman-0.3.0/python_postman.egg-info/SOURCES.txt +0 -34
- python-postman-0.3.0/python_postman.egg-info/dependency_links.txt +0 -1
- python-postman-0.3.0/python_postman.egg-info/entry_points.txt +0 -2
- python-postman-0.3.0/python_postman.egg-info/requires.txt +0 -16
- python-postman-0.3.0/python_postman.egg-info/top_level.txt +0 -1
- python-postman-0.3.0/setup.cfg +0 -4
- {python-postman-0.3.0 → python_postman-0.6.0}/LICENSE +0 -0
- {python-postman-0.3.0/pypostman → python_postman-0.6.0/src/python_postman}/__init__.py +0 -0
- {python-postman-0.3.0/pypostman → python_postman-0.6.0/src/python_postman}/__main__.py +0 -0
- {python-postman-0.3.0/pypostman → python_postman-0.6.0/src/python_postman}/auth.py +0 -0
- {python-postman-0.3.0/pypostman → python_postman-0.6.0/src/python_postman}/collection.py +0 -0
- {python-postman-0.3.0/pypostman → python_postman-0.6.0/src/python_postman}/event.py +0 -0
- {python-postman-0.3.0/pypostman → python_postman-0.6.0/src/python_postman}/header.py +0 -0
- {python-postman-0.3.0/pypostman → python_postman-0.6.0/src/python_postman}/item.py +0 -0
- {python-postman-0.3.0/pypostman → python_postman-0.6.0/src/python_postman}/modules/file.py +0 -0
- {python-postman-0.3.0/pypostman → python_postman-0.6.0/src/python_postman}/modules/logger.py +0 -0
- {python-postman-0.3.0/pypostman → python_postman-0.6.0/src/python_postman}/postman.py +0 -0
- {python-postman-0.3.0/pypostman → python_postman-0.6.0/src/python_postman}/request.py +0 -0
- {python-postman-0.3.0/pypostman → python_postman-0.6.0/src/python_postman}/template.py +0 -0
- {python-postman-0.3.0/pypostman → python_postman-0.6.0/src/python_postman}/url.py +0 -0
- {python-postman-0.3.0/pypostman → python_postman-0.6.0/src/python_postman}/variable.py +0 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: python-postman
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: Pypostman allows users to parse postman environments and postman collections.
|
|
5
|
+
Author: Data Engineering Team
|
|
6
|
+
Author-email: DataEngineering@loves.com
|
|
7
|
+
Requires-Python: >=3.8.1,<4.0.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Requires-Dist: boto3 (>=1.34.67,<2.0.0)
|
|
14
|
+
Requires-Dist: cryptography (>=42.0.5,<43.0.0)
|
|
15
|
+
Requires-Dist: logbook (>=1.7.0.post0,<2.0.0)
|
|
16
|
+
Requires-Dist: pendulum (>=3.0.0,<4.0.0)
|
|
17
|
+
Requires-Dist: pydantic (>=2.6.4,<3.0.0)
|
|
18
|
+
Requires-Dist: pytz (>=2024.1,<2025.0)
|
|
19
|
+
Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
|
|
20
|
+
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
|
21
|
+
Requires-Dist: urllib3 (>=1.25.4,<2.0.0)
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# Python Postman
|
|
25
|
+
|
|
26
|
+
**pypostman** is a command-line interface that allows to `automate` multiple api calls from postman collections, additionally it also allow you to `compress` and `save` the response to a local directory or to an AWS S3 bucket.
|
|
27
|
+
Thereby allowing you to manage your api calls using postman, then `automate` and `process` their response using python.
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
https://github.com/yudiell/energy-apis
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Included Modules
|
|
36
|
+
- http.py
|
|
37
|
+
- logger.py
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
See the example.
|
|
41
|
+
|
|
42
|
+
### Python >= 3.8.1
|
|
43
|
+
|
|
44
|
+
Pypi:
|
|
45
|
+
```
|
|
46
|
+
pip install python-postman
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## How to Use It
|
|
50
|
+
|
|
51
|
+
See example
|
|
52
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Python Postman
|
|
2
|
+
|
|
3
|
+
**pypostman** is a command-line interface that allows to `automate` multiple api calls from postman collections, additionally it also allow you to `compress` and `save` the response to a local directory or to an AWS S3 bucket.
|
|
4
|
+
Thereby allowing you to manage your api calls using postman, then `automate` and `process` their response using python.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
https://github.com/yudiell/energy-apis
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
### Included Modules
|
|
13
|
+
- http.py
|
|
14
|
+
- logger.py
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
See the example.
|
|
18
|
+
|
|
19
|
+
### Python >= 3.8.1
|
|
20
|
+
|
|
21
|
+
Pypi:
|
|
22
|
+
```
|
|
23
|
+
pip install python-postman
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## How to Use It
|
|
27
|
+
|
|
28
|
+
See example
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "python-postman"
|
|
3
|
+
version = "0.6.0"
|
|
4
|
+
description = "Pypostman allows users to parse postman environments and postman collections."
|
|
5
|
+
authors = ["Data Engineering Team <DataEngineering@loves.com>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
|
|
8
|
+
[tool.isort]
|
|
9
|
+
profile = "black"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
[tool.poetry.dependencies]
|
|
13
|
+
python = "^3.8.1"
|
|
14
|
+
requests = "^2.32.3"
|
|
15
|
+
pyyaml = "^6.0.1"
|
|
16
|
+
pydantic = "^2.6.4"
|
|
17
|
+
cryptography = "^42.0.5"
|
|
18
|
+
urllib3 = "^1.25.4"
|
|
19
|
+
logbook = "^1.7.0.post0"
|
|
20
|
+
pendulum = "^3.0.0"
|
|
21
|
+
boto3 = "^1.34.67"
|
|
22
|
+
pytz = "^2024.1"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
[tool.poetry.group.lint.dependencies]
|
|
26
|
+
flake8-annotations = "^3.0.1"
|
|
27
|
+
flake8-builtins = "^2.1.0"
|
|
28
|
+
flake8-docstrings = "^1.7.0"
|
|
29
|
+
flake8-eradicate = "^1.5.0"
|
|
30
|
+
flake8-future-annotations = "^1.1.0"
|
|
31
|
+
flake8-isort = "^6.0.0"
|
|
32
|
+
flake8-new-union-types = "^0.4.1"
|
|
33
|
+
flake8-pep585 = "^0.1.7"
|
|
34
|
+
flake8-secure-coding-standard = "^1.4.0"
|
|
35
|
+
flake8-type-checking = "^2.4.1"
|
|
36
|
+
flake8-use-pathlib = "^0.3.0"
|
|
37
|
+
flake8-variables-names = "^0.0.6"
|
|
38
|
+
pep8-naming = "^0.13.3"
|
|
39
|
+
black = "^24.3.0"
|
|
40
|
+
isort = "^5.12.0"
|
|
41
|
+
pip-audit = "^2.6.1"
|
|
42
|
+
mypy = "^1.9.0"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
[tool.poetry.group.test.dependencies]
|
|
46
|
+
pytest = "^7.4.0"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
[tool.poetry.group.dev.dependencies]
|
|
50
|
+
pre-commit = "^3.3.3"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
[build-system]
|
|
54
|
+
requires = ["poetry-core"]
|
|
55
|
+
build-backend = "poetry.core.masonry.api"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
[tool.poetry.scripts]
|
|
59
|
+
pypostman = "pypostman.__main__:main"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
from .config import Body
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Body:
|
|
6
|
+
def __init__(self, body: Body) -> None:
|
|
7
|
+
self.mode = body.mode
|
|
8
|
+
self.raw = body.raw
|
|
9
|
+
self.options = body.options
|
|
10
|
+
self.formdata = body.formdata
|
|
11
|
+
self.urlencoded = body.urlencoded
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def urlencoded_as_dict(self) -> Dict[str, str]:
|
|
15
|
+
if not self.urlencoded:
|
|
16
|
+
return None
|
|
17
|
+
|
|
18
|
+
body = {}
|
|
19
|
+
for option in self.urlencoded:
|
|
20
|
+
if not option.get("disabled", False):
|
|
21
|
+
body[option["key"]] = option["value"]
|
|
22
|
+
return body
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def formdata_as_dict(self) -> Dict[str, str]:
|
|
26
|
+
if not self.formdata:
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
body = {}
|
|
30
|
+
for option in self.formdata:
|
|
31
|
+
if not option.get("disabled", False):
|
|
32
|
+
body[option["key"]] = option["value"]
|
|
33
|
+
return body
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from typing import List, Dict, Union, Optional
|
|
2
|
+
from pydantic import Field, BaseModel
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Info(BaseModel):
|
|
6
|
+
postman_id: str = Field(None, alias="_postman_id")
|
|
7
|
+
name: str
|
|
8
|
+
postman_schema: str = Field(None, alias="schema")
|
|
9
|
+
exporter_id: str = Field(None, alias="_exporter_id")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Variable Related Objects
|
|
13
|
+
class Variables(BaseModel):
|
|
14
|
+
key: Optional[str] = None
|
|
15
|
+
value: Optional[str] = None
|
|
16
|
+
type: Optional[str] = None
|
|
17
|
+
disabled: Optional[bool] = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Auth Related Objects
|
|
21
|
+
class AuthValues(BaseModel):
|
|
22
|
+
key: str
|
|
23
|
+
value: str
|
|
24
|
+
type: str
|
|
25
|
+
disabled: Optional[bool] = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Auth(BaseModel):
|
|
29
|
+
type: str
|
|
30
|
+
noauth: Optional[List[AuthValues]] = None
|
|
31
|
+
basic: Optional[List[AuthValues]] = None
|
|
32
|
+
apikey: Optional[List[AuthValues]] = None
|
|
33
|
+
bearer: Optional[List[AuthValues]] = None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# Collection and Request Related Objects
|
|
37
|
+
class Script(BaseModel):
|
|
38
|
+
type: Optional[str] = None
|
|
39
|
+
exec: List[str]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class Event(BaseModel):
|
|
43
|
+
listen: Optional[str] = None
|
|
44
|
+
script: Optional[Script] = None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# Request Related Objects
|
|
48
|
+
class Header(BaseModel):
|
|
49
|
+
key: Optional[str] = None
|
|
50
|
+
value: Optional[str] = None
|
|
51
|
+
description: Optional[str] = None
|
|
52
|
+
disabled: Optional[bool] = None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class Variable(BaseModel):
|
|
56
|
+
key: Optional[str] = None
|
|
57
|
+
value: Optional[str] = None
|
|
58
|
+
description: Optional[str] = None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class Param(BaseModel):
|
|
62
|
+
key: Optional[str] = None
|
|
63
|
+
value: Optional[str] = None
|
|
64
|
+
description: Optional[str] = None
|
|
65
|
+
disabled: Optional[bool] = None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class Body(BaseModel):
|
|
69
|
+
mode: Optional[str] = None
|
|
70
|
+
raw: Optional[str] = None
|
|
71
|
+
formdata: Optional[List[Dict[str, Union[str, bool]]]] = None
|
|
72
|
+
urlencoded: Optional[List[Dict[str, Union[str, bool]]]] = None
|
|
73
|
+
options: Optional[Dict[str, Dict[str, Union[str, bool]]]] = None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class Url(BaseModel):
|
|
77
|
+
raw: Optional[str] = None
|
|
78
|
+
protocol: Optional[str] = None
|
|
79
|
+
host: Optional[List[str]] = None
|
|
80
|
+
path: Optional[List[str]] = None
|
|
81
|
+
variable: Optional[List[Variable]] = None
|
|
82
|
+
query: Optional[List[Param]] = None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class Request(BaseModel):
|
|
86
|
+
auth: Optional[Auth] = None
|
|
87
|
+
method: str
|
|
88
|
+
headers: List[Header] = Field(None, alias="header")
|
|
89
|
+
url: Optional[Url] = None
|
|
90
|
+
body: Optional[Body] = None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# Collection Related Objects
|
|
94
|
+
class Item(BaseModel):
|
|
95
|
+
name: str
|
|
96
|
+
item: Optional[List["Item"]] = None
|
|
97
|
+
events: Optional[List[Event]] = Field(None, alias="event")
|
|
98
|
+
request: Optional[Request] = None
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def type(self):
|
|
102
|
+
return "request" if self.request else "folder"
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class Config(BaseModel):
|
|
106
|
+
info: Optional[Info] = None
|
|
107
|
+
items: Optional[List[Item]] = Field(None, alias="item")
|
|
108
|
+
variables: Optional[List[Variables]] = Field(None, alias="variable")
|
|
109
|
+
events: Optional[List[Event]] = Field(None, alias="event")
|
|
110
|
+
auth: Optional[Auth] = None
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import json
|
|
3
|
+
import re
|
|
3
4
|
from requests import Session, Response
|
|
4
5
|
from urllib3 import Timeout
|
|
5
6
|
|
|
@@ -21,46 +22,48 @@ class Request(Session):
|
|
|
21
22
|
self.timeout: Timeout = timeout
|
|
22
23
|
self.stream: bool = stream
|
|
23
24
|
self.url: str = self._request.url.base_url
|
|
25
|
+
self.body = None
|
|
26
|
+
self.prepare_cookies = None
|
|
24
27
|
|
|
25
|
-
def
|
|
28
|
+
def set_headers(self, headers: dict):
|
|
26
29
|
"""
|
|
27
|
-
Set
|
|
30
|
+
Set the headers of the request.
|
|
28
31
|
|
|
29
32
|
Args:
|
|
30
|
-
|
|
33
|
+
headers (dict): The headers to set.
|
|
31
34
|
|
|
32
35
|
Returns:
|
|
33
36
|
None
|
|
34
37
|
"""
|
|
35
|
-
if self._request.
|
|
36
|
-
text = json.dumps(self._request.
|
|
37
|
-
template: str = CustomTemplate(text).safe_substitute(
|
|
38
|
-
|
|
38
|
+
if self._request.headers:
|
|
39
|
+
text = json.dumps(self._request.headers.as_dict)
|
|
40
|
+
template: str = CustomTemplate(text).safe_substitute(headers)
|
|
41
|
+
headers = {
|
|
39
42
|
key: value
|
|
40
43
|
for key, value in json.loads(template).items()
|
|
41
44
|
if "${" not in value
|
|
42
45
|
}
|
|
43
|
-
self.
|
|
46
|
+
self.headers = headers
|
|
44
47
|
|
|
45
|
-
def
|
|
48
|
+
def set_params(self, params: dict):
|
|
46
49
|
"""
|
|
47
|
-
Set
|
|
50
|
+
Set URL parameters on the request object.
|
|
48
51
|
|
|
49
52
|
Args:
|
|
50
|
-
|
|
53
|
+
params (dict): Parameters to set on the request object.
|
|
51
54
|
|
|
52
55
|
Returns:
|
|
53
56
|
None
|
|
54
57
|
"""
|
|
55
|
-
if self._request.
|
|
56
|
-
text = json.dumps(self._request.
|
|
57
|
-
template: str = CustomTemplate(text).safe_substitute(
|
|
58
|
-
|
|
58
|
+
if self._request.url.params:
|
|
59
|
+
text = json.dumps(self._request.url.params)
|
|
60
|
+
template: str = CustomTemplate(text).safe_substitute(params)
|
|
61
|
+
params = {
|
|
59
62
|
key: value
|
|
60
63
|
for key, value in json.loads(template).items()
|
|
61
64
|
if "${" not in value
|
|
62
65
|
}
|
|
63
|
-
self.
|
|
66
|
+
self.params = params
|
|
64
67
|
|
|
65
68
|
def set_path_vars(self, path_variables: dict):
|
|
66
69
|
"""
|
|
@@ -77,6 +80,62 @@ class Request(Session):
|
|
|
77
80
|
path: str = CustomTemplate(request_url).safe_substitute(path_variables)
|
|
78
81
|
self.url = path
|
|
79
82
|
|
|
83
|
+
def set_body(self, body: dict, with_quuotes: bool = True):
|
|
84
|
+
"""
|
|
85
|
+
Set body payload.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
body (dict): Parameters to set on the request object.
|
|
89
|
+
with_quotes (bool) default=True: Add/remove quotes on body parameters.
|
|
90
|
+
Returns:
|
|
91
|
+
None
|
|
92
|
+
"""
|
|
93
|
+
# The pattern looks for ${...} that's not surrounded by quotes
|
|
94
|
+
pattern = r'(?<!")(\$\{[^}]+\})(?!")'
|
|
95
|
+
# Replacement pattern that adds quotes around the matched pattern
|
|
96
|
+
if with_quuotes:
|
|
97
|
+
replacement = r'"\1"'
|
|
98
|
+
else:
|
|
99
|
+
replacement = r"\1"
|
|
100
|
+
raw = (
|
|
101
|
+
re.sub(pattern, replacement, self._request.body.raw)
|
|
102
|
+
if self._request.body.raw
|
|
103
|
+
else None
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
formdata = (
|
|
107
|
+
json.dumps(self._request.body.formdata_as_dict)
|
|
108
|
+
if self._request.body.formdata_as_dict
|
|
109
|
+
else None
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
urlencoded = (
|
|
113
|
+
json.dumps(self._request.body.urlencoded_as_dict)
|
|
114
|
+
if self._request.body.urlencoded_as_dict
|
|
115
|
+
else None
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
options_list = [
|
|
119
|
+
formdata,
|
|
120
|
+
urlencoded,
|
|
121
|
+
]
|
|
122
|
+
options = next(
|
|
123
|
+
(option for option in options_list if option is not None),
|
|
124
|
+
ModuleNotFoundError,
|
|
125
|
+
)
|
|
126
|
+
if self._request.body.formdata or self._request.body.urlencoded:
|
|
127
|
+
text = options
|
|
128
|
+
template: str = CustomTemplate(text).safe_substitute(body)
|
|
129
|
+
items = {
|
|
130
|
+
key: value
|
|
131
|
+
for key, value in json.loads(template).items()
|
|
132
|
+
if "${" not in value
|
|
133
|
+
}
|
|
134
|
+
self.body = items
|
|
135
|
+
else:
|
|
136
|
+
substitute_body: str = CustomTemplate(raw).safe_substitute(body)
|
|
137
|
+
self.body = substitute_body
|
|
138
|
+
|
|
80
139
|
def substitute_bearer_token(self) -> None:
|
|
81
140
|
if self._request.auth and self._request.auth.type == "bearer":
|
|
82
141
|
self._request.auth.http_auth.token = CustomTemplate(
|
|
@@ -100,13 +159,13 @@ class Request(Session):
|
|
|
100
159
|
url = self.url
|
|
101
160
|
headers = self._request.headers.as_dict
|
|
102
161
|
params = self.params
|
|
103
|
-
data =
|
|
162
|
+
data = self.body
|
|
104
163
|
timeout = self.timeout
|
|
105
164
|
stream = self.stream
|
|
106
165
|
auth = request.auth.http_auth
|
|
166
|
+
prepare_cookies = self.prepare_cookies
|
|
107
167
|
|
|
108
168
|
self.log.request(url=url)
|
|
109
|
-
|
|
110
169
|
response = session.request(
|
|
111
170
|
auth=auth,
|
|
112
171
|
method=method,
|
|
@@ -116,6 +175,7 @@ class Request(Session):
|
|
|
116
175
|
data=data,
|
|
117
176
|
stream=stream,
|
|
118
177
|
timeout=timeout,
|
|
178
|
+
cookies=prepare_cookies,
|
|
119
179
|
)
|
|
120
180
|
|
|
121
181
|
if response.ok:
|
|
@@ -7,7 +7,7 @@ class Cli:
|
|
|
7
7
|
|
|
8
8
|
@property
|
|
9
9
|
def envs(self):
|
|
10
|
-
envs: list = ["temp", "dev", "prod"]
|
|
10
|
+
envs: list = ["temp", "dev", "prod", "qa"]
|
|
11
11
|
return envs
|
|
12
12
|
|
|
13
13
|
def parse_arguments(self):
|
|
@@ -28,13 +28,33 @@ class Cli:
|
|
|
28
28
|
required=False,
|
|
29
29
|
help="Tag that determines which tagged flow runs, select from the flows corresponding model flow.",
|
|
30
30
|
)
|
|
31
|
+
|
|
32
|
+
parser.add_argument(
|
|
33
|
+
"--select",
|
|
34
|
+
metavar="SELECT",
|
|
35
|
+
type=str,
|
|
36
|
+
nargs="*",
|
|
37
|
+
required=False,
|
|
38
|
+
help="Select allows you to select multiple tags, ex: --select tag:tag1 tag:tag2 tag:tag3",
|
|
39
|
+
default=[],
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
parser.add_argument(
|
|
43
|
+
"--args",
|
|
44
|
+
metavar="ARGS",
|
|
45
|
+
type=str,
|
|
46
|
+
nargs="*",
|
|
47
|
+
required=False,
|
|
48
|
+
help="Additional arguments to pass to the flow, ex: --args arg1 arg2 arg3",
|
|
49
|
+
default=[],
|
|
50
|
+
)
|
|
51
|
+
|
|
31
52
|
parser.add_argument(
|
|
32
53
|
"-r",
|
|
33
54
|
"--refresh",
|
|
34
55
|
type=str,
|
|
35
56
|
required=False,
|
|
36
|
-
help="",
|
|
37
|
-
choices=["full"],
|
|
57
|
+
help="Add a date to refresh the data.",
|
|
38
58
|
)
|
|
39
59
|
# get args
|
|
40
60
|
args = parser.parse_args()
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This script provides functionalities for encrypting a file and decrypting an AES encrypted file.
|
|
3
|
+
The decrypted file's contents are then loaded into the environment using the dotenv library.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import argparse
|
|
8
|
+
import pyAesCrypt
|
|
9
|
+
from getpass import getpass
|
|
10
|
+
from dotenv import load_dotenv
|
|
11
|
+
|
|
12
|
+
from pypostman.modules.logger import Log
|
|
13
|
+
|
|
14
|
+
log = Log() # Initialize the logger
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def parse_arguments(description, arguments):
|
|
18
|
+
"""
|
|
19
|
+
Parses command line arguments.
|
|
20
|
+
|
|
21
|
+
Parameters:
|
|
22
|
+
description (str): Description of the command line program.
|
|
23
|
+
arguments (list): A list of tuples, where each tuple is an argument in the format (short_option, long_option, description).
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
argparse.Namespace: The parsed command line arguments.
|
|
27
|
+
"""
|
|
28
|
+
parser = argparse.ArgumentParser(description=description)
|
|
29
|
+
for argument in arguments:
|
|
30
|
+
parser.add_argument(
|
|
31
|
+
argument[0], argument[1], type=str, required=True, help=argument[2]
|
|
32
|
+
)
|
|
33
|
+
return parser.parse_args()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def encrypt_file():
|
|
37
|
+
"""
|
|
38
|
+
Encrypts a file with AES encryption. The password and file path are provided as command line arguments.
|
|
39
|
+
"""
|
|
40
|
+
args = parse_arguments(
|
|
41
|
+
"Encrypt a file",
|
|
42
|
+
[
|
|
43
|
+
("-p", "--password", "The password to encrypt the file"),
|
|
44
|
+
("-f", "--file", "The path to the file to encrypt"),
|
|
45
|
+
],
|
|
46
|
+
)
|
|
47
|
+
filepath = args.file
|
|
48
|
+
password = args.password
|
|
49
|
+
bufferSize = 64 * 1024 # Encryption/decryption buffer size
|
|
50
|
+
confirm_password = getpass("Please confirm your password: ")
|
|
51
|
+
|
|
52
|
+
if password != confirm_password:
|
|
53
|
+
log.error("Passwords do not match!")
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
encrypted_filepath = filepath + ".aes" # Generate encrypted file path
|
|
57
|
+
pyAesCrypt.encryptFile(
|
|
58
|
+
filepath, encrypted_filepath, password, bufferSize
|
|
59
|
+
) # Encrypt the file
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
log.info(
|
|
63
|
+
f"Encrypted file: {encrypted_filepath}"
|
|
64
|
+
) # Log the path of the encrypted file
|
|
65
|
+
except ValueError as ve:
|
|
66
|
+
log.error(ve)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def decrypt_and_loadenv(password: str, filepath: str, remove_decrypted: bool = True):
|
|
70
|
+
"""
|
|
71
|
+
Decrypts an AES encrypted file and loads its contents to the environment using the dotenv library.
|
|
72
|
+
|
|
73
|
+
Parameters:
|
|
74
|
+
password (str): The password to decrypt the AES encrypted file.
|
|
75
|
+
filepath (str): The path to the AES encrypted file that needs to be decrypted.
|
|
76
|
+
remove_decrypted (bool): A flag that, if True, removes the decrypted file after its contents are loaded to the environment.
|
|
77
|
+
Default value is True. If False, the decrypted file will remain in the file system,
|
|
78
|
+
and its path will be logged.
|
|
79
|
+
"""
|
|
80
|
+
bufferSize = 64 * 1024 # Encryption/decryption buffer size
|
|
81
|
+
decrypted_filepath = filepath.replace(".aes", "") # Generate decrypted file path
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
pyAesCrypt.decryptFile(
|
|
85
|
+
filepath, decrypted_filepath, password, bufferSize
|
|
86
|
+
) # Decrypt the file
|
|
87
|
+
load_dotenv(
|
|
88
|
+
dotenv_path=decrypted_filepath
|
|
89
|
+
) # Load the decrypted content to environment
|
|
90
|
+
except Exception as e:
|
|
91
|
+
log.error(f"An error occurred during decryption: {str(e)}")
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
if remove_decrypted:
|
|
95
|
+
os.remove(decrypted_filepath) # Remove the decrypted file
|
|
96
|
+
else:
|
|
97
|
+
log.info(decrypted_filepath) # Log the path of the decrypted file
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
if __name__ == "__main__":
|
|
101
|
+
encrypt_file()
|