Merge pull request #1079 from kielgillard/master
Implements creating, updating and deleting Folders/Collections.
This commit is contained in:
commit
c4d7baaeb8
|
@ -234,6 +234,141 @@ final class FeedlyAPICaller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createCollection(named label: String, completionHandler: @escaping (Result<FeedlyCollection, Error>) -> ()) {
|
||||||
|
guard let accessToken = credentials?.secret else {
|
||||||
|
return DispatchQueue.main.async {
|
||||||
|
completionHandler(.failure(CredentialsError.incompleteCredentials))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var components = baseUrlComponents
|
||||||
|
components.path = "/v3/collections"
|
||||||
|
|
||||||
|
guard let url = components.url else {
|
||||||
|
fatalError("\(components) does not produce a valid URL.")
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = URLRequest(url: url)
|
||||||
|
request.httpMethod = "POST"
|
||||||
|
request.addValue("application/json", forHTTPHeaderField: HTTPRequestHeader.contentType)
|
||||||
|
request.addValue("application/json", forHTTPHeaderField: "Accept-Type")
|
||||||
|
request.addValue("OAuth \(accessToken)", forHTTPHeaderField: HTTPRequestHeader.authorization)
|
||||||
|
|
||||||
|
do {
|
||||||
|
struct CreateCollectionBody: Encodable {
|
||||||
|
var label: String
|
||||||
|
}
|
||||||
|
let encoder = JSONEncoder()
|
||||||
|
let data = try encoder.encode(CreateCollectionBody(label: label))
|
||||||
|
request.httpBody = data
|
||||||
|
} catch {
|
||||||
|
return DispatchQueue.main.async {
|
||||||
|
completionHandler(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transport.send(request: request, resultType: [FeedlyCollection].self, dateDecoding: .millisecondsSince1970, keyDecoding: .convertFromSnakeCase) { result in
|
||||||
|
switch result {
|
||||||
|
case .success(let (httpResponse, collections)):
|
||||||
|
if httpResponse.statusCode == 200, let collection = collections?.first {
|
||||||
|
completionHandler(.success(collection))
|
||||||
|
} else {
|
||||||
|
completionHandler(.failure(URLError(.cannotDecodeContentData)))
|
||||||
|
}
|
||||||
|
case .failure(let error):
|
||||||
|
completionHandler(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func renameCollection(with id: String, to name: String, completionHandler: @escaping (Result<FeedlyCollection, Error>) -> ()) {
|
||||||
|
guard let accessToken = credentials?.secret else {
|
||||||
|
return DispatchQueue.main.async {
|
||||||
|
completionHandler(.failure(CredentialsError.incompleteCredentials))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var components = baseUrlComponents
|
||||||
|
components.path = "/v3/collections"
|
||||||
|
|
||||||
|
guard let url = components.url else {
|
||||||
|
fatalError("\(components) does not produce a valid URL.")
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = URLRequest(url: url)
|
||||||
|
request.httpMethod = "POST"
|
||||||
|
request.addValue("application/json", forHTTPHeaderField: HTTPRequestHeader.contentType)
|
||||||
|
request.addValue("application/json", forHTTPHeaderField: "Accept-Type")
|
||||||
|
request.addValue("OAuth \(accessToken)", forHTTPHeaderField: HTTPRequestHeader.authorization)
|
||||||
|
|
||||||
|
do {
|
||||||
|
struct RenameCollectionBody: Encodable {
|
||||||
|
var id: String
|
||||||
|
var label: String
|
||||||
|
}
|
||||||
|
let encoder = JSONEncoder()
|
||||||
|
let data = try encoder.encode(RenameCollectionBody(id: id, label: name))
|
||||||
|
request.httpBody = data
|
||||||
|
} catch {
|
||||||
|
return DispatchQueue.main.async {
|
||||||
|
completionHandler(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transport.send(request: request, resultType: [FeedlyCollection].self, dateDecoding: .millisecondsSince1970, keyDecoding: .convertFromSnakeCase) { result in
|
||||||
|
switch result {
|
||||||
|
case .success(let (httpResponse, collections)):
|
||||||
|
if httpResponse.statusCode == 200, let collection = collections?.first {
|
||||||
|
completionHandler(.success(collection))
|
||||||
|
} else {
|
||||||
|
completionHandler(.failure(URLError(.cannotDecodeContentData)))
|
||||||
|
}
|
||||||
|
case .failure(let error):
|
||||||
|
completionHandler(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func encodeForURLPath(_ pathComponent: String) -> String? {
|
||||||
|
return pathComponent.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteCollection(with id: String, completionHandler: @escaping (Result<Void, Error>) -> ()) {
|
||||||
|
guard let accessToken = credentials?.secret else {
|
||||||
|
return DispatchQueue.main.async {
|
||||||
|
completionHandler(.failure(CredentialsError.incompleteCredentials))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
guard let encodedId = encodeForURLPath(id) else {
|
||||||
|
return DispatchQueue.main.async {
|
||||||
|
completionHandler(.failure(FeedbinAccountDelegateError.invalidParameter))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var components = baseUrlComponents
|
||||||
|
components.percentEncodedPath = "/v3/collections/\(encodedId)"
|
||||||
|
|
||||||
|
guard let url = components.url else {
|
||||||
|
fatalError("\(components) does not produce a valid URL.")
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = URLRequest(url: url)
|
||||||
|
request.httpMethod = "DELETE"
|
||||||
|
request.addValue("application/json", forHTTPHeaderField: HTTPRequestHeader.contentType)
|
||||||
|
request.addValue("application/json", forHTTPHeaderField: "Accept-Type")
|
||||||
|
request.addValue("OAuth \(accessToken)", forHTTPHeaderField: HTTPRequestHeader.authorization)
|
||||||
|
|
||||||
|
transport.send(request: request, resultType: String.self, dateDecoding: .millisecondsSince1970, keyDecoding: .convertFromSnakeCase) { result in
|
||||||
|
switch result {
|
||||||
|
case .success(let (httpResponse, _)):
|
||||||
|
if httpResponse.statusCode == 200 {
|
||||||
|
completionHandler(.success(()))
|
||||||
|
} else {
|
||||||
|
completionHandler(.failure(URLError(.cannotDecodeContentData)))
|
||||||
|
}
|
||||||
|
case .failure(let error):
|
||||||
|
completionHandler(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension FeedlyAPICaller: OAuthAuthorizationCodeGrantRequesting {
|
extension FeedlyAPICaller: OAuthAuthorizationCodeGrantRequesting {
|
||||||
|
|
|
@ -149,15 +149,51 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
func addFolder(for account: Account, name: String, completion: @escaping (Result<Folder, Error>) -> Void) {
|
func addFolder(for account: Account, name: String, completion: @escaping (Result<Folder, Error>) -> Void) {
|
||||||
fatalError()
|
caller.createCollection(named: name) { result in
|
||||||
|
switch result {
|
||||||
|
case .success(let collection):
|
||||||
|
if let folder = account.ensureFolder(with: collection.label) {
|
||||||
|
folder.externalID = collection.id
|
||||||
|
completion(.success(folder))
|
||||||
|
} else {
|
||||||
|
completion(.failure(FeedbinAccountDelegateError.invalidParameter))
|
||||||
|
}
|
||||||
|
case .failure(let error):
|
||||||
|
completion(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
|
func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
fatalError()
|
guard let id = folder.externalID else {
|
||||||
|
completion(.failure(FeedbinAccountDelegateError.invalidParameter))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
caller.renameCollection(with: id, to: name) { result in
|
||||||
|
switch result {
|
||||||
|
case .success(let collection):
|
||||||
|
folder.name = collection.label
|
||||||
|
completion(.success(()))
|
||||||
|
case .failure(let error):
|
||||||
|
completion(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
|
func removeFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||||
fatalError()
|
guard let id = folder.externalID else {
|
||||||
|
completion(.failure(FeedbinAccountDelegateError.invalidParameter))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
caller.deleteCollection(with: id) { result in
|
||||||
|
switch result {
|
||||||
|
case .success:
|
||||||
|
account.removeFolder(folder)
|
||||||
|
completion(.success(()))
|
||||||
|
case .failure(let error):
|
||||||
|
completion(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFeed(for account: Account, url: String, name: String?, container: Container, completion: @escaping (Result<Feed, Error>) -> Void) {
|
func createFeed(for account: Account, url: String, name: String?, container: Container, completion: @escaping (Result<Feed, Error>) -> Void) {
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct FeedlyCollection: Decodable {
|
struct FeedlyCollection: Codable {
|
||||||
var feeds: [FeedlyFeed]
|
var feeds: [FeedlyFeed]
|
||||||
var label: String
|
var label: String
|
||||||
var id: String
|
var id: String
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct FeedlyFeed: Decodable {
|
struct FeedlyFeed: Codable {
|
||||||
var feedId: String
|
var feedId: String
|
||||||
var id: String
|
var id: String
|
||||||
var title: String
|
var title: String
|
||||||
|
|
Loading…
Reference in New Issue