215 lines
8.3 KiB
Swift
215 lines
8.3 KiB
Swift
//
|
|
// DatabaseViewController.swift
|
|
// MongoView
|
|
//
|
|
// Created by Shadowfacts on 1/10/20.
|
|
// Copyright © 2020 Shadowfacts. All rights reserved.
|
|
//
|
|
|
|
import Cocoa
|
|
import MongoSwift
|
|
import NIO
|
|
|
|
struct DatabaseCollections {
|
|
let database: String
|
|
let collections: [String]
|
|
}
|
|
|
|
struct DatabaseCollection {
|
|
let database: String
|
|
let name: String
|
|
}
|
|
|
|
class DatabaseViewController: NSViewController {
|
|
|
|
@IBOutlet weak var collectionsOutlineView: NSOutlineView!
|
|
@IBOutlet weak var detailContainerView: NSView!
|
|
|
|
let mongoController: MongoController
|
|
|
|
private var databaseCollections: [DatabaseCollections] = []
|
|
private var selectedCollection: DatabaseCollection?
|
|
|
|
private var placeholderLabel: NSTextField?
|
|
private var queryViewController: QueryViewController?
|
|
|
|
init(mongoController: MongoController) {
|
|
self.mongoController = mongoController
|
|
|
|
super.init(nibName: "DatabaseViewController", bundle: .main)
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
|
|
mongoController.client.listDatabaseNames().flatMap { (databaseNames) -> EventLoopFuture<[DatabaseCollections]> in
|
|
let futures = databaseNames.map { (name: String) -> EventLoopFuture<DatabaseCollections> in
|
|
let db = self.mongoController.client.db(name)
|
|
return db.listCollectionNames().map { (collectionNames: [String]) -> DatabaseCollections in
|
|
let sortedNames = collectionNames.sorted()
|
|
return DatabaseCollections(database: name, collections: sortedNames)
|
|
}
|
|
}
|
|
return EventLoopFuture.whenAllSucceed(futures, on: futures.first!.eventLoop)
|
|
}.whenComplete { [weak self] (res) in
|
|
guard let self = self else { return }
|
|
|
|
switch res {
|
|
case let .success(databaseCollections):
|
|
let sortedCollections = databaseCollections.sorted(by: { $0.database < $1.database })
|
|
self.databaseCollections = sortedCollections
|
|
DispatchQueue.main.async {
|
|
self.collectionsOutlineView.reloadData()
|
|
}
|
|
|
|
case let .failure(error):
|
|
fatalError("error getting database names: \(error)")
|
|
}
|
|
}
|
|
|
|
collectionsOutlineView.dataSource = self
|
|
collectionsOutlineView.delegate = self
|
|
|
|
collectionsOutlineView.target = self
|
|
collectionsOutlineView.doubleAction = #selector(cellDoubleClicked)
|
|
|
|
updateDetailView()
|
|
}
|
|
|
|
func showCollection(_ collection: DatabaseCollection) {
|
|
selectedCollection = collection
|
|
updateDetailView()
|
|
}
|
|
|
|
private func updateDetailView() {
|
|
if let collection = selectedCollection {
|
|
if let placeholderLabel = placeholderLabel {
|
|
placeholderLabel.removeFromSuperview()
|
|
}
|
|
if let oldQueryViewController = self.queryViewController {
|
|
oldQueryViewController.view.removeFromSuperview()
|
|
oldQueryViewController.removeFromParent()
|
|
}
|
|
|
|
let queryViewController = QueryViewController(mongoController: mongoController, collection: collection)
|
|
self.queryViewController = queryViewController
|
|
queryViewController.view.translatesAutoresizingMaskIntoConstraints = false
|
|
addChild(queryViewController)
|
|
detailContainerView.addSubview(queryViewController.view)
|
|
NSLayoutConstraint.activate([
|
|
queryViewController.view.leadingAnchor.constraint(equalTo: detailContainerView.leadingAnchor),
|
|
queryViewController.view.trailingAnchor.constraint(equalTo: detailContainerView.trailingAnchor),
|
|
queryViewController.view.topAnchor.constraint(equalTo: detailContainerView.topAnchor),
|
|
queryViewController.view.bottomAnchor.constraint(equalTo: detailContainerView.bottomAnchor)
|
|
])
|
|
|
|
self.title = queryViewController.title
|
|
} else {
|
|
if let queryViewController = queryViewController {
|
|
queryViewController.view.removeFromSuperview()
|
|
queryViewController.removeFromParent()
|
|
}
|
|
|
|
if self.placeholderLabel == nil {
|
|
let label = NSTextField(labelWithString: "Select a collection to begin")
|
|
self.placeholderLabel = label
|
|
label.translatesAutoresizingMaskIntoConstraints = false
|
|
detailContainerView.addSubview(label)
|
|
NSLayoutConstraint.activate([
|
|
label.centerXAnchor.constraint(equalTo: detailContainerView.centerXAnchor),
|
|
label.centerYAnchor.constraint(equalTo: detailContainerView.centerYAnchor)
|
|
])
|
|
|
|
self.title = "No Query"
|
|
}
|
|
}
|
|
}
|
|
|
|
@objc func cellDoubleClicked() {
|
|
let item = collectionsOutlineView.item(atRow: collectionsOutlineView.clickedRow)!
|
|
|
|
if item is DatabaseCollections {
|
|
if collectionsOutlineView.isItemExpanded(item) {
|
|
collectionsOutlineView.collapseItem(item)
|
|
} else {
|
|
collectionsOutlineView.expandItem(item)
|
|
}
|
|
} else if let collection = item as? DatabaseCollection {
|
|
// only open a new window/tab if our own query has changed from the default, otherwise replace our query controller
|
|
if let queryViewController = queryViewController, queryViewController.hasFilterChanged {
|
|
(NSApplication.shared.delegate as! AppDelegate).newWindow(mongoController: mongoController, collection: collection)
|
|
} else {
|
|
self.selectedCollection = collection
|
|
updateDetailView()
|
|
}
|
|
}
|
|
}
|
|
|
|
@IBAction func refresh(_ sender: Any) {
|
|
queryViewController?.refresh()
|
|
}
|
|
}
|
|
|
|
extension DatabaseViewController: NSMenuItemValidation {
|
|
func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
|
|
if menuItem.action == #selector(refresh(_:)) {
|
|
return queryViewController != nil
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
extension DatabaseViewController: NSOutlineViewDataSource {
|
|
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
|
|
if item == nil {
|
|
return databaseCollections.count
|
|
} else if let database = item as? DatabaseCollections {
|
|
return database.collections.count
|
|
} else {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
|
|
return item is DatabaseCollections
|
|
}
|
|
|
|
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
|
|
if item == nil {
|
|
return databaseCollections[index]
|
|
} else if let databaseCollections = item as? DatabaseCollections {
|
|
let collection = databaseCollections.collections[index]
|
|
return DatabaseCollection(database: databaseCollections.database, name: collection)
|
|
} else {
|
|
fatalError()
|
|
}
|
|
}
|
|
}
|
|
|
|
extension DatabaseViewController: NSOutlineViewDelegate {
|
|
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
|
|
if let database = item as? DatabaseCollections {
|
|
let cell = outlineView.makeView(withIdentifier: .databaseNameCell, owner: nil) as! NSTableCellView
|
|
cell.textField!.stringValue = database.database
|
|
cell.textField!.isEditable = false
|
|
return cell
|
|
} else if let collection = item as? DatabaseCollection {
|
|
let cell = outlineView.makeView(withIdentifier: .collectionNameCell, owner: nil) as! NSTableCellView
|
|
cell.textField!.stringValue = collection.name
|
|
cell.textField!.isEditable = false
|
|
return cell
|
|
} else {
|
|
fatalError()
|
|
}
|
|
}
|
|
}
|
|
|
|
extension NSUserInterfaceItemIdentifier {
|
|
static let databaseNameCell = NSUserInterfaceItemIdentifier(rawValue: "DatabaseNameCell")
|
|
static let collectionNameCell = NSUserInterfaceItemIdentifier(rawValue: "CollectionNameCell")
|
|
}
|