Fetch rules

This commit is contained in:
Jackson Harper
2023-12-19 21:08:03 +08:00
parent 8d47b67e15
commit ddcbb7835f
4 changed files with 334 additions and 16 deletions

View File

@ -23,6 +23,7 @@ typealias OperationStatusHandler = (_: OperationStatus) -> Void
@Published var isLoading = true
@Published var feeds = [Subscription]()
@Published var newsletters = [Subscription]()
@Published var rules = [Rule]()
@Published var hasNetworkError = false
@Published var subscriptionNameToCancel: String?
@Published var presentingSubscription: Subscription?
@ -42,6 +43,9 @@ typealias OperationStatusHandler = (_: OperationStatus) -> Void
hasNetworkError = true
}
// Also try to get the rules for auto labeling
rules = (try? await dataService.rules()) ?? []
isLoading = false
}
@ -86,6 +90,21 @@ typealias OperationStatusHandler = (_: OperationStatus) -> Void
operationStatus = .failure
}
}
func setLabelsRule(dataService: DataService, ruleName: String, filter: String, labelIDs: [String]) async {
async {
operationMessage = "Creating label rule..."
operationStatus = .isPerforming
do {
try await dataService.createAddLabelsRule(name: ruleName, filter: filter, labelIDs: labelIDs)
operationMessage = "Rule created"
operationStatus = .success
} catch {
operationMessage = "Failed to create label rule"
operationStatus = .failure
}
}
}
}
struct OperationToast: View {
@ -362,6 +381,20 @@ struct SubscriptionSettingsView: View {
@Environment(\.dismiss) private var dismiss
var ruleName: String {
if let url = subscription.url, subscription.type == .newsletter {
return "system.autoLabel.(\(url))"
}
return "system.autoLabel.(\(subscription.name))"
}
var ruleFilter: String {
if let url = subscription.url, subscription.type == .newsletter {
return "rss:\"\(url)\""
}
return "subscription:\"\(subscription.name)\""
}
var folderRow: some View {
HStack {
Picker("Destination Folder", selection: $folderSelection) {
@ -395,6 +428,7 @@ struct SubscriptionSettingsView: View {
Text("Add Labels")
Spacer()
Button(action: { showLabelsSelector = true }, label: {
let rule = viewModel.rules.first { $0.name == ruleName }
Text("[none]")
})
}
@ -451,10 +485,18 @@ struct SubscriptionSettingsView: View {
}
.sheet(isPresented: $showLabelsSelector) {
ApplyLabelsView(mode: .list([]), onSave: { labels in
print("APPLIED LABELSL: ", labels)
// showLabelsModal = false
// item.labels = NSSet(array: labels)
// readerSettingsChangedTransactionID = UUID()
Task {
viewModel.showOperationToast = true
await viewModel.setLabelsRule(
dataService: dataService,
ruleName: ruleName,
filter: ruleFilter,
labelIDs: labels.map(\.unwrappedID)
)
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1500)) {
viewModel.showOperationToast = false
}
}
})
}
}

View File

@ -6652,6 +6652,136 @@ extension Selection where TypeLock == Never, Type == Never {
typealias FeedsSuccess<T> = Selection<T, Objects.FeedsSuccess>
}
extension Objects {
struct FetchContentError {
let __typename: TypeName = .fetchContentError
let errorCodes: [String: [Enums.FetchContentErrorCode]]
enum TypeName: String, Codable {
case fetchContentError = "FetchContentError"
}
}
}
extension Objects.FetchContentError: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
var map = HashMap()
for codingKey in container.allKeys {
if codingKey.isTypenameKey { continue }
let alias = codingKey.stringValue
let field = GraphQLField.getFieldNameFromAlias(alias)
switch field {
case "errorCodes":
if let value = try container.decode([Enums.FetchContentErrorCode]?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
}
default:
throw DecodingError.dataCorrupted(
DecodingError.Context(
codingPath: decoder.codingPath,
debugDescription: "Unknown key \(field)."
)
)
}
}
errorCodes = map["errorCodes"]
}
}
extension Fields where TypeLock == Objects.FetchContentError {
func errorCodes() throws -> [Enums.FetchContentErrorCode] {
let field = GraphQLField.leaf(
name: "errorCodes",
arguments: []
)
select(field)
switch response {
case let .decoding(data):
if let data = data.errorCodes[field.alias!] {
return data
}
throw HttpError.badpayload
case .mocking:
return []
}
}
}
extension Selection where TypeLock == Never, Type == Never {
typealias FetchContentError<T> = Selection<T, Objects.FetchContentError>
}
extension Objects {
struct FetchContentSuccess {
let __typename: TypeName = .fetchContentSuccess
let success: [String: Bool]
enum TypeName: String, Codable {
case fetchContentSuccess = "FetchContentSuccess"
}
}
}
extension Objects.FetchContentSuccess: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
var map = HashMap()
for codingKey in container.allKeys {
if codingKey.isTypenameKey { continue }
let alias = codingKey.stringValue
let field = GraphQLField.getFieldNameFromAlias(alias)
switch field {
case "success":
if let value = try container.decode(Bool?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
}
default:
throw DecodingError.dataCorrupted(
DecodingError.Context(
codingPath: decoder.codingPath,
debugDescription: "Unknown key \(field)."
)
)
}
}
success = map["success"]
}
}
extension Fields where TypeLock == Objects.FetchContentSuccess {
func success() throws -> Bool {
let field = GraphQLField.leaf(
name: "success",
arguments: []
)
select(field)
switch response {
case let .decoding(data):
if let data = data.success[field.alias!] {
return data
}
throw HttpError.badpayload
case .mocking:
return Bool.mockValue
}
}
}
extension Selection where TypeLock == Never, Type == Never {
typealias FetchContentSuccess<T> = Selection<T, Objects.FetchContentSuccess>
}
extension Objects {
struct Filter {
let __typename: TypeName = .filter
@ -11112,6 +11242,7 @@ extension Objects {
let deleteNewsletterEmail: [String: Unions.DeleteNewsletterEmailResult]
let deleteRule: [String: Unions.DeleteRuleResult]
let deleteWebhook: [String: Unions.DeleteWebhookResult]
let fetchContent: [String: Unions.FetchContentResult]
let generateApiKey: [String: Unions.GenerateApiKeyResult]
let googleLogin: [String: Unions.LoginResult]
let googleSignup: [String: Unions.GoogleSignupResult]
@ -11239,6 +11370,10 @@ extension Objects.Mutation: Decodable {
if let value = try container.decode(Unions.DeleteWebhookResult?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
}
case "fetchContent":
if let value = try container.decode(Unions.FetchContentResult?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
}
case "generateApiKey":
if let value = try container.decode(Unions.GenerateApiKeyResult?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
@ -11441,6 +11576,7 @@ extension Objects.Mutation: Decodable {
deleteNewsletterEmail = map["deleteNewsletterEmail"]
deleteRule = map["deleteRule"]
deleteWebhook = map["deleteWebhook"]
fetchContent = map["fetchContent"]
generateApiKey = map["generateApiKey"]
googleLogin = map["googleLogin"]
googleSignup = map["googleSignup"]
@ -11793,6 +11929,25 @@ extension Fields where TypeLock == Objects.Mutation {
}
}
func fetchContent<Type>(id: String, selection: Selection<Type, Unions.FetchContentResult>) throws -> Type {
let field = GraphQLField.composite(
name: "fetchContent",
arguments: [Argument(name: "id", type: "ID!", value: id)],
selection: selection.selection
)
select(field)
switch response {
case let .decoding(data):
if let data = data.fetchContent[field.alias!] {
return try selection.decode(data: data)
}
throw HttpError.badpayload
case .mocking:
return selection.mock()
}
}
func generateApiKey<Type>(input: InputObjects.GenerateApiKeyInput, selection: Selection<Type, Unions.GenerateApiKeyResult>) throws -> Type {
let field = GraphQLField.composite(
name: "generateApiKey",
@ -27864,6 +28019,80 @@ extension Selection where TypeLock == Never, Type == Never {
typealias FeedsResult<T> = Selection<T, Unions.FeedsResult>
}
extension Unions {
struct FetchContentResult {
let __typename: TypeName
let errorCodes: [String: [Enums.FetchContentErrorCode]]
let success: [String: Bool]
enum TypeName: String, Codable {
case fetchContentError = "FetchContentError"
case fetchContentSuccess = "FetchContentSuccess"
}
}
}
extension Unions.FetchContentResult: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: DynamicCodingKeys.self)
var map = HashMap()
for codingKey in container.allKeys {
if codingKey.isTypenameKey { continue }
let alias = codingKey.stringValue
let field = GraphQLField.getFieldNameFromAlias(alias)
switch field {
case "errorCodes":
if let value = try container.decode([Enums.FetchContentErrorCode]?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
}
case "success":
if let value = try container.decode(Bool?.self, forKey: codingKey) {
map.set(key: field, hash: alias, value: value as Any)
}
default:
throw DecodingError.dataCorrupted(
DecodingError.Context(
codingPath: decoder.codingPath,
debugDescription: "Unknown key \(field)."
)
)
}
}
__typename = try container.decode(TypeName.self, forKey: DynamicCodingKeys(stringValue: "__typename")!)
errorCodes = map["errorCodes"]
success = map["success"]
}
}
extension Fields where TypeLock == Unions.FetchContentResult {
func on<Type>(fetchContentError: Selection<Type, Objects.FetchContentError>, fetchContentSuccess: Selection<Type, Objects.FetchContentSuccess>) throws -> Type {
select([GraphQLField.fragment(type: "FetchContentError", selection: fetchContentError.selection), GraphQLField.fragment(type: "FetchContentSuccess", selection: fetchContentSuccess.selection)])
switch response {
case let .decoding(data):
switch data.__typename {
case .fetchContentError:
let data = Objects.FetchContentError(errorCodes: data.errorCodes)
return try fetchContentError.decode(data: data)
case .fetchContentSuccess:
let data = Objects.FetchContentSuccess(success: data.success)
return try fetchContentSuccess.decode(data: data)
}
case .mocking:
return fetchContentError.mock()
}
}
}
extension Selection where TypeLock == Never, Type == Never {
typealias FetchContentResult<T> = Selection<T, Unions.FetchContentResult>
}
extension Unions {
struct FiltersResult {
let __typename: TypeName
@ -33101,6 +33330,8 @@ extension Enums {
enum ArticleSavingRequestStatus: String, CaseIterable, Codable {
case archived = "ARCHIVED"
case contentNotFetched = "CONTENT_NOT_FETCHED"
case deleted = "DELETED"
case failed = "FAILED"
@ -33414,6 +33645,15 @@ extension Enums {
}
}
extension Enums {
/// FetchContentErrorCode
enum FetchContentErrorCode: String, CaseIterable, Codable {
case badRequest = "BAD_REQUEST"
case unauthorized = "UNAUTHORIZED"
}
}
extension Enums {
/// FiltersErrorCode
enum FiltersErrorCode: String, CaseIterable, Codable {

View File

@ -3,19 +3,9 @@ import Foundation
import Models
import SwiftGraphQL
// input SetRuleInput {
// id: ID
// name: String!
// description: String
// filter: String!
// actions: [RuleActionInput!]!
// enabled: Boolean!
// eventTypes: [RuleEventType!]!
// }
public struct Rule {
let id: String
let name: String
public let id: String
public let name: String
}
let ruleSelection = Selection.Rule {

View File

@ -0,0 +1,46 @@
import Foundation
import Models
import SwiftGraphQL
public extension DataService {
func rules() async throws -> [Rule] {
enum QueryResult {
case success(result: [Rule])
case error(error: String)
}
let selection = Selection<QueryResult, Unions.RulesResult> {
try $0.on(
rulesError: .init {
QueryResult.error(error: try $0.errorCodes().description)
},
rulesSuccess: .init {
QueryResult.success(result: try $0.rules(selection: ruleSelection.list))
}
)
}
let query = Selection.Query {
try $0.rules(selection: selection)
}
let path = appEnvironment.graphqlPath
let headers = networker.defaultHeaders
return try await withCheckedThrowingContinuation { continuation in
send(query, to: path, headers: headers) { queryResult in
guard let payload = try? queryResult.get() else {
continuation.resume(throwing: BasicError.message(messageText: "network request failed"))
return
}
switch payload.data {
case let .success(result: result):
continuation.resume(returning: result)
case .error:
continuation.resume(throwing: BasicError.message(messageText: "Rules fetch error"))
}
}
}
}
}