public override async Task <bool> DeleteAsync(string id) { if (id == null) { throw new ArgumentNullException("id"); } CompositeTableKey key = this.GetStorageKey(id); TableOperation operation = null; TData data = await this.GetCurrentItem(key, includeDeleted : !this.IncludeDeleted); if (this.EnableSoftDelete) { data.Deleted = true; operation = TableOperation.Merge(data); } else { operation = TableOperation.Delete(data); } bool deleted = false; if (operation != null) { await this.ExecuteOperationAsync(operation, OperationType.Delete); deleted = true; } return(deleted); }
public async Task LookupAsync_DoesNotCreateStorageTable() { // Behavior is same for Delete, Undelete, Update and Lookup which each depend on GetCurrentItem var isolatedManager = new StorageDomainManagerMock(this, "lookupTableNotFound"); try { await isolatedManager.Table.DeleteIfExistsAsync(); Assert.False(isolatedManager.Table.Exists()); string id = new CompositeTableKey("unknown", "item").ToString(); HttpResponseException ex = await AssertEx.ThrowsAsync <HttpResponseException>(async() => await isolatedManager.LookupAsync(id)); Assert.Equal(HttpStatusCode.NotFound, ex.Response.StatusCode); // Table.ExecuteAsync for retrieve returns TableResult with 404 status code when table is not found // instead of throwing exception. As a result, the lookup fails fast and we do not create the table. Assert.False(isolatedManager.Table.Exists()); } finally { isolatedManager.Dispose(); } }
public async Task LookupAsync_ReturnsSoftDeletedRecord_IfIncludeDeletedIsTrue() { // Arrange Person person = TestData.Persons.First(); person.PartitionKey = person.LastName; person.RowKey = person.FirstName; person.CreatedAt = null; person.UpdatedAt = null; await this.manager.InsertAsync(person); this.manager.EnableSoftDelete = true; bool result = await this.manager.DeleteAsync(person.Id); // Act this.manager.IncludeDeleted = true; CompositeTableKey id = new CompositeTableKey(person.LastName, person.FirstName); Person lookedup = (await this.manager.LookupAsync(id.ToString())).Queryable.First(); // Assert Assert.Equal(person.Id, lookedup.Id); Assert.Equal(true, lookedup.Deleted); Assert.Equal(person.FirstName, lookedup.FirstName); Assert.Equal(person.LastName, lookedup.LastName); }
public void Parse_SucceedsOnValidKeys(string key, string[] expectedSubkeys) { // Act CompositeTableKey tableKey = CompositeTableKey.Parse(key); // Assert Assert.Equal(expectedSubkeys, tableKey.Segments); }
public async Task ConvertStorageException_BadRequest_IfInvalidErrorType() { CompositeTableKey key = new CompositeTableKey("KeyPart1", "KeyPart2"); StorageException storageException = new StorageException("ConvertStorageException invalid error type test", null); HttpResponseException ex = await this.manager.ConvertStorageException(storageException, key); Assert.Equal(HttpStatusCode.BadRequest, ex.Response.StatusCode); }
public async Task UndeleteAsync_ThrowsNotFound_IfIdNotFound() { string id = new CompositeTableKey("unknown", "item").ToString(); // Act/Assert HttpResponseException ex = await AssertEx.ThrowsAsync <HttpResponseException>(async() => await this.manager.UndeleteAsync(id, null)); Assert.Equal(HttpStatusCode.NotFound, ex.Response.StatusCode); }
private void VerifyUpdatedKey(CompositeTableKey key, TData data) { if (data == null || data.PartitionKey != key.Segments[0] || data.RowKey != key.Segments[1]) { string msg = ASResources.TableController_KeyMismatch.FormatForUser("Id", key.ToString(), data.Id); HttpResponseMessage badKey = this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, msg); throw new HttpResponseException(badKey); } }
public void TryParse_FailsOnInvalidKeys(string key) { // Act CompositeTableKey tableKey; bool result = CompositeTableKey.TryParse(key, out tableKey); // Assert Assert.False(result); Assert.Null(tableKey); }
public void TryParse_SucceedsOnValidKeys(string key, string[] expectedSegments) { // Act CompositeTableKey tableKey; bool result = CompositeTableKey.TryParse(key, out tableKey); // Assert Assert.True(result); Assert.Equal(expectedSegments, tableKey.Segments); }
public async Task ReplaceAsync_Throws_BadRequestIfIdNotFound() { // Arrange string id = new CompositeTableKey("unknown", "item").ToString(); Person person = new Person(); // Act/Assert HttpResponseException ex = await AssertEx.ThrowsAsync <HttpResponseException>(async() => await this.manager.ReplaceAsync(id, person)); Assert.Equal(HttpStatusCode.BadRequest, ex.Response.StatusCode); }
public void ToString_GeneratesCorrectTableKey(string[] segments, string expectedKey) { // Arrange CompositeTableKey tableKey = new CompositeTableKey(segments); // Act string actualTableKey = tableKey.ToString(); // Assert Assert.Equal(expectedKey, actualTableKey); }
private async Task <TData> LookupItemAsync(string id) { if (id == null) { throw new ArgumentNullException("id"); } CompositeTableKey key = this.GetStorageKey(id); TData data = await this.GetCurrentItem(key, this.IncludeDeleted); return(data); }
public void ToStringAndParse_Roundtrips(string[] expectedSegments) { // Arrange CompositeTableKey tableKey = new CompositeTableKey(expectedSegments); // Act string actualTableKey = tableKey.ToString(); IEnumerable <string> actualSubkeys = CompositeTableKey.Parse(actualTableKey).Segments; // Assert Assert.Equal(expectedSegments, actualSubkeys); }
/// <summary> /// Attempts creating a new <see cref="CompositeTableKey"/> from a given <paramref name="tableKey"/>. /// The return value indicates whether the parsing succeeded. /// </summary> /// <param name="tableKey">The value containing the composite key.</param> /// <param name="compositeTableKey">If the method returns <c>true</c> then <paramref name="compositeTableKey"/> contains the result; otherwise <c>null</c>.</param> /// <returns></returns> public static bool TryParse(string tableKey, out CompositeTableKey compositeTableKey) { try { compositeTableKey = Parse(tableKey); return(true); } catch { compositeTableKey = null; return(false); } }
/// <summary> /// Attempts creating a new <see cref="CompositeTableKey"/> from a given <paramref name="tableKey"/>. /// The return value indicates whether the parsing succeeded. /// </summary> /// <param name="tableKey">The value containing the composite key.</param> /// <param name="compositeTableKey">If the method returns <c>true</c> then <paramref name="compositeTableKey"/> contains the result; otherwise <c>null</c>.</param> /// <returns></returns> public static bool TryParse(string tableKey, out CompositeTableKey compositeTableKey) { try { compositeTableKey = Parse(tableKey); return true; } catch { compositeTableKey = null; return false; } }
public async Task LookupAsync_ReturnsData() { // Arrange Collection <Person> persons = TestData.Persons; await this.InsertPersons(persons); CompositeTableKey id = new CompositeTableKey("Nielsen", "Henrik"); // Act SingleResult <Person> actual = await this.manager.LookupAsync(id.ToString()); // Assert Assert.Equal("Henrik", actual.Queryable.First().FirstName); }
public async Task UpdateAsync_Throws_NotFoundIfIdNotFound() { // Arrange string id = new CompositeTableKey("unknown", "item").ToString(); Delta <Person> patch = new Delta <Person>(); // Act/Assert this.manager.IncludeDeleted = false; HttpResponseException ex = await AssertEx.ThrowsAsync <HttpResponseException>(async() => await this.manager.UpdateAsync(id, patch)); Assert.Equal(HttpStatusCode.NotFound, ex.Response.StatusCode); this.manager.IncludeDeleted = true; ex = await AssertEx.ThrowsAsync <HttpResponseException>(async() => await this.manager.UpdateAsync(id, patch)); Assert.Equal(HttpStatusCode.NotFound, ex.Response.StatusCode); }
private async Task <TData> GetCurrentItem(CompositeTableKey key, bool includeDeleted) { if (key == null) { throw new ArgumentNullException("key"); } TableOperation query = TableOperation.Retrieve <TData>(key.Segments[0], key.Segments[1]); TData item = await this.ExecuteOperationAsync(query, OperationType.Read); if (item == null || (!includeDeleted && item.Deleted)) { throw new HttpResponseException(this.Request.CreateNotFoundResponse()); } return(item); }
protected virtual CompositeTableKey GetStorageKey(string id) { if (id == null) { throw new ArgumentNullException("id"); } CompositeTableKey compositeKey; if (!CompositeTableKey.TryParse(id, out compositeKey) || compositeKey.Segments.Count != 2) { // We have either invalid, no keys, or too many keys string error = ASResources.StorageTable_InvalidKey.FormatForUser(id); throw new HttpResponseException(this.Request.CreateBadRequestResponse(error)); } return(compositeKey); }
protected virtual CompositeTableKey GetCompositeKey(string id) { if (id == null) { throw new ArgumentNullException("id"); } CompositeTableKey compositeKey; if (CompositeTableKey.TryParse(id, out compositeKey)) { return(compositeKey); } string error = EFResources.TableKeys_InvalidKey.FormatForUser(id); throw new HttpResponseException(this.Request.CreateNotFoundResponse(error)); }
public override async Task <TData> ReplaceAsync(string id, TData data) { if (id == null) { throw new ArgumentNullException("id"); } if (data == null) { throw new ArgumentNullException("data"); } CompositeTableKey key = this.GetStorageKey(id); this.VerifyUpdatedKey(key, data); data.CreatedAt = DateTimeOffset.UtcNow; TableOperation replace = TableOperation.Replace(data); return(await this.ExecuteOperationAsync(replace, OperationType.Replace)); }
private async Task <TData> UpdateAsync(string id, Delta <TData> patch, bool includeDeleted, OperationType operationType) { if (id == null) { throw new ArgumentNullException("id"); } if (patch == null) { throw new ArgumentNullException("patch"); } CompositeTableKey key = this.GetStorageKey(id); TData current = await this.GetCurrentItem(key, includeDeleted); patch.Patch(current); this.VerifyUpdatedKey(key, current); TableOperation update = TableOperation.Merge(current); return(await this.ExecuteOperationAsync(update, operationType, key)); }
public async Task LookupAsync_DoesNotReturnSoftDeletedRecord_IfIncludeDeletedIsFalse() { // Arrange Person person = TestData.Persons.First(); person.PartitionKey = person.LastName; person.RowKey = person.FirstName; person.CreatedAt = null; person.UpdatedAt = null; await this.manager.InsertAsync(person); this.manager.EnableSoftDelete = true; bool result = await this.manager.DeleteAsync(person.Id); // Act this.manager.IncludeDeleted = false; CompositeTableKey id = new CompositeTableKey(person.LastName, person.FirstName); HttpResponseException ex = await AssertEx.ThrowsAsync <HttpResponseException>(async() => await this.manager.LookupAsync(id.ToString())); Assert.Equal(HttpStatusCode.NotFound, ex.Response.StatusCode); }
private async Task <TData> ExecuteOperationAsync(TableOperation operation, OperationType operationType, CompositeTableKey key = null, bool createTable = false) { StorageException storageException = null; try { if (createTable) { await this.Table.CreateIfNotExistsAsync(); } var tableResult = await this.Table.ExecuteAsync(operation); return(tableResult.Result as TData); } catch (StorageException exception) { storageException = exception; } if (TableResponseNotFound(storageException) && !createTable) { return(await this.ExecuteOperationAsync(operation, operationType, key, createTable : true)); } else { throw await this.ConvertStorageException(storageException, key); } }
internal async Task <HttpResponseException> ConvertStorageException(StorageException exception, CompositeTableKey key) { if (exception == null) { throw new ArgumentNullException("exception"); } ITraceWriter traceWriter = this.Request.GetConfiguration().Services.GetTraceWriter(); HttpResponseMessage error = null; if ((exception.RequestInformation != null) && (exception.RequestInformation.ExtendedErrorInformation != null)) { string message = CommonResources.DomainManager_Conflict.FormatForUser(exception.RequestInformation.ExtendedErrorInformation.ErrorMessage); traceWriter.Info(message, this.Request, LogCategories.TableControllers); if (exception.RequestInformation.ExtendedErrorInformation.ErrorCode == TableErrorCodeStrings.EntityAlreadyExists) { error = this.Request.CreateErrorResponse(HttpStatusCode.Conflict, message); } else if (exception.RequestInformation.ExtendedErrorInformation.ErrorCode == StorageErrorCodeStrings.ConditionNotMet || exception.RequestInformation.ExtendedErrorInformation.ErrorCode == TableErrorCodeStrings.UpdateConditionNotSatisfied) { // the two values above indicate that the etag doesn't match, but the error thrown from the emulator // is different from that in the service, so both must be included var content = await this.GetCurrentItem(key); error = this.Request.CreateResponse(HttpStatusCode.PreconditionFailed, content); } else if (exception.RequestInformation.ExtendedErrorInformation.ErrorCode == StorageErrorCodeStrings.ResourceNotFound) { error = this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, message); } } if (error == null) { string message = ASResources.DomainManager_InvalidOperation.FormatForUser(exception.Message); error = this.Request.CreateBadRequestResponse(message); traceWriter.Error(message, this.Request, LogCategories.TableControllers); } return(new HttpResponseException(error)); }
protected virtual Task <TData> GetCurrentItem(CompositeTableKey key) { return(this.GetCurrentItem(key, this.IncludeDeleted)); }
public void ToStringAndParse_Roundtrips(string[] expectedSegments) { // Arrange CompositeTableKey tableKey = new CompositeTableKey(expectedSegments); // Act string actualTableKey = tableKey.ToString(); IEnumerable<string> actualSubkeys = CompositeTableKey.Parse(actualTableKey).Segments; // Assert Assert.Equal(expectedSegments, actualSubkeys); }
public void ToString_GeneratesCorrectTableKey(string[] segments, string expectedKey) { // Arrange CompositeTableKey tableKey = new CompositeTableKey(segments); // Act string actualTableKey = tableKey.ToString(); // Assert Assert.Equal(expectedKey, actualTableKey); }
public void Parse_ThrowsOnInvalidKeys(string key) { Assert.Throws <ArgumentException>(() => CompositeTableKey.Parse(key)); }