diff --git a/Sources/Server/Controllers/AnalyticsController.swift b/Sources/Server/Controllers/AnalyticsController.swift index 90a1144..90c429f 100644 --- a/Sources/Server/Controllers/AnalyticsController.swift +++ b/Sources/Server/Controllers/AnalyticsController.swift @@ -13,17 +13,24 @@ import Vapor struct AnalyticsController: 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)) @@ -32,6 +39,9 @@ struct AnalyticsController: 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)) diff --git a/Sources/Server/Controllers/AnalyticsEntriesController.swift b/Sources/Server/Controllers/AnalyticsEntriesController.swift index 7bce15c..d44d27a 100644 --- a/Sources/Server/Controllers/AnalyticsEntriesController.swift +++ b/Sources/Server/Controllers/AnalyticsEntriesController.swift @@ -15,22 +15,31 @@ struct AnalyticsEntriesController: 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 @@ -42,7 +51,9 @@ struct AnalyticsEntriesController: 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)) diff --git a/Sources/Server/Controllers/AnalyticsEntryController.swift b/Sources/Server/Controllers/AnalyticsEntryController.swift index 6e6925c..01bbf07 100644 --- a/Sources/Server/Controllers/AnalyticsEntryController.swift +++ b/Sources/Server/Controllers/AnalyticsEntryController.swift @@ -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( diff --git a/Sources/Server/Controllers/AndroidRedirectsController.swift b/Sources/Server/Controllers/AndroidRedirectsController.swift index 4899a8c..ed7b0f1 100644 --- a/Sources/Server/Controllers/AndroidRedirectsController.swift +++ b/Sources/Server/Controllers/AndroidRedirectsController.swift @@ -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") } diff --git a/Sources/Server/Controllers/AnnouncementController.swift b/Sources/Server/Controllers/AnnouncementController.swift index 000e719..46856cf 100644 --- a/Sources/Server/Controllers/AnnouncementController.swift +++ b/Sources/Server/Controllers/AnnouncementController.swift @@ -14,17 +14,24 @@ struct AnnouncementController: 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) @@ -38,7 +45,11 @@ struct AnnouncementController: 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) diff --git a/Sources/Server/Controllers/AnnouncementsController.swift b/Sources/Server/Controllers/AnnouncementsController.swift index 97dfc14..814c60f 100644 --- a/Sources/Server/Controllers/AnnouncementsController.swift +++ b/Sources/Server/Controllers/AnnouncementsController.swift @@ -15,16 +15,26 @@ struct AnnouncementsController: 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 { @@ -87,6 +97,9 @@ struct AnnouncementsController: 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)) diff --git a/Sources/Server/Controllers/BusController.swift b/Sources/Server/Controllers/BusController.swift index 4c8a2e7..b6141c4 100644 --- a/Sources/Server/Controllers/BusController.swift +++ b/Sources/Server/Controllers/BusController.swift @@ -14,10 +14,15 @@ struct BusController: 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(_:)) @@ -26,7 +31,10 @@ struct BusController: 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) @@ -44,6 +52,10 @@ struct BusController: 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 { @@ -94,7 +106,10 @@ struct BusController: 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) @@ -110,7 +125,10 @@ struct BusController: 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) diff --git a/Sources/Server/Controllers/BusesController.swift b/Sources/Server/Controllers/BusesController.swift index e9cf706..e9be053 100644 --- a/Sources/Server/Controllers/BusesController.swift +++ b/Sources/Server/Controllers/BusesController.swift @@ -14,16 +14,25 @@ struct BusesController: 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 @@ -44,7 +53,9 @@ struct BusesController: 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 { return Buses.shared.allBusIDs } diff --git a/Sources/Server/Controllers/DataFeedController.swift b/Sources/Server/Controllers/DataFeedController.swift index d5eeda6..59853f6 100644 --- a/Sources/Server/Controllers/DataFeedController.swift +++ b/Sources/Server/Controllers/DataFeedController.swift @@ -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) } diff --git a/Sources/Server/Controllers/LogController.swift b/Sources/Server/Controllers/LogController.swift index e87aa09..af687c9 100644 --- a/Sources/Server/Controllers/LogController.swift +++ b/Sources/Server/Controllers/LogController.swift @@ -14,17 +14,25 @@ struct LogController: 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) @@ -46,7 +54,10 @@ struct LogController: 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) diff --git a/Sources/Server/Controllers/LogsController.swift b/Sources/Server/Controllers/LogsController.swift index 0589649..2940c23 100644 --- a/Sources/Server/Controllers/LogsController.swift +++ b/Sources/Server/Controllers/LogsController.swift @@ -14,23 +14,35 @@ struct LogsController: RouteCollection where DecoderType: ContentDe private let decoder: DecoderType + /// Initializes a new logs 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 logs along with registering a nested route collection for individual log 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: LogController(decoder: self.decoder)) } + /// Creates a new log entry from the request's content, assigns a unique ID, and saves it to the database. + /// - Parameter request: A `Request` object encapsulating details about the incoming request. + /// - Returns: The UUID of the newly created log entry. + /// - Throws: An error if the log data cannot be decoded or saved. private func create(_ request: Request) async throws -> UUID? { let log = try request.content.decode(Log.self, using: self.decoder) log.id = UUID() try await log.save(on: request.db(.psql)) return log.id } - + /// Retrieves all log entry IDs, sorted by the date they were created. + /// - Parameter request: A `Request` object encapsulating details about the incoming request. + /// - Returns: An array of UUIDs representing the IDs of all log entries. + /// - Throws: An error if there is a failure in retrieving the data from the database. + private func read(_ request: Request) async throws -> [UUID] { return try await Log .query(on: request.db(.psql)) diff --git a/Sources/Server/Controllers/MilestoneController.swift b/Sources/Server/Controllers/MilestoneController.swift index 982e805..246edcd 100644 --- a/Sources/Server/Controllers/MilestoneController.swift +++ b/Sources/Server/Controllers/MilestoneController.swift @@ -14,10 +14,16 @@ struct MilestoneController: RouteCollection where DecoderType: Cont private let decoder: DecoderType + /// Initializes a new milestone 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 deleting milestones 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(_:)) @@ -25,7 +31,11 @@ struct MilestoneController: RouteCollection where DecoderType: Cont routes.delete(use: self.delete(_:)) } } - + + /// Retrieves a milestone by its ID. + /// - Parameter request: A `Request` object encapsulating details about the incoming request, including the milestone ID. + /// - Returns: The `Milestone` object requested. + /// - Throws: An `Abort` error if the milestone is not found (404) or the ID is not provided (400). private func read(_ request: Request) async throws -> Milestone { guard let id = request.parameters.get("id", as: UUID.self) else { throw Abort(.badRequest) @@ -39,7 +49,11 @@ struct MilestoneController: RouteCollection where DecoderType: Cont } return milestone } - + /// Updates a milestone by incrementing its progress counter. + /// - Parameter request: A `Request` object encapsulating details about the incoming request, including the milestone ID. + /// - Returns: The updated `Milestone` object. + /// - Throws: An `Abort` error if the milestone is not found (404) or the ID is not provided (400). + private func update(_ request: Request) async throws -> Milestone { guard let id = request.parameters.get("id", as: UUID.self) else { throw Abort(.badRequest) @@ -56,6 +70,10 @@ struct MilestoneController: RouteCollection where DecoderType: Cont return milestone } + /// Deletes a milestone by its ID after verifying the digital signature. + /// - Parameter request: A `Request` object encapsulating details about the incoming request, including the milestone ID and a deletion request with a digital signature. + /// - Returns: The UUID of the deleted milestone. + /// - 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) diff --git a/Sources/Server/Controllers/MilestonesController.swift b/Sources/Server/Controllers/MilestonesController.swift index 3a0ad42..86b496b 100644 --- a/Sources/Server/Controllers/MilestonesController.swift +++ b/Sources/Server/Controllers/MilestonesController.swift @@ -13,17 +13,25 @@ import Vapor struct MilestonesController: RouteCollection where DecoderType: ContentDecoder { private let decoder: DecoderType - + + /// Initializes a new milestones 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 milestones along with registering a nested route collection for individual milestone 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: MilestoneController(decoder: self.decoder)) } - + + /// Creates a new milestone after verifying the digital signature. + /// - Parameter request: A `Request` object encapsulating details about the incoming request. + /// - Returns: The newly created `Milestone` 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 -> Milestone { let milestone = try request.content.decode(Milestone.self, using: self.decoder) guard let data = (milestone.name + milestone.extendedDescription + milestone.goals.description).data(using: .utf8) else { @@ -36,7 +44,9 @@ struct MilestonesController: RouteCollection where DecoderType: Con throw Abort(.forbidden) } } - + /// Reads and returns all milestones from the database. + /// - Parameter request: A `Request` object encapsulating details about the incoming request. + /// - Returns: An array of `Milestone` objects. private func read(_ request: Request) async throws -> [Milestone] { return try await Milestone .query(on: request.db(.psql)) diff --git a/Sources/Server/Controllers/NotificationsController.swift b/Sources/Server/Controllers/NotificationsController.swift index f91c8d9..5fffb1f 100644 --- a/Sources/Server/Controllers/NotificationsController.swift +++ b/Sources/Server/Controllers/NotificationsController.swift @@ -10,7 +10,11 @@ import Vapor /// A structure that registers routes for notifications. /// - Remark: In the context of this structure, the term “route” refers to an HTTP route, not a shuttle route. struct NotificationsController: RouteCollection { - + + + /// Registers routes for managing notification devices within the notifications framework. + /// - Parameter routes: A builder object for registering routes. + /// - Throws: Throws an error if the routes cannot be registered properly. func boot(routes: RoutesBuilder) throws { try routes.register(collection: NotificationsDevicesController(), on: "devices") } diff --git a/Sources/Server/Controllers/NotificationsDeviceController.swift b/Sources/Server/Controllers/NotificationsDeviceController.swift index 4ea561b..4bec074 100644 --- a/Sources/Server/Controllers/NotificationsDeviceController.swift +++ b/Sources/Server/Controllers/NotificationsDeviceController.swift @@ -12,12 +12,21 @@ import Vapor /// - Remark: In the context of this structure, the term “route” refers to an HTTP route, not a shuttle route. struct NotificationsDeviceController: RouteCollection { + /// Registers routes for managing individual notification devices, particularly for creating new device entries. + /// - Parameter routes: A builder object for registering routes. + /// - Throws: Throws an error if the routes cannot be registered properly. + func boot(routes: RoutesBuilder) throws { routes.group(":token") { (routes) in routes.post(use: self.create(_:)) } } + /// Creates or retrieves an existing notification device based on the provided token. + /// - Parameter request: A `Request` object encapsulating details about the incoming request, specifically the device token. + /// - Returns: An `APNSDevice` object representing the notification device. + /// - Throws: An `Abort` error if no token is provided (400) or if database operations fail. + private func create(_ request: Request) async throws -> APNSDevice { guard let token = request.parameters.get("token") else { throw Abort(.badRequest) diff --git a/Sources/Server/Controllers/NotificationsDevicesController.swift b/Sources/Server/Controllers/NotificationsDevicesController.swift index 00ff610..b27ef6e 100644 --- a/Sources/Server/Controllers/NotificationsDevicesController.swift +++ b/Sources/Server/Controllers/NotificationsDevicesController.swift @@ -11,6 +11,10 @@ import Vapor /// - Remark: In the context of this structure, the term “route” refers to an HTTP route, not a shuttle route. struct NotificationsDevicesController: RouteCollection { + /// Registers a route collection that manages individual notification devices. + /// - Parameter routes: A builder object for registering routes. + /// - Throws: Throws an error if the route collection cannot be registered properly. + func boot(routes: RoutesBuilder) throws { try routes.register(collection: NotificationsDeviceController()) } diff --git a/Sources/Server/Controllers/RedirectsController.swift b/Sources/Server/Controllers/RedirectsController.swift index 083d0b1..3d1c192 100644 --- a/Sources/Server/Controllers/RedirectsController.swift +++ b/Sources/Server/Controllers/RedirectsController.swift @@ -12,6 +12,9 @@ import Vapor /// - Remark: In the context of this structure, the term “route” refers to an HTTP route, not a shuttle route. struct RedirectsController: RouteCollection { + /// Registers routes for main, beta, and TestFlight redirects along with nested route collections for platform-specific redirects. + /// - 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(_:)) @@ -44,7 +47,10 @@ struct RedirectsController: RouteCollection { return request.redirect(to: "/web") } } - + + /// Redirects users to the beta section of their respective platform, based on the User-Agent header. + /// - Parameter request: The request object containing the User-Agent header. + /// - Returns: A redirect response to the beta section for the appropriate platform. private func beta(_ request: Request) -> Response { guard let agent = request.headers["User-Agent"].first else { return request.redirect(to: "/web/beta") @@ -60,6 +66,9 @@ struct RedirectsController: RouteCollection { } } + /// Redirects users directly to the TestFlight beta section for iOS. + /// - Parameter request: The request object. + /// - Returns: A redirect response to the TestFlight beta page. private func testflight(_ request: Request) -> Response { return request.redirect(to: "/swiftui/beta") } diff --git a/Sources/Server/Controllers/RoutesController.swift b/Sources/Server/Controllers/RoutesController.swift index af05f93..cc55afd 100644 --- a/Sources/Server/Controllers/RoutesController.swift +++ b/Sources/Server/Controllers/RoutesController.swift @@ -12,11 +12,18 @@ import Vapor /// - Remark: In the context of this structure, the term “route” could refer to either an HTTP route or a shuttle route, depending on the local context. struct RoutesController: RouteCollection { + /// Registers an HTTP route that is used to read shuttle route data. + /// - Parameter routes: A builder object for registering routes. + /// - Throws: Throws an error if the route cannot be registered properly. func boot(routes: any RoutesBuilder) throws { // In the context of this method, the term “route” refers to an HTTP route, not a shuttle route. routes.get(use: self.read(_:)) } + /// Retrieves all active shuttle routes from the database. + /// - Parameter request: A `Request` object encapsulating details about the incoming request. + /// - Returns: An array of `Route` objects representing the active shuttle routes. + /// - Throws: Throws an error if there is a failure in fetching the data from the database. private func read(_ request: Request) async throws -> [Route] { // In the context of this method, the term “route” refers to a shuttle route, not an HTTP route. return try await Route diff --git a/Sources/Server/Controllers/ScheduleController.swift b/Sources/Server/Controllers/ScheduleController.swift index 7348c2b..219dd1b 100644 --- a/Sources/Server/Controllers/ScheduleController.swift +++ b/Sources/Server/Controllers/ScheduleController.swift @@ -12,10 +12,17 @@ import Vapor /// - Remark: In the context of this structure, the term “route” refers to an HTTP route, not a shuttle route. struct ScheduleController: RouteCollection { + /// Registers an HTTP route that redirects to the schedule information. + /// - Parameter routes: A builder object for registering routes. + /// - Throws: Throws an error if the route cannot be registered properly. + func boot(routes: any RoutesBuilder) throws { routes.get(use: self.index(_:)) } + /// Redirects the client to a static JSON file that contains schedule information. + /// - Parameter request: A `Request` object encapsulating details about the incoming request. + /// - Returns: A `Response` object that performs the redirect to the schedule JSON file. private func index(_ request: Request) -> Response { return request.redirect(to: "/schedule.json") } diff --git a/Sources/Server/Controllers/StopController.swift b/Sources/Server/Controllers/StopController.swift index a80af56..b91e9ea 100644 --- a/Sources/Server/Controllers/StopController.swift +++ b/Sources/Server/Controllers/StopController.swift @@ -11,12 +11,18 @@ import Vapor /// - Remark: In the context of this structure, the term “route” refers to an HTTP route, not a shuttle route. struct StopController: RouteCollection { + /// Registers HTTP routes that manage access to information about individual shuttle stops. + /// - Parameter routes: A builder object for registering routes. + /// - Throws: Throws an error if the routes cannot be registered properly. func boot(routes: any RoutesBuilder) throws { routes.group(":shortname") { (routes) in routes.get(use: self.read(_:)) } } - + + /// Provides a placeholder response for a shuttle stop based on its short name. This method needs to be implemented to return actual stop information. + /// - Parameter request: A `Request` object encapsulating details about the incoming request, including the short name of the shuttle stop. + /// - Returns: A temporary redirect response as a placeholder until actual implementation is completed. private func read(_ request: Request) -> Response { // TODO: Return something that’s actually useful return request.redirect(to: "/", redirectType: .temporary) diff --git a/Sources/Server/Controllers/StopsController.swift b/Sources/Server/Controllers/StopsController.swift index e024177..77e8cd7 100644 --- a/Sources/Server/Controllers/StopsController.swift +++ b/Sources/Server/Controllers/StopsController.swift @@ -12,11 +12,18 @@ import Vapor /// - Remark: In the context of this structure, the term “route” refers to an HTTP route, not a shuttle route. struct StopsController: RouteCollection { + /// Registers HTTP routes for retrieving shuttle stop data and manages individual stop details through a nested controller. + /// - Parameter routes: A builder object for registering routes. + /// - Throws: Throws an error if the routes cannot be registered properly. func boot(routes: any RoutesBuilder) throws { routes.get(use: self.read(_:)) try routes.register(collection: StopController()) } + /// Retrieves all active shuttle stops from the database, ensuring each stop is returned only once. + /// - Parameter request: A `Request` object encapsulating details about the incoming request. + /// - Returns: An array of `Stop` objects representing active shuttle stops. + /// - Throws: Throws an error if there is a failure in fetching the data from the database. private func read(_ request: Request) async throws -> [Stop] { let stops = try await Stop .query(on: request.db(.sqlite)) diff --git a/Sources/Server/Controllers/SwiftUIRedirectsController.swift b/Sources/Server/Controllers/SwiftUIRedirectsController.swift index 4e92264..628ef87 100644 --- a/Sources/Server/Controllers/SwiftUIRedirectsController.swift +++ b/Sources/Server/Controllers/SwiftUIRedirectsController.swift @@ -12,15 +12,25 @@ import Vapor /// - Remark: In the context of this structure, the term “route” refers to an HTTP route, not a shuttle route. struct SwiftUIRedirectsController: RouteCollection { + /// Registers HTTP routes for redirecting users to the main and beta versions of the SwiftUI app. + /// - Parameter routes: A builder object for registering routes. + /// - Throws: Throws an error if the routes cannot be registered properly. + func boot(routes: any RoutesBuilder) throws { routes.get(use: self.index(_:)) routes.get("beta", use: self.beta(_:)) } + /// Redirects to the main SwiftUI app page on the App Store. + /// - Parameter request: A `Request` object encapsulating details about the incoming request. + /// - Returns: A `Response` object that performs a redirect to the app's page on the App Store. private func index(_ request: Request) -> Response { return request.redirect(to: "https://apps.apple.com/us/app/shuttle-tracker/id1583503452") } + /// Redirects to the beta version of the SwiftUI app on TestFlight. + /// - 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 on TestFlight. private func beta(_ request: Request) -> Response { return request.redirect(to: "https://testflight.apple.com/join/GsmZkfgd") } diff --git a/Sources/Server/Controllers/VersionController.swift b/Sources/Server/Controllers/VersionController.swift index f559708..0439b59 100644 --- a/Sources/Server/Controllers/VersionController.swift +++ b/Sources/Server/Controllers/VersionController.swift @@ -12,10 +12,16 @@ import Vapor /// - Remark: In the context of this structure, the term “route” refers to an HTTP route, not a shuttle route. struct VersionController: RouteCollection { + /// Registers an HTTP route that provides the API version number. + /// - Parameter routes: A builder object for registering routes. + /// - Throws: Throws an error if the route cannot be registered properly. func boot(routes: any RoutesBuilder) throws { routes.get(use: self.index(_:)) } - + + /// Provides the current version number of the API. + /// - Parameter request: A `Request` object encapsulating details about the incoming request. + /// - Returns: An unsigned integer representing the API version number. private func index(_: Request) -> UInt { return Constants.apiVersion } diff --git a/Sources/Server/Controllers/WebRedirectsController.swift b/Sources/Server/Controllers/WebRedirectsController.swift index bf95e30..141505d 100644 --- a/Sources/Server/Controllers/WebRedirectsController.swift +++ b/Sources/Server/Controllers/WebRedirectsController.swift @@ -12,16 +12,28 @@ import Vapor /// - Remark: In the context of this structure, the term “route” refers to an HTTP route, not a shuttle route. struct WebRedirectsController: RouteCollection { + /// Registers HTTP routes for redirecting users to the main and beta versions of the Web client. + /// - Parameter routes: A builder object for registering routes. + /// - Throws: Throws an error if the routes cannot be registered properly. func boot(routes: any RoutesBuilder) throws { routes.get(use: self.index(_:)) routes.get("beta", use: self.beta(_:)) } + /// Redirects to the main Web client page. + /// - Parameter request: A `Request` object encapsulating details about the incoming request. + /// - Returns: A `Response` object that performs a redirect to the main web client's URL. + private func index(_ request: Request) -> Response { + // Directs users to the main page of the Web client return request.redirect(to: "https://web.shuttletracker.app") } + /// Redirects to the beta version of the Web client. + /// - Parameter request: A `Request` object encapsulating details about the incoming request. + /// - Returns: A `Response` object that performs a redirect to the beta version of the web client's URL. private func beta(_ request: Request) -> Response { + // Directs users to the beta page of the Web client return request.redirect(to: "https://staging.web.shuttletracker.app") }