From e9278886246d8f4fb37339bd57f75adcdaaa8c7b Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Tue, 16 Oct 2018 10:35:03 +0100 Subject: [PATCH 01/30] Refactoring --- Engine/NodeBase.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Engine/NodeBase.cs b/Engine/NodeBase.cs index eca1ba9..4023474 100644 --- a/Engine/NodeBase.cs +++ b/Engine/NodeBase.cs @@ -217,13 +217,6 @@ public string GetFetchDataQuery(int? startRow = null, int? rowCount = null) { var query = new StringBuilder(); - if (this is DataProcessorNode && (DatabaseType == DatabaseType.SQLServer || DatabaseType == DatabaseType.PostgreSQL)) - { - IList nodes = new List(); - FetchOrderedDependencies(nodes); - query.Append(BuildWithSql(nodes)); - } - if (DatabaseType == DatabaseType.MySQL) { query.AppendFormat("SELECT {0} FROM ({1} ORDER BY {2}) AS results", @@ -238,6 +231,10 @@ public string GetFetchDataQuery(int? startRow = null, int? rowCount = null) } else if (DatabaseType == DatabaseType.SQLServer || DatabaseType == DatabaseType.PostgreSQL) { + IList nodes = new List(); + FetchOrderedDependencies(nodes); + query.Append(BuildWithSql(nodes)); + if (startRow.HasValue && rowCount.HasValue) { // In order for this to work, we will need a ROW_NUMBER column in the CTE, which we From 3bca49225d8e6189904c3cee427eeff2b5eb6214 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Tue, 16 Oct 2018 11:54:38 +0100 Subject: [PATCH 02/30] Working on documentation --- docs/advanced.md | 26 +++++++++++++++++++++ docs/customizing.md | 55 ++++++--------------------------------------- 2 files changed, 33 insertions(+), 48 deletions(-) diff --git a/docs/advanced.md b/docs/advanced.md index e69de29..bd7a916 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -0,0 +1,26 @@ +# The Advanced Query Builder + +The advanced query builder in QueryTree is a visual programming language for building database queries by connecting logic blocks together in a chain. It can be used to build more powerful queries in than the default query builder. + +## Enabling + +The advanced query builder is not enabled by default, as some users would find it too advanced to use. However, it can be easily enabled by setting the `AllowAdvancedQuery` setting to `true` in the appsettings.config file (see [here](/docs/customizing.md) for more information) or by setting the Customization__AllowAdvancedQuery environment variable to "true". + +Once enabled, a link to the Advanced Query Builder will be available at the top of the default query builder. + +## The Tools + +Queries can be build by connecting any number of the following tools or nodes togehter: + +* *Data Table*: All queries need to start with at least one data table tool. This tool is used to load data in from a table in the database. The tool's options window allows you to pick which table to load data from. +* *Join*: Data from two sources (e.g. Data Table tools) can be combined using the Join tool. This tool's options window allows you to specify what kind of join is used. +* *Filter*: This tool allows users to filter out rows that are not wanted. +* *Append*: This tool allows users to combine two sets of rows into a single list. Unlike most other tools, the Append tool can have any number of inputs. By combining multiple Filter tools into one Append tool, complex filtering logic can be achieved. +* *Extract*: This tool is useful for extracting blocks of text from inside other columns, e.g. extracting the year from a date field. +* *Summerize*: This tool can be used for calculating the sum, average, maximum, minimum or median value of one or more columns, and for grouping rows by distinct elements. +* *Select*: This tool can be used to pick which columns are shown, and in what order. +* *Sort*: This tool can be used to sort rows by one or more columns. +* *Pie Chart*: This tool can be used to display a pie charting summarizing the rows in its input data. +* *Bar Chart*: This tool can be used to display a bar charting summarizing the rows in its input data. +* *Line Chart*: This tool can be used to display a line charting summarizing the rows in its input data. + diff --git a/docs/customizing.md b/docs/customizing.md index 29f295d..50c9ea2 100644 --- a/docs/customizing.md +++ b/docs/customizing.md @@ -1,30 +1,10 @@ -QueryTree is open source under the [LGPL3](https://en.wikipedia.org/wiki/GNU_Lesser_General_Public_License) license. This means you can use QueryTree in conjunction with your commercial app without the need to open source your own proprietary code. However, any changes you make to the QueryTree code need to be contributed upstream. Note that this applies only to any QueryTree code you modify and nothing else associated with your app. +# Customization Options in QueryTree -QueryTree also comes with a number of configuration settings that allow you to customise the appearance and behaviour of your installation. Altering these settings does not constitute making changes to the QueryTree code so you do not need to release your configuration changes. - -## An example - -So, for example, QueryTree has options for customisation where you can: - - * Replace the QueryTree logo and system name with your own image and name, and add your own CSS stylesheet to alter the appearance of the QueryTree UI. - * Set QueryTree up in a virtual folder or subdomain (e.g. “reports/” or “reporting.”). This enables a seamless integration between QueryTree’s interface and your own app. - * Link to our QueryTree.Engine DLL library in your app without open sourcing your app. Our engine DLL converts JSON data query definitions created by the QueryTree UI and stored in the QueryTree “Queries” table, into SQL queries that can be run by your app. - -## Single Sign On - -QueryTree does not currently support any form of Single Sign On system (e.g. OAuth, SAML, LDAP, ActiveDirectory). This is an area the authors of the system would like to develop as we believe it could be used to enable seamless integrations where users move from a database driven app to it’s QueryTree reporting panel, without having to re-enter their email and password. - -If you would like to submit pull requests to QueryTree to integrate it with single sign on systems, please go ahead. - -Under the conditions of the LGPL license, if you did any of these customisations you’d need to release the changes made to the QueryTree code. Doing so would keep your app closed source while still enabling you to use QueryTree. - -## Technical Details - -### Customising QueryTree’s UI +QueryTree comes with a number of configuration settings that allow you to customize the appearance and behaviour of your installation. Altering these settings does not constitute making changes to the QueryTree code so you do not need to release your configuration changes like you would changes to the source code (see the terms of the [LGPL3](https://en.wikipedia.org/wiki/GNU_Lesser_General_Public_License) license for more information). The [appsettings.json](/Web/appsettings.json) file contains a section titled “Customization”: -``` +```json "Customization": { "SystemName": "QueryTree", "SystemLogo": "/images/querytree_logo.png", @@ -35,28 +15,7 @@ The [appsettings.json](/Web/appsettings.json) file contains a section titled “ The following section details what each of the settings do: - * *SystemName*: used to set the application name that appears on the top left of all the QueryTree pages. Note: the application will still display a footer which includes the words “Powered by QueryTree” - * *SystemLogo*: contains the URL of an image that will be displayed on the top left of each page. Change this setting to display your own system logo on the top left. This image does not necessarily need to be located inside QueryTree’s wwwroot folder. It could potentially be hosted at a different domain. Note: the application will still display a smaller version of the QueryTree logo in the footer. - * *ExtraCSS*: points to a .css file that will be referenced by all the QueryTree pages, after it’s own CSS. This file does not necessarily need to be located inside QueryTree’s wwwroot folder. It could potentially be hosted at a different domain. - * *AllowAdvancedQuery*: indicated whether users will be given the option to use the advanced query builder. See [here](/docs/advanced.md) for more information. - - -### Using QueryTree’s Engine - -The QueryTree.Engine DLL is a dotnet core DLL that converts JSON query definitions that have been created by the QueryTree UI, into SQL queries for a specific database engine. - -You will find QueryTree’s saved queries in its Queries table. The QueryDefinition column contains a JSON definition of the query. - -The engine can be referenced in your dotnet code and called like this: - -``` -TODO: move the GetDbModel stuff into the Engine -var query = ... // fetch row from Queries table -var queryDefinition = JsonConvert.DeserializeObject(query.QueryDefinition); -var nodes = JsonConvert.SerializeObject(queryDefinition.Nodes); -var nodeId = queryDefinition.SelectedNodeId.ToString(); - -var engine = new Engine.Query(Engine.DatabaseType.MySQL, nodes, dbTables); -var sql = engine.GetSql(nodeId, startRow, rowCount); // startRow and rowCount support paging, pass Null to retrieve all rows -``` - +* *SystemName*: used to set the application name that appears on the top left of all the QueryTree pages. Note: the application will still display a footer which includes the words “Powered by QueryTree” +* *SystemLogo*: contains the URL of an image that will be displayed on the top left of each page. Change this setting to display your own system logo on the top left. This image does not necessarily need to be located inside QueryTree’s wwwroot folder. It could potentially be hosted at a different domain. Note: the application will still display a smaller version of the QueryTree logo in the footer. +* *ExtraCSS*: points to a .css file that will be referenced by all the QueryTree pages, after it’s own CSS. This file does not necessarily need to be located inside QueryTree’s wwwroot folder. It could potentially be hosted at a different domain. +* *AllowAdvancedQuery*: indicated whether users will be given the option to use the advanced query builder. See [here](/docs/advanced.md) for more information. From e436a7b1ff3a165dbded262da4f5ea0bee2314c8 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Tue, 16 Oct 2018 13:10:25 +0100 Subject: [PATCH 03/30] Upgraded to dotnet 2.1 (issue #38) --- .circleci/config.yml | 4 +-- Dockerfile | 4 +-- Engine/Engine.csproj | 2 +- README.md | 41 +++++++++++++++----------- Tests/Tests.csproj | 2 +- Web/Managers/PasswordManager.cs | 1 - Web/Managers/ScheduledEmailManager.cs | 1 - Web/QueryTree.csproj | 38 +++++++++++------------- Web/Startup.cs | 1 - Web/Views/Shared/_FooterPartial.cshtml | 5 ++-- Web/Views/Team/Invite.cshtml | 4 +-- appveyor.yml | 2 +- 12 files changed, 52 insertions(+), 53 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0cfa8e3..0d3260e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,14 +2,14 @@ version: 2 jobs: build: docker: - - image: microsoft/aspnetcore-build:2.0-stretch + - image: microsoft/aspnetcore-build:2.1-stretch steps: - checkout - run: npm install less -g - run: dotnet build ./Web/QueryTree.csproj -v n test: docker: - - image: microsoft/aspnetcore-build:2.0-stretch + - image: microsoft/aspnetcore-build:2.1-stretch steps: - checkout - run: npm install less -g diff --git a/Dockerfile b/Dockerfile index 974e72a..ddccabc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM microsoft/aspnetcore-build:2.0-stretch as builder +FROM microsoft/aspnetcore-build:2.1-stretch as builder WORKDIR /build COPY . . RUN npm install less -g @@ -6,7 +6,7 @@ RUN dotnet restore RUN dotnet publish -c Release ./Web/QueryTree.csproj -o /dist -FROM microsoft/aspnetcore:2.0-stretch as runtime +FROM microsoft/aspnetcore:2.1-stretch as runtime WORKDIR /app COPY --from=builder /dist . COPY ./Web/EmailTemplates ./EmailTemplates diff --git a/Engine/Engine.csproj b/Engine/Engine.csproj index 8604837..953ed70 100644 --- a/Engine/Engine.csproj +++ b/Engine/Engine.csproj @@ -1,7 +1,7 @@ - netcoreapp2.0 + netcoreapp2.1 diff --git a/README.md b/README.md index 0ace613..557ca91 100644 --- a/README.md +++ b/README.md @@ -42,56 +42,61 @@ for more information. - [Running the Tests](#running-the-tests) - [Running with Docker](/docs/docker.md) -### Running from Source +### Prerequisites -To run QueryTree from it's source code, you will need to install the .NET Core SDK. You can download -the SDK for your platform here: [https://www.microsoft.com/net/download/core](https://www.microsoft.com/net/download/core) +To build binaries or run from source you need the [.NET Core SDK v2.1](https://www.microsoft.com/net/download) and [NPM](https://nodejs.org/) installed. -Once the .NET Core SDK is installed, follow these steps: +You will need to install the Less.js compiler using NPM: + +```sh +npm install less -g +``` + +### Running from Source + +Check you have the prerequisites installed, then follow these steps: 1. Clone this repo into a folder 2. At the command prompt, cd into the folder, then into the "Web" folder. 3. Type: - + ```sh dotnet run ``` - -4. Visit [http://localhost:5000/](http://localhost:5000/) in your browser. You should see the QueryTree application. *Dotnet may decide to run it on a different port if 5000 is not available, check your terminal output. -If you would like to run QueryTree with your own local development settings, you can add a Web/usersettings.json file containing a modified copy of appsettings.json. Settings in this file will override appsettings.json. However, this file will be ignored by git. +4. Dotnet should report that the applicaiton is running, e.g. -### Building Binaries +```sh +Now listening on: http://localhost:54182 +Application started. Press Ctrl+C to shut down. +``` -You may need to build a release binary to be run with the [.NET Core 2.0.x runtime](https://www.microsoft.com/net/download/core#/runtime). These binaries can be used on systems without the full .NET Core SDK. To build the initial binaries you need the [.NET Core SDK](https://www.microsoft.com/net/download) and [NPM](https://nodejs.org/) installed. +Visit the URL shown in your browser. You should see the QueryTree application. -You will need to install the Less.js compiler using NPM: +If you would like to run QueryTree with your own local development settings, you can add a Web/usersettings.json file containing a modified copy of appsettings.json. Settings in this file will override appsettings.json. However, this file will be ignored by git. -``` -npm install less -g -``` +### Building Binaries To build a release binary from the project root execute: -``` +```sh dotnet publish -c Release ./Web/QueryTree.csproj -o ./dist ``` This will create a release folder in `dist` of all the unpacked QueryTree binaries and its dependencies. - ### Running from Binaries QueryTree is built using .NET Core. To run QueryTree on your server -you will need to install the .NET Core 2.0.x runtime. You can download +you will need to install the .NET Core 2.1.x runtime. You can download the installer [here](https://www.microsoft.com/net/download/core#/run). To verify that you have the .NET runtime installed, open a terminal/cmd window and type -``` +```sh dotnet --version ``` diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 0debf8f..d1655ed 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp2.0 + netcoreapp2.1 diff --git a/Web/Managers/PasswordManager.cs b/Web/Managers/PasswordManager.cs index 582b55b..61b7cd7 100644 --- a/Web/Managers/PasswordManager.cs +++ b/Web/Managers/PasswordManager.cs @@ -6,7 +6,6 @@ using System.Security.Cryptography; using Microsoft.AspNetCore.Hosting; using QueryTree.Models; -using Microsoft.Extensions.PlatformAbstractions; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Options; diff --git a/Web/Managers/ScheduledEmailManager.cs b/Web/Managers/ScheduledEmailManager.cs index 056af6e..5c64fb3 100644 --- a/Web/Managers/ScheduledEmailManager.cs +++ b/Web/Managers/ScheduledEmailManager.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using Microsoft.AspNetCore.Hosting; using QueryTree.Models; -using Microsoft.Extensions.PlatformAbstractions; using Newtonsoft.Json; using MimeKit; using Microsoft.EntityFrameworkCore; diff --git a/Web/QueryTree.csproj b/Web/QueryTree.csproj index a8c4bfa..9d695ff 100644 --- a/Web/QueryTree.csproj +++ b/Web/QueryTree.csproj @@ -1,6 +1,6 @@  - netcoreapp2.0 + netcoreapp2.1 aspnet-QueryTree-46E61BB7-4239-4527-BA0C-1B66D664CC58 0.0.0 D4 Software Ltd @@ -27,29 +27,25 @@ - - - - - - - + + + - + - + - - - - - - + + + + + + - - + + @@ -70,9 +66,9 @@ - - - + + + diff --git a/Web/Startup.cs b/Web/Startup.cs index e5e5991..47c8a22 100644 --- a/Web/Startup.cs +++ b/Web/Startup.cs @@ -91,7 +91,6 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF { app.UseDeveloperExceptionPage(); app.UseDatabaseErrorPage(); - app.UseBrowserLink(); } else { diff --git a/Web/Views/Shared/_FooterPartial.cshtml b/Web/Views/Shared/_FooterPartial.cshtml index 509cac9..f0a3c38 100644 --- a/Web/Views/Shared/_FooterPartial.cshtml +++ b/Web/Views/Shared/_FooterPartial.cshtml @@ -1,5 +1,6 @@ -@{ - var version = Microsoft.Extensions.PlatformAbstractions.PlatformServices.Default.Application.ApplicationVersion; +@using System.Reflection; +@{ + var version = typeof(Program).GetTypeInfo().Assembly.GetCustomAttribute().Version; }
diff --git a/Web/Views/Team/Invite.cshtml b/Web/Views/Team/Invite.cshtml index 6eb8692..132a27d 100644 --- a/Web/Views/Team/Invite.cshtml +++ b/Web/Views/Team/Invite.cshtml @@ -9,7 +9,7 @@ { } @@ -108,7 +108,7 @@

Any databases created by this new user will be visible to everyone in your team.

- @Html.Partial("_AccessTypeHelp") +
diff --git a/appveyor.yml b/appveyor.yml index 4e22aa2..5ff7c61 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,7 +12,7 @@ dotnet_csproj: before_build: - cmd: dotnet restore ./Web/QueryTree.csproj --verbosity m build_script: -- cmd: dotnet publish --framework netcoreapp2.0 -c Release ./Web/QueryTree.csproj -o ./dist +- cmd: dotnet publish --framework netcoreapp2.1 -c Release ./Web/QueryTree.csproj -o ./dist after_build: - cmd: 7z a QueryTree.zip %APPVEYOR_BUILD_FOLDER%/Web/dist/* - cmd: 7z a QueryTree.zip %APPVEYOR_BUILD_FOLDER%/README.md From eda21e5b99f0129f9bf3bc5cf9edc7ca6bd21dce Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Tue, 16 Oct 2018 14:51:30 +0100 Subject: [PATCH 04/30] Documentation improvements --- README.md | 86 ++++++++++++++++++++++--------------------------------- 1 file changed, 35 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 557ca91..586436b 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ Now listening on: http://localhost:54182 Application started. Press Ctrl+C to shut down. ``` -Visit the URL shown in your browser. You should see the QueryTree application. +Visit the URL shown in your browser. You should see the QueryTree application. If you would like to run QueryTree with your own local development settings, you can add a Web/usersettings.json file containing a modified copy of appsettings.json. Settings in this file will override appsettings.json. However, this file will be ignored by git. @@ -89,20 +89,15 @@ This will create a release folder in `dist` of all the unpacked QueryTree binari ### Running from Binaries -QueryTree is built using .NET Core. To run QueryTree on your server -you will need to install the .NET Core 2.1.x runtime. You can download -the installer [here](https://www.microsoft.com/net/download/core#/run). +To run QueryTree on your server you will need to install the .NET Core 2.1.x runtime. (It is not necessary to install the full .NET SDK, just the runtime.) You can download the installer [here](https://www.microsoft.com/net/download/core#/runtime). -To verify that you have the .NET runtime installed, open a terminal/cmd -window and type +To verify that you have the .NET runtime installed, open a terminal/cmd window and type ```sh dotnet --version ``` If the command returns a version number, you're ready to run QueryTree. -If not, please visit [https://www.microsoft.com/net/download/core#/runtime](https://www.microsoft.com/net/download/core#/runtime) -and follow the instructions for your platform. Once the dotnet runtime is installed, follow these steps: @@ -112,11 +107,18 @@ Once the dotnet runtime is installed, follow these steps: 3. At the command prompt, type: -``` +```sh dotnet QueryTree.dll ``` -4. Visit [http://localhost:5000/](http://localhost:5000/) in your browser. You should see the QueryTree application. +4. Dotnet should report that the applicaiton is running, e.g. + +```sh +Now listening on: http://localhost:5000 +Application started. Press Ctrl+C to shut down. +``` + +Visit the URL shown in your browser. You should see the QueryTree application. 5. For use in production environments, QueryTree should be run behind a reverse proxy such as nginx. For more information on hosting QueryTree using nginx see: https://docs.microsoft.com/en-us/aspnet/core/publishing/linuxproduction @@ -125,9 +127,10 @@ in IIS see: https://docs.microsoft.com/en-us/aspnet/core/publishing/iis ### Running the Tests -To run the automated tests in this project, cd into the "Tests" folder, then type: +To run the automated tests in this project, from the project root folder, type the following: -``` +```sh +cd Tests dotnet test ``` @@ -137,8 +140,7 @@ See the full Docker guide: [docs/docker.md](/docs/docker.md) ## Getting Started -1. When first run, QueryTree will have no users and no database connections. Visiting -app, you will be presented with a login page: +1. When first run, QueryTree will have no users and no database connections. Visiting app, you will be presented with a login page: ![The QueryTree login page](http://querytreeapp.com/img/screenshots/querytree-login.png "The QueryTree login page") @@ -146,73 +148,55 @@ app, you will be presented with a login page: ![The QueryTree signup page](http://querytreeapp.com/img/screenshots/querytree-signup.png "The QueryTree signup page") -3. Having signed in, you won't have any database connections configured. The system -will ask you whether you want to set up a connection yourself, or invite another user -who might be able to do it for you. +3. Having signed in, you won't have any database connections configured. The system will ask you whether you want to set up a connection yourself, or invite another user who might be able to do it for you. ![The QueryTree onboard page](http://querytreeapp.com/img/screenshots/querytree-onboarding.png "The QueryTree onboarding page") -4. Assuming you have a database that you can connect to, select the "+ Connect Database" -option. You will see the Create Connection page: +4. Assuming you have a database that you can connect to, select the "+ Connect Database" option. You will see the Create Connection page: ![The QueryTree create connection page](http://querytreeapp.com/img/screenshots/querytree-create-connection.png "The QueryTree create connection page") -Once all the information is entered, you can check the connection by pressing the -"Test Connection" button. If the system reports that the conneciton is working, -press "Save". +Once all the information is entered, you can check the connection by pressing the "Test Connection" button. If the system reports that the conneciton is working, press "Save". ![The QueryTree test connection feature](http://querytreeapp.com/img/screenshots/querytree-test-connection.png "The QueryTree test connection feature") -5. You will be taken to the reports list for this connection, but there won't be -any reports yet. +5. You will be taken to the reports list for this connection, but there won't be any reports yet. ![The QueryTree reports page](http://querytreeapp.com/img/screenshots/querytree-reports-empty.png "The QueryTree reports page") 6. Click on "+ Create Report". You will be taken to the defualt report builder -7. All reports start by picking a datbase table to start from. From there -the report builder will prompt you to select any related tables that it can -join to. For example, in this screenshot, I have selected the "orders" table -and QueryTree is prompting me to join the "users" table. QueryTree can see -that "orders" has a link to "users" so it offers to join the tables. +7. All reports start by picking a datbase table to start from. From there the report builder will prompt you to select any related tables that it can join to. For example, in this screenshot, I have selected the "orders" table and QueryTree is prompting me to join the "users" table. QueryTree can see that "orders" has a link to "users" so it offers to join the tables. ![The QueryTree create report page](http://querytreeapp.com/img/screenshots/querytree-create-report-orders.png "The QueryTree create report page") -For more information on how to help QueryTree automatically join between tables -in your database see [QueryTree's Auto Join feature](/docs/autojoin.md) +For more information on how to help QueryTree automatically join between tables in your database see [QueryTree's Auto Join feature](/docs/autojoin.md) -8. Having selected a starting table, and any relevant related tables, click Next. -The filter panel will open and you will be prompted to add one or more Filters. +8. Having selected a starting table, and any relevant related tables, click Next. The filter panel will open and you will be prompted to add one or more Filters. ![The QueryTree report filter panel](http://querytreeapp.com/img/screenshots/querytree-report-filter.png "The QueryTree report filter panel") -9. Once you are happy with the filters, you have the option to summarize the data -that is being shown in the results panel. Summerizing the data can mean totaling, -averaging, counting or finding the minimum/maximum values, for one or more columns. -You can do this for all the data, or for different groups of values. For example, -you could find the average value of the orders, for each country. +9. Once you are happy with the filters, you have the option to summarize the data that is being shown in the results panel. summerizing the data can mean totaling, averaging, counting or finding the minimum/maximum values, for one or more columns. You can do this for all the data, or for different groups of values. For example, you could find the average value of the orders, for each country. ![The QueryTree report summerize panel](http://querytreeapp.com/img/screenshots/querytree-report-summerize.png "The QueryTree report summerize panel") -10. Finally, you have the option of generating a chart from the data in the results -panel. +10. Finally, you have the option of generating a chart from the data in the results panel. ![The QueryTree report chart panel](http://querytreeapp.com/img/screenshots/querytree-report-chart.png "The QueryTree report chart panel") -11. Once you are happy with your report, save it by clicking the Save button. You -will be returned to the list of reports for this connection. +11. Once you are happy with your report, save it by clicking the Save button. You will be returned to the list of reports for this connection. ## Other Guides - * [Scheduling Reports](/docs/scheduling.md) - * [Sharing Individual Reports](/docs/sharing.md) - * [Team Management](/docs/teams.md) - * [The Advanced Query Builder](/docs/advanced.md) - * [Auto Join](/docs/autojoin.md) - * [Customizing QueryTree](/docs/customizing.md) - * [Building a password manager](/docs/password-manager.md) - * [Using with Docker](/docs/docker.md) - * [Configuring Email](/docs/mail.md) +- [Scheduling Reports](/docs/scheduling.md) +- [Sharing Individual Reports](/docs/sharing.md) +- [Team Management](/docs/teams.md) +- [The Advanced Query Builder](/docs/advanced.md) +- [Auto Join](/docs/autojoin.md) +- [Customizing QueryTree](/docs/customizing.md) +- [Building a password manager](/docs/password-manager.md) +- [Using with Docker](/docs/docker.md) +- [Configuring Email](/docs/mail.md) ## License From 9027ece17e1cd40441c352ad2584a9d6cd6f6576 Mon Sep 17 00:00:00 2001 From: Alexey Borodenko Date: Mon, 29 Oct 2018 22:57:40 +0200 Subject: [PATCH 05/30] Update Dockerfile with dotnet no-restore option. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index ddccabc..401204c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ WORKDIR /build COPY . . RUN npm install less -g RUN dotnet restore -RUN dotnet publish -c Release ./Web/QueryTree.csproj -o /dist +RUN dotnet publish --no-restore -c Release ./Web/QueryTree.csproj -o /dist FROM microsoft/aspnetcore:2.1-stretch as runtime From c43e261c1dddd87330beec318d3402489545f232 Mon Sep 17 00:00:00 2001 From: Alexey Borodenko Date: Mon, 29 Oct 2018 22:58:08 +0200 Subject: [PATCH 06/30] Update appveyor.yml with dotnet no-restore option. --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 5ff7c61..579051b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,7 +12,7 @@ dotnet_csproj: before_build: - cmd: dotnet restore ./Web/QueryTree.csproj --verbosity m build_script: -- cmd: dotnet publish --framework netcoreapp2.1 -c Release ./Web/QueryTree.csproj -o ./dist +- cmd: dotnet publish --framework netcoreapp2.1 --no-restore -c Release ./Web/QueryTree.csproj -o ./dist after_build: - cmd: 7z a QueryTree.zip %APPVEYOR_BUILD_FOLDER%/Web/dist/* - cmd: 7z a QueryTree.zip %APPVEYOR_BUILD_FOLDER%/README.md From bed9f59367c7a6338f419b88bf8e8569e4e0c641 Mon Sep 17 00:00:00 2001 From: Alexey Borodenko Date: Mon, 29 Oct 2018 22:59:06 +0200 Subject: [PATCH 07/30] Remove netcoreapp2.1 framework option from publish due to TargetFramework specified in *.csproj. --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 579051b..a102287 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,7 +12,7 @@ dotnet_csproj: before_build: - cmd: dotnet restore ./Web/QueryTree.csproj --verbosity m build_script: -- cmd: dotnet publish --framework netcoreapp2.1 --no-restore -c Release ./Web/QueryTree.csproj -o ./dist +- cmd: dotnet publish --no-restore -c Release ./Web/QueryTree.csproj -o ./dist after_build: - cmd: 7z a QueryTree.zip %APPVEYOR_BUILD_FOLDER%/Web/dist/* - cmd: 7z a QueryTree.zip %APPVEYOR_BUILD_FOLDER%/README.md From 1fc1469ee5ca301be2afeb64e4f9496f303fb44f Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Wed, 31 Oct 2018 15:14:03 +0000 Subject: [PATCH 08/30] Fixed docker build issues --- Dockerfile | 13 +++++++++++-- Web/Program.cs | 1 - 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 401204c..5604552 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,13 @@ -FROM microsoft/aspnetcore-build:2.1-stretch as builder +FROM microsoft/dotnet:2.1-sdk-stretch as builder +ENV NODE_VERSION 10.13.0 +ENV NODE_DOWNLOAD_SHA b4b5d8f73148dcf277df413bb16827be476f4fa117cbbec2aaabc8cc0a8588e1 + +RUN curl -SL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz" --output nodejs.tar.gz \ + && echo "$NODE_DOWNLOAD_SHA nodejs.tar.gz" | sha256sum -c - \ + && tar -xzf "nodejs.tar.gz" -C /usr/local --strip-components=1 \ + && rm nodejs.tar.gz \ + && ln -s /usr/local/bin/node /usr/local/bin/nodejs + WORKDIR /build COPY . . RUN npm install less -g @@ -6,7 +15,7 @@ RUN dotnet restore RUN dotnet publish --no-restore -c Release ./Web/QueryTree.csproj -o /dist -FROM microsoft/aspnetcore:2.1-stretch as runtime +FROM microsoft/dotnet:2.1-aspnetcore-runtime as runtime WORKDIR /app COPY --from=builder /dist . COPY ./Web/EmailTemplates ./EmailTemplates diff --git a/Web/Program.cs b/Web/Program.cs index 8862698..6125aa6 100644 --- a/Web/Program.cs +++ b/Web/Program.cs @@ -25,7 +25,6 @@ public static void Main(string[] args) using (var scope = host.Services.CreateScope()) { var context = scope.ServiceProvider.GetService(); - context.Database.EnsureCreated(); context.Database.Migrate(); } From 2c059a08ab5ab1397cebb159e0df0bea5bdd9c39 Mon Sep 17 00:00:00 2001 From: Alexey Borodenko Date: Wed, 31 Oct 2018 18:29:47 +0200 Subject: [PATCH 09/30] Delete dead reset.css file. --- Web/QueryTree.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/Web/QueryTree.csproj b/Web/QueryTree.csproj index 9d695ff..abdc7d0 100644 --- a/Web/QueryTree.csproj +++ b/Web/QueryTree.csproj @@ -19,7 +19,6 @@ - From 913e83475887092586a7b39da3b00653f89e3c11 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Mon, 11 Feb 2019 16:13:11 +0000 Subject: [PATCH 10/30] Implemented DataStore option to enable QueryTree to use a MS SQL Server for its data store --- .../20170906223136_Initial Version.cs | 9 +++++ Web/Models/CustomizationConfiguration.cs | 1 + Web/Models/DatabaseConnection.cs | 1 + Web/Models/Organisation.cs | 1 + Web/Models/OrganisationInvite.cs | 2 + Web/Startup.cs | 40 ++++++++++++------- 6 files changed, 39 insertions(+), 15 deletions(-) diff --git a/Web/Migrations/20170906223136_Initial Version.cs b/Web/Migrations/20170906223136_Initial Version.cs index f31a8d0..654267c 100644 --- a/Web/Migrations/20170906223136_Initial Version.cs +++ b/Web/Migrations/20170906223136_Initial Version.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; namespace Web.Migrations @@ -41,6 +42,7 @@ protected override void Up(MigrationBuilder migrationBuilder) columns: table => new { OrganisationId = table.Column(nullable: false) + .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn) .Annotation("Sqlite:Autoincrement", true), CreatedOn = table.Column(nullable: false), NumberOfConnections = table.Column(nullable: false), @@ -57,6 +59,7 @@ protected override void Up(MigrationBuilder migrationBuilder) columns: table => new { Id = table.Column(nullable: false) + .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn) .Annotation("Sqlite:Autoincrement", true), ClaimType = table.Column(nullable: true), ClaimValue = table.Column(nullable: true), @@ -115,6 +118,7 @@ protected override void Up(MigrationBuilder migrationBuilder) columns: table => new { Id = table.Column(nullable: false) + .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn) .Annotation("Sqlite:Autoincrement", true), ClaimType = table.Column(nullable: true), ClaimValue = table.Column(nullable: true), @@ -180,6 +184,7 @@ protected override void Up(MigrationBuilder migrationBuilder) columns: table => new { OrganisationInviteId = table.Column(nullable: false) + .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn) .Annotation("Sqlite:Autoincrement", true), AcceptedOn = table.Column(nullable: true), CreatedById = table.Column(nullable: true), @@ -204,6 +209,7 @@ protected override void Up(MigrationBuilder migrationBuilder) columns: table => new { Id = table.Column(nullable: false) + .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn) .Annotation("Sqlite:Autoincrement", true), ContentType = table.Column(nullable: true), CreatedById = table.Column(nullable: true), @@ -226,6 +232,7 @@ protected override void Up(MigrationBuilder migrationBuilder) columns: table => new { DatabaseConnectionID = table.Column(nullable: false) + .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn) .Annotation("Sqlite:Autoincrement", true), CreatedOn = table.Column(nullable: false), DatabaseName = table.Column(nullable: false), @@ -264,6 +271,7 @@ protected override void Up(MigrationBuilder migrationBuilder) columns: table => new { QueryID = table.Column(nullable: false) + .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn) .Annotation("Sqlite:Autoincrement", true), CreatedById = table.Column(nullable: true), CreatedOn = table.Column(nullable: true), @@ -303,6 +311,7 @@ protected override void Up(MigrationBuilder migrationBuilder) columns: table => new { UserDatabaseConnectionID = table.Column(nullable: false) + .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn) .Annotation("Sqlite:Autoincrement", true), ApplicationUserID = table.Column(nullable: true), CreatedById = table.Column(nullable: true), diff --git a/Web/Models/CustomizationConfiguration.cs b/Web/Models/CustomizationConfiguration.cs index adc2ab0..51e6ac9 100644 --- a/Web/Models/CustomizationConfiguration.cs +++ b/Web/Models/CustomizationConfiguration.cs @@ -11,5 +11,6 @@ public class CustomizationConfiguration public string SystemLogo { get; set; } public string ExtraCSS { get; set; } public bool AllowAdvancedQuery { get; set; } + public string DataStore { get; set; } } } diff --git a/Web/Models/DatabaseConnection.cs b/Web/Models/DatabaseConnection.cs index 265086f..f24079f 100644 --- a/Web/Models/DatabaseConnection.cs +++ b/Web/Models/DatabaseConnection.cs @@ -8,6 +8,7 @@ namespace QueryTree.Models { public class DatabaseConnection { + [Key] public int DatabaseConnectionID { get; set; } public int OrganisationId { get; set; } diff --git a/Web/Models/Organisation.cs b/Web/Models/Organisation.cs index 66d685f..0988021 100644 --- a/Web/Models/Organisation.cs +++ b/Web/Models/Organisation.cs @@ -9,6 +9,7 @@ namespace QueryTree.Models { public class Organisation { + [Key] public int OrganisationId { get; set; } [Display(Name = "Organisation Name")] diff --git a/Web/Models/OrganisationInvite.cs b/Web/Models/OrganisationInvite.cs index 81d4644..4aad873 100644 --- a/Web/Models/OrganisationInvite.cs +++ b/Web/Models/OrganisationInvite.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; using System.Linq; +using System.ComponentModel.DataAnnotations; namespace QueryTree.Models { public class OrganisationInvite { + [Key] public int OrganisationInviteId { get; set; } public virtual ApplicationUser CreatedBy { get; set; } diff --git a/Web/Startup.cs b/Web/Startup.cs index 365a087..73a12e3 100644 --- a/Web/Startup.cs +++ b/Web/Startup.cs @@ -15,7 +15,6 @@ using Microsoft.Extensions.Logging; using QueryTree.Models; using QueryTree.Managers; -using QueryTree.Services; namespace QueryTree { @@ -31,9 +30,30 @@ public Startup(IConfiguration configuration) // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - services.AddDbContext(options => - options.UseSqlite(Configuration.GetConnectionString("DefaultConnection"))); + services.AddSingleton(Configuration); + + services.Configure(Configuration.GetSection("Customization")); + services.Configure(Configuration.GetSection("Passwords")); + switch (Configuration.GetValue("Customization:DataStore")) + { + case "MSSqlServer": + services.AddDbContext(options => + options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); + services.AddHangfire(x => + x.UseSqlServerStorage(Configuration.GetConnectionString("DefaultConnection")) + ); + break; + + default: + services.AddDbContext(options => + options.UseSqlite(Configuration.GetConnectionString("DefaultConnection"))); + services.AddHangfire(x => + x.UseSQLiteStorage(Configuration.GetConnectionString("DefaultConnection")) + ); + break; + } + services.AddIdentity() .AddEntityFrameworkStores() .AddDefaultTokenProviders(); @@ -66,21 +86,11 @@ public void ConfigureServices(IServiceCollection services) options.User.RequireUniqueEmail = true; }); - // Add application services. - services.AddSingleton(); - services.AddTransient(); + // Add application services. + services.AddTransient(); services.AddTransient(); // Allows controllers to set/get/delete database credentials services.AddTransient(); services.AddMemoryCache(); - - services.AddSingleton(Configuration); - - services.Configure(Configuration.GetSection("Customization")); - services.Configure(Configuration.GetSection("Passwords")); - - services.AddHangfire(x => - x.UseSQLiteStorage(Configuration.GetConnectionString("DefaultConnection")) - ); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. From 55301fac15ae7d0710bb370c1cddb5804bed3b5e Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Mon, 11 Feb 2019 17:08:01 +0000 Subject: [PATCH 11/30] Issue #27 removing dep on npm --- .circleci/config.yml | 2 -- Dockerfile | 1 - README.md | 8 +------- Web/QueryTree.csproj | 7 +------ appveyor.yml | 2 -- 5 files changed, 2 insertions(+), 18 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0d3260e..5dc83b8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,14 +5,12 @@ jobs: - image: microsoft/aspnetcore-build:2.1-stretch steps: - checkout - - run: npm install less -g - run: dotnet build ./Web/QueryTree.csproj -v n test: docker: - image: microsoft/aspnetcore-build:2.1-stretch steps: - checkout - - run: npm install less -g - run: dotnet test ./Tests/Tests.csproj -v n docker-publish: machine: true diff --git a/Dockerfile b/Dockerfile index 5604552..2d0f995 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,6 @@ RUN curl -SL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-lin WORKDIR /build COPY . . -RUN npm install less -g RUN dotnet restore RUN dotnet publish --no-restore -c Release ./Web/QueryTree.csproj -o /dist diff --git a/README.md b/README.md index 586436b..f86d813 100644 --- a/README.md +++ b/README.md @@ -44,13 +44,7 @@ for more information. ### Prerequisites -To build binaries or run from source you need the [.NET Core SDK v2.1](https://www.microsoft.com/net/download) and [NPM](https://nodejs.org/) installed. - -You will need to install the Less.js compiler using NPM: - -```sh -npm install less -g -``` +To build binaries or run from source you need the [.NET Core SDK v2.1](https://www.microsoft.com/net/download) installed. ### Running from Source diff --git a/Web/QueryTree.csproj b/Web/QueryTree.csproj index abdc7d0..925a213 100644 --- a/Web/QueryTree.csproj +++ b/Web/QueryTree.csproj @@ -28,6 +28,7 @@ + @@ -63,11 +64,5 @@ - - - - - - diff --git a/appveyor.yml b/appveyor.yml index a102287..bd1c5c6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,8 +3,6 @@ branches: only: - master image: Visual Studio 2017 -install: -- npm install less -g dotnet_csproj: patch: true file: 'Web\QueryTree.csproj' From 7aeb0f5504e3503ec73eeee18de00eee9c579498 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Tue, 12 Feb 2019 10:18:38 +0000 Subject: [PATCH 12/30] Removing nodejs from docker image as it's no longer required --- Dockerfile | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2d0f995..66d7561 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,5 @@ FROM microsoft/dotnet:2.1-sdk-stretch as builder -ENV NODE_VERSION 10.13.0 -ENV NODE_DOWNLOAD_SHA b4b5d8f73148dcf277df413bb16827be476f4fa117cbbec2aaabc8cc0a8588e1 -RUN curl -SL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz" --output nodejs.tar.gz \ - && echo "$NODE_DOWNLOAD_SHA nodejs.tar.gz" | sha256sum -c - \ - && tar -xzf "nodejs.tar.gz" -C /usr/local --strip-components=1 \ - && rm nodejs.tar.gz \ - && ln -s /usr/local/bin/node /usr/local/bin/nodejs WORKDIR /build COPY . . From 783fbf2aa9362f9bf968569ffabc00ec11476b8a Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Tue, 12 Feb 2019 10:18:51 +0000 Subject: [PATCH 13/30] Tidying up docs --- docs/docker.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/docker.md b/docs/docker.md index 41a260a..87b690f 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -4,7 +4,6 @@ We maintain an image of QueryTree on the docker hub at [d4software/querytree](ht ## How to use the docker hub image - ```sh docker run -p 8080:80 --name querytree -d d4software/querytree:latest ``` @@ -16,6 +15,7 @@ The image runs QueryTree Web on port 80 so you'll need to proxy through to port ### Confirm the container is running `docker container ls` returns: + ```sh CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9a75b09da5cd d4software/querytree:latest "/bin/sh -c 'dotnet …" 11 seconds ago Up 11 seconds 0.0.0.0:8080->80/tcp querytree @@ -33,7 +33,7 @@ docker stop querytree docker start querytree ``` -# Override configuration defaults +## Override configuration defaults Query tree uses an appsettings.json file to store advanced configuration settings. You may need to change some of these settings when deploying QueryTree or when you need an advanced feature that is disabled by default. @@ -62,16 +62,16 @@ docker run -p 8080:80 --name querytree -d -e Customization__SystemName="Acme Rep If you need a more permanent solution consider building a custom docker image with the appropriate appsettings.json file in [/Web/appsettings.json](/Web/appsettings.json) Read more about: + - [Enviroment variables in docker](https://docs.docker.com/engine/reference/run/#env-environment-variables) - [appsettings.json in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.1&tabs=basicconfiguration) -# Use QueryTree to query a DB inside a docker network +## Use QueryTree to query a DB inside a docker network You may want to run queries on a database that is not exposed to the host machine and exists soley withina a [docker network](https://docs.docker.com/network/). To link QueryTree and your DB to the same network you'll need to create a [user defined bridge](https://docs.docker.com/network/bridge/#manage-a-user-defined-bridge) and place both containers within it. - ### Create the network ```sh @@ -86,7 +86,7 @@ docker network connect querynet querytree ### Example with running MySQL in the same network -``` +```sh docker run --name some-mysql -e MYSQL_ALLOW_EMPTY_PASSWORD=true --network querynet mysql:latest ``` @@ -119,8 +119,7 @@ You should then see something like this: **some-mysql** will be accesible by the **querytree** container at **172.19.0.3:3306**, without needing some-mysql to be exposed to the network - -# Building locally +## Building locally You can optionally build and run the image locally. This is useful if you do not have the .NET-core sdk or you need to run QueryTree inside a container with some custom changes you've made. @@ -132,7 +131,7 @@ To build a local container image using this method you can run the following com docker build -t querytree . ``` -## Skipping the build step +### Skipping the build step If you don't wish to make use of the first build step and do have .NET installed on the host system you can achieve this by commenting out the build step in the Dockerfile. From a445233c7d885e4b457b890f03b527c594dcc24b Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Mon, 11 Feb 2019 17:15:58 +0000 Subject: [PATCH 14/30] Updating docs --- README.md | 2 +- Web/appsettings.json | 3 ++- docs/customizing.md | 4 +++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f86d813..dff4111 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Supports customization of the logo image, system name and CSS used within the app. Can use either Sqlite or Microsoft SQL Server database for it's own user -and reports data storage. +and reports data storage. Database and SSH passwords are stored in its database in encryped form, using AES encryption. Users may provide their own key file, or let the diff --git a/Web/appsettings.json b/Web/appsettings.json index 15c818c..946b383 100644 --- a/Web/appsettings.json +++ b/Web/appsettings.json @@ -23,6 +23,7 @@ "SystemName": "QueryTree", "SystemLogo": "/images/querytree_logo.png", "ExtraCSS": "", - "AllowAdvancedQuery": false + "AllowAdvancedQuery": false, + "DataStore": "Sqlite" } } diff --git a/docs/customizing.md b/docs/customizing.md index 50c9ea2..e0bc194 100644 --- a/docs/customizing.md +++ b/docs/customizing.md @@ -9,7 +9,8 @@ The [appsettings.json](/Web/appsettings.json) file contains a section titled “ "SystemName": "QueryTree", "SystemLogo": "/images/querytree_logo.png", "ExtraCSS": "", - "AllowAdvancedQuery": false + "AllowAdvancedQuery": false, + "DataStore": "Sqlite" } ``` @@ -19,3 +20,4 @@ The following section details what each of the settings do: * *SystemLogo*: contains the URL of an image that will be displayed on the top left of each page. Change this setting to display your own system logo on the top left. This image does not necessarily need to be located inside QueryTree’s wwwroot folder. It could potentially be hosted at a different domain. Note: the application will still display a smaller version of the QueryTree logo in the footer. * *ExtraCSS*: points to a .css file that will be referenced by all the QueryTree pages, after it’s own CSS. This file does not necessarily need to be located inside QueryTree’s wwwroot folder. It could potentially be hosted at a different domain. * *AllowAdvancedQuery*: indicated whether users will be given the option to use the advanced query builder. See [here](/docs/advanced.md) for more information. +* *DataStore*: Controls what kind of database QueryTree will store it's configuration data in. Valid options are 'MSSqlServer' or 'Sqlite'. The contents of the ConnectionString setting in the [appsettings.json](/Web/appsettings.json) should be set to an appropriate connection string for this database type. From e093f329756dfc903a65cd070b82eb6a9b310239 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Tue, 12 Feb 2019 11:36:03 +0000 Subject: [PATCH 15/30] Formatting --- Dockerfile | 3 --- docs/readme.md | 20 ++++++++++---------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/Dockerfile b/Dockerfile index 66d7561..61f074b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,9 @@ FROM microsoft/dotnet:2.1-sdk-stretch as builder - - WORKDIR /build COPY . . RUN dotnet restore RUN dotnet publish --no-restore -c Release ./Web/QueryTree.csproj -o /dist - FROM microsoft/dotnet:2.1-aspnetcore-runtime as runtime WORKDIR /app COPY --from=builder /dist . diff --git a/docs/readme.md b/docs/readme.md index 284c8ab..4f698ad 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -1,11 +1,11 @@ -## QueryTree Guides and Docs +# QueryTree Guides and Docs - * [Scheduling Reports](/docs/scheduling.md) - * [Sharing Individual Reports](/docs/sharing.md) - * [Team Management](/docs/teams.md) - * [The Advanced Query Builder](/docs/advanced.md) - * [Auto Join](/docs/autojoin.md) - * [Customizing QueryTree](/docs/customizing.md) - * [Building a password manager](/docs/password-manager.md) - * [Using with Docker](/docs/docker.md) - * [Configuring Email](/docs/mail.md) +* [Scheduling Reports](/docs/scheduling.md) +* [Sharing Individual Reports](/docs/sharing.md) +* [Team Management](/docs/teams.md) +* [The Advanced Query Builder](/docs/advanced.md) +* [Auto Join](/docs/autojoin.md) +* [Customizing QueryTree](/docs/customizing.md) +* [Building a password manager](/docs/password-manager.md) +* [Using with Docker](/docs/docker.md) +* [Configuring Email](/docs/mail.md) From 721f82ad514cb6299e34e522b7b022209321b5c6 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Tue, 12 Feb 2019 11:50:16 +0000 Subject: [PATCH 16/30] Working on adding base uri to the configuration --- Web/Models/CustomizationConfiguration.cs | 1 + Web/Startup.cs | 8 ++++++++ Web/appsettings.json | 3 ++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Web/Models/CustomizationConfiguration.cs b/Web/Models/CustomizationConfiguration.cs index 51e6ac9..b2add00 100644 --- a/Web/Models/CustomizationConfiguration.cs +++ b/Web/Models/CustomizationConfiguration.cs @@ -12,5 +12,6 @@ public class CustomizationConfiguration public string ExtraCSS { get; set; } public bool AllowAdvancedQuery { get; set; } public string DataStore { get; set; } + public string BaseUri { get; set; } } } diff --git a/Web/Startup.cs b/Web/Startup.cs index 73a12e3..f28580a 100644 --- a/Web/Startup.cs +++ b/Web/Startup.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; @@ -130,6 +131,13 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); + + if (!String.IsNullOrWhiteSpace(Configuration.GetValue("Customization:BaseUri"))) { + app.Use((context, next) => { + context.Request.PathBase = new PathString(Configuration.GetValue("Customization:BaseUri")); + return next(); + }); + } } } } diff --git a/Web/appsettings.json b/Web/appsettings.json index 946b383..e873c7d 100644 --- a/Web/appsettings.json +++ b/Web/appsettings.json @@ -24,6 +24,7 @@ "SystemLogo": "/images/querytree_logo.png", "ExtraCSS": "", "AllowAdvancedQuery": false, - "DataStore": "Sqlite" + "DataStore": "Sqlite", + "BaseUri": "" } } From f9db2f788a2da8b17bce23489d5838b0cc649ee6 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Tue, 12 Feb 2019 19:19:07 +0000 Subject: [PATCH 17/30] Fixing issues with usign QueryTree at a custom base uri --- Web/Controllers/ApiController.cs | 8 +- Web/Scripts/backend.js | 20 +- Web/Scripts/models.js | 2 +- Web/Scripts/simple.js | 2 +- Web/Startup.cs | 2 + Web/Styles/qt-bootstrap.css | 1 - Web/ViewModels/GetDataRequest.cs | 14 ++ Web/Views/Home/Create.cshtml | 2 +- Web/Views/Home/Edit.cshtml | 2 +- Web/Views/Home/Index.cshtml | 4 +- Web/Views/Queries/Create.cshtml | 4 +- Web/Views/Queries/Details.cshtml | 301 ++++++++++++------------ Web/Views/Queries/Edit.cshtml | 4 +- Web/Views/Shared/_Layout.cshtml | 21 +- Web/Views/Shared/_Qt_Advanced.cshtml | 2 +- Web/Views/Shared/_Qt_simple.cshtml | 24 +- Web/Views/Simple/Create.cshtml | 12 +- Web/Views/Simple/Edit.cshtml | 12 +- Web/Views/Team/Edit.cshtml | 2 +- Web/wwwroot/partials/DatabaseTable.html | 2 +- 20 files changed, 234 insertions(+), 207 deletions(-) create mode 100644 Web/ViewModels/GetDataRequest.cs diff --git a/Web/Controllers/ApiController.cs b/Web/Controllers/ApiController.cs index 2535bb6..23f156a 100644 --- a/Web/Controllers/ApiController.cs +++ b/Web/Controllers/ApiController.cs @@ -440,8 +440,8 @@ public async Task Schedule(int id) #region Query APIs - [HttpGet("/api/queries/{queryId}/")] - public ActionResult QueryData(int queryId, int draw, int start, int length) + [HttpPost("/api/getquerydata/{queryId}/")] + public ActionResult GetData(int queryId, GetDataRequest req) { var query = db.Queries .Include(q => q.DatabaseConnection) @@ -454,11 +454,11 @@ public ActionResult QueryData(int queryId, int draw, int start, int length) var nodes = JsonConvert.SerializeObject(queryDefinition.Nodes); var selectedNodeId = queryDefinition.SelectedNodeId.ToString(); - var data = _dbMgr.GetData(query.DatabaseConnection, nodes, selectedNodeId, start, length); + var data = _dbMgr.GetData(query.DatabaseConnection, nodes, selectedNodeId, req.Start, req.Length); var rows = data.Rows; var totalCount = data.RowCount; - var dataTable = new {draw = draw, recordsTotal = totalCount, recordsFiltered = totalCount, data = rows}; + var dataTable = new {draw = req.Draw, recordsTotal = totalCount, recordsFiltered = totalCount, data = rows}; return Json(dataTable); } diff --git a/Web/Scripts/backend.js b/Web/Scripts/backend.js index b6480eb..61c9fc8 100644 --- a/Web/Scripts/backend.js +++ b/Web/Scripts/backend.js @@ -2,20 +2,22 @@ // // Depends on: utils.js -backend = {}; +backend = { + "baseUri": "" +}; (function () { backend.CheckConnection = function (models, callback) { var databaseId = utils.GetHiddenValByName('DatabaseConnectionID'); - $.getJSON("/api/connections/" + databaseId + "/status/", function (data) { + $.getJSON(backend.baseUri + "/api/connections/" + databaseId + "/status/", function (data) { callback(data); }); }; backend.LoadTables = function (callback) { var databaseId = utils.GetHiddenValByName('DatabaseConnectionID'); - $.getJSON("/api/connections/" + databaseId + "/tables/", function (data) { + $.getJSON(backend.baseUri + "/api/connections/" + databaseId + "/tables/", function (data) { callback(data); }) .fail(function () { @@ -25,7 +27,7 @@ backend = {}; backend.GetJoins = function (tableName, callback) { var databaseId = utils.GetHiddenValByName('DatabaseConnectionID'); - $.getJSON("/api/connections/" + databaseId + "/tables/" + tableName + "/joins/", function (data) { + $.getJSON(backend.baseUri + "/api/connections/" + databaseId + "/tables/" + tableName + "/joins/", function (data) { callback(data); }) .fail(function () { @@ -48,7 +50,7 @@ backend = {}; lock = true; latestNodes = null; $.ajax({ - "url": "/api/cache/", + "url": backend.baseUri + "/api/cache/", "type": 'POST', "data": { id: serverQueryKey(), @@ -84,7 +86,7 @@ backend = {}; backend.LoadData(serverQueryKey, nodes, nodeId, startRow, rowCount, format, output, callback); }); } else { - $.getJSON("/api/cache/" + serverQueryKey() + "/" + nodeId + "/?startRow=" + startRow + "&rowCount=" + rowCount, function (data) { + $.getJSON(backend.baseUri + "/api/cache/" + serverQueryKey() + "/" + nodeId + "/?startRow=" + startRow + "&rowCount=" + rowCount, function (data) { if (data.query) { console.log(data.query); } @@ -103,7 +105,7 @@ backend = {}; backend.SaveSchedule = function (schedule, callback) { $.ajax({ - "url": '/api/schedule', + "url": backend.baseUri + '/api/schedule', "type": 'POST', "contentType": "application/json", "data": JSON.stringify(schedule), @@ -117,7 +119,7 @@ backend = {}; backend.GetSchedule = function (queryId) { return $.ajax({ - "url": '/api/schedule?id=' + queryId, + "url": backend.baseUri + '/api/schedule?id=' + queryId, "type": 'GET', "contentType": "application/json", "dataType": "json" @@ -126,7 +128,7 @@ backend = {}; backend.LoadQueryColumnsName = function (queryId) { return $.ajax({ - "url": "/api/queries/" + queryId + "/columns/", + "url": backend.baseUri + "/api/queries/" + queryId + "/columns/", "type": 'GET' }); }; diff --git a/Web/Scripts/models.js b/Web/Scripts/models.js index 9c4b0cb..787d203 100644 --- a/Web/Scripts/models.js +++ b/Web/Scripts/models.js @@ -198,7 +198,7 @@ models.Export = function () { return true; } else { if (models.ServerQueryKey()) { - models.ExportUrl("/api/cache/" + models.ServerQueryKey() + "/" + node.Id + "/export/"); + models.ExportUrl(backend.baseUri + "/api/cache/" + models.ServerQueryKey() + "/" + node.Id + "/export/"); models.ExportFileName("export.xlsx"); } else { models.ExportUrl(null); diff --git a/Web/Scripts/simple.js b/Web/Scripts/simple.js index a727a7d..075b28c 100644 --- a/Web/Scripts/simple.js +++ b/Web/Scripts/simple.js @@ -843,7 +843,7 @@ var SimpleQueryBuilderViewModel = function () { self.exportUrl = ko.pureComputed(function () { if (self.serverQueryKey()) { - return "/api/cache/" + self.serverQueryKey() + "/" + self.selectedNode().Id + "/export/"; + return backend.baseUri + "/api/cache/" + self.serverQueryKey() + "/" + self.selectedNode().Id + "/export/"; } else { return null; } diff --git a/Web/Startup.cs b/Web/Startup.cs index f28580a..e1cc265 100644 --- a/Web/Startup.cs +++ b/Web/Startup.cs @@ -16,6 +16,7 @@ using Microsoft.Extensions.Logging; using QueryTree.Models; using QueryTree.Managers; +using QueryTree.Services; namespace QueryTree { @@ -88,6 +89,7 @@ public void ConfigureServices(IServiceCollection services) }); // Add application services. + services.AddTransient(); services.AddTransient(); services.AddTransient(); // Allows controllers to set/get/delete database credentials services.AddTransient(); diff --git a/Web/Styles/qt-bootstrap.css b/Web/Styles/qt-bootstrap.css index fdf6fcf..0b80c1c 100644 --- a/Web/Styles/qt-bootstrap.css +++ b/Web/Styles/qt-bootstrap.css @@ -51,7 +51,6 @@ h2,h3{page-break-after:avoid} } .img-thumbnail,body{background-color:#fff} .btn,.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-warning.active,.btn-warning:active,.btn.active,.btn:active,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover,.form-control,.navbar-toggle,.open>.dropdown-toggle.btn-danger,.open>.dropdown-toggle.btn-default,.open>.dropdown-toggle.btn-info,.open>.dropdown-toggle.btn-primary,.open>.dropdown-toggle.btn-warning{background-image:none} -@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')} .glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale} .glyphicon-asterisk:before{content:"\002a"} .glyphicon-plus:before{content:"\002b"} diff --git a/Web/ViewModels/GetDataRequest.cs b/Web/ViewModels/GetDataRequest.cs new file mode 100644 index 0000000..95f620e --- /dev/null +++ b/Web/ViewModels/GetDataRequest.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace QueryTree.ViewModels +{ + public class GetDataRequest + { + public int Draw { get; set; } + public int Start { get; set; } + public int Length { get; set; } + } +} diff --git a/Web/Views/Home/Create.cshtml b/Web/Views/Home/Create.cshtml index 0b1b4df..e6d6321 100644 --- a/Web/Views/Home/Create.cshtml +++ b/Web/Views/Home/Create.cshtml @@ -169,5 +169,5 @@ var sshUploadUrl = '@Url.Action("Upload", "SshKeyFile")', testConnectionUrl = '@Url.Action("TestConnection", "Api")' - + } diff --git a/Web/Views/Home/Edit.cshtml b/Web/Views/Home/Edit.cshtml index 34a0eec..e53166d 100644 --- a/Web/Views/Home/Edit.cshtml +++ b/Web/Views/Home/Edit.cshtml @@ -168,5 +168,5 @@ var sshUploadUrl = '@Url.Action("Upload", "SshKeyFile")', testConnectionUrl = '@Url.Action("TestConnection", "Api")' - + } diff --git a/Web/Views/Home/Index.cshtml b/Web/Views/Home/Index.cshtml index 4229ea7..aa92fe0 100644 --- a/Web/Views/Home/Index.cshtml +++ b/Web/Views/Home/Index.cshtml @@ -13,7 +13,7 @@

Set Up the Connection Myself

-

+

You'll need to know your database connection details

 Connect Database
@@ -22,7 +22,7 @@

Get Someone Else to Do It

-

+

Invite a technical colleague who can set up a connection

 Invite User
diff --git a/Web/Views/Queries/Create.cshtml b/Web/Views/Queries/Create.cshtml index 1c3da10..996d5f7 100644 --- a/Web/Views/Queries/Create.cshtml +++ b/Web/Views/Queries/Create.cshtml @@ -60,8 +60,8 @@ @section Scripts { - - + + - + + $(function () { + var query = JSON.parse($('input[name=QueryDefinition]').val()); - + var serverQueryKey = ko.observable(); + backend.LoadData(serverQueryKey, query.Nodes, query.SelectedNodeId, 0, num,"JSON", null, function(data) { + utils.RenderChart('#chart', data.rows, selectedNode.Type, 0, data.columnTypes[0], 1, data.columnTypes[1]); + $('#spinner').remove(); + }); + } else { + $('#results').show(); + + backend.LoadQueryColumnsName(@this.Model.QueryID).done(function (data) { + + ko.applyBindings(new ViewModel(data), $("#results")[0]); + + $("#results").DataTable({ + "dom": '<"top">rt<"pagingTable"p><"clear">', + responsive: false, + searching: true, + fixedHeader: true, + scrollX: true, + ordering: false, + processing: true, + serverSide: true, + info: true, + stateSave: true, + ajax:{ + "url": "@CustomizationConfiguration.Value.BaseUri/api/getquerydata/" + @this.Model.QueryID + "/", + "type": "POST" + }, + }); + + function ViewModel(data) { + var self = this; + self.columns = data; + }; + + }); + + $('#results').on('init.dt', function () { + $('#spinner').remove(); + }); + } + }); + } \ No newline at end of file diff --git a/Web/Views/Queries/Edit.cshtml b/Web/Views/Queries/Edit.cshtml index 806f4bd..d0a027c 100644 --- a/Web/Views/Queries/Edit.cshtml +++ b/Web/Views/Queries/Edit.cshtml @@ -67,8 +67,8 @@ @section Scripts { - - + + + @RenderSection("scripts", required: false) + @if (!String.IsNullOrEmpty(CustomizationConfiguration.Value.BaseUri)) + { + + } diff --git a/Web/Views/Shared/_Qt_Advanced.cshtml b/Web/Views/Shared/_Qt_Advanced.cshtml index eb9630c..d0ad1b2 100644 --- a/Web/Views/Shared/_Qt_Advanced.cshtml +++ b/Web/Views/Shared/_Qt_Advanced.cshtml @@ -28,7 +28,7 @@
Connecting to your database
- + This tool is not set up yet, please click options to edit its settings No data was returned diff --git a/Web/Views/Shared/_Qt_simple.cshtml b/Web/Views/Shared/_Qt_simple.cshtml index ec0aa8a..b2a89c0 100644 --- a/Web/Views/Shared/_Qt_simple.cshtml +++ b/Web/Views/Shared/_Qt_simple.cshtml @@ -1,15 +1,17 @@ 
Connecting to your database...
- +
- - - - - - - - - - \ No newline at end of file +
+ + + + + + + + + + +
\ No newline at end of file diff --git a/Web/Views/Simple/Create.cshtml b/Web/Views/Simple/Create.cshtml index e67fc2d..7219586 100644 --- a/Web/Views/Simple/Create.cshtml +++ b/Web/Views/Simple/Create.cshtml @@ -25,13 +25,13 @@
Connecting to your database
- +
\ No newline at end of file +