Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: asyncio microgen transaction #123

Merged
merged 21 commits into from Jul 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
560fcf4
refactor: move generated client instantiation out of base class
rafilong Jul 17, 2020
908b85a
feat: integrate microgen async client to client
rafilong Jul 17, 2020
16841d2
feat: make collections call backed by async
rafilong Jul 20, 2020
e75b65b
fix: failing asyncmock assertion
rafilong Jul 21, 2020
c3f450a
refactor: remove unused install
rafilong Jul 21, 2020
0acde8e
fix: lint
rafilong Jul 21, 2020
2fecef7
refactor: shared functionality in client to base class
rafilong Jul 21, 2020
65b753f
refactor: move AsyncMock to test helpers
rafilong Jul 21, 2020
7ca2c67
fix: return type in client docs
rafilong Jul 21, 2020
8c231a5
feat: integrate microgen async client to collection
rafilong Jul 21, 2020
267844f
fix: lint
rafilong Jul 21, 2020
d8e5c30
feat: integrate microgen async client to document
rafilong Jul 21, 2020
cdb8d2b
feat: integrate microgen async client to batch
rafilong Jul 17, 2020
237a609
fix: use AsyncMock for batch async tests:
rafilong Jul 21, 2020
da6d7f8
fix: collection and document testing batch
rafilong Jul 21, 2020
8d99b21
feat: integrate microgen async client to transaction
rafilong Jul 21, 2020
c7793d4
Merge branch 'v2-staging' into asyncio-microgen-transaction
rafilong Jul 22, 2020
670e20a
fix: remove unused imports
rafilong Jul 22, 2020
429bc8d
Merge branch 'asyncio-microgen-transaction' of github.com:rafilong/py…
rafilong Jul 22, 2020
e1d00fe
merge: upstream/v2-staging
rafilong Jul 22, 2020
8fb907e
Merge remote-tracking branch 'upstream/v2-staging' into asyncio-micro…
rafilong Jul 23, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 7 additions & 7 deletions google/cloud/firestore_v1/async_transaction.py
Expand Up @@ -85,7 +85,7 @@ async def _begin(self, retry_id=None):
msg = _CANT_BEGIN.format(self._id)
raise ValueError(msg)

transaction_response = self._client._firestore_api.begin_transaction(
transaction_response = await self._client._firestore_api.begin_transaction(
request={
"database": self._client._database_string,
"options": self._options_protobuf(retry_id),
Expand All @@ -105,7 +105,7 @@ async def _rollback(self):

try:
# NOTE: The response is just ``google.protobuf.Empty``.
self._client._firestore_api.rollback(
await self._client._firestore_api.rollback(
request={
"database": self._client._database_string,
"transaction": self._id,
Expand Down Expand Up @@ -148,7 +148,7 @@ async def get_all(self, references):
.DocumentSnapshot: The next document snapshot that fulfills the
query, or :data:`None` if the document does not exist.
"""
return self._client.get_all(references, transaction=self)
return await self._client.get_all(references, transaction=self)
rafilong marked this conversation as resolved.
Show resolved Hide resolved

async def get(self, ref_or_query):
"""
Expand All @@ -160,9 +160,9 @@ async def get(self, ref_or_query):
query, or :data:`None` if the document does not exist.
"""
if isinstance(ref_or_query, AsyncDocumentReference):
return self._client.get_all([ref_or_query], transaction=self)
return await self._client.get_all([ref_or_query], transaction=self)
elif isinstance(ref_or_query, AsyncQuery):
return ref_or_query.stream(transaction=self)
return await ref_or_query.stream(transaction=self)
else:
raise ValueError(
'Value for argument "ref_or_query" must be a AsyncDocumentReference or a AsyncQuery.'
Expand Down Expand Up @@ -192,7 +192,7 @@ async def _pre_commit(self, transaction, *args, **kwargs):

Args:
transaction
(:class:`~google.cloud.firestore_v1.transaction.Transaction`):
(:class:`~google.cloud.firestore_v1.async_transaction.AsyncTransaction`):
A transaction to execute the callable within.
args (Tuple[Any, ...]): The extra positional arguments to pass
along to the wrapped callable.
Expand Down Expand Up @@ -330,7 +330,7 @@ async def _commit_with_retry(client, write_pbs, transaction_id):
current_sleep = _INITIAL_SLEEP
while True:
try:
return client._firestore_api.commit(
return await client._firestore_api.commit(
request={
"database": client._database_string,
"writes": write_pbs,
Expand Down
83 changes: 20 additions & 63 deletions tests/unit/v1/test_async_transaction.py
Expand Up @@ -14,7 +14,9 @@

import pytest
import aiounittest

import mock
from tests.unit.v1.test__helpers import AsyncMock


class TestAsyncTransaction(aiounittest.AsyncTestCase):
Expand Down Expand Up @@ -80,15 +82,10 @@ def test__clean_up(self):

@pytest.mark.asyncio
async def test__begin(self):
from google.cloud.firestore_v1.services.firestore import (
client as firestore_client,
)
from google.cloud.firestore_v1.types import firestore

# Create a minimal fake GAPIC with a dummy result.
firestore_api = mock.create_autospec(
firestore_client.FirestoreClient, instance=True
tritone marked this conversation as resolved.
Show resolved Hide resolved
)
firestore_api = AsyncMock()
txn_id = b"to-begin"
response = firestore.BeginTransactionResponse(transaction=txn_id)
firestore_api.begin_transaction.return_value = response
Expand Down Expand Up @@ -128,14 +125,9 @@ async def test__begin_failure(self):
@pytest.mark.asyncio
async def test__rollback(self):
from google.protobuf import empty_pb2
from google.cloud.firestore_v1.services.firestore import (
client as firestore_client,
)

# Create a minimal fake GAPIC with a dummy result.
firestore_api = mock.create_autospec(
firestore_client.FirestoreClient, instance=True
)
firestore_api = AsyncMock()
firestore_api.rollback.return_value = empty_pb2.Empty()

# Attach the fake GAPIC to a real client.
Expand Down Expand Up @@ -172,14 +164,9 @@ async def test__rollback_not_allowed(self):
@pytest.mark.asyncio
async def test__rollback_failure(self):
from google.api_core import exceptions
from google.cloud.firestore_v1.services.firestore import (
client as firestore_client,
)

# Create a minimal fake GAPIC with a dummy failure.
firestore_api = mock.create_autospec(
firestore_client.FirestoreClient, instance=True
)
firestore_api = AsyncMock()
exc = exceptions.InternalServerError("Fire during rollback.")
firestore_api.rollback.side_effect = exc

Expand Down Expand Up @@ -207,16 +194,11 @@ async def test__rollback_failure(self):

@pytest.mark.asyncio
async def test__commit(self):
from google.cloud.firestore_v1.services.firestore import (
client as firestore_client,
)
from google.cloud.firestore_v1.types import firestore
from google.cloud.firestore_v1.types import write

# Create a minimal fake GAPIC with a dummy result.
firestore_api = mock.create_autospec(
firestore_client.FirestoreClient, instance=True
)
firestore_api = AsyncMock()
commit_response = firestore.CommitResponse(write_results=[write.WriteResult()])
firestore_api.commit.return_value = commit_response

Expand Down Expand Up @@ -262,14 +244,9 @@ async def test__commit_not_allowed(self):
@pytest.mark.asyncio
async def test__commit_failure(self):
from google.api_core import exceptions
from google.cloud.firestore_v1.services.firestore import (
client as firestore_client,
)

# Create a minimal fake GAPIC with a dummy failure.
firestore_api = mock.create_autospec(
firestore_client.FirestoreClient, instance=True
)
firestore_api = AsyncMock()
exc = exceptions.InternalServerError("Fire during commit.")
firestore_api.commit.side_effect = exc

Expand Down Expand Up @@ -304,7 +281,7 @@ async def test__commit_failure(self):

@pytest.mark.asyncio
async def test_get_all(self):
client = mock.Mock(spec=["get_all"])
client = AsyncMock(spec=["get_all"])
transaction = self._make_one(client)
ref1, ref2 = mock.Mock(), mock.Mock()
result = await transaction.get_all([ref1, ref2])
Expand All @@ -315,7 +292,7 @@ async def test_get_all(self):
async def test_get_document_ref(self):
from google.cloud.firestore_v1.async_document import AsyncDocumentReference

client = mock.Mock(spec=["get_all"])
client = AsyncMock(spec=["get_all"])
transaction = self._make_one(client)
ref = AsyncDocumentReference("documents", "doc-id")
result = await transaction.get(ref)
Expand All @@ -326,10 +303,10 @@ async def test_get_document_ref(self):
async def test_get_w_query(self):
from google.cloud.firestore_v1.async_query import AsyncQuery

client = mock.Mock(spec=[])
client = AsyncMock(spec=[])
transaction = self._make_one(client)
query = AsyncQuery(parent=mock.Mock(spec=[]))
query.stream = mock.MagicMock()
query = AsyncQuery(parent=AsyncMock(spec=[]))
query.stream = AsyncMock()
result = await transaction.get(query)
query.stream.assert_called_once_with(transaction=transaction)
self.assertIs(result, query.stream.return_value)
Expand Down Expand Up @@ -804,14 +781,9 @@ async def _call_fut(client, write_pbs, transaction_id):
@mock.patch("google.cloud.firestore_v1.async_transaction._sleep")
@pytest.mark.asyncio
async def test_success_first_attempt(self, _sleep):
from google.cloud.firestore_v1.services.firestore import (
client as firestore_client,
)

# Create a minimal fake GAPIC with a dummy result.
firestore_api = mock.create_autospec(
firestore_client.FirestoreClient, instance=True
)
firestore_api = AsyncMock()

# Attach the fake GAPIC to a real client.
client = _make_client("summer")
Expand Down Expand Up @@ -839,14 +811,10 @@ async def test_success_first_attempt(self, _sleep):
@pytest.mark.asyncio
async def test_success_third_attempt(self, _sleep):
from google.api_core import exceptions
from google.cloud.firestore_v1.services.firestore import (
client as firestore_client,
)

# Create a minimal fake GAPIC with a dummy result.
firestore_api = mock.create_autospec(
firestore_client.FirestoreClient, instance=True
)
firestore_api = AsyncMock()

# Make sure the first two requests fail and the third succeeds.
firestore_api.commit.side_effect = [
exceptions.ServiceUnavailable("Server sleepy."),
Expand Down Expand Up @@ -885,14 +853,10 @@ async def test_success_third_attempt(self, _sleep):
@pytest.mark.asyncio
async def test_failure_first_attempt(self, _sleep):
from google.api_core import exceptions
from google.cloud.firestore_v1.services.firestore import (
client as firestore_client,
)

# Create a minimal fake GAPIC with a dummy result.
firestore_api = mock.create_autospec(
firestore_client.FirestoreClient, instance=True
)
firestore_api = AsyncMock()

# Make sure the first request fails with an un-retryable error.
exc = exceptions.ResourceExhausted("We ran out of fries.")
firestore_api.commit.side_effect = exc
Expand Down Expand Up @@ -923,14 +887,10 @@ async def test_failure_first_attempt(self, _sleep):
@pytest.mark.asyncio
async def test_failure_second_attempt(self, _sleep):
from google.api_core import exceptions
from google.cloud.firestore_v1.services.firestore import (
client as firestore_client,
)

# Create a minimal fake GAPIC with a dummy result.
firestore_api = mock.create_autospec(
firestore_client.FirestoreClient, instance=True
)
firestore_api = AsyncMock()

# Make sure the first request fails retry-able and second
# fails non-retryable.
exc1 = exceptions.ServiceUnavailable("Come back next time.")
Expand Down Expand Up @@ -1031,15 +991,12 @@ def _make_client(project="feral-tom-cat"):

def _make_transaction(txn_id, **txn_kwargs):
from google.protobuf import empty_pb2
from google.cloud.firestore_v1.services.firestore import client as firestore_client
from google.cloud.firestore_v1.types import firestore
from google.cloud.firestore_v1.types import write
from google.cloud.firestore_v1.async_transaction import AsyncTransaction

# Create a fake GAPIC ...
firestore_api = mock.create_autospec(
firestore_client.FirestoreClient, instance=True
)
firestore_api = AsyncMock()
# ... with a dummy ``BeginTransactionResponse`` result ...
begin_response = firestore.BeginTransactionResponse(transaction=txn_id)
firestore_api.begin_transaction.return_value = begin_response
Expand Down