Skip to content

Commit

Permalink
feat: async client implementation (#3)
Browse files Browse the repository at this point in the history
* feat: async client implementation
* docs: updated documentation
  • Loading branch information
akimrx authored Apr 2, 2024
1 parent daf20fc commit b890983
Show file tree
Hide file tree
Showing 30 changed files with 2,820 additions and 89 deletions.
58 changes: 56 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

This library is a simple client for working with **[Yandex Lockbox](https://cloud.yandex.ru/en/docs/lockbox/)** over [REST API](https://cloud.yandex.ru/en/docs/lockbox/api-ref/), simplifying work with secrets and allowing you to work with them in the OOP paradigm.

Supports two modes: synchronous and asynchronous.

**[Full library documentation link](https://akimrx.github.io/python-yc-lockbox/)**

**Supported Python versions**:
Expand All @@ -23,6 +25,11 @@ This library is a simple client for working with **[Yandex Lockbox](https://clou
* [Requests](https://github.com/psf/requests)


**Extra dependencies:**

* [aiohttp](https://github.com/aio-libs/aiohttp)


**Currently, the following operations are not supported by the library:**

* List secret access bindings
Expand All @@ -34,7 +41,7 @@ This library is a simple client for working with **[Yandex Lockbox](https://clou
**In the near future release:**

- [x] Tests
- [ ] Async client implementation
- [x] Async client implementation
- [ ] Implement access bindings methods and view operations
- [ ] Ansible action and lookup plugins

Expand Down Expand Up @@ -189,4 +196,51 @@ for secret in lockbox.list_secrets(folder_id="b1xxxxxxxxxx", iterator=True):
version.schedule_version_destruction()
version.cancel_version_destruction()

```
```

## Async mode

The client supports asynchronous mode using the aiohttp library. The signature of the methods does not differ from the synchronous implementation.


Just import async client:

```python

from yc_lockbox import AsyncYandexLockboxClient

lockbox = AsyncYandexLockboxClient("oauth_or_iam_token")
```

Alternative:

```python

from yc_lockbox import YandexLockboxFacade

lockbox = YandexLockboxFacade("oauth_or_iam_token", enable_async=True).client
```

Example usage:

```python
secret: Secret = await lockbox.get_secret("e6qxxxxxxxxxx")
payload = await secret.payload()
print(payload.entries) # list of SecretPayloadEntry objects

# Direct access

entry = payload["secret_entry_1"] # or payload.get("secret_entry_1")

print(entry.text_value) # return MASKED value like ***********
print(entry.reveal_text_value()) # similar to entry.text_value.get_secret_value()

# Async iterators

secret_versions = await secret.list_versions(iterator=True)

async for version in secret_versions:
if version.id != secret.current_version.id:
await version.schedule_version_destruction()
await version.cancel_version_destruction()
```
2 changes: 1 addition & 1 deletion docs/_modules/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ <h1>All modules for which code is available</h1>

</aside>
</div>
</div><script src="../_static/documentation_options.js?v=360bc84d"></script>
</div><script src="../_static/documentation_options.js?v=938c9ccc"></script>
<script src="../_static/doctools.js?v=888ff710"></script>
<script src="../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../_static/scripts/furo.js?v=32e29ea5"></script>
Expand Down
2 changes: 1 addition & 1 deletion docs/_modules/yc_lockbox/_auth.html
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ <h1>Source code for yc_lockbox._auth</h1><div class="highlight"><pre>

</aside>
</div>
</div><script src="../../_static/documentation_options.js?v=360bc84d"></script>
</div><script src="../../_static/documentation_options.js?v=938c9ccc"></script>
<script src="../../_static/doctools.js?v=888ff710"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../_static/scripts/furo.js?v=32e29ea5"></script>
Expand Down
585 changes: 573 additions & 12 deletions docs/_modules/yc_lockbox/_lockbox.html

Large diffs are not rendered by default.

32 changes: 23 additions & 9 deletions docs/_modules/yc_lockbox/_models.html
Original file line number Diff line number Diff line change
Expand Up @@ -197,13 +197,13 @@
<article role="main">
<h1>Source code for yc_lockbox._models</h1><div class="highlight"><pre>
<span></span><span class="kn">import</span> <span class="nn">logging</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Any</span><span class="p">,</span> <span class="n">Iterator</span><span class="p">,</span> <span class="n">Union</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Any</span><span class="p">,</span> <span class="n">AsyncGenerator</span><span class="p">,</span> <span class="n">Iterator</span><span class="p">,</span> <span class="n">Union</span>
<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
<span class="kn">from</span> <span class="nn">pydantic</span> <span class="kn">import</span> <span class="n">BaseModel</span><span class="p">,</span> <span class="n">ConfigDict</span><span class="p">,</span> <span class="n">Field</span><span class="p">,</span> <span class="n">SecretStr</span><span class="p">,</span> <span class="n">SecretBytes</span><span class="p">,</span> <span class="n">computed_field</span>

<span class="kn">from</span> <span class="nn">yc_lockbox._constants</span> <span class="kn">import</span> <span class="n">RpcError</span>
<span class="kn">from</span> <span class="nn">yc_lockbox._abc</span> <span class="kn">import</span> <span class="n">AbstractYandexLockboxClient</span>
<span class="kn">from</span> <span class="nn">yc_lockbox._types</span> <span class="kn">import</span> <span class="n">T</span>
<span class="kn">from</span> <span class="nn">yc_lockbox._types</span> <span class="kn">import</span> <span class="n">T</span><span class="p">,</span> <span class="n">SecretVersionsResponse</span>
<span class="kn">from</span> <span class="nn">yc_lockbox._exceptions</span> <span class="kn">import</span> <span class="n">LockboxError</span>


Expand Down Expand Up @@ -500,18 +500,32 @@ <h1>Source code for yc_lockbox._models</h1><div class="highlight"><pre>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">client</span><span class="o">.</span><span class="n">delete_secret</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span></div>


<span class="k">async</span> <span class="k">def</span> <span class="nf">_async_refresh</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="s2">&quot;Secret&quot;</span><span class="p">:</span>
<span class="n">data</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">client</span><span class="o">.</span><span class="n">get_secret</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_update_attributes</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span>

<span class="k">def</span> <span class="nf">_sync_refresh</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="s2">&quot;Secret&quot;</span><span class="p">:</span>
<span class="n">data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">client</span><span class="o">.</span><span class="n">get_secret</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_update_attributes</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span>

<span class="k">def</span> <span class="nf">_update_attributes</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kc">None</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Method for update model attributes after refresh.&quot;&quot;&quot;</span>
<span class="k">for</span> <span class="n">attr</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">data</span><span class="o">.</span><span class="n">model_dump</span><span class="p">()</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">if</span> <span class="n">value</span> <span class="o">!=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attr</span><span class="p">,</span> <span class="kc">None</span><span class="p">):</span>
<span class="nb">setattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attr</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>

<div class="viewcode-block" id="Secret.refresh">
<a class="viewcode-back" href="../../pages/models.html#yc_lockbox._models.Secret.refresh">[docs]</a>
<span class="k">def</span> <span class="nf">refresh</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="s2">&quot;Secret&quot;</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Shortcut for refresh attributes for this secret.&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_raise_when_empty_client</span><span class="p">()</span>
<span class="n">data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">client</span><span class="o">.</span><span class="n">get_secret</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">id</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>

<span class="k">for</span> <span class="n">attr</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">data</span><span class="o">.</span><span class="n">model_dump</span><span class="p">()</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">if</span> <span class="n">value</span> <span class="o">!=</span> <span class="nb">getattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attr</span><span class="p">):</span>
<span class="nb">setattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">attr</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">client</span><span class="p">,</span> <span class="s2">&quot;enable_async&quot;</span><span class="p">)</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">client</span><span class="o">.</span><span class="n">enable_async</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_async_refresh</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>

<span class="k">return</span> <span class="n">data</span></div>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_sync_refresh</span><span class="p">(</span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span></div>


<div class="viewcode-block" id="Secret.payload">
Expand All @@ -525,7 +539,7 @@ <h1>Source code for yc_lockbox._models</h1><div class="highlight"><pre>
<a class="viewcode-back" href="../../pages/models.html#yc_lockbox._models.Secret.list_versions">[docs]</a>
<span class="k">def</span> <span class="nf">list_versions</span><span class="p">(</span>
<span class="bp">self</span><span class="p">,</span> <span class="n">page_size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">100</span><span class="p">,</span> <span class="n">page_token</span><span class="p">:</span> <span class="nb">str</span> <span class="o">|</span> <span class="kc">None</span> <span class="o">=</span> <span class="kc">None</span><span class="p">,</span> <span class="n">iterator</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="kc">False</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Union</span><span class="p">[</span><span class="s2">&quot;SecretVersionsList&quot;</span><span class="p">,</span> <span class="n">Iterator</span><span class="p">[</span><span class="s2">&quot;SecretVersion&quot;</span><span class="p">],</span> <span class="s2">&quot;YandexCloudError&quot;</span><span class="p">]:</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="n">SecretVersionsResponse</span><span class="p">:</span>
<span class="w"> </span><span class="sd">&quot;&quot;&quot;Shortcut for list all available versions of the current secret.&quot;&quot;&quot;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_raise_when_empty_client</span><span class="p">()</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">client</span><span class="o">.</span><span class="n">list_secret_versions</span><span class="p">(</span>
Expand Down Expand Up @@ -664,7 +678,7 @@ <h1>Source code for yc_lockbox._models</h1><div class="highlight"><pre>

</aside>
</div>
</div><script src="../../_static/documentation_options.js?v=360bc84d"></script>
</div><script src="../../_static/documentation_options.js?v=938c9ccc"></script>
<script src="../../_static/doctools.js?v=888ff710"></script>
<script src="../../_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="../../_static/scripts/furo.js?v=32e29ea5"></script>
Expand Down
50 changes: 50 additions & 0 deletions docs/_sources/index.rst.txt
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,56 @@ Other operations with secret
Async mode
----------

The client supports asynchronous mode using the aiohttp library. The signature of the methods does not differ from the synchronous implementation.


Just import async client:

.. code-block:: python
from yc_lockbox import AsyncYandexLockboxClient
lockbox = AsyncYandexLockboxClient("oauth_or_iam_token")
Alternative:

.. code-block:: python
from yc_lockbox import YandexLockboxFacade
lockbox = YandexLockboxFacade("oauth_or_iam_token", enable_async=True).client
Example usage:

.. code-block:: python
secret: Secret = await lockbox.get_secret("e6qxxxxxxxxxx")
payload = await secret.payload()
print(payload.entries) # list of SecretPayloadEntry objects
# Direct access
entry = payload["secret_entry_1"] # or payload.get("secret_entry_1")
print(entry.text_value) # return MASKED value like ***********
print(entry.reveal_text_value()) # similar to entry.text_value.get_secret_value()
# Async iterators
secret_versions = await secret.list_versions(iterator=True)
async for version in secret_versions:
if version.id != secret.current_version.id:
await version.schedule_version_destruction()
await version.cancel_version_destruction()
Modules
-------
Expand Down
12 changes: 11 additions & 1 deletion docs/_sources/pages/clients.rst.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
Client
======

.. autoclass:: yc_lockbox.YandexLockboxFacade
:members:
:undoc-members:
:show-inheritance:

.. autoclass:: yc_lockbox.YandexLockboxClient
:members:
:undoc-members:
:show-inheritance:

.. autoclass:: yc_lockbox.AsyncYandexLockboxClient
:members:
:undoc-members:
:show-inheritance:

.. autoclass:: yc_lockbox._auth.YandexAuthClient
:members:
:undoc-members:
:show-inheritance:
:show-inheritance:
2 changes: 1 addition & 1 deletion docs/_static/documentation_options.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const DOCUMENTATION_OPTIONS = {
VERSION: '0.1.3',
VERSION: '0.2.0',
LANGUAGE: 'en',
COLLAPSE_INDEX: false,
BUILDER: 'html',
Expand Down
Loading

0 comments on commit b890983

Please sign in to comment.