Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
ControlCenter
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import Foundation
import Alamofire
enum ReadingListDeleteRecordResult {
case success(ReadingListRecordResponse)
case preconditionFailed(ReadingListResponse)
case notFound(ReadingListResponse)
case failure(ReadingListResponse)
case error(NSError)
}
enum ReadingListGetRecordResult {
case success(ReadingListRecordResponse)
case notModified(ReadingListResponse) // TODO Should really call this NotModified for clarity
case notFound(ReadingListResponse)
case failure(ReadingListResponse)
case error(NSError)
}
enum ReadingListGetAllRecordsResult {
case success(ReadingListRecordsResponse)
case notModified(ReadingListResponse) // TODO Should really call this NotModified for clarity
case failure(ReadingListResponse)
case error(NSError)
}
enum ReadingListPatchRecordResult {
}
enum ReadingListAddRecordResult {
case success(ReadingListRecordResponse)
case failure(ReadingListResponse)
case conflict(ReadingListResponse)
case error(NSError)
}
enum ReadingListBatchAddRecordsResult {
case success(ReadingListBatchRecordResponse)
case failure(ReadingListResponse)
case error(NSError)
}
private let ReadingListClientUnknownError = NSError(domain: "org.mozilla.ios.Fennec.ReadingListClient", code: -1, userInfo: nil)
class ReadingListClient {
var serviceURL: URL
var authenticator: ReadingListAuthenticator
var articlesURL: URL!
var articlesBaseURL: URL!
var batchURL: URL!
func getRecordWithGuid(_ guid: String, ifModifiedSince: ReadingListTimestamp?, completion: @escaping (ReadingListGetRecordResult) -> Void) {
if let url = URL(string: guid, relativeTo: articlesBaseURL) {
SessionManager.default.request(createRequest("GET", url, ifModifiedSince: ifModifiedSince)).responseJSON(options: [], completionHandler: { response -> Void in
if let json = response.result.value as? [String: Any], let response = response.response {
switch response.statusCode {
case 200:
completion(.success(ReadingListRecordResponse(response: response, json: json)!))
case 304:
completion(.notModified(ReadingListResponse(response: response, json: json)!))
case 404:
completion(.notFound(ReadingListResponse(response: response, json: json)!))
default:
completion(.failure(ReadingListResponse(response: response, json: json)!))
}
} else {
completion(.error(response.result.error as NSError? ?? ReadingListClientUnknownError))
}
})
} else {
// TODO ???
}
}
func getRecordWithGuid(_ guid: String, completion: @escaping (ReadingListGetRecordResult) -> Void) {
getRecordWithGuid(guid, ifModifiedSince: nil, completion: completion)
}
func getAllRecordsWithFetchSpec(_ fetchSpec: ReadingListFetchSpec, ifModifiedSince: ReadingListTimestamp?, completion: @escaping (ReadingListGetAllRecordsResult) -> Void) {
if let url = fetchSpec.getURL(serviceURL: serviceURL, path: "/v1/articles") {
SessionManager.default.request(createRequest("GET", url)).responseJSON(options: [], completionHandler: { response -> Void in
if let json = response.result.value as? [String: Any], let response = response.response {
switch response.statusCode {
case 200:
completion(.success(ReadingListRecordsResponse(response: response, json: json)!))
case 304:
completion(.notModified(ReadingListResponse(response: response, json: json)!))
default:
completion(.failure(ReadingListResponse(response: response, json: json)!))
}
} else {
completion(.error(response.result.error as NSError? ?? ReadingListClientUnknownError))
}
})
} else {
// TODO ???
}
}
func getAllRecordsWithFetchSpec(_ fetchSpec: ReadingListFetchSpec, completion: @escaping (ReadingListGetAllRecordsResult) -> Void) {
getAllRecordsWithFetchSpec(fetchSpec, ifModifiedSince: nil, completion: completion)
}
func patchRecord(_ record: ReadingListClientRecord, completion: (ReadingListPatchRecordResult) -> Void) {
}
func addRecord(_ record: ReadingListClientRecord, completion: @escaping (ReadingListAddRecordResult) -> Void) {
SessionManager.default.request(createRequest("POST", articlesURL, json: record.json)).responseJSON(options: [], completionHandler: { response in
if let json = response.result.value as? [String: Any], let response = response.response {
switch response.statusCode {
case 200, 201: // TODO Should we have different results for these? Do we care about 200 vs 201?
completion(.success(ReadingListRecordResponse(response: response, json: json)!))
case 303:
completion(.conflict(ReadingListResponse(response: response, json: json)!))
default:
completion(.failure(ReadingListResponse(response: response, json: json)!))
}
} else {
completion(.error(response.result.error as NSError? ?? ReadingListClientUnknownError))
}
})
}
/// Build the JSON body for POST /v1/batch { defaults: {}, request: [ {body: {} } ] }
fileprivate func recordsToBatchJSON(_ records: [ReadingListClientRecord]) -> AnyObject {
return [
"defaults": ["method": "POST", "path": "/v1/articles", "headers": ["Content-Type": "application/json"]],
"requests": records.map { ["body": $0.json] }
] as NSDictionary
}
func batchAddRecords(_ records: [ReadingListClientRecord], completion: @escaping (ReadingListBatchAddRecordsResult) -> Void) {
SessionManager.default.request(createRequest("POST", batchURL, json: recordsToBatchJSON(records))).responseJSON(options: [], completionHandler: { response in
if let json = response.result.value as? [String: Any], let response = response.response {
switch response.statusCode {
case 200:
completion(.success(ReadingListBatchRecordResponse(response: response, json: json)!))
default:
completion(.failure(ReadingListResponse(response: response, json: json)!))
}
} else {
completion(.error(response.result.error as NSError? ?? ReadingListClientUnknownError))
}
})
}
func deleteRecordWithGuid(_ guid: String, ifUnmodifiedSince: ReadingListTimestamp?, completion: @escaping (ReadingListDeleteRecordResult) -> Void) {
if let url = URL(string: guid, relativeTo: articlesBaseURL) {
SessionManager.default.request(createRequest("DELETE", url, ifUnmodifiedSince: ifUnmodifiedSince)).responseJSON(options: [], completionHandler: { response in
if let json = response.result.value as? [String: Any], let response = response.response {
switch response.statusCode {
case 200:
completion(.success(ReadingListRecordResponse(response: response, json: json)!))
case 412:
completion(.preconditionFailed(ReadingListResponse(response: response, json: json)!))
case 404:
completion(.notFound(ReadingListResponse(response: response, json: json)!))
default:
completion(.failure(ReadingListResponse(response: response, json: json)!))
}
} else {
completion(.error(response.result.error as NSError? ?? ReadingListClientUnknownError))
}
})
} else {
// TODO ???
}
}
func deleteRecordWithGuid(_ guid: String, completion: @escaping (ReadingListDeleteRecordResult) -> Void) {
deleteRecordWithGuid(guid, ifUnmodifiedSince: nil, completion: completion)
}
func createRequest(_ method: String, _ url: URL, ifUnmodifiedSince: ReadingListTimestamp? = nil, ifModifiedSince: ReadingListTimestamp? = nil, json: AnyObject? = nil) -> URLRequest {
let request = NSMutableURLRequest(url: url)
request.httpMethod = method
if let ifUnmodifiedSince = ifUnmodifiedSince {
request.setValue(String(ifUnmodifiedSince), forHTTPHeaderField: "If-Unmodified-Since")
}
if let ifModifiedSince = ifModifiedSince {
request.setValue(String(ifModifiedSince), forHTTPHeaderField: "If-Modified-Since")
}
for (headerField, value) in authenticator.headers {
request.setValue(value, forHTTPHeaderField: headerField)
}
request.addValue("application/json", forHTTPHeaderField: "Accept")
if let json = json {
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
do {
request.httpBody = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted)
} catch _ {
request.httpBody = nil
} // TODO Handle errors here
}
return request as URLRequest
}
init(serviceURL: URL, authenticator: ReadingListAuthenticator) {
self.serviceURL = serviceURL
self.authenticator = authenticator
self.articlesURL = URL(string: "/v1/articles", relativeTo: self.serviceURL)
self.articlesBaseURL = URL(string: "/v1/articles/", relativeTo: self.serviceURL)
self.batchURL = URL(string: "/v1/batch", relativeTo: self.serviceURL)
}
}