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 _logger; public DataSourcesController(IDataSourceService dataSourceService, IDataQueryService dataQueryService, IExportService exportService, ILogger logger) { _dataSourceService = dataSourceService; _dataQueryService = dataQueryService; _exportService = exportService; _logger = logger; } [HttpGet] public async Task> GetDataSourcesAsync(CancellationToken cancellationToken) { return await _dataSourceService.GetAllAsync(cancellationToken); } [HttpGet("{dataSourceId}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesDefaultResponseType] public async Task> 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> 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> 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 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> 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> 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 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>> GetRoleAssignments([FromRoute] int dataSourceId, CancellationToken cancellationToken) { return Ok(await _dataSourceService.GetRoleAssignmentsAsync(new List { dataSourceId }, cancellationToken)); } [HttpPost("{dataSourceId}/role-assignments/batch")] [Authorize(Policy = Policies.DataSourceEditor)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesDefaultResponseType] public async Task>> SaveRoleAssignments([FromRoute] int dataSourceId, [FromBody] Batch 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>> GetColumnValues([FromRoute] int dataSourceId, [FromBody] ColumnQueryConfig columnQueryConfig, CancellationToken cancellationToken) { try { return Ok(await _dataQueryService.GetColumnValues(columnQueryConfig, cancellationToken)); } catch (UnauthorizedAccessException) { var dataColumnIds = new List { 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>> 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> 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 DownloadDataSheet([FromRoute] int dataSourceId, [FromBody] ExportDataSheetRequest request, CancellationToken cancellationToken) { var stream = await _exportService.DownloadDataSheet(request, cancellationToken); return File(stream, "application/octet-stream"); } } }