diff --git a/aspnetcore/src/api.Tests/Services_Tests/TtvSqlServiceTest.cs b/aspnetcore/src/api.Tests/Services_Tests/TtvSqlServiceTest.cs index c732d543..985a7f23 100644 --- a/aspnetcore/src/api.Tests/Services_Tests/TtvSqlServiceTest.cs +++ b/aspnetcore/src/api.Tests/Services_Tests/TtvSqlServiceTest.cs @@ -901,11 +901,23 @@ public void GetSqlQuery_Select_GetHiddenInUserprofile() { // Arrange TtvSqlService ttvSqlService = new(); - string expectedSqlString = $"SELECT hidden as 'Hidden' FROM dim_user_profile WHERE id={53445623}"; + string expectedSqlString = $"SELECT hidden as 'Hidden' FROM dim_user_profile WHERE id=53445623"; // Act string actualSqlString = ttvSqlService.GetSqlQuery_Select_GetHiddenInUserprofile(53445623); // Assert Assert.Equal(expectedSqlString, actualSqlString); } + + [Fact(DisplayName = "Get SQL query for setting 'modified' timestamp in userprofile")] + public void GetSqlQuery_Update_DimUserProfile_Modified() + { + // Arrange + TtvSqlService ttvSqlService = new(); + string expectedSqlString = $"UPDATE dim_user_profile SET modified=GETDATE() WHERE id=445566778"; + // Act + string actualSqlString = ttvSqlService.GetSqlQuery_Update_DimUserProfile_Modified(445566778); + // Assert + Assert.Equal(expectedSqlString, actualSqlString); + } } } \ No newline at end of file diff --git a/aspnetcore/src/api/Background/BackgroundProfileData.cs b/aspnetcore/src/api/Background/BackgroundProfileData.cs index 7ff89082..c94dedd8 100644 --- a/aspnetcore/src/api/Background/BackgroundProfileData.cs +++ b/aspnetcore/src/api/Background/BackgroundProfileData.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using api.Models.Log; using api.Models.Ttv; +using api.Models.Common; using api.Models.Elasticsearch; using Microsoft.Extensions.DependencyInjection; using api.Models.ProfileEditor.Items; @@ -58,6 +59,13 @@ await localUserProfileService.GetProfileDataAsync( .Where(duc => duc.DimUserProfileId == userprofileId && duc.UserChoiceValue == true).AsNoTracking().ProjectTo(mapper.ConfigurationProvider).ToListAsync(); elasticsearchPerson.cooperation.AddRange(userChoices); + // Add updated timestamp + DateTimeDTO userProfileModified = await localTtvContext.DimUserProfiles.Where(dup => dup.Id == userprofileId).AsNoTracking().Select(dimUserProfile => new DateTimeDTO() + { + Value = dimUserProfile.Modified + }).FirstOrDefaultAsync(); + elasticsearchPerson.updated = userProfileModified.Value; + return elasticsearchPerson; } } diff --git a/aspnetcore/src/api/Controllers/CooperationChoicesController.cs b/aspnetcore/src/api/Controllers/CooperationChoicesController.cs index 624e630f..656b3d89 100644 --- a/aspnetcore/src/api/Controllers/CooperationChoicesController.cs +++ b/aspnetcore/src/api/Controllers/CooperationChoicesController.cs @@ -147,9 +147,11 @@ public async Task PatchMany([FromBody] List PostMany([FromBody] List RemoveMany([FromBody] List projectIds) } await _ttvContext.SaveChangesAsync(); + // Refresh 'modified' timestamp in user profile + await _userProfileService.SetModifiedTimestampInUserProfile(userprofileId); + // Update Elasticsearch index. LogUserIdentification logUserIdentification = this.GetLogUserIdentification(); await _userProfileService.UpdateProfileInElasticsearch( diff --git a/aspnetcore/src/api/Controllers/ProfileDataController.cs b/aspnetcore/src/api/Controllers/ProfileDataController.cs index 7d8c78c7..d0dfb2fa 100644 --- a/aspnetcore/src/api/Controllers/ProfileDataController.cs +++ b/aspnetcore/src/api/Controllers/ProfileDataController.cs @@ -119,6 +119,9 @@ public async Task PatchMany([FromBody] ProfileEditorDataModificat profileEditorDataModificationResponse.items.Add(profileEditorItemMeta); } + // Refresh 'modified' timestamp in user profile + await _userProfileService.SetModifiedTimestampInUserProfile(userprofileId); + // Update Elasticsearch index. LogUserIdentification logUserIdentification = this.GetLogUserIdentification(); await _userProfileService.UpdateProfileInElasticsearch( diff --git a/aspnetcore/src/api/Controllers/PublicationController.cs b/aspnetcore/src/api/Controllers/PublicationController.cs index eb5a4051..bca663f2 100644 --- a/aspnetcore/src/api/Controllers/PublicationController.cs +++ b/aspnetcore/src/api/Controllers/PublicationController.cs @@ -184,6 +184,9 @@ public async Task PostMany([FromBody] List RemoveMany([FromBody] List publicationI } await _ttvContext.SaveChangesAsync(); + // Refresh 'modified' timestamp in user profile + await _userProfileService.SetModifiedTimestampInUserProfile(userprofileId); // Update Elasticsearch index. LogUserIdentification logUserIdentification = this.GetLogUserIdentification(); diff --git a/aspnetcore/src/api/Controllers/ResearchDatasetController.cs b/aspnetcore/src/api/Controllers/ResearchDatasetController.cs index 7d59e777..f247ba1c 100644 --- a/aspnetcore/src/api/Controllers/ResearchDatasetController.cs +++ b/aspnetcore/src/api/Controllers/ResearchDatasetController.cs @@ -161,6 +161,9 @@ public async Task PostMany([FromBody] List RemoveMany([FromBody] List localIdentif } await _ttvContext.SaveChangesAsync(); + // Refresh 'modified' timestamp in user profile + await _userProfileService.SetModifiedTimestampInUserProfile(userprofileId); // Update Elasticsearch index. LogUserIdentification logUserIdentification = this.GetLogUserIdentification(); diff --git a/aspnetcore/src/api/Controllers/WebhookController.cs b/aspnetcore/src/api/Controllers/WebhookController.cs index b462291a..94db94d4 100644 --- a/aspnetcore/src/api/Controllers/WebhookController.cs +++ b/aspnetcore/src/api/Controllers/WebhookController.cs @@ -26,12 +26,14 @@ namespace api.Controllers public class WebhookController : ControllerBase { private readonly IUserProfileService _userProfileService; + private readonly ITtvSqlService _ttvSqlService; private readonly ILogger _logger; private readonly IBackgroundTaskQueue _taskQueue; private readonly IServiceScopeFactory _serviceScopeFactory; private readonly IMemoryCache _cache; public WebhookController(IUserProfileService userProfileService, + ITtvSqlService ttvSqlService, ILogger logger, IBackgroundTaskQueue taskQueue, IServiceScopeFactory serviceScopeFactory, @@ -40,6 +42,7 @@ public WebhookController(IUserProfileService userProfileService, IMemoryCache memoryCache) { _userProfileService = userProfileService; + _ttvSqlService = ttvSqlService; _logger = logger; _taskQueue = taskQueue; _serviceScopeFactory = serviceScopeFactory; @@ -91,6 +94,10 @@ public async Task HandleOrcidWebhook(string webhookOrcidId) // Remove cached profile data response. Cache key is ORCID ID. _cache.Remove(webhookOrcidId); + // Refresh 'modified' timestamp in table dim_user_profile + string setModifiedSql = _ttvSqlService.GetSqlQuery_Update_DimUserProfile_Modified(dimUserProfile.Id); + await _userProfileService.ExecuteRawSql(setModifiedSql); + // Store values to be used in background task. orcidAccessToken = dimUserProfile.OrcidAccessToken; dimUserprofileId = dimUserProfile.Id; diff --git a/aspnetcore/src/api/Models/Common/DateTimeDTO.cs b/aspnetcore/src/api/Models/Common/DateTimeDTO.cs new file mode 100644 index 00000000..346ee66c --- /dev/null +++ b/aspnetcore/src/api/Models/Common/DateTimeDTO.cs @@ -0,0 +1,9 @@ +using System; + +namespace api.Models.Common +{ + public partial class DateTimeDTO + { + public DateTime? Value { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Common/IntegerDTO.cs b/aspnetcore/src/api/Models/Common/IntegerDTO.cs index 41fcbd7d..845e8358 100644 --- a/aspnetcore/src/api/Models/Common/IntegerDTO.cs +++ b/aspnetcore/src/api/Models/Common/IntegerDTO.cs @@ -2,6 +2,6 @@ { public partial class IntegerDTO { - public int Integer { get; set; } + public int Value { get; set; } } } diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPerson.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPerson.cs index 0b232af9..be107a82 100644 --- a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPerson.cs +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPerson.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Nest; namespace api.Models.Elasticsearch @@ -30,5 +31,6 @@ public ElasticsearchPerson(string orcidId) public ElasticsearchActivity activity { get; set; } public List cooperation { get; set; } public List uniqueDataSources { get; set; } + public DateTime? updated { get; set; } } } diff --git a/aspnetcore/src/api/Services/ITtvSqlService.cs b/aspnetcore/src/api/Services/ITtvSqlService.cs index 0858d60d..6f2fde08 100644 --- a/aspnetcore/src/api/Services/ITtvSqlService.cs +++ b/aspnetcore/src/api/Services/ITtvSqlService.cs @@ -48,5 +48,6 @@ public interface ITtvSqlService string GetSqlQuery_Select_FactFieldValues(int userprofileId); string GetSqlQuery_Select_GetHiddenInUserprofile(int dimUserProfileId); string GetSqlQuery_Update_FactFieldValues(int dimUserProfileId, ProfileEditorItemMeta profileEditorItemMeta); + string GetSqlQuery_Update_DimUserProfile_Modified(int dimUserProfileId); } } \ No newline at end of file diff --git a/aspnetcore/src/api/Services/IUserProfileService.cs b/aspnetcore/src/api/Services/IUserProfileService.cs index be82642b..78c4c9ee 100644 --- a/aspnetcore/src/api/Services/IUserProfileService.cs +++ b/aspnetcore/src/api/Services/IUserProfileService.cs @@ -47,5 +47,6 @@ public interface IUserProfileService string GetCMemoryCacheKey_SharePurposes(); string GetCMemoryCacheKey_SharePermissions(); string GetCMemoryCacheKey_GivenPermissions(string orcidId); + Task SetModifiedTimestampInUserProfile(int Id); } } \ No newline at end of file diff --git a/aspnetcore/src/api/Services/TtvSqlService.cs b/aspnetcore/src/api/Services/TtvSqlService.cs index 2e0153cb..f81c22d2 100644 --- a/aspnetcore/src/api/Services/TtvSqlService.cs +++ b/aspnetcore/src/api/Services/TtvSqlService.cs @@ -95,6 +95,12 @@ public string GetSqlQuery_Update_FactFieldValues(int dimUserProfileId, ProfileEd {fk_column_name}={profileEditorItemMeta.Id}"; } + // Return SQL update statement for setting 'modified' timestamp in userprofile. + public string GetSqlQuery_Update_DimUserProfile_Modified(int dimUserProfileId) + { + return $@"UPDATE dim_user_profile SET modified=GETDATE() WHERE id={dimUserProfileId}"; + } + // Convert list of integers into a comma separated string public string ConvertListOfIntsToCommaSeparatedString(List listOfInts) { diff --git a/aspnetcore/src/api/Services/UserProfileService.cs b/aspnetcore/src/api/Services/UserProfileService.cs index 3204c64d..4b51e879 100644 --- a/aspnetcore/src/api/Services/UserProfileService.cs +++ b/aspnetcore/src/api/Services/UserProfileService.cs @@ -156,16 +156,26 @@ public async Task GetUserprofileById(int Id) IntegerDTO dimUserProfileIdDTO = await _ttvContext.DimUserProfiles.Where(dup => dup.OrcidId == orcidId).AsNoTracking() .Select(dimUserProfile => new IntegerDTO() { - Integer = dimUserProfile.Id + Value = dimUserProfile.Id }).FirstOrDefaultAsync(); - if (dimUserProfileIdDTO == null || dimUserProfileIdDTO.Integer < 0) { + if (dimUserProfileIdDTO == null || dimUserProfileIdDTO.Value < 0) { return (UserProfileExists: false, UserProfileId: -1); } else { - return (UserProfileExists: true, UserProfileId: dimUserProfileIdDTO.Integer); + return (UserProfileExists: true, UserProfileId: dimUserProfileIdDTO.Value); } } + /* + * Set 'modified' timestamp in user profile + */ + public async Task SetModifiedTimestampInUserProfile(int Id) + { + await ExecuteRawSql( + _ttvSqlService.GetSqlQuery_Update_DimUserProfile_Modified(Id) + ); + } + /* * Add or update DimName. */ @@ -1009,6 +1019,7 @@ public async Task CreateProfile(string orcidId, LogUserIdentification logUserIde SourceId = Constants.SourceIdentifiers.PROFILE_API, SourceDescription = Constants.SourceDescriptions.PROFILE_API, Created = currentDateTime, + Modified = currentDateTime, AllowAllSubscriptions = false, Hidden = false, PublishNewOrcidData = false