Skip to content
This repository was archived by the owner on Sep 22, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c0a17d0
added inline documentation to analyticsController.swift
user7239232932 Apr 12, 2024
504f228
added inline documentation to analyticsEntriesController.swift
user7239232932 Apr 12, 2024
f430819
added inline documentation to analyticsEntryController.swift
user7239232932 Apr 12, 2024
0e0942d
added inline documentation to AndroidRedirectsController.swift
user7239232932 Apr 12, 2024
2d87b1e
added inline documentation to AnnouncementController.swift
user7239232932 Apr 12, 2024
2f9bead
added inline documentation to AnnouncementsController.swift
user7239232932 Apr 12, 2024
1b72958
added inline documentation to BusController.swift
user7239232932 Apr 12, 2024
a033cb5
added inline documentation to BusesController.swift
user7239232932 Apr 12, 2024
621ca4d
added inline documentation to DataFeedController.swift
user7239232932 Apr 14, 2024
deac79c
added inline documentation to LogController.swift
user7239232932 Apr 14, 2024
405d49e
added inline documentation to LogsController.swift
user7239232932 Apr 14, 2024
93b384b
added inline documentation to MilestoneController.swift
user7239232932 Apr 14, 2024
f240b5c
added inline documentation to MilestonesController.swift
user7239232932 Apr 14, 2024
7ec6f6e
added inline documentation to NotificationsController.swift
user7239232932 Apr 14, 2024
b78f73d
added inline documentation to NotificationsDeviceController.swift
user7239232932 Apr 14, 2024
44e780e
added inline documentation to NotificationsDevicesController.swift
user7239232932 Apr 14, 2024
df4e72b
added inline documentation to RedirectsController.swift
user7239232932 Apr 14, 2024
5730e0b
added inline documentation to RoutesController.swift
user7239232932 Apr 14, 2024
30756ac
added inline documentation to ScheduleController.swift
user7239232932 Apr 14, 2024
892617f
added inline documentation to StopController.swift
user7239232932 Apr 14, 2024
31fcafb
added inline documentation to StopsController.swift
user7239232932 Apr 14, 2024
93cdd32
added inline documentation to SwiftUIReadirectsController.swift
user7239232932 Apr 14, 2024
5458bec
added inline documentation to VersionController.swift
user7239232932 Apr 14, 2024
630dcf1
added inline documentation to WebRedirectsController.swift
user7239232932 Apr 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Sources/Server/Controllers/AnalyticsController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,24 @@ import Vapor
struct AnalyticsController<DecoderType>: RouteCollection where DecoderType: ContentDecoder {

private let decoder: DecoderType
/// Initialize a new analytics controller with the specified decoder.
/// - Parameter decoder: An object that conforms to the `ContentDecoder` protocol.

init(decoder: DecoderType) {
self.decoder = decoder
}

/// Registers all the routes related to analytics.
/// - Parameter routes: A builder object for registering routes.
func boot(routes: any RoutesBuilder) throws {
routes.get("userids", use: self.userIDs(_:))
routes.get("boardbus", "average", use: self.boardBusAverage(_:))
try routes.register(collection: AnalyticsEntriesController(decoder: self.decoder), on: "entries")
}

/// Retrieves a list of unique user IDs from analytics entries.
/// - Parameter request: A `Request` object encapsulating details about the incoming request.
/// - Returns: An array of `UUID` representing unique user IDs.
private func userIDs(_ request: Request) async throws -> [UUID] {
return try await AnalyticsEntry
.query(on: request.db(.psql))
Expand All @@ -32,6 +39,9 @@ struct AnalyticsController<DecoderType>: RouteCollection where DecoderType: Cont
.compactMap { return $0 } // Remove nil elements
}

/// Calculates the average number of board bus counts per user.
/// - Parameter request: A `Request` object encapsulating details about the incoming request.
/// - Returns: The average number of times users have boarded the bus.
private func boardBusAverage(_ request: Request) async throws -> Double {
let chunks = try await AnalyticsEntry
.query(on: request.db(.psql))
Expand Down
15 changes: 13 additions & 2 deletions Sources/Server/Controllers/AnalyticsEntriesController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,31 @@ struct AnalyticsEntriesController<DecoderType>: RouteCollection where DecoderTyp

private let decoder: DecoderType

/// Initializes a new analytics entries controller with the specified decoder.
/// - Parameter decoder: An object that conforms to the `ContentDecoder` protocol, used for decoding the content of incoming requests.
init(decoder: DecoderType) {
self.decoder = decoder
}

/// Registers routes for creating, reading, and counting analytics entries along with a nested route collection.
/// - Parameter routes: A builder object for registering routes.
func boot(routes: any RoutesBuilder) throws {
routes.post(use: self.create(_:))
routes.get(use: self.read(_:))
routes.get("count", use: self.count(_:))
try routes.register(collection: AnalyticsEntryController())
}

/// Creates a new analytics entry from the request content and saves it to the database.
/// - Parameter request: A `Request` object encapsulating details about the incoming request.
/// - Returns: The newly created `AnalyticsEntry` object after it is saved to the database.
private func create(_ request: Request) async throws -> AnalyticsEntry {
let analyticsEntry = try request.content.decode(AnalyticsEntry.self, using: self.decoder)
try await analyticsEntry.save(on: request.db(.psql))
return analyticsEntry
}
/// Retrieves all analytics entries or entries matching a specific user ID if provided in the query parameters.
/// - Parameter request: A `Request` object encapsulating details about the incoming request.
/// - Returns: An array of `AnalyticsEntry` objects matching the query.

private func read(_ request: Request) async throws -> [AnalyticsEntry] {
var query = AnalyticsEntry
Expand All @@ -42,7 +51,9 @@ struct AnalyticsEntriesController<DecoderType>: RouteCollection where DecoderTyp
}
return try await query.all()
}

/// Counts the total number of analytics entries in the database.
/// - Parameter request: A `Request` object encapsulating details about the incoming request.
/// - Returns: The count of `AnalyticsEntry` objects in the database.
private func count(_ request: Request) async throws -> Int {
return try await AnalyticsEntry
.query(on: request.db(.psql))
Expand Down
9 changes: 8 additions & 1 deletion Sources/Server/Controllers/AnalyticsEntryController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,19 @@ import Vapor
/// A structure that registers routes for managing individual analytics entries.
/// - Remark: In the context of this structure, the term “route” refers to an HTTP route, not a shuttle route.
struct AnalyticsEntryController: RouteCollection {


/// Registers a route for reading an individual analytics entry.
/// - Parameter routes: A builder object for registering routes. Routes are grouped by entry ID.
func boot(routes: any RoutesBuilder) throws {
routes.group(":id") { (routes) in
routes.get(use: self.read(_:))
}
}

/// Retrieves an analytics entry by its ID.
/// - Parameter request: A `Request` object encapsulating details about the incoming request, including the entry ID.
/// - Returns: The requested `AnalyticsEntry` object.
/// - Throws: An `Abort` error if the entry is not found, returning a 404 Not Found status.

private func read(_ request: Request) async throws -> AnalyticsEntry {
let entry = try await AnalyticsEntry.find(
Expand Down
8 changes: 8 additions & 0 deletions Sources/Server/Controllers/AndroidRedirectsController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,23 @@ import Vapor
/// - Remark: In the context of this structure, the term “route” refers to an HTTP route, not a shuttle route.
struct AndroidRedirectsController: RouteCollection {

/// Registers routes for redirecting to the main and beta versions of the Android app.
/// - Parameter routes: A builder object for registering routes
func boot(routes: any RoutesBuilder) throws {
routes.get(use: self.index(_:))
routes.get("beta", use: self.beta(_:))
}

/// Redirects to the main Android app page on Google Play.
/// - Parameter request: A `Request` object encapsulating details about the incoming request.
/// - Returns: A `Response` object that performs a redirect to the app's page.
private func index(_ request: Request) -> Response {
return request.redirect(to: "https://play.google.com/store/apps/details?id=edu.rpi.shuttletracker")
}

/// Redirects to the beta version of the Android app page on Google Play.
/// - Parameter request: A `Request` object encapsulating details about the incoming request.
/// - Returns: A `Response` object that performs a redirect to the beta app's page.
private func beta(_ request: Request) -> Response {
return request.redirect(to: "https://play.google.com/store/apps/details?id=edu.rpi.shuttletracker")
}
Expand Down
15 changes: 13 additions & 2 deletions Sources/Server/Controllers/AnnouncementController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,24 @@ struct AnnouncementController<DecoderType>: RouteCollection where DecoderType: C

private let decoder: DecoderType

/// Initializes a new announcement controller with the specified decoder.
/// - Parameter decoder: An object that conforms to the `ContentDecoder` protocol, used for decoding the content of incoming requests.
init(decoder: DecoderType) {
self.decoder = decoder
}

/// Registers routes for accessing and deleting announcements by their ID.
/// - Parameter routes: A builder object for registering routes.
func boot(routes: any RoutesBuilder) throws {
routes.group(":id") { (routes) in
routes.get(use: self.read(_:))
routes.delete(use: self.delete(_:))
}
}

/// Retrieves an announcement by its ID.
/// - Parameter request: A `Request` object encapsulating details about the incoming request, including the announcement ID.
/// - Returns: The `Announcement` object requested.
/// - Throws: An `Abort` error if the announcement is not found (404) or if the ID is not provided (400).
private func read(_ request: Request) async throws -> Announcement {
guard let id = request.parameters.get("id", as: UUID.self) else {
throw Abort(.badRequest)
Expand All @@ -38,7 +45,11 @@ struct AnnouncementController<DecoderType>: RouteCollection where DecoderType: C
}
return announcement
}


/// Deletes an announcement by its ID after verifying the provided digital signature.
/// - Parameter request: A `Request` object encapsulating details about the incoming request, including the announcement ID and a deletion request with a digital signature.
/// - Returns: The UUID of the deleted announcement.
/// - Throws: An `Abort` error if the ID is not provided (400), if signature verification fails (403), or if there are server issues during verification (500).
private func delete(_ request: Request) async throws -> UUID {
guard let id = request.parameters.get("id", as: UUID.self) else {
throw Abort(.badRequest)
Expand Down
13 changes: 13 additions & 0 deletions Sources/Server/Controllers/AnnouncementsController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,26 @@ struct AnnouncementsController<DecoderType>: RouteCollection where DecoderType:

private let decoder: DecoderType

/// Initializes a new announcements controller with the specified decoder.
/// - Parameter decoder: An object that conforms to the `ContentDecoder` protocol, used for decoding the content of incoming requests.

init(decoder: DecoderType) {
self.decoder = decoder
}

/// Registers routes for creating and reading announcements along with registering a nested route collection for individual announcement management.
/// - Parameter routes: A builder object for registering routes.

func boot(routes: any RoutesBuilder) throws {
routes.post(use: self.create(_:))
routes.get(use: self.read(_:))
try routes.register(collection: AnnouncementController(decoder: self.decoder))
}

/// Creates a new announcement after verifying the digital signature and sends a push notification to all Apple devices registered in the database.
/// - Parameter request: A `Request` object encapsulating details about the incoming request.
/// - Returns: The newly created `Announcement` object after it is saved to the database.
/// - Throws: An `Abort` error if the digital signature verification fails (403) or if internal issues occur (500).
private func create(_ request: Request) async throws -> Announcement {
let announcement = try request.content.decode(Announcement.self, using: self.decoder)
guard let data = (announcement.subject + announcement.body).data(using: .utf8) else {
Expand Down Expand Up @@ -87,6 +97,9 @@ struct AnnouncementsController<DecoderType>: RouteCollection where DecoderType:
}
}

/// Reads and returns all announcements from the database.
/// - Parameter request: A `Request` object encapsulating details about the incoming request.
/// - Returns: An array of `Announcement` objects.
private func read(_ request: Request) async throws -> [Announcement] {
return try await Announcement
.query(on: request.db(.psql))
Expand Down
24 changes: 21 additions & 3 deletions Sources/Server/Controllers/BusController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ struct BusController<DecoderType>: RouteCollection where DecoderType: ContentDec

private let decoder: DecoderType

/// Initializes a new bus controller with the specified decoder.
/// - Parameter decoder: An object that conforms to the `ContentDecoder` protocol, used for decoding the content of incoming requests.

init(decoder: DecoderType) {
self.decoder = decoder
}

/// Registers routes for accessing, updating, and managing the board and leave operations of shuttle buses.
/// - Parameter routes: A builder object for registering routes.
func boot(routes: any RoutesBuilder) throws {
routes.group(":id") { (routes) in
routes.get(use: self.read(_:))
Expand All @@ -26,7 +31,10 @@ struct BusController<DecoderType>: RouteCollection where DecoderType: ContentDec
routes.put("leave", use: self.leave(_:))
}
}

/// Reads the current location of a bus by its ID.
/// - Parameter request: A `Request` object encapsulating details about the incoming request, including the bus ID.
/// - Returns: The current `Bus.Location` of the requested bus.
/// - Throws: An `Abort` error if the bus ID is not provided or if no bus is found with that ID.
private func read(_ request: Request) async throws -> Bus.Location {
guard let id = request.parameters.get("id", as: Int.self) else {
throw Abort(.badRequest)
Expand All @@ -44,6 +52,10 @@ struct BusController<DecoderType>: RouteCollection where DecoderType: ContentDec
return location
}

/// Updates the location and route status of a bus based on the provided data.
/// - Parameter request: A `Request` object encapsulating details about the incoming request, including the bus ID and the new location data.
/// - Returns: An optional `Bus.Resolved` object representing the updated bus state.
/// - Throws: An `Abort` error if the bus ID is not provided, if the bus cannot be updated, or if it conflicts with route checks.
private func update(_ request: Request) async throws -> Bus.Resolved? {
// In the context of this method, the term “route” refers to a shuttle route, not an HTTP route.
guard let id = request.parameters.get("id", as: Int.self) else {
Expand Down Expand Up @@ -94,7 +106,10 @@ struct BusController<DecoderType>: RouteCollection where DecoderType: ContentDec
try await bus.update(on: request.db(.sqlite))
return bus.resolved
}

/// Increments the congestion count of a bus when a passenger boards.
/// - Parameter request: A `Request` object encapsulating details about the incoming request, including the bus ID.
/// - Returns: The updated congestion level of the bus.
/// - Throws: An `Abort` error if the bus ID is not provided or if no bus is found with that ID.
private func board(_ request: Request) async throws -> Int? {
guard let id = request.parameters.get("id", as: Int.self) else {
throw Abort(.badRequest)
Expand All @@ -110,7 +125,10 @@ struct BusController<DecoderType>: RouteCollection where DecoderType: ContentDec
try await bus.update(on: request.db(.sqlite))
return bus.congestion
}

/// Decrements the congestion count of a bus when a passenger leaves.
/// - Parameter request: A `Request` object encapsulating details about the incoming request, including the bus ID.
/// - Returns: The updated congestion level of the bus.
/// - Throws: An `Abort` error if the bus ID is not provided or if no bus is found with that ID.
private func leave(_ request: Request) async throws -> Int? {
guard let id = request.parameters.get("id", as: Int.self) else {
throw Abort(.badRequest)
Expand Down
15 changes: 13 additions & 2 deletions Sources/Server/Controllers/BusesController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,25 @@ struct BusesController<DecoderType>: RouteCollection where DecoderType: ContentD

private let decoder: DecoderType

/// Initializes a new buses controller with the specified decoder.
/// - Parameter decoder: An object that conforms to the `ContentDecoder` protocol, used for decoding the content of incoming requests.

init(decoder: DecoderType) {
self.decoder = decoder
}

/// Registers routes for reading specific or all shuttle bus data along with registering a nested route collection for individual bus management.
/// - Parameter routes: A builder object for registering routes.
func boot(routes: any RoutesBuilder) throws {
routes.get(use: self.read(_:))
routes.get("all", use: self.all(_:))
try routes.register(collection: BusController(decoder: self.decoder))
}


/// Retrieves detailed resolved data for shuttle buses that are currently active on their routes.
/// - Parameter request: A `Request` object encapsulating details about the incoming request.
/// - Returns: An array of `Bus.Resolved` representing each active bus with details resolved based on their current route.
/// - Throws: Throws an error if the bus data or route data could not be fetched from the database.
private func read(_ request: Request) async throws -> [Bus.Resolved] {
// In the context of this method, the term “route” refers to a shuttle route, not an HTTP route.
let routes = try await Route
Expand All @@ -44,7 +53,9 @@ struct BusesController<DecoderType>: RouteCollection where DecoderType: ContentD
}
}
}

/// Provides a set of all bus IDs currently tracked.
/// - Parameter request: A `Request` object encapsulating details about the incoming request.
/// - Returns: A set containing the IDs of all buses.
private func all(_: Request) -> Set<Int> {
return Buses.shared.allBusIDs
}
Expand Down
8 changes: 8 additions & 0 deletions Sources/Server/Controllers/DataFeedController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,18 @@ import Vapor
/// - Remark: In the context of this structure, the term “route” refers to an HTTP route, not a shuttle route.
struct DataFeedController: RouteCollection {

/// Registers a route that provides access to a data feed.
/// - Parameter routes: A builder object for registering routes.
func boot(routes: any RoutesBuilder) throws {
routes.get(use: self.index(_:))
}


/// Returns the contents of a data feed URL as a string.
/// - Parameter request: A `Request` object encapsulating details about the incoming request.
/// - Returns: A string containing the contents of the data feed.
/// - Throws: Throws an error if the contents cannot be loaded from the specified URL.

private func index(_: Request) throws -> String {
return try String(contentsOf: Constants.dataFeedURL)
}
Expand Down
15 changes: 13 additions & 2 deletions Sources/Server/Controllers/LogController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,25 @@ struct LogController<DecoderType>: RouteCollection where DecoderType: ContentDec

private let decoder: DecoderType

/// Initializes a new log controller with the specified decoder.
/// - Parameter decoder: An object that conforms to the `ContentDecoder` protocol, used for decoding the content of incoming requests.
init(decoder: DecoderType) {
self.decoder = decoder
}

/// Registers routes for accessing and deleting logs by their ID.
/// - Parameter routes: A builder object for registering routes.
func boot(routes: any RoutesBuilder) throws {
routes.group(":id") { (routes) in
routes.get(use: self.read(_:))
routes.delete(use: self.delete(_:))
}
}


/// Retrieves a log by its ID after verifying the digital signature.
/// - Parameter request: A `Request` object encapsulating details about the incoming request, including the log ID.
/// - Returns: The `Log` object requested.
/// - Throws: An `Abort` error if the log is not found (404), the ID is not provided (400), or the digital signature verification fails (403).
private func read(_ request: Request) async throws -> Log {
guard let id = request.parameters.get("id", as: UUID.self) else {
throw Abort(.badRequest)
Expand All @@ -46,7 +54,10 @@ struct LogController<DecoderType>: RouteCollection where DecoderType: ContentDec
throw Abort(.forbidden)
}
}

/// Deletes a log by its ID after verifying the digital signature.
/// - Parameter request: A `Request` object encapsulating details about the incoming request, including the log ID and a deletion request with a digital signature.
/// - Returns: The UUID of the deleted log as a string.
/// - Throws: An `Abort` error if the ID is not provided (400), if signature verification fails (403), or if there are server issues during verification (500).
private func delete(_ request: Request) async throws -> String {
guard let id = request.parameters.get("id", as: UUID.self) else {
throw Abort(.badRequest)
Expand Down
Loading