""" Test 3: External API Connectivity (Container Runtime Compatible) Purpose: Validate external API access capabilities within Container Runtime environment Fixed for older Streamlit versions without st.rerun() """ import streamlit as st import time import json from datetime import datetime # Page configuration st.set_page_config( page_title="Spark POC - API Test", page_icon="🌐", layout="wide" ) # Header st.title("🌐 Spark POC - External API Connectivity Test (Fixed)") st.write("Testing external API access capabilities within Container Runtime environment for TikTok music analytics POC") # Initialize session state for caching results (Container Runtime compatibility) if 'api_test_results' not in st.session_state: st.session_state.api_test_results = {} if 'test_data_cache' not in st.session_state: st.session_state.test_data_cache = {} if 'last_test_time' not in st.session_state: st.session_state.last_test_time = {} # Test 3.1: Basic HTTP Connectivity st.header("Test 3.1: Basic HTTP Connectivity") col1, col2 = st.columns(2) with col1: st.subheader("HTTPBin.org Connectivity Test") if st.button("🔗 Test Basic HTTP GET"): try: import requests test_url = "https://httpbin.org/get" st.info(f"⏳ Testing GET request to: {test_url}") start_time = time.time() response = requests.get(test_url, timeout=10) request_time = time.time() - start_time if response.status_code == 200: st.success(f"✅ Request successful! ({request_time:.2f}s)") # Cache results in session state st.session_state.api_test_results['basic_http'] = { 'status': 'success', 'response_time': request_time, 'status_code': response.status_code, 'timestamp': datetime.now().strftime("%H:%M:%S") } st.session_state.test_data_cache['basic_response'] = response.json() st.write(f"**Status Code**: {response.status_code}") st.write(f"**Response Time**: {request_time:.3f} seconds") else: st.error(f"❌ Request failed with status: {response.status_code}") st.session_state.api_test_results['basic_http'] = { 'status': 'failed', 'error': f"HTTP {response.status_code}", 'timestamp': datetime.now().strftime("%H:%M:%S") } except ImportError: st.error("❌ 'requests' library not available") st.session_state.api_test_results['basic_http'] = { 'status': 'failed', 'error': 'requests library missing', 'timestamp': datetime.now().strftime("%H:%M:%S") } except requests.exceptions.Timeout: st.error("❌ Request timed out (10s)") st.session_state.api_test_results['basic_http'] = { 'status': 'failed', 'error': 'timeout after 10s', 'timestamp': datetime.now().strftime("%H:%M:%S") } except requests.exceptions.ConnectionError: st.error("❌ Connection error - network may be blocked") st.session_state.api_test_results['basic_http'] = { 'status': 'failed', 'error': 'connection blocked', 'timestamp': datetime.now().strftime("%H:%M:%S") } except Exception as e: st.error(f"❌ Request failed: {str(e)}") st.session_state.api_test_results['basic_http'] = { 'status': 'failed', 'error': str(e), 'timestamp': datetime.now().strftime("%H:%M:%S") } with col2: st.subheader("Request Status & Results") if 'basic_http' in st.session_state.api_test_results: result = st.session_state.api_test_results['basic_http'] if result['status'] == 'success': st.success(f"✅ Last test: {result['timestamp']}") st.write(f"**Response Time**: {result['response_time']:.3f}s") st.write(f"**Status Code**: {result['status_code']}") # Show cached response data if available if 'basic_response' in st.session_state.test_data_cache: with st.expander("View Response Data"): st.json(st.session_state.test_data_cache['basic_response']) else: st.error(f"❌ Last test failed: {result['timestamp']}") st.write(f"**Error**: {result['error']}") else: st.info("Click 'Test Basic HTTP GET' to run connectivity test") # Fallback/Mock data section st.subheader("Mock Response Example") mock_response = { "url": "https://httpbin.org/get", "headers": { "User-Agent": "Spark-POC/1.0", "Accept": "application/json" }, "origin": "container-runtime-ip" } with st.expander("View Mock API Response"): st.json(mock_response) # Test 3.2: Advanced Request Features (Headers, Parameters, Authentication) st.header("Test 3.2: Advanced Request Features") col1, col2 = st.columns(2) with col1: st.subheader("Custom Headers & Parameters") if st.button("🔧 Test Advanced HTTP GET"): try: import requests url = "https://httpbin.org/get" # Custom headers (similar to what Apify might need) headers = { "User-Agent": "Spark-POC-Test/1.0", "Accept": "application/json", "X-Test-Source": "Streamlit-Container-Runtime", "X-API-Version": "v1", "Authorization": "Bearer test-token-placeholder" } # Query parameters params = { "test": "spark_poc", "source": "container_runtime", "timestamp": int(time.time()), "music_id": "test-music-123456789", "format": "json" } st.info("⏳ Testing request with custom headers and parameters...") start_time = time.time() response = requests.get(url, headers=headers, params=params, timeout=15) request_time = time.time() - start_time if response.status_code == 200: st.success(f"✅ Advanced request successful! ({request_time:.2f}s)") # Cache results st.session_state.api_test_results['advanced_http'] = { 'status': 'success', 'response_time': request_time, 'status_code': response.status_code, 'timestamp': datetime.now().strftime("%H:%M:%S") } st.session_state.test_data_cache['advanced_response'] = response.json() else: st.error(f"❌ Advanced request failed: {response.status_code}") st.session_state.api_test_results['advanced_http'] = { 'status': 'failed', 'error': f"HTTP {response.status_code}", 'timestamp': datetime.now().strftime("%H:%M:%S") } except Exception as e: st.error(f"❌ Advanced request failed: {str(e)}") st.session_state.api_test_results['advanced_http'] = { 'status': 'failed', 'error': str(e), 'timestamp': datetime.now().strftime("%H:%M:%S") } with col2: st.subheader("Request Headers & Parameters Sent") if 'advanced_http' in st.session_state.api_test_results: result = st.session_state.api_test_results['advanced_http'] if result['status'] == 'success': st.success(f"✅ Last test: {result['timestamp']}") # Show what was actually sent if 'advanced_response' in st.session_state.test_data_cache: data = st.session_state.test_data_cache['advanced_response'] with st.expander("Headers Sent"): st.json(data.get('headers', {})) with st.expander("Parameters Sent"): st.json(data.get('args', {})) else: st.error(f"❌ Last test failed: {result['timestamp']}") st.write(f"**Error**: {result['error']}") else: st.info("Click 'Test Advanced HTTP GET' to see headers and parameters") # Show example of what would be sent st.write("**Example Headers:**") example_headers = { "User-Agent": "Spark-POC-Test/1.0", "Authorization": "Bearer [token]", "Accept": "application/json" } st.json(example_headers) # Test 3.3: POST Request Test (JSON Data Submission) st.header("Test 3.3: POST Request Test") col1, col2 = st.columns(2) with col1: st.subheader("JSON POST Request") if st.button("📤 Test POST Request with JSON"): try: import requests url = "https://httpbin.org/post" # Sample JSON data (similar to Apify API call structure) post_data = { "test_type": "spark_poc_validation", "container_runtime": True, "timestamp": time.time(), "tiktok_music_urls": [ "https://www.tiktok.com/music/Oh-No-Instrumental-6889520563052645121", "https://www.tiktok.com/music/Originalton-7148348203996367622" ], "settings": { "resultsPerPage": 100, "shouldDownloadCovers": False, "shouldDownloadVideos": False, "maxResults": 10 }, "features": ["tiktok_analytics", "song_tracking", "video_discovery"] } headers = { "Content-Type": "application/json", "Authorization": "Bearer test-token-for-api", "User-Agent": "Spark-POC-Test/1.0" } st.info("⏳ Testing POST request with JSON data...") start_time = time.time() response = requests.post( url, json=post_data, headers=headers, timeout=20 ) request_time = time.time() - start_time if response.status_code == 200: st.success(f"✅ POST request successful! ({request_time:.2f}s)") # Cache results st.session_state.api_test_results['post_request'] = { 'status': 'success', 'response_time': request_time, 'status_code': response.status_code, 'timestamp': datetime.now().strftime("%H:%M:%S") } st.session_state.test_data_cache['post_response'] = response.json() else: st.error(f"❌ POST request failed: {response.status_code}") st.session_state.api_test_results['post_request'] = { 'status': 'failed', 'error': f"HTTP {response.status_code}", 'timestamp': datetime.now().strftime("%H:%M:%S") } except Exception as e: st.error(f"❌ POST request failed: {str(e)}") st.session_state.api_test_results['post_request'] = { 'status': 'failed', 'error': str(e), 'timestamp': datetime.now().strftime("%H:%M:%S") } with col2: st.subheader("POST Request Details") if 'post_request' in st.session_state.api_test_results: result = st.session_state.api_test_results['post_request'] if result['status'] == 'success': st.success(f"✅ Last test: {result['timestamp']}") st.write(f"**Response Time**: {result['response_time']:.3f}s") # Show what was sent and received if 'post_response' in st.session_state.test_data_cache: data = st.session_state.test_data_cache['post_response'] with st.expander("JSON Data Sent"): st.json(data.get('json', {})) with st.expander("Headers Sent"): st.json(data.get('headers', {})) with st.expander("Full Response"): st.json(data) else: st.error(f"❌ Last test failed: {result['timestamp']}") st.write(f"**Error**: {result['error']}") else: st.info("Click 'Test POST Request with JSON' to see request details") # Show example POST structure st.write("**Example POST Data Structure:**") example_post = { "musics": ["https://www.tiktok.com/music/song-id"], "resultsPerPage": 100, "shouldDownloadCovers": False } st.json(example_post) # Test 3.4: Network Timeout & Error Handling st.header("Test 3.4: Timeout & Error Handling") col1, col2 = st.columns(2) with col1: st.subheader("Timeout Testing") if st.button("⏱️ Test Network Timeouts"): try: import requests # Test different timeout scenarios timeout_tests = [ {"url": "https://httpbin.org/delay/2", "timeout": 5, "name": "Normal (2s delay, 5s timeout)"}, {"url": "https://httpbin.org/delay/5", "timeout": 3, "name": "Timeout (5s delay, 3s timeout)"}, {"url": "https://httpbin.org/status/404", "timeout": 10, "name": "404 Error"}, {"url": "https://httpbin.org/status/500", "timeout": 10, "name": "500 Error"} ] timeout_results = [] for test in timeout_tests: st.info(f"⏳ Testing: {test['name']}") start_time = time.time() try: response = requests.get(test['url'], timeout=test['timeout']) request_time = time.time() - start_time if response.status_code == 200: result = {"name": test['name'], "status": "success", "time": request_time, "code": response.status_code} st.success(f"✅ {test['name']}: {response.status_code} ({request_time:.2f}s)") else: result = {"name": test['name'], "status": "http_error", "time": request_time, "code": response.status_code} st.warning(f"⚠️ {test['name']}: HTTP {response.status_code} ({request_time:.2f}s)") except requests.exceptions.Timeout: request_time = time.time() - start_time result = {"name": test['name'], "status": "timeout", "time": request_time, "code": "TIMEOUT"} st.error(f"❌ {test['name']}: Timeout after {request_time:.2f}s") except Exception as e: request_time = time.time() - start_time result = {"name": test['name'], "status": "error", "time": request_time, "code": str(e)} st.error(f"❌ {test['name']}: {str(e)}") timeout_results.append(result) time.sleep(0.5) # Brief pause between tests # Cache timeout test results st.session_state.api_test_results['timeout_tests'] = { 'status': 'completed', 'results': timeout_results, 'timestamp': datetime.now().strftime("%H:%M:%S") } except Exception as e: st.error(f"❌ Timeout testing failed: {str(e)}") with col2: st.subheader("Error Handling Results") if 'timeout_tests' in st.session_state.api_test_results: timeout_data = st.session_state.api_test_results['timeout_tests'] st.success(f"✅ Tests completed: {timeout_data['timestamp']}") # Summary of results for result in timeout_data['results']: if result['status'] == 'success': st.write(f"✅ **{result['name']}**: {result['code']} ({result['time']:.2f}s)") elif result['status'] == 'timeout': st.write(f"⏱️ **{result['name']}**: Timeout ({result['time']:.2f}s)") elif result['status'] == 'http_error': st.write(f"⚠️ **{result['name']}**: HTTP {result['code']} ({result['time']:.2f}s)") else: st.write(f"❌ **{result['name']}**: Error") else: st.info("Click 'Test Network Timeouts' to run error handling tests") # Show what we're testing st.write("**Tests Include:**") st.write("- Normal request (should succeed)") st.write("- Timeout scenario (should timeout)") st.write("- HTTP 404 error handling") st.write("- HTTP 500 error handling") # Test 3.5: Mock Apify API Simulation st.header("Test 3.5: Mock Apify API Simulation") col1, col2 = st.columns(2) with col1: st.subheader("Apify Clockworks API Structure Test") if st.button("🎵 Test Mock Apify API Call"): try: import requests # Simulate the actual Apify Clockworks TikTok Sound Scraper API structure api_url = "https://httpbin.org/post" # Using httpbin to test structure # Realistic Apify payload structure apify_payload = { "musics": [ "https://www.tiktok.com/music/Oh-No-Instrumental-6889520563052645121", "https://www.tiktok.com/music/Originalton-7148348203996367622" ], "resultsPerPage": 100, "shouldDownloadCovers": False, "shouldDownloadMusicCovers": False, "shouldDownloadSlideshowImages": False, "shouldDownloadSubtitles": False, "shouldDownloadVideos": False, "videoKvStoreIdOrName": "tiktok-videos-store" } headers = { "Content-Type": "application/json", "Authorization": "Bearer apify_api_mock_token_placeholder", "User-Agent": "Spark-POC-Container-Runtime/1.0" } st.info("⏳ Simulating Apify Clockworks TikTok Sound Scraper API call...") start_time = time.time() response = requests.post( api_url, json=apify_payload, headers=headers, timeout=30 # Longer timeout for scraping APIs ) request_time = time.time() - start_time if response.status_code == 200: st.success(f"✅ Apify API structure test successful! ({request_time:.2f}s)") # Cache results st.session_state.api_test_results['apify_mock'] = { 'status': 'success', 'response_time': request_time, 'status_code': response.status_code, 'timestamp': datetime.now().strftime("%H:%M:%S") } st.session_state.test_data_cache['apify_response'] = response.json() else: st.error(f"❌ Apify simulation failed: {response.status_code}") st.session_state.api_test_results['apify_mock'] = { 'status': 'failed', 'error': f"HTTP {response.status_code}", 'timestamp': datetime.now().strftime("%H:%M:%S") } except Exception as e: st.error(f"❌ Apify simulation failed: {str(e)}") st.session_state.api_test_results['apify_mock'] = { 'status': 'failed', 'error': str(e), 'timestamp': datetime.now().strftime("%H:%M:%S") } with col2: st.subheader("Mock API Response & Integration") if 'apify_mock' in st.session_state.api_test_results: result = st.session_state.api_test_results['apify_mock'] if result['status'] == 'success': st.success(f"✅ Last test: {result['timestamp']}") # Show the request structure that was sent if 'apify_response' in st.session_state.test_data_cache: data = st.session_state.test_data_cache['apify_response'] with st.expander("Apify Request Payload Sent"): st.json(data.get('json', {})) with st.expander("Request Headers"): st.json(data.get('headers', {})) else: st.error(f"❌ Last test failed: {result['timestamp']}") st.write(f"**Error**: {result['error']}") else: st.info("Click 'Test Mock Apify API Call' to validate API structure") # Show expected real Apify integration st.write("**Real Apify Integration Code:**") st.code(""" # Actual implementation would use: import requests api_token = "your_apify_api_token" actor_id = "clockworks/tiktok-sound-scraper" url = f"https://api.apify.com/v2/acts/{actor_id}/runs" headers = { "Authorization": f"Bearer {api_token}", "Content-Type": "application/json" } payload = { "musics": ["https://www.tiktok.com/music/song-id"], "resultsPerPage": 100 } response = requests.post(url, json=payload, headers=headers) """, language="python") # Test Results Summary st.header("🔍 External API Test Results Summary") # Determine test results based on session state def get_test_status(test_key): if test_key in st.session_state.api_test_results: result = st.session_state.api_test_results[test_key] if result['status'] == 'success': return f"✅ Working ({result['timestamp']})" else: return f"❌ Failed ({result['timestamp']})" return "⚠️ Not tested yet" test_results = { "Basic HTTP Connectivity": get_test_status('basic_http'), "Advanced HTTP Features": get_test_status('advanced_http'), "POST Request Capability": get_test_status('post_request'), "Timeout & Error Handling": get_test_status('timeout_tests'), "Apify API Structure": get_test_status('apify_mock') } for test, status in test_results.items(): st.write(f"**{test}**: {status}") # Container Runtime API Analysis st.header("🐳 Container Runtime API Analysis") col1, col2 = st.columns(2) with col1: st.subheader("Network Capabilities Assessment") # Count successful tests successful_tests = sum(1 for key in ['basic_http', 'advanced_http', 'post_request', 'apify_mock'] if key in st.session_state.api_test_results and st.session_state.api_test_results[key]['status'] == 'success') total_tests = 4 if successful_tests > 0: st.write(f"**Tests Passed**: {successful_tests}/{total_tests}") if successful_tests >= 3: st.success("✅ Good API connectivity - Container Runtime supports external APIs") elif successful_tests >= 2: st.warning("⚠️ Partial API connectivity - Some restrictions may apply") else: st.error("❌ Limited API connectivity - Network restrictions detected") else: st.info("Run tests above to assess network capabilities") st.write("**For TikTok Music Analytics POC:**") st.write("- HTTP/HTTPS requests: Required for Apify API") st.write("- JSON payloads: Required for API calls") st.write("- Authentication headers: Required for API tokens") st.write("- Long timeout support: Required for scraping") with col2: st.subheader("Performance & Reliability") # Calculate average response time if we have test results response_times = [] for key in ['basic_http', 'advanced_http', 'post_request', 'apify_mock']: if key in st.session_state.api_test_results: result = st.session_state.api_test_results[key] if result['status'] == 'success' and 'response_time' in result: response_times.append(result['response_time']) if response_times: avg_time = sum(response_times) / len(response_times) st.write(f"**Average Response Time**: {avg_time:.3f}s") if avg_time < 1.0: st.success("✅ Excellent response times") elif avg_time < 3.0: st.warning("⚠️ Moderate response times") else: st.error("❌ Slow response times") st.write("**Rate Limiting Considerations:**") st.write("- Apify API has rate limits") st.write("- TikTok scraping is resource-intensive") st.write("- Consider caching scraped data") st.write("- Implement retry logic for failures") # Environment Information st.header("📊 API Environment Information") try: import platform import sys col1, col2 = st.columns(2) with col1: st.subheader("Runtime Environment") st.write(f"**Python Version**: {sys.version.split()[0]}") st.write(f"**Platform**: {platform.platform()}") st.write(f"**Streamlit Version**: {st.__version__}") # Test for requests library try: import requests st.write(f"**Requests Library**: ✅ Available (v{requests.__version__})") except: st.write("**Requests Library**: ❌ Not available") with col2: st.subheader("API Dependencies") # Test for other useful libraries dependencies = { 'json': 'JSON processing', 'urllib': 'URL utilities', 'concurrent.futures': 'Async requests', 'time': 'Timing operations' } for lib, description in dependencies.items(): try: __import__(lib) st.write(f"✅ **{lib}**: Available - {description}") except ImportError: st.write(f"❌ **{lib}**: Missing - {description}") except Exception as e: st.error(f"Error getting environment info: {e}") # Notes section st.header("📝 External API Test Notes") notes_placeholder = """ Record your observations about external API connectivity: - Which APIs are accessible from Container Runtime? - Are there any network restrictions or firewall issues? - How are timeouts and errors handled? - Authentication header support? - Performance characteristics? - Any rate limiting encountered? Specific notes for TikTok music analytics POC: - Can reach httpbin.org for testing? - POST requests with JSON working? - Authentication headers properly sent? - Suitable for Apify API integration? """ st.text_area( "API connectivity observations and notes:", placeholder=notes_placeholder, height=150 ) # Container Runtime Specific Notes st.header("🐳 Container Runtime Specific Findings") st.info(""" **Container Runtime API Access Validation:** This test validates that the Spark POC Container Runtime environment can: 1. **Make external HTTP/HTTPS requests** - Essential for Apify API calls 2. **Send custom headers and authentication** - Required for API tokens 3. **Handle JSON request/response payloads** - Core API functionality 4. **Manage timeouts and errors gracefully** - For reliable operation 5. **Support the request patterns needed for TikTok music analytics** **Next Steps Based on Results:** - ✅ If tests pass: Container Runtime is ready for Apify integration - ⚠️ If partial success: Identify and work around limitations - ❌ If tests fail: Network configuration changes may be needed **For Apify TikTok Sound Scraper Integration:** - Endpoint: `https://api.apify.com/v2/acts/clockworks/tiktok-sound-scraper/runs` - Method: POST with JSON payload - Auth: Bearer token in Authorization header - Payload: TikTok music URLs and scraping parameters """) st.write("**Test completed at**: " + datetime.now().strftime("%Y-%m-%d %H:%M:%S"))