/// <summary>
        /// Gets a complete list of customers associated with the partner.
        /// </summary>
        /// <param name="client">Provides the ability to interact with Partner Center.</param>
        /// <param name="environment">The environment that owns the customers be requesteed.</param>
        /// <returns>A list of customers associated with the partner.</returns>
        private static async Task <List <CustomerDetail> > GetCustomersAsync(
            IPartnerServiceClient client,
            EnvironmentDetail environment)
        {
            List <CustomerDetail> customers;
            SeekBasedResourceCollection <Customer> seekCustomers;

            try
            {
                // Request a list of customers from Partner Center.
                seekCustomers = await client.Customers.GetAsync().ConfigureAwait(false);

                customers = new List <CustomerDetail>(
                    seekCustomers.Items.Select(c => ResourceConverter.Convert <Customer, CustomerDetail>(c)));

                while (seekCustomers.Links.Next != null)
                {
                    // Request the next page of customers from Partner Center.
                    seekCustomers = await client.Customers.GetAsync(seekCustomers.Links.Next).ConfigureAwait(false);

                    customers.AddRange(seekCustomers.Items.Select(c => ResourceConverter.Convert <Customer, CustomerDetail>(c)));
                }

                customers.ForEach(c => c.EnvironmentId = environment.Id);

                return(customers);
            }
            finally
            {
                seekCustomers = null;
            }
        }
        private static async Task <List <AuditRecord> > GetAuditRecordsAsyc(
            IPartnerServiceClient client,
            DateTime startDate,
            DateTime endDate)
        {
            List <AuditRecord> auditRecords;
            SeekBasedResourceCollection <AuditRecord> seekAuditRecords;

            try
            {
                auditRecords = new List <AuditRecord>();

                foreach (DateTime date in ChunkDate(startDate, endDate, 30))
                {
                    // Request the audit records for the previous day from Partner Center.
                    seekAuditRecords = await client.AuditRecords.QueryAsync(date).ConfigureAwait(false);

                    auditRecords.AddRange(seekAuditRecords.Items);

                    while (seekAuditRecords.Links.Next != null)
                    {
                        // Request the next page of audit records from Partner Center.
                        seekAuditRecords = await client.AuditRecords.QueryAsync(seekAuditRecords.Links.Next).ConfigureAwait(false);

                        auditRecords.AddRange(seekAuditRecords.Items);
                    }
                }

                return(auditRecords);
            }
            finally
            {
                seekAuditRecords = null;
            }
        }
Example #3
0
        /// <summary>
        /// Gets a complete list of customers associated with the partner.
        /// </summary>
        /// <param name="client">Provides the ability to interact with Partner Center.</param>
        /// <param name="environment">The environment that owns the customers be requesteed.</param>
        /// <returns>A list of customers associated with the partner.</returns>
        private static async Task <List <CustomerDetail> > GetCustomersAsync(
            IPartnerServiceClient client,
            EnvironmentDetail environment)
        {
            Customer customer;
            Customer justOne;
            List <CustomerDetail> customers;
            SeekBasedResourceCollection <Customer> seekCustomers;

            try
            {
                // Request a list of customers from Partner Center.
                //seekCustomers = await client.Customers.GetAsync().ConfigureAwait(false);
                //
                //customers = new List<CustomerDetail>(
                //    seekCustomers.Items.Select(c => ResourceConverter.Convert<Customer, CustomerDetail>(c)));
                //
                //while (seekCustomers.Links.Next == null)
                //{
                // Request the next page of customers from Partner Center.
                //    seekCustomers = await client.Customers.GetAsync(seekCustomers.Links.Next).ConfigureAwait(false);

                //    customers.AddRange(seekCustomers.Items.Select(c => ResourceConverter.Convert<Customer, CustomerDetail>(c)));
                //}

                //customers.ForEach(c => c.EnvironmentId = environment.Id);
                customers = new List <CustomerDetail>();
                justOne   = await client.Customers[environment.Id].GetAsync().ConfigureAwait(false);
                customers.Add(ResourceConverter.Convert <Customer, CustomerDetail>(justOne));

                foreach (CustomerDetail c in customers)
                {
                    try
                    {
                        customer         = await client.Customers[c.Id].GetAsync().ConfigureAwait(false);
                        c.BillingProfile = customer.BillingProfile;
                        c.EnvironmentId  = environment.Id;

                        if (c.RemovedFromPartnerCenter == true)
                        {
                            c.LastProcessed = null;
                        }
                        c.RemovedFromPartnerCenter = false;
                    }
                    catch (ServiceClientException ex)
                    {
                        c.ProcessException = ex;
                    }
                }

                return(customers);
            }
            finally
            {
                seekCustomers = null;
            }
        }
        /// <summary>
        /// Gets a complete list of customers associated with the partner.
        /// </summary>
        /// <param name="client">Provides the ability to interact with Partner Center.</param>
        /// <returns>A list of customers associated with the partner.</returns>
        private static async Task <List <CustomerDetail> > GetCustomersAsync(IPartnerServiceClient client)
        {
            Customer customer;
            List <CustomerDetail> customers;
            SeekBasedResourceCollection <Customer> seekCustomers;

            try
            {
                // Request a list of customers from Partner Center.
                seekCustomers = await client.Customers.GetAsync().ConfigureAwait(false);

                customers = new List <CustomerDetail>(
                    seekCustomers.Items.Select(c => ResourceConverter.Convert <Customer, CustomerDetail>(c)));

                while (seekCustomers.Links.Next != null)
                {
                    // Request the next page of customers from Partner Center.
                    seekCustomers = await client.Customers.GetAsync(seekCustomers.Links.Next).ConfigureAwait(false);

                    customers.AddRange(seekCustomers.Items.Select(c => ResourceConverter.Convert <Customer, CustomerDetail>(c)));
                }

                foreach (CustomerDetail c in customers)
                {
                    try
                    {
                        customer         = await client.Customers[c.Id].GetAsync().ConfigureAwait(false);
                        c.BillingProfile = customer.BillingProfile;
                    }
                    catch (ServiceClientException ex)
                    {
                        c.ProcessException = ex;
                    }
                }

                return(customers);
            }
            finally
            {
                seekCustomers = null;
            }
        }
        /// <summary>
        /// Gets a list of subscriptions for the specified customer.
        /// </summary>
        /// <param name="client">Provides the ability to interact with Partner Center.</param>
        /// <param name="customerId">Identifier for the customer.</param>
        /// <returns>A list of subscriptions for the specified customer.</returns>
        private static async Task <List <SubscriptionDetail> > GetSubscriptionsAsync(IPartnerServiceClient client, string customerId)
        {
            List <SubscriptionDetail> subscriptions;
            SeekBasedResourceCollection <Subscription> seekSubscriptions;

            try
            {
                // Request a list of subscriptions from the Partner Center API.
                seekSubscriptions = await client.Customers.ById(customerId).Subscriptions.GetAsync().ConfigureAwait(false);

                subscriptions = new List <SubscriptionDetail>(
                    seekSubscriptions.Items
                    .Select(s => ResourceConverter.Convert <Subscription, SubscriptionDetail>(
                                s,
                                new Dictionary <string, string> {
                    { "TenantId", customerId }
                })));

                while (seekSubscriptions.Links.Next != null)
                {
                    // Request the next page of subscriptions from the Partner Center API.
                    seekSubscriptions = await client.Customers
                                        .ById(customerId)
                                        .Subscriptions
                                        .GetAsync(seekSubscriptions.Links.Next).ConfigureAwait(false);

                    subscriptions.AddRange(seekSubscriptions.Items
                                           .Select(s => ResourceConverter.Convert <Subscription, SubscriptionDetail>(
                                                       s,
                                                       new Dictionary <string, string> {
                        { "TenantId", customerId }
                    })));
                }

                return(subscriptions);
            }
            finally
            {
                seekSubscriptions = null;
            }
        }
        /// <summary>
        /// Converts the audit records to a list of customer details.
        /// </summary>
        /// <param name="client">Provides the ability to interface with Partner Center.</param>
        /// <param name="records">A list of audit records from Partner Center.</param>
        /// <param name="details">A list of existing customer details.</param>
        /// <param name="additionalInfo">Additional information to be added to the converted customer details.</param>
        /// <returns>
        /// A list of customer details that incorporates the changes reflected by the audit records.
        /// </returns>
        public async static Task <List <CustomerDetail> > ConvertAsync(
            IPartnerServiceClient client,
            List <AuditRecord> records,
            List <CustomerDetail> details,
            Dictionary <string, string> additionalInfo)
        {
            Customer                  resource;
            CustomerDetail            control;
            IEnumerable <AuditRecord> filtered;
            List <CustomerDetail>     results;

            try
            {
                // Filter the audit records, so that only records for successful operations are considered.
                filtered = records
                           .Where(r => r.OperationStatus == OperationStatus.Succeeded && !string.IsNullOrEmpty(r.CustomerId))
                           .OrderBy(r => r.OperationDate);

                results = new List <CustomerDetail>(details);

                foreach (AuditRecord record in filtered)
                {
                    control = results.SingleOrDefault(r => r.Id.Equals(record.CustomerId, StringComparison.InvariantCultureIgnoreCase));

                    /*
                     * If the control variable is null and the operation type value is not equal
                     * to AddCustomer, then that means we have a customer that accepted the reseller
                     * relationship. Currently there is not an audit record for when a customer
                     * accepts the reseller relationship.
                     */

                    if (control == null && record.OperationType != OperationType.AddCustomer)
                    {
                        resource = await client.Customers[record.CustomerId].GetAsync().ConfigureAwait(false);

                        results.Add(
                            ResourceConverter.Convert <Customer, CustomerDetail>(
                                resource,
                                additionalInfo));
                    }
                    else if (control != null)
                    {
                        control.RemovedFromPartnerCenter = false;
                    }

                    if (record.OperationType == OperationType.AddCustomer)
                    {
                        resource = Convert <Customer>(record);

                        results.Add(
                            ResourceConverter.Convert <Customer, CustomerDetail>(
                                resource,
                                additionalInfo));
                    }
                    else if (record.OperationType == OperationType.RemovePartnerCustomerRelationship)
                    {
                        control.RemovedFromPartnerCenter = true;
                    }
                    else if (record.OperationType == OperationType.UpdateCustomerBillingProfile)
                    {
                        control.BillingProfile = Convert <CustomerBillingProfile>(record);
                    }
                }

                return(results);
            }
            finally
            {
                control  = null;
                filtered = null;
                resource = null;
            }
        }
        private static async Task <List <SubscriptionDetail> > ConvertAsync(
            IPartnerServiceClient partner,
            CustomerDetail customer,
            Order order)
        {
            DateTime                  effectiveStartDate;
            DateTimeOffset            creationDate;
            List <SubscriptionDetail> details;
            Offer offer;

            try
            {
                if (order.BillingCycle == BillingCycleType.OneTime)
                {
                    return(null);
                }

                details = new List <SubscriptionDetail>();

                foreach (OrderLineItem lineItem in order.LineItems)
                {
                    creationDate = order.CreationDate.Value;

                    effectiveStartDate = new DateTime(
                        creationDate.UtcDateTime.Year,
                        creationDate.UtcDateTime.Month,
                        creationDate.UtcDateTime.Day);

                    offer = await partner.Offers
                            .ByCountry(customer.BillingProfile.DefaultAddress.Country).ById(lineItem.OfferId)
                            .GetAsync().ConfigureAwait(false);

                    details.Add(new SubscriptionDetail
                    {
                        AutoRenewEnabled  = offer.IsAutoRenewable,
                        BillingCycle      = order.BillingCycle,
                        BillingType       = offer.Billing,
                        CommitmentEndDate = (offer.Billing == BillingType.License) ?
                                            effectiveStartDate.AddYears(1) : DateTime.Parse("9999-12-14T00:00:00Z", CultureInfo.CurrentCulture),
                        CreationDate       = creationDate.UtcDateTime,
                        EffectiveStartDate = effectiveStartDate,
                        FriendlyName       = lineItem.FriendlyName,
                        Id                   = lineItem.SubscriptionId,
                        OfferId              = lineItem.OfferId,
                        OfferName            = offer.Name,
                        ParentSubscriptionId = lineItem.ParentSubscriptionId,
                        PartnerId            = lineItem.PartnerIdOnRecord,
                        Quantity             = lineItem.Quantity,
                        Status               = SubscriptionStatus.Active,
                        SuspensionReasons    = null,
                        TenantId             = customer.Id,
                        UnitType             = offer.UnitType
                    });
                }

                return(details);
            }
            finally
            {
                offer = null;
            }
        }
        /// <summary>
        /// Converts the audit records to a list of subscription details.
        /// </summary>
        /// <param name="client">Provides the ability to interface with Partner Center.</param>
        /// <param name="records">A list of audit records from Partner Center.</param>
        /// <param name="details">A list of existing subscription details.</param>
        /// <param name="customer">The details for the customer that owns the subscription.</param>
        /// <returns>
        /// A list of subscription details that incorporates the changes reflected by the audit records.
        /// </returns>
        public static async Task <List <SubscriptionDetail> > ConvertAsync(
            IPartnerServiceClient client,
            IEnumerable <AuditRecord> auditRecords,
            List <SubscriptionDetail> details,
            CustomerDetail customer)
        {
            IEnumerable <AuditRecord> filteredRecords;
            List <SubscriptionDetail> fromOrders;
            List <SubscriptionDetail> results;
            SubscriptionDetail        control;
            Subscription resource;

            try
            {
                // Extract a list of audit records that are scope to the defined resource type and were successful.
                filteredRecords = auditRecords
                                  .Where(r => (r.ResourceType == ResourceType.Subscription || r.ResourceType == ResourceType.Order) &&
                                         r.OperationStatus == OperationStatus.Succeeded)
                                  .OrderBy(r => r.OperationDate);

                results = new List <SubscriptionDetail>(details);

                foreach (AuditRecord record in filteredRecords)
                {
                    if (record.ResourceType == ResourceType.Order)
                    {
                        fromOrders = await ConvertAsync(
                            client,
                            customer,
                            Convert <Order>(record)).ConfigureAwait(false);

                        if (fromOrders != null)
                        {
                            results.AddRange(fromOrders);
                        }
                    }
                    else if (record.ResourceType == ResourceType.Subscription)
                    {
                        resource = Convert <Subscription>(record);
                        control  = results.SingleOrDefault(r => r.Id.Equals(resource.Id, StringComparison.InvariantCultureIgnoreCase));

                        if (control != null)
                        {
                            results.Remove(control);
                        }

                        results.Add(
                            ResourceConverter.Convert <Subscription, SubscriptionDetail>(
                                resource,
                                new Dictionary <string, string> {
                            { "TenantId", customer.Id }
                        }));
                    }
                }

                return(results);
            }
            finally
            {
                control         = null;
                filteredRecords = null;
                fromOrders      = null;
                resource        = null;
            }
        }
Example #9
0
 /// <summary>
 /// Initializes a new instance of the <see cref="ResourceConverter{TInput, TOutput}" /> class.
 /// </summary>
 /// <param name="client">Provides the ability to interact with Partner Center.</param>
 public ResourceConverter(IPartnerServiceClient client)
 {
     this.client = client;
 }
        public static async Task ProcessCustomerAsync(
            [QueueTrigger(OperationConstants.CustomersQueueName, Connection = "StorageConnectionString")] ProcessCustomerDetail customerDetail,
            [DataRepository(DataType = typeof(AuditRecord))] DocumentRepository <AuditRecord> auditRecordRepository,
            [DataRepository(DataType = typeof(CustomerDetail))] DocumentRepository <CustomerDetail> customerRepository,
            [DataRepository(DataType = typeof(SubscriptionDetail))] DocumentRepository <SubscriptionDetail> subscriptionRepository,
            [PartnerService(
                 ApplicationId = "{PartnerCenterEndpoint.ApplicationId}",
                 Endpoint = "{PartnerCenterEndpoint.ServiceAddress}",
                 SecretName = "{PartnerCenterEndpoint.ApplicationSecretId}",
                 ApplicationTenantId = "{PartnerCenterEndpoint.TenantId}",
                 Resource = "https://graph.windows.net")] IPartnerServiceClient partner,
            [Queue(OperationConstants.SecurityQueueName, Connection = "StorageConnectionString")] ICollector <SecurityDetail> securityQueue,
            [Queue(OperationConstants.UtilizationQueueName, Connection = "StorageConnectionString")] ICollector <ProcessSubscriptionDetail> utilizationQueue,
            ILogger log)
        {
            IEnumerable <AuditRecord> auditRecords;
            List <SubscriptionDetail> subscriptions;
            int period;

            try
            {
                log.LogInformation($"Attempting to process information for {customerDetail.Customer.Id}.");

                if (customerDetail.Customer.RemovedFromPartnerCenter)
                {
                    // The customer no longer has relationship with the partner. So, it should not be processed.
                    return;
                }

                // Configure the value indicating the number of days of score results to retrieve starting from current date.
                period = (customerDetail.Customer.LastProcessed == null) ? 30 : (DateTimeOffset.UtcNow - customerDetail.Customer.LastProcessed).Value.Days;

                // Ensure that the period is at least 1 or greater. This ensure the request to retrieve data is succesfully.
                if (period < 1)
                {
                    period = 1;
                }

                if (period >= 30)
                {
                    period = 30;

                    try
                    {
                        customerDetail.Customer.ProcessException = null;
                        subscriptions = await GetSubscriptionsAsync(
                            partner,
                            customerDetail.Customer.Id).ConfigureAwait(false);
                    }
                    catch (ServiceClientException ex)
                    {
                        customerDetail.Customer.ProcessException = ex;
                        subscriptions = null;

                        log.LogWarning($"Encountered an exception when processing {customerDetail.Customer.Id}. Check the customer record for more information.");
                    }
                }
                else
                {
                    // Obtain a list of audit records for the specified customer that happened since the customer was last processed.
                    auditRecords = await auditRecordRepository.GetAsync(
                        r => r.CustomerId == customerDetail.Customer.Id &&
                        r.OperationDate >= customerDetail.Customer.LastProcessed,
                        customerDetail.Customer.EnvironmentId).ConfigureAwait(false);

                    subscriptions = await subscriptionRepository
                                    .GetAsync(s => s.TenantId == customerDetail.Customer.Id, customerDetail.Customer.Id)
                                    .ConfigureAwait(false);

                    // Since the period is less than 30 we can utilize the audit logs to reconstruct any subscriptions that were created.
                    subscriptions = await AuditRecordConverter.ConvertAsync(
                        partner,
                        auditRecords,
                        subscriptions,
                        customerDetail.Customer).ConfigureAwait(false);
                }

                if (subscriptions?.Count > 0)
                {
                    await subscriptionRepository.AddOrUpdateAsync(
                        subscriptions,
                        customerDetail.Customer.Id).ConfigureAwait(false);
                }

                if (customerDetail.Customer.ProcessException == null)
                {
                    securityQueue.Add(new SecurityDetail
                    {
                        AppEndpoint = customerDetail.AppEndpoint,
                        Customer    = customerDetail.Customer,
                        Period      = period.ToString(CultureInfo.InvariantCulture)
                    });

                    if (customerDetail.ProcessAzureUsage)
                    {
                        foreach (SubscriptionDetail subscription in subscriptions.Where(s => s.BillingType == BillingType.Usage))
                        {
                            utilizationQueue.Add(new ProcessSubscriptionDetail
                            {
                                PartnerCenterEndpoint = customerDetail.PartnerCenterEndpoint,
                                Subscription          = subscription
                            });
                        }
                    }

                    customerDetail.Customer.LastProcessed = DateTimeOffset.UtcNow;
                }

                await customerRepository.AddOrUpdateAsync(customerDetail.Customer).ConfigureAwait(false);

                log.LogInformation($"Successfully processed customer {customerDetail.Customer.Id}. Exception(s): {(customerDetail.Customer.ProcessException != null ? "yes" : "no")}");
            }
            finally
            {
                auditRecords  = null;
                subscriptions = null;
            }
        }
        public static async Task ProcessUsageAsync(
            [QueueTrigger(OperationConstants.UtilizationQueueName, Connection = "StorageConnectionString")] ProcessSubscriptionDetail subscriptionDetail,
            [DataRepository(
                 DataType = typeof(UtilizationDetail))] DocumentRepository <UtilizationDetail> repository,
            [PartnerService(
                 ApplicationId = "{PartnerCenterEndpoint.ApplicationId}",
                 Endpoint = "{PartnerCenterEndpoint.ServiceAddress}",
                 SecretName = "{PartnerCenterEndpoint.ApplicationSecretId}",
                 ApplicationTenantId = "{PartnerCenterEndpoint.TenantId}",
                 Resource = "https://graph.windows.net")] IPartnerServiceClient client,
            ILogger log)
        {
            // Subscriptions with a billing type of usage are the only ones that have utilization records.
            if (subscriptionDetail.Subscription.BillingType != BillingType.Usage)
            {
                return;
            }

            log.LogInformation($"Requesting utilization records for {subscriptionDetail.Subscription.Id}");

            List <UtilizationDetail> records;
            ResourceCollection <AzureUtilizationRecord> utilizationRecords;

            try
            {
                utilizationRecords = await client
                                     .Customers[subscriptionDetail.Subscription.TenantId]
                                     .Subscriptions[subscriptionDetail.Subscription.Id]
                                     .Utilization
                                     .Azure
                                     .QueryAsync(DateTimeOffset.UtcNow.AddDays(-1), DateTimeOffset.UtcNow).ConfigureAwait(false);

                records = new List <UtilizationDetail>();

                if (utilizationRecords.TotalCount > 0)
                {
                    records.AddRange(utilizationRecords.Items
                                     .Select(r => ResourceConverter.Convert <AzureUtilizationRecord, UtilizationDetail>(
                                                 r,
                                                 new Dictionary <string, string>
                    {
                        { "Id", $"{r.Resource.Id}--{r.UsageStartTime}" },
                        { "SubscriptionId", subscriptionDetail.Subscription.Id },
                        { "TenantId", subscriptionDetail.Subscription.TenantId }
                    })));

                    while (utilizationRecords.Links.Next != null)
                    {
                        utilizationRecords = await client
                                             .Customers[subscriptionDetail.Subscription.TenantId]
                                             .Subscriptions[subscriptionDetail.Subscription.Id]
                                             .Utilization
                                             .Azure
                                             .QueryAsync(utilizationRecords.Links.Next).ConfigureAwait(false);

                        records.AddRange(utilizationRecords.Items
                                         .Select(r => ResourceConverter.Convert <AzureUtilizationRecord, UtilizationDetail>(
                                                     r,
                                                     new Dictionary <string, string>
                        {
                            { "Id", $"{r.Resource.Id}--{r.UsageStartTime}" },
                            { "SubscriptionId", subscriptionDetail.Subscription.Id },
                            { "TenantId", subscriptionDetail.Subscription.TenantId }
                        })));
                    }
                }

                if (records.Count > 0)
                {
                    log.LogInformation($"Writing {records.Count} utilization records to the repository.");

                    await repository.AddOrUpdateAsync(
                        records,
                        subscriptionDetail.Subscription.Id).ConfigureAwait(false);
                }
            }
            finally
            {
                records            = null;
                utilizationRecords = null;
            }
        }
        public static async Task ProcessPartnerAsync(
            [QueueTrigger(OperationConstants.PartnersQueueName, Connection = "StorageConnectionString")] EnvironmentDetail environment,
            [DataRepository(DataType = typeof(AuditRecord))] DocumentRepository <AuditRecord> auditRecordRepository,
            [DataRepository(DataType = typeof(CustomerDetail))] DocumentRepository <CustomerDetail> customerRepository,
            [DataRepository(
                 DataType = typeof(EnvironmentDetail))] DocumentRepository <EnvironmentDetail> environmentRepository,
            [PartnerService(
                 ApplicationId = "{PartnerCenterEndpoint.ApplicationId}",
                 Endpoint = "{PartnerCenterEndpoint.ServiceAddress}",
                 SecretName = "{PartnerCenterEndpoint.ApplicationSecretId}",
                 ApplicationTenantId = "{PartnerCenterEndpoint.TenantId}",
                 Resource = "https://graph.windows.net")] IPartnerServiceClient client,
            [Queue(OperationConstants.CustomersQueueName, Connection = "StorageConnectionString")] ICollector <ProcessCustomerDetail> customerQueue,
            ILogger log)
        {
            List <AuditRecord>    auditRecords;
            List <CustomerDetail> customers;
            int days;

            try
            {
                log.LogInformation($"Starting to process the {environment.FriendlyName} CSP environment.");

                // Calculate the number of days that have gone by, since the last successful synchronization.
                days = (environment.LastProcessed == null) ? 30 : (DateTimeOffset.UtcNow - environment.LastProcessed).Days;

                if (days >= 90)
                {
                    // Only audit records for the past 90 days are available from Partner Center.
                    days = 89;
                }
                else if (days <= 0)
                {
                    // Ensure that in all circumstances at least one day of records will be returned.
                    days = 1;
                }

                if (days >= 30)
                {
                    customers = await GetCustomersAsync(client, environment).ConfigureAwait(false);
                }
                else
                {
                    auditRecords = await GetAuditRecordsAsyc(
                        client,
                        DateTime.UtcNow.AddDays(-days),
                        DateTime.UtcNow).ConfigureAwait(false);

                    if (auditRecords.Count > 0)
                    {
                        log.LogInformation($"Importing {auditRecords.Count} audit records available between now and the previous day.");

                        // Add, or update, each audit record to the database.
                        await auditRecordRepository.AddOrUpdateAsync(
                            auditRecords,
                            environment.Id).ConfigureAwait(false);
                    }

                    customers = await customerRepository.GetAsync().ConfigureAwait(false);

                    customers = await AuditRecordConverter.ConvertAsync(
                        client,
                        auditRecords,
                        customers,
                        new Dictionary <string, string> {
                        { "EnvironmentId", environment.Id }
                    },
                        log).ConfigureAwait(false);
                }

                // Add, or update, each customer to the database.
                await customerRepository.AddOrUpdateAsync(customers).ConfigureAwait(false);

                foreach (CustomerDetail customer in customers)
                {
                    // Write the customer to the customers queue to start processing the customer.
                    customerQueue.Add(new ProcessCustomerDetail
                    {
                        AppEndpoint           = environment.AppEndpoint,
                        Customer              = customer,
                        PartnerCenterEndpoint = environment.PartnerCenterEndpoint,
                        ProcessAzureUsage     = environment.ProcessAzureUsage
                    });
                }

                environment.LastProcessed = DateTimeOffset.UtcNow;
                await environmentRepository.AddOrUpdateAsync(environment).ConfigureAwait(false);

                log.LogInformation($"Successfully process the {environment.FriendlyName} CSP environment.");
            }
            finally
            {
                auditRecords = null;
                customers    = null;
            }
        }
        public static async Task ProcessCustomerAsync(
            [QueueTrigger(OperationConstants.CustomersQueueName, Connection = "StorageConnectionString")] XEvent data,
            [DataRepository(
                 CosmosDbEndpoint = "CosmosDbEndpoint",
                 DataType = typeof(CustomerDetail),
                 KeyVaultEndpoint = "KeyVaultEndpoint")] IDocumentRepository <CustomerDetail> customerRepository,
            [DataRepository(
                 CosmosDbEndpoint = "CosmosDbEndpoint",
                 DataType = typeof(SubscriptionDetail),
                 KeyVaultEndpoint = "KeyVaultEndpoint")] IDocumentRepository <SubscriptionDetail> subscriptionRepository,
            [PartnerService(
                 ApplicationId = "{PartnerCenterEndpoint.ApplicationId}",
                 Endpoint = "{PartnerCenterEndpoint.ServiceAddress}",
                 SecretName = "{PartnerCenterEndpoint.ApplicationSecretId}",
                 ApplicationTenantId = "{PartnerCenterEndpoint.TenantId}",
                 KeyVaultEndpoint = "KeyVaultEndpoint",
                 Resource = "https://graph.windows.net")] IPartnerServiceClient partner,
            [StorageService(
                 ConnectionStringName = "StorageConnectionString",
                 KeyVaultEndpoint = "KeyVaultEndpoint")] IStorageService storage,
            TraceWriter log)
        {
            List <SubscriptionDetail> subscriptions;

            try
            {
                log.Info($"Processing data for {data.Customer.Id}");

                if (data.Customer.LastProcessed == null || (DateTimeOffset.UtcNow - data.Customer.LastProcessed).Value.TotalDays >= 30)
                {
                    subscriptions = await GetSubscriptionsAsync(partner, data.Customer.Id).ConfigureAwait(false);
                }
                else
                {
                    subscriptions = await BuildUsingAuditRecordsAsync(
                        data.AuditRecords,
                        subscriptionRepository,
                        partner,
                        data.Customer).ConfigureAwait(false);
                }

                if (subscriptions.Count > 0)
                {
                    await subscriptionRepository.AddOrUpdateAsync(subscriptions).ConfigureAwait(false);
                }

                data.Customer.LastProcessed    = DateTimeOffset.UtcNow;
                data.Customer.ProcessException = null;

                await customerRepository.AddOrUpdateAsync(data.Customer).ConfigureAwait(false);

                await storage.WriteToQueueAsync(
                    OperationConstants.SecurityQueueName,
                    new SecurityDetails
                {
                    AppEndpoint = data.AppEndpoint,
                    Customer    = data.Customer
                }).ConfigureAwait(false);

                log.Info($"Successfully process data for {data.Customer.Id}");
            }
            finally
            {
                subscriptions = null;
            }
        }
        public static async Task ProcessPartnerAsync(
            [QueueTrigger(OperationConstants.PartnersQueueName, Connection = "StorageConnectionString")] EnvironmentDetail environment,
            [DataRepository(
                 CosmosDbEndpoint = "CosmosDbEndpoint",
                 DataType = typeof(AuditRecord),
                 KeyVaultEndpoint = "KeyVaultEndpoint")] IDocumentRepository <AuditRecord> auditRecordRepository,
            [DataRepository(
                 CosmosDbEndpoint = "CosmosDbEndpoint",
                 DataType = typeof(CustomerDetail),
                 KeyVaultEndpoint = "KeyVaultEndpoint")] IDocumentRepository <CustomerDetail> customerRepository,
            [DataRepository(
                 CosmosDbEndpoint = "CosmosDbEndpoint",
                 DataType = typeof(EnvironmentDetail),
                 KeyVaultEndpoint = "KeyVaultEndpoint")] IDocumentRepository <EnvironmentDetail> environmentRepository,
            [PartnerService(
                 ApplicationId = "{PartnerCenterEndpoint.ApplicationId}",
                 Endpoint = "{PartnerCenterEndpoint.ServiceAddress}",
                 SecretName = "{PartnerCenterEndpoint.ApplicationSecretId}",
                 ApplicationTenantId = "{PartnerCenterEndpoint.TenantId}",
                 KeyVaultEndpoint = "KeyVaultEndpoint",
                 Resource = "https://graph.windows.net")] IPartnerServiceClient partner,
            [StorageService(
                 ConnectionStringName = "StorageConnectionString",
                 KeyVaultEndpoint = "KeyVaultEndpoint")] IStorageService storage,
            TraceWriter log)
        {
            List <AuditRecord>    auditRecords;
            List <CustomerDetail> customers;
            SeekBasedResourceCollection <AuditRecord> seekAuditRecords;

            try
            {
                log.Info($"Starting to process the {environment.FriendlyName} CSP environment.");

                // Request the audit records for the previous day from the Partner Center API.
                seekAuditRecords = await partner.AuditRecords.QueryAsync(DateTime.Now.AddDays(-1)).ConfigureAwait(false);

                auditRecords = new List <AuditRecord>(seekAuditRecords.Items);

                while (seekAuditRecords.Links.Next != null)
                {
                    // Request the next page of audit records from the Partner Center API.
                    seekAuditRecords = await partner.AuditRecords.QueryAsync(seekAuditRecords.Links.Next).ConfigureAwait(false);

                    auditRecords.AddRange(seekAuditRecords.Items);
                }

                if (auditRecords.Count > 0)
                {
                    // Add, or update, each audit record to the database.
                    await auditRecordRepository.AddOrUpdateAsync(auditRecords).ConfigureAwait(false);
                }

                if ((DateTimeOffset.UtcNow - environment?.LastProcessed).Value.TotalDays >= 30)
                {
                    customers = await GetCustomersAsync(partner).ConfigureAwait(false);
                }
                else
                {
                    customers = await BuildUsingAuditRecordsAsync(
                        auditRecords,
                        customerRepository).ConfigureAwait(false);
                }

                // Add, or update, each customer to the database.
                await customerRepository.AddOrUpdateAsync(customers).ConfigureAwait(false);

                foreach (CustomerDetail customer in customers)
                {
                    // Write the customer to the customers queue to start processing the customer.
                    await storage.WriteToQueueAsync(
                        OperationConstants.CustomersQueueName,
                        new XEvent
                    {
                        AppEndpoint  = environment.AppEndpoint,
                        AuditRecords = auditRecords
                                       .Where(r => r.CustomerId.Equals(customer.Id, StringComparison.InvariantCultureIgnoreCase))
                                       .ToList(),
                        Customer = customer,
                        PartnerCenterEndpoint = environment.PartnerCenterEndpoint
                    }).ConfigureAwait(false);
                }

                environment.LastProcessed = DateTimeOffset.UtcNow;
                await environmentRepository.AddOrUpdateAsync(environment).ConfigureAwait(false);

                log.Info($"Successfully process the {environment.FriendlyName} CSP environment.");
            }
            finally
            {
                auditRecords     = null;
                customers        = null;
                seekAuditRecords = null;
            }
        }