---
# System prepended metadata

title: Airline (Spring/Firebase) Part 2
tags: [fall23, firebase, spring-boot]

---

# Airline Create, Update, Delete

## Services

### Passenger

When creating a user, we use the POJO Passenger class. We still make the call asynchronously using the add method. Finally we return the new DocumentId that was generated.

```java=55
public String createPassenger(Passenger passenger) throws ExecutionException, InterruptedException {
        String passengerId = null;

        ApiFuture<DocumentReference> future = firestore.collection("Passenger").add(passenger);
        DocumentReference postRef = future.get();
        passengerId = postRef.getId();

        return passengerId;
    }
```

The update method only allows the user to update specific fields. This is managed by the `allowed` array. To process the update, we create a `HashMap` of the key/value pairs we wish to updated. Once again we do this asynchronously. This is `WriteResult` class. This class returns a timestamp of the update.
```java=65
public void updatePassenger(String id, Map<String, String> updateValues){

    String [] allowed = {"firstName", "lastName", "phone", "address", "dateOfBirth"};
    List<String> list = Arrays.asList(allowed);
    Map<String, Object> formattedValues = new HashMap<>();

    for(Map.Entry<String, String> entry : updateValues.entrySet()) {
        String key = entry.getKey();
        if(list.contains(key))
            formattedValues.put(key, entry.getValue());
    }

    DocumentReference passengerDoc = firestore.collection("Passenger").document(id);
    passengerDoc.update(formattedValues);
}
```

### Flight

```java=
   public String createFlight(Flight flight) throws ExecutionException, InterruptedException {

        String flightId = null;
        ApiFuture<DocumentReference> future = firestore.collection("Flight").add(flight);
        DocumentReference postRef = future.get();
        flightId = postRef.getId();

        return  flightId;
    }

    public void updateFlight(String id, Map<String, String> updatedValues) throws ParseException {
        String [] allowed = {"departureDateTime", "arrivalDateTime", "aircraftType", "flightStatus"};
        List<String> list = Arrays.asList(allowed);
        Map<String, Object> formattedValues = new HashMap<>();

        for(Map.Entry<String, String> entry : updatedValues.entrySet())
        {
            String key = entry.getKey();
            if(list.contains(key)) {
                if (key.equals("departureDateTime") || key.equals("arrivalDateTime"))
                {

                    formattedValues.put(key, Timestamp.fromProto(Timestamps.parse(entry.getValue())));
                }
                else
                    formattedValues.put(key, entry.getValue());
            }
        }
        DocumentReference flightDoc = firestore.collection("Flight").document(id);
        if(flightDoc != null)
            flightDoc.update(formattedValues);

    }
```

Well will need  a simple way to update the number of seats available in each class a passangers book and cancel flights. So we create a sell and return seat method. The method accespts the flight id, the seat type, and the number of seats sold and returned.

```java=
public void sellSeat(String flightId, String seatType, int numberOfSeats) throws ExecutionException, InterruptedException {
    DocumentReference flightDoc = firestore.collection("Flight").document(flightId);

    flightDoc.update("availableSeats." + seatType, FieldValue.increment(-numberOfSeats));
}

public void returnSeat(String flightId, String seatType, int numberOfSeats) throws ExecutionException, InterruptedException {
    DocumentReference flightDoc = firestore.collection("Flight").document(flightId);

    flightDoc.update("availableSeats." + seatType, FieldValue.increment(numberOfSeats));
}
```

### Booking

```java=
public String createBooking(RestBooking booking) throws ExecutionException, InterruptedException {
    String bookingId = null;
    ApiFuture<DocumentReference> future = firestore.collection("Booking").add(booking);
    DocumentReference postRef = future.get();
    bookingId = postRef.getId();

    return  bookingId;
}
```

We will not allow the user to update their booking, but they will be allowed to cancel it.

```java=
public void cancelBooking(String bookingId) throws ExecutionException, InterruptedException {
    DocumentReference bookingRef = firestore.collection("Booking").document(bookingId);

    DocumentReference flightRef = (DocumentReference) bookingRef.get().get().get("flightNumber");

    //release seats
    FlightService service = new FlightService();
    DocumentSnapshot flightDoc = flightRef.get().get();
    service.returnSeat(flightDoc.getId(), flightDoc.getString("seatType"), 1);

    //delete booking
    bookingRef.delete();

}
```

## Controllers

### Passenger
Our PassengerController will expose the PassengerService methods. All of our controllers will look this way. We introduce a few new annotations:

- `@PathVariable` an be used to handle template variables in the request URI mapping, and set them as method parameters.
- `@RequestBody` annotation maps the HttpRequest body to a transfer or domain object, enabling automatic deserialization of the inbound HttpRequest body onto a Java object.
- `@PostMapping` annotation for mapping HTTP POST requests onto specific handler methods.
- `@PutMapping` annotation for mapping HTTP PUT requests onto specific handler methods.

This is a review of the **path variable**. Path variables, as you may recall, are values that we want to pass to our resouce (eg. user id). In the mapping annotation, the variable name is placed in curly braces to denote that is is a variable and not a string literal. In front of the corresponding method parameter use the `@PathVariable` annotation including the name property.

```java=
@PostMapping
public ResponseEntity<ApiResponse> createNewPassenger(@RequestBody Passenger passenger)
{
    try{
        return ResponseEntity.ok(new ApiResponse(true, "Success", passengerService.createPassenger(passenger), null));
    } catch (ExecutionException e) {
        return ResponseEntity.status(401).body(new ApiResponse(false, "An error occurred", null, e.getMessage()));
    } catch (InterruptedException e) {
        return ResponseEntity.status(500).body(new ApiResponse(false, "An error occurred", null, e.getMessage()));
    }
}

```

`RequestBody` values are expected to be in JSON format.

```java=12
@PutMapping("/{passengerId}")
public ResponseEntity<ApiResponse> updatePassenger(@PathVariable(name="passengerId") String id, @RequestBody Map<String,String> data)
{
    try{
        passengerService.updatePassenger(id, data);
        return ResponseEntity.ok(new ApiResponse(true, "Passenger successfully updated", null, null));
    } catch (Exception e) {
        return ResponseEntity.status(500).body(new ApiResponse(false, "An error occurred", null, e.getMessage()));
    }
}
```

### Booking

```java=
@PostMapping
    public ResponseEntity<ApiResponse> createBooking(@RequestBody RestBooking booking) {
        try {
            return ResponseEntity.ok(new ApiResponse(true, "Success", bookingService.createBooking(booking), null));
        } catch (Exception e) {
            return ResponseEntity.status(500).body(new ApiResponse(false, "An error occurred", null,e.getMessage()));
        }
    }

    @DeleteMapping("/{bookingId}")
    public ResponseEntity<ApiResponse> cancelBooking(@PathVariable String bookingId) {
        try {
            bookingService.cancelBooking(bookingId);
            return ResponseEntity.ok(new ApiResponse(true, "Success", null, null));
        } catch (Exception e) {
            return ResponseEntity.status(500).body(new ApiResponse(false, "An error occurred", null,e.getMessage()));
        }
    }
```