private async Task GetCustomerFolderAsync(OrgName name, OrgId id, CancellationToken cancellationToken) { if (!OrgSlug.TryFrom(name, out var canonicalSlug) || !OrgSlug.TryFrom(name, id, out var collisionSlug)) { ExceptionlessClient.Default.SubmitLog($"Efecte name '{name}' of organization {id} does not yield a usable folder name.", LogLevel.Warn); return null; } var (prettySlug, uniqueSlug) = (canonicalSlug.Value, collisionSlug.Value); // Fast path: pretty slug exists & meta-id matches => return slug(name) var prettyState = await InspectCustomerFolderAsync(prettySlug, id, cancellationToken); if (prettyState is CustomerFolderState.Owned) return prettySlug; // Conflict resolution: pretty slug exists & meta-id does not match => return slug(name,id) var uniqueState = await InspectCustomerFolderAsync(uniqueSlug, id, cancellationToken); if (uniqueState is CustomerFolderState.Owned) return uniqueSlug; // State cleanup: // Either the folder does not have the name it should have or doesn't exist, so the bucket has to be scanned. And // if necessary and possible, renamed such that the next lookup hits one of the expected slugs. OrgSlug? availableSlug = prettyState is CustomerFolderState.Missing ? prettySlug : uniqueState is CustomerFolderState.Missing ? uniqueSlug : null; var currentSlug = await SearchCustomerPrefixAsync(id, cancellationToken); if (currentSlug is null) { if (availableSlug is not { } available) { ExceptionlessClient.Default.SubmitLog( $"Organization {id} has no customer folder and neither " + $"'{prettySlug}' nor '{uniqueSlug}' is available.", LogLevel.Error); return null; } if (!await CreateCustomerFolderAsync(available, id, cancellationToken)) return null; if (await InspectCustomerFolderAsync(available, id, cancellationToken) is not CustomerFolderState.Owned) { ExceptionlessClient.Default.SubmitLog( $"Organization {id} has no customer folder and " + $"'{available}' could not be created.", LogLevel.Error); return null; } return available; } if (availableSlug is null || availableSlug.Value == currentSlug.Value) return currentSlug; // A failed move leaves every document under the name it already had, so that name is what // gets handed back. The next lookup simply tries the move again. return await RenameCustomerFolderAsync(currentSlug.Value, availableSlug.Value, id, cancellationToken) ? availableSlug : currentSlug; }