api_server.py

The Flask REST server that exposes the Evercycle API over HTTP.

Global Configuration

api_server.ALLOWED_ORIGINS: list[str]

List of origins permitted to make CORS requests.

api_server.token_expiry: datetime | None

Global variable tracking when the current access token expires.

api_server.refresh_token: str

Global variable holding the refresh token.

api_server.token_refresh_lock: threading.Lock

Reentrant lock used to prevent concurrent token refresh operations.

Functions

api_server.is_origin_allowed(origin)

Check if an origin is in the allowed list or matches a wildcard pattern.

Parameters:

origin (str) – The Origin header value.

Returns:

True if allowed, False otherwise.

api_server.add_cors_headers(response)

Flask after_request handler that injects CORS headers for allowed origins.

Parameters:

response – Flask Response object.

Returns:

Modified Response object.

api_server.handle_options(path)

Handle CORS preflight OPTIONS requests for any path.

Parameters:

path (str) – The request path (captured by Flask routing).

Returns:

Empty 200 response with CORS headers.

api_server.is_token_valid()

Check if the current token is still valid (with a 60-second buffer).

Returns:

True if valid, False otherwise.

api_server.refresh_auth_token()

Refresh the authentication token if needed.

Uses a lock to prevent race conditions. Attempts token refresh via the Evercycle refresh endpoint first. Falls back to re-authentication using config/auth-signin.json if refresh fails or no refresh token exists.

Returns:

True if a valid token is available, False otherwise.

Flask Routes

api_server.health_check()
GET /health

Health check endpoint. Always returns {"status": "ok", ...}.

api_server.authenticate()
POST /api/auth

Authenticate with the Evercycle API.

Expects JSON body with username and password. On success, stores tokens globally and in auth.properties.

api_server.list_apis()
GET /api/list

List all available APIs from the config directory.

api_server.call_api(api_name)
ANY /api/<api_name>

Dynamic proxy endpoint for any configured Evercycle API.

Special handling per API:

  • get-asset-id (GET or POST): extracts id, monkey-patches api_client.call_api to inject the asset ID into the URL path.

  • edit-asset-id (PUT): extracts id from body, merges body with defaults from config, overrides path to /v1/asset/{id}.

  • grade (PUT): extracts id and grade from body, merges remaining fields with defaults, overrides path to /v1/pricebook/{id}/{grade}.

  • create, single-confirm, create-request (POST): merges request JSON with default body from config, passes merged JSON to call_api.

Implements a retry loop (max 2 attempts). On auth failure (401, token expired, Unauthorized), it forces a token refresh and retries once.

Parameters:

api_name (str) – The API config name (e.g., get-all).

Returns:

JSON response with status, data or message, raw_output, and detailed_error on failure.

api_server.main()

Entry point for running the Flask development server from the command line.

Parses --port, --host, and --debug arguments and calls app.run().