Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ extension FormattableRangeKind {
public static let user = FormattableRangeKind("user")
public static let post = FormattableRangeKind("post")
public static let comment = FormattableRangeKind("comment")
public static let readerStream = FormattableRangeKind("readerstream")
public static let stats = FormattableRangeKind("stat")
public static let follow = FormattableRangeKind("follow")
public static let blockquote = FormattableRangeKind("blockquote")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ public class NotificationContentRange: FormattableContentRange, LinkContentRange
public let userID: NSNumber?
public let siteID: NSNumber?
public let postID: NSNumber?
public let streamKey: String?
public let streamQueryParameters: [String: String]
public let url: URL?

public init(kind: FormattableRangeKind, properties: Properties) {
Expand All @@ -16,6 +18,8 @@ public class NotificationContentRange: FormattableContentRange, LinkContentRange
siteID = properties.siteID
userID = properties.userID
postID = properties.postID
streamKey = properties.streamKey
streamQueryParameters = properties.streamQueryParameters
}
}

Expand All @@ -26,6 +30,8 @@ extension NotificationContentRange {
public var siteID: NSNumber?
public var userID: NSNumber?
public var postID: NSNumber?
public var streamKey: String?
public var streamQueryParameters: [String: String] = [:]

public init(range: NSRange) {
self.range = range
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,25 @@ struct NotificationContentRangeFactory: FormattableRangesFactory {
return contentRangeWithoutKindSpecified(with: properties, from: dictionary)
}

private static func propertiesFrom(_ dictionary: [String: AnyObject], with range: NSRange) -> NotificationContentRange.Properties {
private static func propertiesFrom(
_ dictionary: [String: AnyObject],
with range: NSRange
) -> NotificationContentRange.Properties {
var properties = NotificationContentRange.Properties(range: range)

properties.siteID = dictionary[RangeKeys.siteId] as? NSNumber
properties.postID = dictionary[RangeKeys.postId] as? NSNumber
properties.streamKey = dictionary[RangeKeys.id] as? String

if let url = dictionary[RangeKeys.url] as? String {
properties.url = URL(string: url)
if let urlString = dictionary[RangeKeys.url] as? String, let url = URL(string: urlString) {
properties.url = url
if let items = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems {
for item in items {
if let value = item.value, !value.isEmpty {
properties.streamQueryParameters[item.name] = value
}
}
}
}
return properties
}
Expand Down
85 changes: 54 additions & 31 deletions Modules/Sources/WordPressKit/ReaderPostServiceRemote+Cards.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public enum ReaderStream: String {
case discover = "discover"
case freshlyPressed = "freshly-pressed"
case firstPosts = "first-posts"
case onThisDay = "on-this-day"
}

extension ReaderPostServiceRemote {
Expand All @@ -30,18 +31,24 @@ extension ReaderPostServiceRemote {
/// - Parameter sortingOption: a ReaderSortingOption that represents a sorting option
/// - Parameter success: Called when the request succeeds and the data returned is valid
/// - Parameter failure: Called if the request fails for any reason, or the response data is invalid
public func fetchCards(for topics: [String],
page: String? = nil,
sortingOption: ReaderSortingOption = .noSorting,
refreshCount: Int? = nil,
success: @escaping ([RemoteReaderCard], String?) -> Void,
failure: @escaping (Error) -> Void) {
public func fetchCards(
for topics: [String],
page: String? = nil,
sortingOption: ReaderSortingOption = .noSorting,
refreshCount: Int? = nil,
success: @escaping ([RemoteReaderCard], String?) -> Void,
failure: @escaping (Error) -> Void
) {
let path = "read/tags/cards"
guard let requestUrl = cardsEndpoint(with: path,
topics: topics,
page: page,
sortingOption: sortingOption,
refreshCount: refreshCount) else {
guard
let requestUrl = cardsEndpoint(
with: path,
topics: topics,
page: page,
sortingOption: sortingOption,
refreshCount: refreshCount
)
else {
return
}
fetch(requestUrl, success: success, failure: failure)
Expand All @@ -60,28 +67,36 @@ extension ReaderPostServiceRemote {
/// - Parameter count: the number of cards to fetch. Warning: This also changes the number of objects returned for recommended sites/tags.
/// - Parameter success: Called when the request succeeds and the data returned is valid
/// - Parameter failure: Called if the request fails for any reason, or the response data is invalid
public func fetchStreamCards(stream: ReaderStream = .discover,
for topics: [String],
page: String? = nil,
sortingOption: ReaderSortingOption = .noSorting,
refreshCount: Int? = nil,
count: Int? = nil,
success: @escaping ([RemoteReaderCard], String?) -> Void,
failure: @escaping (Error) -> Void) {
public func fetchStreamCards(
stream: ReaderStream = .discover,
for topics: [String],
page: String? = nil,
sortingOption: ReaderSortingOption = .noSorting,
refreshCount: Int? = nil,
count: Int? = nil,
forwardedQueryParameters: [String: String]? = nil,
success: @escaping ([RemoteReaderCard], String?) -> Void,
failure: @escaping (Error) -> Void
) {
let path = "read/streams/\(stream.rawValue)"
guard let requestUrl = cardsEndpoint(with: path,
topics: topics,
page: page,
sortingOption: sortingOption,
count: count,
refreshCount: refreshCount) else {
guard
let requestUrl = cardsEndpoint(
with: path,
topics: topics,
page: page,
sortingOption: sortingOption,
count: count,
refreshCount: refreshCount,
forwardedQueryParameters: forwardedQueryParameters
)
else {
return
}
fetch(requestUrl, success: success, failure: failure)
}

private func fetch(_ endpoint: String,
success: @escaping ([RemoteReaderCard], String?) -> Void,
success: @escaping ([RemoteReaderCard], String?) -> Void,
failure: @escaping (Error) -> Void) {
Task { @MainActor [wordPressComRestApi] in
await wordPressComRestApi.perform(.get, URLString: endpoint, type: ReaderCardEnvelope.self)
Expand All @@ -92,11 +107,13 @@ extension ReaderPostServiceRemote {
}

private func cardsEndpoint(with path: String,
topics: [String],
page: String? = nil,
sortingOption: ReaderSortingOption = .noSorting,
count: Int? = nil,
refreshCount: Int? = nil) -> String? {
topics: [String],
page: String? = nil,
sortingOption: ReaderSortingOption = .noSorting,
count: Int? = nil,
refreshCount: Int? = nil,
forwardedQueryParameters: [String: String]? = nil
) -> String? {
var path = URLComponents(string: path)

path?.queryItems = topics.map { URLQueryItem(name: "tags[]", value: $0) }
Expand All @@ -117,6 +134,12 @@ extension ReaderPostServiceRemote {
path?.queryItems?.append(URLQueryItem(name: "refresh", value: String(refreshCount)))
}

if let forwardedQueryParameters {
for (name, value) in forwardedQueryParameters {
path?.queryItems?.append(URLQueryItem(name: name, value: value))
}
}

guard let endpoint = path?.string else {
return nil
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"url" : "https://wordpress.com/reader/on-this-day",
"type" : "readerstream",
"id" : "on-this-day",
"indices" : [
0,
12
]
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

@testable import WordPress

class MockContentCoordinator: ContentCoordinator {
Expand Down Expand Up @@ -44,6 +43,15 @@ class MockContentCoordinator: ContentCoordinator {
streamSiteID = siteID
}

var streamWasDisplayedByStreamKey = false
var streamKey: String?
var streamQueryParameters: [String: String]?
func displayStreamWithStreamKey(_ streamKey: String?, queryParameters: [String: String]?) throws {
streamWasDisplayedByStreamKey = true
self.streamKey = streamKey
streamQueryParameters = queryParameters
}

func displayWebViewWithURL(_ url: URL, source: String) {

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ final class NotificationContentRangeFactoryTests: XCTestCase {
XCTAssertNotNil(subject)
}

func testReaderStreamRangeReturnsExpectedKind() throws {
let subject =
NotificationContentRangeFactory.contentRange(from: try mockReaderStreamRange()) as? NotificationContentRange

XCTAssertEqual(subject?.kind, .readerStream)
}

private func mockCommentRange() throws -> JSONObject {
return try JSONObject(fromFileNamed: "notifications-comment-range.json")
}
Expand All @@ -64,4 +71,8 @@ final class NotificationContentRangeFactoryTests: XCTestCase {
return try JSONObject(fromFileNamed: "notifications-blockquote-range.json")
}

private func mockReaderStreamRange() throws -> JSONObject {
return try JSONObject(fromFileNamed: "notifications-readerstream-range.json")
}

}
41 changes: 22 additions & 19 deletions Tests/KeystoneTests/Tests/Services/ReaderCardServiceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ class ReaderCardServiceTests: CoreDataTestCase {

service.fetch(isFirstPage: true, success: { _, _ in
let cards = try? self.mainContext.fetch(NSFetchRequest(entityName: ReaderCard.classNameWithoutNamespaces()))
XCTAssertEqual(cards?.count, 9)
expectation.fulfill()
XCTAssertEqual(cards?.count, 9)
expectation.fulfill()
}, failure: { _ in })

waitForExpectations(timeout: 5, handler: nil)
Expand All @@ -41,8 +41,8 @@ class ReaderCardServiceTests: CoreDataTestCase {

service.fetch(isFirstPage: true, success: { _, _ in
let cards = try? self.mainContext.fetch(NSFetchRequest(entityName: ReaderCard.classNameWithoutNamespaces())) as? [ReaderCard]
XCTAssertEqual(cards?.filter { $0.post != nil }.count, 8)
expectation.fulfill()
XCTAssertEqual(cards?.filter { $0.post != nil }.count, 8)
expectation.fulfill()
}, failure: { _ in })

waitForExpectations(timeout: 5, handler: nil)
Expand All @@ -56,8 +56,8 @@ class ReaderCardServiceTests: CoreDataTestCase {
remoteService.shouldCallFailure = true

service.fetch(isFirstPage: true, success: { _, _ in }, failure: { error in
XCTAssertNotNil(error)
expectation.fulfill()
XCTAssertNotNil(error)
expectation.fulfill()
})

waitForExpectations(timeout: 5, handler: nil)
Expand All @@ -70,11 +70,11 @@ class ReaderCardServiceTests: CoreDataTestCase {
let service = ReaderCardService(service: remoteService, coreDataStack: contextManager, followedInterestsService: followedInterestsService)

service.fetch(isFirstPage: false, success: { _, _ in
// Fetch again, this time the 1st page
// Fetch again, this time the 1st page
service.fetch(isFirstPage: true, success: { _, _ in
let cards = try? self.mainContext.fetch(NSFetchRequest(entityName: ReaderCard.classNameWithoutNamespaces())) as? [ReaderCard]
XCTAssertEqual(cards?.count, 9)
expectation.fulfill()
XCTAssertEqual(cards?.count, 9)
expectation.fulfill()
}, failure: { _ in })
}, failure: {_ in })

Expand All @@ -89,14 +89,17 @@ class ReaderCardServiceTests: CoreDataTestCase {
final class ReaderPostServiceRemoteMock: ReaderCardServiceRemote {
var shouldCallFailure = false

func fetchStreamCards(stream: WordPressKit.ReaderStream,
for topics: [String],
page: String?,
sortingOption: WordPressKit.ReaderSortingOption,
refreshCount: Int?,
count: Int?,
success: @escaping ([WordPressKit.RemoteReaderCard], String?) -> Void,
failure: @escaping (any Error) -> Void) {
func fetchStreamCards(
stream: WordPressKit.ReaderStream,
for topics: [String],
page: String?,
sortingOption: WordPressKit.ReaderSortingOption,
refreshCount: Int?,
count: Int?,
forwardedQueryParameters: [String: String]?,
success: @escaping ([WordPressKit.RemoteReaderCard], String?) -> Void,
failure: @escaping (any Error) -> Void
) {
mockFetch(success: success, failure: failure)
}

Expand All @@ -108,7 +111,7 @@ final class ReaderPostServiceRemoteMock: ReaderCardServiceRemote {
}

guard let fileUrl = Bundle(for: ReaderPostServiceRemoteMock.self).url(forResource: "reader-cards.json", withExtension: nil),
let data = try? Data(contentsOf: fileUrl),
let data = try? Data(contentsOf: fileUrl),
let cards = try? JSONDecoder().decode([RemoteReaderCard].self, from: data) else {
XCTFail("Error setting up mock data")
return
Expand Down Expand Up @@ -166,6 +169,6 @@ private class ReaderFollowedInterestsServiceMock: ReaderFollowedInterestsService
}

func path(slug: String) -> String {
return ""
""
}
}
Loading