private ChangeFeedProcessorOptions BuildProcessorOptions(CosmosDBTriggerAttribute attribute) { ChangeFeedHostOptions leasesOptions = _options.LeaseOptions; ChangeFeedProcessorOptions processorOptions = new ChangeFeedProcessorOptions { LeasePrefix = ResolveAttributeValue(attribute.LeaseCollectionPrefix) ?? leasesOptions.LeasePrefix, FeedPollDelay = ResolveTimeSpanFromMilliseconds(nameof(CosmosDBTriggerAttribute.FeedPollDelay), leasesOptions.FeedPollDelay, attribute.FeedPollDelay), LeaseAcquireInterval = ResolveTimeSpanFromMilliseconds(nameof(CosmosDBTriggerAttribute.LeaseAcquireInterval), leasesOptions.LeaseAcquireInterval, attribute.LeaseAcquireInterval), LeaseExpirationInterval = ResolveTimeSpanFromMilliseconds(nameof(CosmosDBTriggerAttribute.LeaseExpirationInterval), leasesOptions.LeaseExpirationInterval, attribute.LeaseExpirationInterval), LeaseRenewInterval = ResolveTimeSpanFromMilliseconds(nameof(CosmosDBTriggerAttribute.LeaseRenewInterval), leasesOptions.LeaseRenewInterval, attribute.LeaseRenewInterval), CheckpointFrequency = leasesOptions.CheckpointFrequency ?? new CheckpointFrequency() }; if (attribute.CheckpointInterval > 0) { processorOptions.CheckpointFrequency.TimeInterval = TimeSpan.FromMilliseconds(attribute.CheckpointInterval); } if (attribute.CheckpointDocumentCount > 0) { processorOptions.CheckpointFrequency.ProcessedDocumentCount = attribute.CheckpointDocumentCount; } return(processorOptions); }
public void CompleteArguments_Succeed() { const string collectionName = "someCollection"; const string databaseName = "someDatabase"; const string leaseCollectionName = "someLeaseCollection"; const string leaseDatabaseName = "someLeaseDatabase"; const string defaultLeaseCollectionName = "leases"; CosmosDBTriggerAttribute attributeWithNoLeaseSpecified = new CosmosDBTriggerAttribute(databaseName, collectionName); Assert.Equal(collectionName, attributeWithNoLeaseSpecified.CollectionName); Assert.Equal(databaseName, attributeWithNoLeaseSpecified.DatabaseName); Assert.Equal(defaultLeaseCollectionName, attributeWithNoLeaseSpecified.LeaseCollectionName); Assert.Equal(databaseName, attributeWithNoLeaseSpecified.LeaseDatabaseName); CosmosDBTriggerAttribute attributeWithLeaseSpecified = new CosmosDBTriggerAttribute(databaseName, collectionName) { LeaseDatabaseName = leaseDatabaseName, LeaseCollectionName = leaseCollectionName }; Assert.Equal(collectionName, attributeWithLeaseSpecified.CollectionName); Assert.Equal(databaseName, attributeWithLeaseSpecified.DatabaseName); Assert.Equal(leaseCollectionName, attributeWithLeaseSpecified.LeaseCollectionName); Assert.Equal(leaseDatabaseName, attributeWithLeaseSpecified.LeaseDatabaseName); }
private string ResolveAttributeConnectionString(CosmosDBTriggerAttribute attribute) { string connectionString = ResolveConnectionString(attribute.ConnectionStringSetting, nameof(CosmosDBTriggerAttribute.ConnectionStringSetting)); if (string.IsNullOrEmpty(connectionString)) { ThrowMissingConnectionStringException(); } return(connectionString); }
private string ResolveAttributeConnectionString(CosmosDBTriggerAttribute attribute) { if (string.IsNullOrEmpty(_monitorConnectionString) && string.IsNullOrEmpty(attribute.ConnectionStringSetting)) { throw new InvalidOperationException( string.Format(CultureInfo.CurrentCulture, "The DocumentDBTrigger connection string must be set either via a '{0}' app setting, via the DocumentDBTriggerAttribute.ConnectionString property or via DocumentDBConfiguration.TriggerConnectionString.", DocumentDBConfiguration.AzureWebJobsDocumentDBConnectionStringName)); } return(attribute.ConnectionStringSetting ?? _monitorConnectionString); }
private string ResolveAttributeLeasesConnectionString(CosmosDBTriggerAttribute attribute, string triggerConnectionString) { // If the leases connection is not specified, it connects to the monitored service string connectionString = string.IsNullOrEmpty(this._leasesConnectionString) ? triggerConnectionString : this._leasesConnectionString; if (!TryReadFromSettings(attribute.LeaseConnectionStringSetting, connectionString, out connectionString)) { throw new InvalidOperationException( string.Format(CultureInfo.CurrentCulture, "The CosmosDBTriggerAttribute.LeaseConnectionStringSetting '{0}' property specified does not exist in the Application Settings.", attribute.ConnectionStringSetting)); } return(connectionString); }
public override Collection <Attribute> GetAttributes() { Collection <Attribute> attributes = new Collection <Attribute>(); string databaseName = Context.GetMetadataValue <string>("databaseName"); string collectionName = Context.GetMetadataValue <string>("collectionName"); if (Context.IsTrigger) { CosmosDBTriggerAttribute attributeTrigger = new CosmosDBTriggerAttribute(databaseName, collectionName); attributeTrigger.ConnectionStringSetting = Context.GetMetadataValue <string>("connectionStringSetting"); attributeTrigger.LeaseDatabaseName = Context.GetMetadataValue <string>("leaseDatabaseName"); attributeTrigger.LeaseCollectionName = Context.GetMetadataValue <string>("leaseCollectionName"); attributeTrigger.LeaseConnectionStringSetting = Context.GetMetadataValue <string>("leaseConnectionStringSetting"); attributeTrigger.CreateLeaseCollectionIfNotExists = Context.GetMetadataValue <bool>("createLeaseCollectionIfNotExists", false); attributeTrigger.LeasesCollectionThroughput = Context.GetMetadataValue <int>("leaseCollectionThroughput", 0); attributes.Add(attributeTrigger); } else { DocumentDBAttribute attribute = null; if (!string.IsNullOrEmpty(databaseName) || !string.IsNullOrEmpty(collectionName)) { attribute = new DocumentDBAttribute(databaseName, collectionName); } else { attribute = new DocumentDBAttribute(); } attribute.CreateIfNotExists = Context.GetMetadataValue <bool>("createIfNotExists"); attribute.ConnectionStringSetting = Context.GetMetadataValue <string>("connection"); attribute.Id = Id; attribute.PartitionKey = Context.GetMetadataValue <string>("partitionKey"); attribute.CollectionThroughput = Context.GetMetadataValue <int>("collectionThroughput"); attribute.SqlQuery = Context.GetMetadataValue <string>("sqlQuery"); attributes.Add(attribute); } return(attributes); }
private string ResolveAttributeLeasesConnectionString(CosmosDBTriggerAttribute attribute) { // If the lease connection string is not set, use the trigger's string keyToResolve = attribute.LeaseConnectionStringSetting; if (string.IsNullOrEmpty(keyToResolve)) { keyToResolve = attribute.ConnectionStringSetting; } string connectionString = ResolveConnectionString(keyToResolve, nameof(CosmosDBTriggerAttribute.LeaseConnectionStringSetting)); if (string.IsNullOrEmpty(connectionString)) { ThrowMissingConnectionStringException(true); } return(connectionString); }
private string ResolveAttributeConnectionString(CosmosDBTriggerAttribute attribute) { string connectionString = this._monitorConnectionString; if (!TryReadFromSettings(attribute.ConnectionStringSetting, connectionString, out connectionString)) { throw new InvalidOperationException( string.Format(CultureInfo.CurrentCulture, "The CosmosDBTriggerAttribute.ConnectionStringSetting '{0}' property specified does not exist in the Application Settings.", attribute.ConnectionStringSetting)); } if (string.IsNullOrEmpty(connectionString)) { throw new InvalidOperationException( string.Format(CultureInfo.CurrentCulture, "The CosmosDBTrigger connection string must be set either via a '{0}' app setting, via the CosmosDBTriggerAttribute.ConnectionStringSetting property or via DocumentDBConfiguration.ConnectionString.", DocumentDBConfiguration.AzureWebJobsDocumentDBConnectionStringName)); } return(connectionString); }
public async Task ValidChangeFeedOptions_Succeed(ParameterInfo parameter) { var config = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary <string, string> { // Verify we load from root config { "CosmosDBConnectionString", "AccountEndpoint=https://fromSettings;AccountKey=c29tZV9rZXk=;" } }) .Build(); CosmosDBTriggerAttributeBindingProvider provider = new CosmosDBTriggerAttributeBindingProvider(config, new TestNameResolver(), _options, CreateExtensionConfigProvider(_options), _loggerFactory); CosmosDBTriggerBinding binding = (CosmosDBTriggerBinding)await provider.TryCreateAsync(new TriggerBindingProviderContext(parameter, CancellationToken.None)); CosmosDBTriggerAttribute cosmosDBTriggerAttribute = parameter.GetCustomAttribute <CosmosDBTriggerAttribute>(inherit: false); Assert.Equal(typeof(IReadOnlyList <Document>), binding.TriggerValueType); Assert.Equal(new Uri("https://fromSettings"), binding.DocumentCollectionLocation.Uri); Assert.Equal(new Uri("https://fromSettings"), binding.LeaseCollectionLocation.Uri); Assert.Equal(cosmosDBTriggerAttribute.MaxItemsPerInvocation, binding.ChangeFeedProcessorOptions.MaxItemCount); Assert.Equal(cosmosDBTriggerAttribute.StartFromBeginning, binding.ChangeFeedProcessorOptions.StartFromBeginning); Assert.Equal(cosmosDBTriggerAttribute.StartFromTime, binding.ChangeFeedProcessorOptions.StartTime?.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss%K")); }
public Task <ITriggerBinding> TryCreateAsync(TriggerBindingProviderContext context) { // Tries to parse the context parameters and see if it belongs to this [CosmosDBTrigger] binder if (context == null) { throw new ArgumentNullException("context"); } ParameterInfo parameter = context.Parameter; CosmosDBTriggerAttribute attribute = parameter.GetCustomAttribute <CosmosDBTriggerAttribute>(inherit: false); if (attribute == null) { return(Task.FromResult <ITriggerBinding>(null)); } DocumentCollectionInfo documentCollectionLocation; DocumentCollectionInfo leaseCollectionLocation; ChangeFeedHostOptions leaseHostOptions = ResolveLeaseOptions(attribute); try { string triggerConnectionString = ResolveAttributeConnectionString(attribute); DocumentDBConnectionString triggerConnection = new DocumentDBConnectionString(triggerConnectionString); if (triggerConnection.ServiceEndpoint == null) { throw new InvalidOperationException("The connection string for the monitored collection is in an invalid format, please use AccountEndpoint=XXXXXX;AccountKey=XXXXXX;."); } DocumentDBConnectionString leasesConnection = new DocumentDBConnectionString(ResolveAttributeLeasesConnectionString(attribute, triggerConnectionString)); if (triggerConnection.ServiceEndpoint == null) { throw new InvalidOperationException("The connection string for the leases collection is in an invalid format, please use AccountEndpoint=XXXXXX;AccountKey=XXXXXX;."); } documentCollectionLocation = new DocumentCollectionInfo { Uri = triggerConnection.ServiceEndpoint, MasterKey = triggerConnection.AuthKey, DatabaseName = attribute.DatabaseName, CollectionName = attribute.CollectionName }; leaseCollectionLocation = new DocumentCollectionInfo { Uri = leasesConnection.ServiceEndpoint, MasterKey = leasesConnection.AuthKey, DatabaseName = attribute.LeaseDatabaseName, CollectionName = attribute.LeaseCollectionName }; if (documentCollectionLocation.Uri.Equals(leaseCollectionLocation.Uri) && documentCollectionLocation.DatabaseName.Equals(leaseCollectionLocation.DatabaseName) && documentCollectionLocation.CollectionName.Equals(leaseCollectionLocation.CollectionName)) { throw new InvalidOperationException("The monitored collection cannot be the same as the collection storing the leases."); } } catch (Exception ex) { throw new InvalidOperationException(string.Format("Cannot create Collection Information for {0} in database {1} with lease {2} in database {3} : {4}", attribute.CollectionName, attribute.DatabaseName, attribute.LeaseCollectionName, attribute.LeaseDatabaseName, ex.Message), ex); } return(Task.FromResult <ITriggerBinding>(new CosmosDBTriggerBinding(parameter, documentCollectionLocation, leaseCollectionLocation, leaseHostOptions))); }
private ChangeFeedHostOptions ResolveLeaseOptions(CosmosDBTriggerAttribute attribute) { return(attribute.LeaseOptions ?? _leasesOptions); }
private string ResolveAttributeLeasesConnectionString(CosmosDBTriggerAttribute attribute, string triggerConnectionString) { // If the leases connection is not specified, it connects to the monitored service return(attribute.LeaseConnectionStringSetting ?? _leasesConnectionString ?? triggerConnectionString); }
public async Task <ITriggerBinding> TryCreateAsync(TriggerBindingProviderContext context) { // Tries to parse the context parameters and see if it belongs to this [CosmosDBTrigger] binder if (context == null) { throw new ArgumentNullException("context"); } ParameterInfo parameter = context.Parameter; CosmosDBTriggerAttribute attribute = parameter.GetCustomAttribute <CosmosDBTriggerAttribute>(inherit: false); if (attribute == null) { return(null); } ConnectionMode?desiredConnectionMode = _options.ConnectionMode; Protocol? desiredConnectionProtocol = _options.Protocol; DocumentCollectionInfo documentCollectionLocation; DocumentCollectionInfo leaseCollectionLocation; ChangeFeedProcessorOptions processorOptions = BuildProcessorOptions(attribute); processorOptions.StartFromBeginning = attribute.StartFromBeginning; if (attribute.MaxItemsPerInvocation > 0) { processorOptions.MaxItemCount = attribute.MaxItemsPerInvocation; } ICosmosDBService monitoredCosmosDBService; ICosmosDBService leaseCosmosDBService; try { string triggerConnectionString = ResolveAttributeConnectionString(attribute); CosmosDBConnectionString triggerConnection = new CosmosDBConnectionString(triggerConnectionString); if (triggerConnection.ServiceEndpoint == null) { throw new InvalidOperationException("The connection string for the monitored collection is in an invalid format, please use AccountEndpoint=XXXXXX;AccountKey=XXXXXX;."); } string leasesConnectionString = ResolveAttributeLeasesConnectionString(attribute); CosmosDBConnectionString leasesConnection = new CosmosDBConnectionString(leasesConnectionString); if (leasesConnection.ServiceEndpoint == null) { throw new InvalidOperationException("The connection string for the leases collection is in an invalid format, please use AccountEndpoint=XXXXXX;AccountKey=XXXXXX;."); } documentCollectionLocation = new DocumentCollectionInfo { Uri = triggerConnection.ServiceEndpoint, MasterKey = triggerConnection.AuthKey, DatabaseName = ResolveAttributeValue(attribute.DatabaseName), CollectionName = ResolveAttributeValue(attribute.CollectionName) }; documentCollectionLocation.ConnectionPolicy.UserAgentSuffix = CosmosDBTriggerUserAgentSuffix; if (desiredConnectionMode.HasValue) { documentCollectionLocation.ConnectionPolicy.ConnectionMode = desiredConnectionMode.Value; } if (desiredConnectionProtocol.HasValue) { documentCollectionLocation.ConnectionPolicy.ConnectionProtocol = desiredConnectionProtocol.Value; } leaseCollectionLocation = new DocumentCollectionInfo { Uri = leasesConnection.ServiceEndpoint, MasterKey = leasesConnection.AuthKey, DatabaseName = ResolveAttributeValue(attribute.LeaseDatabaseName), CollectionName = ResolveAttributeValue(attribute.LeaseCollectionName) }; leaseCollectionLocation.ConnectionPolicy.UserAgentSuffix = CosmosDBTriggerUserAgentSuffix; if (desiredConnectionMode.HasValue) { leaseCollectionLocation.ConnectionPolicy.ConnectionMode = desiredConnectionMode.Value; } if (desiredConnectionProtocol.HasValue) { leaseCollectionLocation.ConnectionPolicy.ConnectionProtocol = desiredConnectionProtocol.Value; } string resolvedPreferredLocations = ResolveAttributeValue(attribute.PreferredLocations); foreach (var location in CosmosDBUtility.ParsePreferredLocations(resolvedPreferredLocations)) { documentCollectionLocation.ConnectionPolicy.PreferredLocations.Add(location); leaseCollectionLocation.ConnectionPolicy.PreferredLocations.Add(location); } if (string.IsNullOrEmpty(documentCollectionLocation.DatabaseName) || string.IsNullOrEmpty(documentCollectionLocation.CollectionName) || string.IsNullOrEmpty(leaseCollectionLocation.DatabaseName) || string.IsNullOrEmpty(leaseCollectionLocation.CollectionName)) { throw new InvalidOperationException("Cannot establish database and collection values. If you are using environment and configuration values, please ensure these are correctly set."); } if (documentCollectionLocation.Uri.Equals(leaseCollectionLocation.Uri) && documentCollectionLocation.DatabaseName.Equals(leaseCollectionLocation.DatabaseName) && documentCollectionLocation.CollectionName.Equals(leaseCollectionLocation.CollectionName)) { throw new InvalidOperationException("The monitored collection cannot be the same as the collection storing the leases."); } monitoredCosmosDBService = _configProvider.GetService(triggerConnectionString, resolvedPreferredLocations); leaseCosmosDBService = _configProvider.GetService(leasesConnectionString, resolvedPreferredLocations); if (attribute.CreateLeaseCollectionIfNotExists) { await CreateLeaseCollectionIfNotExistsAsync(leaseCosmosDBService, leaseCollectionLocation.DatabaseName, leaseCollectionLocation.CollectionName, attribute.LeasesCollectionThroughput); } } catch (Exception ex) { throw new InvalidOperationException(string.Format("Cannot create Collection Information for {0} in database {1} with lease {2} in database {3} : {4}", attribute.CollectionName, attribute.DatabaseName, attribute.LeaseCollectionName, attribute.LeaseDatabaseName, ex.Message), ex); } return(new CosmosDBTriggerBinding( parameter, documentCollectionLocation, leaseCollectionLocation, processorOptions, monitoredCosmosDBService, leaseCosmosDBService, _logger)); }
public async Task <ITriggerBinding> TryCreateAsync(TriggerBindingProviderContext context) { // Tries to parse the context parameters and see if it belongs to this [CosmosDBTrigger] binder if (context == null) { throw new ArgumentNullException("context"); } ParameterInfo parameter = context.Parameter; CosmosDBTriggerAttribute attribute = parameter.GetCustomAttribute <CosmosDBTriggerAttribute>(inherit: false); if (attribute == null) { return(null); } _monitorConnectionString = _nameResolver.Resolve(CosmosDBConfiguration.AzureWebJobsCosmosDBConnectionStringName); _leasesConnectionString = _nameResolver.Resolve(CosmosDBConfiguration.AzureWebJobsCosmosDBConnectionStringName); DocumentCollectionInfo documentCollectionLocation; DocumentCollectionInfo leaseCollectionLocation; ChangeFeedHostOptions leaseHostOptions = ResolveLeaseOptions(attribute); int?maxItemCount = null; if (attribute.MaxItemsPerInvocation > 0) { maxItemCount = attribute.MaxItemsPerInvocation; } try { string triggerConnectionString = ResolveAttributeConnectionString(attribute); CosmosDBConnectionString triggerConnection = new CosmosDBConnectionString(triggerConnectionString); if (triggerConnection.ServiceEndpoint == null) { throw new InvalidOperationException("The connection string for the monitored collection is in an invalid format, please use AccountEndpoint=XXXXXX;AccountKey=XXXXXX;."); } string leasesConnectionString = ResolveAttributeLeasesConnectionString(attribute, triggerConnectionString); CosmosDBConnectionString leasesConnection = new CosmosDBConnectionString(leasesConnectionString); if (leasesConnection.ServiceEndpoint == null) { throw new InvalidOperationException("The connection string for the leases collection is in an invalid format, please use AccountEndpoint=XXXXXX;AccountKey=XXXXXX;."); } documentCollectionLocation = new DocumentCollectionInfo { Uri = triggerConnection.ServiceEndpoint, MasterKey = triggerConnection.AuthKey, DatabaseName = ResolveAttributeValue(attribute.DatabaseName), CollectionName = ResolveAttributeValue(attribute.CollectionName) }; documentCollectionLocation.ConnectionPolicy.UserAgentSuffix = CosmosDBTriggerUserAgentSuffix; leaseCollectionLocation = new DocumentCollectionInfo { Uri = leasesConnection.ServiceEndpoint, MasterKey = leasesConnection.AuthKey, DatabaseName = ResolveAttributeValue(attribute.LeaseDatabaseName), CollectionName = ResolveAttributeValue(attribute.LeaseCollectionName) }; leaseCollectionLocation.ConnectionPolicy.UserAgentSuffix = CosmosDBTriggerUserAgentSuffix; if (string.IsNullOrEmpty(documentCollectionLocation.DatabaseName) || string.IsNullOrEmpty(documentCollectionLocation.CollectionName) || string.IsNullOrEmpty(leaseCollectionLocation.DatabaseName) || string.IsNullOrEmpty(leaseCollectionLocation.CollectionName)) { throw new InvalidOperationException("Cannot establish database and collection values. If you are using environment and configuration values, please ensure these are correctly set."); } if (documentCollectionLocation.Uri.Equals(leaseCollectionLocation.Uri) && documentCollectionLocation.DatabaseName.Equals(leaseCollectionLocation.DatabaseName) && documentCollectionLocation.CollectionName.Equals(leaseCollectionLocation.CollectionName)) { throw new InvalidOperationException("The monitored collection cannot be the same as the collection storing the leases."); } if (attribute.CreateLeaseCollectionIfNotExists) { // Not disposing this because it might be reused on other Trigger since Triggers could share lease collection ICosmosDBService service = _config.GetService(leasesConnectionString); await CosmosDBUtility.CreateDatabaseAndCollectionIfNotExistAsync(service, leaseCollectionLocation.DatabaseName, leaseCollectionLocation.CollectionName, null, attribute.LeasesCollectionThroughput); } } catch (Exception ex) { throw new InvalidOperationException(string.Format("Cannot create Collection Information for {0} in database {1} with lease {2} in database {3} : {4}", attribute.CollectionName, attribute.DatabaseName, attribute.LeaseCollectionName, attribute.LeaseDatabaseName, ex.Message), ex); } return(new CosmosDBTriggerBinding(parameter, documentCollectionLocation, leaseCollectionLocation, leaseHostOptions, maxItemCount)); }