diff --git a/Source/Common/Network/HttpClient.swift b/Source/Common/Network/HttpClient.swift index 21d3d11..21dd1cf 100644 --- a/Source/Common/Network/HttpClient.swift +++ b/Source/Common/Network/HttpClient.swift @@ -15,12 +15,22 @@ import Foundation import Harmony protocol HttpClient { - func prepareURLRequest(path: String, externalId: String?, email: String?, hmac: String?, additionalHTTPHeaders: [String: String]?) -> URLRequest + func prepareURLRequest(path: String, externalId: String?, email: String?, hmac: String?, additionalHTTPHeaders: [String: String]?, queryItems: [URLQueryItem]?) -> URLRequest + func performRequest(_ urlRequest: URLRequest) -> Future } + extension HttpClient { func prepareURLRequest(path: String, externalId: String?, email: String?, hmac: String?) -> URLRequest { - prepareURLRequest(path: path, externalId: externalId, email: email, hmac: hmac, additionalHTTPHeaders: [:]) + prepareURLRequest(path: path, externalId: externalId, email: email, hmac: hmac, additionalHTTPHeaders: nil, queryItems: nil) + } + + func prepareURLRequest(path: String, externalId: String?, email: String?, hmac: String?, additionalHTTPHeaders: [String: String]?) -> URLRequest { + prepareURLRequest(path: path, externalId: externalId, email: email, hmac: hmac, additionalHTTPHeaders: additionalHTTPHeaders, queryItems: nil) + } + + func prepareURLRequest(path: String, externalId: String?, email: String?, hmac: String?, queryItems: [URLQueryItem]?) -> URLRequest { + prepareURLRequest(path: path, externalId: externalId, email: email, hmac: hmac, additionalHTTPHeaders: nil, queryItems: queryItems) } } @@ -34,8 +44,10 @@ class DefaultHttpClient: HttpClient { self.environment = environment } - func prepareURLRequest(path: String, externalId: String?, email: String?, hmac: String?, additionalHTTPHeaders: [String: String]?) -> URLRequest { - var urlRequest = URLRequest(url: environment.baseUrl.appendingPathComponent(path)) + func prepareURLRequest(path: String, externalId: String?, email: String?, hmac: String?, additionalHTTPHeaders: [String: String]? = nil, queryItems: [URLQueryItem]? = nil) -> URLRequest { + var urlComponents = URLComponents(url:environment.baseUrl.appendingPathComponent(path), resolvingAgainstBaseURL: false)! + urlComponents.queryItems = queryItems + var urlRequest = URLRequest(url: urlComponents.url!) urlRequest.addValue(environment.apiKey, forHTTPHeaderField: "X-MAGICBELL-API-KEY") diff --git a/Source/Features/Store/Data/StoreContext+QueryItems.swift b/Source/Features/Store/Data/StoreContext+QueryItems.swift new file mode 100644 index 0000000..251800d --- /dev/null +++ b/Source/Features/Store/Data/StoreContext+QueryItems.swift @@ -0,0 +1,65 @@ +// +// By downloading or using this software made available by MagicBell, Inc. +// ("MagicBell") or any documentation that accompanies it (collectively, the +// "Software"), you and the company or entity that you represent (collectively, +// "you" or "your") are consenting to be bound by and are becoming a party to this +// License Agreement (this "Agreement"). You hereby represent and warrant that you +// are authorized and lawfully able to bind such company or entity that you +// represent to this Agreement. If you do not have such authority or do not agree +// to all of the terms of this Agreement, you may not download or use the Software. +// +// For more information, read the LICENSE file. +// + +import Foundation + +extension StorePredicate { + var asQueryItems: [URLQueryItem] { + var queryItems: [URLQueryItem] = [] + + ["read": read, + "seen": seen, + "archived": archived].forEach { (key: String, value: Bool?) in + if let value = value { + queryItems.append(URLQueryItem(name: key, value: value.description)) + } + } + + for category in categories { + queryItems.append(URLQueryItem(name: "category", value: category)) + } + for topic in topics { + queryItems.append(URLQueryItem(name: "topic", value: topic)) + } + + return queryItems + } +} + + +extension StorePagePredicate { + var asQueryItems: [URLQueryItem] { + var queryItems: [URLQueryItem] = [] + + ["page": page, + "per_page": size].forEach { (key: String, value: Int?) in + if let value = value { + queryItems.append(URLQueryItem(name: key, value: value.description)) + } + } + + return queryItems + } +} + + +extension StoreContext { + var asQueryItems: [URLQueryItem] { + var queryItems: [URLQueryItem] = [] + + queryItems.append(contentsOf: self.store.asQueryItems) + queryItems.append(contentsOf: self.page.asQueryItems) + + return queryItems + } +} diff --git a/Source/Features/Store/Data/StoreDataSource.swift b/Source/Features/Store/Data/StoreDataSource.swift index 721c967..25116ff 100644 --- a/Source/Features/Store/Data/StoreDataSource.swift +++ b/Source/Features/Store/Data/StoreDataSource.swift @@ -28,12 +28,14 @@ class StoreDataSource: GetDataSource { func get(_ query: Query) -> Future { switch query { - case let userQuery as StoreQuery: + case let storeQuery as StoreQuery: + let userQuery = storeQuery.userQuery let urlRequest = httpClient.prepareURLRequest( path: "/notifications", - externalId: userQuery.userQuery.externalId, - email: userQuery.userQuery.email, - hmac: userQuery.userQuery.hmac + externalId: userQuery.externalId, + email: userQuery.email, + hmac: userQuery.hmac, + queryItems: storeQuery.context.asQueryItems ) return httpClient .performRequest(urlRequest) diff --git a/Source/Features/Store/Data/StoreGraphQLRepresentableExtensions.swift b/Source/Features/Store/Data/StoreGraphQLRepresentableExtensions.swift deleted file mode 100644 index bd6227c..0000000 --- a/Source/Features/Store/Data/StoreGraphQLRepresentableExtensions.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// By downloading or using this software made available by MagicBell, Inc. -// ("MagicBell") or any documentation that accompanies it (collectively, the -// "Software"), you and the company or entity that you represent (collectively, -// "you" or "your") are consenting to be bound by and are becoming a party to this -// License Agreement (this "Agreement"). You hereby represent and warrant that you -// are authorized and lawfully able to bind such company or entity that you -// represent to this Agreement. If you do not have such authority or do not agree -// to all of the terms of this Agreement, you may not download or use the Software. -// -// For more information, read the LICENSE file. -// - -import Foundation - -//extension StorePredicate: GraphQLRepresentable { -// var graphQLValue: String { -// var string: [String] = [] -// -// if read == true { -// string.append("read: true") -// } else if read == false { -// string.append("read: false") -// } -// -// if seen == true { -// string.append("seen: true") -// } else if seen == false { -// string.append("seen: false") -// } -// -// if archived { -// string.append("archived: true") -// } else { -// string.append("archived: false") -// } -// -// if !categories.isEmpty { -// string.append("categories:[\(categories.map { "\"\($0)\"" }.joined(separator: ", "))]") -// } -// -// if !topics.isEmpty { -// string.append("topics:[\(topics.map { "\"\($0)\"" }.joined(separator: ", "))]") -// } -// -// return string.joined(separator: ", ") -// } -//} -// -// -//extension StoreContext: GraphQLRepresentable { -// var graphQLValue: String { -// let storePredicateString = store.graphQLValue -// let cursorPredicateString = cursor.graphQLValue -// return " \(name):notifications (\(storePredicateString) \(cursorPredicateString)) { ...notification }" -// } -//} -// -//extension StoreQuery: GraphQLRepresentable { -// var graphQLValue: String { -// return contexts.map { context in -// context.graphQLValue -// }.joined(separator: "\n") -// } -//} diff --git a/Source/Features/Store/NotificationStore.swift b/Source/Features/Store/NotificationStore.swift index 121a279..ca67787 100644 --- a/Source/Features/Store/NotificationStore.swift +++ b/Source/Features/Store/NotificationStore.swift @@ -40,10 +40,10 @@ public class NotificationStore: Collection, StoreRealTimeObserver { public private(set) var unseenCount: Int = 0 /// Whether there are more items or not when paginating forwards - public private(set) var hasNextPage = true + public private(set) var hasNextPage = false private let logger: Logger - private var nextPage: Int? + private var nextPage: Int = 1 init(predicate: StorePredicate, userQuery: UserQuery, @@ -166,13 +166,7 @@ public class NotificationStore: Collection, StoreRealTimeObserver { completion(.success([])) return } - let pagePredicate: StorePagePredicate = { - if let next = nextPage { - return StorePagePredicate(page: next, size: pageSize) - } else { - return StorePagePredicate(size: pageSize) - } - }() + let pagePredicate: StorePagePredicate = StorePagePredicate(page: nextPage, size: pageSize) fetchStorePageInteractor.execute(storePredicate: predicate, userQuery: userQuery, pagePredicate: pagePredicate) .then { storePage in self.configurePagination(storePage) @@ -298,7 +292,7 @@ public class NotificationStore: Collection, StoreRealTimeObserver { setTotalCount(0, notifyObservers: notifyChanges) setUnreadCount(0, notifyObservers: notifyChanges) setUnseenCount(0, notifyObservers: notifyChanges) - nextPage = nil + nextPage = 1 setHasNextPage(true) if notifyChanges {