From afba642b3a9556661b16220751283b8a2f9fc1c4 Mon Sep 17 00:00:00 2001
From: Nico Alt <nicoalt@posteo.org>
Date: Mon, 8 Mar 2021 13:00:00 +0000
Subject: [PATCH] Add function to delete pending contact

---
 .../pending_already_exists_contact.py         |  4 +-
 .../pending_already_exists_pending_contact.py |  5 +-
 briar_wrapper/models/contacts.py              | 67 +++++++++----
 tests/briar_wrapper/models/test_contacts.py   | 99 +++++++++++++++++++
 4 files changed, 153 insertions(+), 22 deletions(-)

diff --git a/briar_wrapper/exceptions/pending_already_exists_contact.py b/briar_wrapper/exceptions/pending_already_exists_contact.py
index 4c9bf32..1b29392 100644
--- a/briar_wrapper/exceptions/pending_already_exists_contact.py
+++ b/briar_wrapper/exceptions/pending_already_exists_contact.py
@@ -12,5 +12,5 @@ class PendingContactAlreadyExistsContact(BriarWrapperException):
     def __init__(self, response, message=""):
         self.response = response
         error = response.json()
-        self.remoteAuthorName = error["remoteAuthorName"]
-        super().__init__(message)
+        self.remote_author_name = error["remoteAuthorName"]
+        super().__init__(response, message)
diff --git a/briar_wrapper/exceptions/pending_already_exists_pending_contact.py b/briar_wrapper/exceptions/pending_already_exists_pending_contact.py
index a2329ce..63c23a3 100644
--- a/briar_wrapper/exceptions/pending_already_exists_pending_contact.py
+++ b/briar_wrapper/exceptions/pending_already_exists_pending_contact.py
@@ -12,5 +12,6 @@ class PendingContactAlreadyExistsPendingContact(BriarWrapperException):
     def __init__(self, response, message=""):
         self.response = response
         error = response.json()
-        self.pendingContactAlias = error["pendingContactAlias"]
-        super().__init__(message)
+        self.pending_contact_alias = error["pendingContactAlias"]
+        self.pending_contact_id = error["pendingContactId"]
+        super().__init__(response, message)
diff --git a/briar_wrapper/models/contacts.py b/briar_wrapper/models/contacts.py
index 52e2276..9c02b48 100644
--- a/briar_wrapper/models/contacts.py
+++ b/briar_wrapper/models/contacts.py
@@ -4,7 +4,7 @@
 """
 Wrapper around Briar API's _/contacts/_ resource
 """
-
+from json.decoder import JSONDecodeError
 from operator import itemgetter
 from typing import Callable, List
 from urllib.parse import urljoin
@@ -33,16 +33,21 @@ class Contacts(Model):
 
     _connections_callback = None
 
-    def add_pending(self, link: str, alias: str) -> bool:
+    def add_pending(self, link: str, alias: str) -> None:
         # pylint: disable=line-too-long
         """
         Adds pending contact to Briar with `link` URL and `alias`
 
         Raises:
-        * `briar_wrapper.exceptions.pending_already_exists_pending_contact.PendingContactAlreadyExistsContact`
+
+        * `briar_wrapper.exceptions.pending_already_exists_contact.PendingContactAlreadyExistsContact`
+
         * `briar_wrapper.exceptions.pending_already_exists_pending_contact.PendingContactAlreadyExistsPendingContact`
+
         * `briar_wrapper.exceptions.pending_invalid_link.PendingContactInvalidLinkException`
+
         * `briar_wrapper.exceptions.pending_invalid_public_key.PendingContactInvalidPublicKeyException`
+
         * `briar_wrapper.exception.BriarWrapperException` for unknown API errors
 
         [Upstream documentation](https://code.briarproject.org/briar/briar/blob/master/briar-headless/README.md#adding-a-contact)
@@ -54,24 +59,26 @@ class Contacts(Model):
         response = _post(url, headers=self._headers,
                          json={"link": link, "alias": alias})
         if response.status_code == 200:
-            return True
+            return
         self._handle_add_pending_error(response)
 
-    @staticmethod
-    def _handle_add_pending_error(response):
-        error = response.json()
-        if response.status_code == 400:
-            if error["error"] == "INVALID_PUBLIC_KEY":
-                raise PendingContactInvalidPublicKeyException(response)
-            if error["error"] == "INVALID_LINK":
-                raise PendingContactInvalidLinkException(response)
-        if response.status_code == 403:
-            if error["error"] == "CONTACT_EXISTS":
-                raise PendingContactAlreadyExistsContact(response)
-            if error["error"] == "PENDING_EXISTS":
-                raise PendingContactAlreadyExistsPendingContact(response)
+    def delete_pending(self, contact_id: str) -> None:
+        # pylint: disable=line-too-long
+        """
+        Deletes pending contact with `id`
+
+        [Upstream documentation](https://code.briarproject.org/briar/briar/blob/master/briar-headless/README.md#adding-a-contact)
+
+        .. versionadded:: 0.0.7
+        """
+        url = urljoin(BASE_HTTP_URL, self._API_ENDPOINT + "add/pending/")
+        response = _delete(url, headers=self._headers,
+                           json={"pendingContactId": contact_id})
+        if response.status_code == 200:
+            return
         raise BriarWrapperException(response, "An unknown error occurred while"
-                                    f"adding a pending contact: {response.text}")
+                                              f"deleting a pending contact: "
+                                              f"{response.text}")
 
     def set_alias(self, contact_id: int, alias: str) -> None:
         # pylint: disable=line-too-long
@@ -156,6 +163,30 @@ class Contacts(Model):
         else:
             raise Exception(f"Wrong event in callback: {message['name']}")
 
+    @staticmethod
+    def _handle_add_pending_error(response):
+        error = dict()
+        try:
+            error = response.json()
+        except JSONDecodeError as json_error:
+            raise BriarWrapperException(response,
+                                        "An unknown error occurred while"
+                                        "adding a pending contact: "
+                                        "JSONDecodeError") from json_error
+        if response.status_code == 400:
+            if error["error"] == "INVALID_PUBLIC_KEY":
+                raise PendingContactInvalidPublicKeyException(response)
+            if error["error"] == "INVALID_LINK":
+                raise PendingContactInvalidLinkException(response)
+        if response.status_code == 403:
+            if error["error"] == "CONTACT_EXISTS":
+                raise PendingContactAlreadyExistsContact(response)
+            if error["error"] == "PENDING_EXISTS":
+                raise PendingContactAlreadyExistsPendingContact(response)
+        raise BriarWrapperException(response, "An unknown error occurred while"
+                                              f"adding a pending contact: "
+                                              f"{response.text}")
+
     @staticmethod
     def _sort_contact_list(contacts: list) -> list:
         contacts.sort(key=itemgetter("lastChatActivity"),
diff --git a/tests/briar_wrapper/models/test_contacts.py b/tests/briar_wrapper/models/test_contacts.py
index 2017565..ba49324 100644
--- a/tests/briar_wrapper/models/test_contacts.py
+++ b/tests/briar_wrapper/models/test_contacts.py
@@ -6,6 +6,15 @@ import json
 
 import pytest
 import requests_mock
+from briar_wrapper.exception import BriarWrapperException
+from briar_wrapper.exceptions.pending_already_exists_contact import \
+    PendingContactAlreadyExistsContact
+from briar_wrapper.exceptions.pending_already_exists_pending_contact import \
+    PendingContactAlreadyExistsPendingContact
+from briar_wrapper.exceptions.pending_invalid_link import \
+    PendingContactInvalidLinkException
+from briar_wrapper.exceptions.pending_invalid_public_key import \
+    PendingContactInvalidPublicKeyException
 
 from briar_wrapper.models.contacts import Contacts
 from briar_wrapper.models.socket_listener import SocketListener
@@ -14,6 +23,7 @@ BASE_HTTP_URL = "http://localhost:7000/v1/contacts/"
 
 TEST_ALIAS = "Alice"
 TEST_CONTACT_ID = 42
+TEST_PENDING_CONTACT_ID = "jsTgWcsEQ2g9rnomeK1g/hmO8M1Ix6ZIGWAjgBtlS9U="
 
 TEST_CONTACT_FIRST = {
     "lastChatActivity": 1
@@ -50,6 +60,95 @@ def match_request_add_pending(request):
     return {"alias": TEST_ALIAS, "link": TEST_LINK} == request.json()
 
 
+@requests_mock.Mocker(kw="requests_mock")
+def test_add_pending_invalid_public_key(api, request_headers, requests_mock):
+    contacts = Contacts(api)
+    requests_mock.post(BASE_HTTP_URL + "add/pending/",
+                       status_code=400,
+                       json={"error": "INVALID_PUBLIC_KEY"})
+    with pytest.raises(PendingContactInvalidPublicKeyException):
+        contacts.add_pending(TEST_LINK, TEST_ALIAS)
+
+
+@requests_mock.Mocker(kw="requests_mock")
+def test_add_pending_invalid_link(api, request_headers, requests_mock):
+    contacts = Contacts(api)
+    requests_mock.post(BASE_HTTP_URL + "add/pending/",
+                       status_code=400,
+                       json={"error": "INVALID_LINK"})
+    with pytest.raises(PendingContactInvalidLinkException):
+        contacts.add_pending(TEST_LINK, TEST_ALIAS)
+
+
+@requests_mock.Mocker(kw="requests_mock")
+def test_add_pending_contact_exists(api, request_headers, requests_mock):
+    contacts = Contacts(api)
+    requests_mock.post(BASE_HTTP_URL + "add/pending/",
+                       status_code=403,
+                       json={
+                           "error": "CONTACT_EXISTS",
+                           "remoteAuthorName": TEST_ALIAS
+                       })
+    with pytest.raises(PendingContactAlreadyExistsContact):
+        contacts.add_pending(TEST_LINK, TEST_ALIAS)
+
+
+@requests_mock.Mocker(kw="requests_mock")
+def test_add_pending_pending_exists(api, request_headers, requests_mock):
+    contacts = Contacts(api)
+    requests_mock.post(BASE_HTTP_URL + "add/pending/",
+                       status_code=403,
+                       json={
+                           "error": "PENDING_EXISTS",
+                           "pendingContactAlias": TEST_ALIAS,
+                           "pendingContactId": TEST_PENDING_CONTACT_ID
+                       })
+    with pytest.raises(PendingContactAlreadyExistsPendingContact):
+        contacts.add_pending(TEST_LINK, TEST_ALIAS)
+
+
+@requests_mock.Mocker(kw="requests_mock")
+def test_add_pending_unknown_error(api, request_headers, requests_mock):
+    contacts = Contacts(api)
+    requests_mock.post(BASE_HTTP_URL + "add/pending/",
+                       status_code=500,
+                       json={"error": "CRAZY_ERROR"})
+    with pytest.raises(BriarWrapperException):
+        contacts.add_pending(TEST_LINK, TEST_ALIAS)
+
+
+@requests_mock.Mocker(kw="requests_mock")
+def test_add_pending_no_json(api, request_headers, requests_mock):
+    contacts = Contacts(api)
+    requests_mock.post(BASE_HTTP_URL + "add/pending/",
+                       status_code=500)
+    with pytest.raises(BriarWrapperException):
+        contacts.add_pending(TEST_LINK, TEST_ALIAS)
+
+
+@requests_mock.Mocker(kw="requests_mock")
+def test_delete_pending(api, request_headers, requests_mock):
+    contacts = Contacts(api)
+    requests_mock.register_uri("DELETE",
+                               BASE_HTTP_URL + "add/pending/",
+                               request_headers=request_headers,
+                               additional_matcher=match_request_delete_pending)
+    contacts.delete_pending(TEST_PENDING_CONTACT_ID)
+
+
+def match_request_delete_pending(request):
+    return {"pendingContactId": TEST_PENDING_CONTACT_ID} == request.json()
+
+
+@requests_mock.Mocker(kw="requests_mock")
+def test_delete_pending_unknown_error(api, request_headers, requests_mock):
+    contacts = Contacts(api)
+    requests_mock.delete(BASE_HTTP_URL + "add/pending/",
+                         status_code=500)
+    with pytest.raises(BriarWrapperException):
+        contacts.delete_pending(TEST_PENDING_CONTACT_ID)
+
+
 @requests_mock.Mocker(kw="requests_mock")
 def test_set_alias(api, request_headers, requests_mock):
     contacts = Contacts(api)
-- 
GitLab