# BambangShop Receiver App
Tutorial and Example for Advanced Programming 2024 - Faculty of Computer Science, Universitas Indonesia

---

## About this Project
In this repository, we have provided you a REST (REpresentational State Transfer) API project using Rocket web framework.

This project consists of four modules:
1.  `controller`: this module contains handler functions used to receive request and send responses.
    In Model-View-Controller (MVC) pattern, this is the Controller part.
2.  `model`: this module contains structs that serve as data containers.
    In MVC pattern, this is the Model part.
3.  `service`: this module contains structs with business logic methods.
    In MVC pattern, this is also the Model part.
4.  `repository`: this module contains structs that serve as databases.
    You can use methods of the struct to get list of objects, or operating an object (create, read, update, delete).

This repository provides a Rocket web framework skeleton that you can work with.

As this is an Observer Design Pattern tutorial repository, you need to implement a feature: `Notification`.
This feature will receive notifications of creation, promotion, and deletion of a product, when this receiver instance is subscribed to a certain product type.
The notification will be sent using HTTP POST request, so you need to make the receiver endpoint in this project.

## API Documentations

You can download the Postman Collection JSON here: https://ristek.link/AdvProgWeek7Postman

After you download the Postman Collection, you can try the endpoints inside "BambangShop Receiver" folder.

Postman is an installable client that you can use to test web endpoints using HTTP request.
You can also make automated functional testing scripts for REST API projects using this client.
You can install Postman via this website: https://www.postman.com/downloads/

## How to Run in Development Environment
1.  Set up environment variables first by creating `.env` file.
    Here is the example of `.env` file:
    ```bash
    ROCKET_PORT=8001
    APP_INSTANCE_ROOT_URL=http://localhost:${ROCKET_PORT}
    APP_PUBLISHER_ROOT_URL=http://localhost:8000
    APP_INSTANCE_NAME=Safira Sudrajat
    ```
    Here are the details of each environment variable:
    | variable                | type   | description                                                     |
    |-------------------------|--------|-----------------------------------------------------------------|
    | ROCKET_PORT             | string | Port number that will be listened by this receiver instance.    |
    | APP_INSTANCE_ROOT_URL   | string | URL address where this receiver instance can be accessed.       |
    | APP_PUUBLISHER_ROOT_URL | string | URL address where the publisher instance can be accessed.       |
    | APP_INSTANCE_NAME       | string | Name of this receiver instance, will be shown on notifications. |
2.  Use `cargo run` to run this app.
    (You might want to use `cargo check` if you only need to verify your work without running the app.)
3.  To simulate multiple instances of BambangShop Receiver (as the tutorial mandates you to do so),
    you can open new terminal, then edit `ROCKET_PORT` in `.env` file, then execute another `cargo run`.

    For example, if you want to run 3 (three) instances of BambangShop Receiver at port `8001`, `8002`, and `8003`, you can do these steps:
    -   Edit `ROCKET_PORT` in `.env` to `8001`, then execute `cargo run`.
    -   Open new terminal, edit `ROCKET_PORT` in `.env` to `8002`, then execute `cargo run`.
    -   Open another new terminal, edit `ROCKET_PORT` in `.env` to `8003`, then execute `cargo run`.

## Mandatory Checklists (Subscriber)
-   [ ] Clone https://gitlab.com/ichlaffterlalu/bambangshop-receiver to a new repository.
-   **STAGE 1: Implement models and repositories**
    -   [ ] Commit: `Create Notification model struct.`
    -   [ ] Commit: `Create SubscriberRequest model struct.`
    -   [ ] Commit: `Create Notification database and Notification repository struct skeleton.`
    -   [ ] Commit: `Implement add function in Notification repository.`
    -   [ ] Commit: `Implement list_all_as_string function in Notification repository.`
    -   [ ] Write answers of your learning module's "Reflection Subscriber-1" questions in this README.
-   **STAGE 3: Implement services and controllers**
    -   [ ] Commit: `Create Notification service struct skeleton.`
    -   [ ] Commit: `Implement subscribe function in Notification service.`
    -   [ ] Commit: `Implement subscribe function in Notification controller.`
    -   [ ] Commit: `Implement unsubscribe function in Notification service.`
    -   [ ] Commit: `Implement unsubscribe function in Notification controller.`
    -   [ ] Commit: `Implement receive_notification function in Notification service.`
    -   [ ] Commit: `Implement receive function in Notification controller.`
    -   [ ] Commit: `Implement list_messages function in Notification service.`
    -   [ ] Commit: `Implement list function in Notification controller.`
    -   [ ] Write answers of your learning module's "Reflection Subscriber-2" questions in this README.

## Your Reflections
This is the place for you to write reflections:

### Mandatory (Subscriber) Reflections

#### Reflection Subscriber-1
### 1. Why is `RwLock<>` necessary, and why not use `Mutex<>`?

In this tutorial, we used `RwLock<>` to synchronize access to the `Vec` of notifications. The main reason for using `RwLock<>` instead of `Mutex<>` is performance optimization in a concurrent environment. `RwLock<>` allows multiple readers to access the data simultaneously while ensuring that only one writer can modify the data at any given time. This is useful in scenarios where reads are more frequent than writes, as it prevents unnecessary blocking. If we had used `Mutex<>`, every read operation would require exclusive access to the lock, blocking other readers and leading to performance bottlenecks. With `RwLock<>`, multiple threads can read the notifications concurrently without interfering with each other, while still maintaining exclusive access when modifications are necessary.

### 2. Why does Rust not allow mutating the content of a static variable like in Java?

In Java, static variables are stored in a shared memory space, and their contents can be modified freely through static functions. Rust, however, enforces strict ownership and borrowing rules to prevent data races and ensure memory safety at compile time. A `static` variable in Rust has a fixed memory location and must be immutable unless explicitly wrapped in synchronization primitives like `Mutex<>` or `RwLock<>`. The reason for this restriction is that, in a multi-threaded environment, direct mutation of a static variable without synchronization could lead to undefined behavior, such as data races. Rust’s approach forces developers to explicitly handle synchronization, ensuring thread safety and memory correctness. This design makes Rust safer by preventing unintended modifications and enforcing concurrency safety at the language level.

#### Reflection Subscriber-2
# Assignment Answers

## 1. Exploration Beyond the Tutorial Steps

Yes, I have explored parts of the code outside the tutorial steps, particularly in `src/lib.rs`. From this, I learned that `lib.rs` serves as the main entry point for defining modules, re-exporting items, and managing dependencies within the project. It acts as a central hub where various components, such as controllers, services, and models, are structured. By understanding `lib.rs`, I gained insights into how Rust organizes modular code and how different components interact within the notification system. Additionally, exploring this file helped me understand the role of the `mod` keyword in declaring modules and how functions from different files are made accessible throughout the project.

## 2. Observer Pattern and Multiple Instances

The Observer pattern makes it easier to add new subscribers to the notification system because it decouples the notification sender from its listeners. In this implementation, a publisher (the notification service) maintains a list of observers (subscribers) and automatically notifies them of any changes. This design allows us to dynamically register new subscribers without modifying the core notification logic, making the system highly extensible. 

However, spawning multiple instances of the main application introduces challenges. While adding new subscribers is straightforward, running multiple instances of the main app might lead to issues like message duplication, race conditions, or inconsistent state synchronization. Proper handling through distributed messaging queues, shared databases, or a consensus mechanism would be necessary to maintain consistency across multiple app instances.

## 3. Writing Tests and Enhancing Documentation

I have tried writing my own tests and enhancing documentation in the Postman collection. Writing unit tests for critical functions helped validate the correctness of the notification system, ensuring that each component behaves as expected. It also helped identify edge cases and potential bugs early in development. 

Enhancing the Postman documentation was particularly useful in simplifying API testing and collaboration. By providing clear descriptions of request parameters, expected responses, and example cases, it became easier for other team members to understand and test the API endpoints. These enhancements proved beneficial both for my tutorial work and my group project, as they improved debugging efficiency and streamlined API integration.