implementet Unit

This commit is contained in:
Jonas Hinterdorfer 2025-03-12 21:51:06 +01:00
parent 88620c004f
commit 4af44feb7a
4 changed files with 237 additions and 1 deletions

153
README.md
View File

@ -1,2 +1,153 @@
# db-04-flights # Flight Management
We return to the flight database we used in one of the first assignments.
However, this time it got extended by a passenger list instead of a simple count.
```plantuml
@startuml
hide circle
hide empty methods
skinparam nodesep 80
skinparam linetype ortho
entity Airport {
* icao: string <<PK>>
* name: string
* country: string
* runwayLength: int
}
entity Plane {
* tailNo: string <<PK>>
* model: string
* manufacturer: string
* capacity: int
}
entity Flight {
* flightNo: string <<PK>>
* departure: datetime
arrival: datetime
}
entity Passenger {
* ssn: string <<PK>>
* name: string
* dateOfBirth: datetime
isFemale: bool
}
entity Reservation {
* ticketNo: int <<PK>>
* seat: string
}
Airport ||-l-o{ Flight: departs
Airport ||-l-o{ Flight: arrives
Flight }o-l-|| Plane: operates
Flight ||--o{ Reservation: has
Passenger ||-r-|{ Reservation: has
@enduml
```
## Task 1
- Create the tables
- The original three tables are _mostly_ unchanged and can be taken from the previous assignment
- Make sure to use proper data types & foreign keys again
- Mind the following hints for the new tables
- Passenger: `isFemale` is _nullable_ on purpose, so we can put null in case a person is neither male nor female
- Reservation: we don't use a composite primary key here (despite it normally being the way to go here), because
the `ticketNo` has to use the 'autoincrement' feature
- You already know this feature from DBI (a sequence in Oracle)
- [Read up on how to use it in SQLite](https://www.sqlite.org/autoinc.html) - you _won't_ need any keywords,
read the page 😉
- During inserts the ticketNo has _not_ to be provided for this to work
- **You'll have to figure out how to get the last insert (row)id on your own** - a short internet research will
turn up the answer
## Task 2
- To get things going populate the database with several planes and airports
- No endpoint is needed here, just run the script when the server boots
- You have to check if the data already exists before inserting to prevent unique constraint violations
- Hint: prepared statements can be `reset` and then used again
- You may use the following data
| **Tail Number** | **Model** | **Manufacturer** | **Capacity** |
|-----------------|-----------|------------------|--------------|
| OE-LAA | A320 Neo | Airbus | 164 |
| OE-LAB | B737-800 | Boeing | 188 |
| OE-LAC | A319 | Airbus | 152 |
| OE-LAD | B787-900 | Boeing | 260 |
| OE-LAE | B737-800 | Boeing | 188 |
| OH-LPE | B777-200 | Boeing | 330 |
| **ICAO** | **Name** | **Country** | **RunwayLength** |
|----------|------------------------------------------|-------------|------------------|
| LOWL | Blue Danube Airport Linz | Austria | 9842 |
| LOWW | Vienna International | Austria | 11811 |
| LIRF | Leonardo da VinciFiumicino | Italy | 12795 |
| EGLL | Heathrow | UK | 12802 |
| EDDF | Frankfurt | Germany | 13123 |
| RJAA | Narita International | Japan | 13123 |
| KATL | HartsfieldJackson Atlanta International | USA | 12390 |
## Task 3
- Allow passenger registration
- Create an endpoint for adding new and updating existing passengers to/in the database
- Data is provided as JSON in the body of the request
- Use a `PUT` operation (with proper resource URI using the ssn)
- This allows us to use this method for updates (by replacement) as well
- Be careful when reading entities back from the database: types are lost at runtime, so what you expect to be
a `Date` may not be one - the same is true for a `boolean`, some casting/mapping may be required
- Provide another endpoint for retrieving:
1. a specific passenger by ssn
2. a list of all passengers
- Apply the best practices you know by now
## Task 4
- Provide information about airports
- Two-fold process:
1. One endpoint provides a list of all ICAO codes
2. A second endpoint allows retrieving detail information of an airport _by_ its ICAO code
## Task 5
- Provide information about planes
- A list of available planes
- A new flight can only be planned for a plane which does not already operate a different flight at the same time
- The request has to contain departure time information
- We will use a simplified method to determine that:
- We assume that any flight takes at most 12 hours (no matter which airports are connected)
- Flights can start at any airport, independent of the last destination of the plane - it can reach any departure airport within 12 hours as well
- Thus, we _block_ a plane for _24_ hours after its last _departure_ time
- All airports in our database have runways long enough for each plane in our database (it might be close for the 777 in Linz, but we'll ignore that 🙂)
- Again, a two-step process:
1. Query for a list of airplane ids which are available in the specified time frame
2. Query for details of a plane by id
## Task 6
- To create a flight the following information has to be supplied:
- Departure & destination airport
- Both have to exist
- Departure time
- Has to be in the future
- Plane which will operate the flight
- Has to be one of the available planes
- Implement an endpoint for creating such flights
- Also allow for updates - for which all conditions still apply
- _Hint_: if the plane does not change, that one will be occupied during an update, handle that case accordingly
## Task 7
- Finally, we will book passengers on flights
- The endpoint will receive
- The SSN
- The seat
- the flight number
- It has to be verified that
- Person exists
- As a simplification you do _not_ have to check if the Person is already booked on _another_ flight at the same time
- Flight exists
- Flight still has open seats (= capacity not exceeded)
- Seat not taken by another passenger
- The ticket number is created automatically by the database and returned to the user
- To make it easier you don't have to worry about updates or delete this time
## Task 8
- Provide an endpoint for cancelling (= deleting a flight)
- This includes cancelling all reservations as well
- Return the ticket numbers which have been removed so passengers can be notified

7
src/app.ts Normal file
View File

@ -0,0 +1,7 @@
import express from 'express';
const app = express();
app.listen(3000, () => {
console.log("Started Server on Port 3000")
})

51
src/database/Unit.ts Normal file
View File

@ -0,0 +1,51 @@
import { Database, Statement } from "sqlite";
import { DB } from "./data";
// the following code uses the bang operator in several places when non-null is guaranteed by context for brevity - not a recommended approach overall
export class Unit {
private db: Database | null;
private readonly statements: Statement[];
private constructor(public readonly readOnly: boolean)
{
this.db = null;
this.statements = [];
}
private async init(): Promise<void> {
this.db = await DB.createDBConnection();
if (!this.readOnly) {
await DB.beginTransaction(this.db);
}
}
public async prepare(sql: string, bindings: any | null = null): Promise<Statement> {
// ! due to late init and logically guaranteed non-null - don't do that at home!
const stmt = await this.db!.prepare(sql);
if (bindings !== null){
await stmt!.bind(bindings);
}
this.statements.push(stmt);
return stmt!;
}
public async complete(commit: boolean | null = null): Promise<void> {
if (commit !== null) {
await (commit ? DB.commitTransaction(this.db!) : DB.rollbackTransaction(this.db!));
} else if (!this.readOnly) {
throw new Error('transaction has been opened, requires information if commit or rollback needed');
}
for (const stmt of this.statements) {
await stmt.finalize();
}
await this.db!.close();
}
public static async create(readOnly: boolean): Promise<Unit> {
const unit = new Unit(readOnly);
await unit.init();
return unit;
}
}

27
src/database/data.ts Normal file
View File

@ -0,0 +1,27 @@
import {Database,open} from "sqlite";
import sqlite3 from "sqlite";
export class DB {
static async createDBConnection() : Promise<Database> {
const connection = await open({
filename: `./flights.db`,
driver: sqlite3.Database
});
await connection.exec('PRAGMA foreign_keys = ON;');
return connection;
}
static async beginTransaction(db: Database) {
await db.exec("begin transaction;");
}
static async commitTransaction(db: Database) {
await db.exec("commit;")
}
static async rollbackTransaction(db: Database) {
await db.exec("rollback;")
}
}