[TASK-FIX-COMMENTED-BIND-PARAMS-V1.18.4] Pembersihan Parameter Bind dalam Komentar SQL, Normalisasi Undefined Parameter, dan Observabilitas Query Warnings
Modul: Pengangkut | Tipe: Bugfix / Hardening | Stage Saat Ini:
STAGING| Tanggal Dokumen: 2026-09-26
STAGING STAGE (UAT VERIFICATION)
Dokumen ini sedang berada dalam tahap STAGING. Perubahan logika backend, hardening adapter database, dan penambahan observabilitas query warnings telah diterapkan di lingkungan Staging (UAT) untuk proses verifikasi QA.
1. Informasi Tugas & Referensi Bisnis
Task ID / Nomor Referensi:
TASK-FIX-COMMENTED-BIND-PARAMS-V1.18.4Sumber Instruksi: Perbaikan Bug Raw SQL Comment Bind Parameter (
POST /summary/status), Normalisasi Parameter Undefined, dan Observabilitas Diagnostik DatabaseDokumen Referensi / Lampiran: Tidak ada
Task / Dependensi Terkait (Cross-Service): Tidak ada
Modul Terkait:
PengangkutService / Repository:
pengangkut-backendPIC Pengembang: Hermansyah Efendi
Latar Belakang & Ringkasan Kebutuhan: Pasca unifikasi connection pool ke Sequelize ORM pada core database adapter, ditemukan dua anomali kritis pada eksekusi raw SQL query di lingkungan Staging:
- Commented Bind Parameters: Eksekusi raw SQL query yang memuat parameter bind di dalam baris komentar SQL (seperti
-- AND flag = $4atau-- VALUES ($1, $2, $3, $4, $5)pada modelsuperset_summary_models.jsdi endpointPOST /summary/status) mengalami kegagalan dengan pesan error"Named bind parameter $4 has no value in the given object". Hal ini terjadi karena parser internal Sequelize memvalidasi parameter bind menggunakan regex global tanpa mengindahkan konteks sintaks komentar SQL. - Undefined Parameter Crash vs Native PG Compatibility: Pada driver native
pgterdahulu, nilai bind parameter bernilaiundefineddiperlakukan toleran oleh Postgres protocol (dikonversi/diabaikan sebagaiNULL). Namun, Sequelize ORM secara ketat melempar pengecualian saat mendeteksi nilai parameterundefined(contoh pada pemanggilangetManifestInaportContainersaat variabelidBongkarContainerbernilaiundefineddiwarta_kapal_controller.js:1687).
Untuk mengatasi kedua anomali tersebut secara menyeluruh tanpa merusak stabilitas puluhan file model lama, dilakukan perbaikan transparan pada adapter database (
app/connection.js) yang mencakup pembersihan bind parameter di dalam baris komentar via state-machine lexerstripCommentedBindParams, normalisasi otomatis parameterundefinedmenjadinull, pemasangan defensive guard pada query model item manifest, serta pembangunan suite observabilitas diagnostikquery-warning.service.jsbeserta endpoint admin/health/query-warnings.- Commented Bind Parameters: Eksekusi raw SQL query yang memuat parameter bind di dalam baris komentar SQL (seperti
Ruang Lingkup & Rincian Sub-Pekerjaan (Technical Scope & Milestones):
- [x] Implementasi lexer scanner
stripCommentedBindParamsdan helper modular (scanQuotedString,scanLineComment,scanBlockComment) diapp/connection.js - [x] Proteksi string literal kutip tunggal (
'...', escape''dan\') serta identifier kutip ganda ("...") agar tidak terhapus - [x] Integrasi pembersihan komentar SQL ke
normalizeQueryInputpada adapterapp/connection.js(berlaku untuk master, replica, dan transaction client) - [x] Normalisasi otomatis parameter bind bernilai
undefinedmenjadinullpadanormalizeQueryInputuntuk memulihkan kompatibilitas driverpg - [x] Pemasangan defensive guard check
if (!id_header) return [];padaitem_models.js(getManifestInaportContainerdangetManifestInaportCargo) guna mencegah query database tanpa ID - [x] Pembangunan service observabilitas
app/services/query-warning.service.jsdengan persistensi Redis list (monitoring:query_warnings, TTL 7 hari), in-memory fallback ring buffer, dan ekstraksi otomatis lokasi caller (extractCallerFromStack) - [x] Penyediaan endpoint admin
GET /health/query-warnings(dengan filterdatedanlimit) sertaDELETE /health/query-warningsdengan validasi Zod - [x] Standardisasi seluruh controller pada
app/controllers/health-controller.jsmengikuti konvensi arsitekturasyncHandler,res.sendJson, dan semantic errorInternalServerError - [x] Registrasi 2 variabel lingkungan baru
DB_QUERY_WARNINGS_KEYdanDB_QUERY_WARNINGS_MAXdiapp/utils/env-validator.jsdan.env.example - [x] Penambahan unit test dan integration test komprehensif (99 skenario passed) pada
tests/connection.test.js,tests/services/query-warning.service.test.js,tests/controllers/health-controller.test.js, dantests/validations/health.validation.test.js - [x] Bump versi rilis ke
v1.18.4padapackage.json,package-lock.json, dan dokumentasi changelog diCHANGELOG.md
- [x] Implementasi lexer scanner
2. Deployment Lifecycle
| # | Environment | Tanggal & Waktu | PIC | Status | Catatan |
|---|---|---|---|---|---|
| 1 | Development | 2026-09-26 12:27 | Hermansyah Efendi | DEPLOYED | Branch: development |
| 2 | Staging (UAT) | 2026-09-26 12:44 | Hermansyah Efendi | DEPLOYED | Release Candidate v1.18.4 via MR Staging. Co-deployed: [TASK-FIX-COMMENTED-BIND-PARAMS-V1.18.4, TASK-UNIFY-DB-POOL-AND-HEALTH-OBSERVABILITY, TASK-DRAGONFLYDB-MIGRATION, TASK-SPLIT-BILLING-202608, FEAT-MANUAL-BILLING-REQUEST, MAINT-SUPERSET-SUMMARY-SHIP-SYNC-SECURITY, REFACTOR-DEPRECATE-POST-STATUS-SERVICE-MIGRATION, TASK-PINO-LOGGER-REFACTOR, ND-304-LNSW.34-2026, ND-256-LNSW.34-2026, ND-183-LNSW.34-2026] |
| 3 | Production | — | — | PENDING | — |
Riwayat Update Log
| Tanggal | Environment | Perubahan | PIC |
|---|---|---|---|
| 2026-09-26 | Staging | Promosi ke STAGING (Release Candidate v1.18.4) | Hermansyah Efendi |
| 2026-09-26 | Development | Initial log created & updated with query warnings monitoring & undefined parameter normalization | Hermansyah Efendi |
3. Analisis Teknis & Rincian Perubahan
Alur Logika & Bisnis:
- State-Machine Lexer
stripCommentedBindParams:- Menghapus bind parameters (
$1, $2, ...atau$name) yang berada di dalam komentar baris (-- ...) maupun blok komentar (/* ... */) sebelum dikirim ke engine validasi Sequelize. - Melindungi literal string kutip tunggal (
'...', escape''dan\') dan identifier kutip ganda ("...") agar konten data bisnis (misal parameter pencarian berkode'ABC--123') tidak terpotong. - Membagi kompleksitas scanner ke sub-fungsi modular (
scanQuotedString,scanLineComment,scanBlockComment) sesuai batas ambang SonarQube (< 15).
- Menghapus bind parameters (
- Normalisasi Parameter
undefinedkenull:- Pada
normalizeQueryInput, arraybindParamsdipetakan secara terpusat: jika ditemukan elemen bernilaiundefined, nilainya otomatis dikonversi menjadinull. - Konversi ini mereplikasi perilaku native driver PostgreSQL (di mana
WHERE column = NULLmengevaluasi ke falsy tanpa error crash runtime).
- Pada
- Perekaman Diagnostik Query Warnings:
- Saat terdeteksi parameter
undefined, sistem membangkitkanError('Undefined parameter').stackdan memanggilrecordQueryWarning({ sql, paramIndex, errorStack }). - Helper
extractCallerFromStackmemfilter stack frame internal adapter (connection.js) dan Node.js internals untuk mengidentifikasi file dan nomor baris pemanggil asli di application layer (misal:app/controllers/warta_kapal_controller.js:1687). - Data warning disimpan ke Redis list (
monitoring:query_warnings, TTL 7 hari, max buffer 100 entri) dan dilengkapi in-memory ring-buffer fallback jika Redis tidak tersedia.
- Saat terdeteksi parameter
- Defensive Guard
item_models.js:- Pada fungsi
getManifestInaportContainerdangetManifestInaportCargo, ditambahkan pengecekanif (!id_header) return [];guna mengeliminasi roundtrip query database jika ID induk tidak tersedia.
- Pada fungsi
- Standardisasi Controller Kesehatan (
health-controller.js):- Seluruh endpoint observabilitas dibungkus oleh
asyncHandlerdan divalidasi via Zod middlewareparseWithZod. - Format respons diseragamkan menggunakan envelope standar
res.sendJson({ status: 200, message, data }).
- Seluruh endpoint observabilitas dibungkus oleh
- State-Machine Lexer
Perubahan Database (DDL / DML):
sql-- Tidak ada migration file yang terdeteksi. -- Perubahan murni internal engine level pada adapter query database (app/connection.js) dan service observabilitas.Konfigurasi Variabel Lingkungan Baru (.env.example & ENV_SCHEMA):
No Variabel Lingkungan Tipe / Default Kategori Deskripsi / Fungsi Teknis 1 DB_QUERY_WARNINGS_KEYString(monitoring:query_warnings)Query Diagnostics Key Redis List untuk buffer pencatatan query yang menerima bind parameter undefined.2 DB_QUERY_WARNINGS_MAXNumber(100)Query Diagnostics Kapasitas maksimum buffer riwayat warning query yang disimpan di Redis list. Kontrak API & Response Payload:
GET/health/query-warnings- Deskripsi: Mengambil riwayat query SQL yang terdeteksi menerima bind parameter
undefinedbeserta file caller aslinya. - Autentikasi / Guard:
Bearer Token(doIntrospection,requireAdmin) - Request Header / Query / Params:
Header:Authorization: Bearer <admin_token>Query:date=YYYY-MM-DD(opsional),limit=50(opsional)
- Response Payload (
200 OK):json{ "status": 200, "message": "Query warnings retrieved successfully", "data": { "status": "UP", "count": 1, "filter_date": "2026-09-26", "data": [ { "id": "warn-e05edfc1509f", "timestamp": "2026-09-26T12:27:49.000Z", "source_caller": "app/controllers/warta_kapal_controller.js:1687:25", "param_index": 1, "query_sample": "SELECT * FROM pengangkut.inaport_container WHERE id_header = $1", "normalized_to": "NULL" } ] } }
DELETE/health/query-warnings- Deskripsi: Membersihkan buffer Redis dan in-memory cache riwayat query warning.
- Autentikasi / Guard:
Bearer Token(doIntrospection,requireAdmin) - Request Header / Query / Params:
Header:Authorization: Bearer <admin_token>
- Response Payload (
200 OK):json{ "status": 200, "message": "Query warnings buffer cleared successfully", "data": { "cleared": true } }
- Deskripsi: Mengambil riwayat query SQL yang terdeteksi menerima bind parameter
4. Bukti Teknis (Technical Proof & Audit Trail)
- Repository:
pengangkut-backend - Branch Utama:
development - Merge Request / PR:
— (Draft MR v1.18.4)
Daftar Commit Terkait (Multi-Commit Trail)
| Commit ID | Branch | Author | Tanggal & Jam | Ringkasan Perubahan |
|---|---|---|---|---|
fe6f854f4879f1965b0b316581be13575860d479 | development | Hermansyah Efendi | 2026-09-26 12:27 | feat(monitoring): track undefined query parameters and standardize health controller |
cea0193a04a250ff6a62fb0c48bb7e643a652891 | development | Hermansyah Efendi | 2026-09-24 16:58 | chore(release): v1.18.4 |
0f1509814accb05bd0e1f30f0a1d6423466aa3b0 | development | Hermansyah Efendi | 2026-09-24 16:56 | fix(database): strip commented bind parameters from queries before validation |
Cuplikan Kode Kunci (Real Code)
// app/connection.js - Normalisasi Parameter Undefined & Perekaman Query Warning
export const normalizeQueryInput = (queryTextOrConfig, params) => {
let sql = queryTextOrConfig;
let bindParams = params;
if (typeof queryTextOrConfig === 'object' && queryTextOrConfig !== null) {
sql = queryTextOrConfig.text;
bindParams = queryTextOrConfig.values !== undefined ? queryTextOrConfig.values : params;
}
if (bindParams === undefined || bindParams === null) {
bindParams = [];
} else if (!Array.isArray(bindParams)) {
bindParams = [bindParams];
}
// Normalize undefined parameter values to null (matches native pg driver behavior)
// and record diagnostic warning for tracking in /health/query-warnings
let hasCapturedStack = false;
let errorStack = '';
bindParams = bindParams.map((param, index) => {
if (param === undefined) {
if (!hasCapturedStack) {
errorStack = new Error('Undefined parameter').stack || '';
hasCapturedStack = true;
}
recordQueryWarning({ sql, paramIndex: index + 1, errorStack }).catch(() => {
// Fire-and-forget: fail silently to prevent monitoring failures from interrupting queries
});
return null;
}
return param;
});
if (typeof sql === 'string') {
sql = stripCommentedBindParams(sql);
}
return { sql, bindParams };
};
// app/models/item_models.js - Defensive Guard untuk Mencegah Query Tanpa Header ID
const getManifestInaportContainer = async (id_header) => {
if (!id_header) {
return [];
}
let queryText = `
select
id.id_barang "idBarang",
id.id_manifest "idManifest"
from pengangkut.inaport_container ic
...`;
...
};5. Bukti Verifikasi Pengujian (QA / Unit Test / API Verification)
A. Verifikasi Unit Test (Development Stage)
- Test Suite Files:
tests/connection.test.jstests/services/query-warning.service.test.jstests/controllers/health-controller.test.jstests/validations/health.validation.test.js
- Status Eksekusi CLI:
PASSED(99 tests passed, 100% coverage pada modul terkait)
RUN v4.1.11 D:/WORK/LNSW/pengangkut-backend
✓ tests/connection.test.js (55 tests) 170ms
✓ tests/services/query-warning.service.test.js (13 tests) 497ms
✓ tests/controllers/health-controller.test.js (21 tests) 82ms
✓ tests/validations/health.validation.test.js (10 tests) 35ms
Test Files 4 passed (4)
Tests 99 passed (99)
Start at 12:30:24
Duration 3.00s (transform 1.01s, setup 0ms, import 3.72s, tests 928ms, environment 1ms)B. Panduan Pengujian QA & Verifikasi Staging (QA Playbook)
- Cakupan / Surface Pengujian:
API / Postman Only - Kebutuhan Akses / Role Akun:
PPJK / Agent Pengangkut (Bearer Token)danAdministrator (Role 00 / 07 / 20) - Hasil Pengujian UAT:
PENDING - Catatan QA: —
Skenario Pengujian QA (Test Scenarios)
Skenario 1 — Update Status Ringkasan (Happy Path dengan Komentar SQL Warisan):
- Pre-kondisi: Record
history_status_headertersedia pada database untuk service_number terkait. - Trigger / Request:
POST/summary/status?showAll=true&type=mjson{ "service_number": "101019B85DBC3", "service_type": "wkk", "status_id": 2, "status_code": 2, "status_info": "Updated via QA Test" } - Ekspektasi Response: HTTP
200 OK(atau201 Created), proses query database berhasil dieksekusi tanpa errorNamed bind parameter "$4" has no value in the given object. - Verifikasi Database / Log: Record baru bertambah pada tabel
pengangkut.history_status_detaildenganservice_type = 'wkk'danstatus_id = 2.
- Pre-kondisi: Record
Skenario 2 — Eksekusi Raw Query dengan String yang Mengandung Karakter Dash:
- Pre-kondisi: Parameter pencarian mengandung karakter tanda hubung ganda (misal kode registrasi
'ABC--123'). - Trigger / Request: Request query melalui endpoint yang memfilter kode tersebut.
- Ekspektasi Response: HTTP
200 OK, string literal'ABC--123'tidak terpotong oleh parser lexer dan nilai bind tetap utuh. - Verifikasi Database / Log: Tidak ada log database query error pada aplikasi.
- Pre-kondisi: Parameter pencarian mengandung karakter tanda hubung ganda (misal kode registrasi
Skenario 3 — Query dengan Parameter
undefined(Normalisasi kenull& Perekaman Warning):- Pre-kondisi: Server berjalan dan terhubung ke Redis.
- Trigger / Request: Request endpoint warta kapal yang memanggil
getManifestInaportContainertanpa menyertakan ID bongkar. - Ekspektasi Response: HTTP
200 OK(mengembalikan array kosong[]), tidak terjadi crash runtime 500. - Verifikasi Database / Log: Terbit log peringatan
WARN: Query warning: undefined parameter detected at position $1 and converted to NULL.
Skenario 4 — Monitoring & Pembersihan Buffer Query Warnings (Admin Only):
- Pre-kondisi: Token admin valid tersedia.
- Trigger / Request 1:
GET /health/query-warnings - Ekspektasi Response 1: HTTP
200 OK, memuat daftar objek warning besertasource_caller,param_index, danquery_sample. - Trigger / Request 2:
DELETE /health/query-warnings - Ekspektasi Response 2: HTTP
200 OK,cleared: true.
Bukti Response Verifikasi Staging (Diisi saat UAT)
{
"status": "PENDING",
"note": "Menunggu deployment ke environment Staging untuk eksekusi skenario uji QA."
}