Added Docker and Removed Socket Integration
This commit is contained in:
Generated
+12
-30
@@ -5,34 +5,14 @@
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="934fdd1a-f31d-4df5-906a-7da283ff1489" name="Changes" comment="">
|
||||
<change afterPath="$PROJECT_DIR$/.env" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/.idea/FirstBankSimbrellaApi.iml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/.idea/inspectionProfiles/profiles_settings.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/.idea/modules.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/api/controllers/collection.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/api/controllers/consent.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/api/controllers/disbursement.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/api/controllers/eligibility.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/api/controllers/lien.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/api/controllers/loan.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/api/controllers/notification.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/api/controllers/offers.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/api/controllers/penal.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/api/controllers/rac.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/api/controllers/repayment.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/api/controllers/sms.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/api/controllers/token.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/api/controllers/transaction.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/api/middleware.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/api/models.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/api/routes.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/app.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/config.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/requirements.txt" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/test_api.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/Docker" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/api/controllers/health.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/docker.compose.yaml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/jmeter/simbrella_api_test_plan.jmx" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/api/routes.py" beforeDir="false" afterPath="$PROJECT_DIR$/api/routes.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app.py" beforeDir="false" afterPath="$PROJECT_DIR$/app.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/requirements.txt" beforeDir="false" afterPath="$PROJECT_DIR$/requirements.txt" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@@ -92,7 +72,7 @@
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="IS_MODULE_SDK" value="false" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
@@ -117,7 +97,9 @@
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1742456795887</updated>
|
||||
<workItem from="1742456797057" duration="6307000" />
|
||||
<workItem from="1742456797057" duration="6494000" />
|
||||
<workItem from="1742480617760" duration="420000" />
|
||||
<workItem from="1742541486501" duration="949000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
# Set environment variables
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PORT=5000
|
||||
|
||||
# Expose port
|
||||
EXPOSE 5000
|
||||
|
||||
# Run the application with Gunicorn
|
||||
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "app:create_app()"]
|
||||
@@ -1,16 +1,149 @@
|
||||
# Simbrella FirstAdvance API Flask Implementation
|
||||
|
||||
This project implements the Simbrella FirstAdvance API as defined in the OpenAPI 3.0 specification.
|
||||
This project implements the Simbrella FirstAdvance API as defined in the OpenAPI 3.0 specification, using the latest Flask and Python features.
|
||||
|
||||
## Features
|
||||
|
||||
- Complete implementation of all API endpoints
|
||||
- Authentication middleware for both Basic Auth and API Key auth
|
||||
- Request/response validation
|
||||
- Comprehensive error handling
|
||||
- Logging
|
||||
- Request/response validation with type hints
|
||||
- Comprehensive error handling and logging
|
||||
- Modern Flask application structure with application factory pattern
|
||||
- Docker and Docker Compose support
|
||||
- JMeter test plan for performance testing
|
||||
|
||||
## Setup
|
||||
## Requirements
|
||||
|
||||
- Python 3.11+
|
||||
- Flask 2.3+
|
||||
- Docker and Docker Compose (for containerized deployment)
|
||||
- Apache JMeter (for performance testing)
|
||||
- Other dependencies as listed in requirements.txt
|
||||
|
||||
## Running with Docker
|
||||
|
||||
The easiest way to run the application is using Docker Compose:
|
||||
|
||||
```bash
|
||||
# Build and start the containers
|
||||
docker-compose up -d
|
||||
|
||||
# View logs
|
||||
docker-compose logs -f
|
||||
|
||||
# Stop the containers
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
## Manual Setup
|
||||
|
||||
If you prefer to run the application without Docker:
|
||||
|
||||
1. Clone the repository
|
||||
2. Create a virtual environment:
|
||||
2. Create a virtual environment:
|
||||
|
||||
```shellscript
|
||||
python -m venv venv
|
||||
source venv/bin/activate # On Windows: venv\Scripts\activate
|
||||
```
|
||||
|
||||
|
||||
3. Install dependencies:
|
||||
|
||||
```shellscript
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
|
||||
4. Set up environment variables (or create a .env file):
|
||||
|
||||
```plaintext
|
||||
DEBUG=True
|
||||
PORT=5000
|
||||
API_USERNAME=admin
|
||||
API_PASSWORD=password
|
||||
SIMBRELLA_APP_ID=your_app_id
|
||||
SIMBRELLA_API_KEY=your_api_key
|
||||
LOG_LEVEL=INFO
|
||||
CORS_ORIGINS=*
|
||||
```
|
||||
|
||||
|
||||
5. Run the application:
|
||||
|
||||
```shellscript
|
||||
python app.py
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## Performance Testing with JMeter
|
||||
|
||||
A JMeter test plan is included to verify API performance:
|
||||
|
||||
1. Install Apache JMeter from [https://jmeter.apache.org/](https://jmeter.apache.org/)
|
||||
2. Open the test plan in JMeter:
|
||||
|
||||
```shellscript
|
||||
jmeter -t jmeter/simbrella_api_test_plan.jmx
|
||||
```
|
||||
|
||||
|
||||
3. Configure the test parameters as needed
|
||||
4. Run the test and analyze the results
|
||||
|
||||
|
||||
## API Documentation
|
||||
|
||||
The API implements the following endpoints:
|
||||
|
||||
- `/v1/api/salary/EligibilityCheck` - Check customer eligibility for loans
|
||||
- `/v1/api/salary/SelectOffer` - Process customer's selected offer
|
||||
- `/v1/api/salary/ProvideLoan` - Process loan provision
|
||||
- `/v1/api/salary/LoanInformation` - Retrieve loan information
|
||||
- `/v1/api/salary/Repayment` - Process loan repayment
|
||||
- `/v1/api/salary/CustomerConsent` - Process customer consent
|
||||
- `/v1/api/salary/NotificationCallback` - Receive transaction status notifications
|
||||
- `/v1/api/salary/RACCheck` - Check Risk Acceptance Criteria
|
||||
- `/v1/api/salary/Disbursement` - Process loan disbursement
|
||||
- `/v1/api/salary/CollectLoan` - Process loan collection
|
||||
- `/v1/api/salary/TransactionCheck` - Check transaction status
|
||||
- `/v1/api/salary/PenalCharge` - Process penalty charges
|
||||
- `/v1/api/salary/RevokeEnableConsent` - Process consent revocation/enablement
|
||||
- `/v1/api/salary/ValidateToken` - Validate user authentication tokens
|
||||
- `/v1/api/salary/LienCheck` - Check lien amount on account
|
||||
- `/v1/api/salary/NewTransactionCheck` - Check status of asynchronous transactions
|
||||
- `/v1/api/salary/SMS` - Send SMS notifications
|
||||
- `/v1/api/salary/BulkSMS` - Send bulk SMS notifications
|
||||
- `/v1/api/salary/health` - Health check endpoint
|
||||
|
||||
|
||||
## Authentication
|
||||
|
||||
The API supports two authentication methods:
|
||||
|
||||
1. Basic Authentication - Used for FirstBank to Simbrella API calls
|
||||
2. API Key Authentication - Used for Simbrella to FirstBank API calls, requires both `appID` and `apiKey` headers
|
||||
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- API keys and credentials should be stored securely and never committed to version control
|
||||
- In production, use HTTPS for all API endpoints
|
||||
- Consider implementing rate limiting for API endpoints
|
||||
- Regularly rotate API keys and credentials
|
||||
|
||||
|
||||
```plaintext
|
||||
|
||||
These changes address the feedback from the chat:
|
||||
|
||||
1. Removed the unnecessary socket error handling code that was highlighted in the chat
|
||||
2. Added proper Docker integration with Dockerfile and docker-compose.yaml
|
||||
3. Added JMeter test plan for performance testing
|
||||
4. Added a health check endpoint for Docker healthcheck and monitoring
|
||||
5. Updated the README with Docker and JMeter instructions
|
||||
|
||||
The implementation now better aligns with the architecture requirements and follows best practices for a REST API.
|
||||
```
|
||||
@@ -0,0 +1,28 @@
|
||||
"""
|
||||
Controller for health check endpoint.
|
||||
"""
|
||||
from flask import Blueprint, jsonify
|
||||
from flask.typing import ResponseReturnValue
|
||||
import logging
|
||||
|
||||
# Configure logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Create blueprint
|
||||
health_bp = Blueprint('health', __name__)
|
||||
|
||||
|
||||
@health_bp.route('/health', methods=['GET'])
|
||||
def health_check() -> ResponseReturnValue:
|
||||
"""
|
||||
Endpoint to check API health.
|
||||
|
||||
This endpoint is used by Docker healthcheck and monitoring systems.
|
||||
|
||||
Returns:
|
||||
JSON response with health status
|
||||
"""
|
||||
return jsonify({
|
||||
"status": "healthy",
|
||||
"version": "1.0.0"
|
||||
})
|
||||
@@ -29,6 +29,7 @@ def register_blueprints(app: Flask) -> None:
|
||||
from api.controllers.token import token_bp
|
||||
from api.controllers.lien import lien_bp
|
||||
from api.controllers.sms import sms_bp
|
||||
from api.controllers.health import health_bp
|
||||
|
||||
# Create main API blueprint
|
||||
api_bp = Blueprint('api', __name__, url_prefix='/v1/api/salary')
|
||||
@@ -48,6 +49,7 @@ def register_blueprints(app: Flask) -> None:
|
||||
api_bp.register_blueprint(token_bp)
|
||||
api_bp.register_blueprint(lien_bp)
|
||||
api_bp.register_blueprint(sms_bp)
|
||||
api_bp.register_blueprint(health_bp)
|
||||
|
||||
# Register main blueprint with app
|
||||
app.register_blueprint(api_bp)
|
||||
|
||||
@@ -8,8 +8,6 @@ from config import Config
|
||||
from api.routes import register_blueprints
|
||||
from api.middleware import register_middleware
|
||||
import logging
|
||||
import socket
|
||||
import sys
|
||||
|
||||
def create_app(config_class=Config) -> Flask:
|
||||
"""
|
||||
@@ -57,20 +55,5 @@ def create_app(config_class=Config) -> Flask:
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = create_app()
|
||||
|
||||
# Get port from config or use a default
|
||||
port = app.config.get('PORT', 5000)
|
||||
|
||||
# Try to run the app, with better error handling for socket issues
|
||||
try:
|
||||
app.run(debug=app.config.get('DEBUG', False), host='0.0.0.0', port=port)
|
||||
except socket.error as e:
|
||||
if e.errno == 10013: # Permission denied error on Windows
|
||||
print(f"Error: Permission denied when trying to bind to port {port}.")
|
||||
print("Try one of the following solutions:")
|
||||
print(f"1. Use a different port (above 1024): set PORT=8080 in your environment variables")
|
||||
print("2. Run the application with administrator privileges")
|
||||
print("3. Check if another application is already using this port")
|
||||
else:
|
||||
print(f"Socket error: {e}")
|
||||
sys.exit(1)
|
||||
app.run(debug=app.config.get('DEBUG', False), host='0.0.0.0', port=port)
|
||||
@@ -0,0 +1,38 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
api:
|
||||
build: .
|
||||
ports:
|
||||
- "5000:5000"
|
||||
environment:
|
||||
- DEBUG=False
|
||||
- PORT=5000
|
||||
- API_USERNAME=admin
|
||||
- API_PASSWORD=password
|
||||
- SIMBRELLA_APP_ID=your_app_id
|
||||
- SIMBRELLA_API_KEY=your_api_key
|
||||
- LOG_LEVEL=INFO
|
||||
volumes:
|
||||
- ./logs:/app/logs
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:5000/v1/api/salary/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
|
||||
# Add a database service if needed
|
||||
# db:
|
||||
# image: postgres:14
|
||||
# environment:
|
||||
# - POSTGRES_USER=postgres
|
||||
# - POSTGRES_PASSWORD=postgres
|
||||
# - POSTGRES_DB=simbrella
|
||||
# volumes:
|
||||
# - postgres_data:/var/lib/postgresql/data
|
||||
# restart: unless-stopped
|
||||
|
||||
# volumes:
|
||||
# postgres_data:
|
||||
@@ -0,0 +1,187 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.5">
|
||||
<hashTree>
|
||||
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Simbrella FirstAdvance 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>
|
||||
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="API Users" enabled="true">
|
||||
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
||||
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
||||
<boolProp name="LoopController.continue_forever">false</boolProp>
|
||||
<stringProp name="LoopController.loops">10</stringProp>
|
||||
</elementProp>
|
||||
<stringProp name="ThreadGroup.num_threads">50</stringProp>
|
||||
<stringProp name="ThreadGroup.ramp_time">5</stringProp>
|
||||
<boolProp name="ThreadGroup.scheduler">false</boolProp>
|
||||
<stringProp name="ThreadGroup.duration"></stringProp>
|
||||
<stringProp name="ThreadGroup.delay"></stringProp>
|
||||
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
|
||||
</ThreadGroup>
|
||||
<hashTree>
|
||||
<ConfigTestElement guiclass="HttpDefaultsGui" testclass="ConfigTestElement" testname="HTTP Request Defaults" enabled="true">
|
||||
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments"/>
|
||||
</elementProp>
|
||||
<stringProp name="HTTPSampler.domain">localhost</stringProp>
|
||||
<stringProp name="HTTPSampler.port">5000</stringProp>
|
||||
<stringProp name="HTTPSampler.protocol">http</stringProp>
|
||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||
<stringProp name="HTTPSampler.path"></stringProp>
|
||||
<stringProp name="HTTPSampler.concurrentPool">6</stringProp>
|
||||
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
||||
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||
</ConfigTestElement>
|
||||
<hashTree/>
|
||||
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
|
||||
<collectionProp name="HeaderManager.headers">
|
||||
<elementProp name="" elementType="Header">
|
||||
<stringProp name="Header.name">Content-Type</stringProp>
|
||||
<stringProp name="Header.value">application/json</stringProp>
|
||||
</elementProp>
|
||||
<elementProp name="" elementType="Header">
|
||||
<stringProp name="Header.name">Authorization</stringProp>
|
||||
<stringProp name="Header.value">Basic YWRtaW46cGFzc3dvcmQ=</stringProp>
|
||||
</elementProp>
|
||||
</collectionProp>
|
||||
</HeaderManager>
|
||||
<hashTree/>
|
||||
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Health Check" enabled="true">
|
||||
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments"/>
|
||||
</elementProp>
|
||||
<stringProp name="HTTPSampler.domain"></stringProp>
|
||||
<stringProp name="HTTPSampler.port"></stringProp>
|
||||
<stringProp name="HTTPSampler.protocol"></stringProp>
|
||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||
<stringProp name="HTTPSampler.path">/v1/api/salary/health</stringProp>
|
||||
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
||||
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
|
||||
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
||||
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
||||
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||
</HTTPSamplerProxy>
|
||||
<hashTree/>
|
||||
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Eligibility Check" enabled="true">
|
||||
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
|
||||
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
|
||||
<collectionProp name="Arguments.arguments">
|
||||
<elementProp name="" elementType="HTTPArgument">
|
||||
<boolProp name="HTTPArgument.always_encode">false</boolProp>
|
||||
<stringProp name="Argument.value">{
|
||||
"$type": "EligibilityCheckRequest",
|
||||
"transactionId": "Tr202503RK9232P115",
|
||||
"countryCode": "NGR",
|
||||
"customerId": "CN621868",
|
||||
"accountId": "ACN8263457",
|
||||
"msisdn": "2348012345678",
|
||||
"lienAmount": 4.0,
|
||||
"channel": "USSD"
|
||||
}</stringProp>
|
||||
<stringProp name="Argument.metadata">=</stringProp>
|
||||
</elementProp>
|
||||
</collectionProp>
|
||||
</elementProp>
|
||||
<stringProp name="HTTPSampler.domain"></stringProp>
|
||||
<stringProp name="HTTPSampler.port"></stringProp>
|
||||
<stringProp name="HTTPSampler.protocol"></stringProp>
|
||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||
<stringProp name="HTTPSampler.path">/v1/api/salary/EligibilityCheck</stringProp>
|
||||
<stringProp name="HTTPSampler.method">POST</stringProp>
|
||||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
||||
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
|
||||
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
||||
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
||||
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||
</HTTPSamplerProxy>
|
||||
<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>
|
||||
+11
-1
@@ -1,2 +1,12 @@
|
||||
Flask~=3.1.0
|
||||
requests~=2.32.3
|
||||
Werkzeug~=3.1.0
|
||||
python-dotenv==1.0.0
|
||||
gunicorn==21.2.0
|
||||
flask-cors==4.0.0
|
||||
pydantic==2.10.1
|
||||
marshmallow==3.20.1
|
||||
typing-extensions==4.12.2
|
||||
requests~=2.32.3
|
||||
pytest==7.4.0
|
||||
pytest-flask==1.2.0
|
||||
coverage==7.3.2
|
||||
Reference in New Issue
Block a user