public async Task ComplexTypeUpdate() { // Arrange var container = new RestierContainerBuilder(typeof(LibraryApi)); var provider = container.BuildContainer(); var libraryApi = provider.GetService<ApiBase>(); var item = new DataModificationItem( "Readers", typeof(Person), null, DataModificationItemAction.Update, new Dictionary<string, object> { { "Id", new Guid("53162782-EA1B-4712-AF26-8AA1D2AC0461") } }, new Dictionary<string, object>(), new Dictionary<string, object> { { "Addr", new Dictionary<string, object> { { "Zip", "332" } } } }); var changeSet = new ChangeSet(new[] { item }); var sc = new SubmitContext(libraryApi.Context, changeSet); // Act var changeSetPreparer = libraryApi.Context.GetApiService<IChangeSetInitializer>(); await changeSetPreparer.InitializeAsync(sc, CancellationToken.None); var person = item.Resource as Person; // Assert Assert.NotNull(person); Assert.Equal("332", person.Addr.Zip); }
private static async Task<object> FindEntity( SubmitContext context, DataModificationEntry entry, CancellationToken cancellationToken) { IQueryable query = context.ApiContext.Source(entry.EntitySetName); query = entry.ApplyTo(query); QueryResult result = await context.ApiContext.QueryAsync(new QueryRequest(query), cancellationToken); object entity = result.Results.SingleOrDefault(); if (entity == null) { // TODO GitHubIssue#38 : Handle the case when entity is resolved // there are 2 cases where the entity is not found: // 1) it doesn't exist // 2) concurrency checks have failed // we should account for both - I can see 3 options: // a. always return "PreConditionFailed" result // - this is the canonical behavior of WebAPI OData, see the following post: // "Getting started with ASP.NET Web API 2.2 for OData v4.0" on http://blogs.msdn.com/b/webdev/. // - this makes sense because if someone deleted the record, then you still have a concurrency error // b. possibly doing a 2nd query with just the keys to see if the record still exists // c. only query with the keys, and then set the DbEntityEntry's OriginalValues to the ETag values, // letting the save fail if there are concurrency errors ////throw new EntityNotFoundException throw new InvalidOperationException(Resources.ResourceNotFound); } return entity; }
/// <inheritdoc/> public Task<bool> AuthorizeAsync( SubmitContext context, ChangeSetEntry entry, CancellationToken cancellationToken) { Ensure.NotNull(context, "context"); bool result = true; Type returnType = typeof(bool); string methodName = ConventionBasedChangeSetAuthorizer.GetAuthorizeMethodName(entry); MethodInfo method = this.targetType.GetQualifiedMethod(methodName); if (method != null && method.IsFamily && method.ReturnType == returnType) { object target = null; if (!method.IsStatic) { target = context.GetApiService<ApiBase>(); if (target == null || !this.targetType.IsAssignableFrom(target.GetType())) { return Task.FromResult(result); } } var parameters = method.GetParameters(); if (parameters.Length == 0) { result = (bool)method.Invoke(target, null); } } return Task.FromResult(result); }
/// <summary> /// Asynchronously executes the submit flow. /// </summary> /// <param name="context"> /// The submit context. /// </param> /// <param name="cancellationToken"> /// A cancellation token. /// </param> /// <returns> /// A task that represents the asynchronous /// operation whose result is a submit result. /// </returns> public static async Task<SubmitResult> SubmitAsync( SubmitContext context, CancellationToken cancellationToken) { Ensure.NotNull(context, "context"); var preparer = context.GetApiService<IChangeSetPreparer>(); if (preparer == null) { throw new NotSupportedException(Resources.ChangeSetPreparerMissing); } await preparer.PrepareAsync(context, cancellationToken); if (context.Result != null) { return context.Result; } var eventsChangeSet = context.ChangeSet; IEnumerable<ChangeSetEntry> currentChangeSetItems = eventsChangeSet.Entries.ToArray(); await PerformValidate(context, currentChangeSetItems, cancellationToken); await PerformPreEvent(context, currentChangeSetItems, cancellationToken); await PerformPersist(context, currentChangeSetItems, cancellationToken); context.ChangeSet.Entries.Clear(); await PerformPostEvent(context, currentChangeSetItems, cancellationToken); return context.Result; }
/// <summary> /// Asynchronously executes the submit flow. /// </summary> /// <param name="context"> /// The submit context. /// </param> /// <param name="cancellationToken"> /// A cancellation token. /// </param> /// <returns> /// A task that represents the asynchronous /// operation whose result is a submit result. /// </returns> public static async Task<SubmitResult> SubmitAsync( SubmitContext context, CancellationToken cancellationToken) { Ensure.NotNull(context, "context"); var preparer = context.GetHookHandler<IChangeSetPreparer>(); if (preparer == null) { throw new NotSupportedException(Resources.ChangeSetPreparerMissing); } await preparer.PrepareAsync(context, cancellationToken); if (context.Result != null) { return context.Result; } ChangeSet eventsChangeSet = context.ChangeSet; IEnumerable<ChangeSetEntry> currentChangeSetItems; int outerLoopCount = 0; do { outerLoopCount++; int innerLoopCount = 0; do { innerLoopCount++; eventsChangeSet.AnEntityHasChanged = false; currentChangeSetItems = eventsChangeSet.Entries.ToArray(); if (eventsChangeSet.AnEntityHasChanged) { eventsChangeSet.AnEntityHasChanged = false; currentChangeSetItems = eventsChangeSet.Entries.ToArray(); } await PerformValidate(context, currentChangeSetItems, cancellationToken); await PerformPreEvent(context, currentChangeSetItems, cancellationToken); } while (eventsChangeSet.AnEntityHasChanged && (innerLoopCount < MaxLoop)); VerifyNoEntityHasChanged(eventsChangeSet); await PerformPersist(context, currentChangeSetItems, cancellationToken); eventsChangeSet.Entries.Clear(); await PerformPostEvent(context, currentChangeSetItems, cancellationToken); } while (eventsChangeSet.AnEntityHasChanged && (outerLoopCount < MaxLoop)); VerifyNoEntityHasChanged(eventsChangeSet); return context.Result; }
/// <inheritdoc/> public Task OnExecutedEntryAsync( SubmitContext context, ChangeSetEntry entry, CancellationToken cancellationToken) { return this.InvokeFilterMethodAsync( context, entry, ConventionBasedChangeSetConstants.FilterMethodNamePostFilterSuffix); }
/// <inheritdoc/> public Task OnChangeSetItemProcessedAsync( SubmitContext context, ChangeSetItem item, CancellationToken cancellationToken) { return this.InvokeProcessorMethodAsync( context, item, ConventionBasedChangeSetConstants.FilterMethodNamePostFilterSuffix); }
/// <summary> /// Asynchronously executes the submission. /// </summary> /// <param name="context">The submit context class used for preparation.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The task object that represents this asynchronous operation.</returns> public async Task<SubmitResult> ExecuteSubmitAsync( SubmitContext context, CancellationToken cancellationToken) { DbContext dbContext = context.ApiContext.GetProperty<DbContext>(DbApiConstants.DbContextKey); await dbContext.SaveChangesAsync(cancellationToken); return new SubmitResult(context.ChangeSet); }
/// <summary> /// Asynchronously executes the submission. /// </summary> /// <param name="context">The submit context class used for preparation.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The task object that represents this asynchronous operation.</returns> public async Task<SubmitResult> ExecuteSubmitAsync( SubmitContext context, CancellationToken cancellationToken) { DbContext dbContext = context.DomainContext.GetProperty<DbContext>("DbContext"); await dbContext.SaveChangesAsync(cancellationToken); return new SubmitResult(context.ChangeSet); }
/// <summary> /// Asynchronously executes the submission. /// </summary> /// <param name="context">The submit context class used for preparation.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The task object that represents this asynchronous operation.</returns> public async Task<SubmitResult> ExecuteSubmitAsync( SubmitContext context, CancellationToken cancellationToken) { DbContext dbContext = context.GetApiService<DbContext>(); await dbContext.SaveChangesAsync(cancellationToken); return new SubmitResult(context.ChangeSet); }
private static async Task PerformPostEvent( SubmitContext context, IEnumerable <ChangeSetEntry> changeSetItems, CancellationToken cancellationToken) { foreach (ChangeSetEntry entry in changeSetItems) { foreach (var filter in context.GetHookPoints <IChangeSetEntryFilter>()) { await filter.OnExecutedEntryAsync(context, entry, cancellationToken); } } }
private static async Task PerformPostEvent( SubmitContext context, IEnumerable <ChangeSetItem> changeSetItems, CancellationToken cancellationToken) { foreach (ChangeSetItem item in changeSetItems) { var processor = context.GetApiService <IChangeSetItemProcessor>(); if (processor != null) { await processor.OnProcessedChangeSetItemAsync(context, item, cancellationToken); } } }
private static async Task PerformPostEvent( SubmitContext context, IEnumerable <ChangeSetEntry> changeSetItems, CancellationToken cancellationToken) { foreach (ChangeSetEntry entry in changeSetItems) { var filter = context.GetApiService <IChangeSetEntryFilter>(); if (filter != null) { await filter.OnExecutedEntryAsync(context, entry, cancellationToken); } } }
private static async Task PerformPersist( SubmitContext context, IEnumerable <ChangeSetItem> changeSetItems, CancellationToken cancellationToken) { var executor = context.GetApiService <ISubmitExecutor>(); if (executor == null) { throw new NotSupportedException(Resources.SubmitExecutorMissing); } context.Result = await executor.ExecuteSubmitAsync(context, cancellationToken); }
private async Task InvokeAuthorizers(SubmitContext context, IEnumerable <ChangeSetItem> changeSetItems, CancellationToken cancellationToken) { if (authorizer == null) { return; } foreach (var item in changeSetItems.Where(i => i.HasChanged())) { if (!await authorizer.AuthorizeAsync(context, item, cancellationToken).ConfigureAwait(false)) { throw new SecurityException(GetAuthorizeFailedMessage(item)); } } }
/// <summary> /// Asynchronously prepare the <see cref="ChangeSet"/>. /// </summary> /// <param name="context">The submit context class used for preparation.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The task object that represents this asynchronous operation.</returns> public async Task InitializeAsync( SubmitContext context, CancellationToken cancellationToken) { DbContext dbContext = context.ApiContext.GetApiService<DbContext>(); foreach (var entry in context.ChangeSet.Entries.OfType<DataModificationItem>()) { object strongTypedDbSet = dbContext.GetType().GetProperty(entry.ResourceSetName).GetValue(dbContext); Type resourceType = strongTypedDbSet.GetType().GetGenericArguments()[0]; // This means request resource is sub type of resource type if (entry.ActualResourceType != null && resourceType != entry.ActualResourceType) { // Set type to derived type resourceType = entry.ActualResourceType; } DbSet set = dbContext.Set(resourceType); object resource; if (entry.DataModificationItemAction == DataModificationItemAction.Insert) { resource = set.Create(); SetValues(resource, resourceType, entry.LocalValues); set.Add(resource); } else if (entry.DataModificationItemAction == DataModificationItemAction.Remove) { resource = await FindResource(context, entry, cancellationToken); set.Remove(resource); } else if (entry.DataModificationItemAction == DataModificationItemAction.Update) { resource = await FindResource(context, entry, cancellationToken); DbEntityEntry dbEntry = dbContext.Entry(resource); SetValues(dbEntry, entry, resourceType); } else { throw new NotSupportedException(Resources.DataModificationMustBeCUD); } entry.Resource = resource; } }
/// <inheritdoc/> public Task ValidateChangeSetItemAsync( SubmitContext context, ChangeSetItem item, Collection<ChangeSetItemValidationResult> validationResults, CancellationToken cancellationToken) { Ensure.NotNull(validationResults, "validationResults"); DataModificationItem dataModificationItem = item as DataModificationItem; if (dataModificationItem != null) { object resource = dataModificationItem.Resource; // TODO GitHubIssue#50 : should this PropertyDescriptorCollection be cached? PropertyDescriptorCollection properties = new DataAnnotations.AssociatedMetadataTypeTypeDescriptionProvider(resource.GetType()) .GetTypeDescriptor(resource).GetProperties(); DataAnnotations.ValidationContext validationContext = new DataAnnotations.ValidationContext(resource); foreach (PropertyDescriptor property in properties) { validationContext.MemberName = property.Name; IEnumerable<DataAnnotations.ValidationAttribute> validationAttributes = property.Attributes.OfType<DataAnnotations.ValidationAttribute>(); foreach (DataAnnotations.ValidationAttribute validationAttribute in validationAttributes) { object value = property.GetValue(resource); DataAnnotations.ValidationResult validationResult = validationAttribute.GetValidationResult(value, validationContext); if (validationResult != DataAnnotations.ValidationResult.Success) { validationResults.Add(new ChangeSetItemValidationResult() { Id = validationAttribute.GetType().FullName, Message = validationResult.ErrorMessage, Severity = EventLevel.Error, Target = resource, PropertyName = property.Name }); } } } } return Task.WhenAll(); }
/// <inheritdoc/> public Task ValidateEntityAsync( SubmitContext context, ChangeSetEntry entry, ValidationResults validationResults, CancellationToken cancellationToken) { Ensure.NotNull(validationResults, "validationResults"); DataModificationEntry dataModificationEntry = entry as DataModificationEntry; if (dataModificationEntry != null) { object entity = dataModificationEntry.Entity; // TODO GitHubIssue#50 : should this PropertyDescriptorCollection be cached? PropertyDescriptorCollection properties = new DataAnnotations.AssociatedMetadataTypeTypeDescriptionProvider(entity.GetType()) .GetTypeDescriptor(entity).GetProperties(); DataAnnotations.ValidationContext validationContext = new DataAnnotations.ValidationContext(entity); foreach (PropertyDescriptor property in properties) { validationContext.MemberName = property.Name; IEnumerable<DataAnnotations.ValidationAttribute> validationAttributes = property.Attributes.OfType<DataAnnotations.ValidationAttribute>(); foreach (DataAnnotations.ValidationAttribute validationAttribute in validationAttributes) { object value = property.GetValue(entity); DataAnnotations.ValidationResult validationResult = validationAttribute.GetValidationResult(value, validationContext); if (validationResult != DataAnnotations.ValidationResult.Success) { validationResults.Add(new ValidationResult() { Id = validationAttribute.GetType().FullName, Message = validationResult.ErrorMessage, Severity = ValidationSeverity.Error, Target = entity, PropertyName = property.Name }); } } } } return Task.WhenAll(); }
/// <inheritdoc/> public Task ValidateEntityAsync( SubmitContext context, ChangeSetEntry entry, ValidationResults validationResults, CancellationToken cancellationToken) { Ensure.NotNull(validationResults, "validationResults"); DataModificationEntry dataModificationEntry = entry as DataModificationEntry; if (dataModificationEntry != null) { object entity = dataModificationEntry.Entity; // TODO (.NETCORE): Use PropertyDescriptorCollection in .NET Core (when available?) // TODO GitHubIssue#50 : should this PropertyDescriptorCollection be cached? IEnumerable<PropertyInfo> properties = entity.GetType().GetTypeInfo().DeclaredProperties; DataAnnotations.ValidationContext validationContext = new DataAnnotations.ValidationContext(entity); foreach (var property in properties) { validationContext.MemberName = property.Name; IEnumerable<DataAnnotations.ValidationAttribute> validationAttributes = property.GetCustomAttributes().OfType<DataAnnotations.ValidationAttribute>(); foreach (var validationAttribute in validationAttributes) { object value = property.GetValue(entity); DataAnnotations.ValidationResult validationResult = validationAttribute.GetValidationResult(value, validationContext); if (validationResult != DataAnnotations.ValidationResult.Success) { validationResults.Add(new ValidationResult() { Id = validationAttribute.GetType().FullName, Message = validationResult.ErrorMessage, Severity = ValidationSeverity.Error, Target = entity, PropertyName = property.Name }); } } } } return Task.WhenAll(); }
public Task OnChangeSetItemProcessedAsync(SubmitContext context, ChangeSetItem item, CancellationToken cancellationToken) { var dataModificationItem = item as DataModificationItem; if (dataModificationItem != null) { object myEntity = dataModificationItem.Resource; string entitySetName = dataModificationItem.ResourceSetName; DataModificationItemAction operation = dataModificationItem.DataModificationItemAction; // In case of insert, the request URL has no key, and request body may not have key neither as the key may be generated by database var keyAttrbiutes = new Dictionary<string, object>(); var keyConvention = new Dictionary<string, object>(); var entityTypeName = myEntity.GetType().Name; PropertyInfo[] properties = myEntity.GetType().GetProperties(); foreach (PropertyInfo property in properties) { var attribute = Attribute.GetCustomAttribute(property, typeof(KeyAttribute)) as KeyAttribute; var propName = property.Name; // This is getting key with Key attribute defined if (attribute != null) // This property has a KeyAttribute { // Do something, to read from the property: object val = property.GetValue(myEntity); keyAttrbiutes.Add(propName, val); } // This is getting key based on convention else if(propName.ToLower().Equals("id") || propName.ToLower().Equals(entityTypeName.ToLower()+"id")) { object val = property.GetValue(myEntity); keyConvention.Add(propName, val); } } if (keyAttrbiutes.Count > 0) { // Use property with key attribute as keys } else if(keyConvention.Count > 0) { // Key is defined based on convention } } return Inner.OnChangeSetItemProcessedAsync(context, item, cancellationToken); }
private async Task PerformValidate(SubmitContext context, IEnumerable <ChangeSetItem> changeSetItems, CancellationToken cancellationToken) { await InvokeAuthorizers(context, changeSetItems, cancellationToken).ConfigureAwait(false); await InvokeValidators(context, changeSetItems, cancellationToken).ConfigureAwait(false); foreach (var item in changeSetItems.Where(i => i.HasChanged())) { if (item.ChangeSetItemProcessingStage == ChangeSetItemProcessingStage.ChangedWithinOwnPreEventing) { item.ChangeSetItemProcessingStage = ChangeSetItemProcessingStage.PreEvented; } else { item.ChangeSetItemProcessingStage = ChangeSetItemProcessingStage.Validated; } } }
/// <summary> /// Asynchronously prepare the <see cref="ChangeSet"/>. /// </summary> /// <param name="context">The submit context class used for preparation.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The task object that represents this asynchronous operation.</returns> public async Task PrepareAsync( SubmitContext context, CancellationToken cancellationToken) { DbContext dbContext = context.DomainContext.GetProperty<DbContext>("DbContext"); foreach (var entry in context.ChangeSet.Entries.OfType<DataModificationEntry>()) { object strongTypedDbSet = dbContext.GetType().GetProperty(entry.EntitySetName).GetValue(dbContext); Type entityType = strongTypedDbSet.GetType().GetGenericArguments()[0]; DbSet set = dbContext.Set(entityType); object entity; if (entry.IsNew) { entity = set.Create(); SetValues(entity, entityType, entry.LocalValues); set.Add(entity); } else if (entry.IsDelete) { entity = await FindEntity(context, entry, cancellationToken); set.Remove(entity); } else if (entry.IsUpdate) { entity = await FindEntity(context, entry, cancellationToken); DbEntityEntry dbEntry = dbContext.Entry(entity); SetValues(dbEntry, entry, entityType); } else { throw new NotSupportedException(Resources.DataModificationMustBeCUD); } entry.Entity = entity; } }
public async Task ComplexTypeUpdate() { // Arrange var libraryDomain = new LibraryDomain(); var entry = new DataModificationEntry( "Readers", "Person", new Dictionary<string, object> { { "Id", new Guid("53162782-EA1B-4712-AF26-8AA1D2AC0461") } }, new Dictionary<string, object>(), new Dictionary<string, object> { { "Addr", new Dictionary<string, object> { { "Zip", "332" } } } }); var changeSet = new ChangeSet(new[] { entry }); var sc = new SubmitContext(libraryDomain.Context, changeSet); // Act await ChangeSetPreparer.Instance.PrepareAsync(sc, CancellationToken.None); var person = entry.Entity as Person; // Assert Assert.NotNull(person); Assert.Equal("332", person.Addr.Zip); }
private static async Task InvokeAuthorizers( SubmitContext context, IEnumerable <ChangeSetItem> changeSetItems, CancellationToken cancellationToken) { var authorizer = context.GetApiService <IChangeSetItemAuthorizer>(); if (authorizer == null) { return; } foreach (ChangeSetItem item in changeSetItems.Where(i => i.HasChanged())) { if (!await authorizer.AuthorizeAsync(context, item, cancellationToken)) { var message = DefaultSubmitHandler.GetAuthorizeFailedMessage(item); throw new SecurityException(message); } } }
private static async Task PerformValidate( SubmitContext context, IEnumerable <ChangeSetEntry> changeSetItems, CancellationToken cancellationToken) { await InvokeAuthorizers(context, changeSetItems, cancellationToken); await InvokeValidators(context, changeSetItems, cancellationToken); foreach (ChangeSetEntry item in changeSetItems.Where(i => i.HasChanged())) { if (item.ChangeSetEntityState == DynamicChangeSetEntityState.ChangedWithinOwnPreEventing) { item.ChangeSetEntityState = DynamicChangeSetEntityState.PreEvented; } else { item.ChangeSetEntityState = DynamicChangeSetEntityState.Validated; } } }
private static async Task PerformPersist( SubmitContext context, IEnumerable <ChangeSetEntry> changeSetItems, CancellationToken cancellationToken) { // Once the change is persisted, the EntityState is lost. // In order to invoke the correct post-CUD event, remember which action was performed on the entity. foreach (ChangeSetEntry item in changeSetItems) { if (item.Type == ChangeSetEntryType.DataModification) { DataModificationEntry dataModification = (DataModificationEntry)item; if (dataModification.IsNew) { dataModification.AddAction = AddAction.Inserting; } else if (dataModification.IsUpdate) { dataModification.AddAction = AddAction.Updating; } else if (dataModification.IsDelete) { dataModification.AddAction = AddAction.Removing; } } } var executor = context.GetHookHandler <ISubmitExecutor>(); if (executor == null) { throw new NotSupportedException(Resources.SubmitExecutorMissing); } context.Result = await executor.ExecuteSubmitAsync(context, cancellationToken); }
private static async Task InvokeValidators( SubmitContext context, IEnumerable<ChangeSetEntry> changeSetItems, CancellationToken cancellationToken) { ValidationResults validationResults = new ValidationResults(); foreach (ChangeSetEntry entry in changeSetItems.Where(i => i.HasChanged())) { foreach (var validator in context .GetHookPoints<IChangeSetEntryValidator>().Reverse()) { await validator.ValidateEntityAsync(context, entry, validationResults, cancellationToken); } } if (validationResults.HasErrors) { string validationErrorMessage = Resources.ValidationFailsTheOperation; throw new ValidationException(validationErrorMessage) { ValidationResults = validationResults.Errors }; } }
private static async Task InvokeAuthorizers( SubmitContext context, IEnumerable<ChangeSetEntry> changeSetItems, CancellationToken cancellationToken) { foreach (ChangeSetEntry entry in changeSetItems.Where(i => i.HasChanged())) { string message = null; foreach (var authorizer in context .GetHookPoints<IChangeSetEntryAuthorizer>().Reverse()) { if (!await authorizer.AuthorizeAsync(context, entry, cancellationToken)) { message = DefaultSubmitHandler.GetAuthorizeFailedMessage(entry); break; } } if (message != null) { throw new SecurityException(message); } } }
private static async Task PerformValidate( SubmitContext context, IEnumerable<ChangeSetEntry> changeSetItems, CancellationToken cancellationToken) { await InvokeAuthorizers(context, changeSetItems, cancellationToken); await InvokeValidators(context, changeSetItems, cancellationToken); foreach (ChangeSetEntry item in changeSetItems.Where(i => i.HasChanged())) { if (item.ChangeSetEntityState == DynamicChangeSetEntityState.ChangedWithinOwnPreEventing) { item.ChangeSetEntityState = DynamicChangeSetEntityState.PreEvented; } else { item.ChangeSetEntityState = DynamicChangeSetEntityState.Validated; } } }
public Task OnChangeSetItemProcessingAsync(SubmitContext context, ChangeSetItem item, CancellationToken cancellationToken) { return Inner.OnChangeSetItemProcessingAsync(context, item, cancellationToken); }
/// <summary> /// Asynchronously executes the submit flow. /// </summary> /// <param name="context"> /// The submit context. /// </param> /// <param name="cancellationToken"> /// A cancellation token. /// </param> /// <returns> /// A task that represents the asynchronous /// operation whose result is a submit result. /// </returns> public async Task <SubmitResult> SubmitAsync( SubmitContext context, CancellationToken cancellationToken) { Ensure.NotNull(context, "context"); var preparer = context.GetHookPoint <IChangeSetPreparer>(); if (preparer == null) { throw new NotSupportedException(); } await preparer.PrepareAsync(context, cancellationToken); // authorize var authorized = true; foreach (var authorizer in context .GetHookPoints <ISubmitAuthorizer>().Reverse()) { authorized = await authorizer.AuthorizeAsync( context, cancellationToken); if (!authorized || context.Result != null) { break; } } if (!authorized) { // TODO GitHubIssue#32 : Figure out a more appropriate exception throw new SecurityException(); } if (context.Result != null) { return(context.Result); } ChangeSet eventsChangeSet = context.ChangeSet; IEnumerable <ChangeSetEntry> currentChangeSetItems; int outerLoopCount = 0; do { outerLoopCount++; int innerLoopCount = 0; do { innerLoopCount++; eventsChangeSet.AnEntityHasChanged = false; currentChangeSetItems = eventsChangeSet.Entries.ToArray(); if (eventsChangeSet.AnEntityHasChanged) { eventsChangeSet.AnEntityHasChanged = false; currentChangeSetItems = eventsChangeSet.Entries.ToArray(); } await PerformValidate(context, currentChangeSetItems, cancellationToken); await PerformPreEvent(context, currentChangeSetItems, cancellationToken); }while (eventsChangeSet.AnEntityHasChanged && (innerLoopCount < MaxLoop)); VerifyNoEntityHasChanged(eventsChangeSet); await PerformPersist(context, currentChangeSetItems, cancellationToken); eventsChangeSet.Entries.Clear(); await PerformPostEvent(context, currentChangeSetItems, cancellationToken); }while (eventsChangeSet.AnEntityHasChanged && (outerLoopCount < MaxLoop)); VerifyNoEntityHasChanged(eventsChangeSet); return(context.Result); }
private static async Task PerformPostEvent( SubmitContext context, IEnumerable<ChangeSetEntry> changeSetItems, CancellationToken cancellationToken) { foreach (ChangeSetEntry entry in changeSetItems) { foreach (var filter in context.GetHookPoints<IChangeSetEntryFilter>()) { await filter.OnExecutedEntryAsync(context, entry, cancellationToken); } } }
/// <summary> /// /// </summary> /// <param name="context"></param> /// <param name="cancellationToken"></param> /// <returns></returns> public virtual Task InitializeAsync(SubmitContext context, CancellationToken cancellationToken) { Ensure.NotNull(context, nameof(context)); context.ChangeSet = new ChangeSet(); return(Task.FromResult(0)); }
private Task InvokeProcessorMethodAsync( SubmitContext context, ChangeSetItem item, string methodNameSuffix) { string methodName = GetMethodName(item, methodNameSuffix); object[] parameters = GetParameters(item); MethodInfo method = this.targetType.GetQualifiedMethod(methodName); if (method != null && (method.ReturnType == typeof(void) || typeof(Task).IsAssignableFrom(method.ReturnType))) { object target = null; if (!method.IsStatic) { target = context.GetApiService<ApiBase>(); if (target == null || !this.targetType.IsInstanceOfType(target)) { return Task.WhenAll(); } } ParameterInfo[] methodParameters = method.GetParameters(); if (ParametersMatch(methodParameters, parameters)) { object result = method.Invoke(target, parameters); Task resultTask = result as Task; if (resultTask != null) { return resultTask; } } } return Task.WhenAll(); }
private static async Task PerformPostEvent( SubmitContext context, IEnumerable<ChangeSetEntry> changeSetItems, CancellationToken cancellationToken) { foreach (ChangeSetEntry entry in changeSetItems) { var filter = context.GetApiService<IChangeSetEntryFilter>(); if (filter != null) { await filter.OnExecutedEntryAsync(context, entry, cancellationToken); } } }
private static async Task InvokeAuthorizers( SubmitContext context, IEnumerable<ChangeSetEntry> changeSetItems, CancellationToken cancellationToken) { var authorizer = context.GetApiService<IChangeSetEntryAuthorizer>(); if (authorizer == null) { return; } foreach (ChangeSetEntry entry in changeSetItems.Where(i => i.HasChanged())) { if (!await authorizer.AuthorizeAsync(context, entry, cancellationToken)) { var message = DefaultSubmitHandler.GetAuthorizeFailedMessage(entry); throw new SecurityException(message); } } }
private static async Task PerformPreEvent( SubmitContext context, IEnumerable<ChangeSetEntry> changeSetItems, CancellationToken cancellationToken) { foreach (ChangeSetEntry entry in changeSetItems) { if (entry.ChangeSetEntityState == DynamicChangeSetEntityState.Validated) { entry.ChangeSetEntityState = DynamicChangeSetEntityState.PreEventing; foreach (var filter in context .GetHookPoints<IChangeSetEntryFilter>().Reverse()) { await filter.OnExecutingEntryAsync(context, entry, cancellationToken); } if (entry.ChangeSetEntityState == DynamicChangeSetEntityState.PreEventing) { // if the state is still the intermediate state, // the entity was not changed during processing // and can move to the next step entry.ChangeSetEntityState = DynamicChangeSetEntityState.PreEvented; } else if (entry.ChangeSetEntityState == DynamicChangeSetEntityState.Changed /*&& entity.Details.EntityState == originalEntityState*/) { entry.ChangeSetEntityState = DynamicChangeSetEntityState.ChangedWithinOwnPreEventing; } } } }
private static async Task PerformPersist( SubmitContext context, IEnumerable<ChangeSetEntry> changeSetItems, CancellationToken cancellationToken) { // Once the change is persisted, the EntityState is lost. // In order to invoke the correct post-CUD event, remember which action was performed on the entity. foreach (ChangeSetEntry item in changeSetItems) { if (item.Type == ChangeSetEntryType.DataModification) { DataModificationEntry dataModification = (DataModificationEntry)item; if (dataModification.IsNew) { dataModification.AddAction = AddAction.Inserting; } else if (dataModification.IsUpdate) { dataModification.AddAction = AddAction.Updating; } else if (dataModification.IsDelete) { dataModification.AddAction = AddAction.Removing; } } } var executor = context.GetHookPoint<ISubmitExecutor>(); if (executor == null) { throw new NotSupportedException(); } context.Result = await executor.ExecuteSubmitAsync(context, cancellationToken); }
private static async Task<object> FindResource( SubmitContext context, DataModificationItem item, CancellationToken cancellationToken) { IQueryable query = context.ApiContext.GetQueryableSource(item.ResourceSetName); query = item.ApplyTo(query); QueryResult result = await context.ApiContext.QueryAsync(new QueryRequest(query), cancellationToken); object resource = result.Results.SingleOrDefault(); if (resource == null) { throw new ResourceNotFoundException(Resources.ResourceNotFound); } // This means no If-Match or If-None-Match header if (item.OriginalValues == null || item.OriginalValues.Count == 0) { return resource; } resource = item.ValidateEtag(result.Results.AsQueryable()); return resource; }
/// <summary> /// Asynchronously executes the submit flow. /// </summary> /// <param name="context"> /// The submit context. /// </param> /// <param name="cancellationToken"> /// A cancellation token. /// </param> /// <returns> /// A task that represents the asynchronous /// operation whose result is a submit result. /// </returns> public async Task<SubmitResult> SubmitAsync( SubmitContext context, CancellationToken cancellationToken) { Ensure.NotNull(context, "context"); var preparer = context.GetHookPoint<IChangeSetPreparer>(); if (preparer == null) { throw new NotSupportedException(); } await preparer.PrepareAsync(context, cancellationToken); // authorize var authorized = true; foreach (var authorizer in context .GetHookPoints<ISubmitAuthorizer>().Reverse()) { authorized = await authorizer.AuthorizeAsync( context, cancellationToken); if (!authorized || context.Result != null) { break; } } if (!authorized) { // TODO GitHubIssue#32 : Figure out a more appropriate exception throw new SecurityException(); } if (context.Result != null) { return context.Result; } ChangeSet eventsChangeSet = context.ChangeSet; IEnumerable<ChangeSetEntry> currentChangeSetItems; int outerLoopCount = 0; do { outerLoopCount++; int innerLoopCount = 0; do { innerLoopCount++; eventsChangeSet.AnEntityHasChanged = false; currentChangeSetItems = eventsChangeSet.Entries.ToArray(); if (eventsChangeSet.AnEntityHasChanged) { eventsChangeSet.AnEntityHasChanged = false; currentChangeSetItems = eventsChangeSet.Entries.ToArray(); } await PerformValidate(context, currentChangeSetItems, cancellationToken); await PerformPreEvent(context, currentChangeSetItems, cancellationToken); } while (eventsChangeSet.AnEntityHasChanged && (innerLoopCount < MaxLoop)); VerifyNoEntityHasChanged(eventsChangeSet); await PerformPersist(context, currentChangeSetItems, cancellationToken); eventsChangeSet.Entries.Clear(); await PerformPostEvent(context, currentChangeSetItems, cancellationToken); } while (eventsChangeSet.AnEntityHasChanged && (outerLoopCount < MaxLoop)); VerifyNoEntityHasChanged(eventsChangeSet); return context.Result; }
/// <summary> /// /// </summary> /// <param name="context"></param> /// <param name="cancellationToken"></param> /// <returns></returns> public virtual Task <SubmitResult> ExecuteSubmitAsync(SubmitContext context, CancellationToken cancellationToken) { Ensure.NotNull(context, nameof(context)); return(Task.FromResult(new SubmitResult(context.ChangeSet))); }
public Task<SubmitResult> ExecuteSubmitAsync(SubmitContext context, CancellationToken cancellationToken) { return Task.FromResult(new SubmitResult(context.ChangeSet)); }
private async Task PerformPersist(SubmitContext context, CancellationToken cancellationToken) { context.Result = await executor.ExecuteSubmitAsync(context, cancellationToken).ConfigureAwait(false); }