| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| |
| """ |
| Vercel Serverless Function for counter application |
| Endpoint: /api/counter |
| """ |
| import json |
| from http.server import BaseHTTPRequestHandler |
| |
| from app import counter_app |
| |
| |
| class handler(BaseHTTPRequestHandler): |
| """Vercel Serverless Function handler. |
| |
| This class handles HTTP requests for the counter API endpoint. |
| Must inherit from BaseHTTPRequestHandler to work with Vercel's Python runtime. |
| """ |
| |
| def do_POST(self): |
| """Handle POST requests to increment counter. |
| |
| Expects JSON body with 'number' field indicating count limit. |
| Returns serialized application state on success. |
| """ |
| try: |
| # Read request body |
| content_length = int(self.headers.get("Content-Length", 0)) |
| body = self.rfile.read(content_length) |
| |
| # Parse JSON payload |
| data = json.loads(body.decode("utf-8")) |
| |
| # Extract parameter (equivalent to Lambda's event["body"]["number"]) |
| count_up_to = int(data.get("number", 0)) |
| |
| # Validate input |
| if count_up_to <= 0: |
| self.send_error_response(400, "number must be greater than 0") |
| return |
| |
| # Execute business logic (identical to Lambda implementation) |
| app = counter_app.application(count_up_to) |
| action, result, state = app.run(halt_after=["result"]) |
| |
| # Return success response with serialized state |
| self.send_json_response(200, state.serialize()) |
| |
| except json.JSONDecodeError: |
| self.send_error_response(400, "Invalid JSON format") |
| except ValueError as e: |
| self.send_error_response(400, f"Invalid number format: {str(e)}") |
| except KeyError as e: |
| self.send_error_response(400, f"Missing required field: {str(e)}") |
| except Exception as e: |
| # Log error for debugging |
| print(f"Error in counter handler: {str(e)}") |
| import traceback |
| |
| traceback.print_exc() |
| self.send_error_response(500, "Internal server error") |
| |
| def do_GET(self): |
| """Handle GET requests - not allowed. |
| |
| Returns 405 Method Not Allowed error. |
| """ |
| self.send_error_response(405, "Only POST method is allowed") |
| |
| def do_PUT(self): |
| """Handle PUT requests - not allowed. |
| |
| Returns 405 Method Not Allowed error. |
| """ |
| self.send_error_response(405, "Only POST method is allowed") |
| |
| def do_DELETE(self): |
| """Handle DELETE requests - not allowed. |
| |
| Returns 405 Method Not Allowed error. |
| """ |
| self.send_error_response(405, "Only POST method is allowed") |
| |
| def send_json_response(self, status_code, data): |
| """Send JSON response to client. |
| |
| Args: |
| status_code: HTTP status code |
| data: Response data (dict, list, or any JSON-serializable object) |
| """ |
| self.send_response(status_code) |
| self.send_header("Content-Type", "application/json") |
| self.end_headers() |
| |
| if isinstance(data, (dict, list)): |
| response_body = json.dumps(data, ensure_ascii=False) |
| else: |
| response_body = str(data) |
| |
| self.wfile.write(response_body.encode("utf-8")) |
| |
| def send_error_response(self, status_code, message): |
| """Send error response to client. |
| |
| Args: |
| status_code: HTTP error status code |
| message: Error message string to include in response body |
| """ |
| self.send_response(status_code) |
| self.send_header("Content-Type", "application/json") |
| self.end_headers() |
| |
| error_body = json.dumps({"error": message}) |
| self.wfile.write(error_body.encode("utf-8")) |