188 lines
8.2 KiB
C#
188 lines
8.2 KiB
C#
using System.Collections.Generic;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.Routing;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.Extensions.Logging;
|
|
using AyaNova.Models;
|
|
using AyaNova.Api.ControllerHelpers;
|
|
using AyaNova.Biz;
|
|
using AyaNova.Util;
|
|
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
using System.IO;
|
|
using System.IO.Compression;
|
|
using ChoETL;
|
|
using System;
|
|
|
|
namespace AyaNova.Api.Controllers
|
|
{
|
|
[ApiController]
|
|
[ApiVersion("8.0")]
|
|
[Route("api/v{version:apiVersion}/export")]
|
|
[Produces("application/json")]
|
|
[Authorize]
|
|
public class ExportController : ControllerBase
|
|
{
|
|
private readonly AyContext ct;
|
|
private readonly ILogger<ExportController> log;
|
|
private readonly ApiServerState serverState;
|
|
|
|
/// <summary>
|
|
/// ctor
|
|
/// </summary>
|
|
/// <param name="dbcontext"></param>
|
|
/// <param name="logger"></param>
|
|
/// <param name="apiServerState"></param>
|
|
public ExportController(AyContext dbcontext, ILogger<ExportController> logger, ApiServerState apiServerState)
|
|
{
|
|
ct = dbcontext;
|
|
log = logger;
|
|
serverState = apiServerState;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Export to file
|
|
/// </summary>
|
|
/// <param name="format">Valid values are: "csv","json"</param>
|
|
/// <param name="selectedRequest"></param>
|
|
/// <returns>downloadable export file name</returns>
|
|
[HttpPost("render/{format}")]
|
|
public async Task<IActionResult> RenderExport([FromRoute] string format, [FromBody] DataListSelectedRequest selectedRequest)
|
|
{
|
|
|
|
if (!serverState.IsOpen)
|
|
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
|
|
if (!ModelState.IsValid)
|
|
return BadRequest(new ApiErrorResponse(ModelState));
|
|
|
|
if (selectedRequest == null)
|
|
return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, null, "DataListSelectedRequest is required"));
|
|
|
|
if (!Authorized.HasReadFullRole(HttpContext.Items, selectedRequest.AType))
|
|
return StatusCode(403, new ApiNotAuthorizedResponse());
|
|
|
|
if (string.IsNullOrWhiteSpace(format))
|
|
return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_REQUIRED, null, "format required"));
|
|
|
|
if (format != "csv" && format != "json")
|
|
return BadRequest(new ApiErrorResponse(ApiErrorCode.VALIDATION_INVALID_VALUE, null, "format not valid, must be 'csv' or 'json'"));
|
|
|
|
|
|
var UserId = UserIdFromContext.Id(HttpContext.Items);
|
|
var UserRoles = UserRolesFromContext.Roles(HttpContext.Items);
|
|
var UserTranslationId = UserTranslationIdFromContext.Id(HttpContext.Items);
|
|
|
|
//Rehydrate id list if necessary
|
|
if (selectedRequest.SelectedRowIds.Length == 0)
|
|
selectedRequest.SelectedRowIds = await DataListSelectedProcessingOptions.RehydrateIdList(
|
|
selectedRequest,
|
|
ct,
|
|
UserRoles,
|
|
log,
|
|
UserId,
|
|
UserTranslationId);
|
|
|
|
|
|
log.LogDebug($"Instantiating biz object handler for {selectedRequest.AType}");
|
|
var biz = BizObjectFactory.GetBizObject(selectedRequest.AType, ct, UserId, UserRoles, UserTranslationId);
|
|
log.LogDebug($"Fetching data for {selectedRequest.SelectedRowIds.Length} {selectedRequest.AType} items");
|
|
string baseFileName = FileUtil.StringToSafeFileName($"{selectedRequest.AType.ToString().ToLowerInvariant()}-{format}-{FileUtil.GetSafeDateFileName()}");
|
|
string outputSourceFileName = baseFileName + "." + format;
|
|
string outputSourceFullPath = System.IO.Path.Combine(FileUtil.TemporaryFilesFolder, outputSourceFileName);
|
|
string outputZipFullpath = System.IO.Path.Combine(FileUtil.TemporaryFilesFolder, baseFileName + ".zip");
|
|
|
|
log.LogDebug($"Calling render export data to file {outputZipFullpath}");
|
|
try
|
|
{
|
|
switch (format)
|
|
{
|
|
case "csv":
|
|
using (var w = new ChoCSVWriter(outputSourceFullPath).WithFirstLineHeader().ThrowAndStopOnMissingField(false).WithMaxScanRows(100))
|
|
{
|
|
//max scan rows means how many rows it will scan to determine field types so this affects tags because it will scan the first 100 to see the maximum tag count then only ever output that many
|
|
var dat = await ((IExportAbleObject)biz).GetExportData(selectedRequest, Guid.Empty);//todo: jobify
|
|
w.Write(ToDynamicList(dat));
|
|
}
|
|
break;
|
|
case "json":
|
|
using (StreamWriter file = System.IO.File.CreateText(outputSourceFullPath))
|
|
using (JsonTextWriter writer = new JsonTextWriter(file))
|
|
{
|
|
var dat = await ((IExportAbleObject)biz).GetExportData(selectedRequest, Guid.Empty);//todo: jobify
|
|
dat.WriteTo(writer);
|
|
}
|
|
break;
|
|
}
|
|
|
|
//zip it
|
|
using (FileStream fs = new FileStream(outputZipFullpath, FileMode.Create))
|
|
using (ZipArchive arch = new ZipArchive(fs, ZipArchiveMode.Create))
|
|
{
|
|
arch.CreateEntryFromFile(outputSourceFullPath, outputSourceFileName);
|
|
}
|
|
|
|
log.LogDebug($"Completed, returning results");
|
|
return Ok(ApiOkResponse.Response(baseFileName + ".zip"));
|
|
}
|
|
catch (ReportRenderTimeOutException)
|
|
{
|
|
log.LogInformation($"RenderExport timeout data list key: {selectedRequest.DataListKey}, record count:{selectedRequest.SelectedRowIds.LongLength}, user:{UserNameFromContext.Name(HttpContext.Items)} ");
|
|
return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, null, "timeout - select fewer records"));
|
|
}
|
|
}
|
|
|
|
public static IList<dynamic> ToDynamicList(JArray data)
|
|
{
|
|
var dynamicData = new List<dynamic>();
|
|
var expConverter = new Newtonsoft.Json.Converters.ExpandoObjectConverter();
|
|
|
|
foreach (var dataItem in data)
|
|
{
|
|
dynamic obj = JsonConvert.DeserializeObject<System.Dynamic.ExpandoObject>(dataItem.ToString(), expConverter);
|
|
dynamicData.Add(obj);
|
|
}
|
|
return dynamicData;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Download a rendered Export
|
|
/// </summary>
|
|
/// <param name="fileName"></param>
|
|
/// <param name="t">download token</param>
|
|
/// <returns></returns>
|
|
[HttpGet("download/{fileName}")]
|
|
[AllowAnonymous]
|
|
public async Task<IActionResult> DownloadAsync([FromRoute] string fileName, [FromQuery] string t)
|
|
{
|
|
if (!serverState.IsOpen)
|
|
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
|
|
|
|
if (await UserBiz.ValidateDownloadTokenAndReturnUserAsync(t, ct) == null)
|
|
{
|
|
await Task.Delay(ServerBootConfig.FAILED_AUTH_DELAY);//DOS protection
|
|
return StatusCode(401, new ApiErrorResponse(ApiErrorCode.AUTHENTICATION_FAILED));
|
|
}
|
|
|
|
if (!FileUtil.TemporaryFileExists(fileName))
|
|
{
|
|
await Task.Delay(ServerBootConfig.FAILED_AUTH_DELAY);//fishing protection
|
|
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
|
|
}
|
|
|
|
var FilePath = FileUtil.GetFullPathForTemporaryFile(fileName);
|
|
return PhysicalFile(FilePath, "application/zip");
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
}//eoc
|
|
}//eons |