From a16e5bed000a36be5f5c2a3f40659001208c90f2 Mon Sep 17 00:00:00 2001
From: Dariusz Rybicki <dariusz@elixxir.io>
Date: Thu, 11 Aug 2022 11:15:28 +0100
Subject: [PATCH] Filter groups by leader's blocked and banned status

---
 Sources/XXDatabase/Models/Group+GRDB.swift  | 13 ++++
 Sources/XXModels/Models/Group.swift         | 26 +++++++
 Tests/XXDatabaseTests/Group+GRDBTests.swift | 80 +++++++++++++++++++++
 3 files changed, 119 insertions(+)

diff --git a/Sources/XXDatabase/Models/Group+GRDB.swift b/Sources/XXDatabase/Models/Group+GRDB.swift
index 5cd9090..42455de 100644
--- a/Sources/XXDatabase/Models/Group+GRDB.swift
+++ b/Sources/XXDatabase/Models/Group+GRDB.swift
@@ -58,6 +58,19 @@ extension Group: PersistableRecord, FetchableRecord {
       request = request.filter(authStatus.map(\.rawValue).contains(Column.authStatus))
     }
 
+    if query.isLeaderBlocked != nil || query.isLeaderBanned != nil {
+      let leader = TableAlias(name: "leader")
+      request = request.joining(required: Association.leader.aliased(leader))
+
+      if let isLeaderBlocked = query.isLeaderBlocked {
+        request = request.filter(leader[Contact.Column.isBlocked] == isLeaderBlocked)
+      }
+
+      if let isLeaderBanned = query.isLeaderBanned {
+        request = request.filter(leader[Contact.Column.isBanned] == isLeaderBanned)
+      }
+    }
+
     switch query.sortBy {
     case .createdAt(desc: false):
       request = request.order(Column.createdAt)
diff --git a/Sources/XXModels/Models/Group.swift b/Sources/XXModels/Models/Group.swift
index 4ac7e79..b7f9b0d 100644
--- a/Sources/XXModels/Models/Group.swift
+++ b/Sources/XXModels/Models/Group.swift
@@ -99,16 +99,28 @@ extension Group {
     ///   - authStatus: Filter groups by auth status.
     ///     If set, only groups with any of the provided auth statuses will be fetched.
     ///     If `nil` (default), the filter is not used.
+    ///   - isLeaderBlocked: Filter by leader contact's `isBlocked` status.
+    ///     If `true`, only groups with blocked leader contacts are included.
+    ///     If `false`, only groups with non-blocked contacts are included.
+    ///     If `nil` (default), the filter is not used.
+    ///   - isLeaderBanned: Filter by leader contact's `isBlocked` status.
+    ///     If `true`, only groups with blocked leader contacts are included.
+    ///     If `false`, only groups with non-blocked contacts are included.
+    ///     If `nil` (default), the filter is not used.
     ///   - sortBy: Sort order (defaults to `.createdAt(desc: true)`).
     public init(
       id: Set<Group.ID>? = nil,
       withMessages: Bool? = nil,
       authStatus: Set<AuthStatus>? = nil,
+      isLeaderBlocked: Bool? = nil,
+      isLeaderBanned: Bool? = nil,
       sortBy: SortOrder = .createdAt(desc: true)
     ) {
       self.id = id
       self.withMessages = withMessages
       self.authStatus = authStatus
+      self.isLeaderBlocked = isLeaderBlocked
+      self.isLeaderBanned = isLeaderBanned
       self.sortBy = sortBy
     }
 
@@ -128,6 +140,20 @@ extension Group {
     /// If `nil`, the filter is not used.
     public var authStatus: Set<AuthStatus>?
 
+    /// Filter by leader contact's `isBlocked` status
+    ///
+    /// If `true`, only groups with blocked leader contacts are included.
+    /// If `false`, only groups with non-blocked contacts are included.
+    /// If `nil`, the filter is not used.
+    public var isLeaderBlocked: Bool?
+
+    /// Filter by leader contact's `isBanned` status
+    ///
+    /// If `true`, only groups with banned leader contacts are included.
+    /// If `false`, only groups with non-banned leader contacts are included.
+    /// If `nil`, the filter is not used.
+    public var isLeaderBanned: Bool?
+
     /// Groups sort order
     public var sortBy: SortOrder
   }
diff --git a/Tests/XXDatabaseTests/Group+GRDBTests.swift b/Tests/XXDatabaseTests/Group+GRDBTests.swift
index 1a255b6..ad3786a 100644
--- a/Tests/XXDatabaseTests/Group+GRDBTests.swift
+++ b/Tests/XXDatabaseTests/Group+GRDBTests.swift
@@ -159,4 +159,84 @@ final class GroupGRDBTests: XCTestCase {
       [groupA, groupB]
     )
   }
+
+  func testFetchingWithBlockedLeaders() throws {
+    // Mock up contacts:
+
+    let contactA = try db.saveContact(.stub("A").withBlocked(false))
+    let contactB = try db.saveContact(.stub("B").withBlocked(true))
+
+    // Mock up groups:
+
+    let groupA = try db.saveGroup(.stub(
+      "A",
+      leaderId: contactA.id,
+      createdAt: .stub(1)
+    ))
+
+    let groupB = try db.saveGroup(.stub(
+      "B",
+      leaderId: contactB.id,
+      createdAt: .stub(2)
+    ))
+
+    // Fetch groups with blocked leaders:
+
+    XCTAssertNoDifference(try db.fetchGroups(.init(isLeaderBlocked: true)), [
+      groupB,
+    ])
+
+    // Fetch groups with non-blocked leaders:
+
+    XCTAssertNoDifference(try db.fetchGroups(.init(isLeaderBlocked: false)), [
+      groupA,
+    ])
+
+    // Fetch groups regardless leader's blocked status:
+
+    XCTAssertNoDifference(try db.fetchGroups(.init(isLeaderBlocked: nil)), [
+      groupB,
+      groupA,
+    ])
+  }
+
+  func testFetchingWithBannedLeaders() throws {
+    // Mock up contacts:
+
+    let contactA = try db.saveContact(.stub("A").withBanned(false))
+    let contactB = try db.saveContact(.stub("B").withBanned(true))
+
+    // Mock up groups:
+
+    let groupA = try db.saveGroup(.stub(
+      "A",
+      leaderId: contactA.id,
+      createdAt: .stub(1)
+    ))
+
+    let groupB = try db.saveGroup(.stub(
+      "B",
+      leaderId: contactB.id,
+      createdAt: .stub(2)
+    ))
+
+    // Fetch groups with banned leaders:
+
+    XCTAssertNoDifference(try db.fetchGroups(.init(isLeaderBanned: true)), [
+      groupB,
+    ])
+
+    // Fetch groups with non-banned leaders:
+
+    XCTAssertNoDifference(try db.fetchGroups(.init(isLeaderBanned: false)), [
+      groupA,
+    ])
+
+    // Fetch groups regardless leader's banned status:
+
+    XCTAssertNoDifference(try db.fetchGroups(.init(isLeaderBanned: nil)), [
+      groupB,
+      groupA,
+    ])
+  }
 }
-- 
GitLab