diff --git a/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift b/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift index e73aa03f8..10ecb95af 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift @@ -107,6 +107,12 @@ struct ProfileView: View { } #endif + Section { + NavigationLink(destination: GroupsView()) { + Text("Recommendation Groups") + } + } + Section { NavigationLink( destination: BasicWebAppView.privacyPolicyWebView(baseURL: dataService.appEnvironment.webAppBaseURL) diff --git a/apple/OmnivoreKit/Sources/App/Views/Profile/RecommendationGroupView.swift b/apple/OmnivoreKit/Sources/App/Views/Profile/RecommendationGroupView.swift new file mode 100644 index 000000000..e4bdcba4e --- /dev/null +++ b/apple/OmnivoreKit/Sources/App/Views/Profile/RecommendationGroupView.swift @@ -0,0 +1,74 @@ +import Models +import Services +import SwiftUI +import Views + +@MainActor final class RecommendationsGroupViewModel: ObservableObject { + @Published var isLoading = false + @Published var networkError = true + @Published var recommendationGroup: InternalRecommendationGroup + + init(recommendationGroup: InternalRecommendationGroup) { + self.recommendationGroup = recommendationGroup + } + + func loadGroups(dataService _: DataService) async { + isLoading = true + +// do { +// recommendationGroups = try await dataService.recommendationGroups() +// } catch { +// networkError = true +// } + + isLoading = false + } +} + +struct RecommendationGroupView: View { + @EnvironmentObject var dataService: DataService + @StateObject var viewModel: RecommendationsGroupViewModel + + var body: some View { + Group { + #if os(iOS) + Form { + innerBody + } + #elseif os(macOS) + List { + innerBody + } + .listStyle(InsetListStyle()) + #endif + } + .task { await viewModel.loadGroups(dataService: dataService) } + } + + private var innerBody: some View { + Group { + Section("Name") { + Text(viewModel.recommendationGroup.name) + } + + Section("Invite Link") { + Button(action: { + #if os(iOS) + UIPasteboard.general.string = viewModel.recommendationGroup.inviteUrl + #endif + + #if os(macOS) + let pasteBoard = NSPasteboard.general + pasteBoard.clearContents() + pasteBoard.writeObjects([viewModel.recommendationGroup.inviteUrl as NSString]) + #endif + + Snackbar.show(message: "Invite link copied") + }, label: { + Text("[\(viewModel.recommendationGroup.inviteUrl)](\(viewModel.recommendationGroup.inviteUrl))") + }) + } + } + .navigationTitle(viewModel.recommendationGroup.name) + } +} diff --git a/apple/OmnivoreKit/Sources/App/Views/Profile/RecommendationGroupsView.swift b/apple/OmnivoreKit/Sources/App/Views/Profile/RecommendationGroupsView.swift new file mode 100644 index 000000000..570072b41 --- /dev/null +++ b/apple/OmnivoreKit/Sources/App/Views/Profile/RecommendationGroupsView.swift @@ -0,0 +1,145 @@ +import Models +import Services +import SwiftUI +import Views + +@MainActor final class RecommendationsGroupsViewModel: ObservableObject { + @Published var isLoading = false + @Published var isCreating = false + @Published var networkError = true + @Published var recommendationGroups = [InternalRecommendationGroup]() + + @Published var showCreateSheet = false + + @Published var showCreateError = false + @Published var createGroupError: String? + + func loadGroups(dataService: DataService) async { + isLoading = true + + do { + recommendationGroups = try await dataService.recommendationGroups() + } catch { + networkError = true + } + + isLoading = false + } + + func createGroup(dataService: DataService, name: String) async { + isCreating = true + + if let group = try? await dataService.createRecommendationGroup(name: name) { + print("CREATED GROUP: ", group) + await loadGroups(dataService: dataService) + showCreateSheet = false + } else { + createGroupError = "Error creating group" + showCreateError = true + } + + isCreating = false + } +} + +struct CreateRecommendationGroupView: View { + @State var name = "" + @EnvironmentObject var dataService: DataService + @StateObject var viewModel = RecommendationsGroupsViewModel() + + var nextButton: some View { + if viewModel.isCreating { + return AnyView(ProgressView()) + } else { + return AnyView(Button(action: { + Task { + await viewModel.createGroup(dataService: dataService, name: self.name) + } + }, label: { + Text("Next") + }) + .disabled(name.isEmpty) + ) + } + } + + var body: some View { + NavigationView { + Form { + TextField("Name", text: $name, prompt: Text("Group Name")) + } + .alert(isPresented: $viewModel.showCreateError) { + Alert( + title: Text(viewModel.createGroupError ?? "Error creating group"), + dismissButton: .cancel(Text("Ok")) { + viewModel.createGroupError = nil + viewModel.showCreateError = false + } + ) + } + } + .navigationViewStyle(.stack) + .navigationTitle("Create Group") + .navigationBarTitleDisplayMode(.inline) + .navigationBarItems(leading: + Button(action: { + viewModel.showCreateSheet = false + }, label: { Text("Cancel") }), + trailing: nextButton) + } +} + +struct GroupsView: View { + @EnvironmentObject var dataService: DataService + @StateObject var viewModel = RecommendationsGroupsViewModel() + + var body: some View { + Group { + #if os(iOS) + Form { + innerBody + } + #elseif os(macOS) + List { + innerBody + } + .listStyle(InsetListStyle()) + #endif + } + .sheet(isPresented: $viewModel.showCreateSheet) { + NavigationView { + CreateRecommendationGroupView(viewModel: self.viewModel) + } + } + .task { await viewModel.loadGroups(dataService: dataService) } + } + + private var innerBody: some View { + Group { + Section { + Button( + action: { viewModel.showCreateSheet = true }, + label: { + HStack { + Image(systemName: "plus.circle.fill").foregroundColor(.green) + Text("Create a new group") + Spacer() + } + } + ) + .disabled(viewModel.isLoading) + } + + Section(header: Text("Your recommendation groups")) { + ForEach(viewModel.recommendationGroups) { recommendationGroup in + NavigationLink( + destination: RecommendationGroupView(viewModel: RecommendationsGroupViewModel(recommendationGroup: recommendationGroup)) + ) { + Text(recommendationGroup.name) + } + } + } + } + .navigationTitle("Recommendation Groups") + } +} diff --git a/apple/OmnivoreKit/Sources/Models/CoreData/CoreDataModel.xcdatamodeld/CoreDataModel.xcdatamodel/contents b/apple/OmnivoreKit/Sources/Models/CoreData/CoreDataModel.xcdatamodeld/CoreDataModel.xcdatamodel/contents index ace07414f..4b026f83e 100644 --- a/apple/OmnivoreKit/Sources/Models/CoreData/CoreDataModel.xcdatamodeld/CoreDataModel.xcdatamodel/contents +++ b/apple/OmnivoreKit/Sources/Models/CoreData/CoreDataModel.xcdatamodeld/CoreDataModel.xcdatamodel/contents @@ -91,6 +91,13 @@ + + + + + + + diff --git a/apple/OmnivoreKit/Sources/Services/DataService/GQLSchema.swift b/apple/OmnivoreKit/Sources/Services/DataService/GQLSchema.swift index 2b4d961e2..c5554e066 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/GQLSchema.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/GQLSchema.swift @@ -2596,6 +2596,137 @@ extension Selection where TypeLock == Never, Type == Never { typealias CreateArticleSuccess = Selection } +extension Objects { + struct CreateGroupError { + let __typename: TypeName = .createGroupError + let errorCodes: [String: [Enums.CreateGroupErrorCode]] + + enum TypeName: String, Codable { + case createGroupError = "CreateGroupError" + } + } +} + +extension Objects.CreateGroupError: 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.CreateGroupErrorCode]?.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.CreateGroupError { + func errorCodes() throws -> [Enums.CreateGroupErrorCode] { + 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 CreateGroupError = Selection +} + +extension Objects { + struct CreateGroupSuccess { + let __typename: TypeName = .createGroupSuccess + let group: [String: Objects.RecommendationGroup] + + enum TypeName: String, Codable { + case createGroupSuccess = "CreateGroupSuccess" + } + } +} + +extension Objects.CreateGroupSuccess: 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 "group": + if let value = try container.decode(Objects.RecommendationGroup?.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)." + ) + ) + } + } + + group = map["group"] + } +} + +extension Fields where TypeLock == Objects.CreateGroupSuccess { + func group(selection: Selection) throws -> Type { + let field = GraphQLField.composite( + name: "group", + arguments: [], + selection: selection.selection + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.group[field.alias!] { + return try selection.decode(data: data) + } + throw HttpError.badpayload + case .mocking: + return selection.mock() + } + } +} + +extension Selection where TypeLock == Never, Type == Never { + typealias CreateGroupSuccess = Selection +} + extension Objects { struct CreateHighlightError { let __typename: TypeName = .createHighlightError @@ -3512,6 +3643,137 @@ extension Selection where TypeLock == Never, Type == Never { typealias DeleteAccountSuccess = Selection } +extension Objects { + struct DeleteFilterError { + let __typename: TypeName = .deleteFilterError + let errorCodes: [String: [Enums.DeleteFilterErrorCode]] + + enum TypeName: String, Codable { + case deleteFilterError = "DeleteFilterError" + } + } +} + +extension Objects.DeleteFilterError: 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.DeleteFilterErrorCode]?.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.DeleteFilterError { + func errorCodes() throws -> [Enums.DeleteFilterErrorCode] { + 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 DeleteFilterError = Selection +} + +extension Objects { + struct DeleteFilterSuccess { + let __typename: TypeName = .deleteFilterSuccess + let filter: [String: Objects.Filter] + + enum TypeName: String, Codable { + case deleteFilterSuccess = "DeleteFilterSuccess" + } + } +} + +extension Objects.DeleteFilterSuccess: 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 "filter": + if let value = try container.decode(Objects.Filter?.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)." + ) + ) + } + } + + filter = map["filter"] + } +} + +extension Fields where TypeLock == Objects.DeleteFilterSuccess { + func filter(selection: Selection) throws -> Type { + let field = GraphQLField.composite( + name: "filter", + arguments: [], + selection: selection.selection + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.filter[field.alias!] { + return try selection.decode(data: data) + } + throw HttpError.badpayload + case .mocking: + return selection.mock() + } + } +} + +extension Selection where TypeLock == Never, Type == Never { + typealias DeleteFilterSuccess = Selection +} + extension Objects { struct DeleteHighlightError { let __typename: TypeName = .deleteHighlightError @@ -5654,6 +5916,343 @@ extension Selection where TypeLock == Never, Type == Never { typealias FeedArticlesSuccess = Selection } +extension Objects { + struct Filter { + let __typename: TypeName = .filter + let createdAt: [String: DateTime] + let description: [String: String] + let filter: [String: String] + let id: [String: String] + let name: [String: String] + let position: [String: Int] + let updatedAt: [String: DateTime] + + enum TypeName: String, Codable { + case filter = "Filter" + } + } +} + +extension Objects.Filter: 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 "createdAt": + if let value = try container.decode(DateTime?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "description": + if let value = try container.decode(String?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "filter": + if let value = try container.decode(String?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "id": + if let value = try container.decode(String?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "name": + if let value = try container.decode(String?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "position": + if let value = try container.decode(Int?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "updatedAt": + if let value = try container.decode(DateTime?.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)." + ) + ) + } + } + + createdAt = map["createdAt"] + description = map["description"] + filter = map["filter"] + id = map["id"] + name = map["name"] + position = map["position"] + updatedAt = map["updatedAt"] + } +} + +extension Fields where TypeLock == Objects.Filter { + func createdAt() throws -> DateTime { + let field = GraphQLField.leaf( + name: "createdAt", + arguments: [] + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.createdAt[field.alias!] { + return data + } + throw HttpError.badpayload + case .mocking: + return DateTime.mockValue + } + } + + func description() throws -> String? { + let field = GraphQLField.leaf( + name: "description", + arguments: [] + ) + select(field) + + switch response { + case let .decoding(data): + return data.description[field.alias!] + case .mocking: + return nil + } + } + + func filter() throws -> String { + let field = GraphQLField.leaf( + name: "filter", + arguments: [] + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.filter[field.alias!] { + return data + } + throw HttpError.badpayload + case .mocking: + return String.mockValue + } + } + + func id() throws -> String { + let field = GraphQLField.leaf( + name: "id", + arguments: [] + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.id[field.alias!] { + return data + } + throw HttpError.badpayload + case .mocking: + return String.mockValue + } + } + + func name() throws -> String { + let field = GraphQLField.leaf( + name: "name", + arguments: [] + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.name[field.alias!] { + return data + } + throw HttpError.badpayload + case .mocking: + return String.mockValue + } + } + + func position() throws -> Int { + let field = GraphQLField.leaf( + name: "position", + arguments: [] + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.position[field.alias!] { + return data + } + throw HttpError.badpayload + case .mocking: + return Int.mockValue + } + } + + func updatedAt() throws -> DateTime { + let field = GraphQLField.leaf( + name: "updatedAt", + arguments: [] + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.updatedAt[field.alias!] { + return data + } + throw HttpError.badpayload + case .mocking: + return DateTime.mockValue + } + } +} + +extension Selection where TypeLock == Never, Type == Never { + typealias Filter = Selection +} + +extension Objects { + struct FiltersError { + let __typename: TypeName = .filtersError + let errorCodes: [String: [Enums.FiltersErrorCode]] + + enum TypeName: String, Codable { + case filtersError = "FiltersError" + } + } +} + +extension Objects.FiltersError: 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.FiltersErrorCode]?.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.FiltersError { + func errorCodes() throws -> [Enums.FiltersErrorCode] { + 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 FiltersError = Selection +} + +extension Objects { + struct FiltersSuccess { + let __typename: TypeName = .filtersSuccess + let filters: [String: [Objects.Filter]] + + enum TypeName: String, Codable { + case filtersSuccess = "FiltersSuccess" + } + } +} + +extension Objects.FiltersSuccess: 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 "filters": + if let value = try container.decode([Objects.Filter]?.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)." + ) + ) + } + } + + filters = map["filters"] + } +} + +extension Fields where TypeLock == Objects.FiltersSuccess { + func filters(selection: Selection) throws -> Type { + let field = GraphQLField.composite( + name: "filters", + arguments: [], + selection: selection.selection + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.filters[field.alias!] { + return try selection.decode(data: data) + } + throw HttpError.badpayload + case .mocking: + return selection.mock() + } + } +} + +extension Selection where TypeLock == Never, Type == Never { + typealias FiltersSuccess = Selection +} + extension Objects { struct GenerateApiKeyError { let __typename: TypeName = .generateApiKeyError @@ -6306,6 +6905,137 @@ extension Selection where TypeLock == Never, Type == Never { typealias GoogleSignupSuccess = Selection } +extension Objects { + struct GroupsError { + let __typename: TypeName = .groupsError + let errorCodes: [String: [Enums.GroupsErrorCode]] + + enum TypeName: String, Codable { + case groupsError = "GroupsError" + } + } +} + +extension Objects.GroupsError: 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.GroupsErrorCode]?.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.GroupsError { + func errorCodes() throws -> [Enums.GroupsErrorCode] { + 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 GroupsError = Selection +} + +extension Objects { + struct GroupsSuccess { + let __typename: TypeName = .groupsSuccess + let groups: [String: [Objects.RecommendationGroup]] + + enum TypeName: String, Codable { + case groupsSuccess = "GroupsSuccess" + } + } +} + +extension Objects.GroupsSuccess: 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 "groups": + if let value = try container.decode([Objects.RecommendationGroup]?.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)." + ) + ) + } + } + + groups = map["groups"] + } +} + +extension Fields where TypeLock == Objects.GroupsSuccess { + func groups(selection: Selection) throws -> Type { + let field = GraphQLField.composite( + name: "groups", + arguments: [], + selection: selection.selection + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.groups[field.alias!] { + return try selection.decode(data: data) + } + throw HttpError.badpayload + case .mocking: + return selection.mock() + } + } +} + +extension Selection where TypeLock == Never, Type == Never { + typealias GroupsSuccess = Selection +} + extension Objects { struct Highlight { let __typename: TypeName = .highlight @@ -8498,6 +9228,137 @@ extension Selection where TypeLock == Never, Type == Never { typealias MergeHighlightSuccess = Selection } +extension Objects { + struct MoveFilterError { + let __typename: TypeName = .moveFilterError + let errorCodes: [String: [Enums.MoveFilterErrorCode]] + + enum TypeName: String, Codable { + case moveFilterError = "MoveFilterError" + } + } +} + +extension Objects.MoveFilterError: 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.MoveFilterErrorCode]?.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.MoveFilterError { + func errorCodes() throws -> [Enums.MoveFilterErrorCode] { + 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 MoveFilterError = Selection +} + +extension Objects { + struct MoveFilterSuccess { + let __typename: TypeName = .moveFilterSuccess + let filter: [String: Objects.Filter] + + enum TypeName: String, Codable { + case moveFilterSuccess = "MoveFilterSuccess" + } + } +} + +extension Objects.MoveFilterSuccess: 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 "filter": + if let value = try container.decode(Objects.Filter?.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)." + ) + ) + } + } + + filter = map["filter"] + } +} + +extension Fields where TypeLock == Objects.MoveFilterSuccess { + func filter(selection: Selection) throws -> Type { + let field = GraphQLField.composite( + name: "filter", + arguments: [], + selection: selection.selection + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.filter[field.alias!] { + return try selection.decode(data: data) + } + throw HttpError.badpayload + case .mocking: + return selection.mock() + } + } +} + +extension Selection where TypeLock == Never, Type == Never { + typealias MoveFilterSuccess = Selection +} + extension Objects { struct MoveLabelError { let __typename: TypeName = .moveLabelError @@ -8635,6 +9496,7 @@ extension Objects { let addPopularRead: [String: Unions.AddPopularReadResult] let createArticle: [String: Unions.CreateArticleResult] let createArticleSavingRequest: [String: Unions.CreateArticleSavingRequestResult] + let createGroup: [String: Unions.CreateGroupResult] let createHighlight: [String: Unions.CreateHighlightResult] let createHighlightReply: [String: Unions.CreateHighlightReplyResult] let createLabel: [String: Unions.CreateLabelResult] @@ -8642,6 +9504,7 @@ extension Objects { let createReaction: [String: Unions.CreateReactionResult] let createReminder: [String: Unions.CreateReminderResult] let deleteAccount: [String: Unions.DeleteAccountResult] + let deleteFilter: [String: Unions.DeleteFilterResult] let deleteHighlight: [String: Unions.DeleteHighlightResult] let deleteHighlightReply: [String: Unions.DeleteHighlightReplyResult] let deleteIntegration: [String: Unions.DeleteIntegrationResult] @@ -8656,12 +9519,15 @@ extension Objects { let googleSignup: [String: Unions.GoogleSignupResult] let logOut: [String: Unions.LogOutResult] let mergeHighlight: [String: Unions.MergeHighlightResult] + let moveFilter: [String: Unions.MoveFilterResult] let moveLabel: [String: Unions.MoveLabelResult] let optInFeature: [String: Unions.OptInFeatureResult] + let recommend: [String: Unions.RecommendResult] let reportItem: [String: Objects.ReportItemResult] let revokeApiKey: [String: Unions.RevokeApiKeyResult] let saveArticleReadingProgress: [String: Unions.SaveArticleReadingProgressResult] let saveFile: [String: Unions.SaveResult] + let saveFilter: [String: Unions.SaveFilterResult] let savePage: [String: Unions.SaveResult] let saveUrl: [String: Unions.SaveResult] let setBookmarkArticle: [String: Unions.SetBookmarkArticleResult] @@ -8719,6 +9585,10 @@ extension Objects.Mutation: Decodable { if let value = try container.decode(Unions.CreateArticleSavingRequestResult?.self, forKey: codingKey) { map.set(key: field, hash: alias, value: value as Any) } + case "createGroup": + if let value = try container.decode(Unions.CreateGroupResult?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } case "createHighlight": if let value = try container.decode(Unions.CreateHighlightResult?.self, forKey: codingKey) { map.set(key: field, hash: alias, value: value as Any) @@ -8747,6 +9617,10 @@ extension Objects.Mutation: Decodable { if let value = try container.decode(Unions.DeleteAccountResult?.self, forKey: codingKey) { map.set(key: field, hash: alias, value: value as Any) } + case "deleteFilter": + if let value = try container.decode(Unions.DeleteFilterResult?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } case "deleteHighlight": if let value = try container.decode(Unions.DeleteHighlightResult?.self, forKey: codingKey) { map.set(key: field, hash: alias, value: value as Any) @@ -8803,6 +9677,10 @@ extension Objects.Mutation: Decodable { if let value = try container.decode(Unions.MergeHighlightResult?.self, forKey: codingKey) { map.set(key: field, hash: alias, value: value as Any) } + case "moveFilter": + if let value = try container.decode(Unions.MoveFilterResult?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } case "moveLabel": if let value = try container.decode(Unions.MoveLabelResult?.self, forKey: codingKey) { map.set(key: field, hash: alias, value: value as Any) @@ -8811,6 +9689,10 @@ extension Objects.Mutation: Decodable { if let value = try container.decode(Unions.OptInFeatureResult?.self, forKey: codingKey) { map.set(key: field, hash: alias, value: value as Any) } + case "recommend": + if let value = try container.decode(Unions.RecommendResult?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } case "reportItem": if let value = try container.decode(Objects.ReportItemResult?.self, forKey: codingKey) { map.set(key: field, hash: alias, value: value as Any) @@ -8827,6 +9709,10 @@ extension Objects.Mutation: Decodable { if let value = try container.decode(Unions.SaveResult?.self, forKey: codingKey) { map.set(key: field, hash: alias, value: value as Any) } + case "saveFilter": + if let value = try container.decode(Unions.SaveFilterResult?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } case "savePage": if let value = try container.decode(Unions.SaveResult?.self, forKey: codingKey) { map.set(key: field, hash: alias, value: value as Any) @@ -8944,6 +9830,7 @@ extension Objects.Mutation: Decodable { addPopularRead = map["addPopularRead"] createArticle = map["createArticle"] createArticleSavingRequest = map["createArticleSavingRequest"] + createGroup = map["createGroup"] createHighlight = map["createHighlight"] createHighlightReply = map["createHighlightReply"] createLabel = map["createLabel"] @@ -8951,6 +9838,7 @@ extension Objects.Mutation: Decodable { createReaction = map["createReaction"] createReminder = map["createReminder"] deleteAccount = map["deleteAccount"] + deleteFilter = map["deleteFilter"] deleteHighlight = map["deleteHighlight"] deleteHighlightReply = map["deleteHighlightReply"] deleteIntegration = map["deleteIntegration"] @@ -8965,12 +9853,15 @@ extension Objects.Mutation: Decodable { googleSignup = map["googleSignup"] logOut = map["logOut"] mergeHighlight = map["mergeHighlight"] + moveFilter = map["moveFilter"] moveLabel = map["moveLabel"] optInFeature = map["optInFeature"] + recommend = map["recommend"] reportItem = map["reportItem"] revokeApiKey = map["revokeApiKey"] saveArticleReadingProgress = map["saveArticleReadingProgress"] saveFile = map["saveFile"] + saveFilter = map["saveFilter"] savePage = map["savePage"] saveUrl = map["saveUrl"] setBookmarkArticle = map["setBookmarkArticle"] @@ -9058,6 +9949,25 @@ extension Fields where TypeLock == Objects.Mutation { } } + func createGroup(input: InputObjects.CreateGroupInput, selection: Selection) throws -> Type { + let field = GraphQLField.composite( + name: "createGroup", + arguments: [Argument(name: "input", type: "CreateGroupInput!", value: input)], + selection: selection.selection + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.createGroup[field.alias!] { + return try selection.decode(data: data) + } + throw HttpError.badpayload + case .mocking: + return selection.mock() + } + } + func createHighlight(input: InputObjects.CreateHighlightInput, selection: Selection) throws -> Type { let field = GraphQLField.composite( name: "createHighlight", @@ -9191,6 +10101,25 @@ extension Fields where TypeLock == Objects.Mutation { } } + func deleteFilter(id: String, selection: Selection) throws -> Type { + let field = GraphQLField.composite( + name: "deleteFilter", + arguments: [Argument(name: "id", type: "ID!", value: id)], + selection: selection.selection + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.deleteFilter[field.alias!] { + return try selection.decode(data: data) + } + throw HttpError.badpayload + case .mocking: + return selection.mock() + } + } + func deleteHighlight(highlightId: String, selection: Selection) throws -> Type { let field = GraphQLField.composite( name: "deleteHighlight", @@ -9457,6 +10386,25 @@ extension Fields where TypeLock == Objects.Mutation { } } + func moveFilter(input: InputObjects.MoveFilterInput, selection: Selection) throws -> Type { + let field = GraphQLField.composite( + name: "moveFilter", + arguments: [Argument(name: "input", type: "MoveFilterInput!", value: input)], + selection: selection.selection + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.moveFilter[field.alias!] { + return try selection.decode(data: data) + } + throw HttpError.badpayload + case .mocking: + return selection.mock() + } + } + func moveLabel(input: InputObjects.MoveLabelInput, selection: Selection) throws -> Type { let field = GraphQLField.composite( name: "moveLabel", @@ -9495,6 +10443,25 @@ extension Fields where TypeLock == Objects.Mutation { } } + func recommend(input: InputObjects.RecommendInput, selection: Selection) throws -> Type { + let field = GraphQLField.composite( + name: "recommend", + arguments: [Argument(name: "input", type: "RecommendInput!", value: input)], + selection: selection.selection + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.recommend[field.alias!] { + return try selection.decode(data: data) + } + throw HttpError.badpayload + case .mocking: + return selection.mock() + } + } + func reportItem(input: InputObjects.ReportItemInput, selection: Selection) throws -> Type { let field = GraphQLField.composite( name: "reportItem", @@ -9571,6 +10538,25 @@ extension Fields where TypeLock == Objects.Mutation { } } + func saveFilter(input: InputObjects.SaveFilterInput, selection: Selection) throws -> Type { + let field = GraphQLField.composite( + name: "saveFilter", + arguments: [Argument(name: "input", type: "SaveFilterInput!", value: input)], + selection: selection.selection + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.saveFilter[field.alias!] { + return try selection.decode(data: data) + } + throw HttpError.badpayload + case .mocking: + return selection.mock() + } + } + func savePage(input: InputObjects.SavePageInput, selection: Selection) throws -> Type { let field = GraphQLField.composite( name: "savePage", @@ -11126,9 +12112,11 @@ extension Objects { let articles: [String: Unions.ArticlesResult] let deviceTokens: [String: Unions.DeviceTokensResult] let feedArticles: [String: Unions.FeedArticlesResult] + let filters: [String: Unions.FiltersResult] let getFollowers: [String: Unions.GetFollowersResult] let getFollowing: [String: Unions.GetFollowingResult] let getUserPersonalization: [String: Unions.GetUserPersonalizationResult] + let groups: [String: Unions.GroupsResult] let hello: [String: String] let integrations: [String: Unions.IntegrationsResult] let labels: [String: Unions.LabelsResult] @@ -11191,6 +12179,10 @@ extension Objects.Query: Decodable { if let value = try container.decode(Unions.FeedArticlesResult?.self, forKey: codingKey) { map.set(key: field, hash: alias, value: value as Any) } + case "filters": + if let value = try container.decode(Unions.FiltersResult?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } case "getFollowers": if let value = try container.decode(Unions.GetFollowersResult?.self, forKey: codingKey) { map.set(key: field, hash: alias, value: value as Any) @@ -11203,6 +12195,10 @@ extension Objects.Query: Decodable { if let value = try container.decode(Unions.GetUserPersonalizationResult?.self, forKey: codingKey) { map.set(key: field, hash: alias, value: value as Any) } + case "groups": + if let value = try container.decode(Unions.GroupsResult?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } case "hello": if let value = try container.decode(String?.self, forKey: codingKey) { map.set(key: field, hash: alias, value: value as Any) @@ -11295,9 +12291,11 @@ extension Objects.Query: Decodable { articles = map["articles"] deviceTokens = map["deviceTokens"] feedArticles = map["feedArticles"] + filters = map["filters"] getFollowers = map["getFollowers"] getFollowing = map["getFollowing"] getUserPersonalization = map["getUserPersonalization"] + groups = map["groups"] hello = map["hello"] integrations = map["integrations"] labels = map["labels"] @@ -11435,6 +12433,25 @@ extension Fields where TypeLock == Objects.Query { } } + func filters(selection: Selection) throws -> Type { + let field = GraphQLField.composite( + name: "filters", + arguments: [], + selection: selection.selection + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.filters[field.alias!] { + return try selection.decode(data: data) + } + throw HttpError.badpayload + case .mocking: + return selection.mock() + } + } + func getFollowers(userId: OptionalArgument = .absent(), selection: Selection) throws -> Type { let field = GraphQLField.composite( name: "getFollowers", @@ -11492,6 +12509,25 @@ extension Fields where TypeLock == Objects.Query { } } + func groups(selection: Selection) throws -> Type { + let field = GraphQLField.composite( + name: "groups", + arguments: [], + selection: selection.selection + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.groups[field.alias!] { + return try selection.decode(data: data) + } + throw HttpError.badpayload + case .mocking: + return selection.mock() + } + } + func hello() throws -> String? { let field = GraphQLField.leaf( name: "hello", @@ -12384,6 +13420,347 @@ extension Selection where TypeLock == Never, Type == Never { typealias RecentSearchesSuccess = Selection } +extension Objects { + struct RecommendError { + let __typename: TypeName = .recommendError + let errorCodes: [String: [Enums.RecommendErrorCode]] + + enum TypeName: String, Codable { + case recommendError = "RecommendError" + } + } +} + +extension Objects.RecommendError: 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.RecommendErrorCode]?.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.RecommendError { + func errorCodes() throws -> [Enums.RecommendErrorCode] { + 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 RecommendError = Selection +} + +extension Objects { + struct RecommendSuccess { + let __typename: TypeName = .recommendSuccess + let taskNames: [String: [String]] + + enum TypeName: String, Codable { + case recommendSuccess = "RecommendSuccess" + } + } +} + +extension Objects.RecommendSuccess: 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 "taskNames": + if let value = try container.decode([String]?.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)." + ) + ) + } + } + + taskNames = map["taskNames"] + } +} + +extension Fields where TypeLock == Objects.RecommendSuccess { + func taskNames() throws -> [String] { + let field = GraphQLField.leaf( + name: "taskNames", + arguments: [] + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.taskNames[field.alias!] { + return data + } + throw HttpError.badpayload + case .mocking: + return [] + } + } +} + +extension Selection where TypeLock == Never, Type == Never { + typealias RecommendSuccess = Selection +} + +extension Objects { + struct RecommendationGroup { + let __typename: TypeName = .recommendationGroup + let admins: [String: [Objects.User]] + let createdAt: [String: DateTime] + let id: [String: String] + let inviteUrl: [String: String] + let members: [String: [Objects.User]] + let name: [String: String] + let updatedAt: [String: DateTime] + + enum TypeName: String, Codable { + case recommendationGroup = "RecommendationGroup" + } + } +} + +extension Objects.RecommendationGroup: 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 "admins": + if let value = try container.decode([Objects.User]?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "createdAt": + if let value = try container.decode(DateTime?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "id": + if let value = try container.decode(String?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "inviteUrl": + if let value = try container.decode(String?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "members": + if let value = try container.decode([Objects.User]?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "name": + if let value = try container.decode(String?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "updatedAt": + if let value = try container.decode(DateTime?.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)." + ) + ) + } + } + + admins = map["admins"] + createdAt = map["createdAt"] + id = map["id"] + inviteUrl = map["inviteUrl"] + members = map["members"] + name = map["name"] + updatedAt = map["updatedAt"] + } +} + +extension Fields where TypeLock == Objects.RecommendationGroup { + func admins(selection: Selection) throws -> Type { + let field = GraphQLField.composite( + name: "admins", + arguments: [], + selection: selection.selection + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.admins[field.alias!] { + return try selection.decode(data: data) + } + throw HttpError.badpayload + case .mocking: + return selection.mock() + } + } + + func createdAt() throws -> DateTime { + let field = GraphQLField.leaf( + name: "createdAt", + arguments: [] + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.createdAt[field.alias!] { + return data + } + throw HttpError.badpayload + case .mocking: + return DateTime.mockValue + } + } + + func id() throws -> String { + let field = GraphQLField.leaf( + name: "id", + arguments: [] + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.id[field.alias!] { + return data + } + throw HttpError.badpayload + case .mocking: + return String.mockValue + } + } + + func inviteUrl() throws -> String { + let field = GraphQLField.leaf( + name: "inviteUrl", + arguments: [] + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.inviteUrl[field.alias!] { + return data + } + throw HttpError.badpayload + case .mocking: + return String.mockValue + } + } + + func members(selection: Selection) throws -> Type { + let field = GraphQLField.composite( + name: "members", + arguments: [], + selection: selection.selection + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.members[field.alias!] { + return try selection.decode(data: data) + } + throw HttpError.badpayload + case .mocking: + return selection.mock() + } + } + + func name() throws -> String { + let field = GraphQLField.leaf( + name: "name", + arguments: [] + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.name[field.alias!] { + return data + } + throw HttpError.badpayload + case .mocking: + return String.mockValue + } + } + + func updatedAt() throws -> DateTime { + let field = GraphQLField.leaf( + name: "updatedAt", + arguments: [] + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.updatedAt[field.alias!] { + return data + } + throw HttpError.badpayload + case .mocking: + return DateTime.mockValue + } + } +} + +extension Selection where TypeLock == Never, Type == Never { + typealias RecommendationGroup = Selection +} + extension Objects { struct Reminder { let __typename: TypeName = .reminder @@ -13495,6 +14872,137 @@ extension Selection where TypeLock == Never, Type == Never { typealias SaveError = Selection } +extension Objects { + struct SaveFilterError { + let __typename: TypeName = .saveFilterError + let errorCodes: [String: [Enums.SaveFilterErrorCode]] + + enum TypeName: String, Codable { + case saveFilterError = "SaveFilterError" + } + } +} + +extension Objects.SaveFilterError: 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.SaveFilterErrorCode]?.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.SaveFilterError { + func errorCodes() throws -> [Enums.SaveFilterErrorCode] { + 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 SaveFilterError = Selection +} + +extension Objects { + struct SaveFilterSuccess { + let __typename: TypeName = .saveFilterSuccess + let filter: [String: Objects.Filter] + + enum TypeName: String, Codable { + case saveFilterSuccess = "SaveFilterSuccess" + } + } +} + +extension Objects.SaveFilterSuccess: 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 "filter": + if let value = try container.decode(Objects.Filter?.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)." + ) + ) + } + } + + filter = map["filter"] + } +} + +extension Fields where TypeLock == Objects.SaveFilterSuccess { + func filter(selection: Selection) throws -> Type { + let field = GraphQLField.composite( + name: "filter", + arguments: [], + selection: selection.selection + ) + select(field) + + switch response { + case let .decoding(data): + if let data = data.filter[field.alias!] { + return try selection.decode(data: data) + } + throw HttpError.badpayload + case .mocking: + return selection.mock() + } + } +} + +extension Selection where TypeLock == Never, Type == Never { + typealias SaveFilterSuccess = Selection +} + extension Objects { struct SaveSuccess { let __typename: TypeName = .saveSuccess @@ -20940,6 +22448,80 @@ extension Selection where TypeLock == Never, Type == Never { typealias CreateArticleSavingRequestResult = Selection } +extension Unions { + struct CreateGroupResult { + let __typename: TypeName + let errorCodes: [String: [Enums.CreateGroupErrorCode]] + let group: [String: Objects.RecommendationGroup] + + enum TypeName: String, Codable { + case createGroupError = "CreateGroupError" + case createGroupSuccess = "CreateGroupSuccess" + } + } +} + +extension Unions.CreateGroupResult: 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.CreateGroupErrorCode]?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "group": + if let value = try container.decode(Objects.RecommendationGroup?.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"] + group = map["group"] + } +} + +extension Fields where TypeLock == Unions.CreateGroupResult { + func on(createGroupError: Selection, createGroupSuccess: Selection) throws -> Type { + select([GraphQLField.fragment(type: "CreateGroupError", selection: createGroupError.selection), GraphQLField.fragment(type: "CreateGroupSuccess", selection: createGroupSuccess.selection)]) + + switch response { + case let .decoding(data): + switch data.__typename { + case .createGroupError: + let data = Objects.CreateGroupError(errorCodes: data.errorCodes) + return try createGroupError.decode(data: data) + case .createGroupSuccess: + let data = Objects.CreateGroupSuccess(group: data.group) + return try createGroupSuccess.decode(data: data) + } + case .mocking: + return createGroupError.mock() + } + } +} + +extension Selection where TypeLock == Never, Type == Never { + typealias CreateGroupResult = Selection +} + extension Unions { struct CreateHighlightReplyResult { let __typename: TypeName @@ -21458,6 +23040,80 @@ extension Selection where TypeLock == Never, Type == Never { typealias DeleteAccountResult = Selection } +extension Unions { + struct DeleteFilterResult { + let __typename: TypeName + let errorCodes: [String: [Enums.DeleteFilterErrorCode]] + let filter: [String: Objects.Filter] + + enum TypeName: String, Codable { + case deleteFilterError = "DeleteFilterError" + case deleteFilterSuccess = "DeleteFilterSuccess" + } + } +} + +extension Unions.DeleteFilterResult: 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.DeleteFilterErrorCode]?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "filter": + if let value = try container.decode(Objects.Filter?.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"] + filter = map["filter"] + } +} + +extension Fields where TypeLock == Unions.DeleteFilterResult { + func on(deleteFilterError: Selection, deleteFilterSuccess: Selection) throws -> Type { + select([GraphQLField.fragment(type: "DeleteFilterError", selection: deleteFilterError.selection), GraphQLField.fragment(type: "DeleteFilterSuccess", selection: deleteFilterSuccess.selection)]) + + switch response { + case let .decoding(data): + switch data.__typename { + case .deleteFilterError: + let data = Objects.DeleteFilterError(errorCodes: data.errorCodes) + return try deleteFilterError.decode(data: data) + case .deleteFilterSuccess: + let data = Objects.DeleteFilterSuccess(filter: data.filter) + return try deleteFilterSuccess.decode(data: data) + } + case .mocking: + return deleteFilterError.mock() + } + } +} + +extension Selection where TypeLock == Never, Type == Never { + typealias DeleteFilterResult = Selection +} + extension Unions { struct DeleteHighlightReplyResult { let __typename: TypeName @@ -22278,6 +23934,80 @@ extension Selection where TypeLock == Never, Type == Never { typealias FeedArticlesResult = Selection } +extension Unions { + struct FiltersResult { + let __typename: TypeName + let errorCodes: [String: [Enums.FiltersErrorCode]] + let filters: [String: [Objects.Filter]] + + enum TypeName: String, Codable { + case filtersError = "FiltersError" + case filtersSuccess = "FiltersSuccess" + } + } +} + +extension Unions.FiltersResult: 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.FiltersErrorCode]?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "filters": + if let value = try container.decode([Objects.Filter]?.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"] + filters = map["filters"] + } +} + +extension Fields where TypeLock == Unions.FiltersResult { + func on(filtersError: Selection, filtersSuccess: Selection) throws -> Type { + select([GraphQLField.fragment(type: "FiltersError", selection: filtersError.selection), GraphQLField.fragment(type: "FiltersSuccess", selection: filtersSuccess.selection)]) + + switch response { + case let .decoding(data): + switch data.__typename { + case .filtersError: + let data = Objects.FiltersError(errorCodes: data.errorCodes) + return try filtersError.decode(data: data) + case .filtersSuccess: + let data = Objects.FiltersSuccess(filters: data.filters) + return try filtersSuccess.decode(data: data) + } + case .mocking: + return filtersError.mock() + } + } +} + +extension Selection where TypeLock == Never, Type == Never { + typealias FiltersResult = Selection +} + extension Unions { struct GenerateApiKeyResult { let __typename: TypeName @@ -22648,6 +24378,80 @@ extension Selection where TypeLock == Never, Type == Never { typealias GoogleSignupResult = Selection } +extension Unions { + struct GroupsResult { + let __typename: TypeName + let errorCodes: [String: [Enums.GroupsErrorCode]] + let groups: [String: [Objects.RecommendationGroup]] + + enum TypeName: String, Codable { + case groupsError = "GroupsError" + case groupsSuccess = "GroupsSuccess" + } + } +} + +extension Unions.GroupsResult: 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.GroupsErrorCode]?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "groups": + if let value = try container.decode([Objects.RecommendationGroup]?.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"] + groups = map["groups"] + } +} + +extension Fields where TypeLock == Unions.GroupsResult { + func on(groupsError: Selection, groupsSuccess: Selection) throws -> Type { + select([GraphQLField.fragment(type: "GroupsError", selection: groupsError.selection), GraphQLField.fragment(type: "GroupsSuccess", selection: groupsSuccess.selection)]) + + switch response { + case let .decoding(data): + switch data.__typename { + case .groupsError: + let data = Objects.GroupsError(errorCodes: data.errorCodes) + return try groupsError.decode(data: data) + case .groupsSuccess: + let data = Objects.GroupsSuccess(groups: data.groups) + return try groupsSuccess.decode(data: data) + } + case .mocking: + return groupsError.mock() + } + } +} + +extension Selection where TypeLock == Never, Type == Never { + typealias GroupsResult = Selection +} + extension Unions { struct IntegrationsResult { let __typename: TypeName @@ -23024,6 +24828,80 @@ extension Selection where TypeLock == Never, Type == Never { typealias MergeHighlightResult = Selection } +extension Unions { + struct MoveFilterResult { + let __typename: TypeName + let errorCodes: [String: [Enums.MoveFilterErrorCode]] + let filter: [String: Objects.Filter] + + enum TypeName: String, Codable { + case moveFilterError = "MoveFilterError" + case moveFilterSuccess = "MoveFilterSuccess" + } + } +} + +extension Unions.MoveFilterResult: 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.MoveFilterErrorCode]?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "filter": + if let value = try container.decode(Objects.Filter?.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"] + filter = map["filter"] + } +} + +extension Fields where TypeLock == Unions.MoveFilterResult { + func on(moveFilterError: Selection, moveFilterSuccess: Selection) throws -> Type { + select([GraphQLField.fragment(type: "MoveFilterError", selection: moveFilterError.selection), GraphQLField.fragment(type: "MoveFilterSuccess", selection: moveFilterSuccess.selection)]) + + switch response { + case let .decoding(data): + switch data.__typename { + case .moveFilterError: + let data = Objects.MoveFilterError(errorCodes: data.errorCodes) + return try moveFilterError.decode(data: data) + case .moveFilterSuccess: + let data = Objects.MoveFilterSuccess(filter: data.filter) + return try moveFilterSuccess.decode(data: data) + } + case .mocking: + return moveFilterError.mock() + } + } +} + +extension Selection where TypeLock == Never, Type == Never { + typealias MoveFilterResult = Selection +} + extension Unions { struct MoveLabelResult { let __typename: TypeName @@ -23320,6 +25198,80 @@ extension Selection where TypeLock == Never, Type == Never { typealias RecentSearchesResult = Selection } +extension Unions { + struct RecommendResult { + let __typename: TypeName + let errorCodes: [String: [Enums.RecommendErrorCode]] + let taskNames: [String: [String]] + + enum TypeName: String, Codable { + case recommendError = "RecommendError" + case recommendSuccess = "RecommendSuccess" + } + } +} + +extension Unions.RecommendResult: 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.RecommendErrorCode]?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "taskNames": + if let value = try container.decode([String]?.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"] + taskNames = map["taskNames"] + } +} + +extension Fields where TypeLock == Unions.RecommendResult { + func on(recommendError: Selection, recommendSuccess: Selection) throws -> Type { + select([GraphQLField.fragment(type: "RecommendError", selection: recommendError.selection), GraphQLField.fragment(type: "RecommendSuccess", selection: recommendSuccess.selection)]) + + switch response { + case let .decoding(data): + switch data.__typename { + case .recommendError: + let data = Objects.RecommendError(errorCodes: data.errorCodes) + return try recommendError.decode(data: data) + case .recommendSuccess: + let data = Objects.RecommendSuccess(taskNames: data.taskNames) + return try recommendSuccess.decode(data: data) + } + case .mocking: + return recommendError.mock() + } + } +} + +extension Selection where TypeLock == Never, Type == Never { + typealias RecommendResult = Selection +} + extension Unions { struct ReminderResult { let __typename: TypeName @@ -23616,6 +25568,80 @@ extension Selection where TypeLock == Never, Type == Never { typealias SaveArticleReadingProgressResult = Selection } +extension Unions { + struct SaveFilterResult { + let __typename: TypeName + let errorCodes: [String: [Enums.SaveFilterErrorCode]] + let filter: [String: Objects.Filter] + + enum TypeName: String, Codable { + case saveFilterError = "SaveFilterError" + case saveFilterSuccess = "SaveFilterSuccess" + } + } +} + +extension Unions.SaveFilterResult: 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.SaveFilterErrorCode]?.self, forKey: codingKey) { + map.set(key: field, hash: alias, value: value as Any) + } + case "filter": + if let value = try container.decode(Objects.Filter?.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"] + filter = map["filter"] + } +} + +extension Fields where TypeLock == Unions.SaveFilterResult { + func on(saveFilterError: Selection, saveFilterSuccess: Selection) throws -> Type { + select([GraphQLField.fragment(type: "SaveFilterError", selection: saveFilterError.selection), GraphQLField.fragment(type: "SaveFilterSuccess", selection: saveFilterSuccess.selection)]) + + switch response { + case let .decoding(data): + switch data.__typename { + case .saveFilterError: + let data = Objects.SaveFilterError(errorCodes: data.errorCodes) + return try saveFilterError.decode(data: data) + case .saveFilterSuccess: + let data = Objects.SaveFilterSuccess(filter: data.filter) + return try saveFilterSuccess.decode(data: data) + } + case .mocking: + return saveFilterError.mock() + } + } +} + +extension Selection where TypeLock == Never, Type == Never { + typealias SaveFilterResult = Selection +} + extension Unions { struct SaveResult { let __typename: TypeName @@ -26227,6 +28253,15 @@ extension Enums { } } +extension Enums { + /// CreateGroupErrorCode + enum CreateGroupErrorCode: String, CaseIterable, Codable { + case badRequest = "BAD_REQUEST" + + case unauthorized = "UNAUTHORIZED" + } +} + extension Enums { /// CreateHighlightErrorCode enum CreateHighlightErrorCode: String, CaseIterable, Codable { @@ -26314,6 +28349,17 @@ extension Enums { } } +extension Enums { + /// DeleteFilterErrorCode + enum DeleteFilterErrorCode: String, CaseIterable, Codable { + case badRequest = "BAD_REQUEST" + + case notFound = "NOT_FOUND" + + case unauthorized = "UNAUTHORIZED" + } +} + extension Enums { /// DeleteHighlightErrorCode enum DeleteHighlightErrorCode: String, CaseIterable, Codable { @@ -26429,6 +28475,15 @@ extension Enums { } } +extension Enums { + /// FiltersErrorCode + enum FiltersErrorCode: String, CaseIterable, Codable { + case badRequest = "BAD_REQUEST" + + case unauthorized = "UNAUTHORIZED" + } +} + extension Enums { /// GenerateApiKeyErrorCode enum GenerateApiKeyErrorCode: String, CaseIterable, Codable { @@ -26461,6 +28516,15 @@ extension Enums { } } +extension Enums { + /// GroupsErrorCode + enum GroupsErrorCode: String, CaseIterable, Codable { + case badRequest = "BAD_REQUEST" + + case unauthorized = "UNAUTHORIZED" + } +} + extension Enums { /// IntegrationType enum IntegrationType: String, CaseIterable, Codable { @@ -26527,6 +28591,17 @@ extension Enums { } } +extension Enums { + /// MoveFilterErrorCode + enum MoveFilterErrorCode: String, CaseIterable, Codable { + case badRequest = "BAD_REQUEST" + + case notFound = "NOT_FOUND" + + case unauthorized = "UNAUTHORIZED" + } +} + extension Enums { /// MoveLabelErrorCode enum MoveLabelErrorCode: String, CaseIterable, Codable { @@ -26601,6 +28676,17 @@ extension Enums { } } +extension Enums { + /// RecommendErrorCode + enum RecommendErrorCode: String, CaseIterable, Codable { + case badRequest = "BAD_REQUEST" + + case notFound = "NOT_FOUND" + + case unauthorized = "UNAUTHORIZED" + } +} + extension Enums { /// ReminderErrorCode enum ReminderErrorCode: String, CaseIterable, Codable { @@ -26680,6 +28766,17 @@ extension Enums { } } +extension Enums { + /// SaveFilterErrorCode + enum SaveFilterErrorCode: String, CaseIterable, Codable { + case badRequest = "BAD_REQUEST" + + case notFound = "NOT_FOUND" + + case unauthorized = "UNAUTHORIZED" + } +} + extension Enums { /// SearchErrorCode enum SearchErrorCode: String, CaseIterable, Codable { @@ -27208,6 +29305,29 @@ extension InputObjects { } } +extension InputObjects { + struct CreateGroupInput: Encodable, Hashable { + var expiresInDays: OptionalArgument = .absent() + + var maxMembers: OptionalArgument = .absent() + + var name: String + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + if expiresInDays.hasValue { try container.encode(expiresInDays, forKey: .expiresInDays) } + if maxMembers.hasValue { try container.encode(maxMembers, forKey: .maxMembers) } + try container.encode(name, forKey: .name) + } + + enum CodingKeys: String, CodingKey { + case expiresInDays + case maxMembers + case name + } + } +} + extension InputObjects { struct CreateHighlightInput: Encodable, Hashable { var annotation: OptionalArgument = .absent() @@ -27495,6 +29615,25 @@ extension InputObjects { } } +extension InputObjects { + struct MoveFilterInput: Encodable, Hashable { + var afterFilterId: OptionalArgument = .absent() + + var filterId: String + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + if afterFilterId.hasValue { try container.encode(afterFilterId, forKey: .afterFilterId) } + try container.encode(filterId, forKey: .filterId) + } + + enum CodingKeys: String, CodingKey { + case afterFilterId + case filterId + } + } +} + extension InputObjects { struct MoveLabelInput: Encodable, Hashable { var afterLabelId: OptionalArgument = .absent() @@ -27587,6 +29726,25 @@ extension InputObjects { } } +extension InputObjects { + struct RecommendInput: Encodable, Hashable { + var groupIds: [String] + + var pageId: String + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(groupIds, forKey: .groupIds) + try container.encode(pageId, forKey: .pageId) + } + + enum CodingKeys: String, CodingKey { + case groupIds + case pageId + } + } +} + extension InputObjects { struct ReportItemInput: Encodable, Hashable { var itemUrl: String @@ -27687,6 +29845,33 @@ extension InputObjects { } } +extension InputObjects { + struct SaveFilterInput: Encodable, Hashable { + var description: OptionalArgument = .absent() + + var filter: String + + var id: OptionalArgument = .absent() + + var name: String + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + if description.hasValue { try container.encode(description, forKey: .description) } + try container.encode(filter, forKey: .filter) + if id.hasValue { try container.encode(id, forKey: .id) } + try container.encode(name, forKey: .name) + } + + enum CodingKeys: String, CodingKey { + case description + case filter + case id + case name + } + } +} + extension InputObjects { struct SavePageInput: Encodable, Hashable { var clientRequestId: String @@ -28149,6 +30334,8 @@ extension InputObjects { var pageId: String + var savedAt: OptionalArgument = .absent() + var title: OptionalArgument = .absent() func encode(to encoder: Encoder) throws { @@ -28156,6 +30343,7 @@ extension InputObjects { if byline.hasValue { try container.encode(byline, forKey: .byline) } if description.hasValue { try container.encode(description, forKey: .description) } try container.encode(pageId, forKey: .pageId) + if savedAt.hasValue { try container.encode(savedAt, forKey: .savedAt) } if title.hasValue { try container.encode(title, forKey: .title) } } @@ -28163,6 +30351,7 @@ extension InputObjects { case byline case description case pageId + case savedAt case title } } diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/CreateRecommendationGroup.swift b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/CreateRecommendationGroup.swift new file mode 100644 index 000000000..646cf45dc --- /dev/null +++ b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/CreateRecommendationGroup.swift @@ -0,0 +1,53 @@ +import CoreData +import Foundation +import Models +import SwiftGraphQL + +public extension DataService { + func createRecommendationGroup(name: String) async throws -> InternalRecommendationGroup { + enum MutationResult { + case saved(recommendationGroup: InternalRecommendationGroup) + case error(errorCode: Enums.CreateGroupErrorCode) + } + + let selection = Selection { + try $0.on( + createGroupError: .init { + .error(errorCode: try $0.errorCodes().first ?? .badRequest) + }, + createGroupSuccess: .init { + .saved(recommendationGroup: try $0.group(selection: recommendationGroupSelection)) + } + ) + } + + let input = InputObjects.CreateGroupInput(name: name) + + let mutation = Selection.Mutation { + try $0.createGroup(input: input, selection: selection) + } + + let path = appEnvironment.graphqlPath + let headers = networker.defaultHeaders + + return try await withCheckedThrowingContinuation { continuation in + send(mutation, to: path, headers: headers) { [weak self] queryResult in + guard let payload = try? queryResult.get(), let self = self else { + continuation.resume(throwing: BasicError.message(messageText: "network error")) + return + } + + switch payload.data { + case let .saved(recommendationGroup: recommendationGroup): + if [recommendationGroup].persist(context: self.backgroundContext) != nil { + continuation.resume(returning: recommendationGroup) + } else { + continuation.resume(throwing: BasicError.message(messageText: "CoreData error")) + } + case let .error(errorCode: errorCode): + continuation.resume(throwing: BasicError.message(messageText: errorCode.rawValue)) + } + } + } + } +} diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Queries/RecommendationGroupsQuery.swift b/apple/OmnivoreKit/Sources/Services/DataService/Queries/RecommendationGroupsQuery.swift new file mode 100644 index 000000000..a17a5c52c --- /dev/null +++ b/apple/OmnivoreKit/Sources/Services/DataService/Queries/RecommendationGroupsQuery.swift @@ -0,0 +1,52 @@ +import CoreData +import Foundation +import Models +import SwiftGraphQL + +public extension DataService { + func recommendationGroups() async throws -> [InternalRecommendationGroup] { + enum QueryResult { + case success(result: [InternalRecommendationGroup]) + case error(error: String) + } + + let selection = Selection { + try $0.on( + groupsError: .init { + QueryResult.error(error: try $0.errorCodes().description) + }, + groupsSuccess: .init { + QueryResult.success(result: try $0.groups(selection: recommendationGroupSelection.list)) + } + ) + } + + let query = Selection.Query { + try $0.groups(selection: selection) + } + + let path = appEnvironment.graphqlPath + let headers = networker.defaultHeaders + let context = backgroundContext + + 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): + if result.persist(context: context) != nil { + continuation.resume(returning: result) + } else { + continuation.resume(throwing: BasicError.message(messageText: "CoreData error")) + } + case .error: + continuation.resume(throwing: BasicError.message(messageText: "Recommendation Groups fetch error")) + } + } + } + } +} diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Selections/RecommendationGroupSelection.swift b/apple/OmnivoreKit/Sources/Services/DataService/Selections/RecommendationGroupSelection.swift new file mode 100644 index 000000000..4064c1bf9 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Services/DataService/Selections/RecommendationGroupSelection.swift @@ -0,0 +1,10 @@ +import Models +import SwiftGraphQL + +let recommendationGroupSelection = Selection.RecommendationGroup { + InternalRecommendationGroup( + id: try $0.id(), + name: try $0.name(), + inviteUrl: try $0.inviteUrl() + ) +} diff --git a/apple/OmnivoreKit/Sources/Services/InternalModels/InternalRecommendationGroup.swift b/apple/OmnivoreKit/Sources/Services/InternalModels/InternalRecommendationGroup.swift new file mode 100644 index 000000000..c787cc1f8 --- /dev/null +++ b/apple/OmnivoreKit/Sources/Services/InternalModels/InternalRecommendationGroup.swift @@ -0,0 +1,65 @@ +// +// InternalRecommendationGroup.swift +// +// +// Created by Jackson Harper on 12/5/22. +// + +import CoreData +import Foundation +import Models + +public struct InternalRecommendationGroup: Encodable, Identifiable { + public let id: String + public let name: String + public let inviteUrl: String + + func asManagedObject(inContext context: NSManagedObjectContext) -> RecommendationGroup { + let fetchRequest: NSFetchRequest = RecommendationGroup.fetchRequest() + fetchRequest.predicate = NSPredicate( + format: "id == %@", id + ) + let existing = (try? context.fetch(fetchRequest))?.first + let recommendationGroup = existing ?? RecommendationGroup(entity: RecommendationGroup.entity(), insertInto: context) + + recommendationGroup.id = id + recommendationGroup.name = name + recommendationGroup.inviteUrl = inviteUrl + + return recommendationGroup + } + + static func make(from recommendationGroup: RecommendationGroup) -> InternalRecommendationGroup? { + if let id = recommendationGroup.id, + let name = recommendationGroup.name, + let inviteUrl = recommendationGroup.inviteUrl + { + return InternalRecommendationGroup( + id: id, + name: name, + inviteUrl: inviteUrl + ) + } + return nil + } +} + +extension Sequence where Element == InternalRecommendationGroup { + func persist(context: NSManagedObjectContext) -> [NSManagedObjectID]? { + var result: [NSManagedObjectID]? + + context.performAndWait { + let recommendationGroups = map { $0.asManagedObject(inContext: context) } + do { + try context.save() + logger.debug("InternalRecommendationGroup saved succesfully") + result = recommendationGroups.map(\.objectID) + } catch { + context.rollback() + logger.debug("Failed to save InternalRecommendationGroups: \(error.localizedDescription)") + } + } + + return result + } +}