From 697e8e29aa29ca35e97f71a6f3791b5b90c7b720 Mon Sep 17 00:00:00 2001 From: sabuhish Date: Thu, 9 Jun 2022 14:57:49 +0400 Subject: [PATCH 1/6] Added poetry as a dependecy and formatter --- opa_client/OpaExceptions/__init__.py | 19 ++++++++----- opa_client/opa.py | 29 +++++++++++++------- opa_client/test/test_opa.py | 1 + setup.py | 40 ---------------------------- 4 files changed, 33 insertions(+), 56 deletions(-) delete mode 100644 setup.py diff --git a/opa_client/OpaExceptions/__init__.py b/opa_client/OpaExceptions/__init__.py index d456387..0b49788 100644 --- a/opa_client/OpaExceptions/__init__.py +++ b/opa_client/OpaExceptions/__init__.py @@ -1,6 +1,13 @@ -from .OpaExceptions import (CheckPermissionError, - ConnectionsError, DeleteDataError, - DeletePolicyError, PathNotFoundError, - PolicyNotFoundError, RegoParseError, - SSLError, FileError, TypeExecption, - QueryExecuteError) +from .OpaExceptions import ( + CheckPermissionError, + ConnectionsError, + DeleteDataError, + DeletePolicyError, + FileError, + PathNotFoundError, + PolicyNotFoundError, + QueryExecuteError, + RegoParseError, + SSLError, + TypeExecption, +) diff --git a/opa_client/opa.py b/opa_client/opa.py index 5a51efe..2e80ab9 100644 --- a/opa_client/opa.py +++ b/opa_client/opa.py @@ -8,19 +8,28 @@ # \|_______| \|__| \|__|\|__| # ############################################ -import requests -import urllib3 import json import os -from user_agent import generate_user_agent -from typing import Union, Dict +from typing import Dict, Union from urllib.parse import urlencode -from .OpaExceptions import (CheckPermissionError, - ConnectionsError, DeleteDataError, - DeletePolicyError, PathNotFoundError, - PolicyNotFoundError, RegoParseError, - SSLError, FileError, TypeExecption, - QueryExecuteError) + +import requests +import urllib3 +from user_agent import generate_user_agent + +from .OpaExceptions import ( + CheckPermissionError, + ConnectionsError, + DeleteDataError, + DeletePolicyError, + FileError, + PathNotFoundError, + PolicyNotFoundError, + QueryExecuteError, + RegoParseError, + SSLError, + TypeExecption, +) __version__ = "1.3.3" __author__ = "Tural Muradov" diff --git a/opa_client/test/test_opa.py b/opa_client/test/test_opa.py index 393a0b9..9938319 100644 --- a/opa_client/test/test_opa.py +++ b/opa_client/test/test_opa.py @@ -6,6 +6,7 @@ from unittest import TestCase + from opa_client.opa import OpaClient from opa_client.OpaExceptions import DeleteDataError, DeletePolicyError diff --git a/setup.py b/setup.py deleted file mode 100644 index ca5d3e3..0000000 --- a/setup.py +++ /dev/null @@ -1,40 +0,0 @@ -""" - OPA-python-client - Python client integrates with Open Policy Agent (OPA) service - -""" - -import setuptools - -with open("README.md", "r") as fh: - long_description = fh.read() - -setuptools.setup( - name="OPA-python-client", - version="1.3.3", - author="Tural Muradov", - author_email="tural_m@hotmail.com", - description="Client for connection to the OPA service", - long_description=long_description, - long_description_content_type="text/markdown", - url="https://github.com/Turall/OPA-python-client.git", - license="MIT", - install_requires=[ - "user-agent>=0.1.10", - "requests>=2.27.1", - "urllib3>=1.26.9", - "certifi>=2021.10.8", - ], - packages=setuptools.find_packages(), - classifiers=[ - "Intended Audience :: Developers", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3 :: Only", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - ], - python_requires=">=3.6", -) From e17344bfd5e954ed7d3b500dbcd5978da97325e9 Mon Sep 17 00:00:00 2001 From: sabuhish Date: Thu, 9 Jun 2022 14:58:00 +0400 Subject: [PATCH 2/6] Added poetry as a dependecy and formatter --- .flake8 | 4 + Makefile | 14 ++ poetry.lock | 378 +++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 43 ++++++ 4 files changed, 439 insertions(+) create mode 100644 .flake8 create mode 100644 Makefile create mode 100644 poetry.lock create mode 100644 pyproject.toml diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..5643697 --- /dev/null +++ b/.flake8 @@ -0,0 +1,4 @@ +[flake8] +max_line_length = 100 +exclude = .venv,.mypy_cache,.pytest_cache +ignore = PT013,PT018,W503 \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..117fc2d --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +lint: + @echo + isort --diff -c --skip-glob '*.venv' . + @echo + blue --check --diff --color . + @echo + flake8 . + @echo + mypy --ignore-missing-imports . + + +format_code: + isort . + blue . \ No newline at end of file diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..490e292 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,378 @@ +[[package]] +name = "black" +version = "22.1.0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = ">=1.1.0" +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "blue" +version = "0.9.0" +description = "Blue -- Some folks like black but I prefer blue." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +black = "22.1.0" +flake8 = ">=3.8" + +[[package]] +name = "certifi" +version = "2022.5.18.1" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "charset-normalizer" +version = "2.0.12" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.5.0" + +[package.extras] +unicode_backport = ["unicodedata2"] + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "flake8" +version = "4.0.1" +description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.8.0,<2.9.0" +pyflakes = ">=2.4.0,<2.5.0" + +[[package]] +name = "idna" +version = "3.3" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "isort" +version = "5.10.1" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=3.6.1,<4.0" + +[package.extras] +pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +requirements_deprecated_finder = ["pipreqs", "pip-api"] +colors = ["colorama (>=0.4.3,<0.5.0)"] +plugins = ["setuptools"] + +[[package]] +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "mypy" +version = "0.961" +description = "Optional static typing for Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +mypy-extensions = ">=0.4.3" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=3.10" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<2)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "pathspec" +version = "0.9.0" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[[package]] +name = "platformdirs" +version = "2.5.2" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] +test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] + +[[package]] +name = "pycodestyle" +version = "2.8.0" +description = "Python style guide checker" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pyflakes" +version = "2.4.0" +description = "passive checker of Python programs" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "requests" +version = "2.27.1" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} +idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "typing-extensions" +version = "4.2.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "urllib3" +version = "1.26.9" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" + +[package.extras] +brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "user-agent" +version = "0.1.10" +description = "User-Agent generator" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +six = "*" + +[metadata] +lock-version = "1.1" +python-versions = "^3.9" +content-hash = "9823adb8c5b4b0c3bf8b4a60e28d5b80d25ce5b8749e821d9cba9ed07f6c24eb" + +[metadata.files] +black = [ + {file = "black-22.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1297c63b9e1b96a3d0da2d85d11cd9bf8664251fd69ddac068b98dc4f34f73b6"}, + {file = "black-22.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2ff96450d3ad9ea499fc4c60e425a1439c2120cbbc1ab959ff20f7c76ec7e866"}, + {file = "black-22.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e21e1f1efa65a50e3960edd068b6ae6d64ad6235bd8bfea116a03b21836af71"}, + {file = "black-22.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f69158a7d120fd641d1fa9a921d898e20d52e44a74a6fbbcc570a62a6bc8ab"}, + {file = "black-22.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:228b5ae2c8e3d6227e4bde5920d2fc66cc3400fde7bcc74f480cb07ef0b570d5"}, + {file = "black-22.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b1a5ed73ab4c482208d20434f700d514f66ffe2840f63a6252ecc43a9bc77e8a"}, + {file = "black-22.1.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35944b7100af4a985abfcaa860b06af15590deb1f392f06c8683b4381e8eeaf0"}, + {file = "black-22.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7835fee5238fc0a0baf6c9268fb816b5f5cd9b8793423a75e8cd663c48d073ba"}, + {file = "black-22.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dae63f2dbf82882fa3b2a3c49c32bffe144970a573cd68d247af6560fc493ae1"}, + {file = "black-22.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fa1db02410b1924b6749c245ab38d30621564e658297484952f3d8a39fce7e8"}, + {file = "black-22.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c8226f50b8c34a14608b848dc23a46e5d08397d009446353dad45e04af0c8e28"}, + {file = "black-22.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2d6f331c02f0f40aa51a22e479c8209d37fcd520c77721c034517d44eecf5912"}, + {file = "black-22.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:742ce9af3086e5bd07e58c8feb09dbb2b047b7f566eb5f5bc63fd455814979f3"}, + {file = "black-22.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fdb8754b453fb15fad3f72cd9cad3e16776f0964d67cf30ebcbf10327a3777a3"}, + {file = "black-22.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5660feab44c2e3cb24b2419b998846cbb01c23c7fe645fee45087efa3da2d61"}, + {file = "black-22.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:6f2f01381f91c1efb1451998bd65a129b3ed6f64f79663a55fe0e9b74a5f81fd"}, + {file = "black-22.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:efbadd9b52c060a8fc3b9658744091cb33c31f830b3f074422ed27bad2b18e8f"}, + {file = "black-22.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8871fcb4b447206904932b54b567923e5be802b9b19b744fdff092bd2f3118d0"}, + {file = "black-22.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccad888050f5393f0d6029deea2a33e5ae371fd182a697313bdbd835d3edaf9c"}, + {file = "black-22.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07e5c049442d7ca1a2fc273c79d1aecbbf1bc858f62e8184abe1ad175c4f7cc2"}, + {file = "black-22.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:373922fc66676133ddc3e754e4509196a8c392fec3f5ca4486673e685a421321"}, + {file = "black-22.1.0-py3-none-any.whl", hash = "sha256:3524739d76b6b3ed1132422bf9d82123cd1705086723bc3e235ca39fd21c667d"}, + {file = "black-22.1.0.tar.gz", hash = "sha256:a7c0192d35635f6fc1174be575cb7915e92e5dd629ee79fdaf0dcfa41a80afb5"}, +] +blue = [ + {file = "blue-0.9.0-py3-none-any.whl", hash = "sha256:7eec33ef8d39c60f1336229b226a004b9a26743b156054844dc86117d0a1d0e6"}, + {file = "blue-0.9.0.tar.gz", hash = "sha256:da06d09f8417ad475e21a26dd0a7dae6771146fdd7f5760450183183230f9ef1"}, +] +certifi = [ + {file = "certifi-2022.5.18.1-py3-none-any.whl", hash = "sha256:f1d53542ee8cbedbe2118b5686372fb33c297fcd6379b050cca0ef13a597382a"}, + {file = "certifi-2022.5.18.1.tar.gz", hash = "sha256:9c5705e395cd70084351dd8ad5c41e65655e08ce46f2ec9cf6c2c08390f71eb7"}, +] +charset-normalizer = [ + {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, + {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, +] +click = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +flake8 = [ + {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, + {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, +] +idna = [ + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, +] +isort = [ + {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, + {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, +] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] +mypy = [ + {file = "mypy-0.961-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:697540876638ce349b01b6786bc6094ccdaba88af446a9abb967293ce6eaa2b0"}, + {file = "mypy-0.961-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b117650592e1782819829605a193360a08aa99f1fc23d1d71e1a75a142dc7e15"}, + {file = "mypy-0.961-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bdd5ca340beffb8c44cb9dc26697628d1b88c6bddf5c2f6eb308c46f269bb6f3"}, + {file = "mypy-0.961-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3e09f1f983a71d0672bbc97ae33ee3709d10c779beb613febc36805a6e28bb4e"}, + {file = "mypy-0.961-cp310-cp310-win_amd64.whl", hash = "sha256:e999229b9f3198c0c880d5e269f9f8129c8862451ce53a011326cad38b9ccd24"}, + {file = "mypy-0.961-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b24be97351084b11582fef18d79004b3e4db572219deee0212078f7cf6352723"}, + {file = "mypy-0.961-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f4a21d01fc0ba4e31d82f0fff195682e29f9401a8bdb7173891070eb260aeb3b"}, + {file = "mypy-0.961-cp36-cp36m-win_amd64.whl", hash = "sha256:439c726a3b3da7ca84a0199a8ab444cd8896d95012c4a6c4a0d808e3147abf5d"}, + {file = "mypy-0.961-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a0b53747f713f490affdceef835d8f0cb7285187a6a44c33821b6d1f46ed813"}, + {file = "mypy-0.961-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0e9f70df36405c25cc530a86eeda1e0867863d9471fe76d1273c783df3d35c2e"}, + {file = "mypy-0.961-cp37-cp37m-win_amd64.whl", hash = "sha256:b88f784e9e35dcaa075519096dc947a388319cb86811b6af621e3523980f1c8a"}, + {file = "mypy-0.961-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d5aaf1edaa7692490f72bdb9fbd941fbf2e201713523bdb3f4038be0af8846c6"}, + {file = "mypy-0.961-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9f5f5a74085d9a81a1f9c78081d60a0040c3efb3f28e5c9912b900adf59a16e6"}, + {file = "mypy-0.961-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f4b794db44168a4fc886e3450201365c9526a522c46ba089b55e1f11c163750d"}, + {file = "mypy-0.961-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:64759a273d590040a592e0f4186539858c948302c653c2eac840c7a3cd29e51b"}, + {file = "mypy-0.961-cp38-cp38-win_amd64.whl", hash = "sha256:63e85a03770ebf403291ec50097954cc5caf2a9205c888ce3a61bd3f82e17569"}, + {file = "mypy-0.961-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f1332964963d4832a94bebc10f13d3279be3ce8f6c64da563d6ee6e2eeda932"}, + {file = "mypy-0.961-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:006be38474216b833eca29ff6b73e143386f352e10e9c2fbe76aa8549e5554f5"}, + {file = "mypy-0.961-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9940e6916ed9371809b35b2154baf1f684acba935cd09928952310fbddaba648"}, + {file = "mypy-0.961-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a5ea0875a049de1b63b972456542f04643daf320d27dc592d7c3d9cd5d9bf950"}, + {file = "mypy-0.961-cp39-cp39-win_amd64.whl", hash = "sha256:1ece702f29270ec6af25db8cf6185c04c02311c6bb21a69f423d40e527b75c56"}, + {file = "mypy-0.961-py3-none-any.whl", hash = "sha256:03c6cc893e7563e7b2949b969e63f02c000b32502a1b4d1314cabe391aa87d66"}, + {file = "mypy-0.961.tar.gz", hash = "sha256:f730d56cb924d371c26b8eaddeea3cc07d78ff51c521c6d04899ac6904b75492"}, +] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] +pathspec = [ + {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, + {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, +] +platformdirs = [ + {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, + {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, +] +pycodestyle = [ + {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, + {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, +] +pyflakes = [ + {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, + {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, +] +requests = [ + {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, + {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] +tomli = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] +typing-extensions = [ + {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"}, + {file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"}, +] +urllib3 = [ + {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"}, + {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"}, +] +user-agent = [ + {file = "user_agent-0.1.10.tar.gz", hash = "sha256:b86537cb2a9d3bda0e2afcc654ec15b383502836877a67520654acadf73f1723"}, +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b3d3bb8 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,43 @@ +[tool.poetry] +name = "opa-python-client" +version = "1.3.3" +description = "Client for connection to the OPA service" +authors = ["Tural Muradov "] +license = "MIT" +readme = "README.md" +homepage = "https://github.com/Turall/OPA-python-client" +repository = "https://github.com/Turall/OPA-python-client" +classifiers = [ + "Intended Audience :: Developers", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3 :: Only", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", +] + +[tool.poetry.dependencies] +python = "^3.9" +requests = "^2.27.1" +urllib3 = "^1.26.9" +certifi = "^2022.5.18" +user-agent = "^0.1.10" + +[tool.poetry.dev-dependencies] +isort = "^5.10.1" +flake8 = "^4.0.1" +mypy = "^0.961" +blue = "^0.9.0" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.isort] +profile = "black" +line_length = 100 + +[tool.blue] +line-length = 100 \ No newline at end of file From c92e7e44ab89ea9d55416786e52c0f810d956a46 Mon Sep 17 00:00:00 2001 From: sabuhish Date: Thu, 9 Jun 2022 22:11:49 +0400 Subject: [PATCH 3/6] Added fake and mypi --- opa_client/OpaExceptions/OpaExceptions.py | 116 ------ opa_client/OpaExceptions/__init__.py | 13 - opa_client/__init__.py | 3 +- opa_client/opa.py | 480 ++++++++++------------ opa_client/test/test_opa.py | 74 ++-- poetry.lock | 154 ++++++- pyproject.toml | 2 + 7 files changed, 412 insertions(+), 430 deletions(-) delete mode 100644 opa_client/OpaExceptions/OpaExceptions.py delete mode 100644 opa_client/OpaExceptions/__init__.py diff --git a/opa_client/OpaExceptions/OpaExceptions.py b/opa_client/OpaExceptions/OpaExceptions.py deleted file mode 100644 index ec73d33..0000000 --- a/opa_client/OpaExceptions/OpaExceptions.py +++ /dev/null @@ -1,116 +0,0 @@ -class ConnectionsError(Exception): - """ - expression -- input expression in which the error occurred - message -- explanation of the error - """ - - def __init__(self, expression, message): - self.expression = expression - self.message = message - - -class QueryExecuteError(Exception): - """ - expression -- input expression in which the error occurred - message -- explanation of the error - """ - - def __init__(self, expression, message): - self.expression = expression - self.message = message - - -class PolicyNotFoundError(Exception): - """ - expression -- input expression in which the error occurred - message -- explanation of the error - """ - - def __init__(self, expression, message): - self.expression = expression - self.message = message - - -class CheckPermissionError(Exception): - """ - expression -- input expression in which the error occurred - message -- explanation of the error - """ - - def __init__(self, expression, message): - self.expression = expression - self.message = message - - -class DeleteDataError(Exception): - """ - expression -- input expression in which the error occurred - message -- explanation of the error - """ - - def __init__(self, expression, message): - self.expression = expression - self.message = message - - -class DeletePolicyError(Exception): - """ - expression -- input expression in which the error occurred - message -- explanation of the error - """ - - def __init__(self, expression, message): - self.expression = expression - self.message = message - - -class PathNotFoundError(Exception): - """ - expression -- input expression in which the error occurred - message -- explanation of the error - """ - - def __init__(self, expression, message): - self.expression = expression - self.message = message - - -class RegoParseError(Exception): - """ - expression -- input expression in which the error occurred - message -- explanation of the error - """ - - def __init__(self, expression, message): - self.expression = expression - self.message = message - - -class SSLError(Exception): - """ - expression -- input expression in which the error occurred - message -- explanation of the error - """ - - def __init__(self, expression, message): - self.expression = expression - self.message = message - - -class FileError(ValueError): - """ - expression -- input expression in which the error occurred - message -- explanation of the error - """ - def __init__(self, expression, message): - self.expression = expression - self.message = message - - -class TypeExecption(TypeError): - """ - expression -- input expression in which the error occurred - message -- explanation of the error - """ - def __init__(self, expression): - self.expression = expression diff --git a/opa_client/OpaExceptions/__init__.py b/opa_client/OpaExceptions/__init__.py deleted file mode 100644 index 0b49788..0000000 --- a/opa_client/OpaExceptions/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from .OpaExceptions import ( - CheckPermissionError, - ConnectionsError, - DeleteDataError, - DeletePolicyError, - FileError, - PathNotFoundError, - PolicyNotFoundError, - QueryExecuteError, - RegoParseError, - SSLError, - TypeExecption, -) diff --git a/opa_client/__init__.py b/opa_client/__init__.py index 9e8bc5b..45576be 100644 --- a/opa_client/__init__.py +++ b/opa_client/__init__.py @@ -1,8 +1,7 @@ """Initialize the OpaClient package.""" from .opa import OpaClient -from .OpaExceptions import * __all__ = [ - "OpaClient", + 'OpaClient', ] diff --git a/opa_client/opa.py b/opa_client/opa.py index 2e80ab9..108723b 100644 --- a/opa_client/opa.py +++ b/opa_client/opa.py @@ -17,7 +17,7 @@ import urllib3 from user_agent import generate_user_agent -from .OpaExceptions import ( +from .errors import ( CheckPermissionError, ConnectionsError, DeleteDataError, @@ -31,35 +31,34 @@ TypeExecption, ) -__version__ = "1.3.3" -__author__ = "Tural Muradov" +__version__ = '1.3.3' +__author__ = 'Tural Muradov' __license__ = 'MIT' class OpaClient: - """ OpaClient client object to connect and manipulate OPA service. - ``` - The class object holds session information necesseary to connect OPA service. - param :: host : to connect OPA service ,defaults to localhost - type :: host: str - param :: port : to connect OPA service ,defaults to 8181 - type :: port : str or int - param :: version : provided REST API version by OPA,defaults to v1 - type :: version : str - param :: ssl : verify ssl certificates for https requests,defaults to False - type :: ssl : bool - param :: cert : path to client certificate information to use for mutual TLS authentification - type :: cert : str - param :: headers : dictionary of headers to send, defaults to None - ``` - + """OpaClient client object to connect and manipulate OPA service. + ``` + The class object holds session information necesseary to connect OPA service. + param :: host : to connect OPA service ,defaults to localhost + type :: host: str + param :: port : to connect OPA service ,defaults to 8181 + type :: port : str or int + param :: version : provided REST API version by OPA,defaults to v1 + type :: version : str + param :: ssl : verify ssl certificates for https requests,defaults to False + type :: ssl : bool + param :: cert : path to client certificate information to use for mutual TLS authentification + type :: cert : str + param :: headers : dictionary of headers to send, defaults to None + ``` """ def __init__( self, - host: str = "localhost", + host: str = 'localhost', port: int = 8181, - version: str = "v1", + version: str = 'v1', ssl: bool = False, cert: Union[None, str] = None, headers: Union[None, dict] = None, @@ -68,46 +67,45 @@ def __init__( host = host.lstrip() self.__port = port self.__version = version - self.__policy_root = "{}/policies/{}" - self.__data_root = "{}/data/{}" + self.__policy_root = '{}/policies/{}' + self.__data_root = '{}/data/{}' self.__secure = False - self.__schema = "http://" - self.retries = kwargs.get("retries", 2) - self.timeout = kwargs.get("timeout", 1.5) + self.__schema = 'http://' + self.retries = kwargs.get('retries', 2) + self.timeout = kwargs.get('timeout', 1.5) if not isinstance(self.__port, int): - raise TypeError("The port must be integer") + raise TypeError('The port must be integer') if ssl: self.__ssl = ssl self.__cert = cert self.__secure = True - self.__schema = "https://" + self.__schema = 'https://' if not cert and ssl is True: - raise SSLError("ssl=True", "Make sure you provide cert file") + raise SSLError('ssl=True', 'Make sure you provide cert file') - if host.startswith("https://"): + if host.startswith('https://'): self.__host = host - self.__root_url = "{}:{}/{}".format(self.__host, self.__port, - self.__version) + self.__root_url = '{}:{}/{}'.format(self.__host, self.__port, self.__version) - elif host.startswith("http://"): + elif host.startswith('http://'): self.__host = host if self.__secure: raise SSLError( - "ssl=True", - "With ssl enabled not possible to have connection with http", + 'ssl=True', + 'With ssl enabled not possible to have connection with http', ) - self.__root_url = "{}:{}/{}".format(self.__host, self.__port, - self.__version) + self.__root_url = '{}:{}/{}'.format(self.__host, self.__port, self.__version) else: self.__host = host - self.__root_url = "{}{}:{}/{}".format(self.__schema, self.__host, - self.__port, self.__version) + self.__root_url = '{}{}:{}/{}'.format( + self.__schema, self.__host, self.__port, self.__version + ) if headers: self.__headers = requests.utils.default_headers() @@ -115,11 +113,11 @@ def __init__( else: self.__headers = requests.utils.default_headers() - self.__headers.update({"User-Agent": generate_user_agent()}) + self.__headers.update({'User-Agent': generate_user_agent()}) if self.__secure: self.__manager = urllib3.PoolManager( - cert_reqs="CERT_REQUIRED", + cert_reqs='CERT_REQUIRED', assert_hostname=False, ca_certs=self.__cert, headers=self.__headers, @@ -143,91 +141,85 @@ def close_connection(self): def check_connection(self): """ - Checks whether established connection config True or not. - if not properly configured will raise an ConnectionError. + Checks whether established connection config True or not. + if not properly configured will raise an ConnectionError. """ - url = self.__policy_root.format(self.__root_url, "") + url = self.__policy_root.format(self.__root_url, '') try: - response = self.__session( - "GET", url, retries=self.retries, timeout=self.timeout) + response = self.__session('GET', url, retries=self.retries, timeout=self.timeout) if response.status == 200: return "Yes I'm here :)" except Exception: - raise ConnectionsError("service unreachable", - "check config and try again") + raise ConnectionsError('service unreachable', 'check config and try again') - raise ConnectionsError("service unreachable", - "check config and try again") + raise ConnectionsError('service unreachable', 'check config and try again') def check_health(self, query: Dict[str, bool] = None, diagnostic_url: str = None) -> bool: """ - Check OPA healthy. If you want check bundels or plugins, add query params for this. - If your diagnostic url different than default url, you can provide it. - ``` - param :: query : it is the url query string. default None - param :: diagnostic_url : OPA diagnostic url - - example: - print(client.check_health()) - print(client.check_health({"bundle": True})) - print(client.check_health(diagnostic_url="http://localhost:8282/health")) - print(client.check_health(query={"bundle": True}, diagnostic_url="http://localhost:8282/health")) - + Check OPA healthy. If you want check bundels or plugins, add query params for this. + If your diagnostic url different than default url, you can provide it. + ``` + param :: query : it is the url query string. default None + param :: diagnostic_url : OPA diagnostic url + + example: + print(client.check_health()) + print(client.check_health({"bundle": True})) + print(client.check_health(diagnostic_url="http://localhost:8282/health")) + print(client.check_health( + query={"bundle": True}, diagnostic_url="http://localhost:8282/health") + ) """ if diagnostic_url: url = diagnostic_url else: - url = "{}{}:{}/{}".format(self.__schema, self.__host, - self.__port, "health") + url = '{}{}:{}/{}'.format(self.__schema, self.__host, self.__port, 'health') if query: url = self.prepare_args(url, query) - response = self.__session( - "GET", url, retries=self.retries, timeout=self.timeout) + response = self.__session('GET', url, retries=self.retries, timeout=self.timeout) if response.status == 200: return True return False def get_policies_list(self) -> list: - """ Returns all OPA policies in the service""" + """Returns all OPA policies in the service""" return self.__get_policies_list() def get_policies_info(self) -> dict: - """ - Returns information about each policy, including - policy path and policy rules + """ + Returns information about each policy, including + policy path and policy rules """ return self.__get_policies_info() - def update_opa_policy_fromstring(self, new_policy: str, - endpoint: str) -> bool: - """ Write your rego policy with using python string type and update your OPA policies. - ``` - param :: new_policy : is the name of your new defined rego policy. - param :: endpoint : is the path of your new policy in OPA - - example: - new_policy= - package play + def update_opa_policy_fromstring(self, new_policy: str, endpoint: str) -> bool: + """Write your rego policy with using python string type and update your OPA policies. + ``` + param :: new_policy : is the name of your new defined rego policy. + param :: endpoint : is the path of your new policy in OPA - default hello = false + example: + new_policy= + package play - hello { - m := input.message - m == "world" - } - client = OpaClient() - client.update_opa_policy_fromstring(new_policy,'exampleapi') - ``` + default hello = false + hello { + m := input.message + m == "world" + } + client = OpaClient() + client.update_opa_policy_fromstring(new_policy,'exampleapi') + ``` """ return self.__update_opa_policy_fromstring(new_policy, endpoint) def update_opa_policy_fromfile(self, filepath: str, endpoint: str) -> bool: - """ Write your rego policy to file and update your OPA policies. """ + """Write your rego policy to file and update your OPA policies.""" return self.__update_opa_policy_fromfile(filepath, endpoint) @@ -237,91 +229,91 @@ def update_opa_policy_fromurl(self, url: str, endpoint: str) -> bool: return self.__update_opa_policy_fromurl(url, endpoint) def update_or_create_opa_data(self, new_data: dict, endpoint: str) -> bool: - """ Updates existing data or create new data for policy. - ``` - param :: new_data : name of defined data - type :: new_data : dict - param :: endpoint : is the path of your new data or existing one in OPA - type :: endpoint : str - example: - my_policy_list = [ - {"resource": "/api/someapi", "identity": "your_identity", "method": "PUT"}, - {"resource": "/api/someapi", "identity": "your_identity", "method": "GET"}, - ] - - client = OpaClient() - client.update_or_create_opa_data(my_policy_list,'exampledata/accesses') - ``` + """Updates existing data or create new data for policy. + ``` + param :: new_data : name of defined data + type :: new_data : dict + param :: endpoint : is the path of your new data or existing one in OPA + type :: endpoint : str + example: + my_policy_list = [ + {"resource": "/api/someapi", "identity": "your_identity", "method": "PUT"}, + {"resource": "/api/someapi", "identity": "your_identity", "method": "GET"}, + ] + + client = OpaClient() + client.update_or_create_opa_data(my_policy_list,'exampledata/accesses') + ``` """ return self.__update_opa_data(new_data, endpoint) - def get_opa_raw_data(self, data_name: str = "", query_params: Dict[str, bool] = dict()) -> dict: - """ Returns OPA raw data in string type - ``` - param :: data_name : OPA data name you want get - param :: query_params : query params in url for more information about metrics - ``` + def get_opa_raw_data(self, data_name: str = '', query_params: Dict[str, bool] = dict()) -> dict: + """Returns OPA raw data in string type + ``` + param :: data_name : OPA data name you want get + param :: query_params : query params in url for more information about metrics + ``` """ return self.__get_opa_raw_data(data_name, query_params) - def opa_policy_to_file(self, - policy_name: str, - path: Union[str, None] = None, - filename: str = "opa_policy.rego"): - """ Write OPA service policy to the file. + def opa_policy_to_file( + self, policy_name: str, path: Union[str, None] = None, filename: str = 'opa_policy.rego' + ): + """Write OPA service policy to the file. ``` - param :: policy_name : name of OPA policy - type :: policy_name : str + param :: policy_name : name of OPA policy + type :: policy_name : str - param :: path : path to save file ,default current path - type :: path : str + param :: path : path to save file ,default current path + type :: path : str - param :: filename : name of the file,default opa_policy.rego - type :: filename : str + param :: filename : name of the file,default opa_policy.rego + type :: filename : str ``` - """ return self.__opa_policy_to_file(policy_name, path, filename) def get_opa_policy(self, policy_name: str) -> dict: - """Returns full info about policy, provided OPA service """ + """Returns full info about policy, provided OPA service""" return self.__get_opa_policy(policy_name) def delete_opa_policy(self, policy_name: str) -> bool: - """ Deletes given OPA policy name """ + """Deletes given OPA policy name""" return self.__delete_opa_policy(policy_name) def delete_opa_data(self, data_name: str) -> bool: - """ Deletes given OPA policy data name """ + """Deletes given OPA policy data name""" return self.__delete_opa_data(data_name) - def check_permission(self, input_data: dict, policy_name: str, - rule_name: str, query_params: Dict[str, bool] = dict()) -> dict: + def check_permission( + self, + input_data: dict, + policy_name: str, + rule_name: str, + query_params: Dict[str, bool] = dict(), + ) -> dict: """ ``` - params :: input_data : data which you want check permission - type :: input_data : dict + params :: input_data : data which you want check permission + type :: input_data : dict - params :: policy_name : the name of policy resource - type :: policy_name : str + params :: policy_name : the name of policy resource + type :: policy_name : str - params :: rule_name : the name included in the policy - type :: rule_name : str - param :: query_params : query params in url for more information about metrics - type :: query_params : dict + params :: rule_name : the name included in the policy + type :: rule_name : str + param :: query_params : query params in url for more information about metrics + type :: query_params : dict ``` """ return self.__check(input_data, policy_name, rule_name, query_params) - def check_policy_rule(self, - input_data: dict, - package_path: str, - rule_name: str = None) -> dict: + def check_policy_rule(self, input_data: dict, package_path: str, rule_name: str = None) -> dict: """ Queries a package rule with the given input data """ @@ -329,20 +321,18 @@ def check_policy_rule(self, return self.__query(input_data, package_path, rule_name) def ad_hoc_query(self, *, query_params: Dict[str, str] = None, body: Dict[str, str] = None): - """ Execute an ad-hoc query and return bindings for variables found in the query. + """Execute an ad-hoc query and return bindings for variables found in the query. ``` - param :: query_params for sending query string in url - param :: body for sending query in request body - + param :: query_params for sending query string in url + param :: body for sending query in request body ``` """ - url = "{}{}:{}/{}/{}".format(self.__schema, self.__host, - self.__port, "v1", "query") + url = '{}{}:{}/{}/{}'.format(self.__schema, self.__host, self.__port, 'v1', 'query') if body: - encoded_json = json.dumps(body).encode("utf-8") + encoded_json = json.dumps(body).encode('utf-8') response = self.__session( - "POST", + 'POST', url, body=encoded_json, retries=self.retries, @@ -350,39 +340,33 @@ def ad_hoc_query(self, *, query_params: Dict[str, str] = None, body: Dict[str, s ) elif query_params: url = self.prepare_args(url, query_params) - response = self.__session("GET", - url, - retries=self.retries, - timeout=self.timeout) - data = json.loads(response.data.decode("utf-8")) + response = self.__session('GET', url, retries=self.retries, timeout=self.timeout) + data = json.loads(response.data.decode('utf-8')) if response.status == 200: return data - raise QueryExecuteError(data.get("code"), data.get("message")) + raise QueryExecuteError(data.get('code'), data.get('message')) def prepare_args(self, url: str, query_params: dict) -> str: if query_params: query_params = urlencode(query_params) - url = url + "?" + query_params + url = url + '?' + query_params return url def __get_opa_raw_data(self, data_name: str, query_params: Dict[str, bool]): url = self.__data_root.format(self.__root_url, data_name) url = self.prepare_args(url, query_params) - response = self.__session("GET", - url, - retries=self.retries, - timeout=self.timeout) + response = self.__session('GET', url, retries=self.retries, timeout=self.timeout) code = response.status - response = json.loads(response.data.decode("utf-8")) - return response if code == 200 else (code, "not found") + response = json.loads(response.data.decode('utf-8')) + return response if code == 200 else (code, 'not found') def __update_opa_data(self, new_data: dict, endpoint: str): url = self.__data_root.format(self.__root_url, endpoint) - encoded_json = json.dumps(new_data).encode("utf-8") + encoded_json = json.dumps(new_data).encode('utf-8') response = self.__session( - "PUT", + 'PUT', url, body=encoded_json, retries=self.retries, @@ -394,23 +378,21 @@ def __update_opa_data(self, new_data: dict, endpoint: str): def __update_opa_policy_fromfile(self, filepath: str, endpoint: str): if os.path.isfile(filepath): - with open(filepath, "r") as rf: + with open(filepath, 'r') as rf: return self.__update_opa_policy_fromstring(rf.read(), endpoint) - raise FileError(f"{filepath}", - "is not a file, make sure you provide a file") + raise FileError(f'{filepath}', 'is not a file, make sure you provide a file') - def __update_opa_policy_fromstring(self, new_policy: str, - endpoint: str) -> bool: + def __update_opa_policy_fromstring(self, new_policy: str, endpoint: str) -> bool: if not isinstance(new_policy, str) or not isinstance(endpoint, str): - raise TypeExecption(f"{new_policy} is not string type") + raise TypeExecption(f'{new_policy} is not string type') if new_policy: url = self.__policy_root.format(self.__root_url, endpoint) response = self.__session( - "PUT", + 'PUT', url, body=new_policy.encode(), headers=self.__headers, @@ -421,187 +403,159 @@ def __update_opa_policy_fromstring(self, new_policy: str, if response.status == 200: return True - raise RegoParseError( - response.status, - json.loads(response.data.decode()) - ) + raise RegoParseError(response.status, json.loads(response.data.decode())) return False def __get_opa_policy(self, policy_name: str) -> dict: url = self.__policy_root.format(self.__root_url, policy_name) - response = self.__session("GET", - url, - retries=self.retries, - timeout=self.timeout) - data = json.loads(response.data.decode("utf-8")) + response = self.__session('GET', url, retries=self.retries, timeout=self.timeout) + data = json.loads(response.data.decode('utf-8')) if response.status == 200: return data - raise PolicyNotFoundError(data.get("code"), data.get("message")) + raise PolicyNotFoundError(data.get('code'), data.get('message')) def __update_opa_policy_fromurl(self, url: str, endpoint: str) -> bool: response = requests.get(url, headers=self.__headers) return self.__update_opa_policy_fromstring(response.text, endpoint) - def __opa_policy_to_file(self, policy_name: str, path: Union[str, None], - filename: str) -> bool: + def __opa_policy_to_file(self, policy_name: str, path: Union[str, None], filename: str) -> bool: raw_policy = self.__get_opa_policy(policy_name) if isinstance(raw_policy, dict): try: if path: - - with open(f"{path}/{filename}", "wb") as wr: - wr.write(raw_policy.get("result").get("raw").encode()) + with open(f'{path}/{filename}', 'wb') as wr: + wr.write(raw_policy.get('result').get('raw').encode()) else: - with open(filename, "wb") as wr: - wr.write(raw_policy.get("result").get("raw").encode()) + with open(filename, 'wb') as wr: + wr.write(raw_policy.get('result').get('raw').encode()) return True except: # noqa: E722 - raise PathNotFoundError("error when write file", - "path not found") + raise PathNotFoundError('error when write file', 'path not found') def __delete_opa_policy(self, policy_name: str) -> bool: url = self.__policy_root.format(self.__root_url, policy_name) - response = self.__session("DELETE", - url, - retries=self.retries, - timeout=self.timeout) - data = json.loads(response.data.decode("utf-8")) + response = self.__session('DELETE', url, retries=self.retries, timeout=self.timeout) + data = json.loads(response.data.decode('utf-8')) if response.status == 200: return True - raise DeletePolicyError(data.get("code"), data.get("message")) + raise DeletePolicyError(data.get('code'), data.get('message')) def __get_policies_list(self) -> list: - url = self.__policy_root.format(self.__root_url, "") + url = self.__policy_root.format(self.__root_url, '') temp = [] - response = self.__session("GET", - url, - retries=self.retries, - timeout=self.timeout, - headers=self.__headers) + response = self.__session( + 'GET', url, retries=self.retries, timeout=self.timeout, headers=self.__headers + ) response = json.loads(response.data.decode()) - for policy in response.get("result"): - if policy.get("id"): - temp.append(policy.get("id")) + for policy in response.get('result'): + if policy.get('id'): + temp.append(policy.get('id')) return temp def __delete_opa_data(self, data_name: str) -> bool: url = self.__data_root.format(self.__root_url, data_name) - response = self.__session("DELETE", - url, - retries=self.retries, - timeout=self.timeout) + response = self.__session('DELETE', url, retries=self.retries, timeout=self.timeout) if response.data: - data = json.loads(response.data.decode("utf-8")) + data = json.loads(response.data.decode('utf-8')) if response.status == 204: return True - raise DeleteDataError(data.get("code"), data.get("message")) + raise DeleteDataError(data.get('code'), data.get('message')) def __get_policies_info(self) -> dict: - url = self.__policy_root.format(self.__root_url, "") - policy = self.__session("GET", - url, - retries=self.retries, - timeout=self.timeout, - headers=self.__headers) + url = self.__policy_root.format(self.__root_url, '') + policy = self.__session( + 'GET', url, retries=self.retries, timeout=self.timeout, headers=self.__headers + ) policy = json.loads(policy.data.decode()) - result = policy.get("result") + result = policy.get('result') temp_dict = {} for policy in result: temp_policy = [] temp_rules = [] - temp_url = "" + temp_url = '' permission_url = self.__root_url - for path in policy.get("ast").get("package").get("path"): - permission_url += "/" + path.get("value") + for path in policy.get('ast').get('package').get('path'): + permission_url += '/' + path.get('value') temp_policy.append(permission_url) - for rule in policy.get("ast").get("rules"): - if not rule.get("default"): + for rule in policy.get('ast').get('rules'): + if not rule.get('default'): continue temp_url = permission_url - temp_url += "/" + rule.get("head").get("name") + temp_url += '/' + rule.get('head').get('name') temp_rules.append(temp_url) - temp_dict[policy.get("id")] = { - "path": temp_policy, - "rules": temp_rules - } + temp_dict[policy.get('id')] = {'path': temp_policy, 'rules': temp_rules} return temp_dict - def __check(self, input_data: dict, policy_name: str, - rule_name: str, query_params: Dict[str, bool]) -> dict: + def __check( + self, input_data: dict, policy_name: str, rule_name: str, query_params: Dict[str, bool] + ) -> dict: url = self.__policy_root.format(self.__root_url, policy_name) - policy = self.__session("GET", - url, - headers=self.__headers, - retries=self.retries, - timeout=self.timeout) - policy = json.loads(policy.data.decode("utf-8")) - result = policy.get("result") + policy = self.__session( + 'GET', url, headers=self.__headers, retries=self.retries, timeout=self.timeout + ) + policy = json.loads(policy.data.decode('utf-8')) + result = policy.get('result') find = False permission_url = self.__root_url if result: - for path in result.get("ast").get("package").get("path"): - permission_url += "/" + path.get("value") + for path in result.get('ast').get('package').get('path'): + permission_url += '/' + path.get('value') - for rule in result.get("ast").get("rules"): - if not rule.get("default"): + for rule in result.get('ast').get('rules'): + if not rule.get('default'): continue - if rule.get("head").get("name") == rule_name: + if rule.get('head').get('name') == rule_name: - permission_url += "/" + rule.get("head").get("name") + permission_url += '/' + rule.get('head').get('name') find = True if find: - encoded_json = json.dumps(input_data).encode("utf-8") + encoded_json = json.dumps(input_data).encode('utf-8') permission_url = self.prepare_args(permission_url, query_params) - response = self.__session("POST", - permission_url, - body=encoded_json, - retries=self.retries, - timeout=self.timeout) + response = self.__session( + 'POST', + permission_url, + body=encoded_json, + retries=self.retries, + timeout=self.timeout, + ) if response.data: - data = json.loads(response.data.decode("utf-8")) + data = json.loads(response.data.decode('utf-8')) return data - raise CheckPermissionError(f"{rule_name} rule not found", - "path or rule name not correct") + raise CheckPermissionError(f'{rule_name} rule not found', 'policy or rule name not correct') - def __query(self, - input_data: dict, - package_path: str, - rule_name: str = None) -> dict: + def __query(self, input_data: dict, package_path: str, rule_name: str = None) -> dict: if '.' in package_path: package_path = package_path.replace('.', '/') if rule_name: package_path = package_path + '/' + rule_name url = self.__data_root.format(self.__root_url, package_path) - encoded_json = json.dumps({'input': input_data}).encode("utf-8") - response = self.__session("POST", - url, - body=encoded_json, - retries=self.retries, - timeout=self.timeout) + encoded_json = json.dumps({'input': input_data}).encode('utf-8') + response = self.__session( + 'POST', url, body=encoded_json, retries=self.retries, timeout=self.timeout + ) if response.data: - data = json.loads(response.data.decode("utf-8")) + data = json.loads(response.data.decode('utf-8')) return data - raise CheckPermissionError(f"{rule_name} rule not found", - "policy or rule name not correct") + raise CheckPermissionError(f'{rule_name} rule not found', 'policy or rule name not correct') @property def _host(self): diff --git a/opa_client/test/test_opa.py b/opa_client/test/test_opa.py index 9938319..5c0b887 100644 --- a/opa_client/test/test_opa.py +++ b/opa_client/test/test_opa.py @@ -1,55 +1,55 @@ # -*- coding: utf-8 -*- """ Unit tests for the OpaClient. - """ from unittest import TestCase from opa_client.opa import OpaClient -from opa_client.OpaExceptions import DeleteDataError, DeletePolicyError +from opa_client.errors import DeleteDataError, DeletePolicyError class TestClient(TestCase): - def setUp(self): """Set up the test for OpaClient object""" self.myclient = OpaClient() def tearDown(self): - """ Close the connection to the OPA server by deleting the client""" + """Close the connection to the OPA server by deleting the client""" del self.myclient def test_client(self): """Set up the test for OpaClient object""" - client = OpaClient("localhost", 8181, "v1") - self.assertEqual("http://localhost:8181/v1", client._root_url) + client = OpaClient('localhost', 8181, 'v1') + self.assertEqual('http://localhost:8181/v1', client._root_url) - client = OpaClient("localhost", 8181, "v1") - self.assertEqual("http://localhost:8181/v1", client._root_url) + client = OpaClient('localhost', 8181, 'v1') + self.assertEqual('http://localhost:8181/v1', client._root_url) self.assertFalse(False, self.myclient._secure) - self.assertEqual("http://", self.myclient._schema) - self.assertEqual("v1", self.myclient._version) - self.assertEqual("localhost", self.myclient._host) + self.assertEqual('http://', self.myclient._schema) + self.assertEqual('v1', self.myclient._version) + self.assertEqual('localhost', self.myclient._host) self.assertEqual(8181, self.myclient._port) def test_functions(self): self.assertEqual("Yes I'm here :)", self.myclient.check_connection()) self.assertEqual(list(), self.myclient.get_policies_list()) - + self.assertEqual(dict(), self.myclient.get_policies_info()) - + # _dict = {'test': {'path': [ - # 'http://localhost:8181/v1/data/play'], 'rules': ['http://localhost:8181/v1/data/play/hello']}} + # 'http://localhost:8181/v1/data/play'], + # 'rules': ['http://localhost:8181/v1/data/play/hello']} + # } # self.assertEqual(_dict, self.myclient.get_policies_info()) - - new_policy = ''' + + new_policy = """ package play default hello = false @@ -58,32 +58,36 @@ def test_functions(self): m := input.message m == "world" } - ''' - self.assertEqual( - True, self.myclient.update_opa_policy_fromstring(new_policy, "test")) - - self.assertEqual(["test"], self.myclient.get_policies_list()) - _dict = {'test': {'path': ['http://localhost:8181/v1/data/play'], - 'rules': ['http://localhost:8181/v1/data/play/hello']}} + """ + self.assertEqual(True, self.myclient.update_opa_policy_fromstring(new_policy, 'test')) + + self.assertEqual(['test'], self.myclient.get_policies_list()) + _dict = { + 'test': { + 'path': ['http://localhost:8181/v1/data/play'], + 'rules': ['http://localhost:8181/v1/data/play/hello'], + } + } self.assertEqual(_dict, self.myclient.get_policies_info()) my_policy_list = [ - {"resource": "/api/someapi", "identity": "your_identity", "method": "PUT"}, - {"resource": "/api/someapi", "identity": "your_identity", "method": "GET"}, + {'resource': '/api/someapi', 'identity': 'your_identity', 'method': 'PUT'}, + {'resource': '/api/someapi', 'identity': 'your_identity', 'method': 'GET'}, ] - self.assertTrue(True, self.myclient.update_or_create_opa_data( - my_policy_list, 'exampledata/accesses')) + self.assertTrue( + True, self.myclient.update_or_create_opa_data(my_policy_list, 'exampledata/accesses') + ) value = {'result': {'hello': False}} - - self.assertEqual(True, self.myclient.opa_policy_to_file("test")) - - self.assertEqual(value, self.myclient.get_opa_raw_data("play")) - - self.assertTrue(True, self.myclient.delete_opa_policy("test")) + + self.assertEqual(True, self.myclient.opa_policy_to_file('test')) + + self.assertEqual(value, self.myclient.get_opa_raw_data('play')) + + self.assertTrue(True, self.myclient.delete_opa_policy('test')) with self.assertRaises(DeletePolicyError): - self.myclient.delete_opa_policy("test") + self.myclient.delete_opa_policy('test') with self.assertRaises(DeleteDataError): - self.myclient.delete_opa_data("play") + self.myclient.delete_opa_data('play') diff --git a/poetry.lock b/poetry.lock index 490e292..bc736d4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,3 +1,25 @@ +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "attrs" +version = "21.4.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] + [[package]] name = "black" version = "22.1.0" @@ -91,6 +113,14 @@ category = "main" optional = false python-versions = ">=3.5" +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "isort" version = "5.10.1" @@ -139,6 +169,17 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + [[package]] name = "pathspec" version = "0.9.0" @@ -159,6 +200,26 @@ python-versions = ">=3.7" docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + [[package]] name = "pycodestyle" version = "2.8.0" @@ -175,6 +236,38 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "dev" +optional = false +python-versions = ">=3.6.8" + +[package.extras] +diagrams = ["railroad-diagrams", "jinja2"] + +[[package]] +name = "pytest" +version = "7.1.2" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +tomli = ">=1.0.0" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + [[package]] name = "requests" version = "2.27.1" @@ -209,6 +302,25 @@ category = "dev" optional = false python-versions = ">=3.7" +[[package]] +name = "types-requests" +version = "2.27.30" +description = "Typing stubs for requests" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +types-urllib3 = "<1.27" + +[[package]] +name = "types-urllib3" +version = "1.26.15" +description = "Typing stubs for urllib3" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "typing-extensions" version = "4.2.0" @@ -244,9 +356,17 @@ six = "*" [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "9823adb8c5b4b0c3bf8b4a60e28d5b80d25ce5b8749e821d9cba9ed07f6c24eb" +content-hash = "8e0c9c632a5f3e6d1de137be643d577eed6b13bb5b2fe12d8c5fa416dcc754c4" [metadata.files] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] +attrs = [ + {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, + {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, +] black = [ {file = "black-22.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1297c63b9e1b96a3d0da2d85d11cd9bf8664251fd69ddac068b98dc4f34f73b6"}, {file = "black-22.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2ff96450d3ad9ea499fc4c60e425a1439c2120cbbc1ab959ff20f7c76ec7e866"}, @@ -300,6 +420,10 @@ idna = [ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] isort = [ {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, @@ -337,6 +461,10 @@ mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] pathspec = [ {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, @@ -345,6 +473,14 @@ platformdirs = [ {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, ] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] +py = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] pycodestyle = [ {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, @@ -353,6 +489,14 @@ pyflakes = [ {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, ] +pyparsing = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] +pytest = [ + {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, + {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"}, +] requests = [ {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, @@ -365,6 +509,14 @@ tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] +types-requests = [ + {file = "types-requests-2.27.30.tar.gz", hash = "sha256:ca8d7cc549c3d10dbcb3c69c1b53e3ffd1270089c1001a65c1e9e1017eb5e704"}, + {file = "types_requests-2.27.30-py3-none-any.whl", hash = "sha256:b9b6cd0a6e5d500e56419b79f44ec96f316e9375ff6c8ee566c39d25e9612621"}, +] +types-urllib3 = [ + {file = "types-urllib3-1.26.15.tar.gz", hash = "sha256:c89283541ef92e344b7f59f83ea9b5a295b16366ceee3f25ecfc5593c79f794e"}, + {file = "types_urllib3-1.26.15-py3-none-any.whl", hash = "sha256:6011befa13f901fc934f59bb1fd6973be6f3acf4ebfce427593a27e7f492918f"}, +] typing-extensions = [ {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"}, {file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"}, diff --git a/pyproject.toml b/pyproject.toml index b3d3bb8..b6e4c86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,12 +24,14 @@ requests = "^2.27.1" urllib3 = "^1.26.9" certifi = "^2022.5.18" user-agent = "^0.1.10" +types-requests = "^2.27.30" [tool.poetry.dev-dependencies] isort = "^5.10.1" flake8 = "^4.0.1" mypy = "^0.961" blue = "^0.9.0" +pytest = "^7.1.2" [build-system] requires = ["poetry-core>=1.0.0"] From 4453d9184ce8fad2c64136a8866e3a9d76eeb63a Mon Sep 17 00:00:00 2001 From: sabuhish Date: Thu, 9 Jun 2022 22:20:53 +0400 Subject: [PATCH 4/6] Updated README.md --- README.md | 5 ++ opa_client/errors.py | 106 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 opa_client/errors.py diff --git a/README.md b/README.md index a9e5d36..3851eaa 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,12 @@ # Python Open Policy Agent (OPA) Client +[![MIT licensed](https://img.shields.io/github/license/Turall/OPA-python-client)](https://raw.githubusercontent.com/Turall/OPA-python-client/master/LICENSE) +[![GitHub stars](https://img.shields.io/github/stars/Turall/OPA-python-client.svg)](https://github.com/Turall/OPA-python-client/stargazers) +[![GitHub forks](https://img.shields.io/github/forks/Turall/OPA-python-client.svg)](https://github.com/Turall/OPA-python-client/network) +[![GitHub issues](https://img.shields.io/github/issues-raw/Turall/OPA-python-client)](https://github.com/Turall/OPA-python-client/issues) [![Downloads](https://pepy.tech/badge/opa-python-client)](https://pepy.tech/project/opa-python-client) + See offical documentation page [Open Policy Agent](https://www.openpolicyagent.org/docs/latest/) diff --git a/opa_client/errors.py b/opa_client/errors.py new file mode 100644 index 0000000..f20f145 --- /dev/null +++ b/opa_client/errors.py @@ -0,0 +1,106 @@ +class ConnectionsError(Exception): + def __init__(self, expression, message): + """ + expression -- input expression in which the error occurred + message -- explanation of the error + """ + self.expression = expression + self.message = message + + +class QueryExecuteError(Exception): + def __init__(self, expression, message): + """ + expression -- input expression in which the error occurred + message -- explanation of the error + """ + self.expression = expression + self.message = message + + +class PolicyNotFoundError(Exception): + def __init__(self, expression, message): + """ + expression -- input expression in which the error occurred + message -- explanation of the error + """ + self.expression = expression + self.message = message + + +class CheckPermissionError(Exception): + def __init__(self, expression, message): + """ + expression -- input expression in which the error occurred + message -- explanation of the error + """ + self.expression = expression + self.message = message + + +class DeleteDataError(Exception): + def __init__(self, expression, message): + """ + expression -- input expression in which the error occurred + message -- explanation of the error + """ + self.expression = expression + self.message = message + + +class DeletePolicyError(Exception): + def __init__(self, expression, message): + """ + expression -- input expression in which the error occurred + message -- explanation of the error + """ + self.expression = expression + self.message = message + + +class PathNotFoundError(Exception): + def __init__(self, expression, message): + """ + expression -- input expression in which the error occurred + message -- explanation of the error + """ + self.expression = expression + self.message = message + + +class RegoParseError(Exception): + def __init__(self, expression, message): + """ + expression -- input expression in which the error occurred + message -- explanation of the error + """ + self.expression = expression + self.message = message + + +class SSLError(Exception): + def __init__(self, expression, message): + """ + expression -- input expression in which the error occurred + message -- explanation of the error + """ + self.expression = expression + self.message = message + + +class FileError(ValueError): + def __init__(self, expression, message): + """ + expression -- input expression in which the error occurred + message -- explanation of the error + """ + self.expression = expression + self.message = message + + +class TypeExecption(TypeError): + def __init__(self, expression): + """ + expression -- input expression in which the error occurred + """ + self.expression = expression From b4ca6666b6eb9bdf643b76ebe48898cc0fae2632 Mon Sep 17 00:00:00 2001 From: sabuhish Date: Thu, 9 Jun 2022 22:30:59 +0400 Subject: [PATCH 5/6] Updated README.md --- CONTRIBUTING.md | 28 +++++++++++++++++++++++ README.md | 60 +++++++++++++++++++++---------------------------- 2 files changed, 54 insertions(+), 34 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..1548ce8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,28 @@ +# Contributing to OPA-python-client + +We welcome contributions to [OPA-python-client](https://github.com/Turall/OPA-python-client) + +## Issues + +Feel free to submit issues and enhancement requests. + +[OPA-python-client Issues](https://github.com/Turall/OPA-python-client/issues) + +## Contributing + +Please refer to each project's style and contribution guidelines for submitting patches and additions. In general, we follow the "fork-and-pull" Git workflow. + +1. **Fork** the repo on GitHub +2. **Clone** the project to your own machine +3. **Commit** changes to your own branch +4. **Push** your work +5. Submit a **Pull request** so that we can review your changes + + +## Testing +```sh +$ docker run -it --rm -p 8181:8181 openpolicyagent/opa run --server --addr :8181 +$ pytest +``` + +NOTE: Be sure to merge the latest from "upstream" before making a pull request! diff --git a/README.md b/README.md index 3851eaa..4cb447b 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,19 @@ See offical documentation page [Open Policy Agent](https://www.openpolicyagent.o ### Installation ### ```sh - $ pip install OPA-python-client +$ pip install OPA-python-client ``` +Alternatively, if you prefer to use `poetry` for package dependencies: + +```bash +$ poetry shell +$ poetry add OPA-python-client +``` -## Usage Examples ## +## Usage Examples ```python >>> from opa_client.opa import OpaClient @@ -52,7 +58,7 @@ True ``` -### Connection to OPA service ### +### Connection to OPA service ```python from opa_client.opa import OpaClient @@ -66,10 +72,9 @@ del client ``` -### Connection to OPA service with SSL ### +### Connection to OPA service with SSL ```python - from opa_client.opa import OpaClient @@ -87,9 +92,7 @@ del client ``` - - -### Update policy from rego file ### +### Update policy from rego file ```python from opa_client.opa import OpaClient @@ -104,10 +107,9 @@ del client ``` -### Update policy from URL ### +### Update policy from URL ```python - from opa_client.opa import OpaClient client = OpaClient() @@ -121,12 +123,10 @@ del client ``` -### Delete policy ### +### Delete policy ```python - - from opa_client.opa import OpaClient client = OpaClient() @@ -138,12 +138,10 @@ client.get_policies_list() # response is [] del client ``` -### Get raw data from OPA service ### +### Get raw data from OPA service ```python - - from opa_client.opa import OpaClient client = OpaClient() @@ -164,12 +162,11 @@ print(client.get_opa_raw_data("userinfo",query_params={"metrics": True})) del client ``` -### Save policy to file from OPA service ### - -```python +### Save policy to file from OPA service +```python from opa_client.opa import OpaClient client = OpaClient() @@ -179,12 +176,11 @@ client.opa_policy_to_file(policy_name="fromurl",path="/your/path",filename="exam del client ``` -### Delete data from OPA service ### - -```python +### Delete data from OPA service +```python from opa_client.opa import OpaClient client = OpaClient() @@ -195,12 +191,10 @@ del client ``` -### Information about policy path and rules ### +### Information about policy path and rules ```python - - from opa_client.opa import OpaClient client = OpaClient() @@ -213,12 +207,10 @@ del client ``` -### Check permissions ### +### Check permissions ```python - - from opa_client.opa import OpaClient client = OpaClient() @@ -255,7 +247,6 @@ hello { check_data = {"message": "world"} client.check_policy_rule(input_data=check_data, package_path="play", rule_name="hello") # response {'result': True} - ``` ### Execute an Ad-hoc Query @@ -291,7 +282,6 @@ print(client.ad_hoc_query(query_params={"q": "data.userinfo.user_roles[name]"})) #you can send body request print(client.ad_hoc_query(body={"query": "data.userinfo.user_roles[name] "})) # response is {'result': [{'name': 'eve'}, {'name': 'alice'}, {'name': 'bob'}]} - ``` ### Check OPA healthy. If you want check bundels or plugins, add query params for this. @@ -306,12 +296,14 @@ print(client.check_health({"bundle": True})) # response is True or False # If your diagnostic url different than default url, you can provide it. print(client.check_health(diagnostic_url="http://localhost:8282/health")) # response is True or False print(client.check_health(query={"bundle": True}, diagnostic_url="http://localhost:8282/health")) # response is True or False - ``` -# Contributing # +# Contributing + +Fell free to open issue and send pull request. -#### Free to open issue and send PR #### +Thanks To [Contributors](https://github.com/Turall/OPA-python-client/graphs/contributors). +Contributions of any kind are welcome! -### OPA-python-client supports Python >= 3.5 +Before you start please read [CONTRIBUTING](https://github.com/Turall/OPA-python-client/blob/master/CONTRIBUTING.md) From 9d7910789ce04ac5a643aaf4fc62266fa252991e Mon Sep 17 00:00:00 2001 From: sabuhish Date: Fri, 10 Jun 2022 11:57:30 +0400 Subject: [PATCH 6/6] added poetry deployment --- .github/workflows/poetry-publish.yml | 24 ++++++++++++++++++++++++ .github/workflows/test-package.yml | 0 Makefile | 2 +- opa_client/test/test_opa.py | 2 +- 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/poetry-publish.yml create mode 100644 .github/workflows/test-package.yml diff --git a/.github/workflows/poetry-publish.yml b/.github/workflows/poetry-publish.yml new file mode 100644 index 0000000..b960548 --- /dev/null +++ b/.github/workflows/poetry-publish.yml @@ -0,0 +1,24 @@ +name: Upload OPA-python-client + +on: + release: + types: [created] + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v1 + with: + python-version: '3.8' + architecture: x64 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install poetry + - name: Build and publish + run: | + poetry version $(git describe --tags --abbrev=0) + poetry build + poetry publish --username ${{ secrets.PYPI_USERNAME }} --password ${{ secrets.PYPI_PASSWORD }} diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml new file mode 100644 index 0000000..e69de29 diff --git a/Makefile b/Makefile index 117fc2d..df69527 100644 --- a/Makefile +++ b/Makefile @@ -11,4 +11,4 @@ lint: format_code: isort . - blue . \ No newline at end of file + blue . diff --git a/opa_client/test/test_opa.py b/opa_client/test/test_opa.py index 5c0b887..c60d40b 100644 --- a/opa_client/test/test_opa.py +++ b/opa_client/test/test_opa.py @@ -6,8 +6,8 @@ from unittest import TestCase -from opa_client.opa import OpaClient from opa_client.errors import DeleteDataError, DeletePolicyError +from opa_client.opa import OpaClient class TestClient(TestCase):