Operational Flows ================= This section details the step-by-step operational flows for the most common transactions within the Evercycle API system. Flow 1: Authenticate and Store Tokens ------------------------------------- **Actors:** HTTP client, Flask server, Evercycle backend 1. Client sends ``POST /api/auth`` with JSON body containing ``username`` and ``password``. 2. ``api_server.py`` validates that both fields are present; returns 400 if not. 3. ``api_server.py`` calls ``api_client.authenticate(username, password)``. 4. ``evercycle_api.py`` builds the payload and POSTs to ``https://api.evercycle.io/v1/auth/signin``. 5. On success, the backend returns an ``AuthenticationResult`` with: * ``AccessToken`` * ``IdToken`` * ``RefreshToken`` 6. ``evercycle_api.py`` writes these three tokens to ``evercycle-api/auth.properties``. 7. ``api_server.py`` updates global variables: * ``refresh_token`` ← RefreshToken * ``token_expiry`` ← now + 3600 seconds 8. Flask session is marked authenticated. 9. Response ``200 OK`` with status "success" is returned to the client. Flow 2: Call a Protected API with Valid Token --------------------------------------------- **Actors:** HTTP client, Flask server, Evercycle backend 1. Client sends a request to ``/api/get-all`` with method GET. 2. ``call_api('get-all')`` is invoked. 3. ``refresh_auth_token()`` is called. 4. ``is_token_valid()`` checks ``token_expiry`` against ``now + 60s``. 5. Token is valid → function returns ``True`` immediately. 6. ``api_server.py`` prepares parameters: * For GET, parameters come from ``request.args`` (query string). * No special path-parameter handling needed for ``get-all``. 7. ``api_client.call_api('get-all')`` is executed. 8. Config file ``config/get-all.json`` is loaded. 9. Auth headers are injected: * ``access-token`` ← ``access_token`` from ``auth.properties`` * ``Authorization`` ← ``id_token`` from ``auth.properties`` 10. HTTP GET is dispatched to ``https://api.evercycle.io/v1/asset``. 11. Response JSON is returned to the client wrapped in: ``{"status": "success", "data": }`` Flow 3: Call an API with Expired Token (Auto-Refresh) ----------------------------------------------------- **Actors:** HTTP client, Flask server, Evercycle backend 1. Client sends ``GET /api/get-all``. 2. ``call_api('get-all')`` begins. 3. ``refresh_auth_token()`` detects that ``token_expiry`` has passed. 4. Server attempts to POST to ``https://api.evercycle.io/v1/auth/refresh`` with the stored ``refresh_token``. 5. If refresh succeeds: * New tokens are extracted. * ``auth.properties`` is overwritten. * ``token_expiry`` is updated. * Flow continues as in Flow 2. 6. If refresh fails: * Server falls back to re-authentication. * It reads ``config/auth-signin.json`` for username/password. * Calls ``api_client.authenticate(username, password)``. * On success, updates globals and proceeds. * On failure, returns ``401 Unauthorized`` to the client. Flow 4: Get Asset by ID (Runtime Path Patching) ----------------------------------------------- **Actors:** HTTP client, Flask server 1. Client sends ``GET /api/get-asset-id?id=12345``. 2. ``call_api('get-asset-id')`` is invoked. 3. Authentication check passes (Flow 2/3 logic). 4. Because ``api_name == 'get-asset-id'``, the server enters special handling: * Extracts ``asset_id`` from ``request.args.get('id')``. * Overrides ``api_info['path']`` to ``/v1/asset/{asset_id}``. * Dynamically monkey-patches ``api_client.call_api`` with ``patched_call_api``. 5. The patched function ignores the normal config-driven URL construction and directly builds ``https://api.evercycle.io/v1/asset/12345``. 6. It injects headers and calls ``requests.get``. 7. The response is parsed and returned to the client. 8. Note: the monkey-patch is temporary for the duration of that single call. Flow 5: Create a New Asset -------------------------- **Actors:** HTTP client, Flask server 1. Client sends ``POST /api/create`` with JSON body fields. 2. ``call_api('create')`` begins. 3. Authentication is verified. 4. Server loads ``config/create.json`` to obtain default body values. 5. Request JSON body is merged on top of the defaults: ``merged_data = default_body.copy(); merged_data.update(request.json)`` 6. The merged JSON string is passed as a parameter to ``api_client.call_api``. 7. ``api_client.call_api`` parses the JSON and sets ``user_config['body']``. 8. HTTP POST is dispatched to ``https://api.evercycle.io/v1/asset/create``. 9. Response is returned to the client. Flow 6: Edit Asset (PUT with Path Parameter) -------------------------------------------- **Actors:** HTTP client, Flask server 1. Client sends ``PUT /api/edit-asset-id`` with JSON body containing ``id`` and any fields to update. 2. ``call_api('edit-asset-id')`` begins. 3. Authentication is verified. 4. Server extracts ``asset_id`` from body ``id`` field. 5. It loads ``config/edit-asset-id.json`` for default body values. 6. Request body (minus ``id``) is merged into defaults. 7. ``api_info['path']`` and ``api_info['url']`` are both overridden to ``/v1/asset/{asset_id}``. 8. ``api_client.call_api`` receives ``asset_id`` and the merged JSON string. 9. The path parameter ``{id}`` is substituted in the URL. 10. HTTP PUT is dispatched. Flow 7: Grade Update (PUT with Multiple Path Parameters) -------------------------------------------------------- **Actors:** HTTP client, Flask server 1. Client sends ``PUT /api/grade`` with JSON body containing ``id``, ``grade``, and optionally ``price``. 2. ``call_api('grade')`` begins. 3. Server extracts ``asset_id`` and ``grade_name`` from the body. 4. It loads ``config/grade.json`` for defaults. 5. Body fields other than ``id`` and ``grade`` are merged into defaults. 6. ``api_info['path']`` and ``api_info['url']`` are overridden to ``/v1/pricebook/{asset_id}/{grade_name}``. 7. ``api_client.call_api`` substitutes both path parameters. 8. HTTP PUT is dispatched to the constructed URL. Flow 8: CLI Auth and Config Generation -------------------------------------- **Actors:** Terminal user, ``api_runner.py``, filesystem 1. User runs ``python3 api_runner.py generate`` inside ``evercycle-api/``. 2. ``ApiRunner.load_endpoints()`` reads all ``*.md`` files in the directory. 3. Each markdown file is parsed by ``parse_markdown_file()``: * First line → ``path`` * Second line → ``description`` * Regex search → ``method`` and ``url`` * Table sections → ``headers``, ``parameters``, ``body_params`` 4. For each endpoint, a JSON config file is written to ``config/{api_name}.json`` with empty/default values for headers, path_params, query_params, and body. 5. User edits the JSON config files to set desired values. 6. User runs ``python3 api_runner.py run auth-signin``. 7. On success, ``_handle_auth_signin_response()`` extracts tokens and writes ``auth.properties``. 8. ``_update_config_files_with_tokens()`` iterates all other JSON configs and injects the new tokens into their ``headers`` objects. Flow 9: Interactive Menu Runner ------------------------------- **Actors:** Terminal user, ``evercycle_api_runner.py`` 1. User runs ``python3 evercycle_api_runner.py``. 2. ``EvercycleApiRunner`` loads all markdown specs. 3. A numbered menu is displayed. 4. User selects an endpoint. 5. ``prepare_api_call()`` prompts for: * Header values * Path parameter values * Query parameter values * Body parameter values (with JSON input for objects/arrays) 6. Types are coerced (number, boolean) based on the parameter type declared in the markdown. 7. ``ApiClient.call_endpoint()`` is invoked with the collected data. 8. Response status, headers, and body are printed to the terminal.