From e0634f4de5718bae9cfdab3b68c3ae1da0846c9e Mon Sep 17 00:00:00 2001
From: Jim Wallace <james.wallace@uwaterloo.ca>
Date: Tue, 19 Dec 2023 12:09:43 -0500
Subject: [PATCH] Re-organized Data Collection andrefactored Reddit

---
 .../Top 20k Subreddit Download.swift          |  0
 .../{ => Legacy}/20 Newsgroups.swift          |  0
 .../AuthResponse.swift}                       |  2 +-
 .../Session + Authentication.swift            | 88 +++++++++++++++++++
 .../{ => Data Types}/AnyDecodableValue.swift  |  0
 .../Reddit/{ => Data Types}/Listing.swift     |  2 +-
 .../{ => Data Types}/RedditComment.swift      |  0
 .../{ => Data Types}/RedditCommentData.swift  |  0
 .../{ => Data Types}/RedditContainer.swift    |  0
 .../{ => Data Types}/RedditDataItem.swift     |  0
 .../{ => Data Types}/RedditSubmission.swift   |  0
 .../RedditSubmissionData.swift                |  0
 .../{ => Data Types}/RedditThread.swift       |  0
 .../RedditClient + Comment Search.swift       | 11 +--
 .../RedditClient + Subreddit Search.swift     |  8 +-
 .../RedditClient + User Search.swift          | 17 +---
 .../{ => Reddit}/ReadRedditCommentFile.swift  |  0
 .../{RedditClient.swift => Session.swift}     | 74 ++--------------
 ...itClientError.swift => SessionError.swift} |  2 +-
 .../readRedditSubmissionData.swift            |  0
 ...RedditClient.swift => Session Tests.swift} | 18 ++--
 21 files changed, 118 insertions(+), 104 deletions(-)
 rename Sources/SwiftNLP/1. Data Collection/{ => CWorld.AI}/Top 20k Subreddit Download.swift (100%)
 rename Sources/SwiftNLP/1. Data Collection/{ => Legacy}/20 Newsgroups.swift (100%)
 rename Sources/SwiftNLP/1. Data Collection/Reddit/{RedditAuthResponse.swift => Authentication/AuthResponse.swift} (97%)
 create mode 100644 Sources/SwiftNLP/1. Data Collection/Reddit/Authentication/Session + Authentication.swift
 rename Sources/SwiftNLP/1. Data Collection/Reddit/{ => Data Types}/AnyDecodableValue.swift (100%)
 rename Sources/SwiftNLP/1. Data Collection/Reddit/{ => Data Types}/Listing.swift (97%)
 rename Sources/SwiftNLP/1. Data Collection/Reddit/{ => Data Types}/RedditComment.swift (100%)
 rename Sources/SwiftNLP/1. Data Collection/Reddit/{ => Data Types}/RedditCommentData.swift (100%)
 rename Sources/SwiftNLP/1. Data Collection/Reddit/{ => Data Types}/RedditContainer.swift (100%)
 rename Sources/SwiftNLP/1. Data Collection/Reddit/{ => Data Types}/RedditDataItem.swift (100%)
 rename Sources/SwiftNLP/1. Data Collection/Reddit/{ => Data Types}/RedditSubmission.swift (100%)
 rename Sources/SwiftNLP/1. Data Collection/Reddit/{ => Data Types}/RedditSubmissionData.swift (100%)
 rename Sources/SwiftNLP/1. Data Collection/Reddit/{ => Data Types}/RedditThread.swift (100%)
 rename Sources/SwiftNLP/1. Data Collection/Reddit/{ => Network Endpoints}/RedditClient + Comment Search.swift (90%)
 rename Sources/SwiftNLP/1. Data Collection/Reddit/{ => Network Endpoints}/RedditClient + Subreddit Search.swift (91%)
 rename Sources/SwiftNLP/1. Data Collection/Reddit/{ => Network Endpoints}/RedditClient + User Search.swift (96%)
 rename Sources/SwiftNLP/1. Data Collection/{ => Reddit}/ReadRedditCommentFile.swift (100%)
 rename Sources/SwiftNLP/1. Data Collection/Reddit/{RedditClient.swift => Session.swift} (61%)
 rename Sources/SwiftNLP/1. Data Collection/Reddit/{RedditClientError.swift => SessionError.swift} (97%)
 rename Sources/SwiftNLP/1. Data Collection/{ => Reddit}/readRedditSubmissionData.swift (100%)
 rename Tests/SwiftNLPTests/Reddit API/{RedditClient.swift => Session Tests.swift} (91%)

diff --git a/Sources/SwiftNLP/1. Data Collection/Top 20k Subreddit Download.swift b/Sources/SwiftNLP/1. Data Collection/CWorld.AI/Top 20k Subreddit Download.swift
similarity index 100%
rename from Sources/SwiftNLP/1. Data Collection/Top 20k Subreddit Download.swift
rename to Sources/SwiftNLP/1. Data Collection/CWorld.AI/Top 20k Subreddit Download.swift
diff --git a/Sources/SwiftNLP/1. Data Collection/20 Newsgroups.swift b/Sources/SwiftNLP/1. Data Collection/Legacy/20 Newsgroups.swift
similarity index 100%
rename from Sources/SwiftNLP/1. Data Collection/20 Newsgroups.swift
rename to Sources/SwiftNLP/1. Data Collection/Legacy/20 Newsgroups.swift
diff --git a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditAuthResponse.swift b/Sources/SwiftNLP/1. Data Collection/Reddit/Authentication/AuthResponse.swift
similarity index 97%
rename from Sources/SwiftNLP/1. Data Collection/Reddit/RedditAuthResponse.swift
rename to Sources/SwiftNLP/1. Data Collection/Reddit/Authentication/AuthResponse.swift
index 3414d7cf..0f53233e 100644
--- a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditAuthResponse.swift	
+++ b/Sources/SwiftNLP/1. Data Collection/Reddit/Authentication/AuthResponse.swift	
@@ -21,7 +21,7 @@
 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 // OTHER DEALINGS IN THE SOFTWARE.
 
-struct RedditAuthResponse: Codable
+struct AuthResponse: Codable
 {
     let access_token: String
     let token_type: String
diff --git a/Sources/SwiftNLP/1. Data Collection/Reddit/Authentication/Session + Authentication.swift b/Sources/SwiftNLP/1. Data Collection/Reddit/Authentication/Session + Authentication.swift
new file mode 100644
index 00000000..819eaf27
--- /dev/null
+++ b/Sources/SwiftNLP/1. Data Collection/Reddit/Authentication/Session + Authentication.swift	
@@ -0,0 +1,88 @@
+// Copyright (c) 2023 Jim Wallace
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+
+import Foundation
+
+extension Session {
+    
+    var isAuthenticated: Bool {
+        return access_token != nil
+    }
+    
+    // Authenticate with the Reddit API
+    // After this call, we should have an authentication token, and be able to make requests
+    func authenticate() async throws -> Bool {
+        
+        guard let client_id = client_id, let client_secret = client_secret else {
+            debugPrint("Must initialize client_Id and client_secret to authenticate.")
+            return false
+        }
+        
+        // Create a data request
+        var request = URLRequest(url: URL(string: baseAPIURL + "v1/access_token")!)
+        request.httpMethod = "POST"
+        
+        // Set headers
+        request.allHTTPHeaderFields = httpHeaders
+        let credentials = "\(client_id):\(client_secret)"
+        let encodedCredentials = Data(credentials.utf8).base64EncodedString()
+        request.addValue("Basic \(encodedCredentials)", forHTTPHeaderField: "Authorization")
+        
+        // Set body
+        request.httpBody = parameters
+            .map { "\($0.key)=\($0.value)" }
+            .joined(separator: "&")
+            .data(using: .utf8)
+        
+        let (data, _) = try await session.data(for: request)
+        
+        do {
+            let decoder = JSONDecoder()
+            let authResponse = try decoder.decode(AuthResponse.self, from: data)
+            //debugPrint(authResponse)
+            
+            // Store the information we need
+            access_token = authResponse.access_token
+            self.authResponse = authResponse
+            
+            // Add a header for our bearer token
+            httpHeaders["Authorization"] = "bearer " + authResponse.access_token
+            
+            return true
+            
+        } catch {
+            
+            // Make sure we know that we're no longer authorized
+            access_token = nil
+            self.authResponse = nil
+            if httpHeaders.keys.contains("Authorization") {
+                httpHeaders.removeValue(forKey: "Authorization")
+            }
+            
+            // Handle decoding error
+            //debugPrint(String(data: data, encoding: .utf8)!)
+            print("Error decoding JSON: \(error)")
+            return false
+        }
+    }
+}
diff --git a/Sources/SwiftNLP/1. Data Collection/Reddit/AnyDecodableValue.swift b/Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/AnyDecodableValue.swift
similarity index 100%
rename from Sources/SwiftNLP/1. Data Collection/Reddit/AnyDecodableValue.swift
rename to Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/AnyDecodableValue.swift
diff --git a/Sources/SwiftNLP/1. Data Collection/Reddit/Listing.swift b/Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/Listing.swift
similarity index 97%
rename from Sources/SwiftNLP/1. Data Collection/Reddit/Listing.swift
rename to Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/Listing.swift
index 0a22dafb..6bae633f 100644
--- a/Sources/SwiftNLP/1. Data Collection/Reddit/Listing.swift	
+++ b/Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/Listing.swift	
@@ -62,7 +62,7 @@ extension RedditListingDataItem {
             data = try container.decode(RedditSubmission.self, forKey: .data)
             
         default:
-            throw RedditClientError(message: "Failed to load reddit content from JSON.")
+            throw SessionError(message: "Failed to load reddit content from JSON.")
         }
     }
 }
diff --git a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditComment.swift b/Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/RedditComment.swift
similarity index 100%
rename from Sources/SwiftNLP/1. Data Collection/Reddit/RedditComment.swift
rename to Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/RedditComment.swift
diff --git a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditCommentData.swift b/Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/RedditCommentData.swift
similarity index 100%
rename from Sources/SwiftNLP/1. Data Collection/Reddit/RedditCommentData.swift
rename to Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/RedditCommentData.swift
diff --git a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditContainer.swift b/Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/RedditContainer.swift
similarity index 100%
rename from Sources/SwiftNLP/1. Data Collection/Reddit/RedditContainer.swift
rename to Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/RedditContainer.swift
diff --git a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditDataItem.swift b/Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/RedditDataItem.swift
similarity index 100%
rename from Sources/SwiftNLP/1. Data Collection/Reddit/RedditDataItem.swift
rename to Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/RedditDataItem.swift
diff --git a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditSubmission.swift b/Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/RedditSubmission.swift
similarity index 100%
rename from Sources/SwiftNLP/1. Data Collection/Reddit/RedditSubmission.swift
rename to Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/RedditSubmission.swift
diff --git a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditSubmissionData.swift b/Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/RedditSubmissionData.swift
similarity index 100%
rename from Sources/SwiftNLP/1. Data Collection/Reddit/RedditSubmissionData.swift
rename to Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/RedditSubmissionData.swift
diff --git a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditThread.swift b/Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/RedditThread.swift
similarity index 100%
rename from Sources/SwiftNLP/1. Data Collection/Reddit/RedditThread.swift
rename to Sources/SwiftNLP/1. Data Collection/Reddit/Data Types/RedditThread.swift
diff --git a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditClient + Comment Search.swift b/Sources/SwiftNLP/1. Data Collection/Reddit/Network Endpoints/RedditClient + Comment Search.swift
similarity index 90%
rename from Sources/SwiftNLP/1. Data Collection/Reddit/RedditClient + Comment Search.swift
rename to Sources/SwiftNLP/1. Data Collection/Reddit/Network Endpoints/RedditClient + Comment Search.swift
index d8cfe494..ed1f45dc 100644
--- a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditClient + Comment Search.swift	
+++ b/Sources/SwiftNLP/1. Data Collection/Reddit/Network Endpoints/RedditClient + Comment Search.swift	
@@ -23,7 +23,7 @@
 
 import Foundation
 
-extension RedditClient {
+extension Session {
     
     /// Returns a comment tree corresponding to a search of the r/subreddit/comments/article endpoint
     func searchComment(
@@ -45,7 +45,7 @@ extension RedditClient {
                
         guard let subreddit = submission.subreddit, let articleID = submission.id
         else {
-            throw RedditClientError(message: "Submission must include article data.")
+            throw SessionError(message: "Submission must include article data.")
         }
         
         var parameters: [String : String] = [String:String]()
@@ -74,7 +74,7 @@ extension RedditClient {
         parameters["threaded"] = String(threaded).lowercased()
         
         guard truncate <= 50 else {
-            throw RedditClientError(message: "Value for truncate must be between 0 and 50")
+            throw SessionError(message: "Value for truncate must be between 0 and 50")
         }
         
         parameters["truncate"] = String(truncate)
@@ -84,9 +84,6 @@ extension RedditClient {
             parameters: parameters
         )
         
-        //print(response)
-        //print( String(data: data, encoding: .utf8)! )
-        
         do {
             let redditListing = try JSONDecoder().decode([RedditListing].self, from: data)
             
@@ -97,7 +94,7 @@ extension RedditClient {
             return redditListing
             
         } catch {
-            throw RedditClientError(message: "Unable to decode server response.")
+            throw SessionError(message: "Unable to decode server response.")
         }
     }
     
diff --git a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditClient + Subreddit Search.swift b/Sources/SwiftNLP/1. Data Collection/Reddit/Network Endpoints/RedditClient + Subreddit Search.swift
similarity index 91%
rename from Sources/SwiftNLP/1. Data Collection/Reddit/RedditClient + Subreddit Search.swift
rename to Sources/SwiftNLP/1. Data Collection/Reddit/Network Endpoints/RedditClient + Subreddit Search.swift
index 730584a3..ada3a5c4 100644
--- a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditClient + Subreddit Search.swift	
+++ b/Sources/SwiftNLP/1. Data Collection/Reddit/Network Endpoints/RedditClient + Subreddit Search.swift	
@@ -23,7 +23,7 @@
 
 import Foundation
 
-extension RedditClient {
+extension Session {
     
     /// Returns a `RedditListing` for the provided search terms from the r/subreddit/search endpoint.
     @inlinable
@@ -44,7 +44,7 @@ extension RedditClient {
     ) async throws -> RedditListing {
         
         guard query.count < 512 else {
-            throw RedditClientError(message: "Query length must be less than 512 characters.")
+            throw SessionError(message: "Query length must be less than 512 characters.")
         }
         
         var parameters: [String : String] = [String:String]()
@@ -56,7 +56,7 @@ extension RedditClient {
         
         // TODO: We can expand this to include user, subreddit types... is that useful?
         guard !type.isEmpty else {
-            throw RedditClientError(message: "Must specify at least one type.")
+            throw SessionError(message: "Must specify at least one type.")
         }
         
         let typeList: String = type.map { $0.rawValue }.joined(separator: ", ")
@@ -90,7 +90,7 @@ extension RedditClient {
             return redditListing
             
         } catch {
-            throw RedditClientError(message: "Unable to decode server response.")
+            throw SessionError(message: "Unable to decode server response.")
         }
     }
     
diff --git a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditClient + User Search.swift b/Sources/SwiftNLP/1. Data Collection/Reddit/Network Endpoints/RedditClient + User Search.swift
similarity index 96%
rename from Sources/SwiftNLP/1. Data Collection/Reddit/RedditClient + User Search.swift
rename to Sources/SwiftNLP/1. Data Collection/Reddit/Network Endpoints/RedditClient + User Search.swift
index bf0b7af5..7d5453a3 100644
--- a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditClient + User Search.swift	
+++ b/Sources/SwiftNLP/1. Data Collection/Reddit/Network Endpoints/RedditClient + User Search.swift	
@@ -23,16 +23,7 @@
 
 import Foundation
 
-// TODO: Refactor this to serve *all* `/user/username/` endpoints:
-// - overview
-// - submitted
-// - comments
-// - upvoted
-// - downvoted
-// - hidden
-// - saved
-// - gilded
-extension RedditClient {
+extension Session {
     
     /// Returns a comment listing corresponding to  /user/username/comments/overview endpoint
     func searchUserOverview(
@@ -278,7 +269,7 @@ extension RedditClient {
         var parameters: [String : String] = [String:String]()
         
         guard context >= 2, context <= 10 else {
-            throw RedditClientError(message: "Context must be value between 2 and 10")
+            throw SessionError(message: "Context must be value between 2 and 10")
         }
         
         parameters["sort"] = sort.rawValue
@@ -286,7 +277,7 @@ extension RedditClient {
                 
         
         guard type == .link || type == .comment else {
-            throw RedditClientError(message: "Type must be either .link or .comment")
+            throw SessionError(message: "Type must be either .link or .comment")
         }
         parameters["type"] = type.rawValue
 
@@ -316,7 +307,7 @@ extension RedditClient {
             return redditListing
             
         } catch {
-            throw RedditClientError(message: "Unable to decode server response.")
+            throw SessionError(message: "Unable to decode server response.")
         }
     }
     
diff --git a/Sources/SwiftNLP/1. Data Collection/ReadRedditCommentFile.swift b/Sources/SwiftNLP/1. Data Collection/Reddit/ReadRedditCommentFile.swift
similarity index 100%
rename from Sources/SwiftNLP/1. Data Collection/ReadRedditCommentFile.swift
rename to Sources/SwiftNLP/1. Data Collection/Reddit/ReadRedditCommentFile.swift
diff --git a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditClient.swift b/Sources/SwiftNLP/1. Data Collection/Reddit/Session.swift
similarity index 61%
rename from Sources/SwiftNLP/1. Data Collection/Reddit/RedditClient.swift
rename to Sources/SwiftNLP/1. Data Collection/Reddit/Session.swift
index 09f38b5c..abb14b55 100644
--- a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditClient.swift	
+++ b/Sources/SwiftNLP/1. Data Collection/Reddit/Session.swift	
@@ -23,13 +23,13 @@
 
 import Foundation
 
-class RedditClient {
+class Session {
     
     // Application ID/Token
     var client_id: String?
     var client_secret: String?
     var access_token: String? = nil
-    var authResponse: RedditAuthResponse?
+    var authResponse: AuthResponse?
     
     #if os(macOS)
     var httpHeaders: [String : String] = [
@@ -64,69 +64,7 @@ class RedditClient {
 
 
 
-extension RedditClient {
-    
-    var isAuthenticated: Bool {
-        return access_token != nil
-    }
-    
-    // Authenticate with the Reddit API
-    // After this call, we should have an authentication token, and be able to make requests
-    func authenticate() async throws -> Bool {
-        
-        guard let client_id = client_id, let client_secret = client_secret else {
-            debugPrint("Must initialize client_Id and client_secret to authenticate.")
-            return false
-        }
-        
-        // Create a data request
-        var request = URLRequest(url: URL(string: baseAPIURL + "v1/access_token")!)
-        request.httpMethod = "POST"
-        
-        // Set headers
-        request.allHTTPHeaderFields = httpHeaders
-        let credentials = "\(client_id):\(client_secret)"
-        let encodedCredentials = Data(credentials.utf8).base64EncodedString()
-        request.addValue("Basic \(encodedCredentials)", forHTTPHeaderField: "Authorization")
-
-        // Set body
-        request.httpBody = parameters
-                .map { "\($0.key)=\($0.value)" }
-                .joined(separator: "&")
-                .data(using: .utf8)
-        
-        let (data, _) = try await session.data(for: request)
-        
-        do {
-            let decoder = JSONDecoder()
-            let authResponse = try decoder.decode(RedditAuthResponse.self, from: data)
-            //debugPrint(authResponse)
-            
-            // Store the information we need
-            access_token = authResponse.access_token
-            self.authResponse = authResponse
-            
-            // Add a header for our bearer token
-            httpHeaders["Authorization"] = "bearer " + authResponse.access_token
-            
-            return true
-            
-        } catch {
-            
-            // Make sure we know that we're no longer authorized
-            access_token = nil
-            self.authResponse = nil
-            if httpHeaders.keys.contains("Authorization") {
-                httpHeaders.removeValue(forKey: "Authorization")
-            }
-            
-            // Handle decoding error
-            //debugPrint(String(data: data, encoding: .utf8)!)
-            print("Error decoding JSON: \(error)")
-            return false
-        }
-    }
-    
+extension Session {
     
     //  UTILITY Method
     //  Perform a basic GET given an endpoint and parameters
@@ -134,7 +72,7 @@ extension RedditClient {
     @inlinable
     internal func _GET(endpoint: String, parameters: [String : String]) async throws -> (Data, HTTPURLResponse) {
         guard isAuthenticated else {
-            throw RedditClientError(message: "Client not authenticated.")
+            throw SessionError(message: "Client not authenticated.")
         }
 
         var url = URLComponents(string: authorizedAPIURL + endpoint)
@@ -147,7 +85,7 @@ extension RedditClient {
         url?.queryItems = queryItems
 
         guard let url = url?.url else {
-            throw RedditClientError(message: "Could not form query URL")
+            throw SessionError(message: "Could not form query URL")
         }
         
         // Create a data request
@@ -162,7 +100,7 @@ extension RedditClient {
         print(String(data: data, encoding: .utf8)!)
         
         guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
-            throw RedditClientError(message: "Bad server response" + response.description)
+            throw SessionError(message: "Bad server response" + response.description)
             }
         
         // Monitor rate limits
diff --git a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditClientError.swift b/Sources/SwiftNLP/1. Data Collection/Reddit/SessionError.swift
similarity index 97%
rename from Sources/SwiftNLP/1. Data Collection/Reddit/RedditClientError.swift
rename to Sources/SwiftNLP/1. Data Collection/Reddit/SessionError.swift
index 0acc5e65..d7b6a5f5 100644
--- a/Sources/SwiftNLP/1. Data Collection/Reddit/RedditClientError.swift	
+++ b/Sources/SwiftNLP/1. Data Collection/Reddit/SessionError.swift	
@@ -21,6 +21,6 @@
 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 // OTHER DEALINGS IN THE SOFTWARE.
 
-struct RedditClientError: Error {
+struct SessionError: Error {
     var message: String
 }
diff --git a/Sources/SwiftNLP/1. Data Collection/readRedditSubmissionData.swift b/Sources/SwiftNLP/1. Data Collection/Reddit/readRedditSubmissionData.swift
similarity index 100%
rename from Sources/SwiftNLP/1. Data Collection/readRedditSubmissionData.swift
rename to Sources/SwiftNLP/1. Data Collection/Reddit/readRedditSubmissionData.swift
diff --git a/Tests/SwiftNLPTests/Reddit API/RedditClient.swift b/Tests/SwiftNLPTests/Reddit API/Session Tests.swift
similarity index 91%
rename from Tests/SwiftNLPTests/Reddit API/RedditClient.swift
rename to Tests/SwiftNLPTests/Reddit API/Session Tests.swift
index c4efb021..bd0a6ea4 100644
--- a/Tests/SwiftNLPTests/Reddit API/RedditClient.swift	
+++ b/Tests/SwiftNLPTests/Reddit API/Session Tests.swift	
@@ -36,7 +36,7 @@ final class RedditClientTest: XCTestCase {
         //debugPrint("Client ID: \(id)")
         //debugPrint("Secret: \(secret)")
         
-        let client = RedditClient(id: id, secret: secret)
+        let client = Session(id: id, secret: secret)
         let response = try await client.authenticate()
         
         //let success = String(data: response, encoding: .utf8)!
@@ -60,9 +60,9 @@ final class RedditClientTest: XCTestCase {
             fatalError("Unable to fetch REDDIT_CLIENT_ID and REDDIT_CLIENT_SECRET from ProcessInfo.")
         }
         
-        let client = RedditClient(id: id, secret: secret)
+        let client = Session(id: id, secret: secret)
         guard let _ = try? await client.authenticate() else {
-            throw RedditClientError(message: "Error authenticating client.")
+            throw SessionError(message: "Error authenticating client.")
         }
                 
         let searchParameters = ["q" : "puppies"]        
@@ -82,9 +82,9 @@ final class RedditClientTest: XCTestCase {
             fatalError("Unable to fetch REDDIT_CLIENT_ID and REDDIT_CLIENT_SECRET from ProcessInfo.")
         }
         
-        let client = RedditClient(id: id, secret: secret)
+        let client = Session(id: id, secret: secret)
         guard let _ = try? await client.authenticate() else {
-            throw RedditClientError(message: "Error authenticating client.")
+            throw SessionError(message: "Error authenticating client.")
         }
         
         let result: RedditListing = try await client.searchSubreddit("uwaterloo", query: "goose", limit: 10)
@@ -106,9 +106,9 @@ final class RedditClientTest: XCTestCase {
             fatalError("Unable to fetch REDDIT_CLIENT_ID and REDDIT_CLIENT_SECRET from ProcessInfo.")
         }
         
-        let client = RedditClient(id: id, secret: secret)
+        let client = Session(id: id, secret: secret)
         guard let _ = try? await client.authenticate() else {
-            throw RedditClientError(message: "Error authenticating client.")
+            throw SessionError(message: "Error authenticating client.")
         }
         
         // https://www.reddit.com/r/uwaterloo/comments/18lbokl/conestoga_college_finally_being_called_out_by_the/
@@ -131,9 +131,9 @@ final class RedditClientTest: XCTestCase {
             fatalError("Unable to fetch REDDIT_CLIENT_ID and REDDIT_CLIENT_SECRET from ProcessInfo.")
         }
         
-        let client = RedditClient(id: id, secret: secret)
+        let client = Session(id: id, secret: secret)
         guard let _ = try? await client.authenticate() else {
-            throw RedditClientError(message: "Error authenticating client.")
+            throw SessionError(message: "Error authenticating client.")
         }
                
         let overviewResult = try await client.searchUserOverview(userName: "jimntonik")
-- 
GitLab