From c8d06e02488116a3b87eb157ac7b3dae85abb05d Mon Sep 17 00:00:00 2001 From: Dita Aji Pratama Date: Sat, 13 Jun 2026 12:20:05 +0700 Subject: [PATCH] Updating app and README.md --- README.md | 430 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ app.py | 39 +++-- 2 files changed, 460 insertions(+), 9 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..058ea15 --- /dev/null +++ b/README.md @@ -0,0 +1,430 @@ +# API Playground + +Proyek sederhana untuk playground API enrollment berbasis Python Bottle dan SQLite. + +Aplikasi ini menyediakan halaman UI playground dan API endpoint untuk mengelola data enrollment secara CRUD. + +--- + +## ๐Ÿš€ Fitur + +- Server API berbasis Bottle +- Database SQLite +- UI playground untuk testing endpoint langsung dari browser +- Autentikasi sederhana menggunakan Bearer Token +- Validasi nilai `type` dan `service` + +--- + +## ๐Ÿ“‹ Requirements + +Pastikan sudah terinstall: + +- Python 3 +- pip3 + +--- + +## ๐Ÿ› ๏ธ Instalasi + +### 1. Clone / Masuk ke Folder Project + +```bash +cd api-playground +``` + +### 2. Buat Virtual Environment + +Disarankan menggunakan virtual environment agar dependency tidak bercampur dengan environment global. + +```bash +python3 -m venv venv +``` + +### 3. Aktifkan Virtual Environment + +Di Linux/macOS: + +```bash +source venv/bin/activate +``` + +Di Windows: + +```bash +venv\Scripts\activate +``` + +### 4. Install Dependencies + +```bash +pip install -r requirements.txt +``` + +--- + +## โ–ถ๏ธ Menjalankan Aplikasi + +Jalankan server dengan perintah: + +```bash +python app.py +``` + +Server akan berjalan di: + +```bash +http://localhost:8080 +``` + +atau: + +```bash +http://0.0.0.0:8080 +``` + +Jika kode diubah, server akan otomatis restart karena menggunakan mode `debug=True` dan `reloader=True`. + +--- + +## ๐Ÿ” Autentikasi + +Semua request ke endpoint API wajib menyertakan header: + +```bash +Authorization: Bearer secret123 +``` + +Token default disimpan di `app.py` pada variabel: + +```python +API_TOKEN = "secret123" +``` + +Jika tidak menyertakan token atau token salah, API akan mengembalikan response `401 Unauthorized`. + +--- + +## ๐Ÿ“ก Daftar Endpoint + +| Endpoint | Method | Auth | Fungsi | +|---|---|---|---| +| `/` | `GET` | Tidak | Halaman playground UI | +| `/api/enroll` | `GET` | Ya | List semua enrollment | +| `/api/enroll?id=` | `GET` | Ya | Mendapatkan detail enrollment berdasarkan ID | +| `/api/enroll` | `POST` | Ya | Tambah, edit, atau hapus enrollment | + +--- + +## ๐Ÿงฌ Database Schema + +Tabel: `enroll` + +| Kolom | Tipe | Keterangan | +|---|---|---| +| `id` | INTEGER | Primary key, auto-increment | +| `type` | TEXT | Tipe enrollment, hanya boleh `Annually` atau `Monthly` | +| `service` | TEXT | Paket layanan, hanya boleh `Lite`, `Value`, atau `Pro` | +| `user` | TEXT | Nama pengguna | + +--- + +## ๐Ÿงช Penggunaan Endpoint + +### 1. List Semua Enrollment + +**Request** + +```bash +curl -H "Authorization: Bearer secret123" \ + http://localhost:8080/api/enroll +``` + +**Response** + +```json +{ + "data": [ + { + "id": 2, + "type": "Monthly", + "service": "Lite", + "user": "bob" + }, + { + "id": 1, + "type": "Annually", + "service": "Pro", + "user": "alice" + } + ] +} +``` + +--- + +### 2. Detail Enrollment Berdasarkan ID + +**Request** + +```bash +curl -H "Authorization: Bearer secret123" \ + "http://localhost:8080/api/enroll?id=1" +``` + +**Response** + +```json +{ + "data": { + "id": 1, + "type": "Annually", + "service": "Pro", + "user": "alice" + } +} +``` + +Jika ID tidak ditemukan: + +```json +{ + "error": "not found" +} +``` + +--- + +### 3. Tambah Enrollment Baru + +Endpoint `POST /api/enroll` dapat menerima data dalam format form-urlencoded atau JSON. + +#### Menggunakan Form Data + +```bash +curl -X POST http://localhost:8080/api/enroll \ + -H "Authorization: Bearer secret123" \ + -d "type=Annually" \ + -d "service=Pro" \ + -d "user=alice" +``` + +#### Menggunakan JSON + +```bash +curl -X POST http://localhost:8080/api/enroll \ + -H "Authorization: Bearer secret123" \ + -H "Content-Type: application/json" \ + -d '{ + "type": "Annually", + "service": "Pro", + "user": "alice" + }' +``` + +**Response** + +```json +{ + "message": "created", + "id": 1 +} +``` + +--- + +### 4. Edit Enrollment + +Untuk melakukan edit, kirim field `id` bersama data baru. + +#### Menggunakan Form Data + +```bash +curl -X POST http://localhost:8080/api/enroll \ + -H "Authorization: Bearer secret123" \ + -d "id=1" \ + -d "type=Monthly" \ + -d "service=Value" \ + -d "user=bob" +``` + +#### Menggunakan JSON + +```bash +curl -X POST http://localhost:8080/api/enroll \ + -H "Authorization: Bearer secret123" \ + -H "Content-Type: application/json" \ + -d '{ + "id": 1, + "type": "Monthly", + "service": "Value", + "user": "bob" + }' +``` + +**Response** + +```json +{ + "message": "updated", + "id": 1 +} +``` + +--- + +### 5. Hapus Enrollment + +Untuk menghapus data, gunakan `POST /api/enroll` dengan field: + +- `id` +- `action=remove` + +#### Menggunakan Form Data + +```bash +curl -X POST http://localhost:8080/api/enroll \ + -H "Authorization: Bearer secret123" \ + -d "id=1" \ + -d "action=remove" +``` + +#### Menggunakan JSON + +```bash +curl -X POST http://localhost:8080/api/enroll \ + -H "Authorization: Bearer secret123" \ + -H "Content-Type: application/json" \ + -d '{ + "id": 1, + "action": "remove" + }' +``` + +**Response** + +```json +{ + "message": "removed", + "id": 1 +} +``` + +--- + +## ๐Ÿง  Logika `POST /api/enroll` + +```text +POST /api/enroll +โ”‚ +โ”œโ”€โ”€ action = "remove" โ†’ hapus data, wajib menyertakan id +โ”œโ”€โ”€ ada field id โ†’ edit data +โ””โ”€โ”€ tidak ada field id โ†’ tambah data baru +``` + +Untuk tambah dan edit, field berikut wajib dikirim: + +| Field | Wajib | Keterangan | +|---|---|---| +| `type` | Ya | Harus `Annually` atau `Monthly` | +| `service` | Ya | Harus `Lite`, `Value`, atau `Pro` | +| `user` | Ya | Nama pengguna | + +--- + +## โš ๏ธ Validasi + +Nilai yang diterima API: + +```text +type: +- Annually +- Monthly + +service: +- Lite +- Value +- Pro +``` + +Jika nilai tidak sesuai, API akan mengembalikan error `400 Bad Request`. + +Contoh error: + +```json +{ + "error": "type must be one of: Annually, Monthly" +} +``` + +```json +{ + "error": "service must be one of: Lite, Value, Pro" +} +``` + +--- + +## ๐ŸŽฎ Menggunakan Playground UI + +Setelah server berjalan, buka browser ke: + +```bash +http://localhost:8080 +``` + +Halaman playground dapat digunakan untuk: + +1. Mengisi token Bearer +2. Melihat status autentikasi +3. Mengisi form request +4. Mengirim request ke endpoint +5. Melihat response JSON secara langsung + +--- + +## ๐Ÿงพ Contoh Response Error + +| Status | Kondisi | Response | +|---|---|---| +| `401` | Tidak menyertakan Authorization header | `{"error": "missing or invalid Authorization header"}` | +| `401` | Token tidak valid | `{"error": "invalid token"}` | +| `400` | Field wajib kosong | `{"error": "type, service, user are required"}` | +| `400` | `type` tidak valid | `{"error": "type must be one of: Annually, Monthly"}` | +| `400` | `service` tidak valid | `{"error": "service must be one of: Lite, Value, Pro"}` | +| `400` | `id` bukan integer | `{"error": "id must be an integer"}` | +| `404` | Data tidak ditemukan | `{"error": "not found"}` | + +--- + +## ๐Ÿ“ Struktur Project + +```text +api-playground/ +โ”œโ”€โ”€ app.py # Routing API dan konfigurasi aplikasi +โ”œโ”€โ”€ db.py # Koneksi dan operasi database SQLite +โ”œโ”€โ”€ enroll.db # Database SQLite +โ”œโ”€โ”€ requirements.txt # Dependency Python +โ”œโ”€โ”€ templates/ +โ”‚ โ””โ”€โ”€ playground.html # UI playground +โ””โ”€โ”€ README.md # Dokumentasi project +``` + +--- + +## ๐Ÿ”ง Catatan + +- Token default hanya untuk development. Untuk production, gunakan token yang lebih aman dan simpan melalui environment variable. +- Database SQLite disimpan dalam file `enroll.db`. +- Mode debug dan reloader aktif saat menjalankan `python app.py`. + +--- + +## ๐Ÿค Kontribusi + +Untuk menambahkan fitur baru: + +1. Buat perubahan pada kode +2. Jalankan server untuk memastikan tidak ada error +3. Tes endpoint menggunakan playground UI atau `curl` +4. Dokumentasikan perubahan jika endpoint atau behavior berubah diff --git a/app.py b/app.py index 0b55dc2..2f056f3 100644 --- a/app.py +++ b/app.py @@ -18,6 +18,18 @@ def json_response(data, status=200): return json.dumps(data) +def get_request_params(): + """ + Ambil parameter request dari form URL-encoded atau JSON. + Ini menjaga endpoint tetap kompatibel dengan curl, playground, dan tools API. + """ + if request.content_type.startswith("application/json"): + payload = request.json or {} + return {key: str(value) for key, value in payload.items()} + + return request.forms + + def check_auth(): """ Cek Bearer token. @@ -73,19 +85,28 @@ def api_enroll_post(): if err: return json_response(json.loads(err), 401) - action = request.forms.get("action", "").strip() - enroll_id = request.forms.get("id", "").strip() - type_ = request.forms.get("type", "").strip() - service = request.forms.get("service", "").strip() - user = request.forms.get("user", "").strip() + params = get_request_params() + action = params.get("action", "").strip() + enroll_id = params.get("id", "").strip() + type_ = params.get("type", "").strip() + service = params.get("service", "").strip() + user = params.get("user", "").strip() # โ”€โ”€ remove โ”€โ”€ if action == "remove": if not enroll_id: return json_response({"error": "id is required for remove"}, 400) + if not enroll_id.isdigit(): + return json_response({"error": "id must be an integer"}, 400) + if not db.get_enroll(int(enroll_id)): + return json_response({"error": "not found"}, 404) db.remove_enroll(int(enroll_id)) return json_response({"message": "removed", "id": int(enroll_id)}) + # โ”€โ”€ validate required fields โ”€โ”€ + if not type_ or not service or not user: + return json_response({"error": "type, service, user are required"}, 400) + # โ”€โ”€ validate type โ”€โ”€ if type_ not in db.VALID_TYPES: return json_response( @@ -100,14 +121,14 @@ def api_enroll_post(): # โ”€โ”€ edit โ”€โ”€ if enroll_id: - if not type_ or not service or not user: - return json_response({"error": "type, service, user are required"}, 400) + if not enroll_id.isdigit(): + return json_response({"error": "id must be an integer"}, 400) + if not db.get_enroll(int(enroll_id)): + return json_response({"error": "not found"}, 404) db.edit_enroll(int(enroll_id), type_, service, user) return json_response({"message": "updated", "id": int(enroll_id)}) # โ”€โ”€ add โ”€โ”€ - if not type_ or not service or not user: - return json_response({"error": "type, service, user are required"}, 400) new_id = db.add_enroll(type_, service, user) return json_response({"message": "created", "id": new_id}, 201)