private async Task <(IEnumerable <OrganizationSponsorshipData> data, IEnumerable <OrganizationSponsorship> toOffer)> DoSyncAsync(Organization sponsoringOrg, IEnumerable <OrganizationSponsorshipData> sponsorshipsData) { var existingSponsorshipsDict = (await _organizationSponsorshipRepository.GetManyBySponsoringOrganizationAsync(sponsoringOrg.Id)) .ToDictionary(i => i.SponsoringOrganizationUserId); var sponsorshipsToUpsert = new List <OrganizationSponsorship>(); var sponsorshipIdsToDelete = new List <Guid>(); var sponsorshipsToReturn = new List <OrganizationSponsorshipData>(); foreach (var selfHostedSponsorship in sponsorshipsData) { var requiredSponsoringProductType = StaticStore.GetSponsoredPlan(selfHostedSponsorship.PlanSponsorshipType)?.SponsoringProductType; if (requiredSponsoringProductType == null || StaticStore.GetPlan(sponsoringOrg.PlanType).Product != requiredSponsoringProductType.Value) { continue; // prevent unsupported sponsorships } if (!existingSponsorshipsDict.TryGetValue(selfHostedSponsorship.SponsoringOrganizationUserId, out var cloudSponsorship)) { if (selfHostedSponsorship.ToDelete && selfHostedSponsorship.LastSyncDate == null) { continue; // prevent invalid sponsorships in cloud. These should have been deleted by self hosted } if (OrgDisabledForMoreThanGracePeriod(sponsoringOrg)) { continue; // prevent new sponsorships from disabled orgs } cloudSponsorship = new OrganizationSponsorship { SponsoringOrganizationId = sponsoringOrg.Id, SponsoringOrganizationUserId = selfHostedSponsorship.SponsoringOrganizationUserId, FriendlyName = selfHostedSponsorship.FriendlyName, OfferedToEmail = selfHostedSponsorship.OfferedToEmail, PlanSponsorshipType = selfHostedSponsorship.PlanSponsorshipType, LastSyncDate = DateTime.UtcNow, }; } else { cloudSponsorship.LastSyncDate = DateTime.UtcNow; } if (selfHostedSponsorship.ToDelete) { if (cloudSponsorship.SponsoredOrganizationId == null) { sponsorshipIdsToDelete.Add(cloudSponsorship.Id); selfHostedSponsorship.CloudSponsorshipRemoved = true; } else { cloudSponsorship.ToDelete = true; } } sponsorshipsToUpsert.Add(cloudSponsorship); selfHostedSponsorship.ValidUntil = cloudSponsorship.ValidUntil; selfHostedSponsorship.LastSyncDate = DateTime.UtcNow; sponsorshipsToReturn.Add(selfHostedSponsorship); } var sponsorshipsToEmailOffer = sponsorshipsToUpsert.Where(s => s.Id == default).ToArray(); if (sponsorshipsToUpsert.Any()) { await _organizationSponsorshipRepository.UpsertManyAsync(sponsorshipsToUpsert); } if (sponsorshipIdsToDelete.Any()) { await _organizationSponsorshipRepository.DeleteManyAsync(sponsorshipIdsToDelete); } return(sponsorshipsToReturn, sponsorshipsToEmailOffer); }
public async Task SyncOrganization(Guid organizationId, Guid cloudOrganizationId, OrganizationConnection billingSyncConnection) { if (!_globalSettings.EnableCloudCommunication) { throw new BadRequestException("Failed to sync instance with cloud - Cloud communication is disabled in global settings"); } if (!billingSyncConnection.Enabled) { throw new BadRequestException($"Billing Sync Key disabled for organization {organizationId}"); } if (string.IsNullOrWhiteSpace(billingSyncConnection.Config)) { throw new BadRequestException($"No Billing Sync Key known for organization {organizationId}"); } var billingSyncConfig = billingSyncConnection.GetConfig <BillingSyncConfig>(); if (billingSyncConfig == null || string.IsNullOrWhiteSpace(billingSyncConfig.BillingSyncKey)) { throw new BadRequestException($"Failed to get Billing Sync Key for organization {organizationId}"); } var organizationSponsorshipsDict = (await _organizationSponsorshipRepository.GetManyBySponsoringOrganizationAsync(organizationId)) .ToDictionary(i => i.SponsoringOrganizationUserId); if (!organizationSponsorshipsDict.Any()) { _logger.LogInformation($"No existing sponsorships to sync for organization {organizationId}"); return; } var syncedSponsorships = new List <OrganizationSponsorshipData>(); foreach (var orgSponsorshipsBatch in CoreHelpers.Batch(organizationSponsorshipsDict.Values, 1000)) { var response = await SendAsync <OrganizationSponsorshipSyncRequestModel, OrganizationSponsorshipSyncResponseModel>(HttpMethod.Post, "organization/sponsorship/sync", new OrganizationSponsorshipSyncRequestModel { BillingSyncKey = billingSyncConfig.BillingSyncKey, SponsoringOrganizationCloudId = cloudOrganizationId, SponsorshipsBatch = orgSponsorshipsBatch.Select(s => new OrganizationSponsorshipRequestModel(s)) }); if (response == null) { _logger.LogDebug("Organization sync failed for '{OrgId}'", organizationId); throw new BadRequestException("Organization sync failed"); } syncedSponsorships.AddRange(response.ToOrganizationSponsorshipSync().SponsorshipsBatch); } var sponsorshipsToDelete = syncedSponsorships.Where(s => s.CloudSponsorshipRemoved).Select(i => organizationSponsorshipsDict[i.SponsoringOrganizationUserId].Id); var sponsorshipsToUpsert = syncedSponsorships.Where(s => !s.CloudSponsorshipRemoved).Select(i => { var existingSponsorship = organizationSponsorshipsDict[i.SponsoringOrganizationUserId]; if (existingSponsorship != null) { existingSponsorship.LastSyncDate = i.LastSyncDate; existingSponsorship.ValidUntil = i.ValidUntil; existingSponsorship.ToDelete = i.ToDelete; } else { // shouldn't occur, added in case self hosted loses a sponsorship existingSponsorship = new OrganizationSponsorship { SponsoringOrganizationId = organizationId, SponsoringOrganizationUserId = i.SponsoringOrganizationUserId, FriendlyName = i.FriendlyName, OfferedToEmail = i.OfferedToEmail, PlanSponsorshipType = i.PlanSponsorshipType, LastSyncDate = i.LastSyncDate, ValidUntil = i.ValidUntil, ToDelete = i.ToDelete }; } return(existingSponsorship); }); if (sponsorshipsToDelete.Any()) { await _organizationSponsorshipRepository.DeleteManyAsync(sponsorshipsToDelete); } if (sponsorshipsToUpsert.Any()) { await _organizationSponsorshipRepository.UpsertManyAsync(sponsorshipsToUpsert); } }