Move FeedbinAPICaller to Feedbin module.

This commit is contained in:
Brent Simmons 2024-04-10 14:23:10 -07:00
parent 670e692cac
commit 76fffca825
2 changed files with 116 additions and 77 deletions

View File

@ -40,11 +40,7 @@ public enum FeedbinAccountDelegateError: String, Error {
} }
} }
weak var accountMetadata: AccountMetadata? { weak var accountMetadata: AccountMetadata?
didSet {
caller.accountMetadata = accountMetadata
}
}
var refreshProgress = DownloadProgress(numberOfTasks: 0) var refreshProgress = DownloadProgress(numberOfTasks: 0)
@ -73,9 +69,9 @@ public enum FeedbinAccountDelegateError: String, Error {
} }
caller = FeedbinAPICaller(transport: URLSession(configuration: sessionConfiguration)) caller = FeedbinAPICaller(transport: URLSession(configuration: sessionConfiguration))
} }
caller.delegate = self
} }
func receiveRemoteNotification(for account: Account, userInfo: [AnyHashable : Any]) async { func receiveRemoteNotification(for account: Account, userInfo: [AnyHashable : Any]) async {
@ -981,3 +977,19 @@ private extension FeedbinAccountDelegate {
} }
} }
} }
extension FeedbinAccountDelegate: FeedbinAPICallerDelegate {
@MainActor var conditionalGetInfo: [String: HTTPConditionalGetInfo] {
get {
accountMetadata?.conditionalGetInfo ?? [String: HTTPConditionalGetInfo]()
}
set {
accountMetadata?.conditionalGetInfo = newValue
}
}
@MainActor var lastArticleFetchStartTime: Date? {
accountMetadata?.lastArticleFetchStartTime
}
}

View File

@ -11,25 +11,32 @@
// IP address will become unblocked and you can use the service again. // IP address will become unblocked and you can use the service again.
import Foundation import Foundation
import FoundationExtras
import Web import Web
import Secrets import Secrets
import Feedbin import Feedbin
enum CreateSubscriptionResult { public enum CreateSubscriptionResult: Sendable {
case created(FeedbinSubscription) case created(FeedbinSubscription)
case multipleChoice([FeedbinSubscriptionChoice]) case multipleChoice([FeedbinSubscriptionChoice])
case alreadySubscribed case alreadySubscribed
case notFound case notFound
} }
@MainActor final class FeedbinAPICaller: NSObject { public protocol FeedbinAPICallerDelegate: AnyObject {
struct ConditionalGetKeys { @MainActor var conditionalGetInfo: [String: HTTPConditionalGetInfo] { get set }
static let subscriptions = "subscriptions" @MainActor var lastArticleFetchStartTime: Date? { get }
static let tags = "tags" }
static let taggings = "taggings"
static let unreadEntries = "unreadEntries" @MainActor public final class FeedbinAPICaller {
static let starredEntries = "starredEntries"
public struct ConditionalGetKeys {
public static let subscriptions = "subscriptions"
public static let tags = "tags"
public static let taggings = "taggings"
public static let unreadEntries = "unreadEntries"
public static let starredEntries = "starredEntries"
} }
private let feedbinBaseURL = URL(string: "https://api.feedbin.com/v2/")! private let feedbinBaseURL = URL(string: "https://api.feedbin.com/v2/")!
@ -37,28 +44,27 @@ enum CreateSubscriptionResult {
private var suspended = false private var suspended = false
private var lastBackdateStartTime: Date? private var lastBackdateStartTime: Date?
var credentials: Credentials? public var credentials: Credentials?
weak var accountMetadata: AccountMetadata? public weak var delegate: FeedbinAPICallerDelegate?
init(transport: Transport) { public init(transport: Transport) {
super.init()
self.transport = transport self.transport = transport
} }
/// Cancels all pending requests rejects any that come in later /// Cancels all pending requests rejects any that come in later
func suspend() { public func suspend() {
transport.cancelAll() transport.cancelAll()
suspended = true suspended = true
} }
func resume() { public func resume() {
suspended = false suspended = false
} }
func validateCredentials() async throws -> Credentials? { public func validateCredentials() async throws -> Credentials? {
let callURL = feedbinBaseURL.appendingPathComponent("authentication.json") let callURL = feedbinBaseURL.appendingPathComponent("authentication.json")
let request = URLRequest(url: callURL, credentials: credentials) let request = URLRequest(url: callURL, feedbinCredentials: credentials)
do { do {
try await transport.send(request: request) try await transport.send(request: request)
@ -71,10 +77,10 @@ enum CreateSubscriptionResult {
} }
} }
func importOPML(opmlData: Data) async throws -> FeedbinImportResult { public func importOPML(opmlData: Data) async throws -> FeedbinImportResult {
let callURL = feedbinBaseURL.appendingPathComponent("imports.json") let callURL = feedbinBaseURL.appendingPathComponent("imports.json")
var request = URLRequest(url: callURL, credentials: credentials) var request = URLRequest(url: callURL, feedbinCredentials: credentials)
request.addValue("text/xml; charset=utf-8", forHTTPHeaderField: HTTPRequestHeader.contentType) request.addValue("text/xml; charset=utf-8", forHTTPHeaderField: HTTPRequestHeader.contentType)
let (_, data) = try await transport.send(request: request, method: HTTPMethod.post, payload: opmlData) let (_, data) = try await transport.send(request: request, method: HTTPMethod.post, payload: opmlData)
@ -90,22 +96,22 @@ enum CreateSubscriptionResult {
return importResult return importResult
} }
func retrieveOPMLImportResult(importID: Int) async throws -> FeedbinImportResult? { public func retrieveOPMLImportResult(importID: Int) async throws -> FeedbinImportResult? {
let callURL = feedbinBaseURL.appendingPathComponent("imports/\(importID).json") let callURL = feedbinBaseURL.appendingPathComponent("imports/\(importID).json")
let request = URLRequest(url: callURL, credentials: credentials) let request = URLRequest(url: callURL, feedbinCredentials: credentials)
let (_, importResult) = try await transport.send(request: request, resultType: FeedbinImportResult.self) let (_, importResult) = try await transport.send(request: request, resultType: FeedbinImportResult.self)
return importResult return importResult
} }
func retrieveTags() async throws -> [FeedbinTag]? { public func retrieveTags() async throws -> [FeedbinTag]? {
if suspended { throw TransportError.suspended } if suspended { throw TransportError.suspended }
let callURL = feedbinBaseURL.appendingPathComponent("tags.json") let callURL = feedbinBaseURL.appendingPathComponent("tags.json")
let conditionalGet = accountMetadata?.conditionalGetInfo[ConditionalGetKeys.tags] let conditionalGet = delegate?.conditionalGetInfo[ConditionalGetKeys.tags]
let request = URLRequest(url: callURL, credentials: credentials, conditionalGet: conditionalGet) let request = URLRequest(url: callURL, feedbinCredentials: credentials, conditionalGet: conditionalGet)
let (response, tags) = try await transport.send(request: request, resultType: [FeedbinTag].self) let (response, tags) = try await transport.send(request: request, resultType: [FeedbinTag].self)
@ -114,24 +120,24 @@ enum CreateSubscriptionResult {
return tags return tags
} }
func renameTag(oldName: String, newName: String) async throws { public func renameTag(oldName: String, newName: String) async throws {
if suspended { throw TransportError.suspended } if suspended { throw TransportError.suspended }
let callURL = feedbinBaseURL.appendingPathComponent("tags.json") let callURL = feedbinBaseURL.appendingPathComponent("tags.json")
let request = URLRequest(url: callURL, credentials: credentials) let request = URLRequest(url: callURL, feedbinCredentials: credentials)
let payload = FeedbinRenameTag(oldName: oldName, newName: newName) let payload = FeedbinRenameTag(oldName: oldName, newName: newName)
try await transport.send(request: request, method: HTTPMethod.post, payload: payload) try await transport.send(request: request, method: HTTPMethod.post, payload: payload)
} }
func retrieveSubscriptions() async throws -> [FeedbinSubscription]? { public func retrieveSubscriptions() async throws -> [FeedbinSubscription]? {
var callComponents = URLComponents(url: feedbinBaseURL.appendingPathComponent("subscriptions.json"), resolvingAgainstBaseURL: false)! var callComponents = URLComponents(url: feedbinBaseURL.appendingPathComponent("subscriptions.json"), resolvingAgainstBaseURL: false)!
callComponents.queryItems = [URLQueryItem(name: "mode", value: "extended")] callComponents.queryItems = [URLQueryItem(name: "mode", value: "extended")]
let conditionalGet = accountMetadata?.conditionalGetInfo[ConditionalGetKeys.subscriptions] let conditionalGet = delegate?.conditionalGetInfo[ConditionalGetKeys.subscriptions]
let request = URLRequest(url: callComponents.url!, credentials: credentials, conditionalGet: conditionalGet) let request = URLRequest(url: callComponents.url!, feedbinCredentials: credentials, conditionalGet: conditionalGet)
let (response, subscriptions) = try await transport.send(request: request, resultType: [FeedbinSubscription].self) let (response, subscriptions) = try await transport.send(request: request, resultType: [FeedbinSubscription].self)
@ -140,14 +146,14 @@ enum CreateSubscriptionResult {
return subscriptions return subscriptions
} }
func createSubscription(url: String) async throws -> CreateSubscriptionResult { public func createSubscription(url: String) async throws -> CreateSubscriptionResult {
if suspended { throw TransportError.suspended } if suspended { throw TransportError.suspended }
var callComponents = URLComponents(url: feedbinBaseURL.appendingPathComponent("subscriptions.json"), resolvingAgainstBaseURL: false)! var callComponents = URLComponents(url: feedbinBaseURL.appendingPathComponent("subscriptions.json"), resolvingAgainstBaseURL: false)!
callComponents.queryItems = [URLQueryItem(name: "mode", value: "extended")] callComponents.queryItems = [URLQueryItem(name: "mode", value: "extended")]
var request = URLRequest(url: callComponents.url!, credentials: credentials) var request = URLRequest(url: callComponents.url!, feedbinCredentials: credentials)
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: HTTPRequestHeader.contentType) request.addValue("application/json; charset=utf-8", forHTTPHeaderField: HTTPRequestHeader.contentType)
let payload: Data let payload: Data
@ -202,34 +208,34 @@ enum CreateSubscriptionResult {
} }
} }
func renameSubscription(subscriptionID: String, newName: String) async throws { public func renameSubscription(subscriptionID: String, newName: String) async throws {
if suspended { throw TransportError.suspended } if suspended { throw TransportError.suspended }
let callURL = feedbinBaseURL.appendingPathComponent("subscriptions/\(subscriptionID)/update.json") let callURL = feedbinBaseURL.appendingPathComponent("subscriptions/\(subscriptionID)/update.json")
let request = URLRequest(url: callURL, credentials: credentials) let request = URLRequest(url: callURL, feedbinCredentials: credentials)
let payload = FeedbinUpdateSubscription(title: newName) let payload = FeedbinUpdateSubscription(title: newName)
try await transport.send(request: request, method: HTTPMethod.post, payload: payload) try await transport.send(request: request, method: HTTPMethod.post, payload: payload)
} }
func deleteSubscription(subscriptionID: String) async throws { public func deleteSubscription(subscriptionID: String) async throws {
if suspended { throw TransportError.suspended } if suspended { throw TransportError.suspended }
let callURL = feedbinBaseURL.appendingPathComponent("subscriptions/\(subscriptionID).json") let callURL = feedbinBaseURL.appendingPathComponent("subscriptions/\(subscriptionID).json")
let request = URLRequest(url: callURL, credentials: credentials) let request = URLRequest(url: callURL, feedbinCredentials: credentials)
try await transport.send(request: request, method: HTTPMethod.delete) try await transport.send(request: request, method: HTTPMethod.delete)
} }
func retrieveTaggings() async throws -> [FeedbinTagging]? { public func retrieveTaggings() async throws -> [FeedbinTagging]? {
if suspended { throw TransportError.suspended } if suspended { throw TransportError.suspended }
let callURL = feedbinBaseURL.appendingPathComponent("taggings.json") let callURL = feedbinBaseURL.appendingPathComponent("taggings.json")
let conditionalGet = accountMetadata?.conditionalGetInfo[ConditionalGetKeys.taggings] let conditionalGet = delegate?.conditionalGetInfo[ConditionalGetKeys.taggings]
let request = URLRequest(url: callURL, credentials: credentials, conditionalGet: conditionalGet) let request = URLRequest(url: callURL, feedbinCredentials: credentials, conditionalGet: conditionalGet)
let (response, taggings) = try await transport.send(request: request, resultType: [FeedbinTagging].self) let (response, taggings) = try await transport.send(request: request, resultType: [FeedbinTagging].self)
@ -238,12 +244,12 @@ enum CreateSubscriptionResult {
return taggings return taggings
} }
func createTagging(feedID: Int, name: String) async throws -> Int { public func createTagging(feedID: Int, name: String) async throws -> Int {
if suspended { throw TransportError.suspended } if suspended { throw TransportError.suspended }
let callURL = feedbinBaseURL.appendingPathComponent("taggings.json") let callURL = feedbinBaseURL.appendingPathComponent("taggings.json")
var request = URLRequest(url: callURL, credentials: credentials) var request = URLRequest(url: callURL, feedbinCredentials: credentials)
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: HTTPRequestHeader.contentType) request.addValue("application/json; charset=utf-8", forHTTPHeaderField: HTTPRequestHeader.contentType)
let payload = try JSONEncoder().encode(FeedbinCreateTagging(feedID: feedID, name: name)) let payload = try JSONEncoder().encode(FeedbinCreateTagging(feedID: feedID, name: name))
@ -260,18 +266,18 @@ enum CreateSubscriptionResult {
} }
} }
func deleteTagging(taggingID: String) async throws { public func deleteTagging(taggingID: String) async throws {
if suspended { throw TransportError.suspended } if suspended { throw TransportError.suspended }
let callURL = feedbinBaseURL.appendingPathComponent("taggings/\(taggingID).json") let callURL = feedbinBaseURL.appendingPathComponent("taggings/\(taggingID).json")
var request = URLRequest(url: callURL, credentials: credentials) var request = URLRequest(url: callURL, feedbinCredentials: credentials)
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: HTTPRequestHeader.contentType) request.addValue("application/json; charset=utf-8", forHTTPHeaderField: HTTPRequestHeader.contentType)
try await transport.send(request: request, method: HTTPMethod.delete) try await transport.send(request: request, method: HTTPMethod.delete)
} }
func retrieveEntries(articleIDs: [String]) async throws -> [FeedbinEntry]? { public func retrieveEntries(articleIDs: [String]) async throws -> [FeedbinEntry]? {
if suspended { throw TransportError.suspended } if suspended { throw TransportError.suspended }
@ -288,13 +294,13 @@ enum CreateSubscriptionResult {
URLQueryItem(name: "ids", value: paramIDs), URLQueryItem(name: "ids", value: paramIDs),
URLQueryItem(name: "mode", value: "extended") URLQueryItem(name: "mode", value: "extended")
]) ])
let request = URLRequest(url: url!, credentials: credentials) let request = URLRequest(url: url!, feedbinCredentials: credentials)
let (_, entries) = try await transport.send(request: request, resultType: [FeedbinEntry].self) let (_, entries) = try await transport.send(request: request, resultType: [FeedbinEntry].self)
return entries return entries
} }
func retrieveEntries(feedID: String) async throws -> ([FeedbinEntry]?, String?) { public func retrieveEntries(feedID: String) async throws -> ([FeedbinEntry]?, String?) {
if suspended { throw TransportError.suspended } if suspended { throw TransportError.suspended }
@ -308,7 +314,7 @@ enum CreateSubscriptionResult {
URLQueryItem(name: "per_page", value: "100"), URLQueryItem(name: "per_page", value: "100"),
URLQueryItem(name: "mode", value: "extended") URLQueryItem(name: "mode", value: "extended")
]) ])
let request = URLRequest(url: url!, credentials: credentials) let request = URLRequest(url: url!, feedbinCredentials: credentials)
let (response, entries) = try await transport.send(request: request, resultType: [FeedbinEntry].self) let (response, entries) = try await transport.send(request: request, resultType: [FeedbinEntry].self)
@ -316,7 +322,7 @@ enum CreateSubscriptionResult {
return (entries, pagingInfo.nextPage) return (entries, pagingInfo.nextPage)
} }
func retrieveEntries() async throws -> ([FeedbinEntry]?, String?, Date?, Int?) { public func retrieveEntries() async throws -> ([FeedbinEntry]?, String?, Date?, Int?) {
if suspended { throw TransportError.suspended } if suspended { throw TransportError.suspended }
@ -327,7 +333,7 @@ enum CreateSubscriptionResult {
// getting **updated** articles that normally wouldn't be found with a regular fetch. // getting **updated** articles that normally wouldn't be found with a regular fetch.
// https://github.com/Ranchero-Software/NetNewsWire/issues/2549#issuecomment-722341356 // https://github.com/Ranchero-Software/NetNewsWire/issues/2549#issuecomment-722341356
let since: Date = { let since: Date = {
if let lastArticleFetch = accountMetadata?.lastArticleFetchStartTime { if let lastArticleFetch = delegate?.lastArticleFetchStartTime {
if let lastBackdateStartTime = lastBackdateStartTime { if let lastBackdateStartTime = lastBackdateStartTime {
if lastBackdateStartTime.byAdding(days: 1) < lastArticleFetch { if lastBackdateStartTime.byAdding(days: 1) < lastArticleFetch {
self.lastBackdateStartTime = lastArticleFetch self.lastBackdateStartTime = lastArticleFetch
@ -352,7 +358,7 @@ enum CreateSubscriptionResult {
URLQueryItem(name: "per_page", value: "100"), URLQueryItem(name: "per_page", value: "100"),
URLQueryItem(name: "mode", value: "extended") URLQueryItem(name: "mode", value: "extended")
]) ])
let request = URLRequest(url: url!, credentials: credentials) let request = URLRequest(url: url!, feedbinCredentials: credentials)
let (response, entries) = try await transport.send(request: request, resultType: [FeedbinEntry].self) let (response, entries) = try await transport.send(request: request, resultType: [FeedbinEntry].self)
@ -363,7 +369,7 @@ enum CreateSubscriptionResult {
return (entries, pagingInfo.nextPage, dateInfo?.date, lastPageNumber) return (entries, pagingInfo.nextPage, dateInfo?.date, lastPageNumber)
} }
func retrieveEntries(page: String) async throws -> ([FeedbinEntry]?, String?) { public func retrieveEntries(page: String) async throws -> ([FeedbinEntry]?, String?) {
if suspended { throw TransportError.suspended } if suspended { throw TransportError.suspended }
@ -371,7 +377,7 @@ enum CreateSubscriptionResult {
return (nil, nil) return (nil, nil)
} }
let request = URLRequest(url: url, credentials: credentials) let request = URLRequest(url: url, feedbinCredentials: credentials)
let (response, entries) = try await transport.send(request: request, resultType: [FeedbinEntry].self) let (response, entries) = try await transport.send(request: request, resultType: [FeedbinEntry].self)
@ -379,13 +385,13 @@ enum CreateSubscriptionResult {
return (entries, pagingInfo.nextPage) return (entries, pagingInfo.nextPage)
} }
func retrieveUnreadEntries() async throws -> [Int]? { public func retrieveUnreadEntries() async throws -> [Int]? {
if suspended { throw TransportError.suspended } if suspended { throw TransportError.suspended }
let callURL = feedbinBaseURL.appendingPathComponent("unread_entries.json") let callURL = feedbinBaseURL.appendingPathComponent("unread_entries.json")
let conditionalGet = accountMetadata?.conditionalGetInfo[ConditionalGetKeys.unreadEntries] let conditionalGet = delegate?.conditionalGetInfo[ConditionalGetKeys.unreadEntries]
let request = URLRequest(url: callURL, credentials: credentials, conditionalGet: conditionalGet) let request = URLRequest(url: callURL, feedbinCredentials: credentials, conditionalGet: conditionalGet)
let (response, unreadEntries) = try await transport.send(request: request, resultType: [Int].self) let (response, unreadEntries) = try await transport.send(request: request, resultType: [Int].self)
@ -393,35 +399,35 @@ enum CreateSubscriptionResult {
return unreadEntries return unreadEntries
} }
func createUnreadEntries(entries: [Int]) async throws { public func createUnreadEntries(entries: [Int]) async throws {
if suspended { throw TransportError.suspended } if suspended { throw TransportError.suspended }
let callURL = feedbinBaseURL.appendingPathComponent("unread_entries.json") let callURL = feedbinBaseURL.appendingPathComponent("unread_entries.json")
let request = URLRequest(url: callURL, credentials: credentials) let request = URLRequest(url: callURL, feedbinCredentials: credentials)
let payload = FeedbinUnreadEntry(unreadEntries: entries) let payload = FeedbinUnreadEntry(unreadEntries: entries)
try await transport.send(request: request, method: HTTPMethod.post, payload: payload) try await transport.send(request: request, method: HTTPMethod.post, payload: payload)
} }
func deleteUnreadEntries(entries: [Int]) async throws { public func deleteUnreadEntries(entries: [Int]) async throws {
if suspended { throw TransportError.suspended } if suspended { throw TransportError.suspended }
let callURL = feedbinBaseURL.appendingPathComponent("unread_entries.json") let callURL = feedbinBaseURL.appendingPathComponent("unread_entries.json")
let request = URLRequest(url: callURL, credentials: credentials) let request = URLRequest(url: callURL, feedbinCredentials: credentials)
let payload = FeedbinUnreadEntry(unreadEntries: entries) let payload = FeedbinUnreadEntry(unreadEntries: entries)
try await transport.send(request: request, method: HTTPMethod.delete, payload: payload) try await transport.send(request: request, method: HTTPMethod.delete, payload: payload)
} }
func retrieveStarredEntries() async throws -> [Int]? { public func retrieveStarredEntries() async throws -> [Int]? {
if suspended { throw TransportError.suspended } if suspended { throw TransportError.suspended }
let callURL = feedbinBaseURL.appendingPathComponent("starred_entries.json") let callURL = feedbinBaseURL.appendingPathComponent("starred_entries.json")
let conditionalGet = accountMetadata?.conditionalGetInfo[ConditionalGetKeys.starredEntries] let conditionalGet = delegate?.conditionalGetInfo[ConditionalGetKeys.starredEntries]
let request = URLRequest(url: callURL, credentials: credentials, conditionalGet: conditionalGet) let request = URLRequest(url: callURL, feedbinCredentials: credentials, conditionalGet: conditionalGet)
let (response, starredEntries) = try await transport.send(request: request, resultType: [Int].self) let (response, starredEntries) = try await transport.send(request: request, resultType: [Int].self)
@ -429,23 +435,23 @@ enum CreateSubscriptionResult {
return starredEntries return starredEntries
} }
func createStarredEntries(entries: [Int]) async throws { public func createStarredEntries(entries: [Int]) async throws {
if suspended { throw TransportError.suspended } if suspended { throw TransportError.suspended }
let callURL = feedbinBaseURL.appendingPathComponent("starred_entries.json") let callURL = feedbinBaseURL.appendingPathComponent("starred_entries.json")
let request = URLRequest(url: callURL, credentials: credentials) let request = URLRequest(url: callURL, feedbinCredentials: credentials)
let payload = FeedbinStarredEntry(starredEntries: entries) let payload = FeedbinStarredEntry(starredEntries: entries)
try await transport.send(request: request, method: HTTPMethod.post, payload: payload) try await transport.send(request: request, method: HTTPMethod.post, payload: payload)
} }
func deleteStarredEntries(entries: [Int]) async throws { public func deleteStarredEntries(entries: [Int]) async throws {
if suspended { throw TransportError.suspended } if suspended { throw TransportError.suspended }
let callURL = feedbinBaseURL.appendingPathComponent("starred_entries.json") let callURL = feedbinBaseURL.appendingPathComponent("starred_entries.json")
let request = URLRequest(url: callURL, credentials: credentials) let request = URLRequest(url: callURL, feedbinCredentials: credentials)
let payload = FeedbinStarredEntry(starredEntries: entries) let payload = FeedbinStarredEntry(starredEntries: entries)
try await transport.send(request: request, method: HTTPMethod.delete, payload: payload) try await transport.send(request: request, method: HTTPMethod.delete, payload: payload)
@ -457,9 +463,9 @@ enum CreateSubscriptionResult {
private extension FeedbinAPICaller { private extension FeedbinAPICaller {
func storeConditionalGet(key: String, headers: [AnyHashable : Any]) { func storeConditionalGet(key: String, headers: [AnyHashable : Any]) {
if var conditionalGet = accountMetadata?.conditionalGetInfo { if var conditionalGet = delegate?.conditionalGetInfo {
conditionalGet[key] = HTTPConditionalGetInfo(headers: headers) conditionalGet[key] = HTTPConditionalGetInfo(headers: headers)
accountMetadata?.conditionalGetInfo = conditionalGet delegate?.conditionalGetInfo = conditionalGet
} }
} }
@ -484,3 +490,24 @@ private extension FeedbinAPICaller {
} }
} }
private extension URLRequest {
init(url: URL, feedbinCredentials: Credentials?, conditionalGet: HTTPConditionalGetInfo? = nil) {
self.init(url: url)
guard let credentials = feedbinCredentials else {
return
}
precondition(credentials.type == .basic)
let data = "\(credentials.username):\(credentials.secret)".data(using: .utf8)
let base64 = data?.base64EncodedString()
let auth = "Basic \(base64 ?? "")"
setValue(auth, forHTTPHeaderField: HTTPRequestHeader.authorization)
conditionalGet?.addRequestHeadersToURLRequest(&self)
}
}