Skip to content

Commit

Permalink
fixup! ✨(backend) get signing procedure progress on signature backends
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathanreveille committed Sep 18, 2024
1 parent 0bee6db commit 4579a17
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 82 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ and this project adheres to

### Added

- Signature backend can retrieve signing procedure to see its progress
- Signature backend can now retrieve the signing state of a document
- Send an email to the user when an installment debit has been
refused
- Send an email to the user when an installment is successfully
Expand Down
12 changes: 12 additions & 0 deletions src/backend/joanie/core/utils/signature.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,15 @@ def check_signature(request, settings_name):
)
if not signature_is_valid:
raise exceptions.AuthenticationFailed("Invalid authentication.")


def get_signature_progress_state(value: int) -> dict:
"""
Return a dictionary with boolean values whether the student and the organization have signed,
or nobody did.
The `value` represents the progress:
- 0: Neither the student nor the organization has signed.
- 50: The student has signed, but the organization has not.
- 100: Both the student and the organization have signed.
"""
return {"student": value >= 50, "organization": value == 100} # noqa: PLR2004
6 changes: 3 additions & 3 deletions src/backend/joanie/signature/backends/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,11 @@ def update_signatories(self, reference_id: str, all_signatories: bool):
"update_signatories() method."
)

def get_signature_procedure_progress(self, reference_id: str):
def get_signature_state(self, reference_id: str):
"""
Get the signature procedure progress on a given reference id of a contract.
Get the signature state of a contract to know who has signed yet the document.
"""
raise NotImplementedError(
"subclasses of BaseSignatureBackend must provide a "
"get_signature_procedure_progress() method."
"get_signature_state() method."
)
22 changes: 8 additions & 14 deletions src/backend/joanie/signature/backends/dummy.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,27 +148,21 @@ def update_signatories(self, reference_id: str, all_signatories: bool) -> str:

return contract.signature_backend_reference

def get_signature_procedure_progress(self, reference_id: str) -> int:
def get_signature_state(self, reference_id: str) -> int:
"""
Dummy method that returns the progress of the signature on a contract.
Returns an integer value representing the progress of the signature process:
- 0: No one has signed
- 50: One person has signed
- 100: All required signatories have signed
Dummy method that returns the state of document in signing process.
It returns whether the student and the organization have signed.
"""
if not reference_id.startswith(self.prefix_workflow):
raise ValidationError(
f"Cannot get progress of contract with reference id : {reference_id}."
)
raise ValidationError(f"The reference does not exist: {reference_id}.")
try:
contract = Contract.objects.get(signature_backend_reference=reference_id)
except Contract.DoesNotExist as exception:
raise ValidationError(
f"Contract with reference id {reference_id} does not exist."
) from exception

if contract.student_signed_on and contract.organization_signed_on:
return 100
if contract.student_signed_on:
return 50
return 0
return {
"student": bool(contract.student_signed_on),
"organization": bool(contract.organization_signed_on),
}
13 changes: 5 additions & 8 deletions src/backend/joanie/signature/backends/lex_persona.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from joanie.core import enums, models
from joanie.core.utils.contract import order_has_organization_owner
from joanie.core.utils.signature import get_signature_progress_state
from joanie.signature import exceptions
from joanie.signature.backends.base import BaseSignatureBackend

Expand Down Expand Up @@ -662,14 +663,10 @@ def update_signatories(self, reference_id: str, all_signatories: bool) -> str:

return response.json()["id"]

def get_signature_procedure_progress(self, reference_id: str) -> int:
def get_signature_state(self, reference_id: str) -> dict:
"""
Get the signature procedure progress with the reference.
This method allows us to get the information of how far the signing process has gone
since its creation. When there are 2 required signatories, the value :
- 0 will mean that nobody has signed
- 50 will mean that one person has signed
- 100 will mean that all required signatories have signed.
Get the signature state progress on a signing procedure.
It returns a dictionary whether the student and the organization have signed.
"""
timeout = settings.JOANIE_SIGNATURE_TIMEOUT
base_url = self.get_setting("BASE_URL")
Expand All @@ -695,4 +692,4 @@ def get_signature_procedure_progress(self, reference_id: str) -> int:
f" the reference does not exist {reference_id}"
)

return response.json().get("progress")
return get_signature_progress_state(response.json().get("progress"))
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Test suite for the Lex Persona Signature Backend `get_signature_procedure_progress`"""
"""Test suite for the Lex Persona Signature Backend `get_signature_state`"""

from http import HTTPStatus

Expand All @@ -21,23 +21,23 @@
JOANIE_SIGNATURE_VALIDITY_PERIOD_IN_SECONDS=60 * 60 * 24 * 15,
JOANIE_SIGNATURE_TIMEOUT=3,
)
class LexPersonaBackendGetSignatureProcedureProgress(TestCase):
class LexPersonaBackendGetSignatureState(TestCase):
"""
Test suite for `get_signature_procedure_progress`
Test suite for `get_signature_state`
"""

@responses.activate
def test_backend_lex_persona_get_signature_procedure_progress_nobody_has_signed_yet(
def test_backend_lex_persona_get_signature_state_when_nobody_has_signed_yet(
self,
):
"""
Test that the method `get_signature_procedure_progress` will return the value 0
because nobody signed the contract.
Test that the method `get_signature_state` return that nobody has signed the document.
It should return the value False for the student and the organization in the dictionnary.
"""
backend = get_signature_backend()
workflow_id = "wfl_fake_id"
api_url = f"https://lex_persona.test01.com/api/workflows/{workflow_id}/"
expected_response = {
response = {
"allowConsolidation": True,
"allowedCoManagerUsers": [],
"coManagerNotifiedEvents": [],
Expand Down Expand Up @@ -130,27 +130,28 @@ def test_backend_lex_persona_get_signature_procedure_progress_nobody_has_signed_
responses.add(
responses.GET,
api_url,
json=expected_response,
json=response,
status=HTTPStatus.OK,
)

progress = backend.get_signature_procedure_progress(reference_id=workflow_id)
signature_state = backend.get_signature_state(reference_id=workflow_id)

self.assertEqual(progress, 0)
self.assertEqual(signature_state, {"student": False, "organization": False})

@responses.activate
def test_backend_lex_persona_get_signature_procedure_progress_one_person_signed(
def test_backend_lex_persona_get_signature_state_when_one_person_has_signed(
self,
):
"""
Test that the method `get_signature_procedure_progress` will return the value 50
because 1 person has signed the contract.
Test that the method `get_signature_state` that the student has signed the document.
It should return the value True for the student and False for the organization
in the dictionary.
"""
backend = get_signature_backend()
workflow_id = "wfl_fake_id"
api_url = f"https://lex_persona.test01.com/api/workflows/{workflow_id}/"

expected_response = {
response = {
"allowConsolidation": True,
"allowedCoManagerUsers": [],
"coManagerNotifiedEvents": [],
Expand Down Expand Up @@ -255,26 +256,26 @@ def test_backend_lex_persona_get_signature_procedure_progress_one_person_signed(
responses.add(
responses.GET,
api_url,
json=expected_response,
json=response,
status=HTTPStatus.OK,
)

progress = backend.get_signature_procedure_progress(reference_id=workflow_id)
signature_state = backend.get_signature_state(reference_id=workflow_id)

self.assertEqual(progress, 50)
self.assertEqual(signature_state, {"student": True, "organization": False})

@responses.activate
def test_backend_lex_persona_get_signature_procedure_progress_all_signatories_signed(
def test_backend_lex_persona_get_signature_state_all_signatories_have_signed(
self,
):
"""
Test that the method `get_signature_procedure_progress` will return the value 100
because all signatories have signed the contract.
Test that the method `get_signature_state` that both have signed the document.
It should return the value True for the student and the organization in the dictionary.
"""
backend = get_signature_backend()
workflow_id = "wfl_fake_id"
api_url = f"https://lex_persona.test01.com/api/workflows/{workflow_id}/"
expected_response = {
response = {
"allowConsolidation": True,
"allowedCoManagerUsers": [],
"coManagerNotifiedEvents": [],
Expand Down Expand Up @@ -396,26 +397,26 @@ def test_backend_lex_persona_get_signature_procedure_progress_all_signatories_si
responses.add(
responses.GET,
api_url,
json=expected_response,
json=response,
status=HTTPStatus.OK,
)

progress = backend.get_signature_procedure_progress(reference_id=workflow_id)
signature_state = backend.get_signature_state(reference_id=workflow_id)

self.assertEqual(progress, 100)
self.assertEqual(signature_state, {"student": True, "organization": True})

@responses.activate
def test_backend_lex_persona_get_signature_procedure_progress_returns_not_found(
def test_backend_lex_persona_get_signature_state_returns_not_found(
self,
):
"""
Test that the method `get_signature_procedure_progress` should return a status code
Test that the method `get_signature_state` should return a status code
NOT_FOUND (404) because the reference does not exist at the signature provider.
"""
backend = get_signature_backend()
workflow_id = "wfl_fake_id_not_exist"
api_url = f"https://lex_persona.test01.com/api/workflows/{workflow_id}/"
expected_failing_response = {
response = {
"status": 404,
"error": "Not Found",
"message": "The specified workflow can not be found.",
Expand All @@ -426,12 +427,12 @@ def test_backend_lex_persona_get_signature_procedure_progress_returns_not_found(
responses.add(
responses.GET,
api_url,
json=expected_failing_response,
json=response,
status=HTTPStatus.NOT_FOUND,
)

with self.assertRaises(exceptions.SignatureProcedureNotFound) as context:
backend.get_signature_procedure_progress(reference_id=workflow_id)
backend.get_signature_state(reference_id=workflow_id)

self.assertEqual(
str(context.exception),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,20 +171,20 @@ def test_backend_signature_base_backend_reset_contract(self):
@override_settings(
JOANIE_SIGNATURE_BACKEND="joanie.signature.backends.base.BaseSignatureBackend",
)
def test_backend_signature_base_raise_not_implemented_error_get_signature_procedure_progress(
def test_backend_signature_base_raise_not_implemented_error_get_signature_state(
self,
):
"""
Base backend signature provider should raise NotImplementedError for the method
`get_signature_procedure_progress`.
`get_signature_state`.
"""
backend = get_signature_backend()

with self.assertRaises(NotImplementedError) as context:
backend.get_signature_procedure_progress(reference_id="123")
backend.get_signature_state(reference_id="123")

self.assertEqual(
str(context.exception),
"subclasses of BaseSignatureBackend must provide a "
"get_signature_procedure_progress() method.",
"get_signature_state() method.",
)
41 changes: 18 additions & 23 deletions src/backend/joanie/tests/signature/test_backend_signature_dummy.py
Original file line number Diff line number Diff line change
Expand Up @@ -497,12 +497,11 @@ def test_backend_dummy_update_organization_signatories_order_without_contract(se
"The reference fake_signature_reference does not exist.",
)

def test_backend_dummy_get_signature_procedure_progress(self):
def test_backend_dummy_get_signature_state(self):
"""
Dummy backend instance should return the value of how many people have signed the
document. When the value is 0 it means that nobody has signed the document, if the
value is 50 it means that one person signed, and 100 means that all required signotories
have signed.
document. It returns a dictionary with boolean value that gives us the information
if the student has signed and the organization.
"""
backend = DummySignatureBackend()
order = factories.OrderFactory(
Expand All @@ -520,65 +519,61 @@ def test_backend_dummy_get_signature_procedure_progress(self):
organization_signed_on=None,
)

progress = backend.get_signature_procedure_progress(
signature_state = backend.get_signature_state(
contract.signature_backend_reference
)

self.assertEqual(progress, 0)
self.assertEqual(signature_state, {"student": False, "organization": False})

contract.student_signed_on = django_timezone.now()
contract.save()

progress = backend.get_signature_procedure_progress(
signature_state = backend.get_signature_state(
contract.signature_backend_reference
)

self.assertEqual(progress, 50)
self.assertEqual(signature_state, {"student": True, "organization": False})

contract.organization_signed_on = django_timezone.now()
contract.submitted_for_signature_on = None
contract.save()

progress = backend.get_signature_procedure_progress(
signature_state = backend.get_signature_state(
contract.signature_backend_reference
)

self.assertEqual(progress, 100)
self.assertEqual(signature_state, {"student": True, "organization": True})

def test_backend_dummy_get_signature_procedure_progress_with_non_existing_reference_id(
def test_backend_dummy_get_signature_state_with_non_existing_reference_id(
self,
):
"""
Dummy backend instance should not update a signature procedure if the contract is
fully signed.
Dummy backend instance should not return a dictionary if the passed `reference_id`
is not attached to any contract and raise a `ValidationError`.
"""
backend = DummySignatureBackend()

with self.assertRaises(ValidationError) as context:
backend.get_signature_procedure_progress(
reference_id="wfl_fake_dummy_id_does_not_exist"
)
backend.get_signature_state(reference_id="wfl_fake_dummy_id_does_not_exist")

self.assertEqual(
str(context.exception.message),
"Contract with reference id wfl_fake_dummy_id_does_not_exist does not exist.",
)

def test_backend_dummy_get_signature_procedure_progress_with_wrong_format_reference_id(
def test_backend_dummy_get_signature_state_with_wrong_format_reference_id(
self,
):
"""
Dummy backend instance should not update a signature procedure if the contract is
fully signed.
Dummy backend instance should raise a `ValidationError` if the reference_id
has the wrong format for the Dummy Backend.
"""
backend = DummySignatureBackend()

with self.assertRaises(ValidationError) as context:
backend.get_signature_procedure_progress(
reference_id="fake_dummy_id_does_not_exist"
)
backend.get_signature_state(reference_id="fake_dummy_id_does_not_exist")

self.assertEqual(
str(context.exception.message),
"Cannot get progress of contract with reference id : fake_dummy_id_does_not_exist.",
"The reference does not exist: fake_dummy_id_does_not_exist.",
)

0 comments on commit 4579a17

Please sign in to comment.