/// <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) { if (context == null) { throw new ArgumentNullException(nameof(context)); } var dbContext = context.GetApiService <DbContext>(); foreach (var entry in context.ChangeSet.Entries.OfType <DataModificationItem>()) { var strongTypedDbSet = dbContext.GetType().GetProperty(entry.ResourceSetName).GetValue(dbContext); var 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; } var set = dbContext.Set(resourceType); object resource; if (entry.EntitySetOperation == RestierEntitySetOperation.Insert) { resource = set.Create(); SetValues(resource, resourceType, entry.LocalValues); set.Add(resource); } else if (entry.EntitySetOperation == RestierEntitySetOperation.Delete) { resource = await FindResource(context, entry, cancellationToken).ConfigureAwait(false); set.Remove(resource); } else if (entry.EntitySetOperation == RestierEntitySetOperation.Update) { resource = await FindResource(context, entry, cancellationToken).ConfigureAwait(false); var dbEntry = dbContext.Entry(resource); SetValues(dbEntry, entry, resourceType); } else { throw new NotSupportedException(Resources.DataModificationMustBeCUD); } entry.Resource = resource; } }
public async Task AuthorizeAsyncWithPrivateMethod() { testTraceListener.Clear(); var api = new PrivateMethodApi(serviceProvider); var testClass = new ConventionBasedChangeSetItemAuthorizer(typeof(PrivateMethodApi)); var context = new SubmitContext(api, new ChangeSet()); var cancellationToken = CancellationToken.None; var result = await testClass.AuthorizeAsync(context, dataModificationItem, cancellationToken); result.Should().BeTrue("AuthorizeAsync should return true, because CanInsertObject is private."); testTraceListener.Messages.Should().Contain("inaccessible due to its protection level"); api.InvocationCount.Should().Be(0); }
public async Task AuthorizeAsyncWithWrongNumberOfArguments() { testTraceListener.Clear(); var api = new IncorrectArgumentsApi(serviceProvider); var testClass = new ConventionBasedChangeSetItemAuthorizer(typeof(IncorrectArgumentsApi)); var context = new SubmitContext(api, new ChangeSet()); var cancellationToken = CancellationToken.None; var result = await testClass.AuthorizeAsync(context, dataModificationItem, cancellationToken); result.Should().BeTrue("AuthorizeAsync should return true, because the api type is incorrect."); testTraceListener.Messages.Should().Contain("incorrect number of arguments"); api.InvocationCount.Should().Be(0); }
/// <summary> /// Asynchronously submits changes made using an API context. /// </summary> /// <param name="context"> /// An API context. /// </param> /// <param name="changeSet"> /// A change set, or <c>null</c> to submit existing pending changes. /// </param> /// <param name="cancellationToken"> /// An optional cancellation token. /// </param> /// <returns> /// A task that represents the asynchronous /// operation whose result is a submit result. /// </returns> public static async Task <SubmitResult> SubmitAsync( this ApiContext context, ChangeSet changeSet = null, CancellationToken cancellationToken = default(CancellationToken)) { Ensure.NotNull(context, "context"); var submitContext = new SubmitContext(context, changeSet); var model = await context.GetModelAsync(cancellationToken); submitContext.Model = model; return(await DefaultSubmitHandler.SubmitAsync(submitContext, cancellationToken)); }
public async Task AuthorizeAsyncWithWrongReturnType() { testTraceListener.Clear(); var api = new WrongReturnTypeApi(serviceProvider); var testClass = new ConventionBasedChangeSetItemAuthorizer(typeof(WrongReturnTypeApi)); var context = new SubmitContext(api, new ChangeSet()); var cancellationToken = CancellationToken.None; var result = await testClass.AuthorizeAsync(context, dataModificationItem, cancellationToken); result.Should().BeTrue("AuthorizeAsync should return true, because CanInsertObject returns an int."); testTraceListener.Messages.Should().Contain("does not return a boolean value"); api.InvocationCount.Should().Be(0); }
/// <inheritdoc/> public Task <bool> AuthorizeAsync(SubmitContext context, ChangeSetItem item, CancellationToken cancellationToken) { Ensure.NotNull(context, nameof(context)); Ensure.NotNull(item, nameof(item)); var result = true; var returnType = typeof(bool); var dataModification = (DataModificationItem)item; var methodName = ConventionBasedMethodNameFactory.GetEntitySetMethodName(dataModification, RestierPipelineState.Authorization); var method = targetApiType.GetQualifiedMethod(methodName); if (method == null) { return(Task.FromResult(result)); } if (!method.IsFamily && !method.IsFamilyOrAssembly) { Trace.WriteLine($"Restier Authorizer found '{methodName}' but it is unaccessible due to its protection level. Your method will not be called until you change it to 'protected internal'."); return(Task.FromResult(result)); } if (method.ReturnType != returnType) { Trace.WriteLine($"Restier Authorizer found '{methodName}' but it does not return a boolean value. Your method will not be called until you correct the return type."); return(Task.FromResult(result)); } object target = null; if (!method.IsStatic) { target = context.Api; if (!targetApiType.IsInstanceOfType(target)) { Trace.WriteLine("The Restier API is of the incorrect type."); return(Task.FromResult(result)); } } var parameters = method.GetParameters(); if (parameters.Length == 0) { result = (bool)method.Invoke(target, null); } Trace.WriteLine($"Restier Authorizer found '{methodName}', but it has an incorrect number of arguments. The number of arguments should be 0."); return(Task.FromResult(result)); }
/// <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.EntitySetName).GetValue(dbContext); Type entityType = strongTypedDbSet.GetType().GetGenericArguments()[0]; // This means request entity is sub type of entity type if (entry.ActualEntityType != null && entityType != entry.ActualEntityType) { entityType = entry.ActualEntityType; } DbSet set = dbContext.Set(entityType); object entity; if (entry.DataModificationItemAction == DataModificationItemAction.Insert) { entity = set.Create(); SetValues(entity, entityType, entry.LocalValues); set.Add(entity); } else if (entry.DataModificationItemAction == DataModificationItemAction.Remove) { entity = await FindEntity(context, entry, cancellationToken); set.Remove(entity); } else if (entry.DataModificationItemAction == DataModificationItemAction.Update) { 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 void DefaultSubmitContext_CanLogNullOrEmptyExternalId(string title) { var submitContext = new SubmitContext() { CoreMetaData = new List <SubmissionMetaDataModel>() }; submitContext.CoreMetaData.Add(new SubmissionMetaDataModel() { Name = Fields.ExternalId, Type = nameof(String), Value = title }); Assert.Contains(_expectedNoExternalIdMessage, submitContext.LogPrefix()); }
private async Task AssertFiltered(SubmitContext submitContext, bool expectFiltered) { // We might reuse the same submit context id in the same run - Let's just reset // the invocations before we do a verify _mockNext.ResetCalls(); if (expectFiltered) { await AssertFiltered(submitContext); } else { await AssertNotFiltered(submitContext); } }
private async Task SubmitAggregation(string externalId, CancellationToken cancellationToken) { var submitContext = new SubmitContext { TenantId = _connectorConfigModel.TenantIdAsGuid, ConnectorConfigId = _connectorConfigModel.IdAsGuid, ApiClientFactorySettings = ConnectorApiAuthHelper.GetApiClientFactorySettings(), AuthenticationHelperSettings = ConnectorApiAuthHelper.GetAuthenticationHelperSettings(_connectorConfigModel.TenantDomainName), CoreMetaData = new List <SubmissionMetaDataModel>(), SourceMetaData = new List <SubmissionMetaDataModel>(), Filters = _connectorConfigModel.Filters, CancellationToken = cancellationToken }; submitContext.CoreMetaData.Add(new SubmissionMetaDataModel(Fields.ExternalId, type: nameof(String), value: externalId.ToString())); // Aggregations need to have the special "ItemTypeId" field set to "1" to identify them as being an aggregation. // Submissions that omit this field are identified as items. submitContext.CoreMetaData.Add(new SubmissionMetaDataModel(Fields.ItemTypeId, type: nameof(Double), value: "1")); // Note we do not set the ParentExternalId of the aggregation - in most cases aggregations don't have a parent. submitContext.CoreMetaData.Add(new SubmissionMetaDataModel(Fields.SourceLastModifiedDate, type: nameof(DateTime), value: DateTime.UtcNow.ToString("O"))); submitContext.CoreMetaData.Add(new SubmissionMetaDataModel(Fields.Title, type: nameof(String), value: $"Fake Record Folder from {_connectorConfigModel.Id}")); submitContext.CoreMetaData.Add(new SubmissionMetaDataModel(Fields.Author, type: nameof(String), value: "Record ReferenceConnectorSF")); submitContext.CoreMetaData.Add(new SubmissionMetaDataModel(Fields.SourceCreatedDate, type: nameof(DateTime), value: DateTime.UtcNow.ToString("O"))); submitContext.CoreMetaData.Add(new SubmissionMetaDataModel(Fields.SourceCreatedBy, type: nameof(String), value: "ReferenceConnectorSF")); submitContext.CoreMetaData.Add(new SubmissionMetaDataModel(Fields.SourceLastModifiedBy, type: nameof(String), value: "ReferenceConnectorSF")); submitContext.CoreMetaData.Add(new SubmissionMetaDataModel(Fields.Location, type: nameof(String), value: "Fake Record Folder")); submitContext.CoreMetaData.Add(new SubmissionMetaDataModel(Fields.MediaType, type: nameof(String), value: "Electronic")); submitContext.CoreMetaData.Add(new SubmissionMetaDataModel(Fields.ContentVersion, type: nameof(String), value: "1.0")); submitContext.SourceMetaData.Add(new SubmissionMetaDataModel("Checkin Comments", type: nameof(String), value: "Some checkin comments")); // Submit the aggregation! try { await _aggregationSubmitPipeline.Submit(submitContext).ConfigureAwait(false); HandleSubmitPipelineResult(submitContext); } catch (Exception) { // Something went wrong trying to submit the item. // Dead-letter the item to a durable data store where it can be retried later. (e.g., a message broker). } }
/// <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()); }
/// <summary> /// /// </summary> /// <param name="context"></param> /// <param name="item"></param> /// <param name="pipelineState"></param> /// <returns></returns> private Task InvokeProcessorMethodAsync(SubmitContext context, ChangeSetItem item, RestierPipelineState pipelineState) { var dataModification = (DataModificationItem)item; var expectedMethodName = ConventionBasedMethodNameFactory.GetEntitySetMethodName(dataModification, pipelineState); var expectedMethod = targetApiType.GetQualifiedMethod(expectedMethodName); if (!IsUsable(expectedMethod)) { if (expectedMethod != null) { Trace.WriteLine($"Restier Filter found '{expectedMethodName}' but it is unaccessible due to its protection level. Your method will not be called until you change it to 'protected internal'."); } else { var actualMethodName = expectedMethodName.Replace(dataModification.ExpectedResourceType.Name, dataModification.ResourceSetName); var actualMethod = targetApiType.GetQualifiedMethod(actualMethodName); if (actualMethod != null) { Trace.WriteLine($"BREAKING: Restier Filter expected'{expectedMethodName}' but found '{actualMethodName}'. Your method will not be called until you correct the method name."); } } } else { object target = null; if (!expectedMethod.IsStatic) { target = context.Api; if (target == null || !targetApiType.IsInstanceOfType(target)) { return(Task.WhenAll()); } } var parameters = GetParameters(item); var methodParameters = expectedMethod.GetParameters(); if (ParametersMatch(methodParameters, parameters)) { var result = expectedMethod.Invoke(target, parameters); if (result is Task resultTask) { return(resultTask); } } } return(Task.WhenAll()); }
public static SubmissionMetaDataModel GetField(SubmitContext submitContext, SearchTermModel filter, string expectedFieldType) { // Format of a source field name in Records365: S|<ConnectorTypeId>|<DataType>|<FieldName> if (filter.FieldName.StartsWith(_sourceFieldPrefix)) { // Avoiding String.Split due to performance concerns. Not using LastIndexOf in case a Field name contains a pipe. // We want to skip over S|<ConnectorTypeId>|, which has a known length (39 characters), then find the index of the first pipe following that. // After that pipe, the rest should be the Source property's field name var lastPipeIndex = filter.FieldName.IndexOf(_separator, _indexAfterSeparator); var sourceFieldName = filter.FieldName.Substring(lastPipeIndex + 1); return(submitContext?.SourceMetaData?.FirstOrDefault(x => x.Name == sourceFieldName && x.Type == expectedFieldType)); } return(submitContext?.CoreMetaData?.FirstOrDefault(x => x.Name == filter.FieldName)); }
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; var 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)); }
public async Task PrepareAsync( SubmitContext context, CancellationToken cancellationToken) { DbContext dbContext = context.ApiContext.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]; MethodInfo prepareEntryMethod = _prepareEntryGeneric.MakeGenericMethod(entityType); await(Task) prepareEntryMethod.Invoke( obj: null, parameters: new object[] { context, dbContext, entry, strongTypedDbSet, cancellationToken }); } }
private static async Task PrepareEntry <TEntity>( SubmitContext context, DbContext dbContext, DataModificationItem entry, DbSet <TEntity> set, CancellationToken cancellationToken) where TEntity : class { Type entityType = typeof(TEntity); TEntity entity; if (entry.DataModificationItemAction == DataModificationItemAction.Insert) { // TODO: See if Create method is in DbSet<> in future EF7 releases, as the one EF6 has. entity = (TEntity)Activator.CreateInstance(typeof(TEntity)); ChangeSetInitializer.SetValues(entity, entityType, entry.LocalValues); set.Add(entity); } else if (entry.DataModificationItemAction == DataModificationItemAction.Remove) { entity = (TEntity)await ChangeSetInitializer.FindEntity(context, entry, cancellationToken); set.Remove(entity); } else if (entry.DataModificationItemAction == DataModificationItemAction.Update) { if (entry.IsFullReplaceUpdateRequest) { entity = (TEntity)ChangeSetInitializer.CreateFullUpdateInstance(entry, entityType); dbContext.Update(entity); } else { entity = (TEntity)await ChangeSetInitializer.FindEntity(context, entry, cancellationToken); var dbEntry = dbContext.Attach(entity); ChangeSetInitializer.SetValues(dbEntry, entry); } } else { throw new NotSupportedException(Resources.DataModificationMustBeCUD); } entry.Resource = entity; }
public override Task InitializeAsync(SubmitContext context, CancellationToken cancellationToken) { var changeSetEntry = context.ChangeSet.Entries.Single(); if (changeSetEntry is DataModificationItem dataModificationEntry) { dataModificationEntry.Resource = new Product() { Name = "var1", Addr = new Address() { Zip = 330 } }; } return(Task.FromResult <object>(null)); }
public void DefaultSubmitContext_CanLogNonNullExternalId() { var submitContext = new SubmitContext() { CoreMetaData = new List <SubmissionMetaDataModel>() }; var externalId = Guid.NewGuid().ToString(); submitContext.CoreMetaData.Add(new SubmissionMetaDataModel() { Name = Fields.ExternalId, Type = nameof(String), Value = externalId }); Assert.Contains(externalId, submitContext.LogPrefix()); }
public void DefaultSubmitContext_CanLogNonNullTitle() { var submitContext = new SubmitContext() { CoreMetaData = new List <SubmissionMetaDataModel>() }; var title = Guid.NewGuid().ToString(); submitContext.CoreMetaData.Add(new SubmissionMetaDataModel() { Name = Fields.Title, Type = nameof(String), Value = title }); Assert.Contains(title, submitContext.LogPrefix()); }
/// <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; } }
/// <inheritdoc/> public Task ValidateChangeSetItemAsync(SubmitContext context, ChangeSetItem item, Collection <ChangeSetItemValidationResult> validationResults, CancellationToken cancellationToken) { Ensure.NotNull(validationResults, nameof(validationResults)); Ensure.NotNull(context, nameof(context)); Ensure.NotNull(item, nameof(item)); if (item is DataModificationItem dataModificationItem) { var resource = dataModificationItem.Resource; // TODO GitHubIssue#50 : should this PropertyDescriptorCollection be cached? var properties = new AssociatedMetadataTypeTypeDescriptionProvider(resource.GetType()) .GetTypeDescriptor(resource).GetProperties(); var validationContext = new ValidationContext(resource); foreach (PropertyDescriptor property in properties) { validationContext.MemberName = property.Name; var validationAttributes = property.Attributes.OfType <ValidationAttribute>(); foreach (var validationAttribute in validationAttributes) { var value = property.GetValue(resource); var validationResult = validationAttribute.GetValidationResult(value, validationContext); if (validationResult != ValidationResult.Success) { validationResults.Add(new ChangeSetItemValidationResult() { ValidatorType = validationAttribute.GetType().FullName, Message = validationResult.ErrorMessage, Severity = EventLevel.Error, Target = resource, PropertyName = property.Name, }); } } } } return(Task.CompletedTask); }
public Task PrepareAsync(SubmitContext context, CancellationToken cancellationToken) { var changeSetEntry = context.ChangeSet.Entries.Single(); var dataModificationEntry = changeSetEntry as DataModificationEntry; if (dataModificationEntry != null) { dataModificationEntry.Entity = new Product() { Name = "var1", Addr = new Address() { Zip = 330 } }; } return(Task.FromResult <object>(null)); }
private async Task SubmitAuditEvent(string itemExternalId, CancellationToken cancellationToken) { var submitContext = new SubmitContext { TenantId = _connectorConfigModel.TenantIdAsGuid, ConnectorConfigId = _connectorConfigModel.IdAsGuid, ApiClientFactorySettings = ConnectorApiAuthHelper.GetApiClientFactorySettings(), AuthenticationHelperSettings = ConnectorApiAuthHelper.GetAuthenticationHelperSettings(_connectorConfigModel.TenantDomainName), CoreMetaData = new List <SubmissionMetaDataModel>(), SourceMetaData = new List <SubmissionMetaDataModel>(), Filters = _connectorConfigModel.Filters, CancellationToken = cancellationToken }; // Associate the audit event with the item with the item external id. submitContext.CoreMetaData.Add(new SubmissionMetaDataModel(Fields.AuditEvent.ExternalId, type: nameof(String), value: itemExternalId)); // Set the EventExternalId - this is an ID that uniquely identifies the audit event in the content source. submitContext.CoreMetaData.Add(new SubmissionMetaDataModel(Fields.AuditEvent.EventExternalId, type: nameof(String), value: Guid.NewGuid().ToString())); submitContext.CoreMetaData.Add(new SubmissionMetaDataModel(Fields.AuditEvent.EventType, type: nameof(String), value: "Reference Audit Event")); submitContext.CoreMetaData.Add(new SubmissionMetaDataModel(Fields.AuditEvent.UserName, type: nameof(String), value: "Record ReferenceConnectorSF")); submitContext.CoreMetaData.Add(new SubmissionMetaDataModel(Fields.AuditEvent.UserId, type: nameof(String), value: "Record ReferenceConnectorSF")); submitContext.CoreMetaData.Add(new SubmissionMetaDataModel(Fields.AuditEvent.Created, type: nameof(DateTime), value: DateTime.UtcNow.ToString("O"))); // Set source metadata on the audit event submitContext.SourceMetaData.Add(new SubmissionMetaDataModel("Checkin Comments", type: nameof(String), value: "Some checkin comments")); // Submit the audit event! try { await _auditEventSubmitPipeline.Submit(submitContext).ConfigureAwait(false); HandleSubmitPipelineResult(submitContext); } catch (Exception) { // Something went wrong trying to submit the item. // Dead-letter the item to a durable data store where it can be retried later. (e.g., a message broker). } }
private Task InvokeFilterMethodAsync( SubmitContext context, ChangeSetEntry entry, string methodNameSuffix) { string methodName = ConventionBasedChangeSetEntryFilter.GetMethodName(entry, methodNameSuffix); object[] parameters = ConventionBasedChangeSetEntryFilter.GetParameters(entry); 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.ApiContext.GetProperty( typeof(Api).AssemblyQualifiedName); if (target == null || !this.targetType.IsAssignableFrom(target.GetType())) { return(Task.WhenAll()); } } ParameterInfo[] methodParameters = method.GetParameters(); if (ConventionBasedChangeSetEntryFilter.ParametersMatch(methodParameters, parameters)) { object result = method.Invoke(target, parameters); Task resultTask = result as Task; if (resultTask != null) { return(resultTask); } } } return(Task.WhenAll()); }
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 <object> FindEntity( SubmitContext context, DataModificationItem item, CancellationToken cancellationToken) { IQueryable query = context.ApiContext.GetQueryableSource(item.EntitySetName); query = item.ApplyTo(query); QueryResult result = await context.ApiContext.QueryAsync(new QueryRequest(query), cancellationToken); object entity = result.Results.SingleOrDefault(); if (entity == 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(entity); } var etagEntity = item.ApplyEtag(result.Results.AsQueryable()).SingleOrDefault(); if (etagEntity == null) { // If ETAG does not match, should return 412 Precondition Failed var message = string.Format( CultureInfo.InvariantCulture, Resources.PreconditionCheckFailed, new object[] { item.DataModificationItemAction, entity }); throw new PreconditionFailedException(message); } return(etagEntity); }
public Task <SubmitResult> SubmitAsync(SubmitContext context, CancellationToken cancellationToken) { foreach (var entry in context.ChangeSet.Entries.OfType <DataModificationEntry>()) { if (entry.LocalValues.All(l => l.Key != "Addr")) { throw new Exception("Addr is required."); } entry.Entity = new Product { Id = 1, Addr = new Address { Zip = 0001 }, Addr2 = new Address { Zip = 0002 } }; } return(Task.FromResult(new SubmitResult(new ChangeSet()))); }
private static async Task <object> FindEntity( SubmitContext context, DataModificationEntry entry, CancellationToken cancellationToken) { IQueryable query = Domain.Source(context.DomainContext, entry.EntitySetName); query = entry.ApplyTo(query); QueryResult result = await Domain.QueryAsync( context.DomainContext, 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.ApiContext.GetProperty( typeof(Api).AssemblyQualifiedName); 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)); }