Remove hidden files from indexer results (#4325)
* Added functionality to not display hidden files * Added interfaces for seperating db layer * Updated variable naming and refactored Database connection class * Added tests for WindowsSearchAPI class * Fixed nit with braces * Added function to test that all connections from database are closed
This commit is contained in:
parent
7ed03c8b90
commit
a21a3827fd
|
@ -0,0 +1,10 @@
|
|||
using Microsoft.Plugin.Indexer.SearchHelper;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Plugin.Indexer.Interface
|
||||
{
|
||||
public interface ISearch
|
||||
{
|
||||
List<OleDBResult> Query(string connectionString, string sqlQuery);
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ namespace Microsoft.Plugin.Indexer
|
|||
private PluginJsonStorage<Settings> _storage;
|
||||
|
||||
// To access Windows Search functionalities
|
||||
private readonly WindowsSearchAPI _api = new WindowsSearchAPI();
|
||||
private readonly WindowsSearchAPI _api = new WindowsSearchAPI(new OleDBSearch());
|
||||
|
||||
// Reserved keywords in oleDB
|
||||
private string ReservedStringPattern = @"^[\/\\\$\%]+$";
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Data.OleDb;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Plugin.Indexer.SearchHelper
|
||||
{
|
||||
public class OleDBResult
|
||||
{
|
||||
public List<object> fieldData;
|
||||
|
||||
public OleDBResult(List<object> fieldData)
|
||||
{
|
||||
this.fieldData = fieldData;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
using Microsoft.Plugin.Indexer.Interface;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.OleDb;
|
||||
|
||||
namespace Microsoft.Plugin.Indexer.SearchHelper
|
||||
{
|
||||
public class OleDBSearch : ISearch
|
||||
{
|
||||
private OleDbCommand command;
|
||||
private OleDbConnection conn;
|
||||
private OleDbDataReader WDSResults;
|
||||
|
||||
public List<OleDBResult> Query(string connectionString, string sqlQuery)
|
||||
{
|
||||
List<OleDBResult> result = new List<OleDBResult>();
|
||||
|
||||
using (conn = new OleDbConnection(connectionString))
|
||||
{
|
||||
// open the connection
|
||||
conn.Open();
|
||||
|
||||
// now create an OleDB command object with the query we built above and the connection we just opened.
|
||||
using (command = new OleDbCommand(sqlQuery, conn))
|
||||
{
|
||||
using (WDSResults = command.ExecuteReader())
|
||||
{
|
||||
if (WDSResults.HasRows)
|
||||
{
|
||||
while (WDSResults.Read())
|
||||
{
|
||||
List<Object> fieldData = new List<object>();
|
||||
for (int i = 0; i < WDSResults.FieldCount; i++)
|
||||
{
|
||||
fieldData.Add(WDSResults.GetValue(i));
|
||||
}
|
||||
result.Add(new OleDBResult(fieldData));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Checks if all the variables related to database connection have been properly disposed
|
||||
public bool HaveAllDisposableItemsBeenDisposed()
|
||||
{
|
||||
bool commandDisposed = false;
|
||||
bool connDisposed = false;
|
||||
bool resultDisposed = false;
|
||||
|
||||
try
|
||||
{
|
||||
command.ExecuteReader();
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
commandDisposed = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
WDSResults.Read();
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
resultDisposed = true;
|
||||
}
|
||||
|
||||
if(conn.State == System.Data.ConnectionState.Closed)
|
||||
{
|
||||
connDisposed = true;
|
||||
}
|
||||
|
||||
return commandDisposed && resultDisposed && connDisposed;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,55 +1,55 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.OleDb;
|
||||
using Microsoft.Plugin.Indexer.Interface;
|
||||
using Microsoft.Search.Interop;
|
||||
|
||||
namespace Microsoft.Plugin.Indexer.SearchHelper
|
||||
{
|
||||
public class WindowsSearchAPI
|
||||
{
|
||||
public OleDbConnection conn;
|
||||
public OleDbCommand command;
|
||||
public OleDbDataReader WDSResults;
|
||||
public bool DisplayHiddenFiles { get; set; }
|
||||
|
||||
private readonly ISearch WindowsIndexerSearch;
|
||||
private readonly object _lock = new object();
|
||||
|
||||
private readonly UInt32 FILE_ATTRIBUTE_HIDDEN = 0x2;
|
||||
|
||||
public WindowsSearchAPI(ISearch windowsIndexerSearch, bool displayHiddenFiles = false)
|
||||
{
|
||||
this.WindowsIndexerSearch = windowsIndexerSearch;
|
||||
this.DisplayHiddenFiles = displayHiddenFiles;
|
||||
}
|
||||
|
||||
public List<SearchResult> ExecuteQuery(ISearchQueryHelper queryHelper, string keyword)
|
||||
{
|
||||
List<SearchResult> _Result = new List<SearchResult>();
|
||||
|
||||
// Generate SQL from our parameters, converting the userQuery from AQS->WHERE clause
|
||||
string sqlQuery = queryHelper.GenerateSQLFromUserQuery(keyword);
|
||||
|
||||
// --- Perform the query ---
|
||||
// create an OleDbConnection object which connects to the indexer provider with the windows application
|
||||
using (conn = new OleDbConnection(queryHelper.ConnectionString))
|
||||
{
|
||||
// open the connection
|
||||
conn.Open();
|
||||
// execute the command, which returns the results as an OleDBResults.
|
||||
List<OleDBResult> oleDBResults = WindowsIndexerSearch.Query(queryHelper.ConnectionString, sqlQuery);
|
||||
|
||||
// now create an OleDB command object with the query we built above and the connection we just opened.
|
||||
using (command = new OleDbCommand(sqlQuery, conn))
|
||||
// Loop over all records from the database
|
||||
foreach(OleDBResult oleDBResult in oleDBResults)
|
||||
{
|
||||
if (oleDBResult.fieldData[0] == DBNull.Value || oleDBResult.fieldData[1] == DBNull.Value || oleDBResult.fieldData[2] == DBNull.Value)
|
||||
{
|
||||
// execute the command, which returns the results as an OleDbDataReader.
|
||||
using (WDSResults = command.ExecuteReader())
|
||||
{
|
||||
if(WDSResults.HasRows)
|
||||
{
|
||||
while (WDSResults.Read())
|
||||
{
|
||||
if (WDSResults.GetValue(0) != DBNull.Value && WDSResults.GetValue(1) != DBNull.Value)
|
||||
{
|
||||
var uri_path = new Uri(WDSResults.GetString(0));
|
||||
var result = new SearchResult
|
||||
{
|
||||
Path = uri_path.LocalPath,
|
||||
Title = WDSResults.GetString(1)
|
||||
};
|
||||
_Result.Add(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
UInt32 fileAttributes = (UInt32)((Int64)oleDBResult.fieldData[2]);
|
||||
bool isFileHidden = (fileAttributes & FILE_ATTRIBUTE_HIDDEN) == FILE_ATTRIBUTE_HIDDEN;
|
||||
|
||||
if (DisplayHiddenFiles || !isFileHidden)
|
||||
{
|
||||
var uri_path = new Uri((string)oleDBResult.fieldData[0]);
|
||||
var result = new SearchResult
|
||||
{
|
||||
Path = uri_path.LocalPath,
|
||||
Title = (string)oleDBResult.fieldData[1]
|
||||
};
|
||||
_Result.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
return _Result;
|
||||
|
@ -91,7 +91,7 @@ namespace Microsoft.Plugin.Indexer.SearchHelper
|
|||
queryHelper.QueryMaxResults = maxCount;
|
||||
|
||||
// Set list of columns we want to display, getting the path presently
|
||||
queryHelper.QuerySelectColumns = "System.ItemUrl, System.FileName";
|
||||
queryHelper.QuerySelectColumns = "System.ItemUrl, System.FileName, System.FileAttributes";
|
||||
|
||||
// Set additional query restriction
|
||||
queryHelper.QueryWhereRestrictions = "AND scope='file:'";
|
||||
|
|
|
@ -4,6 +4,9 @@ using System.Collections.Generic;
|
|||
using System.Data.OleDb;
|
||||
using Microsoft.Search.Interop;
|
||||
using Microsoft.Plugin.Indexer.SearchHelper;
|
||||
using Microsoft.Plugin.Indexer.Interface;
|
||||
using Moq;
|
||||
using System.Linq;
|
||||
|
||||
namespace Wox.Test.Plugins
|
||||
{
|
||||
|
@ -11,13 +14,20 @@ namespace Wox.Test.Plugins
|
|||
[TestFixture]
|
||||
public class WindowsIndexerTest
|
||||
{
|
||||
private WindowsSearchAPI _api = new WindowsSearchAPI();
|
||||
|
||||
public WindowsSearchAPI GetWindowsSearchAPI()
|
||||
{
|
||||
var mock = new Mock<ISearch>();
|
||||
mock.Setup(x => x.Query("dummy-connection-string", "dummy-query")).Returns(new List<OleDBResult>());
|
||||
return new WindowsSearchAPI(mock.Object);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void InitQueryHelper_ShouldInitialize_WhenFunctionIsCalled()
|
||||
{
|
||||
// Arrange
|
||||
int maxCount = 10;
|
||||
WindowsSearchAPI _api = GetWindowsSearchAPI();
|
||||
ISearchQueryHelper queryHelper = null;
|
||||
|
||||
// Act
|
||||
|
@ -34,6 +44,7 @@ namespace Wox.Test.Plugins
|
|||
// Arrange
|
||||
ISearchQueryHelper queryHelper;
|
||||
String pattern = "*";
|
||||
WindowsSearchAPI _api = GetWindowsSearchAPI();
|
||||
_api.InitQueryHelper(out queryHelper, 10);
|
||||
|
||||
// Act
|
||||
|
@ -50,6 +61,7 @@ namespace Wox.Test.Plugins
|
|||
// Arrange
|
||||
ISearchQueryHelper queryHelper;
|
||||
String pattern = "tt*^&)";
|
||||
WindowsSearchAPI _api = GetWindowsSearchAPI();
|
||||
_api.InitQueryHelper(out queryHelper, 10);
|
||||
|
||||
// Act
|
||||
|
@ -66,6 +78,7 @@ namespace Wox.Test.Plugins
|
|||
// Arrange
|
||||
ISearchQueryHelper queryHelper;
|
||||
String pattern = "tt%^&)";
|
||||
WindowsSearchAPI _api = GetWindowsSearchAPI();
|
||||
_api.InitQueryHelper(out queryHelper, 10);
|
||||
|
||||
// Act
|
||||
|
@ -82,6 +95,7 @@ namespace Wox.Test.Plugins
|
|||
// Arrange
|
||||
ISearchQueryHelper queryHelper;
|
||||
String pattern = "tt_^&)";
|
||||
WindowsSearchAPI _api = GetWindowsSearchAPI();
|
||||
_api.InitQueryHelper(out queryHelper, 10);
|
||||
|
||||
// Act
|
||||
|
@ -98,6 +112,7 @@ namespace Wox.Test.Plugins
|
|||
// Arrange
|
||||
ISearchQueryHelper queryHelper;
|
||||
String pattern = "tt?^&)";
|
||||
WindowsSearchAPI _api = GetWindowsSearchAPI();
|
||||
_api.InitQueryHelper(out queryHelper, 10);
|
||||
|
||||
// Act
|
||||
|
@ -114,6 +129,7 @@ namespace Wox.Test.Plugins
|
|||
// Arrange
|
||||
ISearchQueryHelper queryHelper;
|
||||
String pattern = "tt^&)bc";
|
||||
WindowsSearchAPI _api = GetWindowsSearchAPI();
|
||||
_api.InitQueryHelper(out queryHelper, 10);
|
||||
|
||||
// Act
|
||||
|
@ -128,37 +144,75 @@ namespace Wox.Test.Plugins
|
|||
public void ExecuteQuery_ShouldDisposeAllConnections_AfterFunctionCall()
|
||||
{
|
||||
// Arrange
|
||||
ISearchQueryHelper queryHelper;
|
||||
_api.InitQueryHelper(out queryHelper, 10);
|
||||
_api.ModifyQueryHelper(ref queryHelper, "*");
|
||||
string keyword = "test";
|
||||
bool commandDisposed = false;
|
||||
bool resultDisposed = false;
|
||||
OleDBSearch oleDbSearch = new OleDBSearch();
|
||||
WindowsSearchAPI _api = new WindowsSearchAPI(oleDbSearch);
|
||||
|
||||
// Act
|
||||
_api.ExecuteQuery(queryHelper, keyword);
|
||||
try
|
||||
{
|
||||
_api.command.ExecuteReader();
|
||||
}
|
||||
catch(InvalidOperationException)
|
||||
{
|
||||
commandDisposed = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_api.WDSResults.Read();
|
||||
}
|
||||
catch(InvalidOperationException)
|
||||
{
|
||||
resultDisposed = true;
|
||||
}
|
||||
_api.Search("FilePath");
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(_api.conn.State == System.Data.ConnectionState.Closed);
|
||||
Assert.IsTrue(commandDisposed);
|
||||
Assert.IsTrue(resultDisposed);
|
||||
Assert.IsTrue(oleDbSearch.HaveAllDisposableItemsBeenDisposed());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WindowsSearchAPI_ShouldShowHiddenFiles_WhenDisplayHiddenFilesIsTrue()
|
||||
{
|
||||
// Arrange
|
||||
OleDBResult unHiddenFile = new OleDBResult(new List<object>() { "C:/test/path/file1.txt", "file1.txt", (Int64)0x0 });
|
||||
OleDBResult hiddenFile = new OleDBResult(new List<object>() { "C:/test/path/file2.txt", "file2.txt", (Int64)0x2 });
|
||||
List<OleDBResult> results = new List<OleDBResult>() { hiddenFile, unHiddenFile };
|
||||
var mock = new Mock<ISearch>();
|
||||
mock.Setup(x => x.Query(It.IsAny<string>(), It.IsAny<string>())).Returns(results);
|
||||
WindowsSearchAPI _api = new WindowsSearchAPI(mock.Object, true);
|
||||
|
||||
// Act
|
||||
var windowsSearchAPIResults = _api.Search("FilePath");
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(windowsSearchAPIResults.Count() == 2);
|
||||
Assert.IsTrue(windowsSearchAPIResults.Any(x => x.Title == "file1.txt"));
|
||||
Assert.IsTrue(windowsSearchAPIResults.Any(x => x.Title == "file2.txt"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WindowsSearchAPI_ShouldNotShowHiddenFiles_WhenDisplayHiddenFilesIsFalse()
|
||||
{
|
||||
// Arrange
|
||||
OleDBResult unHiddenFile = new OleDBResult(new List<object>() { "C:/test/path/file1.txt", "file1.txt", (Int64)0x0 });
|
||||
OleDBResult hiddenFile = new OleDBResult(new List<object>() { "C:/test/path/file2.txt", "file2.txt", (Int64)0x2 });
|
||||
List<OleDBResult> results = new List<OleDBResult>() { hiddenFile, unHiddenFile };
|
||||
var mock = new Mock<ISearch>();
|
||||
mock.Setup(x => x.Query(It.IsAny<string>(), It.IsAny<string>())).Returns(results);
|
||||
WindowsSearchAPI _api = new WindowsSearchAPI(mock.Object, false);
|
||||
|
||||
// Act
|
||||
var windowsSearchAPIResults = _api.Search("FilePath");
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(windowsSearchAPIResults.Count() == 1);
|
||||
Assert.IsTrue(windowsSearchAPIResults.Any(x => x.Title == "file1.txt"));
|
||||
Assert.IsFalse(windowsSearchAPIResults.Any(x => x.Title == "file2.txt"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WindowsSearchAPI_ShouldNotReturnResultsWithNullValue_WhenDbResultHasANullColumn()
|
||||
{
|
||||
// Arrange
|
||||
OleDBResult file1 = new OleDBResult(new List<object>() { "C:/test/path/file1.txt", DBNull.Value, (Int64)0x0 });
|
||||
OleDBResult file2 = new OleDBResult(new List<object>() { "C:/test/path/file2.txt", "file2.txt", (Int64)0x0 });
|
||||
|
||||
List<OleDBResult> results = new List<OleDBResult>() { file1, file2 };
|
||||
var mock = new Mock<ISearch>();
|
||||
mock.Setup(x => x.Query(It.IsAny<string>(), It.IsAny<string>())).Returns(results);
|
||||
WindowsSearchAPI _api = new WindowsSearchAPI(mock.Object, false);
|
||||
|
||||
// Act
|
||||
var windowsSearchAPIResults = _api.Search("FilePath");
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(windowsSearchAPIResults.Count() == 1);
|
||||
Assert.IsFalse(windowsSearchAPIResults.Any(x => x.Title == "file1.txt"));
|
||||
Assert.IsTrue(windowsSearchAPIResults.Any(x => x.Title == "file2.txt"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue