add api spec and mock api server
This commit is contained in:
parent
a5b46bffb1
commit
f523f0fbc9
2
src/api/.gitignore
vendored
Normal file
2
src/api/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
package-lock.json
|
157
src/api/README.md
Normal file
157
src/api/README.md
Normal file
@ -0,0 +1,157 @@
|
||||
# Freezone Backend API
|
||||
|
||||
A comprehensive API for managing digital residents, free zone companies, invoices, and expenses. Designed for authorized resellers to register and manage entities in the freezone system.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Authentication
|
||||
```bash
|
||||
curl -X POST https://api.freezone.com/v1/auth/api-key \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"client_id": "your_client_id",
|
||||
"client_secret": "your_client_secret",
|
||||
"access_password": "freezone_access_password"
|
||||
}'
|
||||
```
|
||||
|
||||
### 2. Use API Key
|
||||
```bash
|
||||
curl -H "X-API-Key: your_api_key" https://api.freezone.com/v1/digital-residents
|
||||
```
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### Dual ID System
|
||||
- **Input**: Provide your reseller IDs when creating entities
|
||||
- **Output**: Receive Freezone-issued IDs for all operations
|
||||
- **Operations**: Use Freezone IDs for all subsequent CRUD operations
|
||||
|
||||
| Entity | Reseller ID (Input) | Freezone ID (Operations) |
|
||||
|--------|-------------------|-------------------------|
|
||||
| Digital Resident | `reseller_user_id` | `resident_id` |
|
||||
| Free Zone Company | `reseller_company_id` | `fzc_id` |
|
||||
| Invoice | `reseller_invoice_id` | `fz_invoice_id` |
|
||||
| Expense | `reseller_expense_id` | `fz_expense_id` |
|
||||
|
||||
### Prerequisites & Dependencies
|
||||
|
||||
#### Creating Free Zone Companies
|
||||
- **Required**: All `shareholder_resident_ids` must be registered digital residents
|
||||
- **Validation**: API validates that all provided resident IDs exist before company creation
|
||||
- **Example**: To create a company with shareholders `["fz_resident_abc123", "fz_resident_def456"]`, both residents must exist
|
||||
|
||||
#### Creating Invoices/Expenses
|
||||
- **Required**: Valid `fzc_id` (company must exist)
|
||||
- **Scope**: All invoices and expenses are company-scoped
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Digital Residents
|
||||
```
|
||||
GET /digital-residents # List residents
|
||||
POST /digital-residents # Create (provide reseller_user_id)
|
||||
GET /digital-residents/{resident_id} # Get by Freezone ID
|
||||
PUT /digital-residents/{resident_id} # Update by Freezone ID
|
||||
DELETE /digital-residents/{resident_id} # Delete by Freezone ID
|
||||
```
|
||||
|
||||
### Free Zone Companies
|
||||
```
|
||||
GET /free-zone-companies # List companies
|
||||
POST /free-zone-companies # Create (provide reseller_company_id)
|
||||
GET /free-zone-companies/{fzc_id} # Get by Freezone ID
|
||||
PUT /free-zone-companies/{fzc_id} # Update by Freezone ID
|
||||
DELETE /free-zone-companies/{fzc_id} # Delete by Freezone ID
|
||||
```
|
||||
|
||||
### Invoices (Company-scoped)
|
||||
```
|
||||
GET /free-zone-companies/{fzc_id}/invoices # List invoices
|
||||
POST /free-zone-companies/{fzc_id}/invoices # Create invoice
|
||||
GET /free-zone-companies/{fzc_id}/invoices/{fz_invoice_id} # Get invoice
|
||||
PUT /free-zone-companies/{fzc_id}/invoices/{fz_invoice_id} # Update invoice
|
||||
DELETE /free-zone-companies/{fzc_id}/invoices/{fz_invoice_id} # Delete invoice
|
||||
```
|
||||
|
||||
### Expenses (Company-scoped)
|
||||
```
|
||||
GET /free-zone-companies/{fzc_id}/expenses # List expenses
|
||||
POST /free-zone-companies/{fzc_id}/expenses # Create expense
|
||||
GET /free-zone-companies/{fzc_id}/expenses/{fz_expense_id} # Get expense
|
||||
PUT /free-zone-companies/{fzc_id}/expenses/{fz_expense_id} # Update expense
|
||||
DELETE /free-zone-companies/{fzc_id}/expenses/{fz_expense_id} # Delete expense
|
||||
```
|
||||
|
||||
## Example Workflow
|
||||
|
||||
### 1. Create Digital Residents
|
||||
```json
|
||||
POST /digital-residents
|
||||
{
|
||||
"reseller_user_id": "reseller_user_123",
|
||||
"email": "john@example.com",
|
||||
"first_name": "John",
|
||||
"last_name": "Doe",
|
||||
"kyc_provider_id": "kyc_john_789"
|
||||
}
|
||||
// Returns: resident_id: "fz_resident_abc123"
|
||||
```
|
||||
|
||||
### 2. Create Free Zone Company
|
||||
```json
|
||||
POST /free-zone-companies
|
||||
{
|
||||
"reseller_company_id": "reseller_comp_456",
|
||||
"name": "Tech Innovations FZC",
|
||||
"type": "company",
|
||||
"shareholder_resident_ids": ["fz_resident_abc123"], // Must exist!
|
||||
"crypto_wallets": [
|
||||
{
|
||||
"public_key": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
|
||||
"chain": "bitcoin",
|
||||
"label": "Main Bitcoin Wallet"
|
||||
}
|
||||
]
|
||||
}
|
||||
// Returns: fzc_id: "fz_company_xyz789"
|
||||
```
|
||||
|
||||
### 3. Create Invoice
|
||||
```json
|
||||
POST /free-zone-companies/fz_company_xyz789/invoices
|
||||
{
|
||||
"reseller_invoice_id": "reseller_inv_789",
|
||||
"invoice_number": "INV-2024-001",
|
||||
"amount": 1500.00,
|
||||
"currency": "USD",
|
||||
"issue_date": "2024-01-15"
|
||||
}
|
||||
// Returns: fz_invoice_id: "fz_invoice_inv123"
|
||||
```
|
||||
|
||||
## Rate Limiting
|
||||
- **Limit**: 100 requests per minute
|
||||
- **Headers**: Check `X-RateLimit-Remaining` and `X-RateLimit-Reset`
|
||||
|
||||
## Error Handling
|
||||
- **400**: Bad request (validation errors)
|
||||
- **401**: Invalid/missing API key
|
||||
- **404**: Resource not found
|
||||
- **409**: Conflict (duplicate reseller IDs)
|
||||
- **429**: Rate limit exceeded
|
||||
|
||||
## KYC Integration
|
||||
Use the `kyc_provider_id` from digital residents to query your KYC provider for verification data.
|
||||
|
||||
## Crypto Wallet Support
|
||||
Supported chains: `bitcoin`, `ethereum`, `polygon`, `binance_smart_chain`, `solana`
|
||||
|
||||
## Documentation
|
||||
- **Interactive API Docs**: [Swagger UI](./swagger-ui.html)
|
||||
- **OpenAPI Spec**: [openapi_updated.yaml](./openapi_updated.yaml)
|
||||
|
||||
## Support
|
||||
- **Email**: api-support@freezone.com
|
||||
- **Rate Limiting**: 100 requests/minute
|
||||
- **Authentication**: API key required for all endpoints
|
1743
src/api/openapi.yaml
Normal file
1743
src/api/openapi.yaml
Normal file
File diff suppressed because it is too large
Load Diff
2032
src/api/openapi_updated.yaml
Normal file
2032
src/api/openapi_updated.yaml
Normal file
File diff suppressed because it is too large
Load Diff
30
src/api/package.json
Normal file
30
src/api/package.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "freezone-api-docs",
|
||||
"version": "1.0.0",
|
||||
"description": "Documentation and mock server for Freezone API",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node server.js",
|
||||
"dev": "nodemon server.js",
|
||||
"install-deps": "npm install",
|
||||
"test-mock": "./test-mock-api.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"http-proxy-middleware": "^2.0.6",
|
||||
"@stoplight/prism-cli": "^5.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.0.2"
|
||||
},
|
||||
"keywords": [
|
||||
"openapi",
|
||||
"mock",
|
||||
"api",
|
||||
"freezone",
|
||||
"swagger",
|
||||
"documentation"
|
||||
],
|
||||
"author": "Freezone API Team",
|
||||
"license": "MIT"
|
||||
}
|
40
src/api/serve.sh
Executable file
40
src/api/serve.sh
Executable file
@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Simple HTTP server to serve the Swagger UI and OpenAPI spec
|
||||
# Usage: ./serve.sh [port]
|
||||
|
||||
PORT=${1:-8080}
|
||||
API_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
echo "🚀 Starting Freezone API Documentation Server..."
|
||||
echo "📁 Serving from: $API_DIR"
|
||||
echo "🌐 URL: http://localhost:$PORT"
|
||||
echo "📖 Swagger UI: http://localhost:$PORT/swagger-ui.html"
|
||||
echo ""
|
||||
echo "📋 Available files:"
|
||||
echo " - swagger-ui.html (Interactive API documentation)"
|
||||
echo " - openapi_updated.yaml (OpenAPI specification)"
|
||||
echo " - README.md (API documentation)"
|
||||
echo ""
|
||||
echo "Press Ctrl+C to stop the server"
|
||||
echo "----------------------------------------"
|
||||
|
||||
# Check if Python 3 is available
|
||||
if command -v python3 &> /dev/null; then
|
||||
cd "$API_DIR"
|
||||
python3 -m http.server $PORT
|
||||
elif command -v python &> /dev/null; then
|
||||
cd "$API_DIR"
|
||||
python -m http.server $PORT
|
||||
elif command -v node &> /dev/null; then
|
||||
# Use Node.js if available
|
||||
cd "$API_DIR"
|
||||
npx http-server -p $PORT
|
||||
else
|
||||
echo "❌ Error: No suitable HTTP server found."
|
||||
echo "Please install one of the following:"
|
||||
echo " - Python 3: python3 -m http.server"
|
||||
echo " - Python 2: python -m SimpleHTTPServer"
|
||||
echo " - Node.js: npx http-server"
|
||||
exit 1
|
||||
fi
|
175
src/api/server.js
Normal file
175
src/api/server.js
Normal file
@ -0,0 +1,175 @@
|
||||
const express = require('express');
|
||||
const path = require('path');
|
||||
const { createProxyMiddleware } = require('http-proxy-middleware');
|
||||
const { spawn } = require('child_process');
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
// Serve static files (documentation)
|
||||
app.use(express.static(__dirname));
|
||||
|
||||
// Start Prism mock server on a different port internally
|
||||
let prismProcess;
|
||||
|
||||
function startPrismServer() {
|
||||
console.log('🔧 Starting internal mock server...');
|
||||
prismProcess = spawn('npx', ['prism', 'mock', 'openapi_updated.yaml', '--host', '127.0.0.1', '--port', '4001', '--dynamic'], {
|
||||
cwd: __dirname,
|
||||
stdio: ['pipe', 'pipe', 'pipe']
|
||||
});
|
||||
|
||||
prismProcess.stdout.on('data', (data) => {
|
||||
const output = data.toString().trim();
|
||||
if (output && !output.includes('Prism is listening')) {
|
||||
console.log(`[Internal] ${output}`);
|
||||
}
|
||||
});
|
||||
|
||||
prismProcess.stderr.on('data', (data) => {
|
||||
const output = data.toString().trim();
|
||||
if (output) {
|
||||
console.log(`[Internal] ${output}`);
|
||||
}
|
||||
});
|
||||
|
||||
prismProcess.on('close', (code) => {
|
||||
console.log(`[Internal] Mock server process exited with code ${code}`);
|
||||
});
|
||||
|
||||
// Give Prism time to start
|
||||
setTimeout(() => {
|
||||
console.log('✅ Mock API service ready at /api/mock');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// Proxy /api/mock/* to the internal Prism server
|
||||
app.use('/api/mock', createProxyMiddleware({
|
||||
target: 'http://127.0.0.1:4001',
|
||||
changeOrigin: true,
|
||||
pathRewrite: {
|
||||
'^/api/mock': '', // Remove /api/mock prefix when forwarding to Prism
|
||||
},
|
||||
onError: (err, req, res) => {
|
||||
console.error('Proxy error:', err.message);
|
||||
res.status(503).json({
|
||||
error: 'mock_server_unavailable',
|
||||
message: 'Mock API server is not available. Please wait a moment and try again.',
|
||||
details: {
|
||||
endpoint: req.originalUrl,
|
||||
internal_error: err.message
|
||||
}
|
||||
});
|
||||
},
|
||||
onProxyReq: (proxyReq, req, res) => {
|
||||
console.log(`[Mock API] ${req.method} ${req.originalUrl} -> http://127.0.0.1:4001${req.url}`);
|
||||
}
|
||||
}));
|
||||
|
||||
// API status endpoint
|
||||
app.get('/api/status', (req, res) => {
|
||||
res.json({
|
||||
status: 'running',
|
||||
services: {
|
||||
documentation: {
|
||||
status: 'active',
|
||||
endpoints: [
|
||||
'GET /',
|
||||
'GET /swagger-ui.html',
|
||||
'GET /openapi_updated.yaml',
|
||||
'GET /README.md'
|
||||
]
|
||||
},
|
||||
mock_api: {
|
||||
status: prismProcess ? 'active' : 'starting',
|
||||
base_url: '/api/mock',
|
||||
endpoints: [
|
||||
'POST /api/mock/auth/api-key',
|
||||
'GET /api/mock/digital-residents',
|
||||
'POST /api/mock/digital-residents',
|
||||
'GET /api/mock/free-zone-companies',
|
||||
'POST /api/mock/free-zone-companies',
|
||||
'GET /api/mock/free-zone-companies/{fzc_id}/invoices',
|
||||
'GET /api/mock/free-zone-companies/{fzc_id}/expenses'
|
||||
]
|
||||
}
|
||||
},
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
|
||||
// Health check endpoint
|
||||
app.get('/health', (req, res) => {
|
||||
res.json({ status: 'healthy', timestamp: new Date().toISOString() });
|
||||
});
|
||||
|
||||
// Default route - redirect to Swagger UI
|
||||
app.get('/', (req, res) => {
|
||||
res.redirect('/swagger-ui.html');
|
||||
});
|
||||
|
||||
// 404 handler
|
||||
app.use((req, res) => {
|
||||
res.status(404).json({
|
||||
error: 'not_found',
|
||||
message: `Endpoint ${req.method} ${req.path} not found`,
|
||||
available_endpoints: {
|
||||
documentation: [
|
||||
'GET /',
|
||||
'GET /swagger-ui.html',
|
||||
'GET /openapi_updated.yaml',
|
||||
'GET /README.md'
|
||||
],
|
||||
mock_api: [
|
||||
'POST /api/mock/auth/api-key',
|
||||
'GET /api/mock/digital-residents',
|
||||
'POST /api/mock/digital-residents',
|
||||
'GET /api/mock/free-zone-companies',
|
||||
'POST /api/mock/free-zone-companies'
|
||||
],
|
||||
status: [
|
||||
'GET /api/status',
|
||||
'GET /health'
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Graceful shutdown
|
||||
process.on('SIGINT', () => {
|
||||
console.log('\n🛑 Shutting down servers...');
|
||||
if (prismProcess) {
|
||||
prismProcess.kill();
|
||||
}
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
// Start the server
|
||||
app.listen(PORT, () => {
|
||||
console.log('🚀 Freezone API Documentation & Mock Server');
|
||||
console.log('==========================================');
|
||||
console.log(`🌐 Server running on: http://localhost:${PORT}`);
|
||||
console.log('');
|
||||
console.log('📋 Available Services:');
|
||||
console.log(` 📖 Documentation: http://localhost:${PORT}/swagger-ui.html`);
|
||||
console.log(` 🔧 Mock API: http://localhost:${PORT}/api/mock`);
|
||||
console.log(` 📄 OpenAPI Spec: http://localhost:${PORT}/openapi_updated.yaml`);
|
||||
console.log(` 📊 Status: http://localhost:${PORT}/api/status`);
|
||||
console.log('');
|
||||
console.log('🔧 Mock API Examples:');
|
||||
console.log(` POST http://localhost:${PORT}/api/mock/auth/api-key`);
|
||||
console.log(` GET http://localhost:${PORT}/api/mock/digital-residents`);
|
||||
console.log(` POST http://localhost:${PORT}/api/mock/digital-residents`);
|
||||
console.log(` GET http://localhost:${PORT}/api/mock/free-zone-companies`);
|
||||
console.log('');
|
||||
console.log('💡 Tips:');
|
||||
console.log(' - Mock API returns example responses from OpenAPI spec');
|
||||
console.log(' - Use X-API-Key header for authenticated endpoints');
|
||||
console.log(' - All services run on the same port for convenience');
|
||||
console.log('');
|
||||
console.log('Press Ctrl+C to stop the server');
|
||||
console.log('==========================================');
|
||||
|
||||
// Start Prism after Express server is running
|
||||
startPrismServer();
|
||||
});
|
75
src/api/start-mock-server.sh
Executable file
75
src/api/start-mock-server.sh
Executable file
@ -0,0 +1,75 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Start both documentation and mock API server
|
||||
# Usage: ./start-mock-server.sh
|
||||
|
||||
API_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$API_DIR"
|
||||
|
||||
echo "🚀 Starting Freezone API Mock Server & Documentation..."
|
||||
echo ""
|
||||
echo "📋 Services:"
|
||||
echo " 📖 Documentation: http://localhost:3000/swagger-ui.html"
|
||||
echo " 🔧 Mock API: http://localhost:4000"
|
||||
echo " 📄 OpenAPI Spec: http://localhost:3000/openapi_updated.yaml"
|
||||
echo ""
|
||||
echo "🔧 Mock API Endpoints:"
|
||||
echo " POST http://localhost:4000/auth/api-key"
|
||||
echo " GET http://localhost:4000/digital-residents"
|
||||
echo " POST http://localhost:4000/digital-residents"
|
||||
echo " GET http://localhost:4000/free-zone-companies"
|
||||
echo " POST http://localhost:4000/free-zone-companies"
|
||||
echo " GET http://localhost:4000/free-zone-companies/{fzc_id}/invoices"
|
||||
echo " GET http://localhost:4000/free-zone-companies/{fzc_id}/expenses"
|
||||
echo ""
|
||||
echo "💡 Tips:"
|
||||
echo " - Mock API returns example responses from OpenAPI spec"
|
||||
echo " - Use 'X-API-Key: test-key' header for authenticated endpoints"
|
||||
echo " - Dynamic responses include realistic data variations"
|
||||
echo ""
|
||||
echo "Press Ctrl+C to stop all servers"
|
||||
echo "----------------------------------------"
|
||||
|
||||
# Check if Node.js and npm are available
|
||||
if ! command -v node &> /dev/null; then
|
||||
echo "❌ Error: Node.js is required but not installed."
|
||||
echo "Please install Node.js from https://nodejs.org/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v npm &> /dev/null; then
|
||||
echo "❌ Error: npm is required but not installed."
|
||||
echo "Please install npm (usually comes with Node.js)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if Prism is installed globally
|
||||
if ! command -v prism &> /dev/null; then
|
||||
echo "📦 Installing Prism CLI globally..."
|
||||
npm install -g @stoplight/prism-cli
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "❌ Failed to install Prism CLI. Trying local installation..."
|
||||
npm install @stoplight/prism-cli
|
||||
PRISM_CMD="npx prism"
|
||||
else
|
||||
PRISM_CMD="prism"
|
||||
fi
|
||||
else
|
||||
PRISM_CMD="prism"
|
||||
fi
|
||||
|
||||
# Check if concurrently is available
|
||||
if ! command -v concurrently &> /dev/null; then
|
||||
echo "📦 Installing concurrently..."
|
||||
npm install concurrently
|
||||
CONCURRENTLY_CMD="npx concurrently"
|
||||
else
|
||||
CONCURRENTLY_CMD="concurrently"
|
||||
fi
|
||||
|
||||
# Start both servers concurrently
|
||||
$CONCURRENTLY_CMD \
|
||||
--names "DOCS,MOCK" \
|
||||
--prefix-colors "blue,green" \
|
||||
"python3 -m http.server 3000" \
|
||||
"$PRISM_CMD mock openapi_updated.yaml --host 0.0.0.0 --port 4000 --dynamic"
|
153
src/api/swagger-ui.html
Normal file
153
src/api/swagger-ui.html
Normal file
@ -0,0 +1,153 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Freezone API Documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@5.10.5/swagger-ui.css" />
|
||||
<style>
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
overflow: -moz-scrollbars-vertical;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
*, *:before, *:after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
background: #fafafa;
|
||||
}
|
||||
.swagger-ui .topbar {
|
||||
background-color: #1f4e79;
|
||||
}
|
||||
.swagger-ui .topbar .download-url-wrapper .select-label {
|
||||
color: #fff;
|
||||
}
|
||||
.swagger-ui .topbar .download-url-wrapper input[type=text] {
|
||||
border: 2px solid #547ca3;
|
||||
}
|
||||
.swagger-ui .info .title {
|
||||
color: #1f4e79;
|
||||
}
|
||||
.custom-header {
|
||||
background: linear-gradient(135deg, #1f4e79 0%, #2980b9 100%);
|
||||
color: white;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.custom-header h1 {
|
||||
margin: 0;
|
||||
font-size: 2.5em;
|
||||
font-weight: 300;
|
||||
}
|
||||
.custom-header p {
|
||||
margin: 10px 0 0 0;
|
||||
font-size: 1.2em;
|
||||
opacity: 0.9;
|
||||
}
|
||||
.api-info {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
margin: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
.api-info h3 {
|
||||
color: #1f4e79;
|
||||
margin-top: 0;
|
||||
}
|
||||
.api-info .highlight {
|
||||
background: #e8f4fd;
|
||||
padding: 15px;
|
||||
border-left: 4px solid #2980b9;
|
||||
margin: 15px 0;
|
||||
}
|
||||
.api-info code {
|
||||
background: #f8f9fa;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
|
||||
<script src="https://unpkg.com/swagger-ui-dist@5.10.5/swagger-ui-bundle.js"></script>
|
||||
<script src="https://unpkg.com/swagger-ui-dist@5.10.5/swagger-ui-standalone-preset.js"></script>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
// Begin Swagger UI call region
|
||||
const ui = SwaggerUIBundle({
|
||||
url: './openapi_updated.yaml',
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl
|
||||
],
|
||||
layout: "StandaloneLayout",
|
||||
tryItOutEnabled: true,
|
||||
requestInterceptor: function(request) {
|
||||
// Add API key header if available
|
||||
const apiKey = localStorage.getItem('freezone_api_key');
|
||||
if (apiKey) {
|
||||
request.headers['X-API-Key'] = apiKey;
|
||||
}
|
||||
return request;
|
||||
},
|
||||
onComplete: function() {
|
||||
// Add custom functionality after Swagger UI loads
|
||||
console.log('Freezone API Documentation loaded');
|
||||
|
||||
// Add API key input functionality
|
||||
const topbar = document.querySelector('.topbar');
|
||||
if (topbar) {
|
||||
const apiKeyInput = document.createElement('div');
|
||||
apiKeyInput.innerHTML = `
|
||||
<div style="display: inline-block; margin-left: 20px;">
|
||||
<label style="color: white; margin-right: 10px;">API Key:</label>
|
||||
<input type="password" id="api-key-input" placeholder="Enter your API key"
|
||||
style="padding: 5px; border-radius: 3px; border: 1px solid #ccc; width: 200px;">
|
||||
<button onclick="setApiKey()" style="margin-left: 5px; padding: 5px 10px; background: #4CAF50; color: white; border: none; border-radius: 3px; cursor: pointer;">Set</button>
|
||||
<button onclick="clearApiKey()" style="margin-left: 5px; padding: 5px 10px; background: #f44336; color: white; border: none; border-radius: 3px; cursor: pointer;">Clear</button>
|
||||
</div>
|
||||
`;
|
||||
topbar.appendChild(apiKeyInput);
|
||||
|
||||
// Load saved API key
|
||||
const savedKey = localStorage.getItem('freezone_api_key');
|
||||
if (savedKey) {
|
||||
document.getElementById('api-key-input').value = savedKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// End Swagger UI call region
|
||||
|
||||
window.ui = ui;
|
||||
};
|
||||
|
||||
function setApiKey() {
|
||||
const apiKey = document.getElementById('api-key-input').value;
|
||||
if (apiKey) {
|
||||
localStorage.setItem('freezone_api_key', apiKey);
|
||||
alert('API key saved! It will be included in all requests.');
|
||||
} else {
|
||||
alert('Please enter an API key.');
|
||||
}
|
||||
}
|
||||
|
||||
function clearApiKey() {
|
||||
localStorage.removeItem('freezone_api_key');
|
||||
document.getElementById('api-key-input').value = '';
|
||||
alert('API key cleared.');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
153
src/api/test-mock-api.sh
Executable file
153
src/api/test-mock-api.sh
Executable file
@ -0,0 +1,153 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Test script for the mock API server
|
||||
# Usage: ./test-mock-api.sh
|
||||
|
||||
BASE_URL="http://localhost:3000/api/mock"
|
||||
API_KEY="test-api-key"
|
||||
|
||||
echo "🧪 Testing Freezone Mock API Server"
|
||||
echo "🌐 Base URL: $BASE_URL"
|
||||
echo "🔑 API Key: $API_KEY"
|
||||
echo "----------------------------------------"
|
||||
|
||||
# Function to make API calls with proper formatting
|
||||
test_endpoint() {
|
||||
local method=$1
|
||||
local endpoint=$2
|
||||
local description=$3
|
||||
local data=$4
|
||||
|
||||
echo ""
|
||||
echo "📋 Testing: $description"
|
||||
echo "🔗 $method $endpoint"
|
||||
|
||||
if [ -n "$data" ]; then
|
||||
echo "📤 Request Body:"
|
||||
echo "$data" | jq '.' 2>/dev/null || echo "$data"
|
||||
echo ""
|
||||
response=$(curl -s -X "$method" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-API-Key: $API_KEY" \
|
||||
-d "$data" \
|
||||
"$BASE_URL$endpoint")
|
||||
else
|
||||
response=$(curl -s -X "$method" \
|
||||
-H "X-API-Key: $API_KEY" \
|
||||
"$BASE_URL$endpoint")
|
||||
fi
|
||||
|
||||
echo "📥 Response:"
|
||||
echo "$response" | jq '.' 2>/dev/null || echo "$response"
|
||||
echo "----------------------------------------"
|
||||
}
|
||||
|
||||
# Check if jq is available for JSON formatting
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "💡 Tip: Install 'jq' for better JSON formatting: brew install jq"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Check if mock server is running
|
||||
echo "🔍 Checking if mock server is running..."
|
||||
if ! curl -s "$BASE_URL" > /dev/null; then
|
||||
echo "❌ Mock server is not running on $BASE_URL"
|
||||
echo "💡 Start it with: ./start-mock-server.sh"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Mock server is running!"
|
||||
|
||||
# Test Authentication
|
||||
test_endpoint "POST" "/auth/api-key" "Generate API Key" '{
|
||||
"client_id": "reseller_123",
|
||||
"client_secret": "secret_abc123xyz",
|
||||
"access_password": "freezone_access_2024"
|
||||
}'
|
||||
|
||||
# Test Digital Residents
|
||||
test_endpoint "GET" "/digital-residents" "List Digital Residents"
|
||||
|
||||
test_endpoint "POST" "/digital-residents" "Create Digital Resident" '{
|
||||
"reseller_user_id": "reseller_user_456",
|
||||
"email": "john.doe@example.com",
|
||||
"first_name": "John",
|
||||
"last_name": "Doe",
|
||||
"kyc_provider_id": "kyc_john_doe_789",
|
||||
"phone": "+1234567890"
|
||||
}'
|
||||
|
||||
test_endpoint "GET" "/digital-residents/fz_resident_abc123" "Get Digital Resident"
|
||||
|
||||
# Test Free Zone Companies
|
||||
test_endpoint "GET" "/free-zone-companies" "List Free Zone Companies"
|
||||
|
||||
test_endpoint "POST" "/free-zone-companies" "Create Free Zone Company" '{
|
||||
"reseller_company_id": "reseller_comp_123",
|
||||
"name": "Tech Innovations FZC",
|
||||
"type": "company",
|
||||
"description": "Technology consulting company",
|
||||
"registration_number": "FZC-2024-001",
|
||||
"tax_id": "TAX123456789",
|
||||
"address": {
|
||||
"street": "123 Business Bay",
|
||||
"city": "Dubai",
|
||||
"state": "Dubai",
|
||||
"postal_code": "12345",
|
||||
"country": "AE"
|
||||
},
|
||||
"shareholder_resident_ids": ["fz_resident_abc123", "fz_resident_def456"],
|
||||
"crypto_wallets": [
|
||||
{
|
||||
"public_key": "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
|
||||
"chain": "bitcoin",
|
||||
"label": "Main Bitcoin Wallet"
|
||||
}
|
||||
]
|
||||
}'
|
||||
|
||||
test_endpoint "GET" "/free-zone-companies/fz_company_xyz789" "Get Free Zone Company"
|
||||
|
||||
# Test Invoices
|
||||
test_endpoint "GET" "/free-zone-companies/fz_company_xyz789/invoices" "List Company Invoices"
|
||||
|
||||
test_endpoint "POST" "/free-zone-companies/fz_company_xyz789/invoices" "Create Invoice" '{
|
||||
"reseller_invoice_id": "reseller_inv_456",
|
||||
"invoice_number": "INV-2024-001",
|
||||
"amount": 1500.00,
|
||||
"currency": "USD",
|
||||
"tax_amount": 150.00,
|
||||
"description": "Consulting services for Q1 2024",
|
||||
"line_items": [
|
||||
{
|
||||
"description": "Strategy consulting",
|
||||
"quantity": 10,
|
||||
"unit_price": 150.00,
|
||||
"amount": 1500.00
|
||||
}
|
||||
],
|
||||
"issue_date": "2024-01-15",
|
||||
"due_date": "2024-02-15"
|
||||
}'
|
||||
|
||||
test_endpoint "GET" "/free-zone-companies/fz_company_xyz789/invoices/fz_invoice_inv123" "Get Invoice"
|
||||
|
||||
# Test Expenses
|
||||
test_endpoint "GET" "/free-zone-companies/fz_company_xyz789/expenses" "List Company Expenses"
|
||||
|
||||
test_endpoint "POST" "/free-zone-companies/fz_company_xyz789/expenses" "Create Expense" '{
|
||||
"reseller_expense_id": "reseller_exp_456",
|
||||
"amount": 500.00,
|
||||
"currency": "USD",
|
||||
"category": "office_supplies",
|
||||
"description": "Office equipment purchase",
|
||||
"vendor": "Office Depot",
|
||||
"receipt_url": "https://receipts.example.com/receipt123.pdf",
|
||||
"date": "2024-01-15"
|
||||
}'
|
||||
|
||||
test_endpoint "GET" "/free-zone-companies/fz_company_xyz789/expenses/fz_expense_exp123" "Get Expense"
|
||||
|
||||
echo ""
|
||||
echo "✅ Mock API testing completed!"
|
||||
echo "💡 All endpoints should return example responses from the OpenAPI specification"
|
||||
echo "🔧 Mock server provides realistic data for development and testing"
|
Loading…
Reference in New Issue
Block a user