This commit is contained in:
Azeez Muibi
2025-04-13 23:58:02 +01:00
parent 6f5b95771f
commit c00bc3ddbf
9 changed files with 563 additions and 53 deletions
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.13 (digifi-FirstCore)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
+6
View File
@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.13 (digifi-FirstCore)" />
</component>
</project>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/digifi-FirstCore.iml" filepath="$PROJECT_DIR$/.idea/digifi-FirstCore.iml" />
</modules>
</component>
</project>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
+7 -3
View File
@@ -62,16 +62,20 @@ def get_loans():
filters = { filters = {
'customer_id': request.args.get('customer_id'), 'customer_id': request.args.get('customer_id'),
'account_id': request.args.get('account_id'), 'account_id': request.args.get('account_id'),
'offer_id': request.args.get('offer_id'),
'status': request.args.get('status'), 'status': request.args.get('status'),
'offer_id': request.args.get('offer_id'),
'product_id': request.args.get('product_id'),
'start_date': request.args.get('start_date'), 'start_date': request.args.get('start_date'),
'end_date': request.args.get('end_date') 'end_date': request.args.get('end_date'),
'due_before': request.args.get('due_before'),
'due_after': request.args.get('due_after')
} }
# logger.info(f"Get loans request received with filters: {filters}") # logger.info(f"Get loans request received with filters: {filters}")
response = LoanService.process_request(filters) response = LoanService.process_request(filters)
return response return response
# Authorize endpoint # Authorize endpoint
@api.route("/Authorize", methods=["POST"]) @api.route("/Authorize", methods=["POST"])
def authorize(): def authorize():
@@ -88,4 +92,4 @@ def refresh():
data = request.get_json() data = request.get_json()
# logger.info(f"Authorize refresh request received: {data}") # logger.info(f"Authorize refresh request received: {data}")
response = AuthorizationService.process_refresh_request() response = AuthorizationService.process_refresh_request()
return response return response
+31 -33
View File
@@ -24,42 +24,36 @@ class LoanService(BaseService):
# Extract filters # Extract filters
customer_id = filters.get('customer_id') customer_id = filters.get('customer_id')
account_id = filters.get('account_id') account_id = filters.get('account_id')
offer_id = filters.get('offer_id')
status = filters.get('status') status = filters.get('status')
offer_id = filters.get('offer_id')
product_id = filters.get('product_id')
start_date = filters.get('start_date') start_date = filters.get('start_date')
end_date = filters.get('end_date') end_date = filters.get('end_date')
due_before = filters.get('due_before')
due_after = filters.get('due_after')
# Convert string dates to datetime objects if provided # Convert string dates to datetime objects if provided
if start_date and isinstance(start_date, str): if start_date and isinstance(start_date, str):
start_date = datetime.fromisoformat(start_date.replace('Z', '+00:00')) start_date = datetime.fromisoformat(start_date.replace('Z', '+00:00'))
if end_date and isinstance(end_date, str): if end_date and isinstance(end_date, str):
end_date = datetime.fromisoformat(end_date.replace('Z', '+00:00')) end_date = datetime.fromisoformat(end_date.replace('Z', '+00:00'))
if due_before and isinstance(due_before, str):
due_before = datetime.fromisoformat(due_before.replace('Z', '+00:00'))
if due_after and isinstance(due_after, str):
due_after = datetime.fromisoformat(due_after.replace('Z', '+00:00'))
# Get loans with filters # Get loans with optional filters
query = Loan.query loans = Loan.get_all_loans(
customer_id=customer_id,
if customer_id: account_id=account_id,
query = query.filter(Loan.customer_id == customer_id) status=status,
offer_id=offer_id,
if account_id: product_id=product_id,
query = query.filter(Loan.account_id == account_id) start_date=start_date,
end_date=end_date,
if offer_id: due_before=due_before,
query = query.filter(Loan.offer_id == offer_id) due_after=due_after
)
if status:
query = query.filter(Loan.status == status)
if start_date:
query = query.filter(Loan.created_at >= start_date)
if end_date:
query = query.filter(Loan.created_at <= end_date)
# Order by created_at descending (newest first)
query = query.order_by(Loan.created_at.desc())
loans = query.all()
# Convert loans to dictionary format # Convert loans to dictionary format
loans_data = [] loans_data = []
@@ -69,10 +63,15 @@ class LoanService(BaseService):
'customer_id': loan.customer_id, 'customer_id': loan.customer_id,
'account_id': loan.account_id, 'account_id': loan.account_id,
'offer_id': loan.offer_id, 'offer_id': loan.offer_id,
'principal_amount': loan.principal_amount, 'initial_loan_amount': loan.initial_loan_amount,
'current_loan_amount': loan.current_loan_amount,
'status': loan.status, 'status': loan.status,
'created_at': loan.created_at.isoformat(), 'product_id': loan.product_id,
'updated_at': loan.updated_at.isoformat() 'default_penalty_fee': loan.default_penalty_fee,
'continuous_fee': loan.continuous_fee,
'due_date': loan.due_date.isoformat() if loan.due_date else None,
'created_at': loan.created_at.isoformat() if loan.created_at else None,
'updated_at': loan.updated_at.isoformat() if loan.updated_at else None
}) })
response_data = { response_data = {
@@ -83,8 +82,7 @@ class LoanService(BaseService):
return response_data return response_data
except Exception as e: except Exception as e:
logger.error(f"Error retrieving loans: {str(e)}", exc_info=True) logger.error(f"An error occurred: {str(e)}", exc_info=True)
return jsonify({ return jsonify({
'status': 'error', "message": "Internal Server Error"
'message': f'Failed to retrieve loans: {str(e)}' }), 500
}), 500
+87 -17
View File
@@ -4,6 +4,7 @@ from app.models.customer import Customer
from app.models.account import Account from app.models.account import Account
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from sqlalchemy import and_, or_, not_
from app.models import Customer from app.models import Customer
@@ -18,10 +19,15 @@ class Loan(db.Model):
customer_id = db.Column(db.String(50), nullable=False) customer_id = db.Column(db.String(50), nullable=False)
account_id = db.Column(db.String(50), nullable=False) account_id = db.Column(db.String(50), nullable=False)
offer_id = db.Column(db.String(20), nullable=False) offer_id = db.Column(db.String(20), nullable=False)
principal_amount = db.Column(db.Float, nullable=False) initial_loan_amount = db.Column(db.Float, nullable=False)
status = db.Column(db.String(20), default='pending') status = db.Column(db.String(20), default='pending')
created_at = db.Column(db.DateTime, default=datetime.now(timezone.utc)) created_at = db.Column(db.DateTime, default=datetime.now(timezone.utc))
updated_at = db.Column(db.DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc)) updated_at = db.Column(db.DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc))
product_id = db.Column(db.String(20))
current_loan_amount = db.Column(db.Float)
default_penalty_fee = db.Column(db.Float)
continuous_fee = db.Column(db.Float)
due_date = db.Column(db.DateTime)
customer = relationship( customer = relationship(
"Customer", "Customer",
@@ -31,26 +37,32 @@ class Loan(db.Model):
) )
@classmethod @classmethod
def create_loan(cls, customer_id, account_id, offer_id, principal_amount, status='pending'): def create_loan(cls, customer_id, account_id, offer_id, initial_loan_amount, status='pending',
product_id=None, current_loan_amount=None, default_penalty_fee=None,
continuous_fee=None, due_date=None):
# Check if customer exists # Check if customer exists
is_valid = Customer.is_valid_customer(customer_id) is_valid = Customer.is_valid_customer(customer_id)
if not is_valid: if not is_valid:
raise ValueError("Customer does not exist") raise ValueError("Customer does not exist")
# # Check for active loans # # Check for active loans
# has_active_loans = cls.has_active_loans(customer_id) # has_active_loans = cls.has_active_loans(customer_id)
# if has_active_loans: # if has_active_loans:
# raise ValueError("Customer has active loans") # raise ValueError("Customer has active loans")
# Create and save the loan # Create and save the loan
loan = cls( loan = cls(
customer_id=customer_id, customer_id=customer_id,
account_id=account_id, account_id=account_id,
offer_id=offer_id, offer_id=offer_id,
principal_amount=principal_amount, initial_loan_amount=initial_loan_amount,
status=status status=status,
product_id=product_id,
current_loan_amount=current_loan_amount or initial_loan_amount,
default_penalty_fee=default_penalty_fee,
continuous_fee=continuous_fee,
due_date=due_date
) )
try: try:
@@ -59,7 +71,6 @@ class Loan(db.Model):
raise ValueError(f"Database integrity error: {err}") raise ValueError(f"Database integrity error: {err}")
return loan return loan
@classmethod @classmethod
def has_active_loans(cls, customer_id): def has_active_loans(cls, customer_id):
active_loans = cls.query.filter_by( active_loans = cls.query.filter_by(
@@ -70,35 +81,89 @@ class Loan(db.Model):
if active_loans > 0: if active_loans > 0:
return False return False
return True return True
@classmethod @classmethod
def get_customer_loan(cls, loan_id, customer_id): def get_customer_loan(cls, loan_id, customer_id):
""" """
Get customer's active loans. Get customer's active loans.
""" """
loan = cls.query.filter_by(id = loan_id, customer_id = customer_id).first() loan = cls.query.filter_by(id=loan_id, customer_id=customer_id).first()
if not loan: if not loan:
raise ValueError(f"Loan with ID {loan_id} does not exist or does not belong to customer {customer_id}.") raise ValueError(f"Loan with ID {loan_id} does not exist or does not belong to customer {customer_id}.")
return loan return loan
@classmethod @classmethod
def update_status(cls, loan_id, status): def update_status(cls, loan_id, status):
""" """
Update the status of the loan with the given loan_id. Update the status of the loan with the given loan_id.
""" """
# Retrieve loan # Retrieve loan
loan = cls.query.get(loan_id) loan = cls.query.get(loan_id)
if not loan: if not loan:
raise ValueError(f"Loan with ID {loan_id} does not exist.") raise ValueError(f"Loan with ID {loan_id} does not exist.")
if loan.status == status: if loan.status == status:
return return
# Update loan status and the updated_at timestamp # Update loan status and the updated_at timestamp
loan.status = status loan.status = status
@classmethod
def get_all_loans(cls, customer_id=None, account_id=None, status=None, offer_id=None,
product_id=None, start_date=None, end_date=None, due_before=None, due_after=None):
"""
Get all loans with optional filtering
Args:
customer_id (str, optional): Filter by customer ID
account_id (str, optional): Filter by account ID
status (str, optional): Filter by loan status
offer_id (str, optional): Filter by offer ID
product_id (str, optional): Filter by product ID
start_date (datetime, optional): Filter by start date (created_at)
end_date (datetime, optional): Filter by end date (created_at)
due_before (datetime, optional): Filter loans due before this date
due_after (datetime, optional): Filter loans due after this date
Returns:
list: List of Loan objects
"""
query = cls.query
# Apply filters if provided
if customer_id:
query = query.filter(cls.customer_id == customer_id)
if account_id:
query = query.filter(cls.account_id == account_id)
if status:
query = query.filter(cls.status == status)
if offer_id:
query = query.filter(cls.offer_id == offer_id)
if product_id:
query = query.filter(cls.product_id == product_id)
if start_date:
query = query.filter(cls.created_at >= start_date)
if end_date:
query = query.filter(cls.created_at <= end_date)
if due_before:
query = query.filter(cls.due_date <= due_before)
if due_after:
query = query.filter(cls.due_date >= due_after)
# Order by created_at descending (newest first)
query = query.order_by(cls.created_at.desc())
return query.all()
def to_dict(self): def to_dict(self):
""" """
Convert the Loan object to a dictionary format for JSON serialization. Convert the Loan object to a dictionary format for JSON serialization.
@@ -108,11 +173,16 @@ class Loan(db.Model):
'customer_id': self.customer_id, 'customer_id': self.customer_id,
'account_id': self.account_id, 'account_id': self.account_id,
'offer_id': self.offer_id, 'offer_id': self.offer_id,
'principal_amount': self.principal_amount, 'initial_loan_amount': self.initial_loan_amount,
'current_loan_amount': self.current_loan_amount,
'status': self.status, 'status': self.status,
'product_id': self.product_id,
'default_penalty_fee': self.default_penalty_fee,
'continuous_fee': self.continuous_fee,
'due_date': self.due_date.isoformat() if self.due_date else None,
'created_at': self.created_at.isoformat() if self.created_at else None, 'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None 'updated_at': self.updated_at.isoformat() if self.updated_at else None
} }
def __repr__(self): def __repr__(self):
return f'<Loan {self.id}>' return f'<Loan {self.id}>'
+402
View File
@@ -0,0 +1,402 @@
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.6.3">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Digifi First Core API Test Plan" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<ConfigTestElement guiclass="HttpDefaultsGui" testclass="ConfigTestElement" testname="HTTP Request Defaults">
<intProp name="HTTPSampler.connect_timeout">10000</intProp>
<intProp name="HTTPSampler.response_timeout">30000</intProp>
<stringProp name="HTTPSampler.domain">localhost</stringProp>
<stringProp name="HTTPSampler.port">4300</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.implementation"></stringProp>
</ConfigTestElement>
<hashTree/>
<Arguments guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments">
<elementProp name="customer_id" elementType="Argument">
<stringProp name="Argument.name">customer_id</stringProp>
<stringProp name="Argument.value">CUST123</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="account_id" elementType="Argument">
<stringProp name="Argument.name">account_id</stringProp>
<stringProp name="Argument.value">ACC456</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="transaction_type" elementType="Argument">
<stringProp name="Argument.name">transaction_type</stringProp>
<stringProp name="Argument.value">PAYMENT</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="channel" elementType="Argument">
<stringProp name="Argument.name">channel</stringProp>
<stringProp name="Argument.value">MOBILE</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="status" elementType="Argument">
<stringProp name="Argument.name">status</stringProp>
<stringProp name="Argument.value">active</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="offer_id" elementType="Argument">
<stringProp name="Argument.name">offer_id</stringProp>
<stringProp name="Argument.value">OFFER789</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="product_id" elementType="Argument">
<stringProp name="Argument.name">product_id</stringProp>
<stringProp name="Argument.value">PROD101</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</Arguments>
<hashTree/>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Loan Endpoint Test" enabled="true">
<intProp name="ThreadGroup.num_threads">10</intProp>
<intProp name="ThreadGroup.ramp_time">5</intProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller">
<stringProp name="LoopController.loops">10</stringProp>
<boolProp name="LoopController.continue_forever">false</boolProp>
</elementProp>
</ThreadGroup>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Get All Loans" enabled="true">
<stringProp name="HTTPSampler.path">/loans</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.postBodyRaw">false</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables">
<collectionProp name="Arguments.arguments"/>
</elementProp>
</HTTPSamplerProxy>
<hashTree>
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Code Assertion" enabled="true">
<collectionProp name="Asserion.test_strings">
<stringProp name="49586">200</stringProp>
</collectionProp>
<stringProp name="Assertion.custom_message"></stringProp>
<stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
<boolProp name="Assertion.assume_success">false</boolProp>
<intProp name="Assertion.test_type">8</intProp>
</ResponseAssertion>
<hashTree/>
<JSONPathAssertion guiclass="JSONPathAssertionGui" testclass="JSONPathAssertion" testname="JSON Path Assertion" enabled="true">
<stringProp name="JSON_PATH">$.loans</stringProp>
<stringProp name="EXPECTED_VALUE"></stringProp>
<boolProp name="JSONVALIDATION">false</boolProp>
<boolProp name="EXPECT_NULL">false</boolProp>
<boolProp name="INVERT">false</boolProp>
<boolProp name="ISREGEX">true</boolProp>
</JSONPathAssertion>
<hashTree/>
</hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Get Loans with Filters">
<stringProp name="HTTPSampler.path">/loans</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.postBodyRaw">false</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables">
<collectionProp name="Arguments.arguments">
<elementProp name="customer_id" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">${customer_id}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
<boolProp name="HTTPArgument.use_equals">true</boolProp>
<stringProp name="Argument.name">customer_id</stringProp>
</elementProp>
<elementProp name="status" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">${status}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
<boolProp name="HTTPArgument.use_equals">true</boolProp>
<stringProp name="Argument.name">status</stringProp>
</elementProp>
</collectionProp>
</elementProp>
</HTTPSamplerProxy>
<hashTree>
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Code Assertion" enabled="true">
<collectionProp name="Asserion.test_strings">
<stringProp name="49586">200</stringProp>
</collectionProp>
<stringProp name="Assertion.custom_message"></stringProp>
<stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
<boolProp name="Assertion.assume_success">false</boolProp>
<intProp name="Assertion.test_type">8</intProp>
</ResponseAssertion>
<hashTree/>
<JSONPathAssertion guiclass="JSONPathAssertionGui" testclass="JSONPathAssertion" testname="JSON Path Assertion" enabled="true">
<stringProp name="JSON_PATH">$.loans</stringProp>
<stringProp name="EXPECTED_VALUE"></stringProp>
<boolProp name="JSONVALIDATION">false</boolProp>
<boolProp name="EXPECT_NULL">false</boolProp>
<boolProp name="INVERT">false</boolProp>
<boolProp name="ISREGEX">true</boolProp>
</JSONPathAssertion>
<hashTree/>
</hashTree>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
<ResultCollector guiclass="SummaryReport" testclass="ResultCollector" testname="Summary Report" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Transaction Endpoint Test" enabled="true">
<intProp name="ThreadGroup.num_threads">10</intProp>
<intProp name="ThreadGroup.ramp_time">5</intProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller">
<stringProp name="LoopController.loops">10</stringProp>
<boolProp name="LoopController.continue_forever">false</boolProp>
</elementProp>
</ThreadGroup>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Get All Transactions" enabled="true">
<stringProp name="HTTPSampler.path">/transactions</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.postBodyRaw">false</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables">
<collectionProp name="Arguments.arguments"/>
</elementProp>
</HTTPSamplerProxy>
<hashTree>
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Code Assertion" enabled="true">
<collectionProp name="Asserion.test_strings">
<stringProp name="49586">200</stringProp>
</collectionProp>
<stringProp name="Assertion.custom_message"></stringProp>
<stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
<boolProp name="Assertion.assume_success">false</boolProp>
<intProp name="Assertion.test_type">8</intProp>
</ResponseAssertion>
<hashTree/>
<JSONPathAssertion guiclass="JSONPathAssertionGui" testclass="JSONPathAssertion" testname="JSON Path Assertion" enabled="true">
<stringProp name="JSON_PATH">$.transactions</stringProp>
<stringProp name="EXPECTED_VALUE"></stringProp>
<boolProp name="JSONVALIDATION">false</boolProp>
<boolProp name="EXPECT_NULL">false</boolProp>
<boolProp name="INVERT">false</boolProp>
<boolProp name="ISREGEX">true</boolProp>
</JSONPathAssertion>
<hashTree/>
</hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Get Transactions with Filters" enabled="true">
<stringProp name="HTTPSampler.path">/transactions</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.postBodyRaw">false</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables">
<collectionProp name="Arguments.arguments">
<elementProp name="account_id" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">${account_id}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
<boolProp name="HTTPArgument.use_equals">true</boolProp>
<stringProp name="Argument.name">account_id</stringProp>
</elementProp>
<elementProp name="type" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">${transaction_type}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
<boolProp name="HTTPArgument.use_equals">true</boolProp>
<stringProp name="Argument.name">type</stringProp>
</elementProp>
<elementProp name="channel" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">${channel}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
<boolProp name="HTTPArgument.use_equals">true</boolProp>
<stringProp name="Argument.name">channel</stringProp>
</elementProp>
</collectionProp>
</elementProp>
</HTTPSamplerProxy>
<hashTree>
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Code Assertion" enabled="true">
<collectionProp name="Asserion.test_strings">
<stringProp name="49586">200</stringProp>
</collectionProp>
<stringProp name="Assertion.custom_message"></stringProp>
<stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
<boolProp name="Assertion.assume_success">false</boolProp>
<intProp name="Assertion.test_type">8</intProp>
</ResponseAssertion>
<hashTree/>
<JSONPathAssertion guiclass="JSONPathAssertionGui" testclass="JSONPathAssertion" testname="JSON Path Assertion" enabled="true">
<stringProp name="JSON_PATH">$.transactions</stringProp>
<stringProp name="EXPECTED_VALUE"></stringProp>
<boolProp name="JSONVALIDATION">false</boolProp>
<boolProp name="EXPECT_NULL">false</boolProp>
<boolProp name="INVERT">false</boolProp>
<boolProp name="ISREGEX">true</boolProp>
</JSONPathAssertion>
<hashTree/>
</hashTree>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
<ResultCollector guiclass="SummaryReport" testclass="ResultCollector" testname="Summary Report" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>