async public Task SaveDefinition_GivenNoYamlWasProvidedButFileNameWas_ReturnsBadRequest() { //Arrange IHeaderDictionary headerDictionary = new HeaderDictionary(); headerDictionary .Add("yaml-file", new StringValues(yamlFile)); HttpRequest request = Substitute.For <HttpRequest>(); request .Headers .Returns(headerDictionary); ILogger logger = CreateLogger(); DefinitionsService service = CreateDefinitionsService(logger); //Act IActionResult result = await service.SaveDefinition(request); //Assert result .Should() .BeOfType <BadRequestObjectResult>(); logger .Received(1) .Error(Arg.Is($"Null or empty yaml provided for file: {yamlFile}")); }
async public Task GetDatasetDefinitions_GivenDefinitionsRequestedButContainsResults_ReturnsArray() { //Arrange HttpRequest request = Substitute.For <HttpRequest>(); IEnumerable <DatasetDefinition> definitions = new[] { new DatasetDefinition(), new DatasetDefinition() }; IDatasetRepository repository = CreateDataSetsRepository(); repository .GetDatasetDefinitions() .Returns(definitions); DefinitionsService service = CreateDefinitionsService(datasetsRepository: repository); //Act IActionResult result = await service.GetDatasetDefinitions(request); //Assert result .Should() .BeOfType <OkObjectResult>(); OkObjectResult objResult = (OkObjectResult)result; IEnumerable <DatasetDefinition> objValue = (IEnumerable <DatasetDefinition>)objResult.Value; objValue .Count() .Should() .Be(2); }
public async Task GetDatasetSchemaSasUrl_GivenNullOrEmptyDefinitionName_ReturnsBadRequest() { //Arrange DatasetSchemaSasUrlRequestModel model = new DatasetSchemaSasUrlRequestModel(); string json = JsonConvert.SerializeObject(model); byte[] byteArray = Encoding.UTF8.GetBytes(json); MemoryStream stream = new MemoryStream(byteArray); HttpRequest request = Substitute.For <HttpRequest>(); request .Body .Returns(stream); ILogger logger = CreateLogger(); DefinitionsService definitionsService = CreateDefinitionsService(logger); //Act IActionResult result = await definitionsService.GetDatasetSchemaSasUrl(request); //Assert result .Should() .BeOfType <BadRequestObjectResult>() .Which .Value .Should() .Be("No dataset schema name was provided"); logger .Received(1) .Warning(Arg.Is("No dataset schema name was provided")); }
public async Task GetDatasetSchemaSasUrl_GivenNullRequestModel_ReturnsBadRequest() { //Arrange HttpRequest request = Substitute.For <HttpRequest>(); ILogger logger = CreateLogger(); DefinitionsService definitionsService = CreateDefinitionsService(logger); //Act IActionResult result = await definitionsService.GetDatasetSchemaSasUrl(request); //Assert result .Should() .BeOfType <BadRequestObjectResult>() .Which .Value .Should() .Be("No dataset schema request model was provided"); logger .Received(1) .Warning(Arg.Is("No dataset schema request model was provided")); }
async public Task SaveDefinition_GivenValidYamlButSavingToDatabaseThrowsException_ReturnsInternalServerError() { //Arrange string yaml = CreateRawDefinition(); byte[] byteArray = Encoding.UTF8.GetBytes(yaml); MemoryStream stream = new MemoryStream(byteArray); IHeaderDictionary headerDictionary = new HeaderDictionary(); headerDictionary .Add("yaml-file", new StringValues(yamlFile)); HttpRequest request = Substitute.For <HttpRequest>(); request .Headers .Returns(headerDictionary); request .Body .Returns(stream); ILogger logger = CreateLogger(); IDatasetRepository dataSetsRepository = CreateDataSetsRepository(); dataSetsRepository .When(x => x.SaveDefinition(Arg.Any <DatasetDefinition>())) .Do(x => { throw new Exception(); }); DatasetDefinitionChanges datasetDefinitionChanges = new DatasetDefinitionChanges(); IDefinitionChangesDetectionService definitionChangesDetectionService = CreateChangesDetectionService(); definitionChangesDetectionService .DetectChanges(Arg.Any <DatasetDefinition>(), Arg.Any <DatasetDefinition>()) .Returns(datasetDefinitionChanges); DefinitionsService service = CreateDefinitionsService(logger, dataSetsRepository, definitionChangesDetectionService: definitionChangesDetectionService); //Act IActionResult result = await service.SaveDefinition(request); //Assert result .Should() .BeOfType <InternalServerErrorResult>() .Which .Value .Should() .Be("Exception occurred writing to yaml file: 12345.yaml to cosmos db"); logger .Received(1) .Error(Arg.Any <Exception>(), Arg.Is($"Exception occurred writing to yaml file: {yamlFile} to cosmos db")); }
public async Task GetDatasetSchemaSasUrl_GivenModelAndDatasetNameContainsSlashes_ReplacesSlashesWithUnderscoreAndReturnsUrl() { // Arrange DatasetSchemaSasUrlRequestModel model = new DatasetSchemaSasUrlRequestModel { DatasetDefinitionId = "12345" }; string json = JsonConvert.SerializeObject(model); byte[] byteArray = Encoding.UTF8.GetBytes(json); MemoryStream stream = new MemoryStream(byteArray); HttpRequest request = Substitute.For <HttpRequest>(); request .Body .Returns(stream); IBlobClient blobClient = CreateBlobClient(); DatasetDefinition datasetDefinition = new DatasetDefinition() { Id = "12345", Name = "TEST/SLASH Definition", }; IDatasetRepository datasetRepository = CreateDataSetsRepository(); datasetRepository .GetDatasetDefinition(Arg.Is(model.DatasetDefinitionId)) .Returns(datasetDefinition); DefinitionsService definitionsService = CreateDefinitionsService( datasetsRepository: datasetRepository, blobClient: blobClient); // Act IActionResult result = await definitionsService.GetDatasetSchemaSasUrl(request); // Assert result .Should() .BeOfType <OkObjectResult>(); blobClient .Received(1) .GetBlobSasUrl(Arg.Is("schemas/TEST_SLASH Definition.xlsx"), Arg.Any <DateTimeOffset>(), Arg.Any <SharedAccessBlobPermissions>()); }
async public Task SaveDefinition_GivenNoYamlWasProvidedWithNoFileName_ReturnsBadRequest() { //Arrange HttpRequest request = Substitute.For <HttpRequest>(); ILogger logger = CreateLogger(); DefinitionsService service = CreateDefinitionsService(logger); //Act IActionResult result = await service.SaveDefinition(request); //Assert result .Should() .BeOfType <BadRequestObjectResult>(); logger .Received(1) .Error(Arg.Is($"Null or empty yaml provided for file: File name not provided")); }
async public Task SaveDefinition_GivenNoYamlWasProvidedButIsInvalid_ReturnsBadRequest() { //Arrange string yaml = "invalid yaml"; byte[] byteArray = Encoding.UTF8.GetBytes(yaml); MemoryStream stream = new MemoryStream(byteArray); IHeaderDictionary headerDictionary = new HeaderDictionary(); headerDictionary .Add("yaml-file", new StringValues(yamlFile)); HttpRequest request = Substitute.For <HttpRequest>(); request .Headers .Returns(headerDictionary); request .Body .Returns(stream); ILogger logger = CreateLogger(); DefinitionsService service = CreateDefinitionsService(logger); //Act IActionResult result = await service.SaveDefinition(request); //Assert result .Should() .BeOfType <BadRequestObjectResult>(); logger .Received(1) .Error(Arg.Any <Exception>(), Arg.Is($"Invalid yaml was provided for file: {yamlFile}")); }
public async Task SaveDefinition_GivenValidYamlAndSearchDoesContainsExistingItemWithNoUpdates_ThenDatasetDefinitionSavedInCosmosAndSearchNotUpdatedAndReturnsOK() { //Arrange string yaml = CreateRawDefinition(); string definitionId = "9183"; byte[] byteArray = Encoding.UTF8.GetBytes(yaml); MemoryStream stream = new MemoryStream(byteArray); IHeaderDictionary headerDictionary = new HeaderDictionary(); headerDictionary .Add("yaml-file", new StringValues(yamlFile)); HttpRequest request = Substitute.For <HttpRequest>(); request .Headers .Returns(headerDictionary); request .Body .Returns(stream); ILogger logger = CreateLogger(); HttpStatusCode statusCode = HttpStatusCode.Created; IDatasetRepository datasetsRepository = CreateDataSetsRepository(); datasetsRepository .SaveDefinition(Arg.Any <DatasetDefinition>()) .Returns(statusCode); DatasetDefinitionIndex existingIndex = new DatasetDefinitionIndex() { Description = "14/15 description", Id = "9183", LastUpdatedDate = new DateTimeOffset(2018, 6, 19, 14, 10, 2, TimeSpan.Zero), ModelHash = "DFBD0E1ACD29CEBCF5AD45674688D3780D916294C4DF878074AFD01B67BF129C", Name = "14/15", ProviderIdentifier = "None", }; ISearchRepository <DatasetDefinitionIndex> searchRepository = CreateDatasetDefinitionSearchRepository(); searchRepository .SearchById(Arg.Is(definitionId)) .Returns(existingIndex); byte[] excelAsBytes = new byte[100]; IExcelWriter <DatasetDefinition> excelWriter = CreateExcelWriter(); excelWriter .Write(Arg.Any <DatasetDefinition>()) .Returns(excelAsBytes); ICloudBlob blob = Substitute.For <ICloudBlob>(); IBlobClient blobClient = CreateBlobClient(); blobClient .GetBlockBlobReference(Arg.Any <string>()) .Returns(blob); DatasetDefinitionChanges datasetDefinitionChanges = new DatasetDefinitionChanges(); IDefinitionChangesDetectionService definitionChangesDetectionService = CreateChangesDetectionService(); definitionChangesDetectionService .DetectChanges(Arg.Any <DatasetDefinition>(), Arg.Any <DatasetDefinition>()) .Returns(datasetDefinitionChanges); DefinitionsService service = CreateDefinitionsService(logger, datasetsRepository, searchRepository, excelWriter: excelWriter, blobClient: blobClient, definitionChangesDetectionService: definitionChangesDetectionService); //Act IActionResult result = await service.SaveDefinition(request); //Assert result .Should() .BeOfType <OkResult>(); await searchRepository .Received(1) .SearchById(Arg.Is(definitionId)); await searchRepository .Received(0) .Index(Arg.Any <IEnumerable <DatasetDefinitionIndex> >()); await datasetsRepository .Received(1) .SaveDefinition(Arg.Is <DatasetDefinition>( i => i.Description == "14/15 description" && i.Id == "9183" && i.Name == "14/15" )); logger .Received(1) .Information(Arg.Is($"Successfully saved file: {yamlFile} to cosmos db")); }
async public Task SaveDefinition_GivenValidYamlAndSearchDoesNotContainExistingItem_ThenSaveWasSuccesfulAndReturnsOK() { //Arrange string yaml = CreateRawDefinition(); string definitionId = "9183"; byte[] byteArray = Encoding.UTF8.GetBytes(yaml); MemoryStream stream = new MemoryStream(byteArray); IHeaderDictionary headerDictionary = new HeaderDictionary(); headerDictionary .Add("yaml-file", new StringValues(yamlFile)); HttpRequest request = Substitute.For <HttpRequest>(); request .Headers .Returns(headerDictionary); request .Body .Returns(stream); ILogger logger = CreateLogger(); HttpStatusCode statusCode = HttpStatusCode.Created; IDatasetRepository datasetsRepository = CreateDataSetsRepository(); datasetsRepository .SaveDefinition(Arg.Any <DatasetDefinition>()) .Returns(statusCode); ISearchRepository <DatasetDefinitionIndex> searchRepository = CreateDatasetDefinitionSearchRepository(); searchRepository .SearchById(Arg.Is(definitionId)) .Returns((DatasetDefinitionIndex)null); byte[] excelAsBytes = new byte[100]; IExcelWriter <DatasetDefinition> excelWriter = CreateExcelWriter(); excelWriter .Write(Arg.Any <DatasetDefinition>()) .Returns(excelAsBytes); ICloudBlob blob = Substitute.For <ICloudBlob>(); IBlobClient blobClient = CreateBlobClient(); blobClient .GetBlockBlobReference(Arg.Any <string>()) .Returns(blob); DatasetDefinitionChanges datasetDefinitionChanges = new DatasetDefinitionChanges(); IDefinitionChangesDetectionService definitionChangesDetectionService = CreateChangesDetectionService(); definitionChangesDetectionService .DetectChanges(Arg.Any <DatasetDefinition>(), Arg.Any <DatasetDefinition>()) .Returns(datasetDefinitionChanges); DefinitionsService service = CreateDefinitionsService(logger, datasetsRepository, searchRepository, excelWriter: excelWriter, blobClient: blobClient, definitionChangesDetectionService: definitionChangesDetectionService); //Act IActionResult result = await service.SaveDefinition(request); //Assert result .Should() .BeOfType <OkResult>(); await searchRepository .Received(1) .SearchById(Arg.Is(definitionId)); await searchRepository .Received(1) .Index(Arg.Is <IEnumerable <DatasetDefinitionIndex> >( i => i.First().Description == "14/15 description" && i.First().Id == "9183" && !string.IsNullOrWhiteSpace(i.First().ModelHash) && i.First().Name == "14/15" && i.First().ProviderIdentifier == "None" )); await datasetsRepository .Received(1) .SaveDefinition(Arg.Is <DatasetDefinition>( i => i.Description == "14/15 description" && i.Id == "9183" && i.Name == "14/15" )); logger .Received(1) .Information(Arg.Is($"Successfully saved file: {yamlFile} to cosmos db")); }
async public Task SaveDefinition_GivenValidYamlButFailsToUploadToBlobStorage_ReturnsInvalidServerError() { //Arrange string yaml = CreateRawDefinition(); string definitionId = "9183"; byte[] byteArray = Encoding.UTF8.GetBytes(yaml); MemoryStream stream = new MemoryStream(byteArray); IHeaderDictionary headerDictionary = new HeaderDictionary(); headerDictionary .Add("yaml-file", new StringValues(yamlFile)); HttpRequest request = Substitute.For <HttpRequest>(); request .Headers .Returns(headerDictionary); request .Body .Returns(stream); ILogger logger = CreateLogger(); HttpStatusCode statusCode = HttpStatusCode.Created; IDatasetRepository datasetsRepository = CreateDataSetsRepository(); datasetsRepository .SaveDefinition(Arg.Any <DatasetDefinition>()) .Returns(statusCode); ISearchRepository <DatasetDefinitionIndex> searchRepository = CreateDatasetDefinitionSearchRepository(); searchRepository .SearchById(Arg.Is(definitionId)) .Returns((DatasetDefinitionIndex)null); byte[] excelAsBytes = new byte[100]; IExcelWriter <DatasetDefinition> excelWriter = CreateExcelWriter(); excelWriter .Write(Arg.Any <DatasetDefinition>()) .Returns(excelAsBytes); ICloudBlob blob = Substitute.For <ICloudBlob>(); blob .When(x => x.UploadFromStreamAsync(Arg.Any <Stream>())) .Do(x => { throw new Exception($"Failed to upload 14/15 blob storage"); }); IBlobClient blobClient = CreateBlobClient(); blobClient .GetBlockBlobReference(Arg.Is("schemas/14_15.xlsx")) .Returns(blob); DatasetDefinitionChanges datasetDefinitionChanges = new DatasetDefinitionChanges(); IDefinitionChangesDetectionService definitionChangesDetectionService = CreateChangesDetectionService(); definitionChangesDetectionService .DetectChanges(Arg.Any <DatasetDefinition>(), Arg.Any <DatasetDefinition>()) .Returns(datasetDefinitionChanges); DefinitionsService service = CreateDefinitionsService(logger, datasetsRepository, searchRepository, excelWriter: excelWriter, blobClient: blobClient, definitionChangesDetectionService: definitionChangesDetectionService); //Act IActionResult result = await service.SaveDefinition(request); //Assert result .Should() .BeOfType <InternalServerErrorResult>() .Which .Value .Should() .Be($"Failed to upload 14/15 blob storage"); }
async public Task SaveDefinition_GivenValidYamlButFailsToGenerateExcelFile_ReturnsInvalidServerError() { //Arrange string yaml = CreateRawDefinition(); string definitionId = "9183"; byte[] byteArray = Encoding.UTF8.GetBytes(yaml); MemoryStream stream = new MemoryStream(byteArray); IHeaderDictionary headerDictionary = new HeaderDictionary(); headerDictionary .Add("yaml-file", new StringValues(yamlFile)); HttpRequest request = Substitute.For <HttpRequest>(); request .Headers .Returns(headerDictionary); request .Body .Returns(stream); ILogger logger = CreateLogger(); HttpStatusCode statusCode = HttpStatusCode.Created; IDatasetRepository datasetsRepository = CreateDataSetsRepository(); datasetsRepository .SaveDefinition(Arg.Any <DatasetDefinition>()) .Returns(statusCode); ISearchRepository <DatasetDefinitionIndex> searchRepository = CreateDatasetDefinitionSearchRepository(); searchRepository .SearchById(Arg.Is(definitionId)) .Returns((DatasetDefinitionIndex)null); byte[] excelAsBytes = new byte[0]; IExcelWriter <DatasetDefinition> excelWriter = CreateExcelWriter(); excelWriter .Write(Arg.Any <DatasetDefinition>()) .Returns(excelAsBytes); DatasetDefinitionChanges datasetDefinitionChanges = new DatasetDefinitionChanges(); IDefinitionChangesDetectionService definitionChangesDetectionService = CreateChangesDetectionService(); definitionChangesDetectionService .DetectChanges(Arg.Any <DatasetDefinition>(), Arg.Any <DatasetDefinition>()) .Returns(datasetDefinitionChanges); DefinitionsService service = CreateDefinitionsService(logger, datasetsRepository, searchRepository, excelWriter: excelWriter, definitionChangesDetectionService: definitionChangesDetectionService); //Act IActionResult result = await service.SaveDefinition(request); //Assert result .Should() .BeOfType <InternalServerErrorResult>() .Which .Value .Should() .Be($"Failed to generate excel file for 14/15"); logger .Received(1) .Error(Arg.Is($"Failed to generate excel file for 14/15")); }
async public Task SaveDefinition_GivenValidYamlButFailedToSaveToDatabase_ReturnsStatusCode() { //Arrange string yaml = CreateRawDefinition(); byte[] byteArray = Encoding.UTF8.GetBytes(yaml); MemoryStream stream = new MemoryStream(byteArray); IHeaderDictionary headerDictionary = new HeaderDictionary(); headerDictionary .Add("yaml-file", new StringValues(yamlFile)); HttpRequest request = Substitute.For <HttpRequest>(); request .Headers .Returns(headerDictionary); request .Body .Returns(stream); ILogger logger = CreateLogger(); HttpStatusCode failedCode = HttpStatusCode.BadGateway; IDatasetRepository dataSetsRepository = CreateDataSetsRepository(); dataSetsRepository .SaveDefinition(Arg.Any <DatasetDefinition>()) .Returns(failedCode); DatasetDefinitionChanges datasetDefinitionChanges = new DatasetDefinitionChanges(); IDefinitionChangesDetectionService definitionChangesDetectionService = CreateChangesDetectionService(); definitionChangesDetectionService .DetectChanges(Arg.Any <DatasetDefinition>(), Arg.Any <DatasetDefinition>()) .Returns(datasetDefinitionChanges); DefinitionsService service = CreateDefinitionsService(logger, dataSetsRepository, definitionChangesDetectionService: definitionChangesDetectionService); //Act IActionResult result = await service.SaveDefinition(request); //Assert result .Should() .BeOfType <StatusCodeResult>(); StatusCodeResult statusCodeResult = (StatusCodeResult)result; statusCodeResult .StatusCode .Should() .Be(502); logger .Received(1) .Error(Arg.Is($"Failed to save yaml file: {yamlFile} to cosmos db with status 502")); }
async public Task SaveDefinition_GivenUpdatedYamlWithChangedIdentifierTypeFieldButAlreadyUsedInRelationship_ReturnsBadRequest() { //Arrange IEnumerable <string> specificationIds = new[] { "spec-1" }; string definitionId = "9183"; string yaml = CreateRawDefinition(); byte[] byteArray = Encoding.UTF8.GetBytes(yaml); MemoryStream stream = new MemoryStream(byteArray); IHeaderDictionary headerDictionary = new HeaderDictionary(); headerDictionary .Add("yaml-file", new StringValues(yamlFile)); HttpRequest request = Substitute.For <HttpRequest>(); request .Headers .Returns(headerDictionary); request .Body .Returns(stream); ILogger logger = CreateLogger(); IDatasetRepository datasetRepository = CreateDataSetsRepository(); datasetRepository .GetDistinctRelationshipSpecificationIdsForDatasetDefinitionId(Arg.Is(definitionId)) .Returns(specificationIds); datasetRepository .GetDatasetDefinition(Arg.Is(definitionId)) .Returns(new DatasetDefinition()); DatasetDefinitionChanges datasetDefinitionChanges = new DatasetDefinitionChanges { Id = definitionId, }; FieldDefinitionChanges fieldDefinitionChanges = new FieldDefinitionChanges(); fieldDefinitionChanges.ChangeTypes.Add(FieldDefinitionChangeType.IdentifierType); TableDefinitionChanges tableDefinitionChanges = new TableDefinitionChanges(); tableDefinitionChanges.FieldChanges.Add(fieldDefinitionChanges); datasetDefinitionChanges.TableDefinitionChanges.Add(tableDefinitionChanges); IDefinitionChangesDetectionService definitionChangesDetectionService = CreateChangesDetectionService(); definitionChangesDetectionService .DetectChanges(Arg.Any <DatasetDefinition>(), Arg.Any <DatasetDefinition>()) .Returns(datasetDefinitionChanges); DefinitionsService service = CreateDefinitionsService(logger, definitionChangesDetectionService: definitionChangesDetectionService, datasetsRepository: datasetRepository); //Act IActionResult result = await service.SaveDefinition(request); //Assert result .Should() .BeOfType <BadRequestObjectResult>() .Which .Value .Should() .Be("Unable to change provider identifier as there are currently relationships setup against this schema"); }
public async Task GetDatasetSchemaSasUrl_GivenModelAndDatasetNameDoesNotContainSlashes_GetSasUrl() { //Arrange const string sasUrl = "https://wherever.naf?jhjhjhjhjhhjhjhjhjjhj"; DatasetSchemaSasUrlRequestModel model = new DatasetSchemaSasUrlRequestModel { DatasetDefinitionId = "12345" }; string json = JsonConvert.SerializeObject(model); byte[] byteArray = Encoding.UTF8.GetBytes(json); MemoryStream stream = new MemoryStream(byteArray); HttpRequest request = Substitute.For <HttpRequest>(); request .Body .Returns(stream); IBlobClient blobClient = CreateBlobClient(); blobClient .GetBlobSasUrl(Arg.Any <string>(), Arg.Any <DateTimeOffset>(), Arg.Any <SharedAccessBlobPermissions>()) .Returns(sasUrl); DatasetDefinition datasetDefinition = new DatasetDefinition() { Id = "12345", Name = "14 15", }; IDatasetRepository datasetRepository = CreateDataSetsRepository(); datasetRepository .GetDatasetDefinition(Arg.Is(model.DatasetDefinitionId)) .Returns(datasetDefinition); DefinitionsService definitionsService = CreateDefinitionsService( datasetsRepository: datasetRepository, blobClient: blobClient); //Act IActionResult result = await definitionsService.GetDatasetSchemaSasUrl(request); //Assert result .Should() .BeOfType <OkObjectResult>(); OkObjectResult okObjectResult = result as OkObjectResult; DatasetSchemaSasUrlResponseModel responseModel = okObjectResult.Value as DatasetSchemaSasUrlResponseModel; responseModel .SchemaUrl .Should() .Be(sasUrl); blobClient .Received(1) .GetBlobSasUrl(Arg.Is("schemas/14 15.xlsx"), Arg.Any <DateTimeOffset>(), Arg.Any <SharedAccessBlobPermissions>()); }
public async Task SaveDefinition_GivenValidYamlAndDoesContainsExistingItemWithNoModelUpdates_ThenDoesNotAddMessageToTopicAndReturnsOK() { //Arrange string yaml = CreateRawDefinition(); string definitionId = "9183"; byte[] byteArray = Encoding.UTF8.GetBytes(yaml); MemoryStream stream = new MemoryStream(byteArray); IHeaderDictionary headerDictionary = new HeaderDictionary(); headerDictionary .Add("yaml-file", new StringValues(yamlFile)); HttpRequest request = Substitute.For <HttpRequest>(); request .Headers .Returns(headerDictionary); request .Body .Returns(stream); ILogger logger = CreateLogger(); HttpStatusCode statusCode = HttpStatusCode.Created; DatasetDefinition existingDatasetDefinition = new DatasetDefinition { Id = definitionId }; IDatasetRepository datasetsRepository = CreateDataSetsRepository(); datasetsRepository .SaveDefinition(Arg.Any <DatasetDefinition>()) .Returns(statusCode); datasetsRepository .GetDatasetDefinition(Arg.Is(definitionId)) .Returns(existingDatasetDefinition); byte[] excelAsBytes = new byte[100]; IExcelWriter <DatasetDefinition> excelWriter = CreateExcelWriter(); excelWriter .Write(Arg.Any <DatasetDefinition>()) .Returns(excelAsBytes); ICloudBlob blob = Substitute.For <ICloudBlob>(); IBlobClient blobClient = CreateBlobClient(); blobClient .GetBlockBlobReference(Arg.Any <string>()) .Returns(blob); DatasetDefinitionChanges datasetDefinitionChanges = new DatasetDefinitionChanges(); IDefinitionChangesDetectionService definitionChangesDetectionService = CreateChangesDetectionService(); definitionChangesDetectionService .DetectChanges(Arg.Any <DatasetDefinition>(), Arg.Is(existingDatasetDefinition)) .Returns(datasetDefinitionChanges); IMessengerService messengerService = CreateMessengerService(); DefinitionsService service = CreateDefinitionsService( logger, datasetsRepository, excelWriter: excelWriter, blobClient: blobClient, definitionChangesDetectionService: definitionChangesDetectionService, messengerService: messengerService); //Act IActionResult result = await service.SaveDefinition(request); //Assert result .Should() .BeOfType <OkResult>(); await messengerService .DidNotReceive() .SendToTopic( Arg.Is(ServiceBusConstants.TopicNames.DataDefinitionChanges), Arg.Any <DatasetDefinitionChanges>(), Arg.Any <IDictionary <string, string> >()); }
public DefinitionsController(DefinitionsService service) { _service = service; }
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) { // (1) Make sure the API caller have provided a tenantId, and extract it try { var cancellation = context.HttpContext.RequestAborted; int tenantId = _tenantIdAccessor.GetTenantId(); // Init the database connection... // The client sometimes makes ambient API calls, not in response to user interaction // Such calls should not update LastAccess of that user bool silent = context.HttpContext.Request.Query["silent"].FirstOrDefault()?.ToString()?.ToLower() == "true"; await _appRepo.InitConnectionAsync(tenantId, setLastActive : !silent, cancellation); // (2) Make sure the user is a member of this tenant UserInfo userInfo = await _appRepo.GetUserInfoAsync(cancellation); if (userInfo.UserId == null) { // If there is no user cut the pipeline short and return a Forbidden 403 context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden); // This indicates to the client to discard all cached information about this // company since the user is no longer a member of it context.HttpContext.Response.Headers.Add("x-settings-version", Constants.Unauthorized); context.HttpContext.Response.Headers.Add("x-definitions-version", Constants.Unauthorized); context.HttpContext.Response.Headers.Add("x-permissions-version", Constants.Unauthorized); context.HttpContext.Response.Headers.Add("x-user-settings-version", Constants.Unauthorized); return; } var userId = userInfo.UserId.Value; var externalId = _externalUserAccessor.GetUserId(); var externalEmail = _externalUserAccessor.GetUserEmail(); // (3) If the user exists but new, set the External Id if (userInfo.ExternalId == null) { // Update external Id in this tenant database await _appRepo.Users__SetExternalIdByUserId(userId, externalId); // Update external Id in the central Admin database too (To avoid an awkward situation // where a user exists on the tenant but not on the Admin db, if they change their email in between) var adminRepo = _serviceProvider.GetRequiredService <AdminRepository>(); await adminRepo.DirectoryUsers__SetExternalIdByEmail(externalEmail, externalId); } else if (userInfo.ExternalId != externalId) { // Note: there is the edge case of identity providers who allow email recycling. I.e. we can get the same email twice with // two different external Ids. This issue is so unlikely to naturally occur and cause problems here that we are not going // to handle it for now. It can however happen artificually if the application is re-configured to a new identity provider, // or if someone messed with the identity database directly, but again out of scope for now. context.Result = new BadRequestObjectResult("The sign-in email already exists but with a different external Id"); return; } // (4) If the user's email address has changed at the identity server, update it locally else if (userInfo.Email != externalEmail) { await _appRepo.Users__SetEmailByUserId(userId, externalEmail); } // (5) Set the tenant info in the context, to make it accessible for model metadata providers var tenantInfo = await _appRepo.GetTenantInfoAsync(cancellation); _tenantInfoAccessor.SetInfo(tenantId, tenantInfo); // (6) Ensure the freshness of the definitions and settings caches { var databaseVersion = tenantInfo.DefinitionsVersion; var serverVersion = _definitionsCache.GetDefinitionsIfCached(tenantId)?.Version; if (serverVersion == null || serverVersion != databaseVersion) { // Update the cache var definitions = await DefinitionsService.LoadDefinitionsForClient(_appRepo, cancellation); if (!cancellation.IsCancellationRequested) { _definitionsCache.SetDefinitions(tenantId, definitions); } } } { var databaseVersion = tenantInfo.SettingsVersion; var serverVersion = _settingsCache.GetSettingsIfCached(tenantId)?.Version; if (serverVersion == null || serverVersion != databaseVersion) { // Update the cache var settings = await GeneralSettingsService.LoadSettingsForClient(_appRepo, cancellation); if (!cancellation.IsCancellationRequested) { _settingsCache.SetSettings(tenantId, settings); } } } // (7) If any version headers are supplied: examine their freshness { // Permissions var clientVersion = context.HttpContext.Request.Headers["X-Permissions-Version"].FirstOrDefault(); if (!string.IsNullOrWhiteSpace(clientVersion)) { var databaseVersion = userInfo.PermissionsVersion; context.HttpContext.Response.Headers.Add("x-permissions-version", clientVersion == databaseVersion ? Constants.Fresh : Constants.Stale); } } { // User Settings var clientVersion = context.HttpContext.Request.Headers["X-User-Settings-Version"].FirstOrDefault(); if (!string.IsNullOrWhiteSpace(clientVersion)) { var databaseVersion = userInfo.UserSettingsVersion; context.HttpContext.Response.Headers.Add("x-user-settings-version", clientVersion == databaseVersion ? Constants.Fresh : Constants.Stale); } } { // Definitions var clientVersion = context.HttpContext.Request.Headers["X-Definitions-Version"].FirstOrDefault(); if (!string.IsNullOrWhiteSpace(clientVersion)) { var databaseVersion = tenantInfo.DefinitionsVersion; context.HttpContext.Response.Headers.Add("x-definitions-version", clientVersion == databaseVersion ? Constants.Fresh : Constants.Stale); } } { // Settings var clientVersion = context.HttpContext.Request.Headers["X-Settings-Version"].FirstOrDefault(); if (!string.IsNullOrWhiteSpace(clientVersion)) { var databaseVersion = tenantInfo.SettingsVersion; context.HttpContext.Response.Headers.Add("x-settings-version", clientVersion == databaseVersion ? Constants.Fresh : Constants.Stale); } } // Call the Action itself await next(); } catch (TaskCanceledException) { context.Result = new OkResult(); return; } catch (MultitenancyException ex) { // If the tenant Id is not provided cut the pipeline short and return a Bad Request 400 context.Result = new BadRequestObjectResult(ex.Message); return; } catch (BadRequestException ex) { // If the tenant Id is not provided cut the pipeline short and return a Bad Request 400 context.Result = new BadRequestObjectResult(ex.Message); return; } catch (Exception ex) { // TODO: Return to logging and 500 status code context.Result = new BadRequestObjectResult(ex.GetType().Name + ": " + ex.Message); //_logger.LogError(ex.Message); //context.Result = new StatusCodeResult(StatusCodes.Status500InternalServerError); return; } }