Repository: MarketSquare/robotframework-openapidriver (language: python)

Issue:
Investigate: DynamicCore is most likely not needed
Due to @keyword decorator usage, direct subclassing will probably be a more direct solution.


Discussion / hints:

Fix patch:
diff --git a/poetry.lock b/poetry.lock
index 9cd0bca..1fe01b2 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,6 +1,6 @@
 [[package]]
 name = "anyio"
-version = "3.3.4"
+version = "3.4.0"
 description = "High level compatibility layer for multiple asynchronous event loop implementations"
 category = "dev"
 optional = false
@@ -12,7 +12,7 @@ sniffio = ">=1.1"
 
 [package.extras]
 doc = ["sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"]
-test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=6.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"]
+test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=6.0)", "pytest-mock (>=3.6.1)", "trustme", "contextlib2", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"]
 trio = ["trio (>=0.16)"]
 
 [[package]]
@@ -28,7 +28,7 @@ tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
 
 [[package]]
 name = "astroid"
-version = "2.8.6"
+version = "2.9.0"
 description = "An abstract syntax tree for Python with inference support."
 category = "dev"
 optional = false
@@ -55,7 +55,7 @@ tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>
 
 [[package]]
 name = "black"
-version = "21.11b1"
+version = "21.12b0"
 description = "The uncompromising code formatter."
 category = "dev"
 optional = false
@@ -66,7 +66,6 @@ click = ">=7.1.2"
 mypy-extensions = ">=0.4.3"
 pathspec = ">=0.9.0,<1"
 platformdirs = ">=2"
-regex = ">=2021.4.4"
 tomli = ">=0.2.6,<2.0.0"
 typing-extensions = [
     {version = ">=3.10.0.0", markers = "python_version < \"3.10\""},
@@ -98,7 +97,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
 
 [[package]]
 name = "charset-normalizer"
-version = "2.0.7"
+version = "2.0.9"
 description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
 category = "main"
 optional = false
@@ -150,7 +149,7 @@ python-versions = ">= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.*, != 3.4.*"
 
 [[package]]
 name = "docutils"
-version = "0.18"
+version = "0.18.1"
 description = "Docutils -- Python Documentation Utilities"
 category = "main"
 optional = false
@@ -273,7 +272,7 @@ python-versions = "*"
 
 [[package]]
 name = "more-itertools"
-version = "8.11.0"
+version = "8.12.0"
 description = "More routines for operating on iterables, beyond itertools"
 category = "main"
 optional = false
@@ -441,19 +440,19 @@ python-versions = ">=3.5"
 
 [[package]]
 name = "pylint"
-version = "2.11.1"
+version = "2.12.2"
 description = "python code static checker"
 category = "dev"
 optional = false
-python-versions = "~=3.6"
+python-versions = ">=3.6.2"
 
 [package.dependencies]
-astroid = ">=2.8.0,<2.9"
+astroid = ">=2.9.0,<2.10"
 colorama = {version = "*", markers = "sys_platform == \"win32\""}
 isort = ">=4.2.5,<6"
 mccabe = ">=0.6,<0.7"
 platformdirs = ">=2.2.0"
-toml = ">=0.7.1"
+toml = ">=0.9.2"
 typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
 
 [[package]]
@@ -472,14 +471,6 @@ category = "main"
 optional = false
 python-versions = ">=3.6"
 
-[[package]]
-name = "regex"
-version = "2021.11.10"
-description = "Alternative regular expression module, to replace re."
-category = "dev"
-optional = false
-python-versions = "*"
-
 [[package]]
 name = "requests"
 version = "2.26.0"
@@ -522,14 +513,6 @@ robotframework = ">=3.1"
 [package.extras]
 xls = ["pandas", "xlrd (>=1.2.0)", "openpyxl"]
 
-[[package]]
-name = "robotframework-pythonlibcore"
-version = "3.0.0"
-description = "Tools to ease creating larger test libraries for Robot Framework using Python."
-category = "main"
-optional = false
-python-versions = ">=3.6, <4"
-
 [[package]]
 name = "robotframework-stacktrace"
 version = "0.4.1"
@@ -635,7 +618,7 @@ python-versions = ">=3.6"
 
 [[package]]
 name = "types-requests"
-version = "2.26.0"
+version = "2.26.1"
 description = "Typing stubs for requests"
 category = "dev"
 optional = false
@@ -643,7 +626,7 @@ python-versions = "*"
 
 [[package]]
 name = "typing-extensions"
-version = "4.0.0"
+version = "4.0.1"
 description = "Backported and Experimental Type Hints for Python 3.6+"
 category = "dev"
 optional = false
@@ -664,7 +647,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
 
 [[package]]
 name = "uvicorn"
-version = "0.15.0"
+version = "0.16.0"
 description = "The lightning-fast ASGI server."
 category = "dev"
 optional = false
@@ -676,7 +659,7 @@ click = ">=7.0"
 h11 = ">=0.8"
 
 [package.extras]
-standard = ["websockets (>=9.1)", "httptools (>=0.2.0,<0.3.0)", "watchgod (>=0.6)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"]
+standard = ["httptools (>=0.2.0,<0.4.0)", "watchgod (>=0.6)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "websockets (>=9.1)", "websockets (>=10.0)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"]
 
 [[package]]
 name = "werkzeug"
@@ -712,28 +695,28 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes
 [metadata]
 lock-version = "1.1"
 python-versions = "^3.8"
-content-hash = "a69a3e7dd1e36c45a0599d2b906c24333a604a9aa032c877966fbbfa4689fcb6"
+content-hash = "79a37fff4941466e9150cfb247e061eea26e855c567ab1fa56f8673ecb8784c9"
 
 [metadata.files]
 anyio = [
-    {file = "anyio-3.3.4-py3-none-any.whl", hash = "sha256:4fd09a25ab7fa01d34512b7249e366cd10358cdafc95022c7ff8c8f8a5026d66"},
-    {file = "anyio-3.3.4.tar.gz", hash = "sha256:67da67b5b21f96b9d3d65daa6ea99f5d5282cb09f50eb4456f8fb51dffefc3ff"},
+    {file = "anyio-3.4.0-py3-none-any.whl", hash = "sha256:2855a9423524abcdd652d942f8932fda1735210f77a6b392eafd9ff34d3fe020"},
+    {file = "anyio-3.4.0.tar.gz", hash = "sha256:24adc69309fb5779bc1e06158e143e0b6d2c56b302a3ac3de3083c705a6ed39d"},
 ]
 asgiref = [
     {file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"},
     {file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"},
 ]
 astroid = [
-    {file = "astroid-2.8.6-py3-none-any.whl", hash = "sha256:cd8326b424c971e7d87678609cf6275d22028afd37d6ac59c16d47f1245882f6"},
-    {file = "astroid-2.8.6.tar.gz", hash = "sha256:5f6f75e45f15290e73b56f9dfde95b4bf96382284cde406ef4203e928335a495"},
+    {file = "astroid-2.9.0-py3-none-any.whl", hash = "sha256:776ca0b748b4ad69c00bfe0fff38fa2d21c338e12c84aa9715ee0d473c422778"},
+    {file = "astroid-2.9.0.tar.gz", hash = "sha256:5939cf55de24b92bda00345d4d0659d01b3c7dafb5055165c330bc7c568ba273"},
 ]
 attrs = [
     {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
     {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
 ]
 black = [
-    {file = "black-21.11b1-py3-none-any.whl", hash = "sha256:802c6c30b637b28645b7fde282ed2569c0cd777dbe493a41b6a03c1d903f99ac"},
-    {file = "black-21.11b1.tar.gz", hash = "sha256:a042adbb18b3262faad5aff4e834ff186bb893f95ba3a8013f09de1e5569def2"},
+    {file = "black-21.12b0-py3-none-any.whl", hash = "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"},
+    {file = "black-21.12b0.tar.gz", hash = "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3"},
 ]
 certifi = [
     {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
@@ -744,8 +727,8 @@ chardet = [
     {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"},
 ]
 charset-normalizer = [
-    {file = "charset-normalizer-2.0.7.tar.gz", hash = "sha256:e019de665e2bcf9c2b64e2e5aa025fa991da8720daa3c1138cadd2fd1856aed0"},
-    {file = "charset_normalizer-2.0.7-py3-none-any.whl", hash = "sha256:f7af805c321bfa1ce6714c51f254e0d5bb5e5834039bc17db7ebe3a4cec9492b"},
+    {file = "charset-normalizer-2.0.9.tar.gz", hash = "sha256:b0b883e8e874edfdece9c28f314e3dd5badf067342e42fb162203335ae61aa2c"},
+    {file = "charset_normalizer-2.0.9-py3-none-any.whl", hash = "sha256:1eecaa09422db5be9e29d7fc65664e6c33bd06f9ced7838578ba40d58bdf3721"},
 ]
 click = [
     {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"},
@@ -815,8 +798,8 @@ dictpath = [
     {file = "dictpath-0.1.3.tar.gz", hash = "sha256:751cde3b76b176d25f961b90c423a11a4d5ede9bd09ab0d64a85abb738c190d8"},
 ]
 docutils = [
-    {file = "docutils-0.18-py2.py3-none-any.whl", hash = "sha256:a31688b2ea858517fa54293e5d5df06fbb875fb1f7e4c64529271b77781ca8fc"},
-    {file = "docutils-0.18.tar.gz", hash = "sha256:c1d5dab2b11d16397406a282e53953fe495a46d69ae329f55aa98a5c4e3c5fbb"},
+    {file = "docutils-0.18.1-py2.py3-none-any.whl", hash = "sha256:23010f129180089fbcd3bc08cfefccb3b890b0050e1ca00c867036e9d161b98c"},
+    {file = "docutils-0.18.1.tar.gz", hash = "sha256:679987caf361a7539d76e584cbeddc311e3aee937877c87346f31debc63e9d06"},
 ]
 fastapi = [
     {file = "fastapi-0.70.0-py3-none-any.whl", hash = "sha256:a36d5f2fad931aa3575c07a3472c784e81f3e664e3bb5c8b9c88d0ec1104f59c"},
@@ -880,8 +863,8 @@ mccabe = [
     {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
 ]
 more-itertools = [
-    {file = "more-itertools-8.11.0.tar.gz", hash = "sha256:0a2fd25d343c08d7e7212071820e7e7ea2f41d8fb45d6bc8a00cd6ce3b7aab88"},
-    {file = "more_itertools-8.11.0-py3-none-any.whl", hash = "sha256:88afff98d83d08fe5e4049b81e2b54c06ebb6b3871a600040865c7a592061cbb"},
+    {file = "more-itertools-8.12.0.tar.gz", hash = "sha256:7dc6ad46f05f545f900dd59e8dfb4e84a4827b97b3cfecb175ea0c7d247f6064"},
+    {file = "more_itertools-8.12.0-py3-none-any.whl", hash = "sha256:43e6dd9942dffd72661a2c4ef383ad7da1e6a3e968a927ad7a6083ab410a688b"},
 ]
 mypy = [
     {file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"},
@@ -971,8 +954,8 @@ pygments = [
     {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"},
 ]
 pylint = [
-    {file = "pylint-2.11.1-py3-none-any.whl", hash = "sha256:0f358e221c45cbd4dad2a1e4b883e75d28acdcccd29d40c76eb72b307269b126"},
-    {file = "pylint-2.11.1.tar.gz", hash = "sha256:2c9843fff1a88ca0ad98a256806c82c5a8f86086e7ccbdb93297d86c3f90c436"},
+    {file = "pylint-2.12.2-py3-none-any.whl", hash = "sha256:daabda3f7ed9d1c60f52d563b1b854632fd90035bcf01443e234d3dc794e3b74"},
+    {file = "pylint-2.12.2.tar.gz", hash = "sha256:9d945a73640e1fec07ee34b42f5669b770c759acd536ec7b16d7e4b87a9c9ff9"},
 ]
 pyrsistent = [
     {file = "pyrsistent-0.18.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f4c8cabb46ff8e5d61f56a037974228e978f26bfefce4f61a4b1ac0ba7a2ab72"},
@@ -1032,57 +1015,6 @@ pyyaml = [
     {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
     {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
 ]
-regex = [
-    {file = "regex-2021.11.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9345b6f7ee578bad8e475129ed40123d265464c4cfead6c261fd60fc9de00bcf"},
-    {file = "regex-2021.11.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:416c5f1a188c91e3eb41e9c8787288e707f7d2ebe66e0a6563af280d9b68478f"},
-    {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0538c43565ee6e703d3a7c3bdfe4037a5209250e8502c98f20fea6f5fdf2965"},
-    {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee1227cf08b6716c85504aebc49ac827eb88fcc6e51564f010f11a406c0a667"},
-    {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6650f16365f1924d6014d2ea770bde8555b4a39dc9576abb95e3cd1ff0263b36"},
-    {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30ab804ea73972049b7a2a5c62d97687d69b5a60a67adca07eb73a0ddbc9e29f"},
-    {file = "regex-2021.11.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68a067c11463de2a37157930d8b153005085e42bcb7ad9ca562d77ba7d1404e0"},
-    {file = "regex-2021.11.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:162abfd74e88001d20cb73ceaffbfe601469923e875caf9118333b1a4aaafdc4"},
-    {file = "regex-2021.11.10-cp310-cp310-win32.whl", hash = "sha256:98ba568e8ae26beb726aeea2273053c717641933836568c2a0278a84987b2a1a"},
-    {file = "regex-2021.11.10-cp310-cp310-win_amd64.whl", hash = "sha256:780b48456a0f0ba4d390e8b5f7c661fdd218934388cde1a974010a965e200e12"},
-    {file = "regex-2021.11.10-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:dba70f30fd81f8ce6d32ddeef37d91c8948e5d5a4c63242d16a2b2df8143aafc"},
-    {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1f54b9b4b6c53369f40028d2dd07a8c374583417ee6ec0ea304e710a20f80a0"},
-    {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fbb9dc00e39f3e6c0ef48edee202f9520dafb233e8b51b06b8428cfcb92abd30"},
-    {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666abff54e474d28ff42756d94544cdfd42e2ee97065857413b72e8a2d6a6345"},
-    {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5537f71b6d646f7f5f340562ec4c77b6e1c915f8baae822ea0b7e46c1f09b733"},
-    {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2e07c6a26ed4bea91b897ee2b0835c21716d9a469a96c3e878dc5f8c55bb23"},
-    {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ca5f18a75e1256ce07494e245cdb146f5a9267d3c702ebf9b65c7f8bd843431e"},
-    {file = "regex-2021.11.10-cp36-cp36m-win32.whl", hash = "sha256:93a5051fcf5fad72de73b96f07d30bc29665697fb8ecdfbc474f3452c78adcf4"},
-    {file = "regex-2021.11.10-cp36-cp36m-win_amd64.whl", hash = "sha256:b483c9d00a565633c87abd0aaf27eb5016de23fed952e054ecc19ce32f6a9e7e"},
-    {file = "regex-2021.11.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fff55f3ce50a3ff63ec8e2a8d3dd924f1941b250b0aac3d3d42b687eeff07a8e"},
-    {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32d2a2b02ccbef10145df9135751abea1f9f076e67a4e261b05f24b94219e36"},
-    {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53db2c6be8a2710b359bfd3d3aa17ba38f8aa72a82309a12ae99d3c0c3dcd74d"},
-    {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2207ae4f64ad3af399e2d30dde66f0b36ae5c3129b52885f1bffc2f05ec505c8"},
-    {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5ca078bb666c4a9d1287a379fe617a6dccd18c3e8a7e6c7e1eb8974330c626a"},
-    {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd33eb9bdcfbabab3459c9ee651d94c842bc8a05fabc95edf4ee0c15a072495e"},
-    {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05b7d6d7e64efe309972adab77fc2af8907bb93217ec60aa9fe12a0dad35874f"},
-    {file = "regex-2021.11.10-cp37-cp37m-win32.whl", hash = "sha256:e71255ba42567d34a13c03968736c5d39bb4a97ce98188fafb27ce981115beec"},
-    {file = "regex-2021.11.10-cp37-cp37m-win_amd64.whl", hash = "sha256:07856afef5ffcc052e7eccf3213317fbb94e4a5cd8177a2caa69c980657b3cb4"},
-    {file = "regex-2021.11.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba05430e819e58544e840a68b03b28b6d328aff2e41579037e8bab7653b37d83"},
-    {file = "regex-2021.11.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7f301b11b9d214f83ddaf689181051e7f48905568b0c7017c04c06dfd065e244"},
-    {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aaa4e0705ef2b73dd8e36eeb4c868f80f8393f5f4d855e94025ce7ad8525f50"},
-    {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:788aef3549f1924d5c38263104dae7395bf020a42776d5ec5ea2b0d3d85d6646"},
-    {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f8af619e3be812a2059b212064ea7a640aff0568d972cd1b9e920837469eb3cb"},
-    {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85bfa6a5413be0ee6c5c4a663668a2cad2cbecdee367630d097d7823041bdeec"},
-    {file = "regex-2021.11.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f23222527b307970e383433daec128d769ff778d9b29343fb3496472dc20dabe"},
-    {file = "regex-2021.11.10-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:da1a90c1ddb7531b1d5ff1e171b4ee61f6345119be7351104b67ff413843fe94"},
-    {file = "regex-2021.11.10-cp38-cp38-win32.whl", hash = "sha256:0617383e2fe465732af4509e61648b77cbe3aee68b6ac8c0b6fe934db90be5cc"},
-    {file = "regex-2021.11.10-cp38-cp38-win_amd64.whl", hash = "sha256:a3feefd5e95871872673b08636f96b61ebef62971eab044f5124fb4dea39919d"},
-    {file = "regex-2021.11.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7f325be2804246a75a4f45c72d4ce80d2443ab815063cdf70ee8fb2ca59ee1b"},
-    {file = "regex-2021.11.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:537ca6a3586931b16a85ac38c08cc48f10fc870a5b25e51794c74df843e9966d"},
-    {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eef2afb0fd1747f33f1ee3e209bce1ed582d1896b240ccc5e2697e3275f037c7"},
-    {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:432bd15d40ed835a51617521d60d0125867f7b88acf653e4ed994a1f8e4995dc"},
-    {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b43c2b8a330a490daaef5a47ab114935002b13b3f9dc5da56d5322ff218eeadb"},
-    {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:962b9a917dd7ceacbe5cd424556914cb0d636001e393b43dc886ba31d2a1e449"},
-    {file = "regex-2021.11.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fa8c626d6441e2d04b6ee703ef2d1e17608ad44c7cb75258c09dd42bacdfc64b"},
-    {file = "regex-2021.11.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3c5fb32cc6077abad3bbf0323067636d93307c9fa93e072771cf9a64d1c0f3ef"},
-    {file = "regex-2021.11.10-cp39-cp39-win32.whl", hash = "sha256:3b5df18db1fccd66de15aa59c41e4f853b5df7550723d26aa6cb7f40e5d9da5a"},
-    {file = "regex-2021.11.10-cp39-cp39-win_amd64.whl", hash = "sha256:83ee89483672b11f8952b158640d0c0ff02dc43d9cb1b70c1564b49abe92ce29"},
-    {file = "regex-2021.11.10.tar.gz", hash = "sha256:f341ee2df0999bfdf7a95e448075effe0db212a59387de1a70690e4acb03d4c6"},
-]
 requests = [
     {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"},
     {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"},
@@ -1095,10 +1027,6 @@ robotframework-datadriver = [
     {file = "robotframework-datadriver-1.5.0.tar.gz", hash = "sha256:67e14bf8d7c4d370ecaca35879cbf786c3f30382d3ceaf50c6ed81820e8166cf"},
     {file = "robotframework_datadriver-1.5.0-py3-none-any.whl", hash = "sha256:58f5009fa2d91fba5cfdefe72927523538e10ecee2657808b927f39b63ef47b9"},
 ]
-robotframework-pythonlibcore = [
-    {file = "robotframework-pythonlibcore-3.0.0.tar.gz", hash = "sha256:1bce3b8dfcb7519789ee3a89320f6402e126f6d0a02794184a1ab8cee0e46b5d"},
-    {file = "robotframework_pythonlibcore-3.0.0-py2.py3-none-any.whl", hash = "sha256:af10c2403cd38834988c4ce68ffb6ec6f9b14bd2cd39ecf836d443377c59b7c4"},
-]
 robotframework-stacktrace = [
     {file = "robotframework-stacktrace-0.4.1.tar.gz", hash = "sha256:e96cb36e7e9ab55104c1f7d3606249a109e0a4c3bb6a0e294bff07d54ee6f6a5"},
     {file = "robotframework_stacktrace-0.4.1-py3-none-any.whl", hash = "sha256:018d7a55b99733e64e3cc0b134771b61a47de61de23609ed35c7bf0a53e9290e"},
@@ -1159,19 +1087,20 @@ tomli = [
     {file = "tomli-1.2.2.tar.gz", hash = "sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee"},
 ]
 types-requests = [
-    {file = "types-requests-2.26.0.tar.gz", hash = "sha256:df5ec8c34b413a42ebb38e4f96bdeb68090b875bdfcc5138dc82989c95445883"},
-    {file = "types_requests-2.26.0-py3-none-any.whl", hash = "sha256:809b5dcd3c408ac39d11d593835b6aff32420b3e7ddb79c7f3e823330f040466"},
+    {file = "types-requests-2.26.1.tar.gz", hash = "sha256:0893e112e1510bbb67f537941c92192de7472e51bf7f236e0e583866f0ed933e"},
+    {file = "types_requests-2.26.1-py3-none-any.whl", hash = "sha256:853571b3accc188976c0f4feffcaebf6cdfc170082b5e43f3358aa78de61f531"},
 ]
 typing-extensions = [
-    {file = "typing_extensions-4.0.0-py3-none-any.whl", hash = "sha256:829704698b22e13ec9eaf959122315eabb370b0884400e9818334d8b677023d9"},
+    {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"},
+    {file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"},
 ]
 urllib3 = [
     {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"},
     {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"},
 ]
 uvicorn = [
-    {file = "uvicorn-0.15.0-py3-none-any.whl", hash = "sha256:17f898c64c71a2640514d4089da2689e5db1ce5d4086c2d53699bf99513421c1"},
-    {file = "uvicorn-0.15.0.tar.gz", hash = "sha256:d9a3c0dd1ca86728d3e235182683b4cf94cd53a867c288eaeca80ee781b2caff"},
+    {file = "uvicorn-0.16.0-py3-none-any.whl", hash = "sha256:d8c839231f270adaa6d338d525e2652a0b4a5f4c2430b5c4ef6ae4d11776b0d2"},
+    {file = "uvicorn-0.16.0.tar.gz", hash = "sha256:eacb66afa65e0648fcbce5e746b135d09722231ffffc61883d4fac2b62fbea8d"},
 ]
 werkzeug = [
     {file = "Werkzeug-2.0.2-py3-none-any.whl", hash = "sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f"},
diff --git a/pyproject.toml b/pyproject.toml
index a6d6966..37f0d82 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -23,7 +23,6 @@ packages = [
 [tool.poetry.dependencies]
 python = "^3.8"
 robotframework = ">=4"
-robotframework-pythonlibcore = ">=3"
 robotframework-datadriver = ">=1.5"
 requests = "*"
 prance = "*"
@@ -94,3 +93,6 @@ disable = ["W1203"]
 
 [tool.pylint.'FORMAT CHECKER']
 max-line-length=120
+
+[tool.pylint.'SIMILARITIES CHECKER']
+ignore-imports="yes"
diff --git a/src/OpenApiDriver/dto_base.py b/src/OpenApiDriver/dto_base.py
index 9a00f59..7873627 100644
--- a/src/OpenApiDriver/dto_base.py
+++ b/src/OpenApiDriver/dto_base.py
@@ -17,7 +17,7 @@
 NOT_SET = object()
 
 
-class ResourceRelation(ABC):
+class ResourceRelation(ABC):  # pylint: disable=too-few-public-methods
     """ABC for all resource relations or restrictions within the API."""
 
     property_name: str
@@ -81,7 +81,8 @@ class UniquePropertyValueConstraint(ResourceRelation):
 ]
 
 
-class DtoBase(ABC):
+@dataclass
+class Dto(ABC):
     """Base class for the Dto class."""
 
     @staticmethod
@@ -183,12 +184,3 @@ def get_invalidated_data(
             )
             return properties
         return properties  # pragma: no cover
-
-
-@dataclass
-class DataClassMixin:
-    """Mixin to add dataclass functionality to an ABC."""
-
-
-class Dto(DataClassMixin, DtoBase):
-    """Abstract base class to support custom mappings of resource dependencies."""
diff --git a/src/OpenApiDriver/openapi_executors.py b/src/OpenApiDriver/openapi_executors.py
index 3c51d24..456bfd0 100644
--- a/src/OpenApiDriver/openapi_executors.py
+++ b/src/OpenApiDriver/openapi_executors.py
@@ -1,7 +1,6 @@
 """Module containing the classes to perform automatic OpenAPI contract validation."""
 
 import json as _json
-import sys
 from dataclasses import asdict
 from enum import Enum
 from logging import getLogger
@@ -9,22 +8,23 @@
 from random import choice
 from typing import Any, Dict, List, Optional, Union
 
-from openapi_core import create_spec
 from openapi_core.contrib.requests import (
     RequestsOpenAPIRequest,
     RequestsOpenAPIResponse,
 )
 from openapi_core.templating.paths.exceptions import ServerNotFound
-from openapi_core.validation.response.validators import ResponseValidator
 from requests import Response
-from requests.auth import AuthBase, HTTPBasicAuth
+from requests.auth import AuthBase
 from robot.api import SkipExecution
 from robot.api.deco import keyword, library
 from robot.libraries.BuiltIn import BuiltIn
-from robotlibcore import DynamicCore
 
-from OpenApiDriver.dto_utils import get_dto_class
-from OpenApiDriver.openapi_libcore import OpenApiLibCore, RequestData, resolve_schema
+from OpenApiDriver.openapi_libcore import (
+    OpenApiLibCore,
+    RequestData,
+    RequestValues,
+    resolve_schema,
+)
 
 run_keyword = BuiltIn().run_keyword
 
@@ -42,13 +42,13 @@ class ValidationLevel(str, Enum):
 
 
 @library
-class OpenApiExecutors(DynamicCore):  # pylint: disable=too-many-instance-attributes
+class OpenApiExecutors(OpenApiLibCore):  # pylint: disable=too-many-instance-attributes
     """Main class providing the keywords and core logic to perform endpoint validations."""
 
     ROBOT_LIBRARY_DOC_FORMAT = "ROBOT"
     ROBOT_LIBRARY_SCOPE = "TEST SUITE"
 
-    def __init__(  # pylint: disable=too-many-arguments, too-many-locals
+    def __init__(  # pylint: disable=too-many-arguments
         self,
         openapi_specification: Dict[str, Any],
         origin: str = "",
@@ -63,42 +63,7 @@ def __init__(  # pylint: disable=too-many-arguments, too-many-locals
         require_body_for_invalid_url: bool = False,
         invalid_property_default_response: int = 422,
     ) -> None:
-        self.openapi_spec: Dict[str, Any] = openapi_specification
-        validation_spec = create_spec(self.openapi_spec)
-        self.response_validator = ResponseValidator(
-            spec=validation_spec,
-            base_url=base_path,
-        )
-        self.origin = origin
-        self.base_url = f"{self.origin}{base_path}"
-        # only username and password, security_token or auth object should be provided
-        # if multiple are provided, username and password take precendence
-        self.security_token = security_token
-        self.auth = auth
-        if username and password:
-            self.auth = HTTPBasicAuth(username, password)
-        self.response_validation = response_validation
-        self.disable_server_validation = disable_server_validation
-        self.require_body_for_invalid_url = require_body_for_invalid_url
-        self.invalid_property_default_response = invalid_property_default_response
-        if mappings_path and str(mappings_path) != ".":
-            mappings_path = Path(mappings_path)
-            if not mappings_path.is_file():
-                logger.warning(
-                    f"mappings_path '{mappings_path}' is not a Python module."
-                )
-            # intermediate variable to ensure path.append is possible so we'll never
-            # path.pop a location that we didn't append
-            mappings_folder = str(mappings_path.parent)
-            sys.path.append(mappings_folder)
-            mappings_module_name = mappings_path.stem
-            self.get_dto_class = get_dto_class(
-                mappings_module_name=mappings_module_name
-            )
-            sys.path.pop()
-        else:
-            self.get_dto_class = get_dto_class(mappings_module_name="no_mapping")
-        openapi_libcore = OpenApiLibCore(
+        super().__init__(
             openapi_specification=openapi_specification,
             origin=origin,
             base_path=base_path,
@@ -108,7 +73,10 @@ def __init__(  # pylint: disable=too-many-arguments, too-many-locals
             security_token=security_token,
             auth=auth,
         )
-        DynamicCore.__init__(self, [openapi_libcore])
+        self.response_validation = response_validation
+        self.disable_server_validation = disable_server_validation
+        self.require_body_for_invalid_url = require_body_for_invalid_url
+        self.invalid_property_default_response = invalid_property_default_response
 
     @keyword
     def test_unauthorized(self, endpoint: str, method: str) -> None:
@@ -191,18 +159,17 @@ def test_endpoint(self, endpoint: str, method: str, status_code: int) -> None:
         request_data: RequestData = self.get_request_data(
             method=method, endpoint=endpoint
         )
-        dto = request_data.dto
         params = request_data.params
         headers = request_data.headers
-        json_data = asdict(dto)
+        json_data = asdict(request_data.dto)
         # when patching, get the original data to check only patched data has changed
         if method == "PATCH":
-            original_data = self.get_original_data(endpoint=endpoint, url=url)
+            original_data = self.get_original_data(url=url)
         # in case of a status code indicating an error, ensure the error occurs
         if status_code >= 400:
-            data_relations = dto.get_relations_for_error_code(status_code)
-            parameter_relations = dto.get_parameter_relations_for_error_code(
-                status_code
+            data_relations = request_data.dto.get_relations_for_error_code(status_code)
+            parameter_relations = (
+                request_data.dto.get_parameter_relations_for_error_code(status_code)
             )
             invalidation_keyword_data = {
                 "get_invalid_json_data": [
@@ -211,7 +178,7 @@ def test_endpoint(self, endpoint: str, method: str, status_code: int) -> None:
                     request_data.dto_schema,
                     url,
                     method,
-                    dto,
+                    request_data.dto,
                     status_code,
                 ],
                 "invalidate_parameters": [
@@ -252,51 +219,56 @@ def test_endpoint(self, endpoint: str, method: str, status_code: int) -> None:
                 logger.error(
                     f"No Dto mapping found to cause status_code {status_code}."
                 )
+        run_keyword(
+            "perform_validated_request",
+            endpoint,
+            status_code,
+            RequestValues(
+                url=url,
+                method=method,
+                params=params,
+                headers=headers,
+                json_data=json_data,
+            ),
+            original_data,
+        )
         if status_code < 300 and (
             request_data.has_optional_properties
             or request_data.has_optional_params
             or request_data.has_optional_headers
         ):
             logger.info("Performing request without optional properties and parameters")
-            url_ = run_keyword("get_valid_url", endpoint, method)
+            url = run_keyword("get_valid_url", endpoint, method)
             request_data = self.get_request_data(method=method, endpoint=endpoint)
-            params_ = request_data.get_required_params()
-            headers_ = request_data.get_required_headers()
-            json_data_ = request_data.get_required_properties_dict()
+            params = request_data.get_required_params()
+            headers = request_data.get_required_headers()
+            json_data = request_data.get_required_properties_dict()
             if method == "PATCH":
-                original_data_ = self.get_original_data(endpoint=endpoint, url=url_)
+                original_data = self.get_original_data(url=url)
             else:
-                original_data_ = None
+                original_data = None
             run_keyword(
                 "perform_validated_request",
                 endpoint,
-                method,
                 status_code,
-                url_,
-                params_,
-                headers_,
-                json_data_,
-                original_data_,
+                RequestValues(
+                    url=url,
+                    method=method,
+                    params=params,
+                    headers=headers,
+                    json_data=json_data,
+                ),
+                original_data,
             )
-        run_keyword(
-            "perform_validated_request",
-            endpoint,
-            method,
-            status_code,
-            url,
-            params,
-            headers,
-            json_data,
-            original_data,
-        )
 
-    def get_original_data(self, endpoint: str, url: str) -> Optional[Dict[str, Any]]:
+    def get_original_data(self, url: str) -> Optional[Dict[str, Any]]:
         """
         Attempt to GET the current data for the given url and return it.
 
         If the GET request fails, None is returned.
         """
         original_data = None
+        endpoint = self.get_parameterized_endpoint_from_url(url)
         get_request_data = self.get_request_data(endpoint=endpoint, method="GET")
         get_params = get_request_data.params
         get_headers = get_request_data.headers
@@ -311,12 +283,8 @@ def get_original_data(self, endpoint: str, url: str) -> Optional[Dict[str, Any]]
     def perform_validated_request(
         self,
         endpoint: str,
-        method: str,
         status_code: int,
-        url: str,
-        params: Dict[str, Any],
-        headers: Dict[str, str],
-        json_data: Dict[str, Any],
+        request_values: RequestValues,
         original_data: Optional[Dict[str, Any]] = None,
     ) -> None:
         """
@@ -325,7 +293,12 @@ def perform_validated_request(
         the target resource was indeed deleted (OK response) or not (error responses).
         """
         response = run_keyword(
-            "authorized_request", url, method, params, headers, json_data
+            "authorized_request",
+            request_values.url,
+            request_values.method,
+            request_values.params,
+            request_values.headers,
+            request_values.json_data,
         )
         if response.status_code != status_code:
             if not response.ok:
@@ -340,37 +313,37 @@ def perform_validated_request(
                 logger.error(f"Failed to get json body from response: {exception}")
                 response_json = {}
             logger.info(
-                f"\nSend: {_json.dumps(json_data, indent=4, sort_keys=True)}"
+                f"\nSend: {_json.dumps(request_values.json_data, indent=4, sort_keys=True)}"
                 f"\nGot: {_json.dumps(response_json, indent=4, sort_keys=True)}"
             )
             raise AssertionError(
                 f"Response status_code {response.status_code} was not {status_code}"
             )
         run_keyword("validate_response", endpoint, response, original_data)
-        if method == "DELETE":
+        if request_values.method == "DELETE":
             get_request_data = self.get_request_data(endpoint=endpoint, method="GET")
             get_params = get_request_data.params
             get_headers = get_request_data.headers
             get_response = run_keyword(
-                "authorized_request", url, "GET", get_params, get_headers
+                "authorized_request", request_values.url, "GET", get_params, get_headers
             )
             if response.ok:
                 if get_response.ok:
                     raise AssertionError(
-                        f"Resource still exists after deletion. Url was {url}"
+                        f"Resource still exists after deletion. Url was {request_values.url}"
                     )
                 # if the endpoint supports GET, 404 is expected, if not 405 is expected
                 if get_response.status_code not in [404, 405]:
                     logger.warning(
                         f"Unexpected response after deleting resource: Status_code "
-                        f"{get_response.status_code} was received after trying to get {url} "
+                        f"{get_response.status_code} was received after trying to get {request_values.url} "
                         f"after sucessfully deleting it."
                     )
             else:
                 if not get_response.ok:
                     raise AssertionError(
                         f"Resource could not be retrieved after failed deletion. "
-                        f"Url was {url}, status_code was {get_response.status_code}"
+                        f"Url was {request_values.url}, status_code was {get_response.status_code}"
                     )
 
     @keyword
@@ -394,24 +367,7 @@ def validate_response(
             assert not response.content
             return None
         # validate the response against the schema
-        openapi_request = RequestsOpenAPIRequest(response.request)
-        openapi_response = RequestsOpenAPIResponse(response)
-        validation_result = self.response_validator.validate(
-            request=openapi_request,
-            response=openapi_response,
-        )
-        if self.disable_server_validation:
-            validation_result.errors = [
-                e for e in validation_result.errors if not isinstance(e, ServerNotFound)
-            ]
-        if self.response_validation == ValidationLevel.STRICT:
-            validation_result.raise_for_errors()
-        if self.response_validation in [ValidationLevel.WARN, ValidationLevel.INFO]:
-            for validation_error in validation_result.errors:
-                if self.response_validation == ValidationLevel.WARN:
-                    logger.warning(validation_error)
-                else:
-                    logger.info(validation_error)
+        self._validate_response_against_spec(response)
 
         request_method = response.request.method
         if request_method is None:
@@ -420,6 +376,7 @@ def validate_response(
                 f"on the request property of the provided response."
             )
             return None
+
         response_spec = self._get_response_spec(
             endpoint=endpoint,
             method=request_method,
@@ -435,11 +392,12 @@ def validate_response(
                 f"Content-Type '{response.headers['Content-Type']}' of the response "
                 f"is not '{content_type}' as specified in the OpenAPI document."
             )
-        json_response = response.json()
 
-        response_schema = response_spec["content"][content_type]["schema"]
-        resolved_schema = resolve_schema(response_schema)
-        if list_item_schema := resolved_schema.get("items"):
+        json_response = response.json()
+        response_schema = resolve_schema(
+            response_spec["content"][content_type]["schema"]
+        )
+        if list_item_schema := response_schema.get("items"):
             if not isinstance(json_response, list):
                 raise AssertionError(
                     f"Response schema violation: the schema specifies an array as "
@@ -461,27 +419,46 @@ def validate_response(
             return None
 
         run_keyword(
-            "validate_resource_properties", json_response, resolved_schema["properties"]
+            "validate_resource_properties", json_response, response_schema["properties"]
         )
         # ensure the href is valid if present in the response
         if href := json_response.get("href"):
-            url = f"{self.origin}{href}"
-            endpoint = url.replace(self.base_url, "")
-            request_data = self.get_request_data(endpoint=endpoint, method="GET")
-            params = request_data.params
-            headers = request_data.headers
-            get_response = self.authorized_request(
-                url=url, method="GET", params=params, headers=headers
-            )
-            assert (
-                get_response.json() == json_response
-            ), f"{get_response.json()} not equal to original {json_response}"
+            self._assert_href_is_valid(href, json_response)
         # every property that was sucessfully send and that is in the response
         # schema must have the value that was send
         if response.ok and response.request.method in ["POST", "PUT", "PATCH"]:
             run_keyword("validate_send_response", response, original_data)
         return None
 
+    def _assert_href_is_valid(self, href: str, json_response: Dict[str, Any]):
+        url = f"{self.origin}{href}"
+        endpoint = url.replace(self.base_url, "")
+        request_data = self.get_request_data(endpoint=endpoint, method="GET")
+        params = request_data.params
+        headers = request_data.headers
+        get_response = run_keyword("authorized_request", url, "GET", params, headers)
+        assert (
+            get_response.json() == json_response
+        ), f"{get_response.json()} not equal to original {json_response}"
+
+    def _validate_response_against_spec(self, response: Response):
+        validation_result = self.response_validator.validate(
+            request=RequestsOpenAPIRequest(response.request),
+            response=RequestsOpenAPIResponse(response),
+        )
+        if self.disable_server_validation:
+            validation_result.errors = [
+                e for e in validation_result.errors if not isinstance(e, ServerNotFound)
+            ]
+        if self.response_validation == ValidationLevel.STRICT:
+            validation_result.raise_for_errors()
+        if self.response_validation in [ValidationLevel.WARN, ValidationLevel.INFO]:
+            for validation_error in validation_result.errors:
+                if self.response_validation == ValidationLevel.WARN:
+                    logger.warning(validation_error)
+                else:
+                    logger.info(validation_error)
+
     @staticmethod
     @keyword
     def validate_resource_properties(
diff --git a/src/OpenApiDriver/openapi_libcore.py b/src/OpenApiDriver/openapi_libcore.py
index f89258f..8d43353 100644
--- a/src/OpenApiDriver/openapi_libcore.py
+++ b/src/OpenApiDriver/openapi_libcore.py
@@ -3,7 +3,7 @@
 import json as _json
 import sys
 from copy import deepcopy
-from dataclasses import asdict, dataclass, field, Field, make_dataclass
+from dataclasses import Field, asdict, dataclass, field, make_dataclass
 from itertools import zip_longest
 from logging import getLogger
 from pathlib import Path
@@ -70,6 +70,17 @@ def merge_schemas(first: Dict[str, Any], second: Dict[str, Any]) -> Dict[str, An
     return merged_schema
 
 
+@dataclass
+class RequestValues:
+    """Helper class to hold parameter values needed to make a request."""
+
+    url: str
+    method: str
+    params: Optional[Dict[str, Any]]
+    headers: Optional[Dict[str, str]]
+    json_data: Optional[Dict[str, Any]]
+
+
 @dataclass
 class RequestData:
     """Helper class to manage parameters used when making requests."""
@@ -82,12 +93,16 @@ class RequestData:
 
     @property
     def has_optional_properties(self) -> bool:
+        """Whether or not the dto data (json data) contains optional properties."""
         properties = asdict(self.dto).keys()
-        in_required_func: Callable[[str], bool] = lambda x: x in self.dto_schema.get("required", [])
+        in_required_func: Callable[[str], bool] = lambda x: x in self.dto_schema.get(
+            "required", []
+        )
         return not all(map(in_required_func, properties))
 
     @property
     def has_optional_params(self) -> bool:
+        """Whether or not any of the query parameters are optional."""
         optional_params = [
             p.get("name")
             for p in self.parameters
@@ -98,6 +113,7 @@ def has_optional_params(self) -> bool:
 
     @property
     def has_optional_headers(self) -> bool:
+        """Whether or not any of the headers are optional."""
         optional_headers = [
             p.get("name")
             for p in self.parameters
@@ -107,6 +123,7 @@ def has_optional_headers(self) -> bool:
         return any(map(in_optional_headers, self.headers))
 
     def get_required_properties_dict(self) -> Dict[str, Any]:
+        """Get the json-compatible dto data containing only the required properties."""
         required_properties = self.dto_schema.get("required", [])
         required_properties_dict: Dict[str, Any] = {}
         for key, value in asdict(self.dto).items():
@@ -115,12 +132,14 @@ def get_required_properties_dict(self) -> Dict[str, Any]:
         return required_properties_dict
 
     def get_required_params(self) -> Dict[str, str]:
+        """Get the params dict containing only the required query parameters."""
         required_parameters = [
             p.get("name") for p in self.parameters if p.get("required")
         ]
         return {k: v for k, v in self.params.items() if k in required_parameters}
 
     def get_required_headers(self) -> Dict[str, str]:
+        """Get the headers dict containing only the required headers."""
         required_parameters = [
             p.get("name") for p in self.parameters if p.get("required")
         ]
@@ -225,42 +244,20 @@ def get_valid_id_for_endpoint(self, endpoint: str, method: str) -> str:
         # Try to create a new resource to prevent conflicts caused by
         # operations performed on the same resource by other test cases
         request_data = self.get_request_data(endpoint=endpoint, method="POST")
-        params = request_data.params
-        headers = request_data.headers
-        dto = request_data.dto
-        try:
-            json_data = asdict(dto)
-            response: Response = run_keyword(
-                "authorized_request", url, "POST", params, headers, json_data
-            )
-        except NotImplementedError as exception:
-            logger.debug(f"get_valid_id_for_endpoint POST failed: {exception}")
+
+        response: Response = run_keyword(
+            "authorized_request",
+            url,
+            "POST",
+            request_data.get_required_params(),
+            request_data.get_required_headers(),
+            request_data.get_required_properties_dict(),
+        )
+        if response.status_code == 405:
             # For endpoints that do no support POST, try to get an existing id using GET
             try:
-                request_data = self.get_request_data(endpoint=endpoint, method="GET")
-                params = request_data.params
-                headers = request_data.headers
-                response = run_keyword(
-                    "authorized_request", url, "GET", params, headers
-                )
-                assert response.ok
-                response_data: Union[
-                    Dict[str, Any], List[Dict[str, Any]]
-                ] = response.json()
-                if isinstance(response_data, list):
-                    valid_ids: List[str] = [item["id"] for item in response_data]
-                    logger.debug(
-                        f"get_valid_id_for_endpoint: returning choice from list {valid_ids}"
-                    )
-                    return choice(valid_ids)
-                if valid_id := response_data.get("id"):
-                    logger.debug(f"get_valid_id_for_endpoint: returning {valid_id}")
-                    return valid_id
-                valid_ids = [item["id"] for item in response_data["items"]]
-                logger.debug(
-                    f"get_valid_id_for_endpoint: returning choice from items {valid_ids}"
-                )
-                return choice(valid_ids)
+                valid_id = choice(run_keyword("get_ids_for_endpoint", url))
+                return valid_id
             except Exception as exception:
                 logger.debug(
                     f"Failed to get a valid id using GET on {url}"
@@ -301,6 +298,31 @@ def get_valid_id_for_endpoint(self, endpoint: str, method: str) -> str:
             valid_id = response_data["id"]
         return valid_id
 
+    @keyword
+    def get_ids_for_endpoint(self, url: str) -> List[str]:
+        """
+        Perform a GET request on the `url` and return the list of resource
+        `ids` from the response.
+        """
+        endpoint = self.get_parameterized_endpoint_from_url(url)
+        request_data = self.get_request_data(endpoint=endpoint, method="GET")
+        response = run_keyword(
+            "authorized_request",
+            url,
+            "GET",
+            request_data.get_required_params(),
+            request_data.get_required_headers(),
+        )
+        assert response.ok
+        response_data: Union[Dict[str, Any], List[Dict[str, Any]]] = response.json()
+        if isinstance(response_data, list):
+            valid_ids: List[str] = [item["id"] for item in response_data]
+            return valid_ids
+        if valid_id := response_data.get("id"):
+            return [valid_id]
+        valid_ids = [item["id"] for item in response_data["items"]]
+        return valid_ids
+
     @keyword
     def get_request_data(self, endpoint: str, method: str) -> RequestData:
         """Return an object with valid request data for body, headers and query params."""
@@ -308,21 +330,18 @@ def get_request_data(self, endpoint: str, method: str) -> RequestData:
         # The endpoint can contain already resolved Ids that have to be matched
         # against the parametrized endpoints in the paths section.
         spec_endpoint = self.get_parametrized_endpoint(endpoint)
+        dto_class = self.get_dto_class(endpoint=spec_endpoint, method=method)
         try:
             method_spec = self.openapi_spec["paths"][spec_endpoint][method]
-        except KeyError as exception:
-            raise NotImplementedError(
-                f"method '{method}' not suported on '{spec_endpoint}"
-            ) from exception
-        dto_class = self.get_dto_class(endpoint=spec_endpoint, method=method)
-
-        parameters = method_spec.get("parameters", [])
-        parameter_relations = dto_class.get_parameter_relations()
-        query_params = [p for p in parameters if p.get("in") == "query"]
-        header_params = [p for p in parameters if p.get("in") == "header"]
-        params = self.get_parameter_data(query_params, parameter_relations)
-        headers = self.get_parameter_data(header_params, parameter_relations)
+        except KeyError:
+            logger.warning(
+                f"method '{method}' not suported on '{spec_endpoint}, using empty spec."
+            )
+            method_spec = {}
 
+        parameters, params, headers = self.get_request_parameters(
+            dto_class=dto_class, method_spec=method_spec
+        )
         if (body_spec := method_spec.get("requestBody", None)) is None:
             if dto_class == DefaultDto:
                 dto_instance: Dto = DefaultDto()
@@ -339,29 +358,16 @@ def get_request_data(self, endpoint: str, method: str) -> RequestData:
                 params=params,
                 headers=headers,
             )
-        # Content should be a single key/value entry, so use tuple assignment
-        (content_type,) = body_spec["content"].keys()
-        if content_type != "application/json":
-            # At present no supported for other types.
-            raise NotImplementedError(f"content_type '{content_type}' not supported")
-        content_schema = body_spec["content"][content_type]["schema"]
-        # TODO: is resolve_schema still needed?
-        resolved_schema: Dict[str, Any] = resolve_schema(content_schema)
+        content_schema = self.get_content_schema(body_spec)
         dto_data = self.get_json_data_for_dto_class(
-            schema=resolved_schema,
+            schema=content_schema,
             dto_class=dto_class,
             operation_id=method_spec.get("operationId"),
         )
         if dto_data is None:
             dto_instance = DefaultDto()
         else:
-            fields: List[Union[str, Tuple[str, type], Tuple[str, type, Optional[Field[Any]]]]] = []
-            for key, value in dto_data.items():
-                required_properties = resolved_schema.get("required", [])
-                if key in required_properties:
-                    fields.append((key, type(value)))
-                else:
-                    fields.append((key, type(value), field(default=None)))
+            fields = self.get_fields_from_dto_data(content_schema, dto_data)
             dto_class = make_dataclass(
                 cls_name=method_spec["operationId"],
                 fields=fields,
@@ -370,12 +376,51 @@ def get_request_data(self, endpoint: str, method: str) -> RequestData:
             dto_instance = dto_class(**dto_data)  # type: ignore[call-arg]
         return RequestData(
             dto=dto_instance,
-            dto_schema=resolved_schema,
+            dto_schema=content_schema,
             parameters=parameters,
             params=params,
             headers=headers,
         )
 
+    @staticmethod
+    def get_fields_from_dto_data(
+        content_schema: Dict[str, Any], dto_data: Dict[str, Any]
+    ):
+        """Get a dataclasses fields list based on the content_schema and dto_data."""
+        fields: List[Union[str, Tuple[str, type], Tuple[str, type, Field[Any]]]] = []
+        for key, value in dto_data.items():
+            required_properties = content_schema.get("required", [])
+            if key in required_properties:
+                fields.append((key, type(value)))
+            else:
+                fields.append((key, type(value), field(default=None)))  # type: ignore[arg-type]
+        return fields
+
+    def get_request_parameters(
+        self, dto_class: Union[Dto, Type[Dto]], method_spec: Dict[str, Any]
+    ) -> Tuple[List[Dict[str, Any]], Dict[str, Any], Dict[str, str]]:
+        """Get the methods parameter spec and params and headers with valid data."""
+        parameters = method_spec.get("parameters", [])
+        parameter_relations = dto_class.get_parameter_relations()
+        query_params = [p for p in parameters if p.get("in") == "query"]
+        header_params = [p for p in parameters if p.get("in") == "header"]
+        params = self.get_parameter_data(query_params, parameter_relations)
+        headers = self.get_parameter_data(header_params, parameter_relations)
+        return parameters, params, headers
+
+    @staticmethod
+    def get_content_schema(body_spec: Dict[str, Any]):
+        """Get the content schema from the requestBody spec."""
+        # Content should be a single key/value entry, so use tuple assignment
+        (content_type,) = body_spec["content"].keys()
+        if content_type != "application/json":
+            # At present no supported for other types.
+            raise NotImplementedError(f"content_type '{content_type}' not supported")
+        content_schema = body_spec["content"][content_type]["schema"]
+        # TODO: is resolve_schema still needed?
+        resolved_schema: Dict[str, Any] = resolve_schema(content_schema)
+        return resolved_schema
+
     def get_parametrized_endpoint(self, endpoint: str) -> str:
         """
         Get the parametrized endpoint as found in the `paths` section of the openapi
@@ -511,11 +556,7 @@ def get_invalidated_url(self, valid_url: str) -> Optional[str]:
         Return an url with all the path parameters in the `valid_url` replaced by a
         random UUID. If the `valid_url` does not contain any parameters, None is returned.
         """
-        endpoint = valid_url.replace(self.base_url, "")
-        endpoint_parts = endpoint.split("/")
-        # first part will be '' since an endpoint starts with /
-        endpoint_parts.pop(0)
-        parameterized_endpoint = self.get_parametrized_endpoint(endpoint=endpoint)
+        parameterized_endpoint = self.get_parameterized_endpoint_from_url(valid_url)
         parameterized_url = self.base_url + parameterized_endpoint
         valid_url_parts = list(reversed(valid_url.split("/")))
         parameterized_parts = reversed(parameterized_url.split("/"))
@@ -530,6 +571,18 @@ def get_invalidated_url(self, valid_url: str) -> Optional[str]:
         # TODO: add support for header / query parameters that can be invalidated
         return None
 
+    @keyword
+    def get_parameterized_endpoint_from_url(self, url: str):
+        """
+        Return the endpoint as found in the `paths` section based on the given `url`.
+        """
+        endpoint = url.replace(self.base_url, "")
+        endpoint_parts = endpoint.split("/")
+        # first part will be '' since an endpoint starts with /
+        endpoint_parts.pop(0)
+        parameterized_endpoint = self.get_parametrized_endpoint(endpoint=endpoint)
+        return parameterized_endpoint
+
     @keyword
     def get_invalid_json_data(
         self,
@@ -652,16 +705,20 @@ def ensure_in_use(self, url: str, resource_relation: IdReference) -> None:
             resource_id = endpoint_parts[-1]
         else:
             resource_id = ""
-        post_endpoint = resource_relation.post_path
-        property_name = resource_relation.property_name
-        request_data = self.get_request_data(method="POST", endpoint=post_endpoint)
+        request_data = self.get_request_data(
+            method="POST", endpoint=resource_relation.post_path
+        )
         params = request_data.params
         headers = request_data.headers
         dto = request_data.dto
         json_data = asdict(dto)
         if resource_id:
-            json_data[property_name] = resource_id
-        post_url: str = run_keyword("get_valid_url", post_endpoint, "POST")
+            json_data[resource_relation.property_name] = resource_id
+        post_url: str = run_keyword(
+            "get_valid_url",
+            resource_relation.post_path,
+            "POST",
+        )
         response: Response = run_keyword(
             "authorized_request", post_url, "POST", params, headers, json_data
         )
@@ -705,10 +762,13 @@ def get_json_data_with_conflict(
                 post_json = json_data
             endpoint = post_url.replace(self.base_url, "")
             request_data = self.get_request_data(endpoint=endpoint, method="POST")
-            params = request_data.params
-            headers = request_data.headers
             response: Response = run_keyword(
-                "authorized_request", post_url, "POST", params, headers, post_json
+                "authorized_request",
+                post_url,
+                "POST",
+                request_data.params,
+                request_data.headers,
+                post_json,
             )
             # conflicting resource may already exist
             assert (
@@ -725,8 +785,8 @@ def authorized_request(
         url: str,
         method: str,
         params: Optional[Dict[str, Any]] = None,
-        headers: Optional[Dict[str, Any]] = None,
-        json: Optional[Dict[str, Any]] = None,
+        headers: Optional[Dict[str, str]] = None,
+        json_data: Optional[Dict[str, Any]] = None,
     ) -> Response:
         """
         Perform a request using the security token or authentication set in the library.
@@ -744,7 +804,7 @@ def authorized_request(
             method=method,
             params=params,
             headers=headers,
-            json=json,
+            json=json_data,
             auth=self.auth,
             verify=False,
         )
diff --git a/src/OpenApiDriver/openapidriver.py b/src/OpenApiDriver/openapidriver.py
index be13ad2..63729f2 100644
--- a/src/OpenApiDriver/openapidriver.py
+++ b/src/OpenApiDriver/openapidriver.py
@@ -137,7 +137,6 @@
 from requests.auth import AuthBase
 from robot.api.deco import library
 from robot.libraries.BuiltIn import BuiltIn
-from robotlibcore import DynamicCore
 
 from OpenApiDriver.openapi_executors import OpenApiExecutors, ValidationLevel
 from OpenApiDriver.openapi_reader import OpenApiReader
@@ -149,7 +148,7 @@
 
 
 @library
-class OpenApiDriver(DataDriver, DynamicCore):
+class OpenApiDriver(DataDriver, OpenApiExecutors):
     # region: docstring
     """
     Visit the [https://github.com/MarketSquare/robotframework-openapidriver | library page]
@@ -171,7 +170,7 @@ class OpenApiDriver(DataDriver, DynamicCore):
     ROBOT_LIBRARY_DOC_FORMAT = "ROBOT"
     ROBOT_LIBRARY_SCOPE = "TEST SUITE"
 
-    def __init__(  # pylint: disable=too-many-arguments
+    def __init__(  # pylint: disable=too-many-arguments, too-many-locals
         self,
         source: str,
         ignored_endpoints: Optional[List[str]] = None,
@@ -293,7 +292,8 @@ def __init__(  # pylint: disable=too-many-arguments
         )
 
         mappings_path = Path(mappings_path).as_posix()
-        openapi_executors = OpenApiExecutors(
+        OpenApiExecutors.__init__(
+            self,
             openapi_specification=openapi_spec,
             origin=origin,
             base_path=base_path,
@@ -307,7 +307,6 @@ def __init__(  # pylint: disable=too-many-arguments
             require_body_for_invalid_url=require_body_for_invalid_url,
             invalid_property_default_response=invalid_property_default_response,
         )
-        DynamicCore.__init__(self, [openapi_executors])
 
     # FIXME: Hack to allow directly loading the OpenApiReader - remove when DataDriver
     # accepts an AbstractReaderClass subclass as reader_class argument


Test patch:
diff --git a/tests/server/testserver.py b/tests/server/testserver.py
index 6d65917..46c1a8f 100644
--- a/tests/server/testserver.py
+++ b/tests/server/testserver.py
@@ -256,7 +256,9 @@ def patch_employee(employee_id: str, employee: EmployeeUpdate) -> EmployeeDetail
 
 @app.get("/available_employees", status_code=200, response_model=List[EmployeeDetails])
 def get_available_employees(weekday: WeekDay = Query(...)) -> List[EmployeeDetails]:
-    return [e for e in EMPLOYEES.values() if getattr(e, "parttime_day", None) != weekday]
+    return [
+        e for e in EMPLOYEES.values() if getattr(e, "parttime_day", None) != weekday
+    ]
 
 
 def main():