mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Continue renaming Id to ID (and similar renames).
This commit is contained in:
@@ -872,13 +872,13 @@ private extension FeedbinAccountDelegate {
|
||||
|
||||
logger.debug("Syncing feeds with \(subscriptions.count, privacy: .public) subscriptions.")
|
||||
|
||||
let subFeedIds = subscriptions.map { String($0.feedID) }
|
||||
let subFeedIDs = subscriptions.map { String($0.feedID) }
|
||||
|
||||
// Remove any feeds that are no longer in the subscriptions
|
||||
if let folders = account.folders {
|
||||
for folder in folders {
|
||||
for feed in folder.topLevelFeeds {
|
||||
if !subFeedIds.contains(feed.feedID) {
|
||||
if !subFeedIDs.contains(feed.feedID) {
|
||||
folder.removeFeed(feed)
|
||||
}
|
||||
}
|
||||
@@ -886,7 +886,7 @@ private extension FeedbinAccountDelegate {
|
||||
}
|
||||
|
||||
for feed in account.topLevelFeeds {
|
||||
if !subFeedIds.contains(feed.feedID) {
|
||||
if !subFeedIDs.contains(feed.feedID) {
|
||||
account.removeFeed(feed)
|
||||
}
|
||||
}
|
||||
@@ -895,9 +895,9 @@ private extension FeedbinAccountDelegate {
|
||||
var subscriptionsToAdd = Set<FeedbinSubscription>()
|
||||
for subscription in subscriptions {
|
||||
|
||||
let subFeedId = String(subscription.feedID)
|
||||
let subFeedID = String(subscription.feedID)
|
||||
|
||||
if let feed = account.existingFeed(withFeedID: subFeedId) {
|
||||
if let feed = account.existingFeed(withFeedID: subFeedID) {
|
||||
feed.name = subscription.name
|
||||
// If the name has been changed on the server remove the locally edited name
|
||||
feed.editedName = nil
|
||||
@@ -956,11 +956,11 @@ private extension FeedbinAccountDelegate {
|
||||
}
|
||||
|
||||
// Add any feeds not in the folder
|
||||
let folderFeedIds = folder.topLevelFeeds.map { $0.feedID }
|
||||
let folderFeedIDs = folder.topLevelFeeds.map { $0.feedID }
|
||||
|
||||
for tagging in groupedTaggings {
|
||||
let taggingFeedID = String(tagging.feedID)
|
||||
if !folderFeedIds.contains(taggingFeedID) {
|
||||
if !folderFeedIDs.contains(taggingFeedID) {
|
||||
guard let feed = account.existingFeed(withFeedID: taggingFeedID) else {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -81,13 +81,13 @@ extension NewsBlurAccountDelegate {
|
||||
|
||||
logger.debug("Syncing feeds with \(feeds.count, privacy: .public) feeds.")
|
||||
|
||||
let newsBlurFeedIds = feeds.map { String($0.feedID) }
|
||||
let newsBlurFeedIDs = feeds.map { String($0.feedID) }
|
||||
|
||||
// Remove any feeds that are no longer in the subscriptions
|
||||
if let folders = account.folders {
|
||||
for folder in folders {
|
||||
for feed in folder.topLevelFeeds {
|
||||
if !newsBlurFeedIds.contains(feed.feedID) {
|
||||
if !newsBlurFeedIDs.contains(feed.feedID) {
|
||||
folder.removeFeed(feed)
|
||||
}
|
||||
}
|
||||
@@ -95,7 +95,7 @@ extension NewsBlurAccountDelegate {
|
||||
}
|
||||
|
||||
for feed in account.topLevelFeeds {
|
||||
if !newsBlurFeedIds.contains(feed.feedID) {
|
||||
if !newsBlurFeedIDs.contains(feed.feedID) {
|
||||
account.removeFeed(feed)
|
||||
}
|
||||
}
|
||||
@@ -166,11 +166,11 @@ extension NewsBlurAccountDelegate {
|
||||
}
|
||||
|
||||
// Add any feeds not in the folder
|
||||
let folderFeedIds = folder.topLevelFeeds.map { $0.feedID }
|
||||
let folderFeedIDs = folder.topLevelFeeds.map { $0.feedID }
|
||||
|
||||
for relationship in folderRelationships {
|
||||
let folderFeedID = String(relationship.feedID)
|
||||
if !folderFeedIds.contains(folderFeedID) {
|
||||
if !folderFeedIDs.contains(folderFeedID) {
|
||||
guard let feed = account.existingFeed(withFeedID: folderFeedID) else {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -641,7 +641,7 @@ final class NewsBlurAccountDelegate: AccountDelegate, Logging {
|
||||
}
|
||||
|
||||
func accountDidInitialize(_ account: Account) {
|
||||
credentials = try? account.retrieveCredentials(type: .newsBlurSessionId)
|
||||
credentials = try? account.retrieveCredentials(type: .newsBlurSessionID)
|
||||
refreshProgress.name = account.nameForDisplay
|
||||
}
|
||||
|
||||
|
||||
@@ -528,7 +528,7 @@ public enum ReaderAPIAccountDelegateError: LocalizedError {
|
||||
addFeed(for: account, with: feed, to: to, completion: completion)
|
||||
} else {
|
||||
guard
|
||||
let subscriptionId = feed.externalID,
|
||||
let subscriptionID = feed.externalID,
|
||||
let fromTag = (from as? Folder)?.name,
|
||||
let toTag = (to as? Folder)?.name
|
||||
else {
|
||||
@@ -537,7 +537,7 @@ public enum ReaderAPIAccountDelegateError: LocalizedError {
|
||||
}
|
||||
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(1)
|
||||
caller.moveSubscription(subscriptionID: subscriptionId, fromTag: fromTag, toTag: toTag) { result in
|
||||
caller.moveSubscription(subscriptionID: subscriptionID, fromTag: fromTag, toTag: toTag) { result in
|
||||
self.refreshProgress.completeTask()
|
||||
switch result {
|
||||
case .success:
|
||||
@@ -784,13 +784,13 @@ private extension ReaderAPIAccountDelegate {
|
||||
|
||||
logger.debug("Syncing feeds with \(subscriptions.count, privacy: .public) subscriptions")
|
||||
|
||||
let subFeedIds = subscriptions.map { $0.feedID }
|
||||
let subFeedIDs = subscriptions.map { $0.feedID }
|
||||
|
||||
// Remove any feeds that are no longer in the subscriptions
|
||||
if let folders = account.folders {
|
||||
for folder in folders {
|
||||
for feed in folder.topLevelFeeds {
|
||||
if !subFeedIds.contains(feed.feedID) {
|
||||
if !subFeedIDs.contains(feed.feedID) {
|
||||
folder.removeFeed(feed)
|
||||
}
|
||||
}
|
||||
@@ -798,7 +798,7 @@ private extension ReaderAPIAccountDelegate {
|
||||
}
|
||||
|
||||
for feed in account.topLevelFeeds {
|
||||
if !subFeedIds.contains(feed.feedID) {
|
||||
if !subFeedIDs.contains(feed.feedID) {
|
||||
account.clearFeedMetadata(feed)
|
||||
account.removeFeed(feed)
|
||||
}
|
||||
@@ -832,11 +832,11 @@ private extension ReaderAPIAccountDelegate {
|
||||
var taggedFeeds = dict
|
||||
|
||||
for category in subscription.categories {
|
||||
if var taggedFeed = taggedFeeds[category.categoryId] {
|
||||
if var taggedFeed = taggedFeeds[category.categoryID] {
|
||||
taggedFeed.append(subscription)
|
||||
taggedFeeds[category.categoryId] = taggedFeed
|
||||
taggedFeeds[category.categoryID] = taggedFeed
|
||||
} else {
|
||||
taggedFeeds[category.categoryId] = [subscription]
|
||||
taggedFeeds[category.categoryID] = [subscription]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -858,11 +858,11 @@ private extension ReaderAPIAccountDelegate {
|
||||
}
|
||||
|
||||
// Add any feeds not in the folder
|
||||
let folderFeedIds = folder.topLevelFeeds.map { $0.feedID }
|
||||
let folderFeedIDs = folder.topLevelFeeds.map { $0.feedID }
|
||||
|
||||
for subscription in groupedTaggings {
|
||||
let taggingFeedID = subscription.feedID
|
||||
if !folderFeedIds.contains(taggingFeedID) {
|
||||
if !folderFeedIDs.contains(taggingFeedID) {
|
||||
guard let feed = account.existingFeed(withFeedID: taggingFeedID) else {
|
||||
continue
|
||||
}
|
||||
@@ -1080,7 +1080,7 @@ private extension ReaderAPIAccountDelegate {
|
||||
// Hope the compiler is happy.
|
||||
var parsedItems = Set<ParsedItem>()
|
||||
for entry in entries {
|
||||
guard let streamID = entry.origin.streamId else {
|
||||
guard let streamID = entry.origin.streamID else {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -245,8 +245,8 @@ final class CloudKitAccountZone: CloudKitZone {
|
||||
query(ckQuery) { result in
|
||||
switch result {
|
||||
case .success(let records):
|
||||
let feedExternalIds = records.map { $0.externalID }
|
||||
completion(.success(feedExternalIds))
|
||||
let feedExternalIDs = records.map { $0.externalID }
|
||||
completion(.success(feedExternalIDs))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
@@ -148,9 +148,9 @@ private extension CloudKitAcountZoneDelegate {
|
||||
feed.homePageURL = homePageURL
|
||||
|
||||
let existingContainers = account.existingContainers(withFeed: feed)
|
||||
let existingContainerExternalIds = existingContainers.compactMap { $0.externalID }
|
||||
let existingContainerExternalIDs = existingContainers.compactMap { $0.externalID }
|
||||
|
||||
let diff = containerExternalIDs.difference(from: existingContainerExternalIds)
|
||||
let diff = containerExternalIDs.difference(from: existingContainerExternalIDs)
|
||||
|
||||
for change in diff {
|
||||
switch change {
|
||||
|
||||
@@ -23,7 +23,7 @@ final class FeedlyAPICaller {
|
||||
case sandbox
|
||||
case cloud
|
||||
|
||||
var baseUrlComponents: URLComponents {
|
||||
var baseURLComponents: URLComponents {
|
||||
var components = URLComponents()
|
||||
components.scheme = "https"
|
||||
switch self{
|
||||
@@ -48,12 +48,12 @@ final class FeedlyAPICaller {
|
||||
}
|
||||
|
||||
private let transport: Transport
|
||||
private let baseUrlComponents: URLComponents
|
||||
private let baseURLComponents: URLComponents
|
||||
private let uriComponentAllowed: CharacterSet
|
||||
|
||||
init(transport: Transport, api: API) {
|
||||
self.transport = transport
|
||||
self.baseUrlComponents = api.baseUrlComponents
|
||||
self.baseURLComponents = api.baseURLComponents
|
||||
|
||||
var urlHostAllowed = CharacterSet.urlHostAllowed
|
||||
urlHostAllowed.remove("+")
|
||||
@@ -65,7 +65,7 @@ final class FeedlyAPICaller {
|
||||
var credentials: Credentials?
|
||||
|
||||
var server: String? {
|
||||
return baseUrlComponents.host
|
||||
return baseURLComponents.host
|
||||
}
|
||||
|
||||
func cancelAll() {
|
||||
@@ -144,7 +144,7 @@ final class FeedlyAPICaller {
|
||||
completion(.failure(CredentialsError.incompleteCredentials))
|
||||
}
|
||||
}
|
||||
var components = baseUrlComponents
|
||||
var components = baseURLComponents
|
||||
components.path = "/v3/opml"
|
||||
|
||||
guard let url = components.url else {
|
||||
@@ -184,7 +184,7 @@ final class FeedlyAPICaller {
|
||||
completion(.failure(CredentialsError.incompleteCredentials))
|
||||
}
|
||||
}
|
||||
var components = baseUrlComponents
|
||||
var components = baseURLComponents
|
||||
components.path = "/v3/collections"
|
||||
|
||||
guard let url = components.url else {
|
||||
@@ -236,7 +236,7 @@ final class FeedlyAPICaller {
|
||||
completion(.failure(CredentialsError.incompleteCredentials))
|
||||
}
|
||||
}
|
||||
var components = baseUrlComponents
|
||||
var components = baseURLComponents
|
||||
components.path = "/v3/collections"
|
||||
|
||||
guard let url = components.url else {
|
||||
@@ -293,13 +293,13 @@ final class FeedlyAPICaller {
|
||||
completion(.failure(CredentialsError.incompleteCredentials))
|
||||
}
|
||||
}
|
||||
guard let encodedId = encodeForURLPath(id) else {
|
||||
guard let encodedID = encodeForURLPath(id) else {
|
||||
return DispatchQueue.main.async {
|
||||
completion(.failure(FeedlyAccountDelegateError.unexpectedResourceId(id)))
|
||||
completion(.failure(FeedlyAccountDelegateError.unexpectedResourceID(id)))
|
||||
}
|
||||
}
|
||||
var components = baseUrlComponents
|
||||
components.percentEncodedPath = "/v3/collections/\(encodedId)"
|
||||
var components = baseURLComponents
|
||||
components.percentEncodedPath = "/v3/collections/\(encodedID)"
|
||||
|
||||
guard let url = components.url else {
|
||||
fatalError("\(components) does not produce a valid URL.")
|
||||
@@ -325,7 +325,7 @@ final class FeedlyAPICaller {
|
||||
}
|
||||
}
|
||||
|
||||
func removeFeed(_ feedId: String, fromCollectionWith collectionId: String, completion: @escaping (Result<Void, Error>) -> ()) {
|
||||
func removeFeed(_ feedID: String, fromCollectionWith collectionID: String, completion: @escaping (Result<Void, Error>) -> ()) {
|
||||
guard !isSuspended else {
|
||||
return DispatchQueue.main.async {
|
||||
completion(.failure(TransportError.suspended))
|
||||
@@ -338,14 +338,14 @@ final class FeedlyAPICaller {
|
||||
}
|
||||
}
|
||||
|
||||
guard let encodedCollectionId = encodeForURLPath(collectionId) else {
|
||||
guard let encodedCollectionID = encodeForURLPath(collectionID) else {
|
||||
return DispatchQueue.main.async {
|
||||
completion(.failure(FeedlyAccountDelegateError.unexpectedResourceId(collectionId)))
|
||||
completion(.failure(FeedlyAccountDelegateError.unexpectedResourceID(collectionID)))
|
||||
}
|
||||
}
|
||||
|
||||
var components = baseUrlComponents
|
||||
components.percentEncodedPath = "/v3/collections/\(encodedCollectionId)/feeds/.mdelete"
|
||||
var components = baseURLComponents
|
||||
components.percentEncodedPath = "/v3/collections/\(encodedCollectionID)/feeds/.mdelete"
|
||||
|
||||
guard let url = components.url else {
|
||||
fatalError("\(components) does not produce a valid URL.")
|
||||
@@ -362,7 +362,7 @@ final class FeedlyAPICaller {
|
||||
let id: String
|
||||
}
|
||||
let encoder = JSONEncoder()
|
||||
let data = try encoder.encode([RemovableFeed(id: feedId)])
|
||||
let data = try encoder.encode([RemovableFeed(id: feedID)])
|
||||
request.httpBody = data
|
||||
} catch {
|
||||
return DispatchQueue.main.async {
|
||||
@@ -389,7 +389,7 @@ final class FeedlyAPICaller {
|
||||
|
||||
extension FeedlyAPICaller: FeedlyAddFeedToCollectionService {
|
||||
|
||||
func addFeed(with feedId: FeedlyFeedResourceID, title: String? = nil, toCollectionWith collectionId: String, completion: @escaping (Result<[FeedlyFeed], Error>) -> ()) {
|
||||
func addFeed(with feedID: FeedlyFeedResourceID, title: String? = nil, toCollectionWith collectionID: String, completion: @escaping (Result<[FeedlyFeed], Error>) -> ()) {
|
||||
guard !isSuspended else {
|
||||
return DispatchQueue.main.async {
|
||||
completion(.failure(TransportError.suspended))
|
||||
@@ -402,13 +402,13 @@ extension FeedlyAPICaller: FeedlyAddFeedToCollectionService {
|
||||
}
|
||||
}
|
||||
|
||||
guard let encodedId = encodeForURLPath(collectionId) else {
|
||||
guard let encodedID = encodeForURLPath(collectionID) else {
|
||||
return DispatchQueue.main.async {
|
||||
completion(.failure(FeedlyAccountDelegateError.unexpectedResourceId(collectionId)))
|
||||
completion(.failure(FeedlyAccountDelegateError.unexpectedResourceID(collectionID)))
|
||||
}
|
||||
}
|
||||
var components = baseUrlComponents
|
||||
components.percentEncodedPath = "/v3/collections/\(encodedId)/feeds"
|
||||
var components = baseURLComponents
|
||||
components.percentEncodedPath = "/v3/collections/\(encodedID)/feeds"
|
||||
|
||||
guard let url = components.url else {
|
||||
fatalError("\(components) does not produce a valid URL.")
|
||||
@@ -426,7 +426,7 @@ extension FeedlyAPICaller: FeedlyAddFeedToCollectionService {
|
||||
var title: String?
|
||||
}
|
||||
let encoder = JSONEncoder()
|
||||
let data = try encoder.encode(AddFeedBody(id: feedId.id, title: title))
|
||||
let data = try encoder.encode(AddFeedBody(id: feedID.id, title: title))
|
||||
request.httpBody = data
|
||||
} catch {
|
||||
return DispatchQueue.main.async {
|
||||
@@ -451,8 +451,8 @@ extension FeedlyAPICaller: FeedlyAddFeedToCollectionService {
|
||||
|
||||
extension FeedlyAPICaller: OAuthAuthorizationCodeGrantRequesting {
|
||||
|
||||
static func authorizationCodeUrlRequest(for request: OAuthAuthorizationRequest, baseUrlComponents: URLComponents) -> URLRequest {
|
||||
var components = baseUrlComponents
|
||||
static func authorizationCodeURLRequest(for request: OAuthAuthorizationRequest, baseURLComponents: URLComponents) -> URLRequest {
|
||||
var components = baseURLComponents
|
||||
components.path = "/v3/auth/auth"
|
||||
components.queryItems = request.queryItems
|
||||
|
||||
@@ -477,7 +477,7 @@ extension FeedlyAPICaller: OAuthAuthorizationCodeGrantRequesting {
|
||||
}
|
||||
}
|
||||
|
||||
var components = baseUrlComponents
|
||||
var components = baseURLComponents
|
||||
components.path = "/v3/auth/token"
|
||||
|
||||
guard let url = components.url else {
|
||||
@@ -524,7 +524,7 @@ extension FeedlyAPICaller: OAuthAcessTokenRefreshRequesting {
|
||||
}
|
||||
}
|
||||
|
||||
var components = baseUrlComponents
|
||||
var components = baseURLComponents
|
||||
components.path = "/v3/auth/token"
|
||||
|
||||
guard let url = components.url else {
|
||||
@@ -576,7 +576,7 @@ extension FeedlyAPICaller: FeedlyGetCollectionsService {
|
||||
completion(.failure(CredentialsError.incompleteCredentials))
|
||||
}
|
||||
}
|
||||
var components = baseUrlComponents
|
||||
var components = baseURLComponents
|
||||
components.path = "/v3/collections"
|
||||
|
||||
guard let url = components.url else {
|
||||
@@ -618,7 +618,7 @@ extension FeedlyAPICaller: FeedlyGetStreamContentsService {
|
||||
}
|
||||
}
|
||||
|
||||
var components = baseUrlComponents
|
||||
var components = baseURLComponents
|
||||
components.path = "/v3/streams/contents"
|
||||
|
||||
var queryItems = [URLQueryItem]()
|
||||
@@ -686,7 +686,7 @@ extension FeedlyAPICaller: FeedlyGetStreamIDsService {
|
||||
}
|
||||
}
|
||||
|
||||
var components = baseUrlComponents
|
||||
var components = baseURLComponents
|
||||
components.path = "/v3/streams/ids"
|
||||
|
||||
var queryItems = [URLQueryItem]()
|
||||
@@ -754,7 +754,7 @@ extension FeedlyAPICaller: FeedlyGetEntriesService {
|
||||
}
|
||||
}
|
||||
|
||||
var components = baseUrlComponents
|
||||
var components = baseURLComponents
|
||||
components.path = "/v3/entries/.mget"
|
||||
|
||||
guard let url = components.url else {
|
||||
@@ -799,10 +799,10 @@ extension FeedlyAPICaller: FeedlyMarkArticlesService {
|
||||
private struct MarkerEntriesBody: Encodable {
|
||||
let type = "entries"
|
||||
var action: String
|
||||
var entryIds: [String]
|
||||
var entryIDs: [String]
|
||||
}
|
||||
|
||||
func mark(_ articleIds: Set<String>, as action: FeedlyMarkAction, completion: @escaping (Result<Void, Error>) -> ()) {
|
||||
func mark(_ articleIDs: Set<String>, as action: FeedlyMarkAction, completion: @escaping (Result<Void, Error>) -> ()) {
|
||||
guard !isSuspended else {
|
||||
return DispatchQueue.main.async {
|
||||
completion(.failure(TransportError.suspended))
|
||||
@@ -814,18 +814,18 @@ extension FeedlyAPICaller: FeedlyMarkArticlesService {
|
||||
completion(.failure(CredentialsError.incompleteCredentials))
|
||||
}
|
||||
}
|
||||
var components = baseUrlComponents
|
||||
var components = baseURLComponents
|
||||
components.path = "/v3/markers"
|
||||
|
||||
guard let url = components.url else {
|
||||
fatalError("\(components) does not produce a valid URL.")
|
||||
}
|
||||
|
||||
let articleIdChunks = Array(articleIds).chunked(into: 300)
|
||||
let articleIDChunks = Array(articleIDs).chunked(into: 300)
|
||||
let dispatchGroup = DispatchGroup()
|
||||
var groupError: Error? = nil
|
||||
|
||||
for articleIdChunk in articleIdChunks {
|
||||
for articleIDChunk in articleIDChunks {
|
||||
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
@@ -834,7 +834,7 @@ extension FeedlyAPICaller: FeedlyMarkArticlesService {
|
||||
request.addValue("OAuth \(accessToken)", forHTTPHeaderField: HTTPRequestHeader.authorization)
|
||||
|
||||
do {
|
||||
let body = MarkerEntriesBody(action: action.actionValue, entryIds: Array(articleIdChunk))
|
||||
let body = MarkerEntriesBody(action: action.actionValue, entryIDs: Array(articleIDChunk))
|
||||
let encoder = JSONEncoder()
|
||||
let data = try encoder.encode(body)
|
||||
request.httpBody = data
|
||||
@@ -878,7 +878,7 @@ extension FeedlyAPICaller: FeedlySearchService {
|
||||
}
|
||||
}
|
||||
|
||||
var components = baseUrlComponents
|
||||
var components = baseURLComponents
|
||||
components.path = "/v3/search/feeds"
|
||||
|
||||
components.queryItems = [
|
||||
@@ -926,7 +926,7 @@ extension FeedlyAPICaller: FeedlyLogoutService {
|
||||
completion(.failure(CredentialsError.incompleteCredentials))
|
||||
}
|
||||
}
|
||||
var components = baseUrlComponents
|
||||
var components = baseURLComponents
|
||||
components.path = "/v3/auth/logout"
|
||||
|
||||
guard let url = components.url else {
|
||||
|
||||
@@ -30,12 +30,12 @@ extension FeedlyAccountDelegate: OAuthAuthorizationGranting {
|
||||
|
||||
static func oauthAuthorizationCodeGrantRequest() -> URLRequest {
|
||||
let client = environment.oauthAuthorizationClient
|
||||
let authorizationRequest = OAuthAuthorizationRequest(clientId: client.id,
|
||||
redirectUri: client.redirectUri,
|
||||
let authorizationRequest = OAuthAuthorizationRequest(clientID: client.id,
|
||||
redirectURI: client.redirectURI,
|
||||
scope: oauthAuthorizationGrantScope,
|
||||
state: client.state)
|
||||
let baseURLComponents = environment.baseUrlComponents
|
||||
return FeedlyAPICaller.authorizationCodeUrlRequest(for: authorizationRequest, baseUrlComponents: baseURLComponents)
|
||||
let baseURLComponents = environment.baseURLComponents
|
||||
return FeedlyAPICaller.authorizationCodeURLRequest(for: authorizationRequest, baseURLComponents: baseURLComponents)
|
||||
}
|
||||
|
||||
static func requestOAuthAccessToken(with response: OAuthAuthorizationResponse, transport: Transport, completion: @escaping (Result<OAuthAuthorizationGrant, Error>) -> ()) {
|
||||
|
||||
@@ -195,7 +195,7 @@ final class FeedlyAccountDelegate: AccountDelegate, Logging {
|
||||
|
||||
}
|
||||
|
||||
let ingestStarred = FeedlyIngestStarredArticleIDsOperation(account: account, userId: credentials.username, service: caller, database: database, newerThan: nil)
|
||||
let ingestStarred = FeedlyIngestStarredArticleIDsOperation(account: account, userID: credentials.username, service: caller, database: database, newerThan: nil)
|
||||
|
||||
group.enter()
|
||||
ingestStarred.completionBlock = { _ in
|
||||
@@ -377,18 +377,18 @@ final class FeedlyAccountDelegate: AccountDelegate, Logging {
|
||||
}
|
||||
|
||||
func renameFeed(for account: Account, with feed: Feed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
let folderCollectionIds = account.folders?.filter { $0.has(feed) }.compactMap { $0.externalID }
|
||||
guard let collectionIds = folderCollectionIds, let collectionId = collectionIds.first else {
|
||||
let folderCollectionIDs = account.folders?.filter { $0.has(feed) }.compactMap { $0.externalID }
|
||||
guard let collectionIDs = folderCollectionIDs, let collectionID = collectionIDs.first else {
|
||||
completion(.failure(FeedlyAccountDelegateError.unableToRenameFeed(feed.nameForDisplay, name)))
|
||||
return
|
||||
}
|
||||
|
||||
let feedId = FeedlyFeedResourceID(id: feed.feedID)
|
||||
let feedID = FeedlyFeedResourceID(id: feed.feedID)
|
||||
let editedNameBefore = feed.editedName
|
||||
|
||||
// Adding an existing feed updates it.
|
||||
// Updating feed name in one folder/collection updates it for all folders/collections.
|
||||
caller.addFeed(with: feedId, title: name, toCollectionWith: collectionId) { result in
|
||||
caller.addFeed(with: feedID, title: name, toCollectionWith: collectionID) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(()))
|
||||
@@ -434,13 +434,13 @@ final class FeedlyAccountDelegate: AccountDelegate, Logging {
|
||||
}
|
||||
|
||||
func removeFeed(for account: Account, with feed: Feed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
guard let folder = container as? Folder, let collectionId = folder.externalID else {
|
||||
guard let folder = container as? Folder, let collectionID = folder.externalID else {
|
||||
return DispatchQueue.main.async {
|
||||
completion(.failure(FeedlyAccountDelegateError.unableToRemoveFeed(feed)))
|
||||
}
|
||||
}
|
||||
|
||||
caller.removeFeed(feed.feedID, fromCollectionWith: collectionId) { result in
|
||||
caller.removeFeed(feed.feedID, fromCollectionWith: collectionID) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(()))
|
||||
|
||||
@@ -10,7 +10,7 @@ import Foundation
|
||||
|
||||
enum FeedlyAccountDelegateError: LocalizedError {
|
||||
case notLoggedIn
|
||||
case unexpectedResourceId(String)
|
||||
case unexpectedResourceID(String)
|
||||
case unableToAddFolder(String)
|
||||
case unableToRenameFolder(String, String)
|
||||
case unableToRemoveFolder(String)
|
||||
@@ -25,9 +25,9 @@ enum FeedlyAccountDelegateError: LocalizedError {
|
||||
case .notLoggedIn:
|
||||
return NSLocalizedString("Please add the Feedly account again. If this problem persists, open Keychain Access and delete all feedly.com entries, then try again.", comment: "Feedly – Credentials not found.")
|
||||
|
||||
case .unexpectedResourceId(let resourceId):
|
||||
case .unexpectedResourceID(let resourceID):
|
||||
let template = NSLocalizedString("Could not encode the identifier “%@”.", comment: "Feedly – Could not encode resource id to send to Feedly.")
|
||||
return String(format: template, resourceId)
|
||||
return String(format: template, resourceID)
|
||||
|
||||
case .unableToAddFolder(let name):
|
||||
let template = NSLocalizedString("Could not create a folder named “%@”.", comment: "Feedly – Could not create a folder/collection.")
|
||||
@@ -67,7 +67,7 @@ enum FeedlyAccountDelegateError: LocalizedError {
|
||||
case .notLoggedIn:
|
||||
return nil
|
||||
|
||||
case .unexpectedResourceId:
|
||||
case .unexpectedResourceID:
|
||||
let template = NSLocalizedString("Please contact NetNewsWire support.", comment: "Feedly – Recovery suggestion for not being able to encode a resource id to send to Feedly..")
|
||||
return String(format: template)
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ struct FeedlyEntry: Decodable {
|
||||
/// the timestamp, in ms, when this article was re-processed and updated by the feedly Cloud servers.
|
||||
let recrawled: Date?
|
||||
|
||||
/// the feed from which this article was crawled. If present, “streamId” will contain the feed id, “title” will contain the feed title, and “htmlUrl” will contain the feed’s website.
|
||||
/// the feed from which this article was crawled. If present, “streamID” will contain the feed id, “title” will contain the feed title, and “htmlUrl” will contain the feed’s website.
|
||||
let origin: FeedlyOrigin?
|
||||
|
||||
/// Used to help find the URL to visit an article on a web site.
|
||||
|
||||
@@ -21,7 +21,7 @@ struct FeedlyEntryParser {
|
||||
|
||||
/// When ingesting articles, the feedURL must match a feed's `feedID` for the article to be reachable between it and its matching feed. It reminds me of a foreign key.
|
||||
var feedURL: String? {
|
||||
guard let id = entry.origin?.streamId else {
|
||||
guard let id = entry.origin?.streamID else {
|
||||
// At this point, check Feedly's API isn't glitching or the response has not changed structure.
|
||||
assertionFailure("Entries need to be traceable to a feed or this entry will be dropped.")
|
||||
return nil
|
||||
|
||||
@@ -44,7 +44,12 @@ struct FeedlyFeedsSearchResponse: Decodable {
|
||||
|
||||
struct Feed: Decodable {
|
||||
let title: String
|
||||
let feedId: String
|
||||
let feedID: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case title = "title"
|
||||
case feedID = "feedId"
|
||||
}
|
||||
}
|
||||
|
||||
let results: [Feed]
|
||||
@@ -61,8 +66,14 @@ struct FeedlyLink: Decodable {
|
||||
|
||||
struct FeedlyOrigin: Decodable {
|
||||
let title: String?
|
||||
let streamId: String?
|
||||
let htmlUrl: String?
|
||||
let streamID: String?
|
||||
let htmlURL: String?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case title = "title"
|
||||
case streamID = "streamId"
|
||||
case htmlURL = "htmlUrl"
|
||||
}
|
||||
}
|
||||
|
||||
struct FeedlyStream: Decodable {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// FeedlyResourceId.swift
|
||||
// FeedlyResourceID.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Kiel Gillard on 3/10/19.
|
||||
@@ -8,10 +8,10 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
/// The kinds of Resource Ids is documented here: https://developer.feedly.com/cloud/
|
||||
/// The kinds of Resource IDs is documented here: https://developer.feedly.com/cloud/
|
||||
protocol FeedlyResourceID {
|
||||
|
||||
/// The resource Id from Feedly.
|
||||
/// The resource ID from Feedly.
|
||||
var id: String { get }
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ struct FeedlyFeedResourceID: FeedlyResourceID {
|
||||
let id: String
|
||||
|
||||
/// The location of the kind of resource a concrete type represents.
|
||||
/// If the concrete type cannot strip the resource type from the Id, it should just return the Id
|
||||
/// since the Id is a legitimate URL.
|
||||
/// If the concrete type cannot strip the resource type from the ID, it should just return the ID
|
||||
/// since the ID is a legitimate URL.
|
||||
/// This is basically assuming Feedly prefixes source feed URLs with `feed/`.
|
||||
/// It is not documented as such and could potentially change.
|
||||
/// Feedly does not include the source feed URL as a separate field.
|
||||
|
||||
@@ -59,8 +59,8 @@ public enum OAuthAccountAuthorizationOperationError: LocalizedError {
|
||||
}
|
||||
}
|
||||
|
||||
guard let redirectUri = URL(string: oauthClient.redirectUri), let scheme = redirectUri.scheme else {
|
||||
assertionFailure("Could not get callback URL scheme from \(oauthClient.redirectUri)")
|
||||
guard let redirectURI = URL(string: oauthClient.redirectURI), let scheme = redirectURI.scheme else {
|
||||
assertionFailure("Could not get callback URL scheme from \(oauthClient.redirectURI)")
|
||||
return DispatchQueue.main.async {
|
||||
self.didEndAuthentication(url: nil, error: URLError(.badURL))
|
||||
}
|
||||
|
||||
@@ -17,13 +17,21 @@ public struct OAuthRefreshAccessTokenRequest: Encodable {
|
||||
public var scope: String?
|
||||
|
||||
// Possibly not part of the standard but specific to certain implementations (e.g.: Feedly).
|
||||
public var clientId: String
|
||||
public var clientID: String
|
||||
public var clientSecret: String
|
||||
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case grantType = "grantType"
|
||||
case refreshToken = "refreshToken"
|
||||
case scope = "scope"
|
||||
case clientID = "clientId"
|
||||
case clientSecret = "clientSecret"
|
||||
}
|
||||
|
||||
public init(refreshToken: String, scope: String?, client: OAuthAuthorizationClient) {
|
||||
self.refreshToken = refreshToken
|
||||
self.scope = scope
|
||||
self.clientId = client.id
|
||||
self.clientID = client.id
|
||||
self.clientSecret = client.secret
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ extension OAuthAuthorizationClient {
|
||||
/// These placeholders are substituted at build time using a Run Script phase with build settings.
|
||||
/// https://developer.feedly.com/v3/auth/#authenticating-a-user-and-obtaining-an-auth-code
|
||||
return OAuthAuthorizationClient(id: SecretsManager.provider.feedlyClientId,
|
||||
redirectUri: "netnewswire://auth/feedly",
|
||||
redirectURI: "netnewswire://auth/feedly",
|
||||
state: nil,
|
||||
secret: SecretsManager.provider.feedlyClientSecret)
|
||||
}
|
||||
@@ -27,9 +27,9 @@ extension OAuthAuthorizationClient {
|
||||
/// The return value models public sandbox API values found at:
|
||||
/// https://groups.google.com/forum/#!topic/feedly-cloud/WwQWMgDmOuw
|
||||
/// They are due to expire on May 31st 2020.
|
||||
/// Verify the sandbox URL host in the FeedlyAPICaller.API.baseUrlComponents method, too.
|
||||
/// Verify the sandbox URL host in the FeedlyAPICaller.API.baseURLComponents method, too.
|
||||
return OAuthAuthorizationClient(id: "sandbox",
|
||||
redirectUri: "urn:ietf:wg:oauth:2.0:oob",
|
||||
redirectURI: "urn:ietf:wg:oauth:2.0:oob",
|
||||
state: nil,
|
||||
secret: "4ZfZ5DvqmJ8vKgMj")
|
||||
}
|
||||
|
||||
@@ -14,13 +14,13 @@ import Secrets
|
||||
/// Accounts are responsible for the scope.
|
||||
public struct OAuthAuthorizationClient: Equatable {
|
||||
public var id: String
|
||||
public var redirectUri: String
|
||||
public var redirectURI: String
|
||||
public var state: String?
|
||||
public var secret: String
|
||||
|
||||
public init(id: String, redirectUri: String, state: String?, secret: String) {
|
||||
public init(id: String, redirectURI: String, state: String?, secret: String) {
|
||||
self.id = id
|
||||
self.redirectUri = redirectUri
|
||||
self.redirectURI = redirectURI
|
||||
self.state = state
|
||||
self.secret = secret
|
||||
}
|
||||
@@ -30,14 +30,14 @@ public struct OAuthAuthorizationClient: Equatable {
|
||||
/// https://tools.ietf.org/html/rfc6749#section-4.1.1
|
||||
public struct OAuthAuthorizationRequest {
|
||||
public let responseType = "code"
|
||||
public var clientId: String
|
||||
public var redirectUri: String
|
||||
public var clientID: String
|
||||
public var redirectURI: String
|
||||
public var scope: String
|
||||
public var state: String?
|
||||
|
||||
public init(clientId: String, redirectUri: String, scope: String, state: String?) {
|
||||
self.clientId = clientId
|
||||
self.redirectUri = redirectUri
|
||||
public init(clientID: String, redirectURI: String, scope: String, state: String?) {
|
||||
self.clientID = clientID
|
||||
self.redirectURI = redirectURI
|
||||
self.scope = scope
|
||||
self.state = state
|
||||
}
|
||||
@@ -45,9 +45,9 @@ public struct OAuthAuthorizationRequest {
|
||||
public var queryItems: [URLQueryItem] {
|
||||
return [
|
||||
URLQueryItem(name: "response_type", value: responseType),
|
||||
URLQueryItem(name: "client_id", value: clientId),
|
||||
URLQueryItem(name: "client_id", value: clientID),
|
||||
URLQueryItem(name: "scope", value: scope),
|
||||
URLQueryItem(name: "redirect_uri", value: redirectUri),
|
||||
URLQueryItem(name: "redirect_uri", value: redirectURI),
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ public struct OAuthAuthorizationResponse {
|
||||
public extension OAuthAuthorizationResponse {
|
||||
|
||||
init(url: URL, client: OAuthAuthorizationClient) throws {
|
||||
guard let scheme = url.scheme, client.redirectUri.hasPrefix(scheme) else {
|
||||
guard let scheme = url.scheme, client.redirectURI.hasPrefix(scheme) else {
|
||||
throw URLError(.unsupportedURL)
|
||||
}
|
||||
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
|
||||
@@ -112,19 +112,29 @@ public enum OAuthAuthorizationError: String {
|
||||
public struct OAuthAccessTokenRequest: Encodable {
|
||||
public let grantType = "authorization_code"
|
||||
public var code: String
|
||||
public var redirectUri: String
|
||||
public var redirectURI: String
|
||||
public var state: String?
|
||||
public var clientId: String
|
||||
public var clientID: String
|
||||
|
||||
// Possibly not part of the standard but specific to certain implementations (e.g.: Feedly).
|
||||
public var clientSecret: String
|
||||
public var scope: String
|
||||
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case grantType = "grantType"
|
||||
case code = "code"
|
||||
case redirectURI = "redirectUri"
|
||||
case state = "state"
|
||||
case clientID = "clientId"
|
||||
case clientSecret = "clientSecret"
|
||||
case scope = "scope"
|
||||
}
|
||||
|
||||
public init(authorizationResponse: OAuthAuthorizationResponse, scope: String, client: OAuthAuthorizationClient) {
|
||||
self.code = authorizationResponse.code
|
||||
self.redirectUri = client.redirectUri
|
||||
self.redirectURI = client.redirectURI
|
||||
self.state = authorizationResponse.state
|
||||
self.clientId = client.id
|
||||
self.clientID = client.id
|
||||
self.clientSecret = client.secret
|
||||
self.scope = scope
|
||||
}
|
||||
@@ -155,8 +165,8 @@ public protocol OAuthAuthorizationCodeGrantRequesting {
|
||||
|
||||
/// Provides the URL request that allows users to consent to the client having access to their information. Typically loaded by a web view.
|
||||
/// - Parameter request: The information about the client requesting authorization to be granted access tokens.
|
||||
/// - Parameter baseUrlComponents: The scheme and host of the url except for the path.
|
||||
static func authorizationCodeUrlRequest(for request: OAuthAuthorizationRequest, baseUrlComponents: URLComponents) -> URLRequest
|
||||
/// - Parameter baseURLComponents: The scheme and host of the url except for the path.
|
||||
static func authorizationCodeURLRequest(for request: OAuthAuthorizationRequest, baseURLComponents: URLComponents) -> URLRequest
|
||||
|
||||
|
||||
/// Performs the request for the access token given an authorization code.
|
||||
|
||||
@@ -19,7 +19,7 @@ class FeedlyAddExistingFeedOperation: FeedlyOperation, FeedlyOperationDelegate,
|
||||
init(account: Account, credentials: Credentials, resource: FeedlyFeedResourceID, service: FeedlyAddFeedToCollectionService, container: Container, progress: DownloadProgress, customFeedName: String? = nil) throws {
|
||||
|
||||
let validator = FeedlyFeedContainerValidator(container: container)
|
||||
let (folder, collectionId) = try validator.getValidContainer()
|
||||
let (folder, collectionID) = try validator.getValidContainer()
|
||||
|
||||
self.operationQueue.suspend()
|
||||
|
||||
@@ -27,7 +27,7 @@ class FeedlyAddExistingFeedOperation: FeedlyOperation, FeedlyOperationDelegate,
|
||||
|
||||
self.downloadProgress = progress
|
||||
|
||||
let addRequest = FeedlyAddFeedToCollectionOperation(account: account, folder: folder, feedResource: resource, feedName: customFeedName, collectionID: collectionId, service: service)
|
||||
let addRequest = FeedlyAddFeedToCollectionOperation(account: account, folder: folder, feedResource: resource, feedName: customFeedName, collectionID: collectionID, service: service)
|
||||
addRequest.delegate = self
|
||||
addRequest.downloadProgress = progress
|
||||
self.operationQueue.add(addRequest)
|
||||
|
||||
@@ -81,7 +81,7 @@ class FeedlyAddNewFeedOperation: FeedlyOperation, FeedlyOperationDelegate, Feedl
|
||||
return didFinish(with: AccountError.createErrorNotFound)
|
||||
}
|
||||
|
||||
let feedResourceID = FeedlyFeedResourceID(id: first.feedId)
|
||||
let feedResourceID = FeedlyFeedResourceID(id: first.feedID)
|
||||
self.feedResourceID = feedResourceID
|
||||
|
||||
let addRequest = FeedlyAddFeedToCollectionOperation(account: account, folder: folder, feedResource: feedResourceID, feedName: feedName, collectionID: collectionID, service: addToCollectionService)
|
||||
|
||||
@@ -32,15 +32,15 @@ class FeedlyDownloadArticlesOperation: FeedlyOperation, Logging {
|
||||
}
|
||||
|
||||
override func run() {
|
||||
var articleIds = missingArticleEntryIDProvider.entryIDs
|
||||
articleIds.formUnion(updatedArticleEntryIDProvider.entryIDs)
|
||||
var articleIDs = missingArticleEntryIDProvider.entryIDs
|
||||
articleIDs.formUnion(updatedArticleEntryIDProvider.entryIDs)
|
||||
|
||||
self.logger.debug("Requesting \(articleIds.count, privacy: .public) articles.")
|
||||
self.logger.debug("Requesting \(articleIDs.count, privacy: .public) articles.")
|
||||
|
||||
let feedlyAPILimitBatchSize = 1000
|
||||
for articleIds in Array(articleIds).chunked(into: feedlyAPILimitBatchSize) {
|
||||
for articleIDs in Array(articleIDs).chunked(into: feedlyAPILimitBatchSize) {
|
||||
|
||||
let provider = FeedlyEntryIdentifierProvider(entryIDs: Set(articleIds))
|
||||
let provider = FeedlyEntryIdentifierProvider(entryIDs: Set(articleIDs))
|
||||
let getEntries = FeedlyGetEntriesOperation(account: account, service: getEntriesService, provider: provider)
|
||||
getEntries.delegate = self
|
||||
self.operationQueue.add(getEntries)
|
||||
|
||||
@@ -22,8 +22,8 @@ final class FeedlyFetchIDsForMissingArticlesOperation: FeedlyOperation, FeedlyEn
|
||||
override func run() {
|
||||
account.fetchArticleIDsForStatusesWithoutArticlesNewerThanCutoffDate { result in
|
||||
switch result {
|
||||
case .success(let articleIds):
|
||||
self.entryIDs.formUnion(articleIds)
|
||||
case .success(let articleIDs):
|
||||
self.entryIDs.formUnion(articleIDs)
|
||||
self.didFinish()
|
||||
|
||||
case .failure(let error):
|
||||
|
||||
@@ -25,8 +25,8 @@ final class FeedlyIngestStarredArticleIDsOperation: FeedlyOperation, Logging {
|
||||
private let database: SyncDatabase
|
||||
private var remoteEntryIDs = Set<String>()
|
||||
|
||||
convenience init(account: Account, userId: String, service: FeedlyGetStreamIDsService, database: SyncDatabase, newerThan: Date?) {
|
||||
let resource = FeedlyTagResourceID.Global.saved(for: userId)
|
||||
convenience init(account: Account, userID: String, service: FeedlyGetStreamIDsService, database: SyncDatabase, newerThan: Date?) {
|
||||
let resource = FeedlyTagResourceID.Global.saved(for: userID)
|
||||
self.init(account: account, resource: resource, service: service, database: database, newerThan: newerThan)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ class FeedlyIngestStreamArticleIDsOperation: FeedlyOperation, Logging {
|
||||
self.service = service
|
||||
}
|
||||
|
||||
convenience init(account: Account, userId: String, service: FeedlyGetStreamIDsService) {
|
||||
let all = FeedlyCategoryResourceID.Global.all(for: userId)
|
||||
convenience init(account: Account, userID: String, service: FeedlyGetStreamIDsService) {
|
||||
let all = FeedlyCategoryResourceID.Global.all(for: userID)
|
||||
self.init(account: account, resource: all, service: service)
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ final class FeedlySyncAllOperation: FeedlyOperation, Logging {
|
||||
createFeedsOperation.addDependency(mirrorCollectionsAsFolders)
|
||||
self.operationQueue.add(createFeedsOperation)
|
||||
|
||||
let getAllArticleIDs = FeedlyIngestStreamArticleIDsOperation(account: account, userId: feedlyUserID, service: getStreamIDsService)
|
||||
let getAllArticleIDs = FeedlyIngestStreamArticleIDsOperation(account: account, userID: feedlyUserID, service: getStreamIDsService)
|
||||
getAllArticleIDs.delegate = self
|
||||
getAllArticleIDs.downloadProgress = downloadProgress
|
||||
getAllArticleIDs.addDependency(createFeedsOperation)
|
||||
@@ -87,7 +87,7 @@ final class FeedlySyncAllOperation: FeedlyOperation, Logging {
|
||||
self.operationQueue.add(getUpdated)
|
||||
|
||||
// Get each page of the article ids for starred articles.
|
||||
let getStarred = FeedlyIngestStarredArticleIDsOperation(account: account, userId: feedlyUserID, service: getStarredService, database: database, newerThan: nil)
|
||||
let getStarred = FeedlyIngestStarredArticleIDsOperation(account: account, userID: feedlyUserID, service: getStarredService, database: database, newerThan: nil)
|
||||
getStarred.delegate = self
|
||||
getStarred.downloadProgress = downloadProgress
|
||||
getStarred.addDependency(createFeedsOperation)
|
||||
|
||||
@@ -31,5 +31,5 @@ enum FeedlyMarkAction: String {
|
||||
}
|
||||
|
||||
protocol FeedlyMarkArticlesService: AnyObject {
|
||||
func mark(_ articleIds: Set<String>, as action: FeedlyMarkAction, completion: @escaping (Result<Void, Error>) -> ())
|
||||
func mark(_ articleIDs: Set<String>, as action: FeedlyMarkAction, completion: @escaping (Result<Void, Error>) -> ())
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public extension URLRequest {
|
||||
URLQueryItem(name: "password", value: credentials.secret),
|
||||
]
|
||||
httpBody = postData.enhancedPercentEncodedQuery?.data(using: .utf8)
|
||||
case .newsBlurSessionId:
|
||||
case .newsBlurSessionID:
|
||||
setValue("\(NewsBlurAPICaller.SessionIdCookie)=\(credentials.secret)", forHTTPHeaderField: "Cookie")
|
||||
httpShouldHandleCookies = true
|
||||
case .readerBasic:
|
||||
|
||||
@@ -65,7 +65,7 @@ class FeedlyCreateFeedsForCollectionFoldersOperationTests: XCTestCase {
|
||||
|
||||
waitForExpectations(timeout: 2)
|
||||
|
||||
let feedIds = Set([feedsForFolderOne, feedsForFolderTwo]
|
||||
let feedIDs = Set([feedsForFolderOne, feedsForFolderTwo]
|
||||
.flatMap { $0 }
|
||||
.map { $0.id })
|
||||
|
||||
@@ -74,28 +74,28 @@ class FeedlyCreateFeedsForCollectionFoldersOperationTests: XCTestCase {
|
||||
.map { $0.title })
|
||||
|
||||
let accountFeeds = account.flattenedFeeds()
|
||||
let ingestedIds = Set(accountFeeds.map { $0.feedID })
|
||||
let ingestedIDs = Set(accountFeeds.map { $0.feedID })
|
||||
let ingestedTitles = Set(accountFeeds.map { $0.nameForDisplay })
|
||||
|
||||
let missingIds = feedIds.subtracting(ingestedIds)
|
||||
let missingIDs = feedIDs.subtracting(ingestedIDs)
|
||||
let missingTitles = feedTitles.subtracting(ingestedTitles)
|
||||
|
||||
XCTAssertTrue(missingIds.isEmpty, "Failed to ingest feeds with these ids.")
|
||||
XCTAssertTrue(missingIDs.isEmpty, "Failed to ingest feeds with these ids.")
|
||||
XCTAssertTrue(missingTitles.isEmpty, "Failed to ingest feeds with these titles.")
|
||||
|
||||
let expectedFolderAndFeedIds = namesAndFeeds
|
||||
let expectedFolderAndFeedIDs = namesAndFeeds
|
||||
.sorted { $0.0.id < $1.0.id }
|
||||
.map { folder, feeds -> [String: [String]] in
|
||||
return [folder.id: feeds.map { $0.id }.sorted(by: <)]
|
||||
}
|
||||
|
||||
let ingestedFolderAndFeedIds = (account.folders ?? Set())
|
||||
let ingestedFolderAndFeedIDs = (account.folders ?? Set())
|
||||
.sorted { $0.externalID! < $1.externalID! }
|
||||
.compactMap { folder -> [String: [String]]? in
|
||||
return [folder.externalID!: folder.topLevelFeeds.map { $0.feedID }.sorted(by: <)]
|
||||
}
|
||||
|
||||
XCTAssertEqual(expectedFolderAndFeedIds, ingestedFolderAndFeedIds, "Did not ingest feeds in their corresponding folders.")
|
||||
XCTAssertEqual(expectedFolderAndFeedIDs, ingestedFolderAndFeedIDs, "Did not ingest feeds in their corresponding folders.")
|
||||
}
|
||||
|
||||
func testRemoveFeeds() {
|
||||
@@ -158,7 +158,7 @@ class FeedlyCreateFeedsForCollectionFoldersOperationTests: XCTestCase {
|
||||
|
||||
waitForExpectations(timeout: 2)
|
||||
|
||||
let feedIds = Set([feedsForFolderOne, feedsForFolderTwo]
|
||||
let feedIDs = Set([feedsForFolderOne, feedsForFolderTwo]
|
||||
.flatMap { $0 }
|
||||
.map { $0.id })
|
||||
|
||||
@@ -167,30 +167,30 @@ class FeedlyCreateFeedsForCollectionFoldersOperationTests: XCTestCase {
|
||||
.map { $0.title })
|
||||
|
||||
let accountFeeds = account.flattenedFeeds()
|
||||
let ingestedIds = Set(accountFeeds.map { $0.feedID })
|
||||
let ingestedIDs = Set(accountFeeds.map { $0.feedID })
|
||||
let ingestedTitles = Set(accountFeeds.map { $0.nameForDisplay })
|
||||
|
||||
XCTAssertEqual(ingestedIds.count, feedIds.count)
|
||||
XCTAssertEqual(ingestedIDs.count, feedIDs.count)
|
||||
XCTAssertEqual(ingestedTitles.count, feedTitles.count)
|
||||
|
||||
let missingIds = feedIds.subtracting(ingestedIds)
|
||||
let missingIDs = feedIDs.subtracting(ingestedIDs)
|
||||
let missingTitles = feedTitles.subtracting(ingestedTitles)
|
||||
|
||||
XCTAssertTrue(missingIds.isEmpty, "Failed to ingest feeds with these ids.")
|
||||
XCTAssertTrue(missingIDs.isEmpty, "Failed to ingest feeds with these ids.")
|
||||
XCTAssertTrue(missingTitles.isEmpty, "Failed to ingest feeds with these titles.")
|
||||
|
||||
let expectedFolderAndFeedIds = namesAndFeeds
|
||||
let expectedFolderAndFeedIDs = namesAndFeeds
|
||||
.sorted { $0.0.id < $1.0.id }
|
||||
.map { folder, feeds -> [String: [String]] in
|
||||
return [folder.id: feeds.map { $0.id }.sorted(by: <)]
|
||||
}
|
||||
|
||||
let ingestedFolderAndFeedIds = (account.folders ?? Set())
|
||||
let ingestedFolderAndFeedIDs = (account.folders ?? Set())
|
||||
.sorted { $0.externalID! < $1.externalID! }
|
||||
.compactMap { folder -> [String: [String]]? in
|
||||
return [folder.externalID!: folder.topLevelFeeds.map { $0.feedID }.sorted(by: <)]
|
||||
}
|
||||
|
||||
XCTAssertEqual(expectedFolderAndFeedIds, ingestedFolderAndFeedIds, "Did not ingest feeds to their corresponding folders.")
|
||||
XCTAssertEqual(expectedFolderAndFeedIDs, ingestedFolderAndFeedIDs, "Did not ingest feeds to their corresponding folders.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class FeedlyEntryParserTests: XCTestCase {
|
||||
func testParsing() {
|
||||
let content = FeedlyEntry.Content(content: "Test Content", direction: .leftToRight)
|
||||
let summary = FeedlyEntry.Content(content: "Test Summary", direction: .leftToRight)
|
||||
let origin = FeedlyOrigin(title: "Test Feed", streamId: "tests://feeds/1", htmlUrl: nil)
|
||||
let origin = FeedlyOrigin(title: "Test Feed", streamID: "tests://feeds/1", htmlURL: nil)
|
||||
let canonicalLink = FeedlyLink(href: "tests://feeds/1/entries/1", type: "text/html")
|
||||
let tags = [
|
||||
FeedlyTag(id: "tests/tags/1", label: "Tag 1"),
|
||||
@@ -38,8 +38,8 @@ class FeedlyEntryParserTests: XCTestCase {
|
||||
let parser = FeedlyEntryParser(entry: entry)
|
||||
|
||||
XCTAssertEqual(parser.id, entry.id)
|
||||
XCTAssertEqual(parser.feedUrl, origin.streamId)
|
||||
XCTAssertEqual(parser.externalUrl, canonicalLink.href)
|
||||
XCTAssertEqual(parser.feedURL, origin.streamID)
|
||||
XCTAssertEqual(parser.externalURL, canonicalLink.href)
|
||||
XCTAssertEqual(parser.title, entry.title)
|
||||
XCTAssertEqual(parser.contentHMTL, content.content)
|
||||
XCTAssertEqual(parser.summary, summary.content)
|
||||
@@ -56,7 +56,7 @@ class FeedlyEntryParserTests: XCTestCase {
|
||||
|
||||
// The following is not an error.
|
||||
// The feedURL must match the feedID for the article to be connected to its matching feed.
|
||||
XCTAssertEqual(item.feedURL, origin.streamId)
|
||||
XCTAssertEqual(item.feedURL, origin.streamID)
|
||||
XCTAssertEqual(item.title, entry.title)
|
||||
XCTAssertEqual(item.contentHTML, content.content)
|
||||
XCTAssertEqual(item.contentText, nil, "Is it now free of HTML characters?")
|
||||
@@ -76,7 +76,7 @@ class FeedlyEntryParserTests: XCTestCase {
|
||||
let content = FeedlyEntry.Content(content: "<div style=\"direction:rtl;text-align:right\">Test Content</div>", direction: .rightToLeft)
|
||||
let summaryContent = "Test Summary"
|
||||
let summary = FeedlyEntry.Content(content: "<div style=\"direction:rtl;text-align:right\">\(summaryContent)</div>", direction: .rightToLeft)
|
||||
let origin = FeedlyOrigin(title: "Test Feed", streamId: "tests://feeds/1", htmlUrl: nil)
|
||||
let origin = FeedlyOrigin(title: "Test Feed", streamID: "tests://feeds/1", htmlURL: nil)
|
||||
let title = "Test Entry 1"
|
||||
let entry = FeedlyEntry(id: "tests/feeds/1/entries/1",
|
||||
title: "<div style=\"direction:rtl;text-align:right\">\(title)</div>",
|
||||
@@ -103,7 +103,7 @@ class FeedlyEntryParserTests: XCTestCase {
|
||||
XCTAssertEqual(parser.contentHMTL, content.content)
|
||||
}
|
||||
|
||||
func testLocatesCanonicalExternalUrl() {
|
||||
func testLocatesCanonicalExternalURL() {
|
||||
let canonicalLink = FeedlyLink(href: "tests://feeds/1/entries/1", type: "text/html")
|
||||
let alternateLink = FeedlyLink(href: "tests://feeds/1/entries/alternate/1", type: "text/html")
|
||||
let entry = FeedlyEntry(id: "tests/feeds/1/entries/1",
|
||||
@@ -123,10 +123,10 @@ class FeedlyEntryParserTests: XCTestCase {
|
||||
|
||||
let parser = FeedlyEntryParser(entry: entry)
|
||||
|
||||
XCTAssertEqual(parser.externalUrl, canonicalLink.href)
|
||||
XCTAssertEqual(parser.externalURL, canonicalLink.href)
|
||||
}
|
||||
|
||||
func testLocatesAlternateExternalUrl() {
|
||||
func testLocatesAlternateExternalURL() {
|
||||
let canonicalLink = FeedlyLink(href: "tests://feeds/1/entries/1", type: "text/json")
|
||||
let alternateLink = FeedlyLink(href: "tests://feeds/1/entries/alternate/1", type: nil)
|
||||
let entry = FeedlyEntry(id: "tests/feeds/1/entries/1",
|
||||
@@ -146,7 +146,7 @@ class FeedlyEntryParserTests: XCTestCase {
|
||||
|
||||
let parser = FeedlyEntryParser(entry: entry)
|
||||
|
||||
XCTAssertEqual(parser.externalUrl, alternateLink.href)
|
||||
XCTAssertEqual(parser.externalURL, alternateLink.href)
|
||||
}
|
||||
|
||||
func testContentPreferredToSummary() {
|
||||
|
||||
@@ -37,21 +37,21 @@ class FeedlyGetCollectionsOperationTests: XCTestCase {
|
||||
let ids = Set(getCollections.collections.map { $0.id })
|
||||
|
||||
let missingLabels = labelsInJSON.subtracting(labels)
|
||||
let missingIds = idsInJSON.subtracting(ids)
|
||||
let missingIDs = idsInJSON.subtracting(ids)
|
||||
|
||||
XCTAssertEqual(getCollections.collections.count, collections.count, "Mismatch between collections provided by operation and test JSON collections.")
|
||||
XCTAssertTrue(missingLabels.isEmpty, "Collections with these labels did not have a corresponding \(FeedlyCollection.self) value with the same name.")
|
||||
XCTAssertTrue(missingIds.isEmpty, "Collections with these ids did not have a corresponding \(FeedlyCollection.self) with the same id.")
|
||||
XCTAssertTrue(missingIDs.isEmpty, "Collections with these ids did not have a corresponding \(FeedlyCollection.self) with the same id.")
|
||||
|
||||
for collection in collections {
|
||||
let collectionId = collection["id"] as! String
|
||||
let collectionID = collection["id"] as! String
|
||||
let collectionFeeds = collection["feeds"] as! [[String: Any]]
|
||||
let collectionFeedIds = Set(collectionFeeds.map { $0["id"] as! String })
|
||||
let collectionFeedIDs = Set(collectionFeeds.map { $0["id"] as! String })
|
||||
|
||||
for operationCollection in getCollections.collections where operationCollection.id == collectionId {
|
||||
let feedIds = Set(operationCollection.feeds.map { $0.id })
|
||||
let missingIds = collectionFeedIds.subtracting(feedIds)
|
||||
XCTAssertTrue(missingIds.isEmpty, "Feeds with these ids were not found in the \"\(operationCollection.label)\" \(FeedlyCollection.self).")
|
||||
for operationCollection in getCollections.collections where operationCollection.id == collectionID {
|
||||
let feedIDs = Set(operationCollection.feeds.map { $0.id })
|
||||
let missingIDs = collectionFeedIDs.subtracting(feedIDs)
|
||||
XCTAssertTrue(missingIDs.isEmpty, "Feeds with these ids were not found in the \"\(operationCollection.label)\" \(FeedlyCollection.self).")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,9 +86,9 @@ class FeedlyGetStreamContentsOperationTests: XCTestCase {
|
||||
XCTAssertEqual(stream.updated, mockStream.updated)
|
||||
XCTAssertEqual(stream.continuation, mockStream.continuation)
|
||||
|
||||
let streamIds = stream.items.map { $0.id }
|
||||
let mockStreamIds = mockStream.items.map { $0.id }
|
||||
XCTAssertEqual(streamIds, mockStreamIds)
|
||||
let streamIDs = stream.items.map { $0.id }
|
||||
let mockStreamIDs = mockStream.items.map { $0.id }
|
||||
XCTAssertEqual(streamIDs, mockStreamIDs)
|
||||
}
|
||||
|
||||
func testGetStreamContentsFromJSON() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// FeedlyGetStreamIdsOperationTests.swift
|
||||
// FeedlyGetStreamIDsOperationTests.swift
|
||||
// AccountTests
|
||||
//
|
||||
// Created by Kiel Gillard on 23/10/19.
|
||||
@@ -10,7 +10,7 @@ import XCTest
|
||||
@testable import Account
|
||||
import RSCore
|
||||
|
||||
class FeedlyGetStreamIdsOperationTests: XCTestCase {
|
||||
class FeedlyGetStreamIDsOperationTests: XCTestCase {
|
||||
|
||||
private var account: Account!
|
||||
private let support = FeedlyTestSupport()
|
||||
@@ -27,39 +27,39 @@ class FeedlyGetStreamIdsOperationTests: XCTestCase {
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testGetStreamIdsFailure() {
|
||||
let service = TestGetStreamIdsService()
|
||||
func testGetStreamIDsFailure() {
|
||||
let service = TestGetStreamIDsService()
|
||||
let resource = FeedlyCategoryResourceID(id: "user/1234/category/5678")
|
||||
|
||||
let getStreamIds = FeedlyGetStreamIdsOperation(account: account, resource: resource, service: service, continuation: nil, newerThan: nil, unreadOnly: nil, log: support.log)
|
||||
let getStreamIDs = FeedlyGetStreamIDsOperation(account: account, resource: resource, service: service, continuation: nil, newerThan: nil, unreadOnly: nil, log: support.log)
|
||||
|
||||
service.mockResult = .failure(URLError(.fileDoesNotExist))
|
||||
|
||||
let completionExpectation = expectation(description: "Did Finish")
|
||||
getStreamIds.completionBlock = { _ in
|
||||
getStreamIDs.completionBlock = { _ in
|
||||
completionExpectation.fulfill()
|
||||
}
|
||||
|
||||
MainThreadOperationQueue.shared.add(getStreamIds)
|
||||
MainThreadOperationQueue.shared.add(getStreamIDs)
|
||||
|
||||
waitForExpectations(timeout: 2)
|
||||
|
||||
XCTAssertNil(getStreamIds.streamIds)
|
||||
XCTAssertNil(getStreamIDs.streamIDs)
|
||||
}
|
||||
|
||||
func testValuesPassingForGetStreamIds() {
|
||||
let service = TestGetStreamIdsService()
|
||||
func testValuesPassingForGetStreamIDs() {
|
||||
let service = TestGetStreamIDsService()
|
||||
let resource = FeedlyCategoryResourceID(id: "user/1234/category/5678")
|
||||
|
||||
let continuation: String? = "gfdsa"
|
||||
let newerThan: Date? = Date(timeIntervalSinceReferenceDate: 1000)
|
||||
let unreadOnly: Bool? = false
|
||||
|
||||
let getStreamIds = FeedlyGetStreamIdsOperation(account: account, resource: resource, service: service, continuation: continuation, newerThan: newerThan, unreadOnly: unreadOnly, log: support.log)
|
||||
let getStreamIDs = FeedlyGetStreamIDsOperation(account: account, resource: resource, service: service, continuation: continuation, newerThan: newerThan, unreadOnly: unreadOnly, log: support.log)
|
||||
|
||||
let mockStreamIds = FeedlyStreamIDs(continuation: "1234", ids: ["item/1", "item/2", "item/3"])
|
||||
service.mockResult = .success(mockStreamIds)
|
||||
service.getStreamIdsExpectation = expectation(description: "Did Call Service")
|
||||
let mockStreamIDs = FeedlyStreamIDs(continuation: "1234", ids: ["item/1", "item/2", "item/3"])
|
||||
service.mockResult = .success(mockStreamIDs)
|
||||
service.getStreamIDsExpectation = expectation(description: "Did Call Service")
|
||||
service.parameterTester = { serviceResource, serviceContinuation, serviceNewerThan, serviceUnreadOnly in
|
||||
// Verify these values given to the operation are passed to the service.
|
||||
XCTAssertEqual(serviceResource.id, resource.id)
|
||||
@@ -69,49 +69,49 @@ class FeedlyGetStreamIdsOperationTests: XCTestCase {
|
||||
}
|
||||
|
||||
let completionExpectation = expectation(description: "Did Finish")
|
||||
getStreamIds.completionBlock = { _ in
|
||||
getStreamIDs.completionBlock = { _ in
|
||||
completionExpectation.fulfill()
|
||||
}
|
||||
|
||||
MainThreadOperationQueue.shared.add(getStreamIds)
|
||||
MainThreadOperationQueue.shared.add(getStreamIDs)
|
||||
|
||||
waitForExpectations(timeout: 2)
|
||||
|
||||
guard let streamIds = getStreamIds.streamIds else {
|
||||
XCTFail("\(FeedlyGetStreamIdsOperation.self) did not store the stream.")
|
||||
guard let streamIDs = getStreamIDs.streamIDs else {
|
||||
XCTFail("\(FeedlyGetStreamIDsOperation.self) did not store the stream.")
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(streamIds.continuation, mockStreamIds.continuation)
|
||||
XCTAssertEqual(streamIds.ids, mockStreamIds.ids)
|
||||
XCTAssertEqual(streamIDs.continuation, mockStreamIDs.continuation)
|
||||
XCTAssertEqual(streamIDs.ids, mockStreamIDs.ids)
|
||||
}
|
||||
|
||||
func testGetStreamIdsFromJSON() {
|
||||
func testGetStreamIDsFromJSON() {
|
||||
let support = FeedlyTestSupport()
|
||||
let (transport, caller) = support.makeMockNetworkStack()
|
||||
let jsonName = "JSON/feedly_unreads_1000"
|
||||
transport.testFiles["/v3/streams/ids"] = "\(jsonName).json"
|
||||
|
||||
let resource = FeedlyCategoryResourceID(id: "user/1234/category/5678")
|
||||
let getStreamIds = FeedlyGetStreamIdsOperation(account: account, resource: resource, service: caller, continuation: nil, newerThan: nil, unreadOnly: nil, log: support.log)
|
||||
let getStreamIDs = FeedlyGetStreamIDsOperation(account: account, resource: resource, service: caller, continuation: nil, newerThan: nil, unreadOnly: nil, log: support.log)
|
||||
|
||||
let completionExpectation = expectation(description: "Did Finish")
|
||||
getStreamIds.completionBlock = { _ in
|
||||
getStreamIDs.completionBlock = { _ in
|
||||
completionExpectation.fulfill()
|
||||
}
|
||||
|
||||
MainThreadOperationQueue.shared.add(getStreamIds)
|
||||
MainThreadOperationQueue.shared.add(getStreamIDs)
|
||||
|
||||
waitForExpectations(timeout: 2)
|
||||
|
||||
guard let streamIds = getStreamIds.streamIds else {
|
||||
guard let streamIDs = getStreamIDs.streamIDs else {
|
||||
return XCTFail("Expected to have a stream of identifiers.")
|
||||
}
|
||||
|
||||
let streamIdsJSON = support.testJSON(named: jsonName) as! [String:Any]
|
||||
let streamIDsJSON = support.testJSON(named: jsonName) as! [String:Any]
|
||||
|
||||
let continuation = streamIdsJSON["continuation"] as! String
|
||||
XCTAssertEqual(streamIds.continuation, continuation)
|
||||
XCTAssertEqual(streamIds.ids, streamIdsJSON["ids"] as! [String])
|
||||
let continuation = streamIDsJSON["continuation"] as! String
|
||||
XCTAssertEqual(streamIDs.continuation, continuation)
|
||||
XCTAssertEqual(streamIDs.ids, streamIDsJSON["ids"] as! [String])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,16 +48,16 @@ class FeedlyMirrorCollectionsAsFoldersOperationTests: XCTestCase {
|
||||
|
||||
let folders = account.folders ?? Set()
|
||||
let folderNames = Set(folders.compactMap { $0.nameForDisplay })
|
||||
let folderExternalIds = Set(folders.compactMap { $0.externalID })
|
||||
let folderExternalIDs = Set(folders.compactMap { $0.externalID })
|
||||
|
||||
let collectionLabels = Set(provider.collections.map { $0.label })
|
||||
let collectionIds = Set(provider.collections.map { $0.id })
|
||||
let collectionIDs = Set(provider.collections.map { $0.id })
|
||||
|
||||
let missingNames = collectionLabels.subtracting(folderNames)
|
||||
let missingIds = collectionIds.subtracting(folderExternalIds)
|
||||
let missingIDs = collectionIDs.subtracting(folderExternalIDs)
|
||||
|
||||
XCTAssertTrue(missingNames.isEmpty, "Collections with these labels have no corresponding folder.")
|
||||
XCTAssertTrue(missingIds.isEmpty, "Collections with these ids have no corresponding folder.")
|
||||
XCTAssertTrue(missingIDs.isEmpty, "Collections with these ids have no corresponding folder.")
|
||||
// XCTAssertEqual(mirrorOperation.collectionsAndFolders.count, provider.collections.count, "Mismatch between collections and folders.")
|
||||
}
|
||||
|
||||
@@ -91,16 +91,16 @@ class FeedlyMirrorCollectionsAsFoldersOperationTests: XCTestCase {
|
||||
|
||||
let folders = account.folders ?? Set()
|
||||
let folderNames = Set(folders.compactMap { $0.nameForDisplay })
|
||||
let folderExternalIds = Set(folders.compactMap { $0.externalID })
|
||||
let folderExternalIDs = Set(folders.compactMap { $0.externalID })
|
||||
|
||||
let collectionLabels = Set(provider.collections.map { $0.label })
|
||||
let collectionIds = Set(provider.collections.map { $0.id })
|
||||
let collectionIDs = Set(provider.collections.map { $0.id })
|
||||
|
||||
let remainingNames = folderNames.subtracting(collectionLabels)
|
||||
let remainingIds = folderExternalIds.subtracting(collectionIds)
|
||||
let remainingIDs = folderExternalIDs.subtracting(collectionIDs)
|
||||
|
||||
XCTAssertTrue(remainingNames.isEmpty, "Folders with these names remain with no corresponding collection.")
|
||||
XCTAssertTrue(remainingIds.isEmpty, "Folders with these ids remain with no corresponding collection.")
|
||||
XCTAssertTrue(remainingIDs.isEmpty, "Folders with these ids remain with no corresponding collection.")
|
||||
|
||||
XCTAssertTrue(removeFolders.feedsAndFolders.isEmpty)
|
||||
}
|
||||
@@ -138,29 +138,29 @@ class FeedlyMirrorCollectionsAsFoldersOperationTests: XCTestCase {
|
||||
|
||||
let folders = account.folders ?? Set()
|
||||
let folderNames = Set(folders.compactMap { $0.nameForDisplay })
|
||||
let folderExternalIds = Set(folders.compactMap { $0.externalID })
|
||||
let folderExternalIDs = Set(folders.compactMap { $0.externalID })
|
||||
|
||||
let collectionLabels = Set(provider.collections.map { $0.label })
|
||||
let collectionIds = Set(provider.collections.map { $0.id })
|
||||
let collectionIDs = Set(provider.collections.map { $0.id })
|
||||
|
||||
let missingNames = collectionLabels.subtracting(folderNames)
|
||||
let missingIds = collectionIds.subtracting(folderExternalIds)
|
||||
let missingIDs = collectionIDs.subtracting(folderExternalIDs)
|
||||
|
||||
XCTAssertTrue(missingNames.isEmpty, "Collections with these labels have no corresponding folder.")
|
||||
XCTAssertTrue(missingIds.isEmpty, "Collections with these ids have no corresponding folder.")
|
||||
XCTAssertTrue(missingIDs.isEmpty, "Collections with these ids have no corresponding folder.")
|
||||
|
||||
let collectionIdsAndFeedIds = provider.collections.map { collection -> [String:[String]] in
|
||||
let collectionIDsAndFeedIDs = provider.collections.map { collection -> [String:[String]] in
|
||||
return [collection.id: collection.feeds.map { $0.id }.sorted(by: <)]
|
||||
}
|
||||
|
||||
let folderIdsAndFeedIds = mirrorOperation.feedsAndFolders.compactMap { feeds, folder -> [String:[String]]? in
|
||||
let folderIDsAndFeedIDs = mirrorOperation.feedsAndFolders.compactMap { feeds, folder -> [String:[String]]? in
|
||||
guard let id = folder.externalID else {
|
||||
return nil
|
||||
}
|
||||
return [id: feeds.map { $0.id }.sorted(by: <)]
|
||||
}
|
||||
|
||||
XCTAssertEqual(collectionIdsAndFeedIds, folderIdsAndFeedIds, "Did not map folders to feeds correctly.")
|
||||
XCTAssertEqual(collectionIDsAndFeedIDs, folderIDsAndFeedIDs, "Did not map folders to feeds correctly.")
|
||||
}
|
||||
|
||||
func testRemovingFolderRemovesFeeds() {
|
||||
|
||||
@@ -30,7 +30,7 @@ class FeedlyOrganiseParsedItemsByFeedOperationTests: XCTestCase {
|
||||
|
||||
struct TestParsedItemsProvider: FeedlyParsedItemProviding {
|
||||
let parsedItemProviderName = "TestParsedItemsProvider"
|
||||
var resource: FeedlyResourceId
|
||||
var resource: FeedlyResourceID
|
||||
var parsedEntries: Set<ParsedItem>
|
||||
}
|
||||
|
||||
@@ -51,11 +51,11 @@ class FeedlyOrganiseParsedItemsByFeedOperationTests: XCTestCase {
|
||||
|
||||
waitForExpectations(timeout: 2)
|
||||
|
||||
let itemsAndFeedIds = organise.parsedItemsKeyedByFeedId
|
||||
XCTAssertEqual(itemsAndFeedIds, entries)
|
||||
let itemsAndFeedIDs = organise.parsedItemsKeyedByFeedID
|
||||
XCTAssertEqual(itemsAndFeedIDs, entries)
|
||||
}
|
||||
|
||||
func testGroupsOneEntryByFeedId() {
|
||||
func testGroupsOneEntryByFeedID() {
|
||||
let entries = support.makeParsedItemTestDataFor(numberOfFeeds: 1, numberOfItemsInFeeds: 1)
|
||||
let resource = FeedlyCategoryResourceID(id: "user/12345/category/6789")
|
||||
let parsedEntries = Set(entries.values.flatMap { $0 })
|
||||
@@ -72,11 +72,11 @@ class FeedlyOrganiseParsedItemsByFeedOperationTests: XCTestCase {
|
||||
|
||||
waitForExpectations(timeout: 2)
|
||||
|
||||
let itemsAndFeedIds = organise.parsedItemsKeyedByFeedId
|
||||
XCTAssertEqual(itemsAndFeedIds, entries)
|
||||
let itemsAndFeedIDs = organise.parsedItemsKeyedByFeedID
|
||||
XCTAssertEqual(itemsAndFeedIDs, entries)
|
||||
}
|
||||
|
||||
func testGroupsManyEntriesByFeedId() {
|
||||
func testGroupsManyEntriesByFeedID() {
|
||||
let entries = support.makeParsedItemTestDataFor(numberOfFeeds: 100, numberOfItemsInFeeds: 100)
|
||||
let resource = FeedlyCategoryResourceID(id: "user/12345/category/6789")
|
||||
let parsedEntries = Set(entries.values.flatMap { $0 })
|
||||
@@ -93,7 +93,7 @@ class FeedlyOrganiseParsedItemsByFeedOperationTests: XCTestCase {
|
||||
|
||||
waitForExpectations(timeout: 2)
|
||||
|
||||
let itemsAndFeedIds = organise.parsedItemsKeyedByFeedId
|
||||
XCTAssertEqual(itemsAndFeedIds, entries)
|
||||
let itemsAndFeedIDs = organise.parsedItemsKeyedByFeedID
|
||||
XCTAssertEqual(itemsAndFeedIDs, entries)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// FeedlyResourceIdTests.swift
|
||||
// FeedlyResourceIDTests.swift
|
||||
// AccountTests
|
||||
//
|
||||
// Created by Kiel Gillard on 3/10/19.
|
||||
@@ -9,18 +9,18 @@
|
||||
import XCTest
|
||||
@testable import Account
|
||||
|
||||
class FeedlyResourceIdTests: XCTestCase {
|
||||
class FeedlyResourceIDTests: XCTestCase {
|
||||
|
||||
func testFeedResourceId() {
|
||||
let expectedUrl = "http://ranchero.com/blog/atom.xml"
|
||||
func testFeedResourceID() {
|
||||
let expectedURL = "http://ranchero.com/blog/atom.xml"
|
||||
|
||||
let feedResource = FeedlyFeedResourceId(id: "feed/\(expectedUrl)")
|
||||
let urlResource = FeedlyFeedResourceId(id: expectedUrl)
|
||||
let otherResource = FeedlyFeedResourceId(id: "whiskey/\(expectedUrl)")
|
||||
let invalidResource = FeedlyFeedResourceId(id: "")
|
||||
let feedResource = FeedlyFeedResourceID(id: "feed/\(expectedURL)")
|
||||
let urlResource = FeedlyFeedResourceID(id: expectedURL)
|
||||
let otherResource = FeedlyFeedResourceID(id: "whiskey/\(expectedURL)")
|
||||
let invalidResource = FeedlyFeedResourceID(id: "")
|
||||
|
||||
XCTAssertEqual(feedResource.url, expectedUrl)
|
||||
XCTAssertEqual(urlResource.url, expectedUrl)
|
||||
XCTAssertEqual(feedResource.url, expectedURL)
|
||||
XCTAssertEqual(urlResource.url, expectedURL)
|
||||
XCTAssertEqual(otherResource.url, otherResource.id)
|
||||
XCTAssertEqual(invalidResource.url, invalidResource.id)
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testSendUnreadSuccess() {
|
||||
let articleIds = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let statuses = articleIds.map { SyncStatus(articleID: $0, key: .read, flag: false) }
|
||||
let articleIDs = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let statuses = articleIDs.map { SyncStatus(articleID: $0, key: .read, flag: false) }
|
||||
|
||||
let insertExpectation = expectation(description: "Inserted Statuses")
|
||||
container.database.insertStatuses(statuses) { error in
|
||||
@@ -60,8 +60,8 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
|
||||
let service = TestMarkArticlesService()
|
||||
service.mockResult = .success(())
|
||||
service.parameterTester = { serviceArticleIds, action in
|
||||
XCTAssertEqual(serviceArticleIds, articleIds)
|
||||
service.parameterTester = { serviceArticleIDs, action in
|
||||
XCTAssertEqual(serviceArticleIDs, articleIDs)
|
||||
XCTAssertEqual(action, .unread)
|
||||
}
|
||||
|
||||
@@ -90,8 +90,8 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testSendUnreadFailure() {
|
||||
let articleIds = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let statuses = articleIds.map { SyncStatus(articleID: $0, key: .read, flag: false) }
|
||||
let articleIDs = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let statuses = articleIDs.map { SyncStatus(articleID: $0, key: .read, flag: false) }
|
||||
|
||||
let insertExpectation = expectation(description: "Inserted Statuses")
|
||||
container.database.insertStatuses(statuses) { error in
|
||||
@@ -103,8 +103,8 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
|
||||
let service = TestMarkArticlesService()
|
||||
service.mockResult = .failure(URLError(.timedOut))
|
||||
service.parameterTester = { serviceArticleIds, action in
|
||||
XCTAssertEqual(serviceArticleIds, articleIds)
|
||||
service.parameterTester = { serviceArticleIDs, action in
|
||||
XCTAssertEqual(serviceArticleIDs, articleIDs)
|
||||
XCTAssertEqual(action, .unread)
|
||||
}
|
||||
|
||||
@@ -133,8 +133,8 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testSendReadSuccess() {
|
||||
let articleIds = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let statuses = articleIds.map { SyncStatus(articleID: $0, key: .read, flag: true) }
|
||||
let articleIDs = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let statuses = articleIDs.map { SyncStatus(articleID: $0, key: .read, flag: true) }
|
||||
|
||||
let insertExpectation = expectation(description: "Inserted Statuses")
|
||||
container.database.insertStatuses(statuses) { error in
|
||||
@@ -146,8 +146,8 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
|
||||
let service = TestMarkArticlesService()
|
||||
service.mockResult = .success(())
|
||||
service.parameterTester = { serviceArticleIds, action in
|
||||
XCTAssertEqual(serviceArticleIds, articleIds)
|
||||
service.parameterTester = { serviceArticleIDs, action in
|
||||
XCTAssertEqual(serviceArticleIDs, articleIDs)
|
||||
XCTAssertEqual(action, .read)
|
||||
}
|
||||
|
||||
@@ -176,8 +176,8 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testSendReadFailure() {
|
||||
let articleIds = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let statuses = articleIds.map { SyncStatus(articleID: $0, key: .read, flag: true) }
|
||||
let articleIDs = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let statuses = articleIDs.map { SyncStatus(articleID: $0, key: .read, flag: true) }
|
||||
|
||||
let insertExpectation = expectation(description: "Inserted Statuses")
|
||||
container.database.insertStatuses(statuses) { error in
|
||||
@@ -189,8 +189,8 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
|
||||
let service = TestMarkArticlesService()
|
||||
service.mockResult = .failure(URLError(.timedOut))
|
||||
service.parameterTester = { serviceArticleIds, action in
|
||||
XCTAssertEqual(serviceArticleIds, articleIds)
|
||||
service.parameterTester = { serviceArticleIDs, action in
|
||||
XCTAssertEqual(serviceArticleIDs, articleIDs)
|
||||
XCTAssertEqual(action, .read)
|
||||
}
|
||||
|
||||
@@ -219,8 +219,8 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testSendStarredSuccess() {
|
||||
let articleIds = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let statuses = articleIds.map { SyncStatus(articleID: $0, key: .starred, flag: true) }
|
||||
let articleIDs = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let statuses = articleIDs.map { SyncStatus(articleID: $0, key: .starred, flag: true) }
|
||||
|
||||
let insertExpectation = expectation(description: "Inserted Statuses")
|
||||
container.database.insertStatuses(statuses) { error in
|
||||
@@ -232,8 +232,8 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
|
||||
let service = TestMarkArticlesService()
|
||||
service.mockResult = .success(())
|
||||
service.parameterTester = { serviceArticleIds, action in
|
||||
XCTAssertEqual(serviceArticleIds, articleIds)
|
||||
service.parameterTester = { serviceArticleIDs, action in
|
||||
XCTAssertEqual(serviceArticleIDs, articleIDs)
|
||||
XCTAssertEqual(action, .saved)
|
||||
}
|
||||
|
||||
@@ -262,8 +262,8 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testSendStarredFailure() {
|
||||
let articleIds = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let statuses = articleIds.map { SyncStatus(articleID: $0, key: .starred, flag: true) }
|
||||
let articleIDs = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let statuses = articleIDs.map { SyncStatus(articleID: $0, key: .starred, flag: true) }
|
||||
|
||||
let insertExpectation = expectation(description: "Inserted Statuses")
|
||||
container.database.insertStatuses(statuses) { error in
|
||||
@@ -275,8 +275,8 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
|
||||
let service = TestMarkArticlesService()
|
||||
service.mockResult = .failure(URLError(.timedOut))
|
||||
service.parameterTester = { serviceArticleIds, action in
|
||||
XCTAssertEqual(serviceArticleIds, articleIds)
|
||||
service.parameterTester = { serviceArticleIDs, action in
|
||||
XCTAssertEqual(serviceArticleIDs, articleIDs)
|
||||
XCTAssertEqual(action, .saved)
|
||||
}
|
||||
|
||||
@@ -305,8 +305,8 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testSendUnstarredSuccess() {
|
||||
let articleIds = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let statuses = articleIds.map { SyncStatus(articleID: $0, key: .starred, flag: false) }
|
||||
let articleIDs = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let statuses = articleIDs.map { SyncStatus(articleID: $0, key: .starred, flag: false) }
|
||||
|
||||
let insertExpectation = expectation(description: "Inserted Statuses")
|
||||
container.database.insertStatuses(statuses) { error in
|
||||
@@ -318,8 +318,8 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
|
||||
let service = TestMarkArticlesService()
|
||||
service.mockResult = .success(())
|
||||
service.parameterTester = { serviceArticleIds, action in
|
||||
XCTAssertEqual(serviceArticleIds, articleIds)
|
||||
service.parameterTester = { serviceArticleIDs, action in
|
||||
XCTAssertEqual(serviceArticleIDs, articleIDs)
|
||||
XCTAssertEqual(action, .unsaved)
|
||||
}
|
||||
|
||||
@@ -348,8 +348,8 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testSendUnstarredFailure() {
|
||||
let articleIds = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let statuses = articleIds.map { SyncStatus(articleID: $0, key: .starred, flag: false) }
|
||||
let articleIDs = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let statuses = articleIDs.map { SyncStatus(articleID: $0, key: .starred, flag: false) }
|
||||
|
||||
let insertExpectation = expectation(description: "Inserted Statuses")
|
||||
container.database.insertStatuses(statuses) { error in
|
||||
@@ -361,8 +361,8 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
|
||||
let service = TestMarkArticlesService()
|
||||
service.mockResult = .failure(URLError(.timedOut))
|
||||
service.parameterTester = { serviceArticleIds, action in
|
||||
XCTAssertEqual(serviceArticleIds, articleIds)
|
||||
service.parameterTester = { serviceArticleIDs, action in
|
||||
XCTAssertEqual(serviceArticleIDs, articleIDs)
|
||||
XCTAssertEqual(action, .unsaved)
|
||||
}
|
||||
|
||||
@@ -391,13 +391,13 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testSendAllSuccess() {
|
||||
let articleIds = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let articleIDs = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let keys = [SyncStatus.Key.read, .starred]
|
||||
let flags = [true, false]
|
||||
let statuses = articleIds.map { articleId -> SyncStatus in
|
||||
let statuses = articleIDs.map { articleID -> SyncStatus in
|
||||
let key = keys.randomElement()!
|
||||
let flag = flags.randomElement()!
|
||||
let status = SyncStatus(articleID: articleId, key: key, flag: flag)
|
||||
let status = SyncStatus(articleID: articleID, key: key, flag: flag)
|
||||
return status
|
||||
}
|
||||
|
||||
@@ -411,7 +411,7 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
|
||||
let service = TestMarkArticlesService()
|
||||
service.mockResult = .success(())
|
||||
service.parameterTester = { serviceArticleIds, action in
|
||||
service.parameterTester = { serviceArticleIDs, action in
|
||||
let syncStatuses: [SyncStatus]
|
||||
switch action {
|
||||
case .read:
|
||||
@@ -423,8 +423,8 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
case .unsaved:
|
||||
syncStatuses = statuses.filter { $0.key == .starred && $0.flag == false }
|
||||
}
|
||||
let expectedArticleIds = Set(syncStatuses.map { $0.articleID })
|
||||
XCTAssertEqual(serviceArticleIds, expectedArticleIds)
|
||||
let expectedArticleIDs = Set(syncStatuses.map { $0.articleID })
|
||||
XCTAssertEqual(serviceArticleIDs, expectedArticleIDs)
|
||||
}
|
||||
let send = FeedlySendArticleStatusesOperation(database: container.database, service: service, log: support.log)
|
||||
|
||||
@@ -451,13 +451,13 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testSendAllFailure() {
|
||||
let articleIds = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let articleIDs = Set((0..<100).map { "feed/0/article/\($0)" })
|
||||
let keys = [SyncStatus.Key.read, .starred]
|
||||
let flags = [true, false]
|
||||
let statuses = articleIds.map { articleId -> SyncStatus in
|
||||
let statuses = articleIDs.map { articleID -> SyncStatus in
|
||||
let key = keys.randomElement()!
|
||||
let flag = flags.randomElement()!
|
||||
let status = SyncStatus(articleID: articleId, key: key, flag: flag)
|
||||
let status = SyncStatus(articleID: articleID, key: key, flag: flag)
|
||||
return status
|
||||
}
|
||||
|
||||
@@ -471,7 +471,7 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
|
||||
let service = TestMarkArticlesService()
|
||||
service.mockResult = .failure(URLError(.timedOut))
|
||||
service.parameterTester = { serviceArticleIds, action in
|
||||
service.parameterTester = { serviceArticleIDs, action in
|
||||
let syncStatuses: [SyncStatus]
|
||||
switch action {
|
||||
case .read:
|
||||
@@ -483,8 +483,8 @@ class FeedlySendArticleStatusesOperationTests: XCTestCase {
|
||||
case .unsaved:
|
||||
syncStatuses = statuses.filter { $0.key == .starred && $0.flag == false }
|
||||
}
|
||||
let expectedArticleIds = Set(syncStatuses.map { $0.articleID })
|
||||
XCTAssertEqual(serviceArticleIds, expectedArticleIds)
|
||||
let expectedArticleIDs = Set(syncStatuses.map { $0.articleID })
|
||||
XCTAssertEqual(serviceArticleIDs, expectedArticleIDs)
|
||||
}
|
||||
|
||||
let send = FeedlySendArticleStatusesOperation(database: container.database, service: service, log: support.log)
|
||||
|
||||
@@ -16,7 +16,7 @@ class FeedlyMockResponseProvider: TestTransportMockResponseProviding {
|
||||
self.subdirectory = subdirectory
|
||||
}
|
||||
|
||||
func mockResponseFileUrl(for components: URLComponents) -> URL? {
|
||||
func mockResponseFileURL(for components: URLComponents) -> URL? {
|
||||
let bundle = Bundle(for: FeedlyMockResponseProvider.self)
|
||||
|
||||
// Match request for collections to build a list of folders.
|
||||
|
||||
@@ -56,9 +56,9 @@ class FeedlySyncStreamContentsOperationTests: XCTestCase {
|
||||
|
||||
waitForExpectations(timeout: 2)
|
||||
|
||||
let expectedArticleIds = Set(items.map { $0.id })
|
||||
let expectedArticles = try account.fetchArticles(.articleIDs(expectedArticleIds))
|
||||
XCTAssertEqual(expectedArticles.count, expectedArticleIds.count, "Did not fetch all the articles.")
|
||||
let expectedArticleIDs = Set(items.map { $0.id })
|
||||
let expectedArticles = try account.fetchArticles(.articleIDs(expectedArticleIDs))
|
||||
XCTAssertEqual(expectedArticles.count, expectedArticleIDs.count, "Did not fetch all the articles.")
|
||||
}
|
||||
|
||||
func testIngestsOnePageFailure() {
|
||||
@@ -132,8 +132,8 @@ class FeedlySyncStreamContentsOperationTests: XCTestCase {
|
||||
waitForExpectations(timeout: 30)
|
||||
|
||||
// Find articles inserted.
|
||||
let articleIds = Set(service.pages.values.map { $0.items }.flatMap { $0 }.map { $0.id })
|
||||
let articles = try account.fetchArticles(.articleIDs(articleIds))
|
||||
XCTAssertEqual(articleIds.count, articles.count)
|
||||
let articleIDs = Set(service.pages.values.map { $0.items }.flatMap { $0 }.map { $0.id })
|
||||
let articles = try account.fetchArticles(.articleIDs(articleIDs))
|
||||
XCTAssertEqual(articleIDs.count, articles.count)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ class FeedlyTestSupport {
|
||||
}
|
||||
|
||||
func makeMockOAuthClient() -> OAuthAuthorizationClient {
|
||||
return OAuthAuthorizationClient(id: "test", redirectUri: "test://test/auth", state: nil, secret: "password")
|
||||
return OAuthAuthorizationClient(id: "test", redirectURI: "test://test/auth", state: nil, secret: "password")
|
||||
}
|
||||
|
||||
func removeCredentials(matching type: CredentialsType, from account: Account) {
|
||||
@@ -103,18 +103,18 @@ class FeedlyTestSupport {
|
||||
func checkFoldersAndFeeds(in account: Account, againstCollectionsAndFeedsInJSONNamed name: String, subdirectory: String? = nil) {
|
||||
let collections = testJSON(named: name, subdirectory: subdirectory) as! [[String:Any]]
|
||||
let collectionNames = Set(collections.map { $0["label"] as! String })
|
||||
let collectionIds = Set(collections.map { $0["id"] as! String })
|
||||
let collectionIDs = Set(collections.map { $0["id"] as! String })
|
||||
|
||||
let folders = account.folders ?? Set()
|
||||
let folderNames = Set(folders.compactMap { $0.name })
|
||||
let folderIds = Set(folders.compactMap { $0.externalID })
|
||||
let folderIDs = Set(folders.compactMap { $0.externalID })
|
||||
|
||||
let missingNames = collectionNames.subtracting(folderNames)
|
||||
let missingIds = collectionIds.subtracting(folderIds)
|
||||
let missingIDs = collectionIDs.subtracting(folderIDs)
|
||||
|
||||
XCTAssertEqual(folders.count, collections.count, "Mismatch between collections and folders.")
|
||||
XCTAssertTrue(missingNames.isEmpty, "Collections with these names did not have a corresponding folder with the same name.")
|
||||
XCTAssertTrue(missingIds.isEmpty, "Collections with these ids did not have a corresponding folder with the same id.")
|
||||
XCTAssertTrue(missingIDs.isEmpty, "Collections with these ids did not have a corresponding folder with the same id.")
|
||||
|
||||
for collection in collections {
|
||||
checkSingleFolderAndFeeds(in: account, againstOneCollectionAndFeedsInJSONPayload: collection)
|
||||
@@ -138,11 +138,11 @@ class FeedlyTestSupport {
|
||||
|
||||
XCTAssertEqual(collectionFeeds.count, folderFeeds.count)
|
||||
|
||||
let collectionFeedIds = Set(collectionFeeds.map { $0["id"] as! String })
|
||||
let folderFeedIds = Set(folderFeeds.map { $0.feedID })
|
||||
let missingFeedIds = collectionFeedIds.subtracting(folderFeedIds)
|
||||
let collectionFeedIDs = Set(collectionFeeds.map { $0["id"] as! String })
|
||||
let folderFeedIDs = Set(folderFeeds.map { $0.feedID })
|
||||
let missingFeedIDs = collectionFeedIDs.subtracting(folderFeedIDs)
|
||||
|
||||
XCTAssertTrue(missingFeedIds.isEmpty, "Feeds with these ids were not found in the \"\(label)\" folder.")
|
||||
XCTAssertTrue(missingFeedIDs.isEmpty, "Feeds with these ids were not found in the \"\(label)\" folder.")
|
||||
}
|
||||
|
||||
func checkArticles(in account: Account, againstItemsInStreamInJSONNamed name: String, subdirectory: String? = nil) throws {
|
||||
@@ -156,14 +156,14 @@ class FeedlyTestSupport {
|
||||
|
||||
private struct ArticleItem {
|
||||
var id: String
|
||||
var feedId: String
|
||||
var feedID: String
|
||||
var content: String
|
||||
var JSON: [String: Any]
|
||||
var unread: Bool
|
||||
|
||||
/// Convoluted external URL logic "documented" here:
|
||||
/// https://groups.google.com/forum/#!searchin/feedly-cloud/feed$20url%7Csort:date/feedly-cloud/Rx3dVd4aTFQ/Hf1ZfLJoCQAJ
|
||||
var externalUrl: String? {
|
||||
var externalURL: String? {
|
||||
return ((JSON["canonical"] as? [[String: Any]]) ?? (JSON["alternate"] as? [[String: Any]]))?.compactMap { link -> String? in
|
||||
let href = link["href"] as? String
|
||||
if let type = link["type"] as? String {
|
||||
@@ -181,7 +181,7 @@ class FeedlyTestSupport {
|
||||
self.id = item["id"] as! String
|
||||
|
||||
let origin = item["origin"] as! [String: Any]
|
||||
self.feedId = origin["streamId"] as! String
|
||||
self.feedID = origin["streamId"] as! String
|
||||
|
||||
let content = item["content"] as? [String: Any]
|
||||
let summary = item["summary"] as? [String: Any]
|
||||
@@ -196,12 +196,12 @@ class FeedlyTestSupport {
|
||||
|
||||
let items = stream["items"] as! [[String: Any]]
|
||||
let articleItems = items.map { ArticleItem(item: $0) }
|
||||
let itemIds = Set(articleItems.map { $0.id })
|
||||
let itemIDs = Set(articleItems.map { $0.id })
|
||||
|
||||
let articles = try testAccount.fetchArticles(.articleIDs(itemIds))
|
||||
let articleIds = Set(articles.map { $0.articleID })
|
||||
let articles = try testAccount.fetchArticles(.articleIDs(itemIDs))
|
||||
let articleIDs = Set(articles.map { $0.articleID })
|
||||
|
||||
let missing = itemIds.subtracting(articleIds)
|
||||
let missing = itemIDs.subtracting(articleIDs)
|
||||
|
||||
XCTAssertEqual(items.count, articles.count)
|
||||
XCTAssertTrue(missing.isEmpty, "Items with these ids did not have a corresponding article with the same id.")
|
||||
@@ -210,67 +210,67 @@ class FeedlyTestSupport {
|
||||
for item in articleItems where item.id == article.articleID {
|
||||
XCTAssertEqual(article.uniqueID, item.id)
|
||||
XCTAssertEqual(article.contentHTML, item.content)
|
||||
XCTAssertEqual(article.feedID, item.feedId)
|
||||
XCTAssertEqual(article.rawExternalLink, item.externalUrl)
|
||||
XCTAssertEqual(article.feedID, item.feedID)
|
||||
XCTAssertEqual(article.rawExternalLink, item.externalURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkUnreadStatuses(in account: Account, againstIdsInStreamInJSONNamed name: String, subdirectory: String? = nil, testCase: XCTestCase) {
|
||||
let streamIds = testJSON(named: name, subdirectory: subdirectory) as! [String:Any]
|
||||
checkUnreadStatuses(in: account, correspondToIdsInJSONPayload: streamIds, testCase: testCase)
|
||||
func checkUnreadStatuses(in account: Account, againstIDsInStreamInJSONNamed name: String, subdirectory: String? = nil, testCase: XCTestCase) {
|
||||
let streamIDs = testJSON(named: name, subdirectory: subdirectory) as! [String:Any]
|
||||
checkUnreadStatuses(in: account, correspondToIDsInJSONPayload: streamIDs, testCase: testCase)
|
||||
}
|
||||
|
||||
func checkUnreadStatuses(in testAccount: Account, correspondToIdsInJSONPayload streamIds: [String: Any], testCase: XCTestCase) {
|
||||
let ids = Set(streamIds["ids"] as! [String])
|
||||
let fetchIdsExpectation = testCase.expectation(description: "Fetch Article Ids")
|
||||
testAccount.fetchUnreadArticleIDs { articleIdsResult in
|
||||
func checkUnreadStatuses(in testAccount: Account, correspondToIDsInJSONPayload streamIDs: [String: Any], testCase: XCTestCase) {
|
||||
let ids = Set(streamIDs["ids"] as! [String])
|
||||
let fetchIDsExpectation = testCase.expectation(description: "Fetch Article IDs")
|
||||
testAccount.fetchUnreadArticleIDs { articleIDsResult in
|
||||
do {
|
||||
let articleIds = try articleIdsResult.get()
|
||||
let articleIDs = try articleIDsResult.get()
|
||||
// Unread statuses can be paged from Feedly.
|
||||
// Instead of joining test data, the best we can do is
|
||||
// make sure that these ids are marked as unread (a subset of the total).
|
||||
XCTAssertTrue(ids.isSubset(of: articleIds), "Some articles in `ids` are not marked as unread.")
|
||||
fetchIdsExpectation.fulfill()
|
||||
XCTAssertTrue(ids.isSubset(of: articleIDs), "Some articles in `ids` are not marked as unread.")
|
||||
fetchIDsExpectation.fulfill()
|
||||
} catch {
|
||||
XCTFail("Error unwrapping article IDs: \(error)")
|
||||
}
|
||||
}
|
||||
testCase.wait(for: [fetchIdsExpectation], timeout: 2)
|
||||
testCase.wait(for: [fetchIDsExpectation], timeout: 2)
|
||||
}
|
||||
|
||||
func checkStarredStatuses(in account: Account, againstItemsInStreamInJSONNamed name: String, subdirectory: String? = nil, testCase: XCTestCase) {
|
||||
let streamIds = testJSON(named: name, subdirectory: subdirectory) as! [String:Any]
|
||||
checkStarredStatuses(in: account, correspondToStreamItemsIn: streamIds, testCase: testCase)
|
||||
let streamIDs = testJSON(named: name, subdirectory: subdirectory) as! [String:Any]
|
||||
checkStarredStatuses(in: account, correspondToStreamItemsIn: streamIDs, testCase: testCase)
|
||||
}
|
||||
|
||||
func checkStarredStatuses(in testAccount: Account, correspondToStreamItemsIn stream: [String: Any], testCase: XCTestCase) {
|
||||
let items = stream["items"] as! [[String: Any]]
|
||||
let ids = Set(items.map { $0["id"] as! String })
|
||||
let fetchIdsExpectation = testCase.expectation(description: "Fetch Article Ids")
|
||||
testAccount.fetchStarredArticleIDs { articleIdsResult in
|
||||
let fetchIDsExpectation = testCase.expectation(description: "Fetch Article IDs")
|
||||
testAccount.fetchStarredArticleIDs { articleIDsResult in
|
||||
do {
|
||||
let articleIds = try articleIdsResult.get()
|
||||
let articleIDs = try articleIDsResult.get()
|
||||
// Starred articles can be paged from Feedly.
|
||||
// Instead of joining test data, the best we can do is
|
||||
// make sure that these articles are marked as starred (a subset of the total).
|
||||
XCTAssertTrue(ids.isSubset(of: articleIds), "Some articles in `ids` are not marked as starred.")
|
||||
fetchIdsExpectation.fulfill()
|
||||
XCTAssertTrue(ids.isSubset(of: articleIDs), "Some articles in `ids` are not marked as starred.")
|
||||
fetchIDsExpectation.fulfill()
|
||||
} catch {
|
||||
XCTFail("Error unwrapping article IDs: \(error)")
|
||||
}
|
||||
}
|
||||
testCase.wait(for: [fetchIdsExpectation], timeout: 2)
|
||||
testCase.wait(for: [fetchIDsExpectation], timeout: 2)
|
||||
}
|
||||
|
||||
func check(_ entries: [FeedlyEntry], correspondToStreamItemsIn stream: [String: Any]) {
|
||||
|
||||
let items = stream["items"] as! [[String: Any]]
|
||||
let itemIds = Set(items.map { $0["id"] as! String })
|
||||
let itemIDs = Set(items.map { $0["id"] as! String })
|
||||
|
||||
let articleIds = Set(entries.map { $0.id })
|
||||
let articleIDs = Set(entries.map { $0.id })
|
||||
|
||||
let missing = itemIds.subtracting(articleIds)
|
||||
let missing = itemIDs.subtracting(articleIDs)
|
||||
|
||||
XCTAssertEqual(items.count, entries.count)
|
||||
XCTAssertTrue(missing.isEmpty, "Failed to create \(FeedlyEntry.self) values from objects in the JSON with these ids.")
|
||||
@@ -278,10 +278,10 @@ class FeedlyTestSupport {
|
||||
|
||||
func makeParsedItemTestDataFor(numberOfFeeds: Int, numberOfItemsInFeeds: Int) -> [String: Set<ParsedItem>] {
|
||||
let ids = (0..<numberOfFeeds).map { "feed/\($0)" }
|
||||
let feedIdsAndItemCounts = ids.map { ($0, numberOfItemsInFeeds) }
|
||||
let feedIDsAndItemCounts = ids.map { ($0, numberOfItemsInFeeds) }
|
||||
|
||||
let entries = feedIdsAndItemCounts.map { (feedId, count) -> (String, [Int]) in
|
||||
return (feedId, (0..<count).map { $0 })
|
||||
let entries = feedIDsAndItemCounts.map { (feedID, count) -> (String, [Int]) in
|
||||
return (feedID, (0..<count).map { $0 })
|
||||
|
||||
}.map { pair -> (String, Set<ParsedItem>) in
|
||||
let items = pair.1.map { index -> ParsedItem in
|
||||
@@ -304,7 +304,7 @@ class FeedlyTestSupport {
|
||||
attachments: nil)
|
||||
}
|
||||
return (pair.0, Set(items))
|
||||
}.reduce([String: Set<ParsedItem>](minimumCapacity: feedIdsAndItemCounts.count)) { (dict, pair) in
|
||||
}.reduce([String: Set<ParsedItem>](minimumCapacity: feedIDsAndItemCounts.count)) { (dict, pair) in
|
||||
var mutant = dict
|
||||
mutant[pair.0] = pair.1
|
||||
return mutant
|
||||
|
||||
@@ -11,11 +11,11 @@ import XCTest
|
||||
|
||||
final class TestGetPagedStreamContentsService: FeedlyGetStreamContentsService {
|
||||
|
||||
var parameterTester: ((FeedlyResourceId, String?, Date?, Bool?) -> ())?
|
||||
var parameterTester: ((FeedlyResourceID, String?, Date?, Bool?) -> ())?
|
||||
var getStreamContentsExpectation: XCTestExpectation?
|
||||
var pages = [String: FeedlyStream]()
|
||||
|
||||
func addAtLeastOnePage(for resource: FeedlyResourceId, continuations: [String], numberOfEntriesPerPage count: Int) {
|
||||
func addAtLeastOnePage(for resource: FeedlyResourceID, continuations: [String], numberOfEntriesPerPage count: Int) {
|
||||
pages = [String: FeedlyStream](minimumCapacity: continuations.count + 1)
|
||||
|
||||
// A continuation is an identifier for the next page.
|
||||
@@ -31,14 +31,14 @@ final class TestGetPagedStreamContentsService: FeedlyGetStreamContentsService {
|
||||
}
|
||||
}
|
||||
|
||||
private func makeStreamContents(for resource: FeedlyResourceId, continuation: String?, between range: Range<Int>) -> FeedlyStream {
|
||||
private func makeStreamContents(for resource: FeedlyResourceID, continuation: String?, between range: Range<Int>) -> FeedlyStream {
|
||||
let entries = range.map { index -> FeedlyEntry in
|
||||
let content = FeedlyEntry.Content(content: "Content \(index)",
|
||||
direction: .leftToRight)
|
||||
|
||||
let origin = FeedlyOrigin(title: "Origin \(index)",
|
||||
streamId: resource.id,
|
||||
htmlUrl: "http://localhost/feedly/origin/\(index)")
|
||||
streamID: resource.id,
|
||||
htmlURL: "http://localhost/feedly/origin/\(index)")
|
||||
|
||||
return FeedlyEntry(id: "/articles/\(index)",
|
||||
title: "Article \(index)",
|
||||
@@ -61,11 +61,11 @@ final class TestGetPagedStreamContentsService: FeedlyGetStreamContentsService {
|
||||
return stream
|
||||
}
|
||||
|
||||
static func getPagingKey(for stream: FeedlyResourceId, continuation: String?) -> String {
|
||||
static func getPagingKey(for stream: FeedlyResourceID, continuation: String?) -> String {
|
||||
return "\(stream.id)@\(continuation ?? "")"
|
||||
}
|
||||
|
||||
func getStreamContents(for resource: FeedlyResourceId, continuation: String?, newerThan: Date?, unreadOnly: Bool?, completion: @escaping (Result<FeedlyStream, Error>) -> ()) {
|
||||
func getStreamContents(for resource: FeedlyResourceID, continuation: String?, newerThan: Date?, unreadOnly: Bool?, completion: @escaping (Result<FeedlyStream, Error>) -> ()) {
|
||||
let key = TestGetPagedStreamContentsService.getPagingKey(for: resource, continuation: continuation)
|
||||
guard let page = pages[key] else {
|
||||
XCTFail("Missing page for \(resource.id) and continuation \(String(describing: continuation)). Test may time out because the completion will not be called.")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// TestGetPagedStreamIdsService.swift
|
||||
// TestGetPagedStreamIDsService.swift
|
||||
// AccountTests
|
||||
//
|
||||
// Created by Kiel Gillard on 29/10/19.
|
||||
@@ -9,13 +9,13 @@
|
||||
import XCTest
|
||||
@testable import Account
|
||||
|
||||
final class TestGetPagedStreamIdsService: FeedlyGetStreamIDsService {
|
||||
final class TestGetPagedStreamIDsService: FeedlyGetStreamIDsService {
|
||||
|
||||
var parameterTester: ((FeedlyResourceId, String?, Date?, Bool?) -> ())?
|
||||
var getStreamIdsExpectation: XCTestExpectation?
|
||||
var parameterTester: ((FeedlyResourceID, String?, Date?, Bool?) -> ())?
|
||||
var getStreamIDsExpectation: XCTestExpectation?
|
||||
var pages = [String: FeedlyStreamIDs]()
|
||||
|
||||
func addAtLeastOnePage(for resource: FeedlyResourceId, continuations: [String], numberOfEntriesPerPage count: Int) {
|
||||
func addAtLeastOnePage(for resource: FeedlyResourceID, continuations: [String], numberOfEntriesPerPage count: Int) {
|
||||
pages = [String: FeedlyStreamIDs](minimumCapacity: continuations.count + 1)
|
||||
|
||||
// A continuation is an identifier for the next page.
|
||||
@@ -25,24 +25,24 @@ final class TestGetPagedStreamIdsService: FeedlyGetStreamIDsService {
|
||||
for index in -1..<continuations.count {
|
||||
let nextIndex = index + 1
|
||||
let continuation: String? = nextIndex < continuations.count ? continuations[nextIndex] : nil
|
||||
let page = makeStreamIds(for: resource, continuation: continuation, between: 0..<count)
|
||||
let key = TestGetPagedStreamIdsService.getPagingKey(for: resource, continuation: index < 0 ? nil : continuations[index])
|
||||
let page = makeStreamIDs(for: resource, continuation: continuation, between: 0..<count)
|
||||
let key = TestGetPagedStreamIDsService.getPagingKey(for: resource, continuation: index < 0 ? nil : continuations[index])
|
||||
pages[key] = page
|
||||
}
|
||||
}
|
||||
|
||||
private func makeStreamIds(for resource: FeedlyResourceId, continuation: String?, between range: Range<Int>) -> FeedlyStreamIDs {
|
||||
let entryIds = range.map { _ in UUID().uuidString }
|
||||
let stream = FeedlyStreamIDs(continuation: continuation, ids: entryIds)
|
||||
private func makeStreamIDs(for resource: FeedlyResourceID, continuation: String?, between range: Range<Int>) -> FeedlyStreamIDs {
|
||||
let entryIDs = range.map { _ in UUID().uuidString }
|
||||
let stream = FeedlyStreamIDs(continuation: continuation, ids: entryIDs)
|
||||
return stream
|
||||
}
|
||||
|
||||
static func getPagingKey(for stream: FeedlyResourceId, continuation: String?) -> String {
|
||||
static func getPagingKey(for stream: FeedlyResourceID, continuation: String?) -> String {
|
||||
return "\(stream.id)@\(continuation ?? "")"
|
||||
}
|
||||
|
||||
func getStreamIds(for resource: FeedlyResourceId, continuation: String?, newerThan: Date?, unreadOnly: Bool?, completion: @escaping (Result<FeedlyStreamIDs, Error>) -> ()) {
|
||||
let key = TestGetPagedStreamIdsService.getPagingKey(for: resource, continuation: continuation)
|
||||
func getStreamIDs(for resource: FeedlyResourceID, continuation: String?, newerThan: Date?, unreadOnly: Bool?, completion: @escaping (Result<FeedlyStreamIDs, Error>) -> ()) {
|
||||
let key = TestGetPagedStreamIDsService.getPagingKey(for: resource, continuation: continuation)
|
||||
guard let page = pages[key] else {
|
||||
XCTFail("Missing page for \(resource.id) and continuation \(String(describing: continuation)). Test may time out because the completion will not be called.")
|
||||
return
|
||||
@@ -50,7 +50,7 @@ final class TestGetPagedStreamIdsService: FeedlyGetStreamIDsService {
|
||||
parameterTester?(resource, continuation, newerThan, unreadOnly)
|
||||
DispatchQueue.main.async {
|
||||
completion(.success(page))
|
||||
self.getStreamIdsExpectation?.fulfill()
|
||||
self.getStreamIDsExpectation?.fulfill()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ import XCTest
|
||||
final class TestGetStreamContentsService: FeedlyGetStreamContentsService {
|
||||
|
||||
var mockResult: Result<FeedlyStream, Error>?
|
||||
var parameterTester: ((FeedlyResourceId, String?, Date?, Bool?) -> ())?
|
||||
var parameterTester: ((FeedlyResourceID, String?, Date?, Bool?) -> ())?
|
||||
var getStreamContentsExpectation: XCTestExpectation?
|
||||
|
||||
func getStreamContents(for resource: FeedlyResourceId, continuation: String?, newerThan: Date?, unreadOnly: Bool?, completion: @escaping (Result<FeedlyStream, Error>) -> ()) {
|
||||
func getStreamContents(for resource: FeedlyResourceID, continuation: String?, newerThan: Date?, unreadOnly: Bool?, completion: @escaping (Result<FeedlyStream, Error>) -> ()) {
|
||||
guard let result = mockResult else {
|
||||
XCTFail("Missing mock result. Test may time out because the completion will not be called.")
|
||||
return
|
||||
@@ -28,7 +28,7 @@ final class TestGetStreamContentsService: FeedlyGetStreamContentsService {
|
||||
}
|
||||
|
||||
func makeMockFeedlyEntryItem() -> [FeedlyEntry] {
|
||||
let origin = FeedlyOrigin(title: "XCTest@localhost", streamId: "user/12345/category/67890", htmlUrl: "http://localhost/nnw/xctest")
|
||||
let origin = FeedlyOrigin(title: "XCTest@localhost", streamID: "user/12345/category/67890", htmlURL: "http://localhost/nnw/xctest")
|
||||
let content = FeedlyEntry.Content(content: "In the beginning...", direction: .leftToRight)
|
||||
let items = [FeedlyEntry(id: "feeds/0/article/0",
|
||||
title: "RSS Reader Ingests Man",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// TestGetStreamIdsService.swift
|
||||
// TestGetStreamIDsService.swift
|
||||
// AccountTests
|
||||
//
|
||||
// Created by Kiel Gillard on 29/10/19.
|
||||
@@ -9,13 +9,13 @@
|
||||
import XCTest
|
||||
@testable import Account
|
||||
|
||||
final class TestGetStreamIdsService: FeedlyGetStreamIDsService {
|
||||
final class TestGetStreamIDsService: FeedlyGetStreamIDsService {
|
||||
|
||||
var mockResult: Result<FeedlyStreamIDs, Error>?
|
||||
var parameterTester: ((FeedlyResourceId, String?, Date?, Bool?) -> ())?
|
||||
var getStreamIdsExpectation: XCTestExpectation?
|
||||
var parameterTester: ((FeedlyResourceID, String?, Date?, Bool?) -> ())?
|
||||
var getStreamIDsExpectation: XCTestExpectation?
|
||||
|
||||
func getStreamIds(for resource: FeedlyResourceId, continuation: String?, newerThan: Date?, unreadOnly: Bool?, completion: @escaping (Result<FeedlyStreamIDs, Error>) -> ()) {
|
||||
func getStreamIDs(for resource: FeedlyResourceID, continuation: String?, newerThan: Date?, unreadOnly: Bool?, completion: @escaping (Result<FeedlyStreamIDs, Error>) -> ()) {
|
||||
guard let result = mockResult else {
|
||||
XCTFail("Missing mock result. Test may time out because the completion will not be called.")
|
||||
return
|
||||
@@ -23,7 +23,7 @@ final class TestGetStreamIdsService: FeedlyGetStreamIDsService {
|
||||
parameterTester?(resource, continuation, newerThan, unreadOnly)
|
||||
DispatchQueue.main.async {
|
||||
completion(result)
|
||||
self.getStreamIdsExpectation?.fulfill()
|
||||
self.getStreamIDsExpectation?.fulfill()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ class TestMarkArticlesService: FeedlyMarkArticlesService {
|
||||
var parameterTester: ((Set<String>, FeedlyMarkAction) -> ())?
|
||||
var mockResult: Result<Void, Error> = .success(())
|
||||
|
||||
func mark(_ articleIds: Set<String>, as action: FeedlyMarkAction, completion: @escaping (Result<Void, Error>) -> ()) {
|
||||
func mark(_ articleIDs: Set<String>, as action: FeedlyMarkAction, completion: @escaping (Result<Void, Error>) -> ()) {
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.parameterTester?(articleIds, action)
|
||||
self.parameterTester?(articleIDs, action)
|
||||
completion(self.mockResult)
|
||||
self.didMarkExpectation?.fulfill()
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import RSWeb
|
||||
import XCTest
|
||||
|
||||
protocol TestTransportMockResponseProviding: AnyObject {
|
||||
func mockResponseFileUrl(for components: URLComponents) -> URL?
|
||||
func mockResponseFileURL(for components: URLComponents) -> URL?
|
||||
}
|
||||
|
||||
final class TestTransport: Transport {
|
||||
@@ -23,7 +23,7 @@ final class TestTransport: Transport {
|
||||
var testFiles = [String: String]()
|
||||
var testStatusCodes = [String: Int]()
|
||||
|
||||
weak var mockResponseFileUrlProvider: TestTransportMockResponseProviding?
|
||||
weak var mockResponseFileURLProvider: TestTransportMockResponseProviding?
|
||||
|
||||
private func httpResponse(for request: URLRequest, statusCode: Int = 200) -> HTTPURLResponse {
|
||||
guard let url = request.url else {
|
||||
@@ -45,12 +45,12 @@ final class TestTransport: Transport {
|
||||
let response = httpResponse(for: request, statusCode: testStatusCodes[urlString] ?? 200)
|
||||
let testFileURL: URL
|
||||
|
||||
if let provider = mockResponseFileUrlProvider {
|
||||
guard let providerUrl = provider.mockResponseFileUrl(for: components) else {
|
||||
if let provider = mockResponseFileURLProvider {
|
||||
guard let providerURL = provider.mockResponseFileURL(for: components) else {
|
||||
XCTFail("Test behaviour undefined. Mock provider failed to provide non-nil URL for \(components).")
|
||||
return
|
||||
}
|
||||
testFileURL = providerUrl
|
||||
testFileURL = providerURL
|
||||
|
||||
} else if let testKeyAndFileName = testFiles.first(where: { urlString.contains($0.key) }) {
|
||||
testFileURL = Bundle.module.resourceURL!.appendingPathComponent(testKeyAndFileName.value)
|
||||
|
||||
@@ -98,7 +98,7 @@ import Secrets
|
||||
|
||||
do {
|
||||
try self.account?.removeCredentials(type: .newsBlurBasic)
|
||||
try self.account?.removeCredentials(type: .newsBlurSessionId)
|
||||
try self.account?.removeCredentials(type: .newsBlurSessionID)
|
||||
try self.account?.storeCredentials(credentials)
|
||||
try self.account?.storeCredentials(validatedCredentials)
|
||||
|
||||
|
||||
@@ -121,10 +121,10 @@ import RSCore
|
||||
|
||||
@objc(valueInFoldersWithUniqueID:)
|
||||
func valueInFolders(withUniqueID id:NSNumber) -> ScriptableFolder? {
|
||||
let folderId = id.intValue
|
||||
let folderID = id.intValue
|
||||
let foldersSet = account.folders ?? Set<Folder>()
|
||||
let folders = Array(foldersSet)
|
||||
guard let folder = folders.first(where:{$0.folderID == folderId}) else { return nil }
|
||||
guard let folder = folders.first(where:{$0.folderID == folderID}) else { return nil }
|
||||
return ScriptableFolder(folder, container:self)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ public enum CredentialsError: Error {
|
||||
public enum CredentialsType: String {
|
||||
case basic = "password"
|
||||
case newsBlurBasic = "newsBlurBasic"
|
||||
case newsBlurSessionId = "newsBlurSessionId"
|
||||
case newsBlurSessionID = "newsBlurSessionId"
|
||||
case readerBasic = "readerBasic"
|
||||
case readerAPIKey = "readerAPIKey"
|
||||
case oauthAccessToken = "oauthAccessToken"
|
||||
|
||||
@@ -45,9 +45,9 @@ public class ArticleThemeDownloader: Logging {
|
||||
private func moveTheme(from location: URL) throws -> URL {
|
||||
var tmpFileName = location.lastPathComponent
|
||||
tmpFileName = tmpFileName.replacingOccurrences(of: ".tmp", with: ".zip")
|
||||
let fileUrl = downloadDirectory().appendingPathComponent("\(tmpFileName)")
|
||||
try FileManager.default.moveItem(at: location, to: fileUrl)
|
||||
return fileUrl
|
||||
let fileURL = downloadDirectory().appendingPathComponent("\(tmpFileName)")
|
||||
try FileManager.default.moveItem(at: location, to: fileURL)
|
||||
return fileURL
|
||||
}
|
||||
|
||||
/// Unzips the zip file
|
||||
|
||||
@@ -116,7 +116,7 @@ extension Article {
|
||||
return IconImageCache.shared.imageForArticle(self)
|
||||
}
|
||||
|
||||
@MainActor func iconImageUrl(feed: Feed) -> URL? {
|
||||
@MainActor func iconImageURL(feed: Feed) -> URL? {
|
||||
if let image = iconImage() {
|
||||
let fm = FileManager.default
|
||||
var path = fm.urls(for: .cachesDirectory, in: .userDomainMask)[0]
|
||||
|
||||
@@ -15,7 +15,7 @@ extension URL {
|
||||
scheme == "mailto" ? URLComponents(url: self, resolvingAgainstBaseURL: false)?.path : nil
|
||||
}
|
||||
|
||||
/// Percent encoded `mailto` URL for use with `canOpenUrl`. If the URL doesn't contain the `mailto` scheme, this is `nil`.
|
||||
/// Percent encoded `mailto` URL for use with `canOpenURL`. If the URL doesn't contain the `mailto` scheme, this is `nil`.
|
||||
var percentEncodedEmailAddress: URL? {
|
||||
guard scheme == "mailto" else {
|
||||
return nil
|
||||
|
||||
@@ -80,7 +80,7 @@ private extension UserNotificationManager {
|
||||
/// - Returns: A `UNNotificationAttachment` if an icon is available. Otherwise nil.
|
||||
/// - Warning: In certain scenarios, this will return the `faviconTemplateImage`.
|
||||
@MainActor func thumbnailAttachment(for article: Article, feed: Feed) -> UNNotificationAttachment? {
|
||||
if let imageURL = article.iconImageUrl(feed: feed) {
|
||||
if let imageURL = article.iconImageURL(feed: feed) {
|
||||
let thumbnail = try? UNNotificationAttachment(identifier: feed.feedID, url: imageURL, options: nil)
|
||||
return thumbnail
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public final class NewsBlurAPICaller {
|
||||
|
||||
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headerFields, for: url)
|
||||
for cookie in cookies where cookie.name == Self.SessionIdCookie {
|
||||
let credentials = Credentials(type: .newsBlurSessionId, username: username, secret: cookie.value)
|
||||
let credentials = Credentials(type: .newsBlurSessionID, username: username, secret: cookie.value)
|
||||
completion(.success(credentials))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -121,11 +121,11 @@ public struct ReaderAPIAlternateLocation: Codable {
|
||||
}
|
||||
|
||||
public struct ReaderAPIEntryOrigin: Codable {
|
||||
public let streamId: String?
|
||||
public let streamID: String?
|
||||
let title: String?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case streamId = "streamId"
|
||||
case streamID = "streamId"
|
||||
case title = "title"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,11 +82,11 @@ public struct ReaderAPISubscription: Codable {
|
||||
}
|
||||
|
||||
public struct ReaderAPICategory: Codable {
|
||||
public let categoryId: String
|
||||
public let categoryID: String
|
||||
let categoryLabel: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case categoryId = "id"
|
||||
case categoryID = "id"
|
||||
case categoryLabel = "label"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,12 +32,12 @@ class AppleScriptXCTestCase: XCTestCase {
|
||||
var errorDict: NSDictionary? = nil
|
||||
let testBundle = Bundle(for: type(of: self))
|
||||
let url = testBundle.url(forResource:filename, withExtension:"scpt")
|
||||
guard let testScriptUrl = url else {
|
||||
guard let testScriptURL = url else {
|
||||
XCTFail("Failed Getting script URL")
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let testScript = NSAppleScript(contentsOf: testScriptUrl, error: &errorDict) else {
|
||||
guard let testScript = NSAppleScript(contentsOf: testScriptURL, error: &errorDict) else {
|
||||
print ("error is \(String(describing: errorDict))")
|
||||
XCTFail("Failed initializing NSAppleScript")
|
||||
return nil
|
||||
|
||||
@@ -32,11 +32,11 @@ class ScriptingTests: AppleScriptXCTestCase {
|
||||
XCTAssert( scriptResult?.stringValue == "Geoducks!")
|
||||
}
|
||||
|
||||
func testGetUrlScript() {
|
||||
func testGetURLScript() {
|
||||
_ = doIndividualScript(filename: "testGetURL")
|
||||
}
|
||||
|
||||
func testNameAndUrlOfEveryFeedScript() {
|
||||
func testNameAndURLOfEveryFeedScript() {
|
||||
_ = doIndividualScript(filename: "testNameAndUrlOfEveryFeed")
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ import RSCore
|
||||
do {
|
||||
do {
|
||||
try self.account?.removeCredentials(type: .newsBlurBasic)
|
||||
try self.account?.removeCredentials(type: .newsBlurSessionId)
|
||||
try self.account?.removeCredentials(type: .newsBlurSessionID)
|
||||
} catch {
|
||||
NewsBlurAddAccountView.logger.error("\(error.localizedDescription)")
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import RSCore
|
||||
@State private var accountCredentials: Credentials?
|
||||
@State private var accountUserName: String = ""
|
||||
@State private var accountSecret: String = ""
|
||||
@State private var accountAPIUrl: String = ""
|
||||
@State private var accountAPIURL: String = ""
|
||||
@State private var showProgressIndicator: Bool = false
|
||||
@State private var accountError: (Error?, Bool) = (nil, false)
|
||||
|
||||
@@ -88,7 +88,7 @@ import RSCore
|
||||
SecureField("Password", text: $accountSecret, prompt: Text("textfield.placeholder.password", comment: "Password"))
|
||||
.textContentType(.password)
|
||||
if accountType == .freshRSS && accountCredentials == nil {
|
||||
TextField("FreshRSS URL", text: $accountAPIUrl, prompt: Text(verbatim: "fresh.rss.net/api/greader.php"))
|
||||
TextField("FreshRSS URL", text: $accountAPIURL, prompt: Text(verbatim: "fresh.rss.net/api/greader.php"))
|
||||
.autocorrectionDisabled()
|
||||
.autocapitalization(.none)
|
||||
}
|
||||
@@ -147,7 +147,7 @@ import RSCore
|
||||
if accountType == nil { return false }
|
||||
switch accountType! {
|
||||
case .freshRSS:
|
||||
if (accountUserName.trimmingWhitespace.count == 0) || (accountSecret.trimmingWhitespace.count == 0) || (accountAPIUrl.trimmingWhitespace.count == 0) {
|
||||
if (accountUserName.trimmingWhitespace.count == 0) || (accountSecret.trimmingWhitespace.count == 0) || (accountAPIURL.trimmingWhitespace.count == 0) {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
@@ -215,7 +215,7 @@ import RSCore
|
||||
private func apiURL() -> URL? {
|
||||
switch accountType! {
|
||||
case .freshRSS:
|
||||
return URL(string: accountAPIUrl)!
|
||||
return URL(string: accountAPIURL)!
|
||||
case .inoreader:
|
||||
return URL(string: ReaderAPIVariant.inoreader.host)!
|
||||
case .bazQux:
|
||||
|
||||
@@ -27,7 +27,7 @@ public class AddFeedIntentHandler: NSObject, AddFeedIntentHandling {
|
||||
super.init()
|
||||
}
|
||||
|
||||
public func resolveUrl(for intent: AddFeedIntent, with completion: @escaping (AddFeedUrlResolutionResult) -> Void) {
|
||||
public func resolveURL(for intent: AddFeedIntent, with completion: @escaping (AddFeedURLResolutionResult) -> Void) {
|
||||
guard let url = intent.url else {
|
||||
completion(.unsupported(forReason: .required))
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user