feat: initial commit
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Strata.Analytics.Api.Authorization
|
||||
{
|
||||
public class AuthorizationOptions
|
||||
{
|
||||
public Guid AuthorizedDbGuid { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Strata.Analytics.Api.Authorization
|
||||
{
|
||||
public class AuthorizedDbMinimumRoleHandler : AuthorizationHandler<AuthorizedDbMinimumRoleRequirement>
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IWebHostEnvironment _env;
|
||||
private Guid _authorizedDbGuid;
|
||||
|
||||
public AuthorizedDbMinimumRoleHandler(IHttpContextAccessor httpContextAccessor, IWebHostEnvironment env, IOptions<AuthorizationOptions> options)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_env = env;
|
||||
_authorizedDbGuid = options.Value.AuthorizedDbGuid;
|
||||
}
|
||||
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AuthorizedDbMinimumRoleRequirement requirement)
|
||||
{
|
||||
var user = _httpContextAccessor.HttpContext.User;
|
||||
var dbGuid = user.GetDatabaseGuid();
|
||||
if (!_env.IsProduction() || dbGuid == _authorizedDbGuid)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
context.Fail();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Strata.Analytics.Api.Authorization
|
||||
{
|
||||
public class AuthorizedDbMinimumRoleRequirement : IAuthorizationRequirement
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Strata.Analytics.Biz.Extracts.Services;
|
||||
using Strata.Analytics.Biz.Extracts.Models;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
|
||||
|
||||
namespace Strata.Analytics.Api.Authorization
|
||||
{
|
||||
public class CustomSQLStrataOnlyRoleHandler : AuthorizationHandler<ExtractCustomSqlStrataOnlyRoleRequirement>
|
||||
{
|
||||
private readonly IExtractService _extractService;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
public CustomSQLStrataOnlyRoleHandler(IExtractService extractService, IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_extractService = extractService;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ExtractCustomSqlStrataOnlyRoleRequirement requirement)
|
||||
{
|
||||
var extractIdObj = _httpContextAccessor.HttpContext!.GetRouteData()?.Values["extractId"];
|
||||
if (extractIdObj == null)
|
||||
{
|
||||
context.Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
var extractId = int.Parse(extractIdObj.ToString()!);
|
||||
|
||||
var extract =
|
||||
await _extractService.GetByIdAsync(extractId, System.Threading.CancellationToken.None);
|
||||
|
||||
if (extract == null)
|
||||
{
|
||||
context.Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
if (extract.ExtractType == ExtractType.CustomSql && !context.User.IsSdtEmployee())
|
||||
{
|
||||
context.Fail();
|
||||
return;
|
||||
}
|
||||
context.Succeed(requirement);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Threading.Tasks;
|
||||
using Strata.Analytics.Biz.DataSources.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Strata.Analytics.Api.Authorization
|
||||
{
|
||||
public class DataSourceMinimumRoleHandler : AuthorizationHandler<DataSourceMinimumRoleRequirement>
|
||||
{
|
||||
private readonly IDataSourceService _dataSourceService;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public DataSourceMinimumRoleHandler(IDataSourceService dataSourceService, IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_dataSourceService = dataSourceService;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, DataSourceMinimumRoleRequirement requirement)
|
||||
{
|
||||
var dataSourceId = await GetDataSourceId(context);
|
||||
if (!dataSourceId.HasValue)
|
||||
{
|
||||
context.Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
var dataSourceRole = await _dataSourceService.GetCurrentUserRoleAsync(dataSourceId.Value, System.Threading.CancellationToken.None);
|
||||
|
||||
if (!dataSourceRole.HasValue || dataSourceRole.Value < requirement.MinimumRole)
|
||||
{
|
||||
context.Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
|
||||
private async Task<int?> GetDataSourceId(AuthorizationHandlerContext context)
|
||||
{
|
||||
// Check route for dataSourceId
|
||||
var dataSourceIdObj = _httpContextAccessor.HttpContext.GetRouteData()?.Values["dataSourceId"];
|
||||
if (dataSourceIdObj == null)
|
||||
{
|
||||
// Check body for dataSourceId
|
||||
var req = _httpContextAccessor.HttpContext.Request;
|
||||
|
||||
try
|
||||
{
|
||||
req.EnableBuffering();
|
||||
req.Body.Position = 0;
|
||||
var body = await req.ReadFromJsonAsync<Dictionary<string, dynamic>>(null);
|
||||
req.Body.Position = 0;
|
||||
if (req.Body.Length > 0)
|
||||
{
|
||||
foreach (var kvp in body)
|
||||
if (string.Equals(kvp.Key, "dataSourceId", StringComparison.InvariantCultureIgnoreCase))
|
||||
dataSourceIdObj = kvp.Value;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!int.TryParse(dataSourceIdObj.ToString(), out int dataSourceId))
|
||||
return null;
|
||||
|
||||
return dataSourceId;
|
||||
}
|
||||
|
||||
// Swallows throw when user does not have SdtEmployee claim and returns false
|
||||
private bool IsSdtEmployee(AuthorizationHandlerContext context)
|
||||
{
|
||||
var isSdtEmployee = false;
|
||||
try
|
||||
{
|
||||
isSdtEmployee = context.User.IsSdtEmployee();
|
||||
}
|
||||
catch { }
|
||||
|
||||
return isSdtEmployee;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Strata.Analytics.Biz.DataSources.Models;
|
||||
|
||||
namespace Strata.Analytics.Api.Authorization
|
||||
{
|
||||
public class DataSourceMinimumRoleRequirement : IAuthorizationRequirement
|
||||
{
|
||||
public DataSourceRole MinimumRole { get; set; }
|
||||
|
||||
public DataSourceMinimumRoleRequirement(DataSourceRole role)
|
||||
{
|
||||
MinimumRole = role;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
public class ExtractCustomSqlStrataOnlyRoleRequirement : IAuthorizationRequirement
|
||||
{
|
||||
|
||||
public ExtractCustomSqlStrataOnlyRoleRequirement()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Strata.Analytics.Biz.Extracts.Services;
|
||||
|
||||
namespace Strata.Analytics.Api.Authorization
|
||||
{
|
||||
public class ExtractMinimumRoleHandler : AuthorizationHandler<ExtractMinimumRoleRequirement>
|
||||
{
|
||||
private readonly IExtractService _extractService;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
public ExtractMinimumRoleHandler(IExtractService extractService, IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_extractService = extractService;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ExtractMinimumRoleRequirement requirement)
|
||||
{
|
||||
var extractIdObj = _httpContextAccessor.HttpContext!.GetRouteData()?.Values["extractId"];
|
||||
if (extractIdObj == null)
|
||||
{
|
||||
context.Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
var extractId = int.Parse(extractIdObj.ToString()!);
|
||||
var extractRole = await _extractService.GetCurrentUserRoleAsync(extractId, System.Threading.CancellationToken.None);
|
||||
|
||||
var extract =
|
||||
await _extractService.GetByIdAsync(extractId, System.Threading.CancellationToken.None);
|
||||
if (extract == null)
|
||||
{
|
||||
context.Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!extractRole.HasValue || extractRole.Value < requirement.MinimumRole)
|
||||
{
|
||||
context.Fail();
|
||||
return;
|
||||
}
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Strata.Analytics.Biz.Extracts.Models;
|
||||
|
||||
namespace Strata.Analytics.Api.Authorization
|
||||
{
|
||||
public class ExtractMinimumRoleRequirement : IAuthorizationRequirement
|
||||
{
|
||||
public ExtractRole MinimumRole { get; set; }
|
||||
|
||||
public ExtractMinimumRoleRequirement(ExtractRole role)
|
||||
{
|
||||
MinimumRole = role;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace Strata.Analytics.Api.Authorization
|
||||
{
|
||||
public class Policies
|
||||
{
|
||||
public const string StrataOnly = "StrataOnly";
|
||||
public const string IsAdmin = "IsAdmin";
|
||||
public const string DataSourceViewer = "DataSourceViewer";
|
||||
public const string DataSourceEditor = "DataSourceEditor";
|
||||
public const string WorkbookViewer = "WorkbookViewer";
|
||||
public const string WorkbookEditor = "WorkbookEditor";
|
||||
public const string ExtractViewer = "ExtractViewer";
|
||||
public const string ExtractEditor = "ExtractEditor";
|
||||
public const string AuthorizedDbOnly = "AuthorizedDbOnly";
|
||||
public const string CustomSqlAccess = "CustomSqlAccess";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Strata.Analytics.Biz.Workbooks.Services;
|
||||
|
||||
namespace Strata.Analytics.Api.Authorization
|
||||
{
|
||||
public class WorkbookMinimumRoleHandler : AuthorizationHandler<WorkbookMinimumRoleRequirement>
|
||||
{
|
||||
private readonly IWorkbookService _workbookService;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
|
||||
public WorkbookMinimumRoleHandler(IWorkbookService workbookService, IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_workbookService = workbookService;
|
||||
_httpContextAccessor = httpContextAccessor;;
|
||||
}
|
||||
|
||||
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, WorkbookMinimumRoleRequirement requirement)
|
||||
{
|
||||
var workbookIdObj = _httpContextAccessor.HttpContext.GetRouteData()?.Values["workbookId"];
|
||||
if (workbookIdObj == null)
|
||||
{
|
||||
context.Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
var workbookId = int.Parse(workbookIdObj.ToString());
|
||||
var workbookRole = await _workbookService.GetCurrentUserRoleAsync(workbookId, System.Threading.CancellationToken.None);
|
||||
|
||||
if (!workbookRole.HasValue || workbookRole.Value < requirement.MinimumRole)
|
||||
{
|
||||
context.Fail();
|
||||
return;
|
||||
}
|
||||
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Strata.Analytics.Models.Workbooks;
|
||||
|
||||
namespace Strata.Analytics.Api.Authorization
|
||||
{
|
||||
public class WorkbookMinimumRoleRequirement : IAuthorizationRequirement
|
||||
{
|
||||
public WorkbookRole MinimumRole { get; set; }
|
||||
|
||||
public WorkbookMinimumRoleRequirement(WorkbookRole role)
|
||||
{
|
||||
MinimumRole = role;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Strata.Analytics.Biz.ContentMigration.Services;
|
||||
using Strata.Analytics.Biz.ContentMigration.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Strata.Analytics.Api.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Strata.Analytics.Biz.ContentMigration.Models.MigrationPlan;
|
||||
|
||||
namespace Strata.Analytics.Api.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/v{api-version:apiVersion}/[controller]")]
|
||||
public class ContentMigrationController : ControllerBase
|
||||
{
|
||||
private readonly IContentMigrationService _contentMigrationService;
|
||||
|
||||
public ContentMigrationController(IContentMigrationService contentMigrationService)
|
||||
{
|
||||
_contentMigrationService = contentMigrationService;
|
||||
}
|
||||
|
||||
[HttpGet("legacy-reports")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<IEnumerable<ReReportDisplay>> GetAdHocReports(CancellationToken cancellationToken)
|
||||
{
|
||||
return await _contentMigrationService.GetAdHocReports(cancellationToken);
|
||||
}
|
||||
|
||||
[HttpPost("{reportGuid}/migrate")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<AdhocMigrationResult>> MigrateReport([FromRoute] Guid reportGuid, [FromQuery, BindRequired] int workbookId, [FromQuery, BindRequired] int dataSourceId, [FromBody] Plan plan, CancellationToken cancellationToken)
|
||||
{
|
||||
var migratedReport = await _contentMigrationService.MigrateReport(reportGuid, workbookId, dataSourceId, plan, cancellationToken);
|
||||
if (migratedReport == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return Ok(migratedReport);
|
||||
}
|
||||
|
||||
[HttpPost("{reportGuid}/migrateExtract")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<ExtractMigrationResult>> MigrateReportToExtract([FromRoute] Guid reportGuid, [FromQuery, BindRequired] int dataSourceId, [FromBody] Plan plan, CancellationToken cancellationToken)
|
||||
{
|
||||
var migratedReport = await _contentMigrationService.MigrateReportToExtract(reportGuid, dataSourceId, plan, cancellationToken);
|
||||
if (migratedReport == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return Ok(migratedReport);
|
||||
}
|
||||
|
||||
[HttpGet("migrationPlan")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<Plan>> GetMigrationPlan([FromQuery, BindRequired] Guid reportGuid, [FromQuery, BindRequired] int dataSourceId, CancellationToken cancellationToken)
|
||||
{
|
||||
var migrationPlan = await _contentMigrationService.GetMigrationPlan(reportGuid, dataSourceId, cancellationToken);
|
||||
if (migrationPlan == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return Ok(migrationPlan);
|
||||
}
|
||||
|
||||
[HttpPut("dataMappings")]
|
||||
[Authorize(Policy = Policies.StrataOnly)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult<DataMapping>> UpdateDataMapping([FromBody] DataMapping dataMapping, CancellationToken cancellationToken)
|
||||
{
|
||||
var updatedDataMapping = await _contentMigrationService.UpdateDataMapping(dataMapping, cancellationToken);
|
||||
if (updatedDataMapping == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return Ok(updatedDataMapping);
|
||||
}
|
||||
|
||||
[HttpGet("dataMappings")]
|
||||
[Authorize(Policy = Policies.StrataOnly)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
public async Task<ActionResult<IEnumerable<DataMapping>>> GetDataMappings(CancellationToken cancellationToken)
|
||||
{
|
||||
var dataMappings = await _contentMigrationService.GetDataMappings(cancellationToken);
|
||||
return Ok(dataMappings);
|
||||
}
|
||||
|
||||
[HttpPost("dataMappings")]
|
||||
[Authorize(Policy = Policies.StrataOnly)]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
public async Task<ActionResult> AddDataMappings([FromBody] IEnumerable<DataMapping> dataMappings, CancellationToken cancellationToken)
|
||||
{
|
||||
await _contentMigrationService.AddDataMappings(dataMappings, cancellationToken);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpDelete("dataMappings/{dataMappingId}")]
|
||||
[Authorize(Policy = Policies.StrataOnly)]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> DeleteDataMapping([FromRoute] int dataMappingId, CancellationToken cancellationToken)
|
||||
{
|
||||
var mapping = await _contentMigrationService.GetDataMapping(dataMappingId, cancellationToken);
|
||||
if (mapping == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
await _contentMigrationService.DeleteDataMapping(mapping, cancellationToken);
|
||||
return NoContent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Strata.Analytics.Api.Authorization;
|
||||
using Strata.Analytics.Biz.DataQuery;
|
||||
using Strata.Analytics.Biz.DataSources.Models;
|
||||
using Strata.Analytics.Biz.DataSources.Services;
|
||||
using Strata.Analytics.Biz.Export.Models;
|
||||
using Strata.Analytics.Biz.Export.Services;
|
||||
using Strata.Analytics.Models.Utils;
|
||||
using Strata.DataSchema.Models.Query;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.Analytics.Api.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/v{api-version:apiVersion}/[controller]")]
|
||||
public class DataSourcesController : ControllerBase
|
||||
{
|
||||
private readonly IDataSourceService _dataSourceService;
|
||||
private readonly IDataQueryService _dataQueryService;
|
||||
private readonly IExportService _exportService;
|
||||
private readonly ILogger<DataSourcesController> _logger;
|
||||
|
||||
public DataSourcesController(IDataSourceService dataSourceService, IDataQueryService dataQueryService, IExportService exportService, ILogger<DataSourcesController> logger)
|
||||
{
|
||||
_dataSourceService = dataSourceService;
|
||||
_dataQueryService = dataQueryService;
|
||||
_exportService = exportService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IEnumerable<DataSource>> GetDataSourcesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
return await _dataSourceService.GetAllAsync(cancellationToken);
|
||||
}
|
||||
|
||||
[HttpGet("{dataSourceId}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<DataSource>> GetById(int dataSourceId, CancellationToken cancellationToken)
|
||||
{
|
||||
var dataSource = await _dataSourceService.GetByIdAsync(dataSourceId, cancellationToken);
|
||||
if (dataSource == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(dataSource);
|
||||
}
|
||||
|
||||
[HttpPut("{dataSourceId}")]
|
||||
[Authorize(Policy = Policies.DataSourceEditor)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<DataSource>> Update(int dataSourceId, DataSourceSaveInfo dataSourceSaveInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
var isFound = await _dataSourceService.ContainsAsync(dataSourceId, cancellationToken);
|
||||
if (!isFound)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var updatedDataSource = await _dataSourceService.UpdateAsync(dataSourceId, dataSourceSaveInfo, cancellationToken);
|
||||
|
||||
return Ok(updatedDataSource);
|
||||
}
|
||||
|
||||
[HttpPut("")]
|
||||
[Authorize(Policy = Policies.DataSourceEditor)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<DataSource>> Save(DataSourceSaveInfo dataSourceSaveInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
var isFound = await _dataSourceService.ContainsAsync(dataSourceSaveInfo.DataSourceId, cancellationToken);
|
||||
if (!isFound)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var updatedDataSource = await _dataSourceService.SaveAsync(dataSourceSaveInfo, cancellationToken);
|
||||
|
||||
return Ok(updatedDataSource);
|
||||
}
|
||||
|
||||
[HttpDelete("{dataSourceId}")]
|
||||
[Authorize(Policy = Policies.DataSourceEditor)]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<IActionResult> Delete(int dataSourceId, CancellationToken cancellationToken)
|
||||
{
|
||||
await _dataSourceService.DeleteAsync(dataSourceId, cancellationToken);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost("")]
|
||||
[Authorize(Policy = Policies.StrataOnly)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
public async Task<ActionResult<DataSource>> Create(DataSourceSaveInfo dataSourceSaveInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _dataSourceService.CreateAsync(dataSourceSaveInfo, cancellationToken);
|
||||
}
|
||||
|
||||
[HttpPost("{dataSourceId}/tables/{dataTableId}")]
|
||||
[Authorize(Policy = Policies.DataSourceEditor)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<DataSourceTable>> AddDataTableLink([FromRoute] int dataSourceId, [FromRoute] int dataTableId, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _dataSourceService.AddDataTableAsync(dataSourceId, dataTableId, cancellationToken);
|
||||
}
|
||||
|
||||
[HttpDelete("{dataSourceId}/tables/{dataTableId}")]
|
||||
[Authorize(Policy = Policies.DataSourceEditor)]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult> DeleteDataTableLink([FromRoute] int dataSourceId, [FromRoute] int dataTableId, CancellationToken cancellationToken)
|
||||
{
|
||||
await _dataSourceService.DeleteDataTableAsync(dataSourceId, dataTableId, cancellationToken);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpGet("{dataSourceId}/role-assignments")]
|
||||
[Authorize(Policy = Policies.DataSourceViewer)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<IEnumerable<DataSourceRoleAssignment>>> GetRoleAssignments([FromRoute] int dataSourceId, CancellationToken cancellationToken)
|
||||
{
|
||||
return Ok(await _dataSourceService.GetRoleAssignmentsAsync(new List<int> { dataSourceId }, cancellationToken));
|
||||
}
|
||||
|
||||
[HttpPost("{dataSourceId}/role-assignments/batch")]
|
||||
[Authorize(Policy = Policies.DataSourceEditor)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<IEnumerable<DataSourceRoleAssignment>>> SaveRoleAssignments([FromRoute] int dataSourceId, [FromBody] Batch<DataSourceRoleAssignmentSaveInfo, Guid> roleAssignments, CancellationToken cancellationToken)
|
||||
{
|
||||
return Ok(await _dataSourceService.SaveRoleAssignmentsAsync(dataSourceId, roleAssignments, cancellationToken));
|
||||
}
|
||||
|
||||
[HttpPost("{dataSourceId}/datacolumn/values")]
|
||||
[Authorize(Policy = Policies.DataSourceViewer)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<IEnumerable<dynamic>>> GetColumnValues([FromRoute] int dataSourceId, [FromBody] ColumnQueryConfig columnQueryConfig, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Ok(await _dataQueryService.GetColumnValues(columnQueryConfig, cancellationToken));
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
var dataColumnIds = new List<int> { columnQueryConfig.DataColumnId };
|
||||
if (columnQueryConfig.Filter != null)
|
||||
dataColumnIds.Add(columnQueryConfig.Filter.DataColumnId);
|
||||
_logger.LogWarning(
|
||||
"Attempted to {method} for data source {dataSourceId} with the following data column ids {dataColumnIds}",
|
||||
nameof(GetColumnValues),
|
||||
dataSourceId,
|
||||
dataColumnIds);
|
||||
return Forbid();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("{dataSourceId}/query")]
|
||||
[Authorize(Policy = Policies.DataSourceViewer)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<IEnumerable<dynamic>>> QueryData([FromRoute] int dataSourceId, [FromBody] QueryConfig query, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Ok(await _dataQueryService.QueryData(query, cancellationToken));
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Attempted to {method} for data source {dataSourceId} with the following data column ids {dataColumnIds}",
|
||||
nameof(QueryData),
|
||||
dataSourceId,
|
||||
query.GetAllColumnIds());
|
||||
return Forbid();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("query")]
|
||||
[Authorize(Policy = Policies.StrataOnly)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<SqlResponse>> GetSqlQuery(QueryConfig queryConfig, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Ok(await _dataQueryService.BuildSqlQuery(queryConfig, cancellationToken));
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Attempted to {method} with the following data column ids {dataColumnIds}",
|
||||
nameof(GetSqlQuery),
|
||||
queryConfig.GetAllColumnIds());
|
||||
return Forbid();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("{dataSourceId}/download")]
|
||||
[Authorize(Policy = Policies.DataSourceViewer)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult> DownloadDataSheet([FromRoute] int dataSourceId, [FromBody] ExportDataSheetRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
var stream = await _exportService.DownloadDataSheet(request, cancellationToken);
|
||||
return File(stream, "application/octet-stream");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using Strata.Analytics.Biz.Database;
|
||||
|
||||
namespace Strata.Analytics.Api.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/v{api-version:apiVersion}/[controller]")]
|
||||
public class DatabasesController : ControllerBase
|
||||
{
|
||||
private readonly IDatabaseService _databaseService;
|
||||
|
||||
public DatabasesController(IDatabaseService databaseService)
|
||||
{
|
||||
_databaseService = databaseService;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<IEnumerable<Database>> GetDatabases(CancellationToken cancellationToken)
|
||||
{
|
||||
return await _databaseService.GetDatabasesAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Strata.Analytics.Biz.Encounters;
|
||||
using Strata.Analytics.Api.Models;
|
||||
|
||||
namespace Strata.Analytics.Api.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/v{api-version:apiVersion}/[controller]")]
|
||||
public class EncountersController : ControllerBase
|
||||
{
|
||||
private readonly IEncounterService _encounterService;
|
||||
|
||||
public EncountersController(IEncounterService encounterService)
|
||||
{
|
||||
_encounterService = encounterService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given an encounter record number, return the encounter id
|
||||
/// </summary>
|
||||
[HttpPost("encounter-id")]
|
||||
public async Task<int> GetEncounterId([FromBody] EncounterInfo encounterInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
// Needs to be POST to prevent PHI data in url string
|
||||
return await _encounterService.GetEncounterIdAsync(encounterInfo.EncounterRecordNumber, cancellationToken);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Strata.Analytics.Biz.Extracts.Models;
|
||||
using Strata.Analytics.Biz.Extracts.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Hangfire;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Strata.Analytics.Api.Authorization;
|
||||
using Strata.Analytics.Models.Utils;
|
||||
using System;
|
||||
using Strata.Analytics.Biz.Extracts.Jobs;
|
||||
using System.Security.Claims;
|
||||
using Hangfire.States;
|
||||
using Strata.CoreLib.Claims;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
|
||||
namespace Strata.Analytics.Api.Controllers
|
||||
{
|
||||
[Authorize(Policy = Policies.IsAdmin)]
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/v{api-version:apiVersion}/[controller]")]
|
||||
public class ExtractsController : ControllerBase
|
||||
{
|
||||
private readonly IExtractService _extractService;
|
||||
private readonly IBackgroundJobClient _backgroundJobClient;
|
||||
private readonly IExtractExportHistoryService _historyService;
|
||||
private readonly IExtractScheduleService _extractScheduleService;
|
||||
private readonly ClaimsPrincipal _user;
|
||||
|
||||
public ExtractsController(IExtractService extractService, IBackgroundJobClient backgroundJobClient, IExtractExportHistoryService historyService, IExtractScheduleService extractScheduleService, IClaimsPrincipalAccessor claimsPrincipalAccessor)
|
||||
{
|
||||
_extractService = extractService;
|
||||
_backgroundJobClient = backgroundJobClient;
|
||||
_historyService = historyService;
|
||||
_extractScheduleService = extractScheduleService;
|
||||
_user = claimsPrincipalAccessor.GetCurrentClaimsPrincipal();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IEnumerable<Extract>> GetExtractsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return await _extractService.GetAllAsync(cancellationToken);
|
||||
}
|
||||
|
||||
[HttpGet("{extractId}")]
|
||||
[Authorize(Policy = Policies.ExtractViewer)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<Extract>> GetExtractById(int extractId, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
var extract = await _extractService.GetByIdAsync(extractId, cancellationToken);
|
||||
if (extract == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(extract);
|
||||
}
|
||||
|
||||
[HttpDelete("{extractId}")]
|
||||
[Authorize(Policy = Policies.CustomSqlAccess)]
|
||||
[Authorize(Policy = Policies.ExtractEditor)]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<IActionResult> DeleteExtract(int extractId, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!await _extractService.ContainsAsync(extractId, cancellationToken))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
await _extractScheduleService.DeleteByExtractIdAsync(extractId, cancellationToken);
|
||||
await _extractService.DeleteAsync(extractId, cancellationToken);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<Extract>> CreateExtract(ExtractCreateInfo extractCreateInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _extractService.CreateAsync(extractCreateInfo, cancellationToken);
|
||||
}
|
||||
|
||||
[HttpPut("{extractId}")]
|
||||
[Authorize(Policy = Policies.CustomSqlAccess)]
|
||||
[Authorize(Policy = Policies.ExtractEditor)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<Extract>> UpdateExtract([FromRoute] int extractId, [FromBody] ExtractSaveInfo extractSaveInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!await _extractService.ContainsAsync(extractId, cancellationToken))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(await _extractService.UpdateAsync(extractId, extractSaveInfo, cancellationToken));
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("export/{extractId}")]
|
||||
[ProducesResponseType(StatusCodes.Status202Accepted)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
[ProducesDefaultResponseType]
|
||||
[Authorize(Policy = Policies.ExtractEditor)]
|
||||
public async Task<IActionResult> ExportExtract(int extractId, [FromBody] ExtractExportRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!await _extractService.ContainsAsync(request.ExtractId, cancellationToken))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
request.Timezone = _user.GetTimezone();
|
||||
|
||||
var jobId = _backgroundJobClient.Enqueue<IExtractExportJob>(job =>
|
||||
job.ExecuteExport(request.OutputFileName, _user.GetDatabaseGuid(), _user.GetStrataId(), _user.GetUserGuid(), request, null, cancellationToken));
|
||||
|
||||
return Accepted(new { jobId });
|
||||
}
|
||||
|
||||
[HttpDelete("{extractId}/history/{extractHistoryId}/cancel/")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
[ProducesDefaultResponseType]
|
||||
[Authorize(Policy = Policies.ExtractEditor)]
|
||||
public async Task<ActionResult<ExtractExportHistory>> CancelExportExtract([FromRoute] int extractId, [FromRoute] int extractHistoryId, CancellationToken cancellationToken)
|
||||
{
|
||||
var history = await _historyService.CancelExtractExport(extractHistoryId, cancellationToken);
|
||||
|
||||
if (history == null) return NotFound();
|
||||
|
||||
_backgroundJobClient.ChangeState(history.JobId, new DeletedState {Reason = "Job cancelled manually" });
|
||||
|
||||
return Ok(history);
|
||||
}
|
||||
|
||||
[HttpGet("history/{extractId}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
[Authorize(Policy = Policies.ExtractViewer)]
|
||||
public async Task<ActionResult<List<ExtractExportHistory>>> GetHistoryById(int extractId, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!await _extractService.ContainsAsync(extractId, cancellationToken))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(await _historyService.GetByExtractIdAsync(extractId, cancellationToken));
|
||||
}
|
||||
|
||||
[HttpGet("{extractId}/role-assignments")]
|
||||
[Authorize(Policy = Policies.ExtractViewer)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<IEnumerable<ExtractRoleAssignment>>> GetRoleAssignments([FromRoute] int extractId, CancellationToken cancellationToken)
|
||||
{
|
||||
return Ok(await _extractService.GetRoleAssignmentsAsync(extractId, cancellationToken));
|
||||
}
|
||||
|
||||
[HttpPost("{extractId}/role-assignments/batch")]
|
||||
[Authorize(Policy = Policies.ExtractEditor)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<IEnumerable<ExtractRoleAssignment>>> SaveRoleAssignments(
|
||||
[FromRoute] int extractId, [FromBody] Batch<ExtractRoleAssignmentSaveInfo, Guid> roleAssignments, CancellationToken cancellationToken)
|
||||
{
|
||||
return Ok(await _extractService.SaveRoleAssignmentsAsync(extractId, roleAssignments, cancellationToken));
|
||||
}
|
||||
|
||||
[HttpPost("{extractId}/schedule")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
[Authorize(Policy = Policies.ExtractEditor)]
|
||||
public async Task<ActionResult<ExtractSchedule>> CreateExtractSchedule([FromRoute] int extractId, [FromBody] ExtractScheduleSaveInfo saveInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!await _extractService.ContainsAsync(extractId, cancellationToken))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(await _extractScheduleService.CreateAsync(saveInfo, cancellationToken));
|
||||
}
|
||||
|
||||
[HttpPut("{extractId}/schedule/{extractScheduleId}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
[Authorize(Policy = Policies.ExtractEditor)]
|
||||
public async Task<ActionResult<ExtractSchedule>> UpdateExtractSchedule([FromRoute] int extractId, [FromRoute] int extractScheduleId, [FromBody] ExtractScheduleSaveInfo saveInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!await _extractScheduleService.ContainsAsync(extractScheduleId, cancellationToken))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(await _extractScheduleService.UpdateAsync(extractScheduleId, saveInfo, cancellationToken));
|
||||
}
|
||||
|
||||
[HttpGet("{extractId}/schedule")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesDefaultResponseType]
|
||||
[Authorize(Policy = Policies.ExtractEditor)]
|
||||
public async Task<ActionResult<ExtractSchedule>> GetScheduleByExtractId([FromRoute] int extractId, CancellationToken cancellationToken)
|
||||
{
|
||||
return Ok(await _extractScheduleService.GetByExtractIdAsync(extractId, cancellationToken));
|
||||
}
|
||||
|
||||
[HttpDelete("{extractId}/schedule/{extractScheduleId}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
[Authorize(Policy = Policies.ExtractEditor)]
|
||||
public async Task<ActionResult> DeleteExtractSchedule([FromRoute] int extractId, [FromRoute] int extractScheduleId, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!await _extractScheduleService.ContainsAsync(extractScheduleId, cancellationToken))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
await _extractScheduleService.DeleteAsync(extractScheduleId, cancellationToken);
|
||||
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Threading.Tasks;
|
||||
using Strata.FeatureFlags.Client;
|
||||
using System.Security.Claims;
|
||||
using Strata.CoreLib.Claims;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
|
||||
namespace Strata.Analytics.Api.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/v{api-version:apiVersion}/[controller]")]
|
||||
public class FeatureController : ControllerBase
|
||||
{
|
||||
private readonly IFeatureFlagServiceClient _featureFlagService;
|
||||
private readonly ClaimsPrincipal _user;
|
||||
|
||||
public FeatureController(IFeatureFlagServiceClient featureFlagService, IClaimsPrincipalAccessor claimsPrincipalAccessor)
|
||||
{
|
||||
_featureFlagService = featureFlagService;
|
||||
_user = claimsPrincipalAccessor.GetCurrentClaimsPrincipal();
|
||||
}
|
||||
|
||||
[HttpGet("dashboards-enabled")]
|
||||
public async Task<bool> IsDashboardsEnabled()
|
||||
{
|
||||
return await _featureFlagService.IsEnabledAsync("analytics-dashboards", _user.GetDatabaseGuid());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Strata.Analytics.Biz.Workbooks.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Strata.Analytics.Models.Workbooks;
|
||||
|
||||
namespace Strata.Analytics.Api.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/v{api-version:apiVersion}/[controller]")]
|
||||
public class SavedFiltersController : ControllerBase
|
||||
{
|
||||
private readonly ISavedFilterService _savedFilterService;
|
||||
|
||||
public SavedFiltersController(ISavedFilterService savedFiltersService)
|
||||
{
|
||||
_savedFilterService = savedFiltersService;
|
||||
}
|
||||
|
||||
[HttpGet("{savedFilterId}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<SavedFilterInfo>> GetSavedFiltersAsync(int savedFilterId, CancellationToken cancellationToken)
|
||||
{
|
||||
var savedFilters = await _savedFilterService.GetSavedFiltersAsync(savedFilterId, cancellationToken);
|
||||
if (savedFilters == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(savedFilters);
|
||||
}
|
||||
|
||||
[HttpDelete("{savedFilterId}")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<IActionResult> Delete(int savedFilterId, CancellationToken cancellationToken)
|
||||
{
|
||||
await _savedFilterService.DeleteSavedFiltersAsync(savedFilterId, cancellationToken);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<int>> Create(SavedFilterInfo savedFilters, CancellationToken cancellationToken)
|
||||
{
|
||||
var cache = await _savedFilterService.CreateSavedFiltersAsync(savedFilters, cancellationToken);
|
||||
|
||||
return cache.SavedFilterId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
using Hangfire;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Strata.Analytics.Api.Authorization;
|
||||
using Strata.Analytics.Api.Models;
|
||||
using Strata.Analytics.Biz.Database;
|
||||
using Strata.Analytics.Biz.DataQuery;
|
||||
using Strata.Analytics.Biz.DataSources.Models;
|
||||
using Strata.Analytics.Biz.Schema;
|
||||
using Strata.Analytics.Biz.Schema.Jobs;
|
||||
using Strata.Analytics.Biz.StandardDataSources.Jobs;
|
||||
using Strata.Analytics.Biz.StandardDataSources.Models;
|
||||
using Strata.Analytics.Biz.StandardDataSources.Services;
|
||||
using Strata.Analytics.Models;
|
||||
using Strata.Analytics.Models.Utils;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
using Strata.DataSchema.Models.Query;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Strata.Analytics.Api.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/v{api-version:apiVersion}/[controller]")]
|
||||
public class StandardDataSourcesController : ControllerBase
|
||||
{
|
||||
private readonly IStandardDataSourceService _standardDataSourceService;
|
||||
private readonly IDataQueryService _dataQueryService;
|
||||
private readonly IDatabaseService _databaseService;
|
||||
private readonly IStandardDataSourcePublishService _standardDataSourcePublishService;
|
||||
private readonly IBackgroundJobClient _backgroundJobClient;
|
||||
private readonly IPublishStandardDataSourcesJob _publishStandardDataSourcesJob;
|
||||
private readonly ILogger<StandardDataSourcesController> _logger;
|
||||
|
||||
public StandardDataSourcesController(
|
||||
IStandardDataSourceService standardDataSourceService,
|
||||
IDataQueryService dataQueryService,
|
||||
IDatabaseService databaseService,
|
||||
IStandardDataSourcePublishService standardDataSourcePublishService,
|
||||
IBackgroundJobClient backgroundJobClient,
|
||||
IPublishStandardDataSourcesJob publishStandardDataSourcesJob,
|
||||
ILogger<StandardDataSourcesController> logger)
|
||||
{
|
||||
_standardDataSourceService = standardDataSourceService;
|
||||
_dataQueryService = dataQueryService;
|
||||
_databaseService = databaseService;
|
||||
_standardDataSourcePublishService = standardDataSourcePublishService;
|
||||
_backgroundJobClient = backgroundJobClient;
|
||||
_publishStandardDataSourcesJob = publishStandardDataSourcesJob;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IEnumerable<StandardDataSource>> GetDataSourcesAsync(bool includeTableLinks = false, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _standardDataSourceService.GetAllAsync(includeTableLinks, cancellationToken: cancellationToken);
|
||||
}
|
||||
|
||||
[HttpGet("{standardDataSourceId}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<StandardDataSource>> GetById(int standardDataSourceId, CancellationToken cancellationToken)
|
||||
{
|
||||
var standardDataSource = await _standardDataSourceService.GetByIdAsync(standardDataSourceId, cancellationToken);
|
||||
if (standardDataSource == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(standardDataSource);
|
||||
}
|
||||
|
||||
[HttpPut("")]
|
||||
[Authorize(Policy = Policies.StrataOnly)]
|
||||
[Authorize(Policy = Policies.AuthorizedDbOnly)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<StandardDataSource>> Save(StandardDataSourceSaveInfo standardDataSourceSaveInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
var isFound = await _standardDataSourceService.ContainsAsync(standardDataSourceSaveInfo.StandardDataSourceId, cancellationToken);
|
||||
if (!isFound)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var updatedDataSource = await _standardDataSourceService.SaveAsync(standardDataSourceSaveInfo, cancellationToken);
|
||||
|
||||
return Ok(updatedDataSource);
|
||||
}
|
||||
|
||||
[HttpDelete("{standardDataSourceId}")]
|
||||
[Authorize(Policy = Policies.StrataOnly)]
|
||||
[Authorize(Policy = Policies.AuthorizedDbOnly)]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<IActionResult> Delete(int standardDataSourceId, CancellationToken cancellationToken)
|
||||
{
|
||||
await _standardDataSourceService.DeleteAsync(standardDataSourceId, cancellationToken);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost("")]
|
||||
[Authorize(Policy = Policies.StrataOnly)]
|
||||
[Authorize(Policy = Policies.AuthorizedDbOnly)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
public async Task<ActionResult<StandardDataSource>> Create(StandardDataSourceSaveInfo standardDataSourceSaveInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _standardDataSourceService.CreateAsync(standardDataSourceSaveInfo, cancellationToken);
|
||||
}
|
||||
|
||||
[HttpGet("{standardDataSourceId}/role-assignments")]
|
||||
[Authorize(Policy = Policies.StrataOnly)]
|
||||
[Authorize(Policy = Policies.AuthorizedDbOnly)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<IEnumerable<StandardDataSourceRoleAssignment>>> GetRoleAssignments([FromRoute] int standardDataSourceId, CancellationToken cancellationToken)
|
||||
{
|
||||
return Ok(await _standardDataSourceService.GetRoleAssignmentsAsync(new List<int> { standardDataSourceId }, cancellationToken));
|
||||
}
|
||||
|
||||
[HttpPost("{standardDataSourceId}/role-assignments/batch")]
|
||||
[Authorize(Policy = Policies.StrataOnly)]
|
||||
[Authorize(Policy = Policies.AuthorizedDbOnly)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<IEnumerable<DataSourceRoleAssignment>>> SaveRoleAssignments([FromRoute] int standardDataSourceId, [FromBody] Batch<StandardDataSourceRoleAssignmentSaveInfo, Guid> roleAssignments, CancellationToken cancellationToken)
|
||||
{
|
||||
return Ok(await _standardDataSourceService.SaveRoleAssignmentsAsync(standardDataSourceId, roleAssignments, cancellationToken));
|
||||
}
|
||||
|
||||
[HttpPost("{standardDataSourceId}/datacolumn/values")]
|
||||
[Authorize(Policy = Policies.StrataOnly)]
|
||||
[Authorize(Policy = Policies.AuthorizedDbOnly)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<IEnumerable<dynamic>>> GetColumnValues([FromRoute] int standardDataSourceId, [FromBody] ColumnQueryConfig columnQueryConfig, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Ok(await _dataQueryService.GetColumnValues(columnQueryConfig, cancellationToken));
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
var dataColumnIds = new List<int> { columnQueryConfig.DataColumnId };
|
||||
if (columnQueryConfig.Filter != null)
|
||||
dataColumnIds.Add(columnQueryConfig.Filter.DataColumnId);
|
||||
_logger.LogWarning(
|
||||
"Attempted to {method} for standard data source {standardDataSourceId} with the following data column ids {dataColumnIds}",
|
||||
nameof(GetColumnValues),
|
||||
standardDataSourceId,
|
||||
dataColumnIds);
|
||||
return Forbid();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("{standardDataSourceId}/query")]
|
||||
[Authorize(Policy = Policies.StrataOnly)]
|
||||
[Authorize(Policy = Policies.AuthorizedDbOnly)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<IEnumerable<dynamic>>> QueryData([FromRoute] int standardDataSourceId, [FromBody] QueryConfig query, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Ok(await _dataQueryService.QueryData(query, cancellationToken));
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Attempted to {method} for standard data source {standardDataSourceId} with the following data column ids {dataColumnsIds}",
|
||||
nameof(QueryData),
|
||||
standardDataSourceId,
|
||||
query.GetAllColumnIds());
|
||||
return Forbid();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("query")]
|
||||
[Authorize(Policy = Policies.StrataOnly)]
|
||||
[Authorize(Policy = Policies.AuthorizedDbOnly)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<SqlResponse>> GetSqlQuery(QueryConfig queryConfig, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Ok(await _dataQueryService.BuildSqlQuery(queryConfig, cancellationToken));
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Attempted to {method} with the following data column ids {dataColumnIds}",
|
||||
nameof(GetSqlQuery),
|
||||
queryConfig.GetAllColumnIds());
|
||||
return Forbid();
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("{standardDataSourceId}/include-in-publish")]
|
||||
[Authorize(Policy = Policies.StrataOnly)]
|
||||
[Authorize(Policy = Policies.AuthorizedDbOnly)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<StandardDataSource>> IncludeDataSourceInPublish([FromRoute] int standardDataSourceId, CancellationToken cancellationToken)
|
||||
{
|
||||
return Ok(await _standardDataSourceService.SetIsIncludedInStandardModel(standardDataSourceId, true, cancellationToken));
|
||||
|
||||
}
|
||||
|
||||
[HttpPost("{standardDataSourceId}/exclude-from-publish")]
|
||||
[Authorize(Policy = Policies.StrataOnly)]
|
||||
[Authorize(Policy = Policies.AuthorizedDbOnly)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<StandardDataSource>> ExcludeDataSourceInPublish([FromRoute] int standardDataSourceId, CancellationToken cancellationToken)
|
||||
{
|
||||
return Ok(await _standardDataSourceService.SetIsIncludedInStandardModel(standardDataSourceId, false, cancellationToken));
|
||||
|
||||
}
|
||||
|
||||
[HttpPost("publish")]
|
||||
[Authorize(Policy = Policies.AuthorizedDbOnly)]
|
||||
public async Task<string> PublishAsync([FromBody] PublishConfig publishConfig, CancellationToken cancellationToken)
|
||||
{
|
||||
if (publishConfig.StrataId == 0)
|
||||
{
|
||||
var databases = await _databaseService.GetDatabasesAsync(cancellationToken);
|
||||
return _backgroundJobClient.Enqueue<PublishStandardDataSourcesAllClientsJob>(job => job.ExecuteAsync(databases.ToList(), User.GetUsername(), DateTime.UtcNow, CancellationToken.None));
|
||||
}
|
||||
|
||||
if (Debugger.IsAttached && publishConfig.StrataId == User.GetStrataId())
|
||||
{
|
||||
await _publishStandardDataSourcesJob.ExecuteAsync(User.GetDatabaseGuid(), User.GetStrataId(), Guid.NewGuid().ToString(), User.GetUsername(), DateTime.UtcNow, cancellationToken);
|
||||
return "";
|
||||
}
|
||||
var database = await _databaseService.GetDatabaseByStrataId(publishConfig.StrataId, cancellationToken);
|
||||
var parentJobId = _backgroundJobClient.Enqueue<PublishStandardDataSourcesJob>(job => job.ExecuteAsync(database.DatabaseGuid, database.StrataId, Guid.NewGuid().ToString(), User.GetUsername(), DateTime.UtcNow, CancellationToken.None));
|
||||
|
||||
return parentJobId;
|
||||
}
|
||||
|
||||
[HttpGet("publish-history")]
|
||||
public async Task<ActionResult<IEnumerable<PublicationEvent>>> GetPublicationHistory(CancellationToken cancellationToken)
|
||||
{
|
||||
var publications = await _standardDataSourcePublishService.GetPublicationsAsync(cancellationToken);
|
||||
|
||||
return Ok(publications
|
||||
.GroupBy(x => x.RequestCorrelationId)
|
||||
.Select(x => new PublicationEvent
|
||||
{
|
||||
RequestDate = x.FirstOrDefault().RequestDate,
|
||||
RequestedBy = x.FirstOrDefault().RequestedBy,
|
||||
StandardDataSourceIds = x.Select(y => y.StandardDataSourceId).Distinct(),
|
||||
StrataIds = x.Select(y => y.ClientId).Distinct()
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Strata.Users.Client;
|
||||
using Strata.Users.Client.UserGroups.Models;
|
||||
using System.Linq;
|
||||
|
||||
namespace Strata.Analytics.Api.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/v{api-version:apiVersion}/[controller]")]
|
||||
public class UserGroupsController : ControllerBase
|
||||
{
|
||||
private readonly IUsersServiceClient _usersService;
|
||||
|
||||
public UserGroupsController(IUsersServiceClient usersService)
|
||||
{
|
||||
_usersService = usersService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IEnumerable<UserGroup>> GetUserGroups(CancellationToken cancellationToken)
|
||||
{
|
||||
var userGroups = await _usersService.GetAllUserGroupsAsync(cancellationToken);
|
||||
return userGroups.Where(ug => !ug.IsHidden);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Strata.Users.Client;
|
||||
using Strata.Users.Client.Users.Models;
|
||||
|
||||
namespace Strata.Analytics.Api.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/v{api-version:apiVersion}/[controller]")]
|
||||
public class UsersController : ControllerBase
|
||||
{
|
||||
private readonly IUsersServiceClient _usersService;
|
||||
|
||||
public UsersController(IUsersServiceClient usersService)
|
||||
{
|
||||
_usersService = usersService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IEnumerable<User>> GetUsers(CancellationToken cancellationToken)
|
||||
{
|
||||
return await _usersService.GetAllUsersAsync(cancellationToken);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Strata.Analytics.Biz.Workbooks.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Strata.Analytics.Api.Authorization;
|
||||
using Strata.Analytics.Models.Utils;
|
||||
using System;
|
||||
using Strata.Analytics.Models.Workbooks;
|
||||
|
||||
namespace Strata.Analytics.Api.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[ApiVersion("1.0")]
|
||||
[Route("api/v{api-version:apiVersion}/[controller]")]
|
||||
public class WorkbooksController : ControllerBase
|
||||
{
|
||||
private readonly IWorkbookService _workbookService;
|
||||
|
||||
public WorkbooksController(IWorkbookService workbookService)
|
||||
{
|
||||
_workbookService = workbookService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IEnumerable<Workbook>> GetWorkbooksAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
return await _workbookService.GetAllAsync(cancellationToken);
|
||||
}
|
||||
|
||||
[HttpGet("{workbookId}")]
|
||||
[Authorize(Policy = Policies.WorkbookViewer)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<Workbook>> GetById(int workbookId, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
var workbook = await _workbookService.GetByIdAsync(workbookId, cancellationToken);
|
||||
if (workbook == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Ok(workbook);
|
||||
}
|
||||
|
||||
// Delete should return 404 if id doesn't exist. Use Contains instead of loading entire object into memory to check for existence.
|
||||
[HttpDelete("{workbookId}")]
|
||||
[Authorize(Policy = Policies.WorkbookEditor)]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<IActionResult> Delete(int workbookId, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!await _workbookService.ContainsAsync(workbookId, cancellationToken))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
await _workbookService.DeleteAsync(workbookId, cancellationToken);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<Workbook>> Create(WorkbookCreateInfo workbookCreateInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
return await _workbookService.CreateAsync(workbookCreateInfo, cancellationToken);
|
||||
}
|
||||
|
||||
[HttpPut("{workbookId}")]
|
||||
[Authorize(Policy = Policies.WorkbookEditor)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<Workbook>> UpdateWorkbook([FromRoute] int workbookId, [FromBody] WorkbookSaveInfo workbookSaveInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!await _workbookService.ContainsAsync(workbookId, cancellationToken))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return await _workbookService.UpdateAsync(workbookId, workbookSaveInfo, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("{workbookId}/role-assignments")]
|
||||
[Authorize(Policy = "WorkbookViewer")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<IEnumerable<WorkbookRoleAssignment>>> GetRoleAssignments([FromRoute] int workbookId, CancellationToken cancellationToken)
|
||||
{
|
||||
return Ok(await _workbookService.GetRoleAssignmentsAsync(workbookId, cancellationToken));
|
||||
}
|
||||
|
||||
[HttpPost("{workbookId}/role-assignments/batch")]
|
||||
[Authorize(Policy = Policies.WorkbookEditor)]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesDefaultResponseType]
|
||||
public async Task<ActionResult<IEnumerable<WorkbookRoleAssignment>>> SaveRoleAssignments([FromRoute] int workbookId, [FromBody] Batch<WorkbookRoleAssignmentSaveInfo, Guid> roleAssignments, CancellationToken cancellationToken)
|
||||
{
|
||||
return Ok(await _workbookService.SaveRoleAssignmentsAsync(workbookId, roleAssignments, cancellationToken));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Strata.Analytics.Api.Models
|
||||
{
|
||||
public class EncounterInfo
|
||||
{
|
||||
public string EncounterRecordNumber { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Strata.Analytics.Models
|
||||
{
|
||||
public class PublicationEvent
|
||||
{
|
||||
public DateTime RequestDate { get; set; }
|
||||
public string RequestedBy { get; set; }
|
||||
public IEnumerable<int> StandardDataSourceIds { get; set; }
|
||||
public IEnumerable<int> StrataIds { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Strata.Analytics.Api.Models
|
||||
{
|
||||
public class PublishConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 0 means to publish to all clients
|
||||
/// </summary>
|
||||
public int StrataId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.CommandLineUtils;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Serilog;
|
||||
using Strata.Configuration.Client.DependencyInjection;
|
||||
using Strata.Logging.DependencyInjection;
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Strata.Analytics.Biz.DbContexts.Factories;
|
||||
|
||||
namespace Strata.Analytics.Api
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var commandLineApplication = new CommandLineApplication(false);
|
||||
var doMigrate = commandLineApplication.Option(
|
||||
"--ef-migrate",
|
||||
"Apply entity framework migrations and exit",
|
||||
CommandOptionType.NoValue);
|
||||
|
||||
commandLineApplication.HelpOption("-? | -h | --help");
|
||||
commandLineApplication.OnExecute(() =>
|
||||
{
|
||||
ExecuteApp(args, doMigrate);
|
||||
return 0;
|
||||
});
|
||||
commandLineApplication.Execute(args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "There was an unhandled exception that caused the program to crash");
|
||||
|
||||
//This is for serilog to ensure that all of the cached logs are flushed when the app crashes
|
||||
//This is useful so we can capture logs when the application crashes during startup:
|
||||
Log.CloseAndFlush();
|
||||
}
|
||||
}
|
||||
|
||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
||||
WebHost.CreateDefaultBuilder(args)
|
||||
.UseStrataConfiguration()
|
||||
.UseStrataLogging()
|
||||
.UseStartup<Startup>();
|
||||
|
||||
public static void ExecuteApp(string[] args, CommandOption doMigrate)
|
||||
{
|
||||
var webHost = CreateWebHostBuilder(args).Build();
|
||||
|
||||
if (doMigrate.HasValue())
|
||||
{
|
||||
Log.Debug("Applying Entity Framework migrations");
|
||||
|
||||
using var scope = webHost.Services.CreateScope();
|
||||
using var context = new AnalyticsDbContextFactory().CreateDbContext(new string[] { });
|
||||
context.Database.Migrate();
|
||||
Log.Debug("All done, closing app");
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
// no flags provided, so just run the webhost
|
||||
webHost.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Strata.Analytics.Api": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"Hangfire__RunLocally": "true",
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"AWS_Profile": "sdt-analytics-service-role",
|
||||
"AWS_REGION": "us-east-1",
|
||||
"AWS_S3_US_EAST_1_REGIONAL_ENDPOINT": "regional"
|
||||
},
|
||||
"applicationUrl": "https://localhost:8443"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
using Hangfire;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Prometheus;
|
||||
using Strata.Analytics.Api.Authorization;
|
||||
using Strata.Analytics.Biz.DataSources.Models;
|
||||
using Strata.Analytics.Biz.Extracts.Models;
|
||||
using Strata.Analytics.Biz.Utils;
|
||||
using Strata.Analytics.Models.Workbooks;
|
||||
using Strata.ApiLib.Core;
|
||||
using Strata.ApiLib.Core.Transformers;
|
||||
using Strata.CoreLib.Claims.Extensions;
|
||||
using Strata.Hangfire.AspNetCore.Filters.Dashboard;
|
||||
using Strata.Hangfire.TaskManagement.AspNetCore;
|
||||
using Strata.SwaggerExtensions;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Net.Mime;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using AuthorizationOptions = Strata.Analytics.Api.Authorization.AuthorizationOptions;
|
||||
|
||||
namespace Strata.Analytics.Api
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class Startup
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddStrataPlatformServices(_configuration);
|
||||
services.AddControllers(options =>
|
||||
{
|
||||
//Prevent browsers from caching API responses (this addresses an IE11 issue):
|
||||
options.Filters.Add(
|
||||
new ResponseCacheAttribute()
|
||||
{
|
||||
Location = ResponseCacheLocation.None,
|
||||
NoStore = false,
|
||||
Duration = -1
|
||||
});
|
||||
options.ReturnHttpNotAcceptable = true;
|
||||
options.Filters.Add(new ProducesAttribute(MediaTypeNames.Application.Json));
|
||||
options.Filters.Add(new ConsumesAttribute(MediaTypeNames.Application.Json));
|
||||
options.Conventions.Add(new RouteTokenTransformerConvention(new SlugifyParameterTransformer()));
|
||||
})
|
||||
.AddJsonOptions(options =>
|
||||
{
|
||||
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
|
||||
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
|
||||
|
||||
// Uncomment this line if you want enum to be serialized / deserialized as string value instead of int value
|
||||
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
|
||||
|
||||
// Date and Time Only serialization/deserialization support
|
||||
options.JsonSerializerOptions.Converters.Add(new DateOnlyJsonConverter());
|
||||
options.JsonSerializerOptions.Converters.Add(new TimeOnlyJsonConverter());
|
||||
});
|
||||
|
||||
// authorization
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy(Policies.DataSourceViewer,
|
||||
policy => policy.Requirements.Add(new DataSourceMinimumRoleRequirement(DataSourceRole.Viewer)));
|
||||
options.AddPolicy(Policies.DataSourceEditor,
|
||||
policy => policy.Requirements.Add(new DataSourceMinimumRoleRequirement(DataSourceRole.Editor)));
|
||||
options.AddPolicy(Policies.WorkbookViewer,
|
||||
policy => policy.Requirements.Add(new WorkbookMinimumRoleRequirement(WorkbookRole.Viewer)));
|
||||
options.AddPolicy(Policies.WorkbookEditor,
|
||||
policy => policy.Requirements.Add(new WorkbookMinimumRoleRequirement(WorkbookRole.Editor)));
|
||||
options.AddPolicy(Policies.ExtractViewer,
|
||||
policy => policy.Requirements.Add(new ExtractMinimumRoleRequirement(ExtractRole.Viewer)));
|
||||
options.AddPolicy(Policies.ExtractEditor,
|
||||
policy => policy.Requirements.Add(new ExtractMinimumRoleRequirement(ExtractRole.Editor)));
|
||||
options.AddPolicy(Policies.CustomSqlAccess,
|
||||
policy => policy.Requirements.Add(new ExtractCustomSqlStrataOnlyRoleRequirement()));
|
||||
options.AddPolicy(Policies.StrataOnly,
|
||||
policy => policy.RequireAssertion(context => context.User.IsSdtEmployee()));
|
||||
options.AddPolicy(Policies.IsAdmin,
|
||||
policy => policy.RequireAssertion(context => context.User.IsAdmin()));
|
||||
options.AddPolicy(Policies.AuthorizedDbOnly, policy => policy.Requirements.Add(new AuthorizedDbMinimumRoleRequirement()));
|
||||
});
|
||||
services
|
||||
.AddOptions<AuthorizationOptions>()
|
||||
.Configure<IConfiguration>((options, configuration) =>
|
||||
{
|
||||
configuration.Bind("authorization", options);
|
||||
})
|
||||
.ValidateDataAnnotations();
|
||||
|
||||
services.AddScoped<IAuthorizationHandler, AuthorizedDbMinimumRoleHandler>();
|
||||
services.AddScoped<IAuthorizationHandler, DataSourceMinimumRoleHandler>();
|
||||
services.AddScoped<IAuthorizationHandler, WorkbookMinimumRoleHandler>();
|
||||
services.AddScoped<IAuthorizationHandler, ExtractMinimumRoleHandler>();
|
||||
services.AddScoped<IAuthorizationHandler, CustomSQLStrataOnlyRoleHandler>();
|
||||
|
||||
// configure hangfire
|
||||
services.AddStrataHangfire();
|
||||
services.AddStrataHangfireTaskManagement();
|
||||
services.AddAnalyticsServices(_configuration);
|
||||
|
||||
services.AddRouting();
|
||||
services.AddSwagger(_configuration);
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider provider)
|
||||
{
|
||||
app.UseRouting();
|
||||
|
||||
app.UseStrataPlatformMiddleware();
|
||||
app.UseMetricServer();
|
||||
app.UseHttpMetrics();
|
||||
app.ConfigureSwagger(env, provider);
|
||||
app.UseStrataHangfireTaskManagement();
|
||||
|
||||
#if DEBUG
|
||||
app.UseHangfireDashboard("/hangfire", new DashboardOptions
|
||||
{
|
||||
Authorization = new[] { new AllowAnyoneDashboardAuthorizationFilter() }
|
||||
});
|
||||
#endif
|
||||
app.UseEndpoints(endpoints => endpoints.MapControllers().RequireAuthorization());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||
<IncludeOpenAPIAnalyzers>true</IncludeOpenAPIAnalyzers>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.22">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />
|
||||
<PackageReference Include="Strata.ApiLib.Core" Version="5.3.0" />
|
||||
<PackageReference Include="Strata.Configuration.Client" Version="8.26.0" />
|
||||
<PackageReference Include="Strata.Hangfire.AspNetCore" Version="4.0.0" />
|
||||
<PackageReference Include="Strata.Hangfire.TaskManagement.AspNetCore" Version="0.6.0" />
|
||||
<PackageReference Include="Strata.Identity.Client" Version="3.1.0" />
|
||||
<PackageReference Include="Strata.Logging" Version="3.6.1" />
|
||||
<PackageReference Include="Strata.SwaggerExtensions" Version="2.3.0" />
|
||||
<PackageReference Include="prometheus-net.AspNetCore" Version="8.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Strata.Analytics.Biz\Strata.Analytics.Biz.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
},
|
||||
"hangfire": {
|
||||
"redis": {
|
||||
"prefix": "analytics-jobs-service",
|
||||
"configOptions": "ddenredisc01.sdt.local:6379,ddenredisc02.sdt.local:6379,ddenredisc03.sdt.local:6379,ddenredisc04.sdt.local:6379,ddenredisc05.sdt.local:6379,ddenredisc06.sdt.local:6379"
|
||||
}
|
||||
},
|
||||
"endpoints": {
|
||||
"dataschema": "https://dataschema-api.dev.stratanetwork.net/",
|
||||
//"dataschema": "https://localhost:9443/",
|
||||
"systemsettings": "https://settings-api.dev.stratanetwork.net/"
|
||||
},
|
||||
"strataApi": {
|
||||
"clientSecret": "V_okTrkWYBs-PqMP.inbJShIvMGWcpdqCDq_ZjntVF.ydgRCoYaDVJYEWEIdsjJ_"
|
||||
},
|
||||
"Snowflake": {
|
||||
"Url": "https://stratadev.us-east-1.privatelink.snowflakecomputing.com",
|
||||
"Account": "stratadev"
|
||||
},
|
||||
"presignedUrlExpirationInMinutes": 5,
|
||||
"ServerSideEncryptionS3Key": "arn:aws:kms:us-east-1:809820578507:key/c8f62293-5726-4342-b614-9b23bf2043c9"
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"Version": "0.0.0",
|
||||
"configuration": {
|
||||
"basicAuthUsername": "BkQsRDdmdjkFNhKVuTI.TpujQJXuKhGDcV.F_hjADMRTaEUWMJnviaesoOrhhfnz",
|
||||
"basicAuthPassword": "GWPIIWECDqh.hjDNdpxVEfeFe-bYFwPSx-oBGFD_od_SUQVk-QqTIkZY_NXMeqeo"
|
||||
},
|
||||
"StrataConfigServerBaseUrl": "https://configuration.dev.stratanetwork.net",
|
||||
"authorization": {
|
||||
"authorizedDbGuid": "45e3b554-86a5-4b43-875d-3da7eed909d8"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user