Skip to content
Snippets Groups Projects
Commit d987abc4 authored by Andreas Ellewsen's avatar Andreas Ellewsen
Browse files

Refactor models and methods for batches

Instead of mixing the Batch model for batches sent *to* with batches
received *from* SETRA, we switch to using an InputBatch and an
OutputBatch. The InputBatch contains the fields that can be used when
sending a Batch (with Vouchers and Transactions), and the OutputBatch
contains all the information one receives from SETRA when fetching a
single batch from the batch/ endpoint, or multiple using the search
functionality.

The get_batch method is reduced to only fetch a single batch, while the
new search_batches method takes the role of searching based on creation
date and interface. In addition, the return_list_of_obj parameter is
removed in favor of the return_objects class attribute.

This commit expects that SETRA includes the id field in Batches.
parent 36aa752f
No related branches found
No related tags found
1 merge request!17Refactor models and methods for batches
Pipeline #79675 passed
...@@ -3,13 +3,17 @@ import logging ...@@ -3,13 +3,17 @@ import logging
import urllib.parse import urllib.parse
from datetime import date from datetime import date
from typing import Union, Optional, List from typing import Union, Optional, List
import requests import requests
from setra_client.models import (Batch, from setra_client.models import (
CompleteBatch, BatchErrors,
Parameter, BatchProgressEnum,
BatchErrors, CompleteBatch,
BatchProgressEnum) InputBatch,
OutputBatch,
Parameter,
)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -203,36 +207,52 @@ class SetraClient(object): ...@@ -203,36 +207,52 @@ class SetraClient(object):
return [cls.from_dict(i) for i in data] return [cls.from_dict(i) for i in data]
raise TypeError(f"Expected list or dict, got {type(data)}") raise TypeError(f"Expected list or dict, got {type(data)}")
def get_batch(self, def search_batches(
batch_id: Optional[int] = None, self,
min_created_date: Optional[date] = None, min_created_date: Optional[date] = None,
max_created_date: Optional[date] = None, max_created_date: Optional[date] = None,
batch_progress: Optional[BatchProgressEnum] = None, batch_progress: Optional[BatchProgressEnum] = None,
interface: Optional[str] = None, interface: Optional[str] = None,
return_list_of_obj: Optional[bool] = False): ) -> Union[List[dict], dict, List[OutputBatch]]:
""" """
GETs one or all batches from SETRA. Search batches in SETRA.
Dates (maximal and minimal creation dates) should Dates (maximal and minimal creation dates) should
be defined like ISO strings - like "2020-01-01". be defined like ISO strings - like "2020-01-01".
Batch Progress and interface - like strings exactly Batch Progress and interface - like strings exactly
as the values that can be found in the corresponding as the values that can be found in the corresponding
fields of Setra. fields of Setra.
""" """
params = None
if batch_id is not None:
batch_id = str(batch_id)
else:
params = {'min_created_date': min_created_date,
'max_created_date': max_created_date,
'batch_progress': batch_progress,
'interface': interface}
url = self.urls.batch(batch_id) params = {
'min_created_date': min_created_date,
'max_created_date': max_created_date,
'batch_progress': batch_progress,
'interface': interface,
}
url = self.urls.batch()
response = self.get(url, params=params) response = self.get(url, params=params)
data = response.json() data = response.json()
if return_list_of_obj: response.raise_for_status()
return [Batch(**item) for item in data] if self.return_objects:
if isinstance(data, list):
return [OutputBatch(**item) for item in data]
elif isinstance(data, dict):
return [OutputBatch(**data)]
else:
return data
def get_batch(self, batch_id: str) -> Union[OutputBatch, dict]:
"""
GETs a batch from SETRA.
"""
url = self.urls.batch(str(batch_id))
response = self.get(url)
data = response.json()
response.raise_for_status()
if self.return_objects:
return OutputBatch(**data)
else: else:
return data return data
...@@ -258,7 +278,7 @@ class SetraClient(object): ...@@ -258,7 +278,7 @@ class SetraClient(object):
response = self.get(url) response = self.get(url)
return response.json() return response.json()
def post_new_batch(self, batchdata: Batch): def post_new_batch(self, batchdata: InputBatch):
""" """
POST combination of batch, vouchers and transactions POST combination of batch, vouchers and transactions
""" """
...@@ -274,7 +294,7 @@ class SetraClient(object): ...@@ -274,7 +294,7 @@ class SetraClient(object):
response.raise_for_status() response.raise_for_status()
return response return response
def put_update_batch(self, batchdata: Batch): def put_update_batch(self, batchdata: InputBatch):
""" """
PUT updates an existing batch with vouchers and transactions, PUT updates an existing batch with vouchers and transactions,
if the batch exists in setra, and has status=created, or validation failed. if the batch exists in setra, and has status=created, or validation failed.
......
...@@ -58,7 +58,7 @@ class Transaction(BaseModel): ...@@ -58,7 +58,7 @@ class Transaction(BaseModel):
dim5: Optional[str] dim5: Optional[str]
dim6: Optional[str] dim6: Optional[str]
dim7: Optional[str] dim7: Optional[str]
sequenceno: Optional[int] sequenceno: int
taxcode: Optional[str] taxcode: Optional[str]
transtype: Optional[str] transtype: Optional[str]
extinvref: Optional[str] extinvref: Optional[str]
...@@ -71,18 +71,45 @@ class Voucher(BaseModel): ...@@ -71,18 +71,45 @@ class Voucher(BaseModel):
transactions: List[Transaction] transactions: List[Transaction]
class Batch(BaseModel): class InputBatch(BaseModel):
""" """
Model representing a batch, with a list of vouchers (and each Model representing a batch, with a list of vouchers (and each
voucher has a list of transactions) voucher has a list of transactions.)
""" """
client: str client: str
batchid: str batchid: str
period: Optional[str] period: int
interface: str
vouchertype: str
vouchers: List[Voucher]
class OutputBatch(BaseModel):
"""
Model representing a batch, with a list of voucher ids connected to that batch.
"""
id: int
created: str
batchid: str
period: Optional[int]
interface: str interface: str
client: str
vouchertype: Optional[str] vouchertype: Optional[str]
batch_validated_ok_date: Optional[str]
batch_rejected_code: Optional[str]
sent_date: Optional[str]
http_response_content: Optional[str]
http_response_code: Optional[int]
orderno: Optional[int]
polling_statuscode: Optional[str]
polling_statuscode_date: Optional[str]
getresult_date: Optional[str]
getresult_statuscode: Optional[str]
getresult_logg: Optional[str]
getresult_report: Optional[str]
batch_progress: Optional[str] batch_progress: Optional[str]
vouchers: Optional[List[Voucher]] vouchers: Optional[List[int]]
class ErrorTransaction(BaseModel): class ErrorTransaction(BaseModel):
......
{ {
"id": 1,
"created": "2020-01-05 03:45",
"client": 1, "client": 1,
"batchid": 2, "batchid": 2,
"period": 3, "period": 3,
......
{ {
"id": 1,
"created":"2020-08-20 00:00",
"client": 10, "client": 10,
"batchid": 20, "batchid": 20,
"period": 30, "period": 30,
......
...@@ -7,7 +7,8 @@ from requests import HTTPError ...@@ -7,7 +7,8 @@ from requests import HTTPError
from setra_client.client import SetraClient from setra_client.client import SetraClient
from setra_client.client import SetraEndpoints from setra_client.client import SetraEndpoints
from setra_client.models import Batch, CompleteBatch, BatchErrors, Parameter from setra_client.models import CompleteBatch, BatchErrors, Parameter, InputBatch, \
OutputBatch
@pytest.fixture @pytest.fixture
...@@ -184,48 +185,43 @@ def test_header_replacement2(client_with_a_header, batch_url, requests_mock, bas ...@@ -184,48 +185,43 @@ def test_header_replacement2(client_with_a_header, batch_url, requests_mock, bas
# Test get_batches method # Test get_batches method
def test_successful_get_all_batches(client, requests_mock, baseurl): def test_successful_get_all_batches(client, requests_mock, baseurl, batch_fixture):
"""A working GET call should return HTTP 200, with json content""" """A working GET call should return HTTP 200, with json content"""
url = SetraEndpoints(baseurl).batch() url = SetraEndpoints(baseurl).batch()
requests_mock.get(url, json={'foo': 'bar'}, status_code=200) requests_mock.get(url, json=[batch_fixture], status_code=200)
response = client.get_batch() response = client.search_batches()
assert response == {'foo': 'bar'} assert response == [OutputBatch(**batch_fixture)]
def test_successful_get_batches_filtered_by_status(client, requests_mock, baseurl): def test_successful_get_batches_filtered_by_status(
client, requests_mock, baseurl, batch_fixture
):
"""A working GET call should return HTTP 200, with json content""" """A working GET call should return HTTP 200, with json content"""
url = SetraEndpoints(baseurl).batch() url = SetraEndpoints(baseurl).batch()
requests_mock.get(url, json={'foo': 'bar'}, status_code=200) requests_mock.get(url, json=[batch_fixture], status_code=200)
response = client.get_batch(batch_progress="ubw_import_ok") response = client.search_batches(batch_progress="ubw_import_ok")
assert response == {'foo': 'bar'} assert response == [OutputBatch(**batch_fixture)]
def test_successfully_getting_single_batch(client, requests_mock, baseurl): def test_successfully_getting_single_batch(
client, requests_mock, baseurl, batch_fixture
):
"""A working GET call should return HTTP 200, with json content""" """A working GET call should return HTTP 200, with json content"""
url = SetraEndpoints(baseurl).batch(batch_id='3') url = SetraEndpoints(baseurl).batch(batch_id='3')
requests_mock.get(url, json={'foo': 'bar'}, status_code=200) requests_mock.get(url, json=batch_fixture, status_code=200)
response = client.get_batch(3) response = client.get_batch(3)
assert response == {'foo': 'bar'} assert response == OutputBatch(**batch_fixture)
def test_failing_to_get_all_batches(client, batch_url, requests_mock, baseurl): def test_failing_to_get_all_batches(client, batch_url, requests_mock, baseurl):
"""A failing GET batches call should still return json""" """Searching for batches with no matches returns empty list"""
requests_mock.get(batch_url, json={'error': 'some json error message'}, status_code=404) requests_mock.get(batch_url, json=[], status_code=200)
response = client.get_batch()
assert response == {'error': 'some json error message'}
def test_failing_to_get_all_batches_with_text(client, batch_url, requests_mock, baseurl):
"""A failing GET batches call that return text instead of json,
will raise JSONDecodeError in setra client"""
requests_mock.get(batch_url, text='some json error message', status_code=404)
with pytest.raises(JSONDecodeError): response = client.search_batches()
client.get_batch() assert response == []
# Test get_voucher method # Test get_voucher method
...@@ -295,7 +291,7 @@ def test_failing_to_get_all_transactions(client, requests_mock, baseurl): ...@@ -295,7 +291,7 @@ def test_failing_to_get_all_transactions(client, requests_mock, baseurl):
def test_successfully_post_batch_with_voucher(client, batch_with_voucher_fixture, requests_mock, baseurl): def test_successfully_post_batch_with_voucher(client, batch_with_voucher_fixture, requests_mock, baseurl):
"""A working GET call should return HTTP 200, with json content""" """A working GET call should return HTTP 200, with json content"""
url = SetraEndpoints(baseurl).post_new_batch() url = SetraEndpoints(baseurl).post_new_batch()
batch = Batch.from_dict(batch_with_voucher_fixture) batch = InputBatch.from_dict(batch_with_voucher_fixture)
requests_mock.post(url, json={'somestatus': 'ok'}, status_code=200, request_headers={"Content-Type": "application/json"}) requests_mock.post(url, json={'somestatus': 'ok'}, status_code=200, request_headers={"Content-Type": "application/json"})
response = client.post_new_batch(batch) # we get a response object back response = client.post_new_batch(batch) # we get a response object back
...@@ -308,7 +304,7 @@ def test_successfully_post_batch_with_voucher_and_response(client, batch_with_vo ...@@ -308,7 +304,7 @@ def test_successfully_post_batch_with_voucher_and_response(client, batch_with_vo
url = SetraEndpoints(baseurl).post_new_batch() url = SetraEndpoints(baseurl).post_new_batch()
requests_mock.post(url, json={'somestatus': 'ok'}, status_code=200, request_headers={"Content-Type": "application/json"}) #expect json content requests_mock.post(url, json={'somestatus': 'ok'}, status_code=200, request_headers={"Content-Type": "application/json"}) #expect json content
batch = Batch.from_dict(batch_with_voucher_fixture) batch = InputBatch.from_dict(batch_with_voucher_fixture)
response = client.post_new_batch(batch) # we get a response object back response = client.post_new_batch(batch) # we get a response object back
assert response.json() == {'somestatus': 'ok'} assert response.json() == {'somestatus': 'ok'}
assert response.status_code == 200 assert response.status_code == 200
......
...@@ -4,22 +4,21 @@ import pytest ...@@ -4,22 +4,21 @@ import pytest
from pydantic import ValidationError from pydantic import ValidationError
from setra_client.models import ( from setra_client.models import (
Batch,
BatchErrors, BatchErrors,
CompleteBatch, CompleteBatch,
ErrorBatch, ErrorBatch,
Parameter, Parameter,
Transaction, Transaction,
Voucher, Voucher, InputBatch, OutputBatch,
) )
def test_batch(batch_fixture): def test_batch(batch_fixture):
assert Batch(**batch_fixture) assert InputBatch(**batch_fixture)
def test_batch(batch_without_voucher_field): def test_batch2(batch_without_voucher_field):
assert Batch(**batch_without_voucher_field) assert OutputBatch(**batch_without_voucher_field)
def test_voucher(voucher_fixture): def test_voucher(voucher_fixture):
...@@ -44,13 +43,13 @@ def test_transaction_fail(trans_fail_fixture): ...@@ -44,13 +43,13 @@ def test_transaction_fail(trans_fail_fixture):
def test_batch_with_voucher(batch_with_voucher_fixture): def test_batch_with_voucher(batch_with_voucher_fixture):
assert Batch(**batch_with_voucher_fixture) assert InputBatch(**batch_with_voucher_fixture)
def test_batch_fail(batch_fail_fixture): def test_batch_fail(batch_fail_fixture):
# Using wrong data format, should fail: # Using wrong data format, should fail:
with pytest.raises(ValidationError): with pytest.raises(ValidationError):
Batch(**batch_fail_fixture) OutputBatch(**batch_fail_fixture)
def test_error_batch(error_batch): def test_error_batch(error_batch):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment