Skip to content

Commit

Permalink
Add endpoint to check number of users per client (#273)
Browse files Browse the repository at this point in the history
* Store users by domain

* Make naming of domain parameter consistent

* Add endpoint to check number of users per client
  • Loading branch information
c-w authored Dec 27, 2019
1 parent 5a008c8 commit 0137791
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 18 deletions.
8 changes: 8 additions & 0 deletions docker/integtest/5-assert-on-results.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,11 @@ if [[ "${num_exceptions}" -ne "${num_exceptions_expected}" ]]; then
echo "Got ${num_exceptions} exceptions but expected ${num_exceptions_expected}" >&2
exit 2
fi

num_users="$(curl -fs 'http://nginx:8888/api/email/metrics/users/developer1.lokole.ca' -u "${REGISTRATION_CREDENTIALS}" | jq -r '.users')"
num_users_expected=1

if [[ "${num_users}" -ne "${num_users_expected}" ]]; then
echo "Got ${num_users} users but expected ${num_users_expected}" >&2
exit 3
fi
28 changes: 22 additions & 6 deletions opwen_email_server/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,10 @@ def _store_users(self, resource_id):
num_stored = 0
for user in users:
email = user['email']
self._user_storage.store_object(email, user)
domain = get_domain(email)
self._user_storage.store_object(f'{domain}/{email}', user)

num_stored += 1
domain = email.split('@')[1]

self.log_event(events.USER_STORED_FROM_CLIENT, {'domain': domain, 'num_users': num_stored}) # noqa: E501 # yapf: disable

Expand Down Expand Up @@ -429,19 +429,35 @@ def _action(self, domain, **auth_args): # type: ignore
return 'OK', 200


class CalculateNumberOfUsersMetric(_Action):
def __init__(self, auth: AzureAuth, user_storage: AzureObjectStorage):
self._auth = auth
self._user_storage = user_storage

def _action(self, domain, **auth_args): # type: ignore
if not self._auth.is_owner(domain, auth_args.get('user')):
return 'client does not belong to the user', 403

users = sum(1 for _ in self._user_storage.iter(f'{domain}/'))

return {
'users': users,
}


class CalculatePendingEmailsMetric(_Action):
def __init__(self, auth: AzureAuth, pending_factory: Callable[[str], AzureTextStorage]):

self._auth = auth
self._pending_factory = pending_factory

def _action(self, client_domain, **auth_args): # type: ignore
client_id = self._auth.client_id_for(client_domain)
def _action(self, domain, **auth_args): # type: ignore
client_id = self._auth.client_id_for(domain)
if not client_id:
self.log_event(events.UNKNOWN_CLIENT_DOMAIN, {'client_domain': client_domain}) # noqa: E501 # yapf: disable
self.log_event(events.UNKNOWN_CLIENT_DOMAIN, {'domain': domain}) # noqa: E501 # yapf: disable
return 'unknown client domain', 404

pending_storage = self._pending_factory(client_domain)
pending_storage = self._pending_factory(domain)
pending_emails = sum(1 for _ in pending_storage.iter())

return {
Expand Down
7 changes: 7 additions & 0 deletions opwen_email_server/integration/connexion.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from opwen_email_server import config
from opwen_email_server.actions import CalculateNumberOfUsersMetric
from opwen_email_server.actions import CalculatePendingEmailsMetric
from opwen_email_server.actions import CreateClient
from opwen_email_server.actions import DeleteClient
Expand All @@ -13,6 +14,7 @@
from opwen_email_server.integration.azure import get_email_storage
from opwen_email_server.integration.azure import get_pending_storage
from opwen_email_server.integration.azure import get_raw_email_storage
from opwen_email_server.integration.azure import get_user_storage
from opwen_email_server.integration.celery import inbound_store
from opwen_email_server.integration.celery import register_client
from opwen_email_server.integration.celery import written_store
Expand Down Expand Up @@ -62,6 +64,11 @@
),
)

metrics_users = CalculateNumberOfUsersMetric(
auth=get_auth(),
user_storage=get_user_storage(),
)

metrics_pending = CalculatePendingEmailsMetric(
auth=get_auth(),
pending_factory=get_pending_storage,
Expand Down
36 changes: 32 additions & 4 deletions opwen_email_server/swagger/client-metrics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ basePath: '/api/email/metrics'

paths:

'/pending/{client_domain}':
'/pending/{domain}':

get:
operationId: opwen_email_server.integration.connexion.metrics_pending
summary: Check how many emails are pending for the client.
produces:
- application/json
parameters:
- $ref: '#/parameters/ClientDomain'
- $ref: '#/parameters/Domain'
responses:
200:
description: The number of pending emails for the client.
Expand All @@ -27,15 +27,34 @@ paths:
security:
- basic: []

'/users/{domain}':

get:
operationId: opwen_email_server.integration.connexion.metrics_users
summary: Check how many users are registered for the client.
produces:
- application/json
parameters:
- $ref: '#/parameters/Domain'
responses:
200:
description: The number of users registered for the client.
schema:
$ref: '#/definitions/UsersMetric'
403:
description: The client does not belong to the user.
security:
- basic: []

securityDefinitions:
basic:
type: basic
x-basicInfoFunc: opwen_email_server.integration.connexion.basic_auth

parameters:

ClientDomain:
name: client_domain
Domain:
name: domain
description: Domain of the Lokole client.
in: path
type: string
Expand All @@ -51,3 +70,12 @@ definitions:
type: integer
required:
- pending_emails

UsersMetric:
type: object
properties:
users:
description: The number of users.
type: integer
required:
- users
58 changes: 50 additions & 8 deletions tests/opwen_email_server/test_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ def _test_200(self, attachment_content_bytes, attachment_content_base64):
self.email_storage.store_object.assert_called_once_with(email_id, server_email)
self.next_task.assert_called_once_with(email_id)
self.client_storage.fetch_objects.assert_any_call(resource_id, (sync.USERS_FILE, from_jsonl_bytes))
self.user_storage.store_object.assert_called_once_with(user_email, user)
self.user_storage.store_object.assert_called_once_with(f'developer1.lokole.ca/{user_email}', user)
self.client_storage.delete.assert_called_once_with(resource_id)

def _execute_action(self, *args, **kwargs):
Expand Down Expand Up @@ -701,25 +701,67 @@ def _execute_action(self, *args, **kwargs):
return action(*args, **kwargs)


class CalculateNumberOfUsersMetricTests(TestCase):
def setUp(self):
self.auth = Mock()
self.user_storage = Mock()

def test_403(self):
domain = 'test.com'
user = 'user'

self.auth.is_owner.return_value = False

_, status = self._execute_action(domain, user=user)

self.assertEqual(status, 403)

def test_200(self):
domain = 'test.com'
user = 'user'
users = [
'test.com/user1',
'test.com/user2',
'test.com/user3',
]

self.auth.is_owner.return_value = True
self.user_storage.iter.return_value = users

response = self._execute_action(domain, user=user)

self.assertEqual(response['users'], len(users))
self.auth.is_owner.assert_called_once_with(domain, user)
self.user_storage.iter.assert_called_once_with(f'{domain}/')

def _execute_action(self, *args, **kwargs):
action = actions.CalculateNumberOfUsersMetric(
auth=self.auth,
user_storage=self.user_storage,
)

return action(*args, **kwargs)


class CalculatePendingEmailsMetricTests(TestCase):
def setUp(self):
self.auth = Mock()
self.pending_storage = Mock()
self.pending_factory = MagicMock()

def test_404(self):
client_domain = 'does.not.exist'
domain = 'does.not.exist'
client_id = None

self.auth.client_id_for.return_value = client_id

_, status = self._execute_action(client_domain)
_, status = self._execute_action(domain)

self.assertEqual(status, 404)
self.auth.client_id_for.assert_called_once_with(client_domain)
self.auth.client_id_for.assert_called_once_with(domain)

def test_200(self):
client_domain = 'test.com'
domain = 'test.com'
client_id = 'e8e5caa4-4ee6-4e7f-99c9-e231b6a27a9f'
pending_email_ids = [
'1de2ceb6-4f82-4cad-86ac-815bcbcb801c',
Expand All @@ -731,11 +773,11 @@ def test_200(self):
self.pending_factory.return_value = self.pending_storage
self.pending_storage.iter.return_value = pending_email_ids

response = self._execute_action(client_domain)
response = self._execute_action(domain)

self.assertEqual(response['pending_emails'], len(pending_email_ids))
self.auth.client_id_for.assert_called_once_with(client_domain)
self.pending_factory.assert_called_once_with(client_domain)
self.auth.client_id_for.assert_called_once_with(domain)
self.pending_factory.assert_called_once_with(domain)
self.pending_storage.iter.assert_called_once_with()

def _execute_action(self, *args, **kwargs):
Expand Down

0 comments on commit 0137791

Please sign in to comment.