Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,22 @@ This module is designed to empower administrators and developers by providing a
* Return results in user-friendly formats for reporting and analysis: HTML, PDF, CSV, XLSX.
* Supports query parameters: Short Text, Dare Time, Boolean, Integer, Deceimal.
* Supports multiple database providers: SQL Server (default), MySQL, and PostgreSQL.
* Supports multiple connection strings.
* Supports multiple connection strings.
* Platform Backup & Restore support.

## Backup & Restore

SQL queries are included in the platform-wide backup and restore process.

When you run a platform export, all SQL queries (Name, Description, Query text, Connection string name and Parameters) are serialized into the backup archive.

On import, the queries are recreated or updated, preserving their identifiers, metadata and parameter definitions.

To run backup/restore:
1. Open Virto Commerce Admin UI.
1. Navigate to **Settings → Platform → Export** (or **Import**).
1. Ensure **Sql Queries** module is selected in the module list.
1. Run the export/import process.

## Screenshots
### No SQL queries yet
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Newtonsoft.Json;
using VirtoCommerce.Platform.Core.Common;
using VirtoCommerce.Platform.Core.ExportImport;
using VirtoCommerce.SqlQueries.Core.Models;
using VirtoCommerce.SqlQueries.Core.Services;

namespace VirtoCommerce.SqlQueries.Data.ExportImport;

public sealed class SqlQueriesExportImport(
ISqlQueryService sqlQueryService,
ISqlQuerySearchService sqlQuerySearchService,
JsonSerializer jsonSerializer)
{
private const int BatchSize = 50;

public async Task DoExportAsync(Stream outStream, Action<ExportImportProgressInfo> progressCallback, ICancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();

var progressInfo = new ExportImportProgressInfo { Description = "SQL queries are loading" };
progressCallback(progressInfo);

using var sw = new StreamWriter(outStream, leaveOpen: true);
using var writer = new JsonTextWriter(sw);

await writer.WriteStartObjectAsync();

progressInfo.Description = "SQL queries are started to export";
progressCallback(progressInfo);

await writer.WritePropertyNameAsync("SqlQueries");
await writer.WriteStartArrayAsync();

var criteria = AbstractTypeFactory<SqlQuerySearchCriteria>.TryCreateInstance();
criteria.Take = BatchSize;

var processedCount = 0;

for (criteria.Skip = 0; ; criteria.Skip += BatchSize)
{
var searchResult = await sqlQuerySearchService.SearchAsync(criteria);

foreach (var query in searchResult.Results)
{
cancellationToken.ThrowIfCancellationRequested();

jsonSerializer.Serialize(writer, query);
processedCount++;
}

await writer.FlushAsync();

progressInfo.Description = $"{processedCount} of {searchResult.TotalCount} SQL queries have been exported";
progressInfo.ProcessedCount = processedCount;
progressInfo.TotalCount = searchResult.TotalCount;
progressCallback(progressInfo);

if (criteria.Skip + BatchSize >= searchResult.TotalCount)
{
break;
}
}

await writer.WriteEndArrayAsync();
await writer.WriteEndObjectAsync();
await writer.FlushAsync();
}

public async Task DoImportAsync(Stream inputStream, Action<ExportImportProgressInfo> progressCallback, ICancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();

var progressInfo = new ExportImportProgressInfo();

using var streamReader = new StreamReader(inputStream, leaveOpen: true);
using var reader = new JsonTextReader(streamReader);

while (await reader.ReadAsync())
{
if (reader.TokenType == JsonToken.PropertyName && reader.Value?.ToString() == "SqlQueries")
{
await ImportSqlQueriesArrayAsync(reader, progressInfo, progressCallback, cancellationToken);
}
}
}

private async Task ImportSqlQueriesArrayAsync(JsonTextReader reader, ExportImportProgressInfo progressInfo, Action<ExportImportProgressInfo> progressCallback, ICancellationToken cancellationToken)
{
if (!await reader.ReadAsync() || reader.TokenType != JsonToken.StartArray)
{
return;
}

var processedCount = 0;
var batch = new List<SqlQuery>(BatchSize);

while (await reader.ReadAsync() && reader.TokenType != JsonToken.EndArray)
{
cancellationToken.ThrowIfCancellationRequested();

var query = jsonSerializer.Deserialize<SqlQuery>(reader);
if (query == null)
{
continue;
}

batch.Add(query);

if (batch.Count >= BatchSize)
{
await sqlQueryService.SaveChangesAsync(batch);
processedCount += batch.Count;
batch.Clear();

progressInfo.Description = $"{processedCount} SQL queries have been imported";
progressInfo.ProcessedCount = processedCount;
progressCallback(progressInfo);
}
}

if (batch.Count > 0)
{
await sqlQueryService.SaveChangesAsync(batch);
processedCount += batch.Count;

progressInfo.Description = $"{processedCount} SQL queries have been imported";
progressInfo.ProcessedCount = processedCount;
progressCallback(progressInfo);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"module": {
"VirtoCommerce.SqlQueries": {
"description": "Sicherung/Wiederherstellung von SQL-Abfragen"
}
},
"sql-queries": {
"blades": {
"sql-query-list": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"module": {
"VirtoCommerce.SqlQueries": {
"description": "Backup/Restore of SQL queries"
}
},
"sql-queries": {
"title": "SQL Queries",
"blades": {
Expand Down Expand Up @@ -106,4 +111,4 @@
"sql-queries:delete": "Delete SQL Queries related data",
"sql-queries:reports": "Execute and access reports"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"module": {
"VirtoCommerce.SqlQueries": {
"description": "Copia de seguridad/Restauración de consultas SQL"
}
},
"sql-queries": {
"blades": {
"sql-query-list": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"module": {
"VirtoCommerce.SqlQueries": {
"description": "SQL-kyselyiden varmuuskopiointi/palautus"
}
},
"sql-queries": {
"title": "SQL-kyselyt",
"blades": {
Expand Down Expand Up @@ -105,4 +110,4 @@
"sql-queries:delete": "Poista SQL-kyselyihin liittyviä tietoja",
"sql-queries:reports": "Suorita ja käytä raportteja"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"module": {
"VirtoCommerce.SqlQueries": {
"description": "Sauvegarde/Restauration de requêtes SQL"
}
},
"sql-queries": {
"blades": {
"sql-query-list": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"module": {
"VirtoCommerce.SqlQueries": {
"description": "Backup/Ripristino di query SQL"
}
},
"sql-queries": {
"blades": {
"sql-query-list": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"module": {
"VirtoCommerce.SqlQueries": {
"description": "SQLクエリのバックアップ/復元"
}
},
"sql-queries": {
"blades": {
"sql-query-list": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"module": {
"VirtoCommerce.SqlQueries": {
"description": "Sikkerhetskopiering/gjenoppretting av SQL-spørringer"
}
},
"sql-queries": {
"title": "SQL-spørringer",
"blades": {
Expand Down Expand Up @@ -105,4 +110,4 @@
"sql-queries:delete": "Slette SQL-spørringer-relaterte data",
"sql-queries:reports": "Kjøre og få tilgang til rapporter"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"module": {
"VirtoCommerce.SqlQueries": {
"description": "Kopia zapasowa/Przywracanie zapytań SQL"
}
},
"sql-queries": {
"blades": {
"sql-query-list": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"module": {
"VirtoCommerce.SqlQueries": {
"description": "Backup/Restauração de consultas SQL"
}
},
"sql-queries": {
"blades": {
"sql-query-list": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"module": {
"VirtoCommerce.SqlQueries": {
"description": "Резервное копирование/Восстановление SQL-запросов"
}
},
"sql-queries": {
"blades": {
"sql-query-list": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"module": {
"VirtoCommerce.SqlQueries": {
"description": "Säkerhetskopiering/Återställning av SQL-frågor"
}
},
"sql-queries": {
"title": "SQL-frågor",
"blades": {
Expand Down Expand Up @@ -105,4 +110,4 @@
"sql-queries:delete": "Ta bort SQL-frågerelaterad data",
"sql-queries:reports": "Kör och få tillgång till rapporter"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{
"module": {
"VirtoCommerce.SqlQueries": {
"description": "SQL 查询的备份/恢复"
}
},
"sql-queries": {
"blades": {
"sql-query-list": {
Expand Down
22 changes: 21 additions & 1 deletion src/VirtoCommerce.SqlQueries.Web/Module.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
using System;
using System.IO;
using System.Threading.Tasks;
using DinkToPdf;
using DinkToPdf.Contracts;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using VirtoCommerce.Platform.Core.Common;
using VirtoCommerce.Platform.Core.ExportImport;
using VirtoCommerce.Platform.Core.Modularity;
using VirtoCommerce.Platform.Core.Security;
using VirtoCommerce.Platform.Core.Settings;
Expand All @@ -13,6 +17,7 @@
using VirtoCommerce.Platform.Data.SqlServer.Extensions;
using VirtoCommerce.SqlQueries.Core;
using VirtoCommerce.SqlQueries.Core.Services;
using VirtoCommerce.SqlQueries.Data.ExportImport;
using VirtoCommerce.SqlQueries.Data.MySql;
using VirtoCommerce.SqlQueries.Data.PostgreSql;
using VirtoCommerce.SqlQueries.Data.Repositories;
Expand All @@ -21,8 +26,10 @@

namespace VirtoCommerce.SqlQueries.Web;

public class Module : IModule, IHasConfiguration
public class Module : IModule, IExportSupport, IImportSupport, IHasConfiguration
{
private IApplicationBuilder _appBuilder;

public ManifestModuleInfo ModuleInfo { get; set; }
public IConfiguration Configuration { get; set; }

Expand Down Expand Up @@ -59,11 +66,14 @@ public void Initialize(IServiceCollection serviceCollection)
serviceCollection.AddTransient<ISqlQueryReportGenerator, HtmlSqlQueryReportGenerator>();
serviceCollection.AddTransient<ISqlQueryReportGenerator, XlsxSqlQueryReportGenerator>();

serviceCollection.AddTransient<SqlQueriesExportImport>();

serviceCollection.AddSingleton(typeof(IConverter), new SynchronizedConverter(new PdfTools()));
}

public void PostInitialize(IApplicationBuilder appBuilder)
{
_appBuilder = appBuilder;
var serviceProvider = appBuilder.ApplicationServices;

// Register permissions
Expand All @@ -80,4 +90,14 @@ public void Uninstall()
{
// Nothing to do here
}

public Task ExportAsync(Stream outStream, ExportImportOptions options, Action<ExportImportProgressInfo> progressCallback, ICancellationToken cancellationToken)
{
return _appBuilder.ApplicationServices.GetRequiredService<SqlQueriesExportImport>().DoExportAsync(outStream, progressCallback, cancellationToken);
}

public Task ImportAsync(Stream inputStream, ExportImportOptions options, Action<ExportImportProgressInfo> progressCallback, ICancellationToken cancellationToken)
{
return _appBuilder.ApplicationServices.GetRequiredService<SqlQueriesExportImport>().DoImportAsync(inputStream, progressCallback, cancellationToken);
}
}