public async Task InitAsync(UserSecret currentUserSecret, SFProjectSecret projectSecret, List <User> ptUsers, string paratextProjectId) { _currentUserSecret = currentUserSecret; _currentParatextUsername = _paratextService.GetParatextUsername(currentUserSecret); _projectSecret = projectSecret; _idToSyncUser.Clear(); _usernameToSyncUser.Clear(); foreach (SyncUser syncUser in projectSecret.SyncUsers) { _idToSyncUser[syncUser.Id] = syncUser; _usernameToSyncUser[syncUser.ParatextUsername] = syncUser; } _ptProjectUsersWhoCanWriteNotes = new HashSet <string>(); IReadOnlyDictionary <string, string> roles = await _paratextService.GetProjectRolesAsync(currentUserSecret, paratextProjectId); var ptRolesCanWriteNote = new HashSet <string> { SFProjectRole.Administrator, SFProjectRole.Translator, SFProjectRole.Consultant, SFProjectRole.WriteNote }; foreach (User user in ptUsers) { // Populate the list with all Paratext users belonging to the project and who can write notes if (roles.TryGetValue(user.ParatextId, out string role) && ptRolesCanWriteNote.Contains(role)) { _ptProjectUsersWhoCanWriteNotes.Add(user.Id); } } }
public async Task <ActionResult <string> > UsernameAsync() { Attempt <UserSecret> attempt = await _userSecrets.TryGetAsync(_userAccessor.UserId); if (!attempt.TryResult(out UserSecret userSecret)) { return(NoContent()); } string username = _paratextService.GetParatextUsername(userSecret); return(Ok(username)); }
public async Task <ActionResult <string> > UsernameAsync() { UserEntity user = await _users.GetAsync(_userAccessor.UserId); if (user.ParatextTokens == null) { return(NoContent()); } string username = _paratextService.GetParatextUsername(user); return(Ok(username)); }
/// <summary> /// First-stage migrator. Synchronize all SF projects to the Paratext Data Access server. /// First query information that will show whether we should be able to sync all projects. In addition to /// reporting information on projects and whether there is an admin that can sync the project, this method shows /// that the admin can successfully perform queries to both the PT Registry and the PT Data Access web APIs, via /// various ParatextService method calls. /// If `doSynchronizations` is false, only do the above reporting. If true, also synchronize the SF DB with the /// Paratext Data Access server. /// </summary> public async Task SynchronizeAllProjectsAsync(bool doSynchronizations, ISet <string> sfProjectIdsToSynchronize = null, IDictionary <string, string> sfAdminsToUse = null) { List <SFProject> allSfProjects = _realtimeService.QuerySnapshots <SFProject>().ToList <SFProject>(); if (sfProjectIdsToSynchronize != null) { allSfProjects.RemoveAll((SFProject sfProject) => !sfProjectIdsToSynchronize.Contains(sfProject.Id)); string ids = string.Join(' ', allSfProjects.Select((SFProject sfProject) => sfProject.Id)); int count = allSfProjects.Count; _logger.Log($"Only working on the subset of projects (count {count}) with these SF project ids: {ids}"); } _realtimeServiceConnection = await _realtimeService.ConnectAsync(); List <Task> syncTasks = new List <Task>(); // Report on all SF projects. foreach (SFProject sfProject in allSfProjects) { _logger.Log($"{Program.Bullet1} PT project {sfProject.ShortName}, " + $"PT project id {sfProject.ParatextId}, SF project id {sfProject.Id}."); List <string> projectSfAdminUserIds = sfProject.UserRoles .Where(ur => ur.Value == SFProjectRole.Administrator).Select(ur => ur.Key).ToList <string>(); if (projectSfAdminUserIds.Count() < 1) { List <string> projectSfUserIds = sfProject.UserRoles.Select(ur => ur.Key).ToList <string>(); string users = string.Join(", ", projectSfUserIds); if (projectSfUserIds.Count() < 1) { users = "None"; } _logger.Log($" {Program.Bullet2} Warning: no admin users. Non-admin users include: {users}"); } // Report on all admins in a project foreach (string sfUserId in projectSfAdminUserIds) { UserSecret userSecret = _userSecretRepo.Query() .FirstOrDefault((UserSecret us) => us.Id == sfUserId); string ptUsername = null; string ptUserId = null; try { ptUsername = _paratextService.GetParatextUsername(userSecret); ptUserId = GetParatextUserId(userSecret); } catch (Exception e) { _logger.Log($" {Program.Bullet2} Failure getting SF user's PT username or PT user id. " + $"Skipping. SF user id was {sfUserId}. If known, PT username was {ptUsername}. " + $"Error with stack was {e}"); continue; } _logger.Log($" {Program.Bullet2} PT user '{ptUsername}', " + $"id {ptUserId}, using SF admin user id {sfUserId} on SF project."); string rt = $"{userSecret.ParatextTokens.RefreshToken.Substring(0, 5)}.."; string at = $"{userSecret.ParatextTokens.AccessToken.Substring(0, 5)}.."; bool atv = userSecret.ParatextTokens.ValidateLifetime(); _logger.Log($" {Program.Bullet3} Paratext RefreshToken: {rt}, " + $"AccessToken: {at}, AccessToken initially valid: {atv}."); // Demonstrate access to PT Registry, and report Registry's statement of role. _logger.Log($" {Program.Bullet3} PT Registry report on role on PT project: ", false); IReadOnlyDictionary <string, string> ptProjectRoles = null; try { ptProjectRoles = await _paratextService.GetProjectRolesAsync(userSecret, sfProject.ParatextId); } catch (Exception e) { Console.WriteLine($" Failure fetching user's PT project roles. Skipping. " + $"Error was {e.Message}"); continue; } if (ptProjectRoles.TryGetValue(ptUserId, out string ptRole)) { Console.WriteLine($"{ptRole}"); } else { Console.WriteLine($"Not found."); } // Demonstrate access to PT Data Access. IReadOnlyList <ParatextProject> userPtProjects = null; try { userPtProjects = await _paratextService.GetProjectsAsync(userSecret); } catch (Exception e) { _logger.Log($" {Program.Bullet3} Failure fetching user's PT projects. Skipping. " + $"Error was {e.Message}"); continue; } _logger.Log($" {Program.Bullet3} PT Data Access and PT Registry " + "based report on projects the user can access, narrowed to this project: ", false); List <string> ptProjectNamesList = userPtProjects .Where(ptProject => ptProject.ParatextId == sfProject.ParatextId) .Select(ptProject => ptProject.ShortName).ToList(); string ptProjectNames = string.Join(',', ptProjectNamesList); if (ptProjectNamesList.Count() < 1) { ptProjectNames = $"User is not on this project. " + $"PT reports they are on this many PT projects: {userPtProjects.Count()}"; } Console.WriteLine(ptProjectNames); if (doSynchronizations) { if (sfAdminsToUse != null && sfAdminsToUse.ContainsKey(sfProject.Id)) { sfAdminsToUse.TryGetValue(sfProject.Id, out string sfAdminIdToUse); bool isUserAtHand = sfUserId == sfAdminIdToUse; if (isUserAtHand) { _logger.Log($" {Program.Bullet2} For SF Project {sfProject.Id}, we were asked to use " + $"this SF user {sfUserId} to sync."); } else { _logger.Log($" {Program.Bullet2} For SF Project {sfProject.Id}, we were asked to use " + $"SF user {sfAdminIdToUse}, not {sfUserId}, to sync. So skipping this user."); continue; } } try { _logger.Log($" {Program.Bullet2} Starting an asynchronous synchronization for " + $"SF project {sfProject.Id} as SF user {sfUserId}."); Task syncTask = SynchronizeProject(sfUserId, sfProject.Id); var projectDoc = await _realtimeServiceConnection.FetchAsync <SFProject>(sfProject.Id); // Increment the queued count (such as done in SyncService), since it gets decremented // later by ParatextSyncRunner. await projectDoc.SubmitJson0OpAsync(op => op.Inc(pd => pd.Sync.QueuedCount)); _logger.Log($" {Program.Bullet3} Synchronization task for SF project {sfProject.Id} as " + $"SF user {sfUserId} has Sync Task Id {syncTask.Id}."); syncTasks.Add(syncTask); break; } catch (Exception e) { // We probably won't get here. But just in case. _logger.Log($" {Program.Bullet3} There was a problem with synchronizing. It might be " + $"tried next with another admin user. Exception is:{Environment.NewLine}{e}"); continue; } } } } if (doSynchronizations) { _logger.Log("Waiting for synchronization tasks to finish (if any). " + $"There are this many tasks: {syncTasks.Count}"); try { Task allTasks = Task.WhenAll(syncTasks); try { await allTasks.ConfigureAwait(false); } catch { if (allTasks.Exception == null) { throw; } ExceptionDispatchInfo.Capture(allTasks.Exception).Throw(); } _logger.Log("Synchronization tasks are finished."); } catch (AggregateException e) { _logger.Log("There was a problem with one or more synchronization tasks. " + $"Exception is:{Environment.NewLine}{e}"); } if (syncTasks.Any(task => !task.IsCompletedSuccessfully)) { _logger.Log("One or more sync tasks did not complete successfully."); } else { _logger.Log("All sync tasks finished with a claimed Task status of Completed Successfully."); } _logger.Log($"{Program.Bullet1} Sync task completion results:"); foreach (Task task in syncTasks) { string exceptionInfo = $"with exception {task.Exception?.InnerException}."; if (task.Exception == null) { exceptionInfo = "with no unhandled exception thrown."; } _logger.Log($" {Program.Bullet2} Sync task Id {task.Id} has status {task.Status} {exceptionInfo}"); if (task.Exception?.InnerExceptions?.Count > 1) { _logger.Log($" {Program.Bullet3} Sync task Id {task.Id} has more than one inner exception. " + "Sorry if this is redundant, but they are:"); foreach (var e in task.Exception.InnerExceptions) { _logger.Log($" {Program.Bullet3} Inner exception: {e}"); } } } } ReportLastSyncSuccesses(allSfProjects); }
/// <summary> /// Iterates through SF projects on the server and identifies one administrator user on the project. /// Using the administrator user's secrets, perform a send/receive with the Paratext server. Effectively, /// this clones the project to the Scripture Forge server. /// This will overwrite any un-synchronized data on SF. /// </summary> public async Task CloneSFProjects(string mode, IEnumerable <SFProject> projectsToClone) { string syncDir = Path.Combine(_siteOptions.Value.SiteDir, "sync"); bool doClone = mode == CLONE || mode == CLONE_AND_MOVE_OLD || mode == CLONE_SILENT; string syncDirOld = Path.Combine(_siteOptions.Value.SiteDir, "sync_old"); if (mode == CLONE_AND_MOVE_OLD) { if (!_fileSystemService.DirectoryExists(syncDirOld)) { _fileSystemService.CreateDirectory(syncDirOld); } } IConnection connection = await _realtimeService.ConnectAsync(); // Get the paratext project ID and admin user for all SF Projects foreach (SFProject proj in projectsToClone) { bool foundAdmin = false; foreach (string userId in proj.UserRoles.Keys) { if (proj.UserRoles.TryGetValue(userId, out string role) && role == SFProjectRole.Administrator) { foundAdmin = true; UserSecret userSecret = _userSecretRepo.Query().FirstOrDefault((UserSecret us) => us.Id == userId); string ptUsername = _paratextService.GetParatextUsername(userSecret); Log($"Project administrator identified on {proj.Name}: {ptUsername} ({userId})"); if (!doClone) { break; } try { var projectDoc = await connection.FetchAsync <SFProject>(proj.Id); await projectDoc.SubmitJson0OpAsync(op => { // Increment the queued count such as in SyncService op.Inc(pd => pd.Sync.QueuedCount); }); bool silent = mode == CLONE_SILENT; // Clone the paratext project and update the SF database with the project data await CloneAndSyncFromParatext(proj, userId, syncDir, silent); if (mode == CLONE_AND_MOVE_OLD) { string projectDir = Path.Combine(syncDir, proj.Id); string projectDirOld = Path.Combine(syncDirOld, proj.Id); _fileSystemService.MoveDirectory(projectDir, projectDirOld); } break; } catch (Exception e) { Log($"Unable to clone {proj.Name} ({proj.Id}) as user: {userId}{Environment.NewLine}" + $"Error was: {e}"); } } } if (!foundAdmin) { Log($"ERROR: Unable to identify a project administrator on {proj.Name}"); } } }