Skip to content

Commit

Permalink
feat: wording improvement, mainly on section B
Browse files Browse the repository at this point in the history
  • Loading branch information
novalagung committed Apr 27, 2024
1 parent 74f439f commit aeacb66
Show file tree
Hide file tree
Showing 27 changed files with 210 additions and 190 deletions.
8 changes: 4 additions & 4 deletions content/A-concurrency-pipeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Go memiliki beberapa API untuk keperluan konkurensi, dua diantaranya adalah *gor

Definisi *pipeline* yang paling mudah versi penulis adalah **beberapa/banyak proses yang berjalan secara konkuren yang masing-masing proses merupakan bagian dari serangkaian tahapan proses yang berhubungan satu sama lain**.

Analoginya seperti ini: bayangkan sebuah flow proses untuk auto backup database secara rutin, yang di mana database server yang perlu di-backup ada banyak. Untuk backup-nya sendiri kita menggunakan program Go, bukan *shell script*. Mungkin secara garis besar serangkaian tahapan proses yang akan dijalankan adalah berikut:
Analoginya seperti ini: bayangkan sebuah flow proses untuk auto backup database secara rutin, yang mana database server yang perlu di-backup ada banyak. Untuk backup-nya sendiri kita menggunakan program Go, bukan *shell script*. Mungkin secara garis besar serangkaian tahapan proses yang akan dijalankan adalah berikut:

1. Kita perlu data *list* dari semua database yang harus di-backup, beserta alamat akses dan kredensial-nya.
2. Kita jalankan proses backup, bisa secara sekuensial (setelah `db1` selesai, lanjut `db2`, lanjut `db3`, dst), atau secara paralel (proses backup `db1`, `db2`, `db3`, dan lainnya dijalankan secara bersamaan).
Expand Down Expand Up @@ -383,12 +383,12 @@ Fungsi `getSum()` menerima channel dan akan secara aktif memantau dan membaca da

Nah, karena di sini kita punya 3 worker yang jelasnya menghasilkan 3 buah channel baru, kita perlu sebuah mekanisme untuk menggabung channel tersebut, agar nanti mudah untuk dikontrol ([SSoT](https://en.wikipedia.org/wiki/Single_source_of_truth)). Di sinilah peran fungsi `mergeChanFileInfo()`.

Fungsi `mergeChanFileInfo()` digunakan untuk *multiplexing* atau menggabung banyak channel ke satu channel saja, yang di mana channel ini juga akan **otomatis di-close ketika channel input (`chanFileContent`) adalah *closed***. Fungsi jenis seperti ini biasa disebut dengan **Fan-in function**.
Fungsi `mergeChanFileInfo()` digunakan untuk *multiplexing* atau menggabung banyak channel ke satu channel saja, yang mana channel ini juga akan **otomatis di-close ketika channel input (`chanFileContent`) adalah *closed***. Fungsi jenis seperti ini biasa disebut dengan **Fan-in function**.

Jadi TL;DR nya:

* Fungsi Fan-out digunakan untuk pembuatan worker, untuk distribusi job, yang proses distribusinya sendiri akan berhenti ketika channel inputan di-close.
* Fungsi Fan-in digunakan untuk *multiplexing* atau menggabung banyak worker ke satu channel saja, yang di mana channel baru ini juga otomatis di-close ketika channel input adalah closed.
* Fungsi Fan-in digunakan untuk *multiplexing* atau menggabung banyak worker ke satu channel saja, yang mana channel baru ini juga otomatis di-close ketika channel input adalah closed.

Sekarang lanjut buat fungsi `getSum()`.

Expand All @@ -408,7 +408,7 @@ func getSum(chanIn <-chan FileInfo) <-chan FileInfo {
}
```

Bisa dilihat, di situ channel inputan `chanIn` di-listen dan setiap ada penerimaan data (via channel tersebut) dilanjut ke proses kalkulasi md5 hash. Hasil hash-nya di tambahkan ke data `FileInfo` kemudian dikirim lagi ke channel `chanOut` yang di mana channel ini merupakan nilai balik fungsi `getSum()`.
Bisa dilihat, di situ channel inputan `chanIn` di-listen dan setiap ada penerimaan data (via channel tersebut) dilanjut ke proses kalkulasi md5 hash. Hasil hash-nya di tambahkan ke data `FileInfo` kemudian dikirim lagi ke channel `chanOut` yang mana channel ini merupakan nilai balik fungsi `getSum()`.

Ketika `chanIn` closed, maka bisa diasumsikan semua data sudah dikirim. Jika memang iya dan data-data tersebut sudah di proses (pencarian md5hash-nya), maka channel `chanOut` juga di-close.

Expand Down
2 changes: 1 addition & 1 deletion content/A-golang-generics.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Penulisan notasi fungsi dengan Generic kurang lebih sebagai berikut:
func FuncName[dataType <ComparableType>](params)
```

Pada kode di atas, tipe data `[]int` kita ganti menjadi tipe data `[]V`, yang di mana tipe `V` dideklarasikan dengan notasi `[V int]`. Tipe data `V` di situ artinya kompatibel atau *comparable* dengan tipe `int`. Bisa diambil kesimpulan kedua fungsi yang telah kita tulis adalah ekuivalen.
Pada kode di atas, tipe data `[]int` kita ganti menjadi tipe data `[]V`, yang mana tipe `V` dideklarasikan dengan notasi `[V int]`. Tipe data `V` di situ artinya kompatibel atau *comparable* dengan tipe `int`. Bisa diambil kesimpulan kedua fungsi yang telah kita tulis adalah ekuivalen.

```go
func Sum(numbers []int) int {
Expand Down
2 changes: 1 addition & 1 deletion content/A-pipeline-context-cancellation.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ Cara pembuatan object context sendiri sebenarnya ada 3:
3. Menggunakan fungsi `context.With...`. Fungsi ini sebenarnya bukan digunakan untuk inisialisasi objek konteks baru, tapi digunakan untuk menambahkan informasi tertentu pada *copied* context yang disisipkan di parameter pertama pemanggilan fungsi. Ada 3 buah fungsi `context.With...` yang bisa digunakan, yaitu:

- Fungsi `context.WithCancel(ctx) (ctx, cancel)`. Fungsi ini digunakan untuk menambahkan fasilitas *cancellable* pada context yang disisipkan sebagai parameter pertama pemanggilan fungsi. Lewat nilai balik kedua, yaitu `cancel` yang tipenya `context.CancelFunc`, kita bisa secara paksa meng-*cancel* context ini.
- Fungsi `context.WithDeadline(ctx, time.Time) (ctx, cancel)`. Fungsi ini juga menambahkan fitur *cancellable* pada context, tapi selain itu juga menambahkan informasi deadline yang di mana jika waktu sekarang sudah melebihi deadline yang sudah ditentukan maka context otomatis di-cancel secara paksa.
- Fungsi `context.WithDeadline(ctx, time.Time) (ctx, cancel)`. Fungsi ini juga menambahkan fitur *cancellable* pada context, tapi selain itu juga menambahkan informasi deadline yang mana jika waktu sekarang sudah melebihi deadline yang sudah ditentukan maka context otomatis di-cancel secara paksa.
- Fungsi `context.WithTimeout(ctx, time.Duration) (ctx, cancel)`. Fungsi ini sama seperti `context.WithDeadline()`, bedanya pada parameter kedua argument bertipe durasi (bukan objek `time.Time`).

Kesamaan dari ketiga fungsi `context.With...` adalah sama-sama menambahkan fasilitas *cancellable* yang bisa dieksekusi lewat nilai balik kedua fungsi tersebut (yang tipenya `context.CancelFunc`).
Expand Down
2 changes: 1 addition & 1 deletion content/A-simplified-fan-in-fan-out-pipeline.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# A.63. Concurrency Pattern: Simplified Fan-out Fan-in Pipeline

Pada chapter sebelumnya, yaitu chapter [A.62. Concurrency Pattern: Pipeline](/A-concurrency-pipeline.html), kita telah mempelajari tentang pipeline pattern, yang di mana pattern tersebut merupakan rekomendasi dari tim Go dalam meng-*handle* jenis kasus sekarangkain proses yang berjalan secara konkuren.
Pada chapter sebelumnya, yaitu chapter [A.62. Concurrency Pattern: Pipeline](/A-concurrency-pipeline.html), kita telah mempelajari tentang pipeline pattern, yang mana pattern tersebut merupakan rekomendasi dari tim Go dalam meng-*handle* jenis kasus sekarangkain proses yang berjalan secara konkuren.

> Penulis sangat anjurkan untuk mencoba mempelajari praktek chapter sebelumnya terlebih dahulu jika belum. Karena chapter kali ini ada hubungannya dengan chapter tersebut.
Expand Down
2 changes: 1 addition & 1 deletion content/A-slice.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ fruits[x:y]

**Slicing** yang dimulai dari indeks **0** hingga **y** akan mengembalikan elemen-elemen mulai indeks **0** hingga sebelum indeks **y**, dengan lebar kapasitas adalah sama dengan slice aslinya.

Sedangkan slicing yang dimulai dari indeks **x**, yang di mana nilai **x** adalah lebih dari **0**, membuat elemen ke-**x** slice yang diambil menjadi elemen ke-0 slice baru. Hal inilah yang membuat kapasitas slice berubah.
Sedangkan slicing yang dimulai dari indeks **x**, yang mana nilai **x** adalah lebih dari **0**, membuat elemen ke-**x** slice yang diambil menjadi elemen ke-0 slice baru. Hal inilah yang membuat kapasitas slice berubah.

## A.16.6. Fungsi `append()`

Expand Down
2 changes: 1 addition & 1 deletion content/A-timer-ticker-scheduler.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func main() {

Pada contoh di atas bisa dilihat, selain ticker disiapkan juga variabel channel `done`. Variabel ini kita gunakan untuk mengontrol kapan ticker harus di stop.

Cara kerja program di atas: teknik `for` - `select` pada channel digunakan untuk mengecek penerimaan data dari channel `done` dan `ticker.C`. By default, channel `ticker.C` akan menerima kiriman data setiap `X` duration yang di mana pada kode di atas adalah 1 detik (lihat argumen inisialisasi objek ticker).
Cara kerja program di atas: teknik `for` - `select` pada channel digunakan untuk mengecek penerimaan data dari channel `done` dan `ticker.C`. By default, channel `ticker.C` akan menerima kiriman data setiap `X` duration yang mana pada kode di atas adalah 1 detik (lihat argumen inisialisasi objek ticker).

Data yang dikirimkan via channel `ticker.C` adalah data date-time kapan event itu terjadi. Pada kode di atas, setiap ada kiriman data via channel tersebut kita tampilkan.

Expand Down
2 changes: 1 addition & 1 deletion content/B-ajax-json-payload.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ Isi payload didapatkan dengan cara men-decode body request (`r.Body`). Proses de
- `json.Decoder` cocok digunakan untuk decode data JSON yang sumber datanya adalah stream `io.Reader`, contohnya seperti `r.Body`.
- `json.Unmarshal()` cocok untuk proses decoding yang sumber datanya sudah tersimpan di variabel (bukan stream).

## B.14.5. Test
## B.14.5. Testing

Jalankan program yang telah dibuat, test hasilnya di browser.

Expand Down
4 changes: 2 additions & 2 deletions content/B-form-value.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Pertama siapkan folder project baru dan sebuah file template view `view.html`. P
{{end}}
```

Aksi dari form di atas adalah `/process`, yang di mana url tersebut nantinya akan mengembalikan output berupa html hasil render template `result`. Silakan tulis template result berikut dalam `view.html` (jadi file view ini berisi 2 buah template).
Aksi dari form di atas adalah `/process`, yang mana url tersebut nantinya akan mengembalikan output berupa html hasil render template `result`. Silakan tulis template result berikut dalam `view.html` (jadi file view ini berisi 2 buah template).

```html
{{define "result"}}
Expand Down Expand Up @@ -123,7 +123,7 @@ Selain lewat method `FormValue()`, pengaksesan data juga bisa dilakukan dengan c

Setelah data dari form sudah ditangkap oleh back-end, data ditampung dalam variabel `data` yang bertipe `map[string]string`. Variabel `data` tersebut kemudian disisipkan ke view, lewat statement `tmpl.Execute(w, data)`.

## B.12.3. Test
## B.12.3. Testing

OK, sekarang coba jalankan program yang telah kita buat, dan cek hasilnya.

Expand Down
60 changes: 33 additions & 27 deletions content/B-http-basic-auth.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
# B.18. HTTP Basic Authentication

HTTP Basic Auth adalah salah satu teknik otentikasi http request. Metode ini membutuhkan informasi username dan password untuk disisipkan dalam header request (dengan format tertentu), jadi cukup sederhana, tidak memerlukan cookies maupun session. Lebih jelasnya silakan baca [RFC-7617](https://tools.ietf.org/html/rfc7617).
HTTP Basic Auth adalah salah satu spesifikasi yang mengatur otentikasi pada HTTP request. Metode ini mewajibkan client request untuk menyertakan username dan password dalam header request. Dengan menerapkan basic auth maka kita tidak perlu menggunakan token untuk mendapatkan session.

Informasi username dan password tidak serta merta disisipkan dalam header, informasi tersebut harus di-encode terlebih dahulu ke dalam format yg sudah ditentukan sesuai spesifikasi, sebelum dimasukan ke header.
> Lebih jelasnya mengenai spesifikasi Basic Auth bisa di lihat di[RFC-7617](https://tools.ietf.org/html/rfc7617)
Berikut adalah contoh penulisan basic auth.
Informasi username dan password harus di-encode terlebih dahulu ke format yg sudah ditentukan sesuai spesifikasi, kemudian dijadikan value dari header `Authentication`.

Berikut adalah contoh format penulisan basic auth:

```js
// Request header
Authorization: Basic c29tZXVzZXJuYW1lOnNvbWVwYXNzd29yZA==
```

Informasi disisipkan dalam request header dengan key `Authorization`, dan value adalah `Basic` spasi hasil enkripsi dari data username dan password. Data username dan password digabung dengan separator tanda titik dua (`:`), lalu di-encode dalam format encoding Base 64.
Informasi disisipkan dalam request header dengan key `Authorization`, dan value adalah `Basic` diikut karakter spasi dan hasil encode terhadap data username dan password. Data username dan password digabung dengan separator tanda titik dua (`:`) lalu di-encode dalam format encoding Base64.

```js
// Username password encryption
base64encode("someusername:somepassword")
// Hasilnya adalah c29tZXVzZXJuYW1lOnNvbWVwYXNzd29yZA==
```

Golang menyediakan fungsi untuk meng-handle request basic auth dengan cukup mudah, jadi tidak perlu untuk memparsing header request terlebih dahulu untuk mendapatkan informasi username dan password.
Go menyediakan fasilitas untuk mengambil informasi basic auth dari suatu HTTP request dengan mudah, tanpa perlu untuk memparsing header request terlebih dahulu secara manual.

## B.18.1. Struktur Folder Proyek dan Endpoint

Ok, mari kita praktekan. Pada chapter ini kita akan membuat sebuah web service sederhana, isinya satu buah endpoint. Endpoint ini kita manfaatkan sebagai dua endpoint, dengan pembeda adalah informasi pada query string-nya.
Pada chapter ini kita akan membuat sebuah web service sederhana, isinya hanya satu buah endpoint. Endpoint ini didesain untuk bisa menerima query parameter atau tanpa query parameter.

- Endpoint `/student`, menampilkan semua data siswa.
- Endpoint `/student?id=s001`, menampilkan data siswa sesuai dengan id yang di minta.
- Endpoint `/student` menghasilkan response berisi semua data siswa
- Endpoint `/student?id=s001` menghasilkan response berisi data siswa sesuai dengan id yang di minta

Data siswa sendiri merupakan slice object yang disimpan di variabel global.

Expand Down Expand Up @@ -56,12 +58,16 @@ func main() {
}
```

Siapkan handler untuk rute `/student`.
Lalu siapkan handler untuk rute `/student`.

```go
func ActionStudent(w http.ResponseWriter, r *http.Request) {
if !Auth(w, r) { return }
if !AllowOnlyGET(w, r) { return }
if !Auth(w, r) {
return
}
if !AllowOnlyGET(w, r) {
return
}

if id := r.URL.Query().Get("id"); id != "" {
OutputJSON(w, SelectStudent(id))
Expand All @@ -74,15 +80,15 @@ func ActionStudent(w http.ResponseWriter, r *http.Request) {

Di dalam rute `/student` terdapat beberapa validasi.

- Validasi `!Auth(w, r)`; Nantinya akan kita buat fungsi `Auth()` untuk mengecek apakah request merupakan valid basic auth request atau tidak.
- Validasi `!AllowOnlyGET(w, r)`; Nantinya juga akan kita siapkan fungsi `AllowOnlyGET()`, gunanya untuk memastikan hanya request dengan method `GET` yang diperbolehkan masuk.
- Validasi `!Auth(w, r)`; Nantinya kita siapkan fungsi `Auth()` yang gunanya adalah untuk mengecek apakah request merupakan valid basic auth request atau tidak.
- Validasi `!AllowOnlyGET(w, r)`; Akan dibuat juga fungsi `AllowOnlyGET()`, tugasnya memastikan hanya request dengan method `GET` yang diperbolehkan masuk.

Setelah request lolos dari 2 validasi di atas, kita cek lagi apakah request ini memiliki parameter student id.
Setelah request lolos dari 2 validasi di atas, lanjut ke pengecekan berikutnya yaitu mendeteksi apakah request memiliki parameter student id.

- Ketika tidak ada parameter student id, maka endpoint ini mengembalikan semua data user yang ada, lewat pemanggilan fungsi `GetStudents()`.
- Sedangkan jika ada parameter student id, maka hanya user dengan id yg diinginkan yg dijadikan nilai balik, lewat fungsi `SelectStudent(id)`.
- Ketika tidak ada parameter student id, maka endpoint ini mengembalikan semua data user yang ada. Fungsi `GetStudents()` dieksekusi.
- Sedangkan jika ada parameter student id, maka hanya user dengan id yg diinginkan yg dijadikan nilai balik. Fungsi `SelectStudent(id)` dieksekusi.

Selanjutnya tambahkan satu fungsi lagi di main, `OutputJSON()`. Fungsi ini digunakan untuk mengkonversi data menjadi JSON string.
Selanjutnya tambahkan satu fungsi lagi di `main()` yaitu `OutputJSON()`. Fungsi ini digunakan konversi data ke bentuk JSON string.

```go
func OutputJSON(w http.ResponseWriter, o interface{}) {
Expand All @@ -97,11 +103,11 @@ func OutputJSON(w http.ResponseWriter, o interface{}) {
}
```

Konversi dari objek atau slice ke JSON string bisa dilakukan dengan memanfaatkan `json.Marshal`. Untuk lebih jelasnya silakan baca lagi chapter [A.53. JSON Data](/A-json.html).
Konversi dari objek atau slice ke JSON string dilakukan via `json.Marshal()`. Lebih jelasnya mengenai fungsi tersebut di bahas di chapter [A.53. JSON Data](/A-json.html).

## B.18.3. Data `Student`

Buka file `student.go`, siapkan struct `Student` dan variabel untuk menampung data yang bertipe `[]Student`. Data inilah yang dijadikan nilai balik di endpoint yang sudah dibuat.
Buka file `student.go`, siapkan struct `Student` dan variabel untuk menampung data yang bertipe `[]Student`. Data inilah yang nantinya dijadikan nilai balik endpoint `/student`.

```go
package main
Expand All @@ -115,7 +121,7 @@ type Student struct {
}
```

Buat fungsi `GetStudents()`, fungsi ini mengembalikan semua data student. Dan buat juga fungsi `SelectStudent(id)`, fungsi ini mengembalikan data student sesuai dengan id terpilih.
Buat fungsi `GetStudents()`, fungsi ini mengembalikan semua data student. Buat juga fungsi `SelectStudent(id)`, fungsi ini mengembalikan data student sesuai dengan id terpilih.

```go
func GetStudents() []*Student {
Expand All @@ -133,7 +139,7 @@ func SelectStudent(id string) *Student {
}
```

*Last but not least*, implementasikan fungsi `init()`, buat beberapa dummy data untuk ditampung pada variabel `students`.
*Last but not least*, implementasikan fungsi `init()` yang didalamnya berisi pembuatan beberapa dummy data untuk ditampung variabel `students`.

> Fungsi `init()` adalah fungsi yang secara otomatis dipanggil ketika package tersebut di import atau di run.
Expand All @@ -147,7 +153,7 @@ func init() {

## B.18.4. Fungsi `Auth()` dan `AllowOnlyGET()`

Selanjutnya, kita perlu menyiapkan beberapa fungsi yg digunakan pada `main.go`, yaitu `Auth()` dan `AllowOnlyGET()`.
Selanjutnya, ada dua fungsi lainnya yang perlu dipersiapkan yaitu `Auth()` dan `AllowOnlyGET()`.

#### ◉ Fungsi `Auth()`

Expand Down Expand Up @@ -186,11 +192,11 @@ Fungsi `r.BasicAuth()` mengembalikan 3 informasi:
2. Password
3. Nilai balik ke-3 ini adalah representasi valid tidak nya basic auth request yang sedang berlangsung

Jika basic auth request tidak valid, maka tampilkan pesan error sebagai nilai balik. Sedangkan jika basic auth adalah valid, maka dilanjutkan ke proses otentikasi, mengecek apakah username dan password yang dikirim cocok dengan username dan password yang ada di aplikasi kita.
Error dimunculkan ketika basic auth terdeteksi adalah tidak valid. Sedangkan jika ternyata valid, maka dilanjutkan ke proses otentikasi, mengecek apakah username dan password yang dikirim cocok dengan username dan password yang sudah di-*hardcode*.

#### ◉ Fungsi `AllowOnlyGET()`

Fungsi ini bertugas untuk memastikan bahwa request yang diperbolehkan hanya yang ber-method `GET`. Selainnya, maka akan dianggap invalid request.
Fungsi ini bertugas memastikan bahwa request yang diperbolehkan hanya yang ber-method `GET`. Selainnya, maka dianggap invalid request.

```go
func AllowOnlyGET(w http.ResponseWriter, r *http.Request) bool {
Expand All @@ -205,17 +211,17 @@ func AllowOnlyGET(w http.ResponseWriter, r *http.Request) bool {

## B.18.5. Testing

Semuanya sudah siap, jalankan aplikasi.
Semuanya sudah siap, sekarang jalankan aplikasi.

```bash
go run *.go
```

Jangan menggunakan `go run main.go`, dikarenakan dalam package `main` terdapat beberapa file lain yang harus di-ikut-sertakan pada saat runtime.
Jangan menggunakan `go run main.go`, dikarenakan dalam package `main` terdapat beberapa file lain yang harus diikutsertakan pada saat runtime.

![Run the server](images/B_http_basic_auth_2_run_server.png)

Test web service kecil ini menggunakan command `curl`.
Test web service yang telah dibuat menggunakan command `curl`.

```bash
$ curl -X GET --user batman:secret http://localhost:9000/student
Expand Down
Loading

0 comments on commit aeacb66

Please sign in to comment.