Skip to main content

SSE ETL Component & Integration Guide

This document provides a technical guide for developers on the three-stage import system. It covers the purpose of each Vue component, their API (props and events), and the required Laravel backend setup for Server-Sent Events (SSE) interaction.

(English)

Part 1: Component Architecture Overview

The import process is managed by three distinct Vue components that create a robust, three-stage user experience:
  1. AttachmentLinkUpload.vue: The initial file uploader. It handles the first stage (Upload) and, when configured, triggers the second stage.
  2. SseProgressBar.vue: A reusable component that provides real-time progress for any long-running background task (Buffering and Commit stages).
  3. EtlImportModal.vue: The main orchestrator. It controls the overall workflow, showing the correct component for each stage and managing the state of the import process.

Part 2: Component Documentation

1. SseProgressBar.vue

A general-purpose component for displaying real-time progress from an SSE stream.

Purpose

To connect to a server endpoint, listen for progress events, and display the status and percentage to the user.

Props

Prop NameTypeRequiredDefaultDescription
titleStringYes''The main title text to display above the progress bar (e.g., “Buffering Data”).
baseUrlStringYes''The base URL of the SSE endpoint (e.g., /api/etl/buffer-progress).
processIdStringYes''The unique ID for the process to track (e.g., the importId).

Events

Event NamePayloadDescription
completeundefinedFired when the SSE stream sends a “Completed” status and closes.
errorundefinedFired if the connection to the SSE stream is lost or fails.

Usage Example

<sse-progress-bar
    ref="myProgress"
    title="Processing Your Request"
    base-url="/api/my-process/progress"
    process-id="unique-job-id-123"
    @complete="onProcessFinished"
/>
// To start listening:
this.$refs.myProgress.start();

2. AttachmentLinkUpload.vue

A flexible file uploader that can operate in a standard mode or in a background-processing mode with SSE.

Purpose

To handle the initial file upload. In “SSE mode,” it uploads the file and then immediately hands off to the SseProgressBar to track the background job it just triggered.

Key Props for ETL Integration

Prop NameTypeRequiredDefaultDescription
uploadurlStringYes''The backend endpoint to which the file will be uploaded.
extraDataObjectNo{}Additional data to send along with the file (e.g., schema_name).
useSseBooleanNofalseCrucial. If true, enables the SSE workflow.
sseProgressUrlStringIf useSse''The base URL for the SSE progress endpoint.
sseTitleStringNo'Processing...'The title to pass to the internal SseProgressBar.
acceptedFilesStringNo*.*A string of accepted file types (e.g., .xlsx,.csv).

Key Events for ETL Integration

Event NamePayloadDescription
completeObjectFired when the entire process (upload + SSE) is done. Payload is { importId: '...' }.
errorStringFired if the upload or the SSE process fails.

Usage Example

<attachment-link-upload
    ref="uploader"
    uploadurl="/api/etl/upload"
    :extra-data="{ schema_name: 'user_schema' }"
    :use-sse="true"
    sse-progress-url="/api/etl/buffer-progress"
    sse-title="Parsing and Buffering File"
    @complete="onFileBuffered"
    @error="onUploadFailed"
/>

3. EtlImportModal.vue

The main modal component that orchestrates the entire import flow.

Purpose

To provide a complete UI for the three-stage import process by managing and displaying the other two components based on the current stage.

Props

Prop NameTypeRequiredDefaultDescription
schemaNameStringYes''The name of the schema to use (e.g., user_schema).
baseUrlStringYes''The base prefix for all API routes (e.g., /api/etl).

Public Methods

MethodDescription
show()Programmatically opens the modal.
hide()Programmatically closes the modal.

Events

Event NamePayloadDescription
hiddenundefinedFired when the modal is fully closed. Used to trigger data refreshes.
shownundefinedFired when the modal is fully visible.

Part 3: Backend Setup for SSE Interaction

The frontend components rely on specific backend endpoints to function correctly.

1. Required Routes

Ensure these routes are defined in your routes/api.php file.
// routes/api.php
use App\Http\Controllers\EtlController;

Route::prefix('etl')->group(function () {
    // Stage 1: Receives the file, returns an `importId`, and dispatches the buffering job.
    Route::post('upload', [EtlController::class, 'upload']);

    // Stage 2: Streams progress for the buffering job.
    Route::get('buffer-progress/{importId}', [EtlController::class, 'bufferProgress']);

    // Preview Stage: Gets a preview of buffered data.
    Route::get('preview/{importId}', [EtlController::class, 'preview']);

    // Stage 3: Starts the commit job.
    Route::post('commit/{importId}', [EtlController::class, 'commit']);

    // Stage 3: Streams progress for the commit job.
    Route::get('commit-progress/{importId}', [EtlController::class, 'commitProgress']);

    // Utility for downloading templates.
    Route::get('template/{schema}', [EtlController::class, 'downloadTemplate']);
});

2. Controller Logic for SSE

The controller needs methods to handle the SSE connections. A helper function can reduce code duplication. File: app/Http/Controllers/EtlController.php
<?php
namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;
use Symfony\Component\HttpFoundation\StreamedResponse;
// ... other imports

class EtlController extends Controller
{
    // ... upload, commit, preview methods ...

    public function bufferProgress(string $importId)
    {
        return $this->streamProgress("buffer_progress_{$importId}");
    }

    public function commitProgress(string $importId)
    {
        return $this->streamProgress("progress_{$importId}");
    }

    private function streamProgress(string $cacheKey)
    {
        return new StreamedResponse(function () use ($cacheKey) {
            header('Content-Type: text/event-stream');
            header('Cache-Control: no-cache');
            header('Connection: keep-alive');
            header('X-Accel-Buffering: no'); // Important for Nginx

            while (true) {
                $progress = Cache::get($cacheKey);
                if ($progress) {
                    echo "data: " . json_encode($progress) . "\n\n";
                    ob_flush(); flush();
                }
                // Stop the loop if the job is done or the client disconnects
                if (($progress['status'] ?? '') === 'Completed' || connection_aborted()) {
                    break;
                }
                sleep(1); // Wait a second before checking the cache again
            }
        });
    }
}

3. Job Progress Reporting

Your background jobs must periodically update a value in the cache that the controller can read. Example from ProcessImportFileJob.php:
private function updateProgress(int $processed, int $total, string $status): void
{
    // The cache key must match what the controller is listening for.
    Cache::put("buffer_progress_{$this->importId}", [
        'processed' => $processed,
        'total' => $total,
        'status' => $status
    ], now()->addHour());
}


(Bahasa Indonesia)

Bagian 1: Gambaran Arsitektur Komponen

Proses impor dikelola oleh tiga komponen Vue yang berbeda untuk menciptakan pengalaman pengguna tiga tahap yang tangguh:
  1. AttachmentLinkUpload.vue: Pengunggah file awal. Komponen ini menangani tahap pertama (Upload) dan, saat dikonfigurasi, akan memicu tahap kedua.
  2. SseProgressBar.vue: Komponen yang dapat digunakan kembali yang menyediakan progres real-time untuk tugas latar belakang apa pun yang berjalan lama (tahap Buffering dan Commit).
  3. EtlImportModal.vue: Orkestrator utama. Komponen ini mengontrol alur kerja secara keseluruhan, menampilkan komponen yang benar untuk setiap tahap, dan mengelola status proses impor.

Bagian 2: Dokumentasi Komponen

1. SseProgressBar.vue

Komponen serbaguna untuk menampilkan progres real-time dari stream Server-Sent Events (SSE).

Tujuan

Untuk terhubung ke endpoint server, mendengarkan event progres, dan menampilkan status serta persentase kepada pengguna.

Props

Nama PropTipeWajibDefaultDeskripsi
titleStringYa''Teks judul utama yang ditampilkan di atas progress bar (misalnya, “Memproses Data”).
baseUrlStringYa''URL dasar dari endpoint SSE (misalnya, /api/etl/buffer-progress).
processIdStringYa''ID unik untuk proses yang akan dilacak (misalnya, importId).

Event

Nama EventPayloadDeskripsi
completeundefinedDijalankan saat stream SSE mengirim status “Completed” dan ditutup.
errorundefinedDijalankan jika koneksi ke stream SSE terputus atau gagal.

2. AttachmentLinkUpload.vue

Pengunggah file fleksibel yang dapat beroperasi dalam mode standar atau mode pemrosesan latar belakang dengan SSE.

Tujuan

Untuk menangani unggahan file awal. Dalam “mode SSE”, komponen ini mengunggah file dan segera menyerahkannya ke SseProgressBar untuk melacak tugas latar belakang yang baru saja dipicu.

Props Kunci untuk Integrasi ETL

Nama PropTipeWajibDefaultDeskripsi
uploadurlStringYa''Endpoint backend tempat file akan diunggah.
extraDataObjectTidak{}Data tambahan untuk dikirim bersama file (misalnya, schema_name).
useSseBooleanTidakfalsePenting. Jika true, mengaktifkan alur kerja SSE.
sseProgressUrlStringJika useSse''URL dasar untuk endpoint progres SSE.
sseTitleStringTidak'Processing...'Judul yang akan diteruskan ke SseProgressBar internal.
acceptedFilesStringTidak*.*String tipe file yang diterima (misalnya, .xlsx,.csv).

Event Kunci untuk Integrasi ETL

Nama EventPayloadDeskripsi
completeObjectDijalankan saat seluruh proses (unggah + SSE) selesai. Payload-nya adalah { importId: '...' }.
errorStringDijalankan jika proses unggah atau SSE gagal.

3. EtlImportModal.vue

Komponen modal utama yang mengatur seluruh alur impor.

Tujuan

Menyediakan UI lengkap untuk proses impor tiga tahap dengan mengelola dan menampilkan dua komponen lainnya berdasarkan stage saat ini.

Props

Nama PropTipeWajibDefaultDeskripsi
schemaNameStringYa''Nama skema yang akan digunakan (misalnya, user_schema).
baseUrlStringYa''Awalan dasar untuk semua rute API (misalnya, /api/etl).

Method Publik

MethodDeskripsi
show()Membuka modal secara terprogram.
hide()Menutup modal secara terprogram.

Event

Nama EventPayloadDeskripsi
hiddenundefinedDijalankan saat modal sepenuhnya tertutup. Digunakan untuk memicu refresh data.
shownundefinedDijalankan saat modal sepenuhnya terlihat.

Bagian 3: Pengaturan Backend untuk Interaksi SSE

Komponen frontend bergantung pada endpoint backend tertentu agar dapat berfungsi dengan benar.

1. Rute yang Diperlukan

Pastikan rute-rute ini didefinisikan dalam file routes/api.php Anda.
// routes/api.php
use App\Http\Controllers\EtlController;

Route::prefix('etl')->group(function () {
    Route::post('upload', [EtlController::class, 'upload']);
    Route::get('buffer-progress/{importId}', [EtlController::class, 'bufferProgress']);
    Route::get('preview/{importId}', [EtlController::class, 'preview']);
    Route::post('commit/{importId}', [EtlController::class, 'commit']);
    Route::get('commit-progress/{importId}', [EtlController::class, 'commitProgress']);
    Route::get('template/{schema}', [EtlController::class, 'downloadTemplate']);
});

2. Logika Controller untuk SSE

Controller memerlukan method untuk menangani koneksi SSE. Fungsi pembantu dapat mengurangi duplikasi kode. File: app/Http/Controllers/EtlController.php
<?php
namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;
use Symfony\Component\HttpFoundation\StreamedResponse;

class EtlController extends Controller
{
    public function bufferProgress(string $importId) {
        return $this->streamProgress("buffer_progress_{$importId}");
    }

    public function commitProgress(string $importId) {
        return $this->streamProgress("progress_{$importId}");
    }

    private function streamProgress(string $cacheKey)
    {
        return new StreamedResponse(function () use ($cacheKey) {
            // ... (logika header dan loop seperti di contoh Inggris) ...
            while (true) {
                $progress = Cache::get($cacheKey);
                if ($progress) {
                    echo "data: " . json_encode($progress) . "\n\n";
                    ob_flush(); flush();
                }
                if (($progress['status'] ?? '') === 'Completed' || connection_aborted()) break;
                sleep(1);
            }
        });
    }
}

3. Pelaporan Progres dari Job

Job latar belakang Anda harus secara berkala memperbarui nilai di cache yang dapat dibaca oleh controller. Contoh dari ProcessImportFileJob.php:
private function updateProgress(int $processed, int $total, string $status): void
{
    // Kunci cache harus cocok dengan yang didengarkan oleh controller.
    Cache::put("buffer_progress_{$this->importId}", [
        'processed' => $processed,
        'total' => $total,
        'status' => $status
    ], now()->addHour());
}