nhance/public/dev_logs/2026-04-02_job_status_service_plan.md

7.7 KiB

Job Status Service Plan

Objective

Create a separate reusable library/service that accepts only a job name and returns:

  • current status
  • parsed response

The service should use JobModel (jobs table) and align with existing queue statuses used in JobWorker (queued, running, done, failed).

Current Context

  • JobWorker writes job execution data into jobs:
    • updates status
    • updates run_time
    • writes JSON-encoded response
  • JobModel is a basic model mapped to jobs table with relevant fields already allowed.

Proposed Design

  1. Create a dedicated library class:

    • Path: app/Libraries/JobStatusService.php
    • Responsibility: read latest job record by name, normalize output payload.
  2. Public API of library:

    • Method: getJobStatusByName(string $jobName): array
    • Input: job name only
    • Output contract (example):
      • success (bool)
      • job_name (string)
      • status (string|null)
      • response (array|string|null)
      • job_id (int|null)
      • uuid (string|null)
      • run_time (float|int|null)
      • message (string)
  3. Query behavior:

    • Search in jobs table by exact name = $jobName
    • Order by latest execution (id DESC) and fetch first row
    • If no record exists, return success=false with clear message.
  4. Response normalization:

    • Attempt json_decode(response, true) when response is non-empty.
    • If decode succeeds, return decoded array/object structure.
    • If decode fails or is plain text, return raw response string.
    • Keep response key consistent regardless of format.
  5. Validation and safety:

    • Trim input name and reject empty values.
    • Avoid exceptions leaking to caller; wrap unexpected errors and return structured failure response.

Integration Plan

  1. Keep the library independent from controllers for reuse.
  2. Optional usage points:
    • API controller endpoint can consume the library and return JSON to frontend.
    • CLI/debug scripts can consume the same library.
  3. No changes required in JobWorker queue execution flow for this feature.

Suggested Controller Endpoint (Optional Next Step)

If needed after library creation:

  • Add endpoint method (example in ApiServiceController) accepting job_name.
  • Validate job_name.
  • Call JobStatusService::getJobStatusByName($jobName).
  • Return JSON response with appropriate HTTP code:
    • 200 for found
    • 404 for not found
    • 422 for invalid input
    • 500 for unexpected server errors

Edge Cases

  • Multiple jobs with same name: return latest record only.
  • queued/running jobs may have empty response; return response=null.
  • Failed jobs may contain JSON error bundle written by JobWorker; return decoded details when valid JSON.
  • Done jobs may return scalar/string payload; preserve as-is when not JSON.

Testing Plan

  1. Unit-level checks for service method:
    • Valid job name with done status and JSON response
    • Valid job name with failed status and JSON error response
    • Valid job name with non-JSON response
    • Valid job name not found
    • Empty job name input
  2. Integration checks (optional):
    • Trigger known queue job, then fetch status by name and verify status/response shape.

Deliverables

  1. app/Libraries/JobStatusService.php with getJobStatusByName() method.
  2. (Optional) Controller method + route for API access.
  3. Minimal usage example in developer notes or inline docblock.

Implementation Steps (Execution Order)

  1. Create app/Libraries/JobStatusService.php.
  2. Add constructor or internal setup to initialize JobModel.
  3. Implement input guard:
    • trim $jobName
    • return failure payload for empty input.
  4. Fetch latest row by name:
    • where('name', $jobName)->orderBy('id', 'DESC')->first()
  5. Build normalized response payload:
    • status, job_id, uuid, run_time, response.
  6. Decode response safely:
    • if empty -> null
    • if valid JSON -> decoded array/object
    • else -> raw string.
  7. Add broad try/catch (\Throwable $e) and return safe error contract.
  8. (Optional) Wire endpoint in ApiServiceController and route mapping.
  9. Verify manually with one known queued/running job and one completed/failed job.

Payload Contract (Final)

[
  'success'  => true|false,
  'job_name' => (string),
  'status'   => (string|null),      // queued|running|done|failed|null
  'response' => (array|string|null),
  'job_id'   => (int|null),
  'uuid'     => (string|null),
  'run_time' => (float|int|null),
  'message'  => (string),
]

Error Handling Rules

  • Invalid input (job_name empty after trim):
    • success=false, message='Job name is required.'
  • Not found:
    • success=false, message='No job record found for given name.'
  • Unexpected exception:
    • success=false, message='Unable to fetch job status right now.'
    • keep internals/logging server-side only, do not leak stack trace in API response.

Optional Route/Endpoint Mapping

If API exposure is required, prefer a read-only endpoint:

  • GET /api/job-status?job_name={name} or
  • POST /api/job-status with body { "job_name": "..." }

Response guidance:

  • 200: success=true
  • 404: not found
  • 422: validation failure
  • 500: unexpected server failure

Acceptance Criteria

  • Given an existing job name, service returns latest row by descending id.
  • status always reflects one of existing worker statuses or null.
  • response is decoded when valid JSON; otherwise preserved as raw string.
  • Empty input never triggers DB query and returns validation failure.
  • Service never throws unhandled exception to caller.

Non-Goals (Current Scope)

  • No change to existing queue insert/update logic in JobWorker.
  • No migration/schema change in jobs table.
  • No polling/real-time websocket updates in this phase.

Rollout Notes

  1. Implement service first and test in isolation.
  2. Add endpoint only if a frontend or external consumer needs it immediately.
  3. Keep endpoint backward-compatible by not changing existing job payload fields.
  4. Add lightweight log entry only for unexpected exceptions to aid debugging.

Completed Tasks (Updated)

  • Created app/Libraries/JobStatusService.php.
  • Added getJobStatusByName(string $jobName): array.
  • Implemented empty input validation with structured failure response.
  • Implemented latest-record lookup by exact name and id DESC.
  • Implemented response normalization (null / decoded JSON / raw string).
  • Added safe exception handling and server-side error logging.
  • Added API endpoint method ApiServiceController::jobStatus.
  • Added routes:
    • GET /jobStatus
    • POST /jobStatus
  • Manual verification against queued/running/done/failed sample jobs (pending).

Route Migration Plan (ApiServiceController -> TestingController)

Objective

Move the jobStatus endpoint ownership from ApiServiceController to TestingController while keeping URL contract unchanged.

Steps

  1. Add JobStatusService import in TestingController.
  2. Add jobStatus() method in TestingController with the same input/output behavior.
  3. Remove jobStatus() method from ApiServiceController to avoid duplicate ownership.
  4. Update route mapping in Routes.php:
    • GET /jobStatus -> TestingController::jobStatus
    • POST /jobStatus -> TestingController::jobStatus
  5. Run syntax checks for updated controller and routes.

Migration Tasks (Updated)

  • Added use App\Libraries\JobStatusService; in TestingController.
  • Added TestingController::jobStatus() (GET/POST + JSON fallback).
  • Removed ApiServiceController::jobStatus().
  • Repointed both jobStatus routes to TestingController.
  • Manual endpoint validation via GET and POST with sample job_name values (pending).