Skip to content

Commit

Permalink
Merge pull request #167 from TootSDK/improve-push-notification-compat…
Browse files Browse the repository at this point in the history
…ibiltiy

Improve compatibility for web push notifications and pagination
  • Loading branch information
kkostov committed May 24, 2023
2 parents 5c1f999 + 060e33f commit 9f8ceef
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 21 deletions.
48 changes: 36 additions & 12 deletions Sources/TootSDK/HTTP/Pagination.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright (c) 2022. All rights reserved.

import Foundation
import WebURL

public struct Pagination {
public var maxId: String?
Expand All @@ -13,10 +14,13 @@ public extension Pagination {
static let paginationTypes: [String] = ["prev", "next"]

init(links: String) {
let links = links.components(separatedBy: ",")
let links = links.components(separatedBy: ",").map({
$0.trimmingCharacters(in: .whitespacesAndNewlines)
})

let paginationQueryItems: [URLQueryItem] = links.compactMap({ link in
let segments = link
let segments =
link
.condensed()
.components(separatedBy: ";")
let url = segments.first.map(trim(left: "<", right: ">"))
Expand All @@ -25,18 +29,37 @@ public extension Pagination {
.trimmingCharacters(in: .whitespaces)
.components(separatedBy: "=")

guard
let validURL = url,
let referenceKey = rel?.first, referenceKey == "rel",
let referenceValue = rel?.last,
Self.paginationTypes.contains(referenceValue),
let queryItems = URLComponents(string: validURL)?.queryItems
else {
print("TootSDK: invalid pagination Link '\(link)'")
guard let validURL = url else {
print("TootSDK: invalid pagination Link (url): '\(link)'")
return []
}

return queryItems
guard let referenceKey = rel?.first else {
print("TootSDK: invalid pagination Link (referenceKey): '\(link)'")
return []
}

guard let referenceValue = rel?.last else {
print("TootSDK: invalid pagination Link (referenceValue): '\(link)'")
return []
}

guard referenceKey == "rel" else {
print("TootSDK: invalid pagination Link (rel): '\(link)'")
return []
}

guard Self.paginationTypes.contains(referenceValue) else {
print("TootSDK: invalid pagination Link (paginationType): '\(link)'")
return []
}

guard let webURL = WebURL(validURL) else {
print("TootSDK: invalid pagination Link (query): '\(link)'")
return []
}

return webURL.formParams.allKeyValuePairs.map({URLQueryItem(name: $0.0, value: $0.1)})
}).reduce([], +)

minId = paginationQueryItems.first { $0.name == "min_id" }?.value
Expand All @@ -48,6 +71,7 @@ public extension Pagination {
func trim(left: Character, right: Character) -> (String) -> String {
return { string in
guard string.hasPrefix("\(left)"), string.hasSuffix("\(right)") else { return string }
return String(string[string.index(after: string.startIndex)..<string.index(before: string.endIndex)])
return String(
string[string.index(after: string.startIndex)..<string.index(before: string.endIndex)])
}
}
7 changes: 2 additions & 5 deletions Sources/TootSDK/Models/PushSubscription.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,13 @@
import Foundation

/// Represents a subscription to the push streaming server.
public struct PushSubscription: Codable, Identifiable, Sendable {
public init(id: String, endpoint: String, alerts: Alerts, serverKey: String) {
self.id = id
public struct PushSubscription: Codable, Sendable {
public init(endpoint: String, alerts: Alerts, serverKey: String) {
self.endpoint = endpoint
self.alerts = alerts
self.serverKey = serverKey
}

/// The id of the push subscription in the database.
public var id: String
/// Where push alerts will be sent to.
public var endpoint: String
/// Which alerts should be delivered to the endpoint.
Expand Down
5 changes: 5 additions & 0 deletions Sources/TootSDK/TootClient/TootClient+Notifications.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public extension TootClient {
/// Add a Web Push API subscription to receive notifications. Each access token can have one push subscription.
///
/// If you create a new subscription, the old subscription is deleted.
@discardableResult
func createPushSubscription(params: PushSubscriptionParams) async throws -> PushSubscription {
let req = try HTTPRequestBuilder {
$0.url = getURL(["api", "v1", "push", "subscription"])
Expand Down Expand Up @@ -114,6 +115,10 @@ public extension TootClient {
queryParameters.append(.init(name: "data[alerts][status]", value: String(alert).lowercased()))
}

if let alert = params.data?.alerts?.repost {
queryParameters.append(.init(name: "data[alerts][reblog]", value: String(alert).lowercased()))
}

if let alert = params.data?.alerts?.follow {
queryParameters.append(.init(name: "data[alerts][follow]", value: String(alert).lowercased()))
}
Expand Down
21 changes: 17 additions & 4 deletions Tests/TootSDKTests/PaginationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright (c) 2022. All rights reserved.

import XCTest

@testable import TootSDK

final class PaginationTests: XCTestCase {
Expand All @@ -10,7 +11,7 @@ final class PaginationTests: XCTestCase {
func testPaginationWithInvalidNextAndPrevious() {
let links = [
"<\(serverUrl)/api/v1/timelines/home?max_id=420>; rel=\"\"",
"this is not a valid URL; rel=\"next\""
"this is not a valid URL; rel=\"next\"",
].joined(separator: ",")

let pagination = Pagination(links: links)
Expand All @@ -23,7 +24,7 @@ final class PaginationTests: XCTestCase {
func testPaginationWithValidNext() {
let links = [
"<\(serverUrl)/api/v1/timelines/home?limit=42&max_id=420>; rel=\"next\"",
"this is not a valid URL; rel=\"prev\""
"this is not a valid URL; rel=\"prev\"",
].joined(separator: ",")

let pagination = Pagination(links: links)
Expand All @@ -36,7 +37,7 @@ final class PaginationTests: XCTestCase {
func testPaginationWithValidPrevious() {
let links = [
"<\(serverUrl)/api/v1/timelines/home?limit=42&since_id=420>; rel=\"prev\"",
"this is not a valid URL; rel=\"next\""
"this is not a valid URL; rel=\"next\"",
].joined(separator: ",")

let pagination = Pagination(links: links)
Expand All @@ -49,7 +50,7 @@ final class PaginationTests: XCTestCase {
func testPaginationWithValidNextAndPrevious() {
let links = [
"<\(serverUrl)/api/v1/timelines/home?limit=42&since_id=123>; rel=\"prev\"",
"<\(serverUrl)/api/v1/timelines/home?limit=52&max_id=321>; rel=\"next\""
"<\(serverUrl)/api/v1/timelines/home?limit=52&max_id=321>; rel=\"next\"",
].joined(separator: ",")

let pagination = Pagination(links: links)
Expand All @@ -58,4 +59,16 @@ final class PaginationTests: XCTestCase {
XCTAssertNil(pagination.minId)
XCTAssertEqual(pagination.maxId, "321")
}

func testPaginationTolleratesSpacesAndNewLines() {
let links: String = [
"\n <https://m.iamkonstantin.eu/api/v1/notifications?limit=20&max_id=15223&offset=0&types[]=mention>; rel=\"next\"\n "
].joined(separator: ",")

let pagination = Pagination(links: links)

XCTAssertNil(pagination.sinceId)
XCTAssertNil(pagination.minId)
XCTAssertEqual(pagination.maxId, "15223")
}
}

0 comments on commit 9f8ceef

Please sign in to comment.