diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml new file mode 100644 index 00000000..c54ea902 --- /dev/null +++ b/.github/workflows/dotnet.yml @@ -0,0 +1,31 @@ +# This workflow will build a .NET project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net + +name: .NET + +on: + pull_request: + branches: + - "devel" + - "qa" + - "master" + - "main" + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 6.0.x + - name: Restore dependencies + run: dotnet restore + working-directory: aspnetcore/ + - name: Build + run: dotnet build --no-restore + working-directory: aspnetcore/ + - name: Test + run: dotnet test --no-build --verbosity normal + working-directory: aspnetcore/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index 21d9bcaf..c53857d9 100644 --- a/.gitignore +++ b/.gitignore @@ -194,6 +194,8 @@ PublishScripts/ # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets +# except custom packages +!**/custom_nuget/* # Microsoft Azure Build Output csx/ diff --git a/aspnetcore/custom_nuget/Serilog.Formatting.OpenSearch.1.0.0.nupkg b/aspnetcore/custom_nuget/Serilog.Formatting.OpenSearch.1.0.0.nupkg new file mode 100644 index 00000000..2fd1c7bb Binary files /dev/null and b/aspnetcore/custom_nuget/Serilog.Formatting.OpenSearch.1.0.0.nupkg differ diff --git a/aspnetcore/custom_nuget/Serilog.Sinks.OpenSearch.1.0.0.nupkg b/aspnetcore/custom_nuget/Serilog.Sinks.OpenSearch.1.0.0.nupkg new file mode 100644 index 00000000..ad29bc0e Binary files /dev/null and b/aspnetcore/custom_nuget/Serilog.Sinks.OpenSearch.1.0.0.nupkg differ diff --git a/aspnetcore/nuget.config b/aspnetcore/nuget.config new file mode 100644 index 00000000..cd211a0a --- /dev/null +++ b/aspnetcore/nuget.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/aspnetcore/openshift/api/Dockerfile b/aspnetcore/openshift/api/Dockerfile index 55a3224d..919ef756 100644 --- a/aspnetcore/openshift/api/Dockerfile +++ b/aspnetcore/openshift/api/Dockerfile @@ -2,6 +2,8 @@ FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env WORKDIR /app # Copy csproj and restore as distinct layers +COPY aspnetcore/custom_nuget/ /app/custom_nuget +COPY aspnetcore/nuget.config ./ COPY aspnetcore/src/api/api.csproj ./ RUN dotnet restore # Copy everything else and build diff --git a/aspnetcore/src/api.Tests/Infrastructure/orcidSandbox_0000-0002-9227-8514_record.json b/aspnetcore/src/api.Tests/Infrastructure/orcidSandbox_0000-0002-9227-8514_record.json index 655c03e7..c59f9d41 100644 --- a/aspnetcore/src/api.Tests/Infrastructure/orcidSandbox_0000-0002-9227-8514_record.json +++ b/aspnetcore/src/api.Tests/Infrastructure/orcidSandbox_0000-0002-9227-8514_record.json @@ -2461,17 +2461,7 @@ }, "url": null, "type": "journal-article C", - "publication-date": { - "year": { - "value": "2012" - }, - "month": { - "value": "10" - }, - "day": { - "value": "01" - } - }, + "publication-date": null, "journal-title": { "value": "Learned Publishing" }, diff --git a/aspnetcore/src/api.Tests/Services_Tests/DuplicateHandlerServiceTest.cs b/aspnetcore/src/api.Tests/Services_Tests/DuplicateHandlerServiceTest.cs index f885080d..b9d4fbb7 100644 --- a/aspnetcore/src/api.Tests/Services_Tests/DuplicateHandlerServiceTest.cs +++ b/aspnetcore/src/api.Tests/Services_Tests/DuplicateHandlerServiceTest.cs @@ -283,13 +283,13 @@ public void addPublicationToProfileEditorData_020() { Id = 1, RegisteredDataSource = "Virta", - Organization = new Organization() {} + Organization = new Organization() { } }; ProfileEditorSource profileEditorSourceOrcid = new() { Id = 2, RegisteredDataSource = "ORCID", - Organization = new Organization() {} + Organization = new Organization() { } }; // Create ProfileDataRaw for Virta publication 1 @@ -351,5 +351,21 @@ public void addPublicationToProfileEditorData_020() // Check that publication list contains two publications Assert.Equal(2, publications3.Count); } + + + [Fact(DisplayName = "TestPublicationYearHandling")] + public void testPublicationYearHandling() + { + DuplicateHandlerService duplicateHandlerService = new(); + + // Valid publication year + Assert.Equal(2022, duplicateHandlerService.HandlePublicationYear(2022)); + // Publication year 0 + Assert.Null(duplicateHandlerService.HandlePublicationYear(0)); + // Publication year negative + Assert.Null(duplicateHandlerService.HandlePublicationYear(-1)); + // Publication year null + Assert.Null(duplicateHandlerService.HandlePublicationYear(null)); + } } } \ No newline at end of file diff --git a/aspnetcore/src/api.Tests/Services_Tests/OrcidApiServicetest.cs b/aspnetcore/src/api.Tests/Services_Tests/OrcidApiServicetest.cs new file mode 100644 index 00000000..e44c639b --- /dev/null +++ b/aspnetcore/src/api.Tests/Services_Tests/OrcidApiServicetest.cs @@ -0,0 +1,190 @@ +using Xunit; +using Moq; +using api.Services; +using Microsoft.Extensions.Configuration; +using System.Collections.Generic; +using static System.Net.WebRequestMethods; +using System.Security.Cryptography; +using System.Collections; + +namespace api.Tests +{ + [Collection("ORCID api service tests")] + public class OrcidApiServiceTests + { + /* + * Helper method for setting up IConfiguration + */ + public IConfiguration CreateTestConfiguration() + { + Dictionary inMemorySettings = new Dictionary { + { "SERVICEURL", "https://mydata-api.service.example.fi"}, + { "ORCID:WEBHOOK:ENABLED", "true"}, + { "ORCID:WEBHOOK:API", "https://api.sandbox.orcid.org/"}, + { "ORCID:WEBHOOK:ACCESSTOKEN", "fake20e90b06-0384f05d-3ffc9bd9-31ab06ff" } + }; + IConfiguration testConfiguration = new ConfigurationBuilder() + .AddInMemoryCollection(inMemorySettings) + .Build(); + return testConfiguration; + } + + /* + * Create test data for unit test IsOrcidWebhookEnabled_ReturnsIsOrcidWebhookFeatureEnabled + */ + public class IsOrcidWebhookEnabledTestData : IEnumerable + { + public IEnumerator GetEnumerator() + { + // Valid configuration + yield return new object[] { + new Dictionary { + { "SERVICEURL", "https://mydata-api.service.example.fi"}, + { "ORCID:WEBHOOK:ENABLED", "true"}, + { "ORCID:WEBHOOK:API", "https://api.sandbox.orcid.org/"}, + { "ORCID:WEBHOOK:ACCESSTOKEN", "fake20e90b06-0384f05d-3ffc9bd9-31ab06ff" } + }, + true + }; + // Required configuration completely missing + yield return new object[] { + new Dictionary {}, + false + }; + // SERVICEURL is not defined + yield return new object[] { + new Dictionary { + { "ORCID:WEBHOOK:ENABLED", "false"}, + { "ORCID:WEBHOOK:API", "https://api.sandbox.orcid.org/"}, + { "ORCID:WEBHOOK:ACCESSTOKEN", "fake20e90b06-0384f05d-3ffc9bd9-31ab06ff" } + }, + false + }; + // ORCID:WEBHOOK:ENABLED = false + yield return new object[] { + new Dictionary { + { "SERVICEURL", "https://mydata-api.service.example.fi"}, + { "ORCID:WEBHOOK:ENABLED", "false"}, + { "ORCID:WEBHOOK:API", "https://api.sandbox.orcid.org/"}, + { "ORCID:WEBHOOK:ACCESSTOKEN", "fake20e90b06-0384f05d-3ffc9bd9-31ab06ff" } + }, + false + }; + // ORCID:WEBHOOK:ENABLED is not defined + yield return new object[] { + new Dictionary { + { "SERVICEURL", "https://mydata-api.service.example.fi"}, + { "ORCID:WEBHOOK:API", "https://api.sandbox.orcid.org/"}, + { "ORCID:WEBHOOK:ACCESSTOKEN", "fake20e90b06-0384f05d-3ffc9bd9-31ab06ff" } + }, + false + }; + // ORCID:WEBHOOK:API is not defined + yield return new object[] { + new Dictionary { + { "SERVICEURL", "https://mydata-api.service.example.fi"}, + { "ORCID:WEBHOOK:ENABLED", "true"}, + { "ORCID:WEBHOOK:ACCESSTOKEN", "fake20e90b06-0384f05d-3ffc9bd9-31ab06ff" } + }, + false + }; + // ORCID:WEBHOOK:ACCESSTOKEN is not defined + yield return new object[] { + new Dictionary { + { "SERVICEURL", "https://mydata-api.service.example.fi"}, + { "ORCID:WEBHOOK:ENABLED", "true"}, + { "ORCID:WEBHOOK:API", "https://api.sandbox.orcid.org/"} + }, + false + }; + } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + [Fact(DisplayName = "Get correct path for ORCID record")] + public void GetOrcidRecordPath_OrcidId_ReturnsCorrectPath() + { + // Arrange + OrcidApiService orcidApiService = new OrcidApiService(); + // Act + string actualOrcidRecordPath = orcidApiService.GetOrcidRecordPath("1234-5678-8765-4321"); + // Assert + Assert.Equal("1234-5678-8765-4321/record", actualOrcidRecordPath); + } + + [Theory(DisplayName = "Check is ORCID webhook feature enabled")] + [ClassData(typeof(IsOrcidWebhookEnabledTestData))] + public void IsOrcidWebhookEnabled_ReturnsIsOrcidWebhookFeatureEnabled(Dictionary inMemorySettings, bool expectedResult) + { + // Arrange + IConfiguration testConfiguration = new ConfigurationBuilder() + .AddInMemoryCollection(inMemorySettings) + .Build(); + OrcidApiService orcidApiService = new OrcidApiService(testConfiguration); + // Act + bool actualIsOrcidWebhookEnabled = orcidApiService.IsOrcidWebhookEnabled(); + // Assert + Assert.Equal(expectedResult, actualIsOrcidWebhookEnabled); + } + + [Fact(DisplayName = "Get ORCID webhook callback URI for ORCID ID")] + public void GetOrcidWebhookCallbackUri_OrcidId_ReturnsCorrectCallbackUri() + { + // Arrange + IConfiguration testConfiguration = CreateTestConfiguration(); + OrcidApiService orcidApiService = new OrcidApiService(testConfiguration); + // Act + string actualOrcidWebhookCallbackUri = orcidApiService.GetOrcidWebhookCallbackUri(orcidId: "0000-0001-2000-3456"); + // Assert + Assert.Equal( + "https://mydata-api.service.example.fi/api/webhook/orcid/0000-0001-2000-3456", + actualOrcidWebhookCallbackUri + ); + } + + [Fact(DisplayName = "Get URL encoded string")] + public void GetUrlEncodedString_URI_ReturnsUrlEncodedString() + { + // Arrange + OrcidApiService orcidApiService = new OrcidApiService(); + // Act + string actualUrlEncodedString = + orcidApiService.GetUrlEncodedString("https://mydata-api.service.example.fi/api/webhook/orcid/0000-0001-2000-3456"); + // Assert + Assert.Equal( + "https%3a%2f%2fmydata-api.service.example.fi%2fapi%2fwebhook%2forcid%2f0000-0001-2000-3456", + actualUrlEncodedString + ); + } + + [Fact(DisplayName = "Get ORCID webhook registration URL for ORCID ID")] + public void GetOrcidWebhookRegistrationUri_OrcidId_ReturnsCorrectRegistrationUri() + { + // Arrange + IConfiguration testConfiguration = CreateTestConfiguration(); + OrcidApiService orcidApiService = new OrcidApiService(testConfiguration); + // Act + string actualOrcidWebhookRegistrationUri = orcidApiService.GetOrcidWebhookRegistrationUri("0000-0001-2000-3456"); + // Assert + Assert.Equal( + "https://api.sandbox.orcid.org/0000-0001-2000-3456/webhook/https%3a%2f%2fmydata-api.service.example.fi%2fapi%2fwebhook%2forcid%2f0000-0001-2000-3456", + actualOrcidWebhookRegistrationUri + ); + } + + [Fact(DisplayName = "Get ORCID webhook access token from configuration")] + public void GetOrcidWebhookAccessToken_ReturnsOrcidWebhookAccessTokenFromConfiguration() + { + // Arrange + IConfiguration testConfiguration = CreateTestConfiguration(); + OrcidApiService orcidApiService = new OrcidApiService(testConfiguration); + // Act + string actualOrcidWebhookAccessToken = orcidApiService.GetOrcidWebhookAccessToken(); + // Assert + Assert.Equal( + "fake20e90b06-0384f05d-3ffc9bd9-31ab06ff", + actualOrcidWebhookAccessToken + ); + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api.Tests/Services_Tests/OrcidJsonParserServiceTest.cs b/aspnetcore/src/api.Tests/Services_Tests/OrcidJsonParserServiceTest.cs index 29474a91..aed755e1 100644 --- a/aspnetcore/src/api.Tests/Services_Tests/OrcidJsonParserServiceTest.cs +++ b/aspnetcore/src/api.Tests/Services_Tests/OrcidJsonParserServiceTest.cs @@ -402,7 +402,7 @@ public void TestGetPublications() Assert.Equal("Another publication", actualPublications[2].PublicationName); Assert.Equal(new OrcidPutCode(733536).Value, actualPublications[2].PutCode.Value); - Assert.Equal(2012, actualPublications[2].PublicationYear); + Assert.Null(actualPublications[2].PublicationYear); Assert.Equal("10.1087/20120404", actualPublications[2].Doi); Assert.Equal("journal-article C", actualPublications[2].Type); } diff --git a/aspnetcore/src/api.Tests/Services_Tests/TtvSqlServiceTest.cs b/aspnetcore/src/api.Tests/Services_Tests/TtvSqlServiceTest.cs index 17b50264..d7bf3805 100644 --- a/aspnetcore/src/api.Tests/Services_Tests/TtvSqlServiceTest.cs +++ b/aspnetcore/src/api.Tests/Services_Tests/TtvSqlServiceTest.cs @@ -22,7 +22,7 @@ public FactFieldValue GetFactFieldValueForTest() DimEmailAddrressId = -1, DimEventId = -1, DimFieldDisplaySettingsId = -1, - DimFieldOfScienceId = -1, + DimReferencedataFieldOfScienceId = -1, DimFundingDecisionId = -1, DimIdentifierlessDataId = -1, DimKeywordId = -1, @@ -167,6 +167,16 @@ public void getFactFieldValuesFKColumnNameFromFieldIdentifier_dim_research_datas ); } + [Fact(DisplayName = "Get FactFieldValues FK column name - dim_research_activity_id")] + public void getFactFieldValuesFKColumnNameFromFieldIdentifier_dim_research_activity_id() + { + TtvSqlService ttvSqlService = new(); + // Research activity + Assert.Equal( + "dim_research_activity_id", ttvSqlService.GetFactFieldValuesFKColumnNameFromFieldIdentifier(Constants.FieldIdentifiers.ACTIVITY_RESEARCH_ACTIVITY) + ); + } + [Fact(DisplayName = "Test that list of integers is converted to a comma separated string")] public void Test_ConvertListOfIntsToCommaSeparatedString() { @@ -497,5 +507,17 @@ public void Test_GetSqlQuery_Delete_DimUserProfile() string actualSqlString = ttvSqlService.GetSqlQuery_Delete_DimUserProfile(221199); Assert.Equal(expectedSqlString, actualSqlString); } + + [Fact(DisplayName = "Get SQL query for counting number of published items in userprofile")] + public void GetSqlQuery_Select_CountPublishedItemsInUserprofile() + { + TtvSqlService ttvSqlService = new(); + string expectedSqlString = $@"SELECT COUNT(ffv.show) AS 'PublishedCount' + FROM fact_field_values AS ffv + JOIN dim_user_profile AS dup ON ffv.dim_user_profile_id=dup.id + WHERE dup.id=335577 AND ffv.show=1"; + string actualSqlString = ttvSqlService.GetSqlQuery_Select_CountPublishedItemsInUserprofile(335577); + Assert.Equal(expectedSqlString, actualSqlString); + } } } \ No newline at end of file diff --git a/aspnetcore/src/api.Tests/Services_Tests/UtilityServiceTest.cs b/aspnetcore/src/api.Tests/Services_Tests/UtilityServiceTest.cs new file mode 100644 index 00000000..c9486828 --- /dev/null +++ b/aspnetcore/src/api.Tests/Services_Tests/UtilityServiceTest.cs @@ -0,0 +1,30 @@ +using Xunit; +using api.Services; + +namespace api.Tests +{ + [Collection("Utility service tests")] + public class UtilityServiceTests + { + [Fact(DisplayName = "Get correct data source organization name - ORCID")] + public void getCorrectDataSourceOrganizationName_ORCID() + { + var utilityService = new UtilityService(); + Assert.Equal("ORCID", utilityService.GetDatasourceOrganizationName_ORCID()); + } + + [Fact(DisplayName = "Get correct data source organization name - TTV")] + public void getCorrectDataSourceOrganizationName_TTV() + { + var utilityService = new UtilityService(); + Assert.Equal("Tiedejatutkimus.fi", utilityService.GetDatasourceOrganizationName_TTV()); + } + + [Fact(DisplayName = "Get correct organization ID - OKM")] + public void getCorrectOrganizationID_OKM() + { + var utilityService = new UtilityService(); + Assert.Equal("02w52zt87", utilityService.GetOrganizationId_OKM()); + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api.Tests/api.Tests.csproj b/aspnetcore/src/api.Tests/api.Tests.csproj index e01e08d1..2492b61b 100644 --- a/aspnetcore/src/api.Tests/api.Tests.csproj +++ b/aspnetcore/src/api.Tests/api.Tests.csproj @@ -7,7 +7,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -15,6 +15,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + @@ -24,6 +25,9 @@ + + + diff --git a/aspnetcore/src/api/Automapper/MappingProfile.cs b/aspnetcore/src/api/Automapper/MappingProfile.cs index 9062d07b..6f5bf677 100644 --- a/aspnetcore/src/api/Automapper/MappingProfile.cs +++ b/aspnetcore/src/api/Automapper/MappingProfile.cs @@ -13,6 +13,8 @@ public MappingProfile() CreateMap(); CreateMap(); CreateMap(); + CreateMap(); + CreateMap(); CreateMap(); CreateMap(); CreateMap(); diff --git a/aspnetcore/src/api/Background/BackgroundProfileData.cs b/aspnetcore/src/api/Background/BackgroundProfileData.cs index 927a3bc4..f11b59ce 100644 --- a/aspnetcore/src/api/Background/BackgroundProfileData.cs +++ b/aspnetcore/src/api/Background/BackgroundProfileData.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using api.Models.Log; using api.Models.Ttv; using api.Models.Elasticsearch; using Microsoft.Extensions.DependencyInjection; @@ -27,14 +28,19 @@ public BackgroundProfiledata(IServiceScopeFactory serviceScopeFactory) /* * Get userprofile data from TTV database and construct entry for Elasticsearch person index. */ - public async Task GetProfiledataForElasticsearch(string orcidId, int userprofileId) + public async Task GetProfiledataForElasticsearch(string orcidId, int userprofileId, LogUserIdentification logUserIdentification) { // Create a scope and get TtvContext for data query. using IServiceScope scope = _serviceScopeFactory.CreateScope(); IUserProfileService localUserProfileService = scope.ServiceProvider.GetRequiredService(); IMapper mapper = scope.ServiceProvider.GetRequiredService(); - ProfileEditorDataResponse profileEditorDataResponse = await localUserProfileService.GetProfileDataAsync2(userprofileId: userprofileId, forElasticsearch: true); + // Get profile data + ProfileEditorDataResponse profileEditorDataResponse = + await localUserProfileService.GetProfileDataAsync( + userprofileId: userprofileId, + logUserIdentification: logUserIdentification, + forElasticsearch: true); // Convert profile editor model into Elasticsearch model using Automapper. // Set id to ORCID ID diff --git a/aspnetcore/src/api/Background/IBackgroundProfiledata.cs b/aspnetcore/src/api/Background/IBackgroundProfiledata.cs index bd2ff6d3..670fb860 100644 --- a/aspnetcore/src/api/Background/IBackgroundProfiledata.cs +++ b/aspnetcore/src/api/Background/IBackgroundProfiledata.cs @@ -1,10 +1,11 @@ using System.Threading.Tasks; using api.Models.Elasticsearch; +using api.Models.Log; namespace api.Services { public interface IBackgroundProfiledata { - Task GetProfiledataForElasticsearch(string orcidId, int userprofileId); + Task GetProfiledataForElasticsearch(string orcidId, int userprofileId, LogUserIdentification logUserIdentification); } } \ No newline at end of file diff --git a/aspnetcore/src/api/Controllers/AccountDeleteController.cs b/aspnetcore/src/api/Controllers/AccountDeleteController.cs index da56dd9a..5db7c195 100644 --- a/aspnetcore/src/api/Controllers/AccountDeleteController.cs +++ b/aspnetcore/src/api/Controllers/AccountDeleteController.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.Http; +using api.Models.Log; namespace api.Controllers { @@ -34,15 +35,25 @@ public AccountDeleteController(IKeycloakAdminApiService keycloakAdminApiService, [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task Delete() { - // Log request. - _logger.LogInformation(this.GetLogPrefix() + " delete user from Keycloak."); + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_ACCOUNT_DELETE, + state: LogContent.ActionState.START)); // Keycloak: logout user - await _keycloakAdminApiService.LogoutUser(this.GetBearerTokenFromHttpRequest()); + await _keycloakAdminApiService.LogoutUser(this.GetBearerTokenFromHttpRequest(), this.GetLogUserIdentification()); // Keycloak: remove user - await _keycloakAdminApiService.RemoveUser(this.GetBearerTokenFromHttpRequest()); + await _keycloakAdminApiService.RemoveUser(this.GetBearerTokenFromHttpRequest(), this.GetLogUserIdentification()); + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_ACCOUNT_DELETE, + state: LogContent.ActionState.COMPLETE)); return Ok(new ApiResponse(success: true)); } } diff --git a/aspnetcore/src/api/Controllers/AccountLinkController.cs b/aspnetcore/src/api/Controllers/AccountLinkController.cs index 4e339104..e70991fb 100644 --- a/aspnetcore/src/api/Controllers/AccountLinkController.cs +++ b/aspnetcore/src/api/Controllers/AccountLinkController.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.Http; +using api.Models.Log; namespace api.Controllers { @@ -18,9 +19,9 @@ public class AccountLinkController : TtvControllerBase { private readonly IKeycloakAdminApiService _keycloakAdminApiService; private readonly ITokenService _tokenService; - private readonly ILogger _logger; + private readonly ILogger _logger; - public AccountLinkController(IKeycloakAdminApiService keycloakAdminApiService, ITokenService tokenService, ILogger logger) + public AccountLinkController(IKeycloakAdminApiService keycloakAdminApiService, ITokenService tokenService, ILogger logger) { _keycloakAdminApiService = keycloakAdminApiService; _tokenService = tokenService; @@ -34,18 +35,36 @@ public AccountLinkController(IKeycloakAdminApiService keycloakAdminApiService, I [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task Get() { - // Log request. - _logger.LogInformation(this.GetLogPrefix() + " set ORCID ID attribute in Keycloak."); + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_LINK_ORCID, + state: LogContent.ActionState.START)); // Decode JWT. System.IdentityModel.Tokens.Jwt.JwtSecurityToken kcJwt = _tokenService.GetJwtFromString(this.GetBearerTokenFromHttpRequest()); // Return immediately if JWT already contains ORCID ID. if (TokenService.JwtContainsOrcid(kcJwt)) { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_LINK_ORCID, + state: LogContent.ActionState.COMPLETE)); return Ok(new ApiResponse(success: true)); } + // Set ORCID ID as a user attribute in Keycloak. - bool setOrcidAttributeSuccess = await _keycloakAdminApiService.SetOrcidAttributedInKeycloak(kcJwt); + bool setOrcidAttributeSuccess = await _keycloakAdminApiService.SetOrcidAttributedInKeycloak(kcJwt, this.GetLogUserIdentification()); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_LINK_ORCID, + state: LogContent.ActionState.COMPLETE)); return Ok(new ApiResponse(success: setOrcidAttributeSuccess)); } } diff --git a/aspnetcore/src/api/Controllers/AdminController.cs b/aspnetcore/src/api/Controllers/AdminController.cs new file mode 100644 index 00000000..dcebc44d --- /dev/null +++ b/aspnetcore/src/api/Controllers/AdminController.cs @@ -0,0 +1,415 @@ +using api.Services; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Caching.Memory; +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Mvc; +using api.Models.Ttv; +using api.Models.Log; + +namespace api.Controllers +{ + /* + * AdminController implements API for administration commands. + */ + [ApiController] + public class AdminController : TtvAdminControllerBase + { + private readonly IOrcidApiService _orcidApiService; + private readonly IUserProfileService _userProfileService; + private readonly IElasticsearchService _elasticsearchService; + private readonly ILogger _logger; + private readonly IBackgroundTaskQueue _taskQueue; + private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly IBackgroundProfiledata _backgroundProfiledata; + public IConfiguration Configuration { get; } + + public AdminController( + IConfiguration configuration, + IOrcidApiService orcidApiService, + IUserProfileService userProfileService, + IElasticsearchService elasticsearchService, + ILogger logger, + IMemoryCache memoryCache, + IBackgroundTaskQueue taskQueue, + IBackgroundProfiledata backgroundProfiledata, + IServiceScopeFactory serviceScopeFactory) + { + _orcidApiService = orcidApiService; + _userProfileService = userProfileService; + _elasticsearchService = elasticsearchService; + _logger = logger; + _taskQueue = taskQueue; + _backgroundProfiledata = backgroundProfiledata; + _serviceScopeFactory = serviceScopeFactory; + Configuration = configuration; + } + + + /// + /// Admin: Register webhook for a single user profile. + /// + [HttpPost] + [Route("/[controller]/orcidwebhook/register/single/{webhookOrcidId}")] + public async Task RegisterOrcidWebhookForSingleUserprofile(string webhookOrcidId) + { + // Validate request data + if (!ModelState.IsValid) + { + return BadRequest(); + } + + // Check admin token authorization + if (!IsAdminTokenAuthorized(Configuration)) + { + return Unauthorized(); + } + + LogUserIdentification logUserIdentification = this.GetLogUserIdentification(); + + // Check that ORCID webhook feature is enabled + if (!_orcidApiService.IsOrcidWebhookEnabled()) + { + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ADMIN_WEBHOOK_ORCID_REGISTER, + state: LogContent.ActionState.FAILED, + error: true, + message: "disabled in configuration")); + return StatusCode(StatusCodes.Status503ServiceUnavailable); + } + + // Register webhook in a background task + await _taskQueue.QueueBackgroundWorkItemAsync(async token => + { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ADMIN_WEBHOOK_ORCID_REGISTER, + state: LogContent.ActionState.START, + message: $"(ORCID={webhookOrcidId})")); + + // Create service scope and get required services. + // Do not use services from controller scope in a background task. + using IServiceScope scope = _serviceScopeFactory.CreateScope(); + IAdminService localAdminService = scope.ServiceProvider.GetRequiredService(); + await localAdminService.RegisterOrcidWebhookForSingleUserprofile(webhookOrcidId); + + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ADMIN_WEBHOOK_ORCID_REGISTER, + state: LogContent.ActionState.COMPLETE, + message: $"(ORCID={webhookOrcidId})")); + }); + + return Ok(); + } + + /// + /// Admin: Unregister webhook for a single user profile. + /// + /// [HttpPost] + [Route("/[controller]/orcidwebhook/unregister/single/{webhookOrcidId}")] + public async Task UnegisterOrcidWebhookForSingleUserprofile(string webhookOrcidId) + { + // Validate request data + if (!ModelState.IsValid) + { + return BadRequest(); + } + + // Check admin token authorization + if (!IsAdminTokenAuthorized(Configuration)) + { + return Unauthorized(); + } + + LogUserIdentification logUserIdentification = this.GetLogUserIdentification(); + + // Check that ORCID webhook feature is enabled + if (!_orcidApiService.IsOrcidWebhookEnabled()) + { + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ADMIN_WEBHOOK_ORCID_UNREGISTER, + state: LogContent.ActionState.FAILED, + error: true, + message: "disabled in configuration")); + return StatusCode(StatusCodes.Status503ServiceUnavailable); + } + + // Register webhook in a background task + await _taskQueue.QueueBackgroundWorkItemAsync(async token => + { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ADMIN_WEBHOOK_ORCID_UNREGISTER, + state: LogContent.ActionState.START, + message: $"ORCID={webhookOrcidId}")); + + // Create service scope and get required services. + // Do not use services from controller scope in a background task. + using IServiceScope scope = _serviceScopeFactory.CreateScope(); + IAdminService localAdminService = scope.ServiceProvider.GetRequiredService(); + await localAdminService.UnregisterOrcidWebhookForSingleUserprofile(webhookOrcidId); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ADMIN_WEBHOOK_ORCID_UNREGISTER, + state: LogContent.ActionState.COMPLETE, + message: $"ORCID={webhookOrcidId}")); + }); + + return Ok(); + } + + /// + /// Admin: Register webhook for all user profiles. + /// + [HttpPost] + [Route("/[controller]/orcidwebhook/register/all")] + public async Task RegisterOrcidWebhookForAllUserprofiles() + { + // Check admin token authorization + if (!IsAdminTokenAuthorized(Configuration)) + { + return Unauthorized(); + } + + LogUserIdentification logUserIdentification = this.GetLogUserIdentification(); + + // Check that ORCID webhook feature is enabled + if (!_orcidApiService.IsOrcidWebhookEnabled()) + { + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ADMIN_WEBHOOK_ORCID_REGISTER_ALL, + state: LogContent.ActionState.FAILED, + error: true, + message: "disabled in configuration")); + return StatusCode(StatusCodes.Status503ServiceUnavailable); + } + + // Register webhooks in a background task + await _taskQueue.QueueBackgroundWorkItemAsync(async token => + { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ADMIN_WEBHOOK_ORCID_REGISTER_ALL, + state: LogContent.ActionState.START)); + + // Create service scope and get required services. + // Do not use services from controller scope in a background task. + using IServiceScope scope = _serviceScopeFactory.CreateScope(); + IAdminService localAdminService = scope.ServiceProvider.GetRequiredService(); + await localAdminService.RegisterOrcidWebhookForAllUserprofiles(); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ADMIN_WEBHOOK_ORCID_REGISTER_ALL, + state: LogContent.ActionState.COMPLETE)); + }); + + return Ok(); + } + + /// + /// Admin: Unregister webhook for all user profiles. + /// + [HttpPost] + [Route("/[controller]/orcidwebhook/unregister/all")] + public async Task UnregisterOrcidWebhookForAllUserprofiles() + { + // Check admin token authorization + if (!IsAdminTokenAuthorized(Configuration)) + { + return Unauthorized(); + } + + LogUserIdentification logUserIdentification = this.GetLogUserIdentification(); + + // Check that ORCID webhook feature is enabled + if (!_orcidApiService.IsOrcidWebhookEnabled()) + { + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ADMIN_WEBHOOK_ORCID_UNREGISTER_ALL, + state: LogContent.ActionState.FAILED, + error: true, + message: "disabled in configuration")); + return StatusCode(StatusCodes.Status503ServiceUnavailable); + } + + // Unregister webhooks in a background task + await _taskQueue.QueueBackgroundWorkItemAsync(async token => + { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ADMIN_WEBHOOK_ORCID_UNREGISTER_ALL, + state: LogContent.ActionState.START)); + + // Create service scope and get required services. + // Do not use services from controller scope in a background task. + using IServiceScope scope = _serviceScopeFactory.CreateScope(); + IAdminService localAdminService = scope.ServiceProvider.GetRequiredService(); + await localAdminService.UnregisterOrcidWebhookForAllUserprofiles(); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ADMIN_WEBHOOK_ORCID_UNREGISTER_ALL, + state: LogContent.ActionState.COMPLETE)); + }); + + return Ok(); + } + + /// + /// Admin: Update user profile in Elasticsearch. + /// + [HttpPost] + [Route("/[controller]/elasticsearch/updateprofile/{dimUserProfileId}")] + public async Task UpdateUserprofileInElasticsearch(int dimUserProfileId) + { + // Validate request data + if (!ModelState.IsValid) + { + return BadRequest(); + } + + // Check admin token authorization + if (!IsAdminTokenAuthorized(Configuration)) + { + return Unauthorized(); + } + + LogUserIdentification logUserIdentification = this.GetLogUserIdentification(); + + // Get DimUserProfile + DimUserProfile dimUserProfile = await _userProfileService.GetUserprofileById(dimUserProfileId); + // Check that user profile exists + if (dimUserProfile == null) + { + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ADMIN_ELASTICSEARCH_PROFILE_UPDATE, + state: LogContent.ActionState.FAILED, + error: true, + message: $"profile not found (dim_user_profile.id={dimUserProfileId})")); + return NotFound(); + } + + + // Store ORCID ID for background process + string orcidId = dimUserProfile.OrcidId; + + // Check if the profile should be updated or deleted in Elasticsearch index + bool isUserprofilePublished = await _userProfileService.IsUserprofilePublished(dimUserProfileId); + + if (isUserprofilePublished) + { + // User profile is published. Update Elasticsearch index. + // Update Elasticsearch index in a background task. + // ElasticsearchService is singleton, no need to create local scope. + if (_elasticsearchService.IsElasticsearchSyncEnabled()) + { + await _taskQueue.QueueBackgroundWorkItemAsync(async token => + { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ADMIN_ELASTICSEARCH_PROFILE_UPDATE, + state: LogContent.ActionState.START)); + + // Get Elasticsearch person entry from profile data. + Models.Elasticsearch.ElasticsearchPerson person = + await _backgroundProfiledata.GetProfiledataForElasticsearch( + orcidId: orcidId, + userprofileId: dimUserProfileId, + logUserIdentification: logUserIdentification); + // Update Elasticsearch person index. + await _elasticsearchService.UpdateEntryInElasticsearchPersonIndex(orcidId, person, logUserIdentification); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ADMIN_ELASTICSEARCH_PROFILE_UPDATE, + state: LogContent.ActionState.COMPLETE)); + }); + } + } + else + { + // User profile is not published. Delete from Elasticsearch index. + // Remove entry from Elasticsearch index in a background task. + // ElasticsearchService is singleton, no need to create local scope. + if (_elasticsearchService.IsElasticsearchSyncEnabled()) + { + await _taskQueue.QueueBackgroundWorkItemAsync(async token => + { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ADMIN_ELASTICSEARCH_PROFILE_DELETE, + state: LogContent.ActionState.START)); + + // Update Elasticsearch person index. + bool deleteSuccess = await _elasticsearchService.DeleteEntryFromElasticsearchPersonIndex(orcidId, logUserIdentification); + if (deleteSuccess) + { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ADMIN_ELASTICSEARCH_PROFILE_DELETE, + state: LogContent.ActionState.COMPLETE)); + } + else + { + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ADMIN_ELASTICSEARCH_PROFILE_DELETE, + state: LogContent.ActionState.FAILED, + error: true)); + } + }); + } + } + + return Ok(); + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Controllers/DebugController.cs b/aspnetcore/src/api/Controllers/DebugController.cs index e2a57b89..cf6bb75a 100644 --- a/aspnetcore/src/api/Controllers/DebugController.cs +++ b/aspnetcore/src/api/Controllers/DebugController.cs @@ -1,6 +1,7 @@ using api.Services; using api.Models.Api; using api.Models.Common; +using api.Models.Log; using api.Models.Ttv; using api.Models.ProfileEditor.Items; using Microsoft.AspNetCore.Mvc; @@ -20,7 +21,7 @@ namespace api.Controllers * DebugController implements API for debugging. */ [ApiController] - public class DebugController: TtvControllerBase + public class DebugController: TtvAdminControllerBase { private readonly TtvContext _ttvContext; private readonly IUserProfileService _userProfileService; @@ -29,6 +30,7 @@ public class DebugController: TtvControllerBase private readonly IMemoryCache _cache; private readonly IBackgroundTaskQueue _taskQueue; public IConfiguration Configuration { get; } + public string logPrefix; public DebugController( IConfiguration configuration, @@ -46,20 +48,22 @@ public DebugController( _taskQueue = taskQueue; _elasticsearchService = elasticsearchService; Configuration = configuration; + + logPrefix = "DebugController: "; } /// - /// Debug: Get number of user profiles. Requires correct "debugtoken" header value. + /// Debug: Get number of user profiles. /// [HttpGet] [Route("/[controller]/profilecount")] public async Task GetNumberOfProfiles() { - _logger.LogInformation(this.GetLogPrefix() + " DEBUG get number of profiles"); + _logger.LogInformation($"{logPrefix}get number of profiles"); - // Check that "DEBUGTOKEN" is defined and has a value in configuration and that the request header "debugtoken" matches. - if (Configuration["DEBUGTOKEN"] == null || Configuration["DEBUGTOKEN"] == "" || Request.Headers["debugtoken"] != Configuration["DEBUGTOKEN"]) + // Check admin token authorization + if (!IsAdminTokenAuthorized(Configuration)) { return Unauthorized(); } @@ -70,16 +74,16 @@ public async Task GetNumberOfProfiles() /// - /// Debug: Get list of ORCID ID which have a user profile. Requires correct "debugtoken" header value. + /// Debug: Get list of ORCID ID which have a user profile. /// [HttpGet] [Route("/[controller]/orcids")] public async Task GetListOfORCIDs() { - _logger.LogInformation(this.GetLogPrefix() + " DEBUG get list of ORCID IDs which have a user profile"); + _logger.LogInformation($"{logPrefix}get list of ORCID IDs which have a user profile"); - // Check that "DEBUGTOKEN" is defined and has a value in configuration and that the request header "debugtoken" matches. - if (Configuration["DEBUGTOKEN"] == null || Configuration["DEBUGTOKEN"] == "" || Request.Headers["debugtoken"] != Configuration["DEBUGTOKEN"]) + // Check admin token authorization + if (!IsAdminTokenAuthorized(Configuration)) { return Unauthorized(); } @@ -95,17 +99,17 @@ public async Task GetListOfORCIDs() /// - /// Debug: Get any user profile data. Requires correct "debugtoken" header value. + /// Debug: Get any user profile data. /// [HttpGet] [Route("/[controller]/profiledata/{orcidId}")] [ProducesResponseType(typeof(ApiResponseProfileDataGet), StatusCodes.Status200OK)] public async Task Get(string orcidId) { - _logger.LogInformation(this.GetLogPrefix() + " DEBUG get profile data for ORCID ID: " + orcidId); + _logger.LogInformation($"{logPrefix}get profile data for ORCID ID: " + orcidId); - // Check that "DEBUGTOKEN" is defined and has a value in configuration and that the request header "debugtoken" matches. - if (Configuration["DEBUGTOKEN"] == null || Configuration["DEBUGTOKEN"] == "" || Request.Headers["debugtoken"] != Configuration["DEBUGTOKEN"]) + // Check admin token authorization + if (!IsAdminTokenAuthorized(Configuration)) { return Unauthorized(); } @@ -123,8 +127,11 @@ public async Task Get(string orcidId) return Ok(new ApiResponse(success: false, reason: "profile not found")); } + // User identification object for logging + LogUserIdentification logUserIdentification = new LogUserIdentification(orcid: orcidId); + // Get profile data - ProfileEditorDataResponse profileDataResponse = await _userProfileService.GetProfileDataAsync2(userprofileId); + ProfileEditorDataResponse profileDataResponse = await _userProfileService.GetProfileDataAsync(userprofileId: userprofileId, logUserIdentification: logUserIdentification); return Ok(new ApiResponseProfileDataGet(success: true, reason: "", data: profileDataResponse, fromCache: false)); } @@ -133,17 +140,16 @@ public async Task Get(string orcidId) /// /// Debug: Create user profile for given ORCID ID. /// Preconditions: ORCID ID must must already exist in dim_pid and it must be linked to a row in dim_known_person. - /// Requires correct "debugtoken" header value. /// [HttpPost] [Route("/[controller]/userprofile/{orcidId}")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task CreateProfile(string orcidId) { - _logger.LogInformation(this.GetLogPrefix() + " DEBUG create user profile, ORCID ID: " + orcidId); + _logger.LogInformation($"{logPrefix}create user profile, ORCID ID: " + orcidId); - // Check that "DEBUGTOKEN" is defined and has a value in configuration and that the request header "debugtoken" matches. - if (Configuration["DEBUGTOKEN"] == null || Configuration["DEBUGTOKEN"] == "" || Request.Headers["debugtoken"] != Configuration["DEBUGTOKEN"]) + // Check admin token authorization + if (!IsAdminTokenAuthorized(Configuration)) { return Unauthorized(); } @@ -170,35 +176,38 @@ public async Task CreateProfile(string orcidId) return Ok(new ApiResponse(success: false, reason: "DEBUG Either ORCID ID does not exist in dim_pid, or it is not linked to any dim_known_person.")); } + // User identification object for logging + LogUserIdentification logUserIdentification = new LogUserIdentification(orcid: orcidId); + // Create profile try { - await _userProfileService.CreateProfile(orcidId: orcidId); + await _userProfileService.CreateProfile(orcidId: orcidId, logUserIdentification: logUserIdentification); } catch { - string msg = " DEBUG profile creation failed"; - _logger.LogError(this.GetLogPrefix() + msg); + string msg = "profile creation failed"; + _logger.LogError($"{logPrefix}{msg}"); return Ok(new ApiResponse(success: false, reason: msg)); } - _logger.LogInformation(this.GetLogPrefix() + " DEBUG profile created"); + _logger.LogInformation($"{logPrefix}profile created"); return Ok(new ApiResponse(success: true)); } /// - /// Debug: Delete user profile for given ORCID ID. Requires correct "debugtoken" header value. + /// Debug: Delete user profile for given ORCID ID. /// [HttpDelete] [Route("/[controller]/userprofile/{orcidId}")] [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] public async Task DeleteProfile(string orcidId) { - _logger.LogInformation(this.GetLogPrefix() + " DEBUG delete user profile, ORCID ID: " + orcidId); + _logger.LogInformation($"{logPrefix}delete user profile, ORCID ID: " + orcidId); - // Check that "DEBUGTOKEN" is defined and has a value in configuration and that the request header "debugtoken" matches. - if (Configuration["DEBUGTOKEN"] == null || Configuration["DEBUGTOKEN"] == "" || Request.Headers["debugtoken"] != Configuration["DEBUGTOKEN"]) + // Check admin token authorization + if (!IsAdminTokenAuthorized(Configuration)) { return Unauthorized(); } @@ -212,8 +221,8 @@ public async Task DeleteProfile(string orcidId) // Return immediately, if profile does not exist. if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) { - string msg = " DEBUG profile does not exist for ORCID ID: " + orcidId; - _logger.LogInformation(this.GetLogPrefix() + msg); + string msg = "profile does not exist for ORCID ID: " + orcidId; + _logger.LogInformation($"{logPrefix}{msg}"); return Ok(new ApiResponse(success: false, reason: msg)); } @@ -223,14 +232,18 @@ public async Task DeleteProfile(string orcidId) // Remove cached profile data response. Cache key is ORCID ID. _cache.Remove(orcidId); + // User identification object for logging + LogUserIdentification logUserIdentification = new LogUserIdentification(orcid: orcidId); + // Remove entry from Elasticsearch index in a background task. + // ElasticsearchService is singleton, no need to create local scope. if (_elasticsearchService.IsElasticsearchSyncEnabled()) { await _taskQueue.QueueBackgroundWorkItemAsync(async token => { _logger.LogInformation($"Elasticsearch removal of {orcidId} started {DateTime.UtcNow}"); // Update Elasticsearch person index. - await _elasticsearchService.DeleteEntryFromElasticsearchPersonIndex(orcidId); + await _elasticsearchService.DeleteEntryFromElasticsearchPersonIndex(orcidId, logUserIdentification); _logger.LogInformation($"Elasticsearch removal of {orcidId} completed {DateTime.UtcNow}"); }); } @@ -238,18 +251,18 @@ await _taskQueue.QueueBackgroundWorkItemAsync(async token => // Delete profile data from database try { - await _userProfileService.DeleteProfileDataAsync(userprofileId: userprofileId); + await _userProfileService.DeleteProfileDataAsync(userprofileId: userprofileId, logUserIdentification: logUserIdentification); } catch { // Log error - string msg = " DEBUG profile deletion failed"; - _logger.LogError(this.GetLogPrefix() + msg); + string msg = "profile deletion failed"; + _logger.LogError($"{logPrefix}{msg}"); return Ok(new ApiResponse(success: false, reason: msg)); } // Log deletion - _logger.LogInformation(this.GetLogPrefix() + " DEBUG profile deleted"); + _logger.LogInformation($"{logPrefix}profile deleted"); return Ok(new ApiResponse(success: true)); } diff --git a/aspnetcore/src/api/Controllers/FundingDecisionController.cs b/aspnetcore/src/api/Controllers/FundingDecisionController.cs index 8c1abc27..e0f2cf11 100644 --- a/aspnetcore/src/api/Controllers/FundingDecisionController.cs +++ b/aspnetcore/src/api/Controllers/FundingDecisionController.cs @@ -1,5 +1,6 @@ using api.Services; using api.Models.Api; +using api.Models.Log; using api.Models.Ttv; using api.Models.ProfileEditor; using api.Models.ProfileEditor.Items; @@ -13,6 +14,7 @@ using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.Http; using api.Models.Common; +using System; namespace api.Controllers { @@ -30,10 +32,18 @@ public class FundingDecisionController : TtvControllerBase private readonly IDataSourceHelperService _dataSourceHelperService; private readonly ILanguageService _languageService; private readonly IMemoryCache _cache; + private readonly ILogger _logger; + private readonly IElasticsearchService _elasticsearchService; + private readonly IBackgroundProfiledata _backgroundProfiledata; + private readonly IBackgroundTaskQueue _taskQueue; public FundingDecisionController(TtvContext ttvContext, IUserProfileService userProfileService, IUtilityService utilityService, IDataSourceHelperService dataSourceHelperService, - IMemoryCache memoryCache, ILanguageService languageService) + IMemoryCache memoryCache, ILanguageService languageService, + IElasticsearchService elasticsearchService, + ILogger logger, + IBackgroundProfiledata backgroundProfiledata, + IBackgroundTaskQueue taskQueue) { _ttvContext = ttvContext; _userProfileService = userProfileService; @@ -41,6 +51,10 @@ public FundingDecisionController(TtvContext ttvContext, IUserProfileService user _dataSourceHelperService = dataSourceHelperService; _languageService = languageService; _cache = memoryCache; + _elasticsearchService = elasticsearchService; + _backgroundProfiledata = backgroundProfiledata; + _logger = logger; + _taskQueue = taskQueue; } /// @@ -84,7 +98,8 @@ public async Task PostMany([FromBody] List dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_FUNDING_DECISION); + DimFieldDisplaySetting dimFieldDisplaySettingsFundingDecision = + dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_FUNDING_DECISION); // Registered data source organization name translation NameTranslation nameTranslation_OrganizationName = _languageService.GetNameTranslation( @@ -109,8 +124,8 @@ public async Task PostMany([FromBody] List f.ProjectId)) { bool fundingDecisionProcessed = false; // Check if userprofile already includes given funding decision @@ -128,7 +143,8 @@ public async Task PostMany([FromBody] List dfd.Id == fundingDecisionToAdd.ProjectId); + DimFundingDecision dimFundingDecision = + await _ttvContext.DimFundingDecisions.AsNoTracking().FirstOrDefaultAsync(dfd => dfd.Id == fundingDecisionToAdd.ProjectId); // Check if exists if (dimFundingDecision == null) { @@ -156,7 +172,39 @@ public async Task PostMany([FromBody] List + { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_UPDATE, + state: LogContent.ActionState.START)); + + // Get Elasticsearch person entry from profile data. + Models.Elasticsearch.ElasticsearchPerson person = + await _backgroundProfiledata.GetProfiledataForElasticsearch( + orcidId: orcidId, + userprofileId: userprofileId, + logUserIdentification: logUserIdentification); + + // Update Elasticsearch person index. + await _elasticsearchService.UpdateEntryInElasticsearchPersonIndex(orcidId, person, logUserIdentification); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_UPDATE, + state: LogContent.ActionState.COMPLETE)); + }); + } // Remove cached profile data response. Cache key is ORCID ID. _cache.Remove(orcidId); @@ -220,7 +268,39 @@ public async Task RemoveMany([FromBody] List projectIds) } await _ttvContext.SaveChangesAsync(); - // TODO: add Elasticsearch sync? + // Update Elasticsearch index in a background task. + // ElasticsearchService is singleton, no need to create local scope. + if (_elasticsearchService.IsElasticsearchSyncEnabled()) + { + LogUserIdentification logUserIdentification = this.GetLogUserIdentification(); + + await _taskQueue.QueueBackgroundWorkItemAsync(async token => + { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_UPDATE, + state: LogContent.ActionState.START)); + + // Get Elasticsearch person entry from profile data. + Models.Elasticsearch.ElasticsearchPerson person = + await _backgroundProfiledata.GetProfiledataForElasticsearch( + orcidId: orcidId, + userprofileId: userprofileId, + logUserIdentification: logUserIdentification); + + // Update Elasticsearch person index. + await _elasticsearchService.UpdateEntryInElasticsearchPersonIndex(orcidId, person, logUserIdentification); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_UPDATE, + state: LogContent.ActionState.COMPLETE)); + }); + } // Remove cached profile data response. Cache key is ORCID ID. _cache.Remove(orcidId); diff --git a/aspnetcore/src/api/Controllers/OrcidController.cs b/aspnetcore/src/api/Controllers/OrcidController.cs index fa222ff1..0b8e882d 100644 --- a/aspnetcore/src/api/Controllers/OrcidController.cs +++ b/aspnetcore/src/api/Controllers/OrcidController.cs @@ -7,6 +7,8 @@ using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.Http; using System; +using Microsoft.AspNetCore.Hosting; +using api.Models.Log; namespace api.Controllers { @@ -23,15 +25,21 @@ public class OrcidController : TtvControllerBase private readonly IOrcidImportService _orcidImportService; private readonly ITokenService _tokenService; private readonly ILogger _logger; + private readonly IWebHostEnvironment _webHostEnvironment; - public OrcidController(IUserProfileService userProfileService, IOrcidApiService orcidApiService, - IOrcidImportService orcidImportService, ILogger logger, ITokenService tokenService) + public OrcidController(IUserProfileService userProfileService, + IOrcidApiService orcidApiService, + IOrcidImportService orcidImportService, + ILogger logger, + ITokenService tokenService, + IWebHostEnvironment webHostEnvironment) { _userProfileService = userProfileService; _orcidApiService = orcidApiService; _orcidImportService = orcidImportService; _tokenService = tokenService; _logger = logger; + _webHostEnvironment = webHostEnvironment; } /// @@ -58,21 +66,43 @@ public async Task Get() int userprofileId = await _userProfileService.GetUserprofileId(orcidId); // Get ORCID record from ORCID member or public API. - // If user access token has claim "use_orcid_public_api", then the record is requested from public API. + // If environment is not "Production" and user access token has claim "use_orcid_public_api", + // then the record is requested from public API. // In all other cases the ORCID member API will be used. string orcidRecordJson = ""; - if (this.GetOrcidPublicApiFlag() != null) + if (_webHostEnvironment.EnvironmentName!="Production" && this.GetOrcidPublicApiFlag() != null) { // ORCID public API should be used try { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.ORCID_RECORD_GET_PUBLIC_API, + state: LogContent.ActionState.START)); + orcidRecordJson = await _orcidApiService.GetRecordFromPublicApi(orcidId); - _logger.LogInformation(this.GetLogPrefix() + " get ORCID record json from ORCID public API OK"); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.ORCID_RECORD_GET_PUBLIC_API, + state: LogContent.ActionState.COMPLETE)); } catch (Exception ex) { - _logger.LogError(this.GetLogPrefix() + " get ORCID record json from ORCID public API failed: " + ex); + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.ORCID_RECORD_GET_PUBLIC_API, + state: LogContent.ActionState.FAILED, + error: true, + message: ex.ToString())); + return Ok(new ApiResponse(success: false)); } } @@ -84,29 +114,69 @@ public async Task Get() OrcidTokens orcidTokens; try { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_GET_ORCID_TOKENS, + state: LogContent.ActionState.START)); + // Get ORCID access token from Keycloak string orcidTokensJson = await _tokenService.GetOrcidTokensJsonFromKeycloak(this.GetBearerTokenFromHttpRequest()); // Parse json from Keycloak into EF model orcidTokens = _tokenService.ParseOrcidTokensJson(orcidTokensJson); // Update ORCID tokens in TTV database. await _userProfileService.UpdateOrcidTokensInDimUserProfile(userprofileId, orcidTokens); - _logger.LogInformation(this.GetLogPrefix() + " get ORCID tokens from Keycloak OK"); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_GET_ORCID_TOKENS, + state: LogContent.ActionState.COMPLETE)); } catch (Exception ex) { - _logger.LogError(this.GetLogPrefix() + " get ORCID tokens from Keycloak failed: " + ex); + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_GET_ORCID_TOKENS, + state: LogContent.ActionState.FAILED, + error: true, + message: ex.ToString())); return Ok(new ApiResponse(success: false)); } // Get record json from ORCID member API try { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.ORCID_RECORD_GET_MEMBER_API, + state: LogContent.ActionState.START)); + orcidRecordJson = await _orcidApiService.GetRecordFromMemberApi(orcidId, orcidTokens.AccessToken); - _logger.LogInformation(this.GetLogPrefix() + " get ORCID record json from ORCID member API OK"); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.ORCID_RECORD_GET_MEMBER_API, + state: LogContent.ActionState.COMPLETE)); } catch (Exception ex) { - _logger.LogError(this.GetLogPrefix() + " get ORCID record json from ORCID member API failed: " + ex); + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.ORCID_RECORD_GET_MEMBER_API, + state: LogContent.ActionState.FAILED, + error: true, + message: ex.ToString())); return Ok(new ApiResponse(success: false)); } } @@ -114,12 +184,33 @@ public async Task Get() // Import record json into userprofile try { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.ORCID_RECORD_IMPORT, + state: LogContent.ActionState.START)); + await _orcidImportService.ImportOrcidRecordJsonIntoUserProfile(userprofileId, orcidRecordJson); - _logger.LogInformation(this.GetLogPrefix() + " import ORCID record to userprofile OK"); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.ORCID_RECORD_IMPORT, + state: LogContent.ActionState.COMPLETE)); } catch (Exception ex) { - _logger.LogError(this.GetLogPrefix() + " import ORCID record to userprofile failed: " + ex); + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.ORCID_RECORD_IMPORT, + state: LogContent.ActionState.FAILED, + error: true, + message: ex.ToString())); + return Ok(new ApiResponse(success: false)); } diff --git a/aspnetcore/src/api/Controllers/ProfileDataController.cs b/aspnetcore/src/api/Controllers/ProfileDataController.cs index a660f810..2fbda4e7 100644 --- a/aspnetcore/src/api/Controllers/ProfileDataController.cs +++ b/aspnetcore/src/api/Controllers/ProfileDataController.cs @@ -12,6 +12,7 @@ using System; using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.Http; +using api.Models.Log; namespace api.Controllers { @@ -68,7 +69,7 @@ public async Task Get() int userprofileId = await _userProfileService.GetUserprofileId(orcidId); // Cache key - string cacheKey = orcidId + "_2"; + string cacheKey = orcidId; // Send cached response, if exists. if (_cache.TryGetValue(cacheKey, out ProfileEditorDataResponse cachedResponse)) @@ -77,7 +78,7 @@ public async Task Get() } // Get profile data - ProfileEditorDataResponse profileDataResponse = await _userProfileService.GetProfileDataAsync2(userprofileId); + ProfileEditorDataResponse profileDataResponse = await _userProfileService.GetProfileDataAsync(userprofileId: userprofileId, logUserIdentification: this.GetLogUserIdentification()); // Save response in cache MemoryCacheEntryOptions cacheEntryOptions = new MemoryCacheEntryOptions() @@ -131,18 +132,38 @@ public async Task PatchMany([FromBody] ProfileEditorDataModificat await _userProfileService.ExecuteRawSql(updateSql); profileEditorDataModificationResponse.items.Add(profileEditorItemMeta); } - + // Update Elasticsearch index in a background task. + // ElasticsearchService is singleton, no need to create local scope. if (_elasticsearchService.IsElasticsearchSyncEnabled()) { + LogUserIdentification logUserIdentification = this.GetLogUserIdentification(); + await _taskQueue.QueueBackgroundWorkItemAsync(async token => { - _logger.LogInformation($"Elasticsearch index update for {orcidId} started {DateTime.UtcNow}"); + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_UPDATE, + state: LogContent.ActionState.START)); + // Get Elasticsearch person entry from profile data. - Models.Elasticsearch.ElasticsearchPerson person = await _backgroundProfiledata.GetProfiledataForElasticsearch(orcidId, userprofileId); + Models.Elasticsearch.ElasticsearchPerson person = + await _backgroundProfiledata.GetProfiledataForElasticsearch( + orcidId: orcidId, + userprofileId: userprofileId, + logUserIdentification: logUserIdentification); + // Update Elasticsearch person index. - await _elasticsearchService.UpdateEntryInElasticsearchPersonIndex(orcidId, person); - _logger.LogInformation($"Elasticsearch index update for {orcidId} completed {DateTime.UtcNow}"); + await _elasticsearchService.UpdateEntryInElasticsearchPersonIndex(orcidId, person, logUserIdentification); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_UPDATE, + state: LogContent.ActionState.COMPLETE)); }); } diff --git a/aspnetcore/src/api/Controllers/ProfileSettingsController.cs b/aspnetcore/src/api/Controllers/ProfileSettingsController.cs new file mode 100644 index 00000000..11e5cf1d --- /dev/null +++ b/aspnetcore/src/api/Controllers/ProfileSettingsController.cs @@ -0,0 +1,93 @@ +using api.Services; +using api.Models.Api; +using api.Models.Common; +using api.Models.Ttv; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using System.Linq; +using System.Collections.Generic; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.AspNetCore.Http; +using api.Models.ProfileEditor; +using System; +using Microsoft.Extensions.Logging; +using api.Models.Log; + +namespace api.Controllers +{ + /* + * ProfileSettingsController implements profile editor settings related APIs. + */ + [Route("api/settings")] + [ApiController] + [Authorize(Policy = "RequireScopeApi1AndClaimOrcid")] + public class ProfileSettingsController : TtvControllerBase + { + private readonly IElasticsearchService _elasticsearchService; + private readonly IBackgroundTaskQueue _taskQueue; + private readonly ILogger _logger; + + public ProfileSettingsController(IElasticsearchService elasticsearchService, ILogger logger, IBackgroundTaskQueue taskQueue) + { + _elasticsearchService = elasticsearchService; + _taskQueue = taskQueue; + _logger = logger; + } + + /// + /// Hide user profile from portal by removing the profile from Elasticsearch index. + /// User profile is not deleted from database. + /// To restore the profile in portal, the user has to publish the profile again. + /// + [HttpGet] + [Route("hideprofile")] + [ProducesResponseType(typeof(ApiResponse), StatusCodes.Status200OK)] + public async Task HideProfileFromPortal() + { + // Get ORCID id + string orcidId = GetOrcidId(); + + LogUserIdentification logUserIdentification = this.GetLogUserIdentification(); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_DELETE, + state: LogContent.ActionState.START)); + + // Remove entry from Elasticsearch index in a background task. + // ElasticsearchService is singleton, no need to create local scope. + if (_elasticsearchService.IsElasticsearchSyncEnabled()) + { + await _taskQueue.QueueBackgroundWorkItemAsync(async token => + { + // Update Elasticsearch person index. + bool deleteSuccess = await _elasticsearchService.DeleteEntryFromElasticsearchPersonIndex(orcidId, logUserIdentification); + if (!deleteSuccess) + { + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_DELETE, + state: LogContent.ActionState.FAILED, + error: true)); + } else + { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_DELETE, + state: LogContent.ActionState.COMPLETE)); + } + }); + } + + return Ok(new ApiResponse()); + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Controllers/PublicationController.cs b/aspnetcore/src/api/Controllers/PublicationController.cs index db9aa05f..5f51f9d0 100644 --- a/aspnetcore/src/api/Controllers/PublicationController.cs +++ b/aspnetcore/src/api/Controllers/PublicationController.cs @@ -1,5 +1,6 @@ using api.Services; using api.Models.Api; +using api.Models.Log; using api.Models.Ttv; using api.Models.ProfileEditor; using api.Models.ProfileEditor.Items; @@ -12,6 +13,8 @@ using Microsoft.Extensions.Caching.Memory; using Microsoft.AspNetCore.Http; using api.Models.Common; +using Microsoft.Extensions.Logging; +using System; namespace api.Controllers { @@ -29,10 +32,18 @@ public class PublicationController : TtvControllerBase private readonly IDataSourceHelperService _dataSourceHelperService; private readonly ILanguageService _languageService; private readonly IMemoryCache _cache; + private readonly ILogger _logger; + private readonly IElasticsearchService _elasticsearchService; + private readonly IBackgroundProfiledata _backgroundProfiledata; + private readonly IBackgroundTaskQueue _taskQueue; public PublicationController(TtvContext ttvContext, IUserProfileService userProfileService, IUtilityService utilityService, IDataSourceHelperService dataSourceHelperService, - IMemoryCache memoryCache, ILanguageService languageService) + IMemoryCache memoryCache, ILanguageService languageService, + IElasticsearchService elasticsearchService, + ILogger logger, + IBackgroundProfiledata backgroundProfiledata, + IBackgroundTaskQueue taskQueue) { _ttvContext = ttvContext; _userProfileService = userProfileService; @@ -40,6 +51,10 @@ public PublicationController(TtvContext ttvContext, IUserProfileService userProf _dataSourceHelperService = dataSourceHelperService; _languageService = languageService; _cache = memoryCache; + _elasticsearchService = elasticsearchService; + _backgroundProfiledata = backgroundProfiledata; + _logger = logger; + _taskQueue = taskQueue; } /// @@ -83,7 +98,8 @@ public async Task PostMany([FromBody] List dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_PUBLICATION); + DimFieldDisplaySetting dimFieldDisplaySettingsPublication = + dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_PUBLICATION); // Registered data source organization name translation NameTranslation nameTranslation_OrganizationName = _languageService.GetNameTranslation( @@ -111,8 +127,8 @@ public async Task PostMany([FromBody] List p.PublicationId)) { bool publicationProcessed = false; // Check if userprofile already includes given publication @@ -179,7 +195,39 @@ public async Task PostMany([FromBody] List + { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_UPDATE, + state: LogContent.ActionState.START)); + + // Get Elasticsearch person entry from profile data. + Models.Elasticsearch.ElasticsearchPerson person = + await _backgroundProfiledata.GetProfiledataForElasticsearch( + orcidId: orcidId, + userprofileId: userprofileId, + logUserIdentification: logUserIdentification); + + // Update Elasticsearch person index. + await _elasticsearchService.UpdateEntryInElasticsearchPersonIndex(orcidId, person, logUserIdentification); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_UPDATE, + state: LogContent.ActionState.COMPLETE)); + }); + } // Remove cached profile data response. Cache key is ORCID ID. _cache.Remove(orcidId); @@ -239,7 +287,39 @@ public async Task RemoveMany([FromBody] List publicationI } await _ttvContext.SaveChangesAsync(); - // TODO: add Elasticsearch sync? + // Update Elasticsearch index in a background task. + // ElasticsearchService is singleton, no need to create local scope. + if (_elasticsearchService.IsElasticsearchSyncEnabled()) + { + LogUserIdentification logUserIdentification = this.GetLogUserIdentification(); + + await _taskQueue.QueueBackgroundWorkItemAsync(async token => + { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_UPDATE, + state: LogContent.ActionState.START)); + + // Get Elasticsearch person entry from profile data. + Models.Elasticsearch.ElasticsearchPerson person = + await _backgroundProfiledata.GetProfiledataForElasticsearch( + orcidId: orcidId, + userprofileId: userprofileId, + logUserIdentification: logUserIdentification); + + // Update Elasticsearch person index. + await _elasticsearchService.UpdateEntryInElasticsearchPersonIndex(orcidId, person, logUserIdentification); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_UPDATE, + state: LogContent.ActionState.COMPLETE)); + }); + } // Remove cached profile data response. Cache key is ORCID ID. _cache.Remove(orcidId); diff --git a/aspnetcore/src/api/Controllers/ResearchDatasetController.cs b/aspnetcore/src/api/Controllers/ResearchDatasetController.cs index 62699f37..1b884c0a 100644 --- a/aspnetcore/src/api/Controllers/ResearchDatasetController.cs +++ b/aspnetcore/src/api/Controllers/ResearchDatasetController.cs @@ -1,5 +1,6 @@ using api.Services; using api.Models.Api; +using api.Models.Log; using api.Models.Ttv; using api.Models.ProfileEditor; using api.Models.ProfileEditor.Items; @@ -12,6 +13,8 @@ using Microsoft.Extensions.Caching.Memory; using Microsoft.AspNetCore.Http; using api.Models.Common; +using Microsoft.Extensions.Logging; +using System; namespace api.Controllers { @@ -29,10 +32,18 @@ public class ResearchDatasetController : TtvControllerBase private readonly IDataSourceHelperService _dataSourceHelperService; private readonly ILanguageService _languageService; private readonly IMemoryCache _cache; + private readonly ILogger _logger; + private readonly IElasticsearchService _elasticsearchService; + private readonly IBackgroundProfiledata _backgroundProfiledata; + private readonly IBackgroundTaskQueue _taskQueue; public ResearchDatasetController(TtvContext ttvContext, IUserProfileService userProfileService, IUtilityService utilityService, IDataSourceHelperService dataSourceHelperService, - IMemoryCache memoryCache, ILanguageService languageService) + IMemoryCache memoryCache, ILanguageService languageService, + IElasticsearchService elasticsearchService, + ILogger logger, + IBackgroundProfiledata backgroundProfiledata, + IBackgroundTaskQueue taskQueue) { _ttvContext = ttvContext; _userProfileService = userProfileService; @@ -40,6 +51,10 @@ public ResearchDatasetController(TtvContext ttvContext, IUserProfileService user _dataSourceHelperService = dataSourceHelperService; _languageService = languageService; _cache = memoryCache; + _elasticsearchService = elasticsearchService; + _backgroundProfiledata = backgroundProfiledata; + _logger = logger; + _taskQueue = taskQueue; } /// @@ -84,7 +99,8 @@ public async Task PostMany([FromBody] List dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_RESEARCH_DATASET); + DimFieldDisplaySetting dimFieldDisplaySettingsResearchDataset = + dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_RESEARCH_DATASET); // Registered data source organization name translation NameTranslation nameTranslation_OrganizationName = _languageService.GetNameTranslation( @@ -108,8 +124,8 @@ public async Task PostMany([FromBody] List r.LocalIdentifier)) { bool researchDatasetProcessed = false; // Check if userprofile already includes given research dataset @@ -127,7 +143,8 @@ public async Task PostMany([FromBody] List drd.LocalIdentifier == researchDatasetToAdd.LocalIdentifier); + DimResearchDataset dimResearchDataset = + await _ttvContext.DimResearchDatasets.AsNoTracking().FirstOrDefaultAsync(drd => drd.LocalIdentifier == researchDatasetToAdd.LocalIdentifier); // Check if DimResearchDataset exists if (dimResearchDataset == null) { @@ -155,7 +172,39 @@ public async Task PostMany([FromBody] List + { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_UPDATE, + state: LogContent.ActionState.START)); + + // Get Elasticsearch person entry from profile data. + Models.Elasticsearch.ElasticsearchPerson person = + await _backgroundProfiledata.GetProfiledataForElasticsearch( + orcidId: orcidId, + userprofileId: userprofileId, + logUserIdentification: logUserIdentification); + + // Update Elasticsearch person index. + await _elasticsearchService.UpdateEntryInElasticsearchPersonIndex(orcidId, person, logUserIdentification); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_UPDATE, + state: LogContent.ActionState.COMPLETE)); + }); + } // Remove cached profile data response. Cache key is ORCID ID. _cache.Remove(orcidId); @@ -219,7 +268,39 @@ public async Task RemoveMany([FromBody] List localIdentif } await _ttvContext.SaveChangesAsync(); - // TODO: add Elasticsearch sync? + // Update Elasticsearch index in a background task. + // ElasticsearchService is singleton, no need to create local scope. + if (_elasticsearchService.IsElasticsearchSyncEnabled()) + { + LogUserIdentification logUserIdentification = this.GetLogUserIdentification(); + + await _taskQueue.QueueBackgroundWorkItemAsync(async token => + { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_UPDATE, + state: LogContent.ActionState.START)); + + // Get Elasticsearch person entry from profile data. + Models.Elasticsearch.ElasticsearchPerson person = + await _backgroundProfiledata.GetProfiledataForElasticsearch( + orcidId: orcidId, + userprofileId: userprofileId, + logUserIdentification: logUserIdentification); + + // Update Elasticsearch person index. + await _elasticsearchService.UpdateEntryInElasticsearchPersonIndex(orcidId, person, logUserIdentification); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_UPDATE, + state: LogContent.ActionState.COMPLETE)); + }); + } // Remove cached profile data response. Cache key is ORCID ID. _cache.Remove(orcidId); diff --git a/aspnetcore/src/api/Controllers/TtvAdminControllerBase.cs b/aspnetcore/src/api/Controllers/TtvAdminControllerBase.cs new file mode 100644 index 00000000..de15e237 --- /dev/null +++ b/aspnetcore/src/api/Controllers/TtvAdminControllerBase.cs @@ -0,0 +1,27 @@ +using api.Models.Log; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; + +/* + * TtvAdminControllerBase implements utility methods which can be used by all admin controllers. + */ +public abstract class TtvAdminControllerBase : ControllerBase +{ + // Get that request contains required admin token + [NonAction] + protected bool IsAdminTokenAuthorized(IConfiguration configuration) + { + return !string.IsNullOrWhiteSpace(configuration["ADMINTOKEN"]) && Request.Headers["admintoken"] == configuration["ADMINTOKEN"]; + } + + // Get user identification object for structured logging. + [NonAction] + protected LogUserIdentification GetLogUserIdentification() + { + return new LogUserIdentification( + keycloakId: "", + orcid: "", + ip: HttpContext.Connection.RemoteIpAddress?.ToString() + ); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Controllers/TtvControllerBase.cs b/aspnetcore/src/api/Controllers/TtvControllerBase.cs index 7bb776af..f80fab36 100644 --- a/aspnetcore/src/api/Controllers/TtvControllerBase.cs +++ b/aspnetcore/src/api/Controllers/TtvControllerBase.cs @@ -2,6 +2,7 @@ using System.Linq; using Microsoft.AspNetCore.Mvc; using Microsoft.Net.Http.Headers; +using api.Models.Log; /* * TtvControllerBase implements utility methods which can be used by all controllers. @@ -37,6 +38,7 @@ protected string GetOrcidAccessToken() return User.Claims.FirstOrDefault(x => x.Type == "orcid_access_token")?.Value; } + /* // Get prefix for log message // [Keycloak user ID][ORCID ID][ip address] [NonAction] @@ -44,6 +46,7 @@ public string GetLogPrefix() { return "[ORCID=" + this.GetOrcidId() + "][IP=" + HttpContext.Connection.RemoteIpAddress?.ToString() + "][Keycloak ID=" + this.GetKeycloakUserId() + "]"; } + */ // Get ORCID public API flag from user claims. // This flag is used in testing phase only. @@ -52,4 +55,15 @@ protected string GetOrcidPublicApiFlag() { return User.Claims.FirstOrDefault(x => x.Type == "use_orcid_public_api")?.Value; } + + // Get user identification object for structured logging. + [NonAction] + protected LogUserIdentification GetLogUserIdentification() + { + return new LogUserIdentification( + keycloakId: this.GetKeycloakUserId(), + orcid: this.GetOrcidId(), + ip: HttpContext.Connection.RemoteIpAddress?.ToString() + ); + } } \ No newline at end of file diff --git a/aspnetcore/src/api/Controllers/UserProfileController.cs b/aspnetcore/src/api/Controllers/UserProfileController.cs index 9b0f7622..283baff1 100644 --- a/aspnetcore/src/api/Controllers/UserProfileController.cs +++ b/aspnetcore/src/api/Controllers/UserProfileController.cs @@ -1,5 +1,6 @@ using api.Services; using api.Models.Api; +using api.Models.Log; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; @@ -7,6 +8,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Caching.Memory; using Microsoft.AspNetCore.Http; +using Serilog; namespace api.Controllers { @@ -21,6 +23,7 @@ public class UserProfileController : TtvControllerBase private readonly IUserProfileService _userProfileService; private readonly IElasticsearchService _elasticsearchService; private readonly IKeycloakAdminApiService _keycloakAdminApiService; + private readonly IOrcidApiService _orcidApiService; private readonly ILogger _logger; private readonly IMemoryCache _cache; private readonly IBackgroundTaskQueue _taskQueue; @@ -29,6 +32,7 @@ public UserProfileController( IElasticsearchService elasticsearchService, IUserProfileService userProfileService, IKeycloakAdminApiService keycloakAdminApiService, + IOrcidApiService orcidApiService, ILogger logger, IMemoryCache memoryCache, IBackgroundTaskQueue taskQueue) @@ -36,6 +40,7 @@ public UserProfileController( _userProfileService = userProfileService; _elasticsearchService = elasticsearchService; _keycloakAdminApiService = keycloakAdminApiService; + _orcidApiService = orcidApiService; _logger = logger; _cache = memoryCache; _taskQueue = taskQueue; @@ -68,28 +73,97 @@ public async Task Create() // Get ORCID id. string orcidId = GetOrcidId(); // Log request - _logger.LogInformation(this.GetLogPrefix() + " create profile request"); + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.PROFILE_CREATE, + state: LogContent.ActionState.START)); // Return immediately, if profile already exist. + // Log error, but pass silently in user profile API. if (await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) { - _logger.LogInformation(this.GetLogPrefix() + " profile already exists"); + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.PROFILE_CREATE, + state: LogContent.ActionState.FAILED, + error: true, + message: "already exists")); return Ok(new ApiResponse(success: true)); } // Create profile try { - await _userProfileService.CreateProfile(orcidId: orcidId); + await _userProfileService.CreateProfile(orcidId: orcidId, logUserIdentification: this.GetLogUserIdentification()); } catch { - string msg = " create profile failed"; - _logger.LogError(this.GetLogPrefix() + msg); - return Ok(new ApiResponse(success: false, reason: msg)); + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.PROFILE_CREATE, + state: LogContent.ActionState.FAILED, + error: true, + message: LogContent.ActionState.FAILED)); + return Ok(new ApiResponse(success: false, reason: "create profile failed")); + } + + // Register ORCID webhook. Continue profile creation in case of error. + if (_orcidApiService.IsOrcidWebhookEnabled()) + { + try + { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.ORCID_WEBHOOK_REGISTER, + state: LogContent.ActionState.START)); + + await _orcidApiService.RegisterOrcidWebhook(orcidId: orcidId); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.ORCID_WEBHOOK_REGISTER, + state: LogContent.ActionState.COMPLETE)); + } + catch (Exception ex) + { + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.ORCID_WEBHOOK_REGISTER, + state: LogContent.ActionState.FAILED, + error: true, + message: ex.ToString())); + } + } + else + { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.ORCID_WEBHOOK_REGISTER, + state: LogContent.ActionState.FAILED, + error: true, + message: "disabled in configuration")); } - _logger.LogInformation(this.GetLogPrefix() + " create profile OK"); + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + this.GetLogUserIdentification(), + new LogApiInfo( + action: LogContent.Action.PROFILE_CREATE, + state: LogContent.ActionState.COMPLETE)); return Ok(new ApiResponse(success: true)); } @@ -103,13 +177,28 @@ public async Task Delete() { // Get ORCID id. string orcidId = GetOrcidId(); + + LogUserIdentification logUserIdentification = this.GetLogUserIdentification(); + // Log request. - _logger.LogInformation(this.GetLogPrefix() + " delete profile request"); + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.PROFILE_DELETE, + state: LogContent.ActionState.START)); // Return immediately, if profile does not exist. if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: orcidId)) { - _logger.LogInformation(this.GetLogPrefix() + " nothing deleted, profile does not exist"); + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.PROFILE_DELETE, + state: LogContent.ActionState.FAILED, + error: true, + message: "profile does not exist")); return Ok(new ApiResponse(success: true)); } @@ -120,52 +209,136 @@ public async Task Delete() bool deleteSuccess = false; try { - deleteSuccess = await _userProfileService.DeleteProfileDataAsync(userprofileId: userprofileId); + deleteSuccess = await _userProfileService.DeleteProfileDataAsync(userprofileId: userprofileId, logUserIdentification: this.GetLogUserIdentification()); } catch (Exception ex) { - _logger.LogError(this.GetLogPrefix() + ex.ToString()); + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.PROFILE_DELETE, + state: LogContent.ActionState.FAILED, + error: true, + message: ex.ToString())); } if (deleteSuccess) { // Log deletion - _logger.LogInformation(this.GetLogPrefix() + " delete profile from database OK"); + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.PROFILE_DELETE, + state: LogContent.ActionState.START)); // Remove cached profile data response. Cache key is ORCID ID. _cache.Remove(orcidId); // Remove entry from Elasticsearch index in a background task. + // ElasticsearchService is singleton, no need to create local scope. if (_elasticsearchService.IsElasticsearchSyncEnabled()) { await _taskQueue.QueueBackgroundWorkItemAsync(async token => { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_DELETE, + state: LogContent.ActionState.START)); + // Update Elasticsearch person index. - bool deleteSuccess = await _elasticsearchService.DeleteEntryFromElasticsearchPersonIndex(orcidId); + bool deleteSuccess = await _elasticsearchService.DeleteEntryFromElasticsearchPersonIndex(orcidId, logUserIdentification); if (deleteSuccess) { - _logger.LogInformation($"Elasticsearch: {orcidId} delete OK."); + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_DELETE, + state: LogContent.ActionState.COMPLETE)); } else { - _logger.LogError($"Elasticsearch: {orcidId} delete failed."); + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_DELETE, + state: LogContent.ActionState.FAILED, + error: true)); } }); } // Keycloak: logout user - await _keycloakAdminApiService.LogoutUser(this.GetBearerTokenFromHttpRequest()); + await _keycloakAdminApiService.LogoutUser(this.GetBearerTokenFromHttpRequest(), logUserIdentification); // Keycloak: remove user - await _keycloakAdminApiService.RemoveUser(this.GetBearerTokenFromHttpRequest()); + await _keycloakAdminApiService.RemoveUser(this.GetBearerTokenFromHttpRequest(), logUserIdentification); + + // Unregister ORCID webhook. Continue profile deletion in case of error. + if (_orcidApiService.IsOrcidWebhookEnabled()) + { + try + { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ORCID_WEBHOOK_UNREGISTER, + state: LogContent.ActionState.START)); + + await _orcidApiService.UnregisterOrcidWebhook(orcidId: orcidId); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ORCID_WEBHOOK_UNREGISTER, + state: LogContent.ActionState.COMPLETE)); + } + catch (Exception ex) + { + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ORCID_WEBHOOK_UNREGISTER, + state: LogContent.ActionState.FAILED, + error: true, + message: ex.ToString())); + } + } + else + { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ORCID_WEBHOOK_UNREGISTER, + state: LogContent.ActionState.FAILED, + error: true, + message: "disabled in configuration")); + } return Ok(new ApiResponse(success: true)); } else { // Log error - string msg = " delete profile from database failed"; - _logger.LogError(this.GetLogPrefix() + msg); + string msg = "database delete failed"; + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.PROFILE_DELETE, + state: LogContent.ActionState.FAILED, + error: true, + message: msg)); + return Ok(new ApiResponse(success: false, reason: msg)); } } diff --git a/aspnetcore/src/api/Controllers/WebhookController.cs b/aspnetcore/src/api/Controllers/WebhookController.cs new file mode 100644 index 00000000..215f85ae --- /dev/null +++ b/aspnetcore/src/api/Controllers/WebhookController.cs @@ -0,0 +1,116 @@ +using api.Services; +using api.Models.Ttv; +using Microsoft.AspNetCore.Mvc; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using System; +using api.Models.Api; +using api.Models.Orcid; +using Microsoft.Extensions.DependencyInjection; + +namespace api.Controllers +{ + /* + * WebhookController handles webhook related actions: + * - ORCID notification webhook: update ORCID data in a background task. + * + * This controller does not require authorization, since the endpoints must + * be accessible from 3rd party services. Each endpoint must prevent misuse + * by, for example, requiring presence of predefined token in the webhook url. + */ + [ApiController] + public class WebhookController : ControllerBase + { + private readonly IUserProfileService _userProfileService; + private readonly ILogger _logger; + private readonly IBackgroundTaskQueue _taskQueue; + private readonly IServiceScopeFactory _serviceScopeFactory; + + public WebhookController(IUserProfileService userProfileService, + ILogger logger, + IBackgroundTaskQueue taskQueue, + IServiceScopeFactory serviceScopeFactory) + { + _userProfileService = userProfileService; + _logger = logger; + _taskQueue = taskQueue; + _serviceScopeFactory = serviceScopeFactory; + } + + + /// + /// Handle ORCID webhook + /// 204 No Content should be returned if webhook is successfully received. + /// + [HttpPost] + [Route("api/webhook/orcid/{webhookOrcidId}")] + public async Task HandleOrcidWebhook(string webhookOrcidId) + { + string logPrefix = "ORCID webhook: "; + string orcidAccessToken = ""; + int dimUserprofileId = -1; + + // Validate request data + if (!ModelState.IsValid) + { + return BadRequest(); + } + + _logger.LogInformation($"{logPrefix}received for {webhookOrcidId}"); + + // Check that userprofile exists. + DimUserProfile dimUserProfile = await _userProfileService.GetUserprofile(orcidId: webhookOrcidId); + if (dimUserProfile == null) + { + _logger.LogError($"{logPrefix}user profile not found: {webhookOrcidId}"); + return NoContent(); + } + + // Store values to be used in background task. + orcidAccessToken = dimUserProfile.OrcidAccessToken; + dimUserprofileId = dimUserProfile.Id; + + // Get ORCID data in a background task. + await _taskQueue.QueueBackgroundWorkItemAsync(async token => + { + _logger.LogInformation($"{logPrefix}background update for {webhookOrcidId} started {DateTime.UtcNow}"); + + // Create service scope and get required services. + // Do not use services from controller scope in a background task. + using IServiceScope scope = _serviceScopeFactory.CreateScope(); + IOrcidApiService localOrcidApiService = scope.ServiceProvider.GetRequiredService(); + IOrcidImportService localOrcidImportService = scope.ServiceProvider.GetRequiredService(); + + // Get record json from ORCID member API + string orcidRecordJson = ""; + try + { + orcidRecordJson = await localOrcidApiService.GetRecordFromMemberApi(webhookOrcidId, orcidAccessToken); + _logger.LogInformation($"{logPrefix}background get record for {webhookOrcidId} OK"); + } + catch (Exception ex) + { + _logger.LogError($"{logPrefix}background get record for {webhookOrcidId} failed: {ex}"); + } + + // Import record json into userprofile + if (orcidRecordJson != "") + { + try + { + await localOrcidImportService.ImportOrcidRecordJsonIntoUserProfile(dimUserprofileId, orcidRecordJson); + _logger.LogInformation($"{logPrefix}background import record for {webhookOrcidId} to userprofile OK"); + } + catch (Exception ex) + { + _logger.LogError($"{logPrefix}background import record for {webhookOrcidId} to userprofile failed: {ex}"); + } + } + + _logger.LogInformation($"{logPrefix}background update for {webhookOrcidId} ended {DateTime.UtcNow}"); + }); + + return NoContent(); + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Models/Common/Constants.cs b/aspnetcore/src/api/Models/Common/Constants.cs index 60603453..0654caf2 100644 --- a/aspnetcore/src/api/Models/Common/Constants.cs +++ b/aspnetcore/src/api/Models/Common/Constants.cs @@ -67,8 +67,8 @@ public static class OrganizationIds public static class OrganizationNames { // TODO: check correct name, when these are properly populated in the database - public const string ORCID = "ORCID (test)"; - public const string TTV = "Tiedejatutkimus.fi (test)"; + public const string ORCID = "ORCID"; + public const string TTV = "Tiedejatutkimus.fi"; } public static class PurposeNames diff --git a/aspnetcore/src/api/Models/Common/DimTableMinimalDTO.cs b/aspnetcore/src/api/Models/Common/DimTableMinimalDTO.cs new file mode 100644 index 00000000..a2229164 --- /dev/null +++ b/aspnetcore/src/api/Models/Common/DimTableMinimalDTO.cs @@ -0,0 +1,8 @@ +namespace api.Models.Common +{ + public partial class DimTableMinimalDTO + { + public int Id { get; set; } + public int DimRegisteredDataSourceId { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Common/FactContributionTableMinimalDTO.cs b/aspnetcore/src/api/Models/Common/FactContributionTableMinimalDTO.cs new file mode 100644 index 00000000..d70698ad --- /dev/null +++ b/aspnetcore/src/api/Models/Common/FactContributionTableMinimalDTO.cs @@ -0,0 +1,9 @@ +namespace api.Models.Common +{ + public partial class FactContributionTableMinimalDTO + { + public int DimResearchActivityId { get; set; } + public int DimResearchDatasetId { get; set; } + public int DimPublicationId { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActivity.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActivity.cs index 37a8cd16..b04865fa 100644 --- a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActivity.cs +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActivity.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; +using Nest; namespace api.Models.Elasticsearch { + [ElasticsearchType(RelationName = "activity")] public partial class ElasticsearchActivity { public ElasticsearchActivity() @@ -14,6 +16,8 @@ public ElasticsearchActivity() activitiesAndRewards = new List(); } + [Nested] + [PropertyName("affiliations")] public List affiliations { get; set; } public List educations { get; set; } public List publications { get; set; } diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActivityAndReward.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActivityAndReward.cs index 2141e9ed..679efc40 100644 --- a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActivityAndReward.cs +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchActivityAndReward.cs @@ -15,6 +15,14 @@ public ElasticsearchActivityAndReward() InternationalCollaboration = false; StartDate = new ElasticsearchDate(); EndDate = new ElasticsearchDate(); + ActivityTypeCode = ""; + ActivityTypeNameFi = ""; + ActivityTypeNameEn = ""; + ActivityTypeNameSv = ""; + RoleCode = ""; + RoleNameFi = ""; + RoleNameEn = ""; + RoleNameSv = ""; } public string NameFi { get; set; } @@ -26,5 +34,13 @@ public ElasticsearchActivityAndReward() public bool InternationalCollaboration { get; set; } public ElasticsearchDate StartDate { get; set; } public ElasticsearchDate EndDate { get; set; } + public string ActivityTypeCode { get; set; } + public string ActivityTypeNameFi { get; set; } + public string ActivityTypeNameEn { get; set; } + public string ActivityTypeNameSv { get; set; } + public string RoleCode { get; set; } + public string RoleNameFi { get; set; } + public string RoleNameEn { get; set; } + public string RoleNameSv { get; set; } } } diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchAffiliation.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchAffiliation.cs index a76f15ab..02237df2 100644 --- a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchAffiliation.cs +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchAffiliation.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; +using Nest; namespace api.Models.Elasticsearch { + [ElasticsearchType(RelationName = "affiliations")] public partial class ElasticsearchAffiliation : ElasticsearchItem { public ElasticsearchAffiliation() @@ -18,6 +20,7 @@ public ElasticsearchAffiliation() Type = ""; StartDate = new ElasticsearchDate(); EndDate = new ElasticsearchDate(); + sector = new List {}; } public string OrganizationNameFi { get; set; } @@ -32,5 +35,9 @@ public ElasticsearchAffiliation() public string Type { get; set; } public ElasticsearchDate StartDate { get; set; } public ElasticsearchDate EndDate { get; set; } + + [Nested] + [PropertyName("sector")] + public List sector { get; set; } } } diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPerson.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPerson.cs index 8ea57fe0..b9f6471e 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 Nest; namespace api.Models.Elasticsearch { @@ -22,6 +23,8 @@ public ElasticsearchPerson(string orcidId) public string id { get; set; } public ElasticsearchPersonal personal { get; set; } + [Nested] + [PropertyName("activity")] public ElasticsearchActivity activity { get; set; } public List uniqueDataSources { get; set; } } diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPublication.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPublication.cs index 3d083847..6cd54698 100644 --- a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPublication.cs +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchPublication.cs @@ -10,13 +10,21 @@ public ElasticsearchPublication() PublicationName = ""; PublicationYear = null; Doi = ""; + AuthorsText = ""; TypeCode = ""; + JournalName = ""; + ConferenceName = ""; + ParentPublicationName = ""; } public string PublicationId { get; set; } public string PublicationName { get; set; } public int? PublicationYear { get; set; } public string Doi { get; set; } + public string AuthorsText { get; set; } public string TypeCode { get; set; } + public string JournalName { get; set; } + public string ConferenceName { get; set; } + public string ParentPublicationName { get; set; } } } diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchSector.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchSector.cs new file mode 100644 index 00000000..bf07257c --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchSector.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Nest; + +namespace api.Models.Elasticsearch +{ + [ElasticsearchType(RelationName = "sector")] + public partial class ElasticsearchSector + { + public ElasticsearchSector() + { + sectorId = ""; + nameFiSector = ""; + nameEnSector = ""; + nameSvSector = ""; + organization = new List { new ElasticsearchSectorOrganization() }; + } + + public string sectorId { get; set; } + public string nameFiSector { get; set; } + public string nameEnSector { get; set; } + public string nameSvSector { get; set; } + + [Nested] + [PropertyName("organization")] + public List organization { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchSectorOrganization.cs b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchSectorOrganization.cs new file mode 100644 index 00000000..588985ba --- /dev/null +++ b/aspnetcore/src/api/Models/Elasticsearch/ElasticsearchSectorOrganization.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace api.Models.Elasticsearch +{ + public partial class ElasticsearchSectorOrganization + { + public ElasticsearchSectorOrganization() + { + organizationId = ""; + OrganizationNameFi = ""; + OrganizationNameEn = ""; + OrganizationNameSv = ""; + } + + public string organizationId { get; set; } + public string OrganizationNameFi { get; set; } + public string OrganizationNameEn { get; set; } + public string OrganizationNameSv { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorActivityAndReward.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorActivityAndReward.cs index 135fba5f..05378609 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorActivityAndReward.cs +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorActivityAndReward.cs @@ -13,6 +13,14 @@ public ProfileEditorActivityAndReward() InternationalCollaboration = false; StartDate = new ProfileEditorDate(); EndDate = new ProfileEditorDate(); + ActivityTypeCode = ""; + ActivityTypeNameFi = ""; + ActivityTypeNameEn = ""; + ActivityTypeNameSv = ""; + RoleCode = ""; + RoleNameFi = ""; + RoleNameEn = ""; + RoleNameSv = ""; } public string NameFi { get; set; } @@ -24,5 +32,13 @@ public ProfileEditorActivityAndReward() public bool InternationalCollaboration { get; set; } public ProfileEditorDate StartDate { get; set; } public ProfileEditorDate EndDate { get; set; } + public string ActivityTypeCode { get; set; } + public string ActivityTypeNameFi { get; set; } + public string ActivityTypeNameEn { get; set; } + public string ActivityTypeNameSv { get; set; } + public string RoleCode { get; set; } + public string RoleNameFi { get; set; } + public string RoleNameEn { get; set; } + public string RoleNameSv { get; set; } } } diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorAffiliation.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorAffiliation.cs index be6909de..53d0e3ef 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorAffiliation.cs +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorAffiliation.cs @@ -1,4 +1,7 @@ -namespace api.Models.ProfileEditor.Items +using api.Models.Elasticsearch; +using System.Collections.Generic; + +namespace api.Models.ProfileEditor.Items { public partial class ProfileEditorAffiliation : ProfileEditorItem { @@ -16,6 +19,7 @@ public ProfileEditorAffiliation() Type = ""; StartDate = new ProfileEditorDate(); EndDate = new ProfileEditorDate(); + sector = new List { }; } public string OrganizationNameFi { get; set; } @@ -30,5 +34,8 @@ public ProfileEditorAffiliation() public string Type { get; set; } public ProfileEditorDate StartDate { get; set; } public ProfileEditorDate EndDate { get; set; } + + // Fields required in Elasticsearch person index. Elasticsearch model is mapped from ProfileEditor model. + public List sector { get; set; } } } diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorPublication.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorPublication.cs index f2ef079b..253f5db2 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorPublication.cs +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorPublication.cs @@ -10,13 +10,21 @@ public ProfileEditorPublication() PublicationName = ""; PublicationYear = null; Doi = ""; + AuthorsText = ""; TypeCode = ""; + JournalName = ""; + ConferenceName = ""; + ParentPublicationName = ""; } public string PublicationId { get; set; } public string PublicationName { get; set; } public int? PublicationYear { get; set; } public string Doi { get; set; } + public string AuthorsText { get; set; } public string TypeCode { get; set; } + public string JournalName { get; set; } + public string ConferenceName { get; set; } + public string ParentPublicationName { get; set; } } } diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorSector.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorSector.cs new file mode 100644 index 00000000..b4790980 --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorSector.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorSector + { + public ProfileEditorSector() + { + sectorId = ""; + nameFiSector = ""; + nameEnSector = ""; + nameSvSector = ""; + organization = new List {}; + } + + public string sectorId { get; set; } + public string nameFiSector { get; set; } + public string nameEnSector { get; set; } + public string nameSvSector { get; set; } + public List organization { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorSectorOrganization.cs b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorSectorOrganization.cs new file mode 100644 index 00000000..b238c8fe --- /dev/null +++ b/aspnetcore/src/api/Models/ProfileEditor/Items/ProfileEditorSectorOrganization.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace api.Models.ProfileEditor.Items +{ + public partial class ProfileEditorSectorOrganization + { + public ProfileEditorSectorOrganization() + { + organizationId = ""; + OrganizationNameFi = ""; + OrganizationNameEn = ""; + OrganizationNameSv = ""; + } + + public string organizationId { get; set; } + public string OrganizationNameFi { get; set; } + public string OrganizationNameEn { get; set; } + public string OrganizationNameSv { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/ProfileEditor/ProfileDataFromSql.cs b/aspnetcore/src/api/Models/ProfileEditor/ProfileDataFromSql.cs index 795ba5eb..bb1d9c97 100644 --- a/aspnetcore/src/api/Models/ProfileEditor/ProfileDataFromSql.cs +++ b/aspnetcore/src/api/Models/ProfileEditor/ProfileDataFromSql.cs @@ -20,7 +20,7 @@ public ProfileDataFromSql() public int FactFieldValues_DimResearcherDescriptionId { get; set; } public int FactFieldValues_DimEmailAddrressId { get; set; } public int FactFieldValues_DimTelephoneNumberId { get; set; } - public int FactFieldValues_DimFieldOfScienceId { get; set; } + public int FactFieldValues_DimReferencedataFieldOfScienceId { get; set; } public int FactFieldValues_DimKeywordId { get; set; } public int FactFieldValues_DimPidId { get; set; } public int FactFieldValues_DimAffiliationId { get; set; } @@ -66,10 +66,6 @@ public ProfileDataFromSql() public string DimEmailAddrress_Email { get; set; } // DimTelephoneNumber public string DimTelephoneNumber_TelephoneNumber { get; set; } - // DimFieldOfScience - public string DimFieldOfScience_NameFi { get; set; } - public string DimFieldOfScience_NameEn { get; set; } - public string DimFieldOfScience_NameSv { get; set; } // DimKeyword public string DimKeyword_Keyword { get; set; } // DimPid @@ -77,9 +73,23 @@ public ProfileDataFromSql() public string DimPid_PidType { get; set; } // DimAffiliation public int DimAffiliation_DimOrganization_Id { get; set; } + public string DimAffiliation_DimOrganization_OrganizationId { get; set; } public string DimAffiliation_DimOrganization_NameFi { get; set; } public string DimAffiliation_DimOrganization_NameEn { get; set; } public string DimAffiliation_DimOrganization_NameSv { get; set; } + public string DimAffiliation_DimOrganization_DimSector_SectorId { get; set; } + public string DimAffiliation_DimOrganization_DimSector_NameFi { get; set; } + public string DimAffiliation_DimOrganization_DimSector_NameEn { get; set; } + public string DimAffiliation_DimOrganization_DimSector_NameSv { get; set; } + public int DimAffiliation_DimOrganizationBroader_Id { get; set; } + public string DimAffiliation_DimOrganizationBroader_OrganizationId { get; set; } + public string DimAffiliation_DimOrganizationBroader_NameFi { get; set; } + public string DimAffiliation_DimOrganizationBroader_NameEn { get; set; } + public string DimAffiliation_DimOrganizationBroader_NameSv { get; set; } + public string DimAffiliation_DimOrganizationBroader_DimSector_SectorId { get; set; } + public string DimAffiliation_DimOrganizationBroader_DimSector_NameFi { get; set; } + public string DimAffiliation_DimOrganizationBroader_DimSector_NameEn { get; set; } + public string DimAffiliation_DimOrganizationBroader_DimSector_NameSv { get; set; } public string DimAffiliation_PositionNameFi { get; set; } public string DimAffiliation_PositionNameEn { get; set; } public string DimAffiliation_PositionNameSv { get; set; } @@ -118,7 +128,11 @@ public ProfileDataFromSql() public string DimPublication_PublicationName { get; set; } public int DimPublication_PublicationYear { get; set; } public string DimPublication_Doi { get; set; } + public string DimPublication_AuthorsText { get; set; } public string DimPublication_PublicationTypeCode { get; set; } + public string DimPublication_JournalName { get; set; } + public string DimPublication_ConferenceName { get; set; } + public string DimPublication_ParentPublicationName { get; set; } // DimOrcidPublication public string DimOrcidPublication_PublicationId { get; set; } public string DimOrcidPublication_PublicationName { get; set; } @@ -138,6 +152,14 @@ public ProfileDataFromSql() public int DimResearchActivity_EndDate_Year { get; set; } public int DimResearchActivity_EndDate_Month { get; set; } public int DimResearchActivity_EndDate_Day { get; set; } + public string DimResearchActivity_ActivityType_CodeValue { get; set; } + public string DimResearchActivity_ActivityType_NameFi { get; set; } + public string DimResearchActivity_ActivityType_NameEn { get; set; } + public string DimResearchActivity_ActivityType_NameSv { get; set; } + public string DimResearchActivity_Role_CodeValue { get; set; } + public string DimResearchActivity_Role_NameFi { get; set; } + public string DimResearchActivity_Role_NameEn { get; set; } + public string DimResearchActivity_Role_NameSv { get; set; } // DimFundingDecision public string DimFundingDecision_Acronym { get; set; } public string DimFundingDecision_FunderProjectNumber { get; set; } diff --git a/aspnetcore/src/api/Models/StructuredLog/LogApiInfo.cs b/aspnetcore/src/api/Models/StructuredLog/LogApiInfo.cs new file mode 100644 index 00000000..279fc5a5 --- /dev/null +++ b/aspnetcore/src/api/Models/StructuredLog/LogApiInfo.cs @@ -0,0 +1,42 @@ +namespace api.Models.Log +{ + public class LogApiInfo + { + public LogApiInfo(string action, string state, bool error, string message) + { + Action = action; + State = state; + Error = error; + Message = message; + } + + public LogApiInfo(string action, string state, bool error) + { + Action = action; + State = state; + Error = error; + Message = ""; + } + + public LogApiInfo(string action, string state, string message) + { + Action = action; + State = state; + Error = false; + Message = message; + } + + public LogApiInfo(string action, string state) + { + Action = action; + State = state; + Error = false; + Message = ""; + } + + public string Action { get; set; } + public string State { get; set; } + public bool Error { get; set; } + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Models/StructuredLog/LogContent.cs b/aspnetcore/src/api/Models/StructuredLog/LogContent.cs new file mode 100644 index 00000000..bba6e309 --- /dev/null +++ b/aspnetcore/src/api/Models/StructuredLog/LogContent.cs @@ -0,0 +1,43 @@ +namespace api.Models.Log +{ + public static class LogContent + { + public const string MESSAGE_TEMPLATE = "{@LogUserIdentification}, {@LogApiInfo}"; + + public static class Action + { + public const string ADMIN_WEBHOOK_ORCID_REGISTER = "Admin: ORCID webhook: register"; + public const string ADMIN_WEBHOOK_ORCID_UNREGISTER = "Admin: ORCID webhook: unregister"; + public const string ADMIN_WEBHOOK_ORCID_REGISTER_ALL = "Admin: ORCID webhook: register all"; + public const string ADMIN_WEBHOOK_ORCID_UNREGISTER_ALL = "Admin: ORCID webhook unregister all"; + public const string ADMIN_ELASTICSEARCH_PROFILE_DELETE = "Admin: Elasticsearch: profile: delete"; + public const string ADMIN_ELASTICSEARCH_PROFILE_UPDATE = "Admin: Elasticsearch: profile: update"; + public const string ELASTICSEARCH_DELETE = "Elasticsearch: profile: delete"; + public const string ELASTICSEARCH_UPDATE = "Elasticsearch: profile: update"; + public const string KEYCLOAK_ACCOUNT_DELETE = "Keycloak: account delete"; + public const string KEYCLOAK_GET_ORCID_TOKENS = "Keycloak: get ORCID tokens"; + public const string KEYCLOAK_GET_RAW_USER_DATA = "Keycloak: get raw user data"; + public const string KEYCLOAK_LINK_ORCID = "Keycloak: link ORCID"; + public const string KEYCLOAK_USER_DELETE = "Keycloak: user delete"; + public const string KEYCLOAK_USER_LOGOUT = "Keycloak: user logout"; + public const string KEYCLOAK_SET_ORCID_ATTRIBUTE = "Keycloak: set ORCID attribute"; + public const string PROFILE_CREATE = "Profile: create"; + public const string PROFILE_CREATE_ADD_TTV_DATA = "Profile: create: add TTV data"; + public const string PROFILE_DELETE = "Profile: delete"; + public const string PROFILE_HIDE = "Profile: hide"; + public const string PROFILE_MODIFY = "Profile: modify"; + public const string ORCID_RECORD_GET_MEMBER_API = "ORCID: record: get from member API"; + public const string ORCID_RECORD_GET_PUBLIC_API = "ORCID: record: get from public API"; + public const string ORCID_RECORD_IMPORT = "ORCID: record: import"; + public const string ORCID_WEBHOOK_REGISTER = "ORCID: webhook: register"; + public const string ORCID_WEBHOOK_UNREGISTER = "ORCID: webhook: unregister"; + } + + public static class ActionState + { + public const string START = "start"; + public const string COMPLETE = "complete"; + public const string FAILED = "failed"; + } + } +} diff --git a/aspnetcore/src/api/Models/StructuredLog/LogUserIdentification.cs b/aspnetcore/src/api/Models/StructuredLog/LogUserIdentification.cs new file mode 100644 index 00000000..daaceaf6 --- /dev/null +++ b/aspnetcore/src/api/Models/StructuredLog/LogUserIdentification.cs @@ -0,0 +1,30 @@ +namespace api.Models.Log +{ + public class LogUserIdentification + { + public LogUserIdentification(string orcid, string keycloakId, string ip) + { + Orcid = orcid; + KeycloakId = keycloakId; + Ip = ip; + } + + public LogUserIdentification(string orcid) + { + Orcid = orcid; + KeycloakId = ""; + Ip = ""; + } + + public LogUserIdentification() + { + Orcid = ""; + KeycloakId = ""; + Ip = ""; + } + + public string Orcid { get; set; } + public string KeycloakId { get; set; } + public string Ip { get; set; } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Models/Ttv/BrDatasetDatasetRelationship.cs b/aspnetcore/src/api/Models/Ttv/BrDatasetDatasetRelationship.cs index 3852c7c6..1836011c 100644 --- a/aspnetcore/src/api/Models/Ttv/BrDatasetDatasetRelationship.cs +++ b/aspnetcore/src/api/Models/Ttv/BrDatasetDatasetRelationship.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class BrDatasetDatasetRelationship diff --git a/aspnetcore/src/api/Models/Ttv/BrFundingConsortiumParticipation.cs b/aspnetcore/src/api/Models/Ttv/BrFundingConsortiumParticipation.cs index 9234a68d..78a564eb 100644 --- a/aspnetcore/src/api/Models/Ttv/BrFundingConsortiumParticipation.cs +++ b/aspnetcore/src/api/Models/Ttv/BrFundingConsortiumParticipation.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class BrFundingConsortiumParticipation diff --git a/aspnetcore/src/api/Models/Ttv/BrGrantedPermission.cs b/aspnetcore/src/api/Models/Ttv/BrGrantedPermission.cs index 1d46ccf7..3ded699f 100644 --- a/aspnetcore/src/api/Models/Ttv/BrGrantedPermission.cs +++ b/aspnetcore/src/api/Models/Ttv/BrGrantedPermission.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class BrGrantedPermission diff --git a/aspnetcore/src/api/Models/Ttv/BrParticipatesInFundingGroup.cs b/aspnetcore/src/api/Models/Ttv/BrParticipatesInFundingGroup.cs index fea062c4..0a098adb 100644 --- a/aspnetcore/src/api/Models/Ttv/BrParticipatesInFundingGroup.cs +++ b/aspnetcore/src/api/Models/Ttv/BrParticipatesInFundingGroup.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class BrParticipatesInFundingGroup diff --git a/aspnetcore/src/api/Models/Ttv/BrParticipatesInFundingGroupBcPoistetut.cs b/aspnetcore/src/api/Models/Ttv/BrParticipatesInFundingGroupBcPoistetut.cs new file mode 100644 index 00000000..8ab6641c --- /dev/null +++ b/aspnetcore/src/api/Models/Ttv/BrParticipatesInFundingGroupBcPoistetut.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +namespace api.Models.Ttv +{ + public partial class BrParticipatesInFundingGroupBcPoistetut + { + public int DimFundingDecisionid { get; set; } + public int DimNameId { get; set; } + public int DimOrganizationId { get; set; } + public string RoleInFundingGroup { get; set; } + public decimal? ShareOfFundingInEur { get; set; } + public string SourceId { get; set; } + public bool? EndOfParticipation { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Ttv/BrServiceSubscription.cs b/aspnetcore/src/api/Models/Ttv/BrServiceSubscription.cs index da5606a9..33df0420 100644 --- a/aspnetcore/src/api/Models/Ttv/BrServiceSubscription.cs +++ b/aspnetcore/src/api/Models/Ttv/BrServiceSubscription.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class BrServiceSubscription diff --git a/aspnetcore/src/api/Models/Ttv/BrWordClusterDimFundingDecision.cs b/aspnetcore/src/api/Models/Ttv/BrWordClusterDimFundingDecision.cs index 7f23e04f..838dba69 100644 --- a/aspnetcore/src/api/Models/Ttv/BrWordClusterDimFundingDecision.cs +++ b/aspnetcore/src/api/Models/Ttv/BrWordClusterDimFundingDecision.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class BrWordClusterDimFundingDecision diff --git a/aspnetcore/src/api/Models/Ttv/BrWordsDefineACluster.cs b/aspnetcore/src/api/Models/Ttv/BrWordsDefineACluster.cs index 52e24880..6895de73 100644 --- a/aspnetcore/src/api/Models/Ttv/BrWordsDefineACluster.cs +++ b/aspnetcore/src/api/Models/Ttv/BrWordsDefineACluster.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class BrWordsDefineACluster diff --git a/aspnetcore/src/api/Models/Ttv/DimAffiliation.cs b/aspnetcore/src/api/Models/Ttv/DimAffiliation.cs index f43b90ca..799b9a33 100644 --- a/aspnetcore/src/api/Models/Ttv/DimAffiliation.cs +++ b/aspnetcore/src/api/Models/Ttv/DimAffiliation.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimAffiliation diff --git a/aspnetcore/src/api/Models/Ttv/DimCallProgramme.cs b/aspnetcore/src/api/Models/Ttv/DimCallProgramme.cs index 7f998803..58065b51 100644 --- a/aspnetcore/src/api/Models/Ttv/DimCallProgramme.cs +++ b/aspnetcore/src/api/Models/Ttv/DimCallProgramme.cs @@ -1,25 +1,24 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimCallProgramme { public DimCallProgramme() { - BrCallProgrammeDimCallProgrammeDimCallProgrammeId2Navigations = new HashSet(); - BrCallProgrammeDimCallProgrammeDimCallProgrammes = new HashSet(); - BrDimReferencedataDimCallProgrammes = new HashSet(); - BrOrganizationsFundCallProgrammes = new HashSet(); DimFundingDecisions = new HashSet(); DimWebLinks = new HashSet(); + DimCallProgrammeId2s = new HashSet(); + DimCallProgrammes = new HashSet(); + DimOrganizations = new HashSet(); + DimReferencedata = new HashSet(); } public int Id { get; set; } public int DimDateIdDue { get; set; } public int DimDateIdOpen { get; set; } + public TimeSpan? DueDateDueTime { get; set; } public string Abbreviation { get; set; } public string EuCallId { get; set; } public string NameFi { get; set; } @@ -42,16 +41,16 @@ public DimCallProgramme() public string ContactInformation { get; set; } public bool? ContinuousApplicationPeriod { get; set; } public bool IsOpenCall { get; set; } - public TimeSpan? DueDateDueTime { get; set; } public virtual DimDate DimDateIdDueNavigation { get; set; } public virtual DimDate DimDateIdOpenNavigation { get; set; } public virtual DimRegisteredDataSource DimRegisteredDataSource { get; set; } - public virtual ICollection BrCallProgrammeDimCallProgrammeDimCallProgrammeId2Navigations { get; set; } - public virtual ICollection BrCallProgrammeDimCallProgrammeDimCallProgrammes { get; set; } - public virtual ICollection BrDimReferencedataDimCallProgrammes { get; set; } - public virtual ICollection BrOrganizationsFundCallProgrammes { get; set; } public virtual ICollection DimFundingDecisions { get; set; } public virtual ICollection DimWebLinks { get; set; } + + public virtual ICollection DimCallProgrammeId2s { get; set; } + public virtual ICollection DimCallProgrammes { get; set; } + public virtual ICollection DimOrganizations { get; set; } + public virtual ICollection DimReferencedata { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimCompetence.cs b/aspnetcore/src/api/Models/Ttv/DimCompetence.cs index 13d6210b..278d98f9 100644 --- a/aspnetcore/src/api/Models/Ttv/DimCompetence.cs +++ b/aspnetcore/src/api/Models/Ttv/DimCompetence.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimCompetence diff --git a/aspnetcore/src/api/Models/Ttv/DimDate.cs b/aspnetcore/src/api/Models/Ttv/DimDate.cs index d4458da0..83fe8bd3 100644 --- a/aspnetcore/src/api/Models/Ttv/DimDate.cs +++ b/aspnetcore/src/api/Models/Ttv/DimDate.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimDate diff --git a/aspnetcore/src/api/Models/Ttv/DimEducation.cs b/aspnetcore/src/api/Models/Ttv/DimEducation.cs index eeeeb926..fe695dc7 100644 --- a/aspnetcore/src/api/Models/Ttv/DimEducation.cs +++ b/aspnetcore/src/api/Models/Ttv/DimEducation.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimEducation @@ -13,7 +11,7 @@ public DimEducation() } public int Id { get; set; } - public int? LocalIdentifier { get; set; } + public string LocalIdentifier { get; set; } public float? Credits { get; set; } public string NameFi { get; set; } public string NameSv { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/DimEmailAddrress.cs b/aspnetcore/src/api/Models/Ttv/DimEmailAddrress.cs index a6f3d5f0..a30c1190 100644 --- a/aspnetcore/src/api/Models/Ttv/DimEmailAddrress.cs +++ b/aspnetcore/src/api/Models/Ttv/DimEmailAddrress.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimEmailAddrress diff --git a/aspnetcore/src/api/Models/Ttv/DimEsfri.cs b/aspnetcore/src/api/Models/Ttv/DimEsfri.cs index d28a9221..edb64e97 100644 --- a/aspnetcore/src/api/Models/Ttv/DimEsfri.cs +++ b/aspnetcore/src/api/Models/Ttv/DimEsfri.cs @@ -1,15 +1,13 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimEsfri { public DimEsfri() { - BrEsfriDimInfrastructures = new HashSet(); + DimInfrastructures = new HashSet(); } public int Id { get; set; } @@ -21,6 +19,6 @@ public DimEsfri() public DateTime? Modified { get; set; } public DateTime? Created { get; set; } - public virtual ICollection BrEsfriDimInfrastructures { get; set; } + public virtual ICollection DimInfrastructures { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimEvent.cs b/aspnetcore/src/api/Models/Ttv/DimEvent.cs index 4fd1d981..ab3019e4 100644 --- a/aspnetcore/src/api/Models/Ttv/DimEvent.cs +++ b/aspnetcore/src/api/Models/Ttv/DimEvent.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimEvent @@ -19,8 +17,8 @@ public DimEvent() public string NameSv { get; set; } public string NameEn { get; set; } public string NameUnd { get; set; } - public int? EventLocationText { get; set; } - public int DimDateIdStartDate { get; set; } + public string EventLocationText { get; set; } + public int? DimDateIdStartDate { get; set; } public int? DimDateIdEndDate { get; set; } public int? DimGeoIdEventCountry { get; set; } public string SourceId { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/DimExternalService.cs b/aspnetcore/src/api/Models/Ttv/DimExternalService.cs index 0a333cf2..0829a70b 100644 --- a/aspnetcore/src/api/Models/Ttv/DimExternalService.cs +++ b/aspnetcore/src/api/Models/Ttv/DimExternalService.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimExternalService diff --git a/aspnetcore/src/api/Models/Ttv/DimFieldDisplaySetting.cs b/aspnetcore/src/api/Models/Ttv/DimFieldDisplaySetting.cs index 87a653f1..bfe62f03 100644 --- a/aspnetcore/src/api/Models/Ttv/DimFieldDisplaySetting.cs +++ b/aspnetcore/src/api/Models/Ttv/DimFieldDisplaySetting.cs @@ -1,16 +1,14 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimFieldDisplaySetting { public DimFieldDisplaySetting() { - BrFieldDisplaySettingsDimRegisteredDataSources = new HashSet(); FactFieldValues = new HashSet(); + DimRegisteredDataSources = new HashSet(); } public int Id { get; set; } @@ -23,7 +21,8 @@ public DimFieldDisplaySetting() public DateTime? Modified { get; set; } public virtual DimUserProfile DimUserProfile { get; set; } - public virtual ICollection BrFieldDisplaySettingsDimRegisteredDataSources { get; set; } public virtual ICollection FactFieldValues { get; set; } + + public virtual ICollection DimRegisteredDataSources { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimFieldOfArt.cs b/aspnetcore/src/api/Models/Ttv/DimFieldOfArt.cs index 6b76cf32..cec1900b 100644 --- a/aspnetcore/src/api/Models/Ttv/DimFieldOfArt.cs +++ b/aspnetcore/src/api/Models/Ttv/DimFieldOfArt.cs @@ -1,16 +1,14 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimFieldOfArt { public DimFieldOfArt() { - BrFieldOfArtDimPublications = new HashSet(); - BrFundingDecisionDimFieldOfArts = new HashSet(); + DimFundingDecisions = new HashSet(); + DimPublications = new HashSet(); } public int Id { get; set; } @@ -23,7 +21,7 @@ public DimFieldOfArt() public DateTime? Created { get; set; } public DateTime? Modified { get; set; } - public virtual ICollection BrFieldOfArtDimPublications { get; set; } - public virtual ICollection BrFundingDecisionDimFieldOfArts { get; set; } + public virtual ICollection DimFundingDecisions { get; set; } + public virtual ICollection DimPublications { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimFieldOfEducation.cs b/aspnetcore/src/api/Models/Ttv/DimFieldOfEducation.cs index 290bf315..fd297ab4 100644 --- a/aspnetcore/src/api/Models/Ttv/DimFieldOfEducation.cs +++ b/aspnetcore/src/api/Models/Ttv/DimFieldOfEducation.cs @@ -1,15 +1,13 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimFieldOfEducation { public DimFieldOfEducation() { - BrFieldOfEducationDimPublications = new HashSet(); + DimPublications = new HashSet(); } public int Id { get; set; } @@ -22,6 +20,6 @@ public DimFieldOfEducation() public DateTime? Created { get; set; } public DateTime? Modified { get; set; } - public virtual ICollection BrFieldOfEducationDimPublications { get; set; } + public virtual ICollection DimPublications { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimFieldOfScience.cs b/aspnetcore/src/api/Models/Ttv/DimFieldOfScience.cs index 8df8a923..53ed73dd 100644 --- a/aspnetcore/src/api/Models/Ttv/DimFieldOfScience.cs +++ b/aspnetcore/src/api/Models/Ttv/DimFieldOfScience.cs @@ -1,22 +1,10 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimFieldOfScience { - public DimFieldOfScience() - { - BrFieldOfScienceDimFundingDecisions = new HashSet(); - BrFieldOfScienceDimPublications = new HashSet(); - BrInfrastructureDimFieldOfSciences = new HashSet(); - DimFieldOfScienceDimResearchActivities = new HashSet(); - DimKnownPersonDimFieldOfSciences = new HashSet(); - FactFieldValues = new HashSet(); - } - public int Id { get; set; } public string FieldId { get; set; } public string NameFi { get; set; } @@ -26,12 +14,5 @@ public DimFieldOfScience() public string SourceDescription { get; set; } public DateTime? Created { get; set; } public DateTime? Modified { get; set; } - - public virtual ICollection BrFieldOfScienceDimFundingDecisions { get; set; } - public virtual ICollection BrFieldOfScienceDimPublications { get; set; } - public virtual ICollection BrInfrastructureDimFieldOfSciences { get; set; } - public virtual ICollection DimFieldOfScienceDimResearchActivities { get; set; } - public virtual ICollection DimKnownPersonDimFieldOfSciences { get; set; } - public virtual ICollection FactFieldValues { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimFundingDecision.cs b/aspnetcore/src/api/Models/Ttv/DimFundingDecision.cs index e9924d1e..9a7a3101 100644 --- a/aspnetcore/src/api/Models/Ttv/DimFundingDecision.cs +++ b/aspnetcore/src/api/Models/Ttv/DimFundingDecision.cs @@ -1,29 +1,27 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimFundingDecision { public DimFundingDecision() { - BrFieldOfScienceDimFundingDecisions = new HashSet(); BrFundingConsortiumParticipations = new HashSet(); - BrFundingDecisionDimFieldOfArts = new HashSet(); - BrKeywordDimFundingDecisions = new HashSet(); BrParticipatesInFundingGroups = new HashSet(); - BrPreviousFundingDecisionDimFundingDecisionFroms = new HashSet(); - BrPreviousFundingDecisionDimFundingDecisionTos = new HashSet(); - BrRelatedFundingDecisionDimFundingDecisionFroms = new HashSet(); - BrRelatedFundingDecisionDimFundingDecisionTos = new HashSet(); BrWordClusterDimFundingDecisions = new HashSet(); DimPids = new HashSet(); DimWebLinks = new HashSet(); FactContributions = new HashSet(); + FactDimReferencedataFieldOfSciences = new HashSet(); FactFieldValues = new HashSet(); InverseDimFundingDecisionIdParentDecisionNavigation = new HashSet(); + DimFieldOfArts = new HashSet(); + DimFundingDecisionFroms = new HashSet(); + DimFundingDecisionFromsNavigation = new HashSet(); + DimFundingDecisionTos = new HashSet(); + DimFundingDecisionTosNavigation = new HashSet(); + DimKeywords = new HashSet(); } public int Id { get; set; } @@ -67,20 +65,21 @@ public DimFundingDecision() public virtual DimOrganization DimOrganizationIdFunderNavigation { get; set; } public virtual DimRegisteredDataSource DimRegisteredDataSource { get; set; } public virtual DimTypeOfFunding DimTypeOfFunding { get; set; } - public virtual ICollection BrFieldOfScienceDimFundingDecisions { get; set; } public virtual ICollection BrFundingConsortiumParticipations { get; set; } - public virtual ICollection BrFundingDecisionDimFieldOfArts { get; set; } - public virtual ICollection BrKeywordDimFundingDecisions { get; set; } public virtual ICollection BrParticipatesInFundingGroups { get; set; } - public virtual ICollection BrPreviousFundingDecisionDimFundingDecisionFroms { get; set; } - public virtual ICollection BrPreviousFundingDecisionDimFundingDecisionTos { get; set; } - public virtual ICollection BrRelatedFundingDecisionDimFundingDecisionFroms { get; set; } - public virtual ICollection BrRelatedFundingDecisionDimFundingDecisionTos { get; set; } public virtual ICollection BrWordClusterDimFundingDecisions { get; set; } public virtual ICollection DimPids { get; set; } public virtual ICollection DimWebLinks { get; set; } public virtual ICollection FactContributions { get; set; } + public virtual ICollection FactDimReferencedataFieldOfSciences { get; set; } public virtual ICollection FactFieldValues { get; set; } public virtual ICollection InverseDimFundingDecisionIdParentDecisionNavigation { get; set; } + + public virtual ICollection DimFieldOfArts { get; set; } + public virtual ICollection DimFundingDecisionFroms { get; set; } + public virtual ICollection DimFundingDecisionFromsNavigation { get; set; } + public virtual ICollection DimFundingDecisionTos { get; set; } + public virtual ICollection DimFundingDecisionTosNavigation { get; set; } + public virtual ICollection DimKeywords { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimGeo.cs b/aspnetcore/src/api/Models/Ttv/DimGeo.cs index ddf16492..3e4e3395 100644 --- a/aspnetcore/src/api/Models/Ttv/DimGeo.cs +++ b/aspnetcore/src/api/Models/Ttv/DimGeo.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimGeo diff --git a/aspnetcore/src/api/Models/Ttv/DimIdentifierlessDatum.cs b/aspnetcore/src/api/Models/Ttv/DimIdentifierlessDatum.cs index cddab7d9..2f7319a1 100644 --- a/aspnetcore/src/api/Models/Ttv/DimIdentifierlessDatum.cs +++ b/aspnetcore/src/api/Models/Ttv/DimIdentifierlessDatum.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimIdentifierlessDatum diff --git a/aspnetcore/src/api/Models/Ttv/DimInfrastructure.cs b/aspnetcore/src/api/Models/Ttv/DimInfrastructure.cs index af86bd7f..db56f201 100644 --- a/aspnetcore/src/api/Models/Ttv/DimInfrastructure.cs +++ b/aspnetcore/src/api/Models/Ttv/DimInfrastructure.cs @@ -1,22 +1,20 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimInfrastructure { public DimInfrastructure() { - BrEsfriDimInfrastructures = new HashSet(); - BrInfrastructureDimFieldOfSciences = new HashSet(); - BrMerilDimInfrastructures = new HashSet(); DimPids = new HashSet(); FactContributions = new HashSet(); + FactDimReferencedataFieldOfSciences = new HashSet(); FactInfraKeywords = new HashSet(); FactUpkeeps = new HashSet(); InverseNextInfastructure = new HashSet(); + DimEsfris = new HashSet(); + DimMerils = new HashSet(); } public int Id { get; set; } @@ -41,13 +39,14 @@ public DimInfrastructure() public string ScientificDescriptionEn { get; set; } public virtual DimInfrastructure NextInfastructure { get; set; } - public virtual ICollection BrEsfriDimInfrastructures { get; set; } - public virtual ICollection BrInfrastructureDimFieldOfSciences { get; set; } - public virtual ICollection BrMerilDimInfrastructures { get; set; } public virtual ICollection DimPids { get; set; } public virtual ICollection FactContributions { get; set; } + public virtual ICollection FactDimReferencedataFieldOfSciences { get; set; } public virtual ICollection FactInfraKeywords { get; set; } public virtual ICollection FactUpkeeps { get; set; } public virtual ICollection InverseNextInfastructure { get; set; } + + public virtual ICollection DimEsfris { get; set; } + public virtual ICollection DimMerils { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimKeyword.cs b/aspnetcore/src/api/Models/Ttv/DimKeyword.cs index e08b8776..612d0364 100644 --- a/aspnetcore/src/api/Models/Ttv/DimKeyword.cs +++ b/aspnetcore/src/api/Models/Ttv/DimKeyword.cs @@ -1,21 +1,20 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimKeyword { public DimKeyword() { - BrKeywordDimFundingDecisions = new HashSet(); - BrKeywordDimPublications = new HashSet(); FactFieldValues = new HashSet(); FactInfraKeywords = new HashSet(); InverseDimKeywordCloseMatchNavigation = new HashSet(); InverseDimKeywordLanguageVariantNavigation = new HashSet(); InverseDimKeywordRelatedNavigation = new HashSet(); + DimFundingDecisions = new HashSet(); + DimPublications = new HashSet(); + DimResearchDatasets = new HashSet(); } public int Id { get; set; } @@ -37,12 +36,14 @@ public DimKeyword() public virtual DimKeyword DimKeywordLanguageVariantNavigation { get; set; } public virtual DimKeyword DimKeywordRelatedNavigation { get; set; } public virtual DimRegisteredDataSource DimRegisteredDataSource { get; set; } - public virtual ICollection BrKeywordDimFundingDecisions { get; set; } - public virtual ICollection BrKeywordDimPublications { get; set; } public virtual ICollection FactFieldValues { get; set; } public virtual ICollection FactInfraKeywords { get; set; } public virtual ICollection InverseDimKeywordCloseMatchNavigation { get; set; } public virtual ICollection InverseDimKeywordLanguageVariantNavigation { get; set; } public virtual ICollection InverseDimKeywordRelatedNavigation { get; set; } + + public virtual ICollection DimFundingDecisions { get; set; } + public virtual ICollection DimPublications { get; set; } + public virtual ICollection DimResearchDatasets { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimKnownPerson.cs b/aspnetcore/src/api/Models/Ttv/DimKnownPerson.cs index 98cc8aa7..73d056c6 100644 --- a/aspnetcore/src/api/Models/Ttv/DimKnownPerson.cs +++ b/aspnetcore/src/api/Models/Ttv/DimKnownPerson.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimKnownPerson @@ -13,7 +11,6 @@ public DimKnownPerson() DimCompetences = new HashSet(); DimEducations = new HashSet(); DimEmailAddrresses = new HashSet(); - DimKnownPersonDimFieldOfSciences = new HashSet(); DimNames = new HashSet(); DimOrcidPublications = new HashSet(); DimPids = new HashSet(); @@ -22,6 +19,7 @@ public DimKnownPerson() DimTelephoneNumbers = new HashSet(); DimUserProfiles = new HashSet(); DimWebLinks = new HashSet(); + FactDimReferencedataFieldOfSciences = new HashSet(); } public int Id { get; set; } @@ -32,11 +30,11 @@ public DimKnownPerson() public string SourceProjectId { get; set; } public int? DimRegisteredDataSourceId { get; set; } + public virtual DimRegisteredDataSource DimRegisteredDataSource { get; set; } public virtual ICollection DimAffiliations { get; set; } public virtual ICollection DimCompetences { get; set; } public virtual ICollection DimEducations { get; set; } public virtual ICollection DimEmailAddrresses { get; set; } - public virtual ICollection DimKnownPersonDimFieldOfSciences { get; set; } public virtual ICollection DimNames { get; set; } public virtual ICollection DimOrcidPublications { get; set; } public virtual ICollection DimPids { get; set; } @@ -45,5 +43,6 @@ public DimKnownPerson() public virtual ICollection DimTelephoneNumbers { get; set; } public virtual ICollection DimUserProfiles { get; set; } public virtual ICollection DimWebLinks { get; set; } + public virtual ICollection FactDimReferencedataFieldOfSciences { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimLocallyReportedPubInfo.cs b/aspnetcore/src/api/Models/Ttv/DimLocallyReportedPubInfo.cs index ab48772c..c36f1558 100644 --- a/aspnetcore/src/api/Models/Ttv/DimLocallyReportedPubInfo.cs +++ b/aspnetcore/src/api/Models/Ttv/DimLocallyReportedPubInfo.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimLocallyReportedPubInfo diff --git a/aspnetcore/src/api/Models/Ttv/DimMeril.cs b/aspnetcore/src/api/Models/Ttv/DimMeril.cs index a81828e9..ea017eca 100644 --- a/aspnetcore/src/api/Models/Ttv/DimMeril.cs +++ b/aspnetcore/src/api/Models/Ttv/DimMeril.cs @@ -1,15 +1,13 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimMeril { public DimMeril() { - BrMerilDimInfrastructures = new HashSet(); + DimInfrastructures = new HashSet(); } public int Id { get; set; } @@ -21,6 +19,6 @@ public DimMeril() public DateTime? Created { get; set; } public DateTime? Modified { get; set; } - public virtual ICollection BrMerilDimInfrastructures { get; set; } + public virtual ICollection DimInfrastructures { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimMinedWord.cs b/aspnetcore/src/api/Models/Ttv/DimMinedWord.cs index 12b82ec6..d7815e4b 100644 --- a/aspnetcore/src/api/Models/Ttv/DimMinedWord.cs +++ b/aspnetcore/src/api/Models/Ttv/DimMinedWord.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimMinedWord diff --git a/aspnetcore/src/api/Models/Ttv/DimName.cs b/aspnetcore/src/api/Models/Ttv/DimName.cs index a14dca32..35e443ed 100644 --- a/aspnetcore/src/api/Models/Ttv/DimName.cs +++ b/aspnetcore/src/api/Models/Ttv/DimName.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimName diff --git a/aspnetcore/src/api/Models/Ttv/DimNewsFeed.cs b/aspnetcore/src/api/Models/Ttv/DimNewsFeed.cs index 35061f52..383be3c5 100644 --- a/aspnetcore/src/api/Models/Ttv/DimNewsFeed.cs +++ b/aspnetcore/src/api/Models/Ttv/DimNewsFeed.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimNewsFeed diff --git a/aspnetcore/src/api/Models/Ttv/DimNewsItem.cs b/aspnetcore/src/api/Models/Ttv/DimNewsItem.cs index dec7dd17..7b2eca3a 100644 --- a/aspnetcore/src/api/Models/Ttv/DimNewsItem.cs +++ b/aspnetcore/src/api/Models/Ttv/DimNewsItem.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimNewsItem diff --git a/aspnetcore/src/api/Models/Ttv/DimOrcidPublication.cs b/aspnetcore/src/api/Models/Ttv/DimOrcidPublication.cs index b71c09cb..5b7427f8 100644 --- a/aspnetcore/src/api/Models/Ttv/DimOrcidPublication.cs +++ b/aspnetcore/src/api/Models/Ttv/DimOrcidPublication.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimOrcidPublication diff --git a/aspnetcore/src/api/Models/Ttv/DimOrganisationMedium.cs b/aspnetcore/src/api/Models/Ttv/DimOrganisationMedium.cs index 49592605..6f5c473c 100644 --- a/aspnetcore/src/api/Models/Ttv/DimOrganisationMedium.cs +++ b/aspnetcore/src/api/Models/Ttv/DimOrganisationMedium.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimOrganisationMedium diff --git a/aspnetcore/src/api/Models/Ttv/DimOrganization.cs b/aspnetcore/src/api/Models/Ttv/DimOrganization.cs index b8d461d6..22901534 100644 --- a/aspnetcore/src/api/Models/Ttv/DimOrganization.cs +++ b/aspnetcore/src/api/Models/Ttv/DimOrganization.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimOrganization @@ -10,12 +8,7 @@ public partial class DimOrganization public DimOrganization() { BrFundingConsortiumParticipations = new HashSet(); - BrOrganizationsFundCallProgrammes = new HashSet(); BrParticipatesInFundingGroups = new HashSet(); - BrPredecessorOrganizationDimOrganizationid2Navigations = new HashSet(); - BrPredecessorOrganizationDimOrganizations = new HashSet(); - BrSuccessorOrganizationDimOrganizationid2Navigations = new HashSet(); - BrSuccessorOrganizationDimOrganizations = new HashSet(); DimAffiliations = new HashSet(); DimExternalServices = new HashSet(); DimFundingDecisions = new HashSet(); @@ -27,6 +20,11 @@ public DimOrganization() FactContributions = new HashSet(); FactUpkeeps = new HashSet(); InverseDimOrganizationBroaderNavigation = new HashSet(); + DimCallProgrammes = new HashSet(); + DimOrganizationid2s = new HashSet(); + DimOrganizationid2sNavigation = new HashSet(); + DimOrganizations = new HashSet(); + DimOrganizationsNavigation = new HashSet(); } public int Id { get; set; } @@ -62,12 +60,7 @@ public DimOrganization() public virtual DimRegisteredDataSource DimRegisteredDataSource { get; set; } public virtual DimSector DimSector { get; set; } public virtual ICollection BrFundingConsortiumParticipations { get; set; } - public virtual ICollection BrOrganizationsFundCallProgrammes { get; set; } public virtual ICollection BrParticipatesInFundingGroups { get; set; } - public virtual ICollection BrPredecessorOrganizationDimOrganizationid2Navigations { get; set; } - public virtual ICollection BrPredecessorOrganizationDimOrganizations { get; set; } - public virtual ICollection BrSuccessorOrganizationDimOrganizationid2Navigations { get; set; } - public virtual ICollection BrSuccessorOrganizationDimOrganizations { get; set; } public virtual ICollection DimAffiliations { get; set; } public virtual ICollection DimExternalServices { get; set; } public virtual ICollection DimFundingDecisions { get; set; } @@ -79,5 +72,11 @@ public DimOrganization() public virtual ICollection FactContributions { get; set; } public virtual ICollection FactUpkeeps { get; set; } public virtual ICollection InverseDimOrganizationBroaderNavigation { get; set; } + + public virtual ICollection DimCallProgrammes { get; set; } + public virtual ICollection DimOrganizationid2s { get; set; } + public virtual ICollection DimOrganizationid2sNavigation { get; set; } + public virtual ICollection DimOrganizations { get; set; } + public virtual ICollection DimOrganizationsNavigation { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimPid.cs b/aspnetcore/src/api/Models/Ttv/DimPid.cs index 1c4431a2..053c3754 100644 --- a/aspnetcore/src/api/Models/Ttv/DimPid.cs +++ b/aspnetcore/src/api/Models/Ttv/DimPid.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimPid @@ -43,6 +41,7 @@ public DimPid() public virtual DimPublicationChannel DimPublicationChannel { get; set; } public virtual DimResearchActivity DimResearchActivity { get; set; } public virtual DimResearchDataCatalog DimResearchDataCatalog { get; set; } + public virtual DimResearchDataset DimResearchDataset { get; set; } public virtual DimService DimService { get; set; } public virtual ICollection FactFieldValueDimPidIdOrcidPutCodeNavigations { get; set; } public virtual ICollection FactFieldValueDimPids { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/DimPublication.cs b/aspnetcore/src/api/Models/Ttv/DimPublication.cs index b132d467..87f1bac1 100644 --- a/aspnetcore/src/api/Models/Ttv/DimPublication.cs +++ b/aspnetcore/src/api/Models/Ttv/DimPublication.cs @@ -1,23 +1,21 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimPublication { public DimPublication() { - BrArtpublicationTypecategories = new HashSet(); - BrFieldOfArtDimPublications = new HashSet(); - BrFieldOfEducationDimPublications = new HashSet(); - BrFieldOfScienceDimPublications = new HashSet(); - BrKeywordDimPublications = new HashSet(); DimLocallyReportedPubInfos = new HashSet(); DimPids = new HashSet(); FactContributions = new HashSet(); + FactDimReferencedataFieldOfSciences = new HashSet(); FactFieldValues = new HashSet(); + DimFieldOfArts = new HashSet(); + DimFieldOfEducations = new HashSet(); + DimKeywords = new HashSet(); + DimReferencedata = new HashSet(); } public int Id { get; set; } @@ -85,14 +83,15 @@ public DimPublication() public virtual DimReferencedatum ParentPublicationTypeCodeNavigation { get; set; } public virtual DimReferencedatum PublicationTypeCode2Navigation { get; set; } public virtual DimReferencedatum TargetAudienceCodeNavigation { get; set; } - public virtual ICollection BrArtpublicationTypecategories { get; set; } - public virtual ICollection BrFieldOfArtDimPublications { get; set; } - public virtual ICollection BrFieldOfEducationDimPublications { get; set; } - public virtual ICollection BrFieldOfScienceDimPublications { get; set; } - public virtual ICollection BrKeywordDimPublications { get; set; } public virtual ICollection DimLocallyReportedPubInfos { get; set; } public virtual ICollection DimPids { get; set; } public virtual ICollection FactContributions { get; set; } + public virtual ICollection FactDimReferencedataFieldOfSciences { get; set; } public virtual ICollection FactFieldValues { get; set; } + + public virtual ICollection DimFieldOfArts { get; set; } + public virtual ICollection DimFieldOfEducations { get; set; } + public virtual ICollection DimKeywords { get; set; } + public virtual ICollection DimReferencedata { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimPublicationChannel.cs b/aspnetcore/src/api/Models/Ttv/DimPublicationChannel.cs index c8848f8f..58d1933f 100644 --- a/aspnetcore/src/api/Models/Ttv/DimPublicationChannel.cs +++ b/aspnetcore/src/api/Models/Ttv/DimPublicationChannel.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimPublicationChannel diff --git a/aspnetcore/src/api/Models/Ttv/DimPurpose.cs b/aspnetcore/src/api/Models/Ttv/DimPurpose.cs index 54e82be6..cd790980 100644 --- a/aspnetcore/src/api/Models/Ttv/DimPurpose.cs +++ b/aspnetcore/src/api/Models/Ttv/DimPurpose.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimPurpose diff --git a/aspnetcore/src/api/Models/Ttv/DimReferencedatum.cs b/aspnetcore/src/api/Models/Ttv/DimReferencedatum.cs index 2cc30f40..13c9db86 100644 --- a/aspnetcore/src/api/Models/Ttv/DimReferencedatum.cs +++ b/aspnetcore/src/api/Models/Ttv/DimReferencedatum.cs @@ -1,16 +1,12 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimReferencedatum { public DimReferencedatum() { - BrArtpublicationTypecategories = new HashSet(); - BrDimReferencedataDimCallProgrammes = new HashSet(); BrGrantedPermissions = new HashSet(); DimAffiliationAffiliationTypeNavigations = new HashSet(); DimAffiliationPositionCodeNavigations = new HashSet(); @@ -28,8 +24,13 @@ public DimReferencedatum() DimResearchDatasetDimReferencedataLicenseNavigations = new HashSet(); DimUserChoices = new HashSet(); FactContributions = new HashSet(); + FactDimReferencedataFieldOfSciences = new HashSet(); + FactFieldValues = new HashSet(); FactJufoClassCodesForPubChannels = new HashSet(); InverseDimReferencedata = new HashSet(); + DimCallProgrammes = new HashSet(); + DimPublications = new HashSet(); + DimResearchDatasets = new HashSet(); } public int Id { get; set; } @@ -46,8 +47,6 @@ public DimReferencedatum() public int DimReferencedataId { get; set; } public virtual DimReferencedatum DimReferencedata { get; set; } - public virtual ICollection BrArtpublicationTypecategories { get; set; } - public virtual ICollection BrDimReferencedataDimCallProgrammes { get; set; } public virtual ICollection BrGrantedPermissions { get; set; } public virtual ICollection DimAffiliationAffiliationTypeNavigations { get; set; } public virtual ICollection DimAffiliationPositionCodeNavigations { get; set; } @@ -65,7 +64,13 @@ public DimReferencedatum() public virtual ICollection DimResearchDatasetDimReferencedataLicenseNavigations { get; set; } public virtual ICollection DimUserChoices { get; set; } public virtual ICollection FactContributions { get; set; } + public virtual ICollection FactDimReferencedataFieldOfSciences { get; set; } + public virtual ICollection FactFieldValues { get; set; } public virtual ICollection FactJufoClassCodesForPubChannels { get; set; } public virtual ICollection InverseDimReferencedata { get; set; } + + public virtual ICollection DimCallProgrammes { get; set; } + public virtual ICollection DimPublications { get; set; } + public virtual ICollection DimResearchDatasets { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimRegisteredDataSource.cs b/aspnetcore/src/api/Models/Ttv/DimRegisteredDataSource.cs index 82af4162..db7027c2 100644 --- a/aspnetcore/src/api/Models/Ttv/DimRegisteredDataSource.cs +++ b/aspnetcore/src/api/Models/Ttv/DimRegisteredDataSource.cs @@ -1,15 +1,12 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimRegisteredDataSource { public DimRegisteredDataSource() { - BrFieldDisplaySettingsDimRegisteredDataSources = new HashSet(); DimAffiliations = new HashSet(); DimCallProgrammes = new HashSet(); DimCompetences = new HashSet(); @@ -18,6 +15,7 @@ public DimRegisteredDataSource() DimEvents = new HashSet(); DimFundingDecisions = new HashSet(); DimKeywords = new HashSet(); + DimKnownPeople = new HashSet(); DimNames = new HashSet(); DimOrcidPublications = new HashSet(); DimOrganizations = new HashSet(); @@ -28,7 +26,9 @@ public DimRegisteredDataSource() DimResearcherDescriptions = new HashSet(); DimResearcherToResearchCommunities = new HashSet(); DimTelephoneNumbers = new HashSet(); + DimWebLinks = new HashSet(); FactFieldValues = new HashSet(); + DimFieldDisplaySettings = new HashSet(); } public int Id { get; set; } @@ -40,7 +40,6 @@ public DimRegisteredDataSource() public DateTime? Created { get; set; } public virtual DimOrganization DimOrganization { get; set; } - public virtual ICollection BrFieldDisplaySettingsDimRegisteredDataSources { get; set; } public virtual ICollection DimAffiliations { get; set; } public virtual ICollection DimCallProgrammes { get; set; } public virtual ICollection DimCompetences { get; set; } @@ -49,6 +48,7 @@ public DimRegisteredDataSource() public virtual ICollection DimEvents { get; set; } public virtual ICollection DimFundingDecisions { get; set; } public virtual ICollection DimKeywords { get; set; } + public virtual ICollection DimKnownPeople { get; set; } public virtual ICollection DimNames { get; set; } public virtual ICollection DimOrcidPublications { get; set; } public virtual ICollection DimOrganizations { get; set; } @@ -59,6 +59,9 @@ public DimRegisteredDataSource() public virtual ICollection DimResearcherDescriptions { get; set; } public virtual ICollection DimResearcherToResearchCommunities { get; set; } public virtual ICollection DimTelephoneNumbers { get; set; } + public virtual ICollection DimWebLinks { get; set; } public virtual ICollection FactFieldValues { get; set; } + + public virtual ICollection DimFieldDisplaySettings { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimResearchActivity.cs b/aspnetcore/src/api/Models/Ttv/DimResearchActivity.cs index b588b0d8..2a776b07 100644 --- a/aspnetcore/src/api/Models/Ttv/DimResearchActivity.cs +++ b/aspnetcore/src/api/Models/Ttv/DimResearchActivity.cs @@ -1,15 +1,12 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimResearchActivity { public DimResearchActivity() { - DimFieldOfScienceDimResearchActivities = new HashSet(); DimPids = new HashSet(); DimResearchActivityDimKeywords = new HashSet(); DimWebLinks = new HashSet(); @@ -47,7 +44,6 @@ public DimResearchActivity() public virtual DimPublicationChannel DimPublicationChannel { get; set; } public virtual DimRegisteredDataSource DimRegisteredDataSource { get; set; } public virtual DimDate DimStartDateNavigation { get; set; } - public virtual ICollection DimFieldOfScienceDimResearchActivities { get; set; } public virtual ICollection DimPids { get; set; } public virtual ICollection DimResearchActivityDimKeywords { get; set; } public virtual ICollection DimWebLinks { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/DimResearchActivityDimKeyword.cs b/aspnetcore/src/api/Models/Ttv/DimResearchActivityDimKeyword.cs index 42a13097..2d8db150 100644 --- a/aspnetcore/src/api/Models/Ttv/DimResearchActivityDimKeyword.cs +++ b/aspnetcore/src/api/Models/Ttv/DimResearchActivityDimKeyword.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimResearchActivityDimKeyword diff --git a/aspnetcore/src/api/Models/Ttv/DimResearchCommunity.cs b/aspnetcore/src/api/Models/Ttv/DimResearchCommunity.cs index 71d4c4de..56421d3d 100644 --- a/aspnetcore/src/api/Models/Ttv/DimResearchCommunity.cs +++ b/aspnetcore/src/api/Models/Ttv/DimResearchCommunity.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimResearchCommunity diff --git a/aspnetcore/src/api/Models/Ttv/DimResearchDataCatalog.cs b/aspnetcore/src/api/Models/Ttv/DimResearchDataCatalog.cs index e695079e..b1fd5aec 100644 --- a/aspnetcore/src/api/Models/Ttv/DimResearchDataCatalog.cs +++ b/aspnetcore/src/api/Models/Ttv/DimResearchDataCatalog.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimResearchDataCatalog diff --git a/aspnetcore/src/api/Models/Ttv/DimResearchDataset.cs b/aspnetcore/src/api/Models/Ttv/DimResearchDataset.cs index 035c469a..ca96801a 100644 --- a/aspnetcore/src/api/Models/Ttv/DimResearchDataset.cs +++ b/aspnetcore/src/api/Models/Ttv/DimResearchDataset.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimResearchDataset @@ -11,9 +9,13 @@ public DimResearchDataset() { BrDatasetDatasetRelationshipDimResearchDatasetId2Navigations = new HashSet(); BrDatasetDatasetRelationshipDimResearchDatasets = new HashSet(); + DimPids = new HashSet(); DimWebLinks = new HashSet(); FactContributions = new HashSet(); + FactDimReferencedataFieldOfSciences = new HashSet(); FactFieldValues = new HashSet(); + DimKeywords = new HashSet(); + DimReferencedata = new HashSet(); } public int Id { get; set; } @@ -48,8 +50,13 @@ public DimResearchDataset() public virtual DimResearchDataCatalog DimResearchDataCatalog { get; set; } public virtual ICollection BrDatasetDatasetRelationshipDimResearchDatasetId2Navigations { get; set; } public virtual ICollection BrDatasetDatasetRelationshipDimResearchDatasets { get; set; } + public virtual ICollection DimPids { get; set; } public virtual ICollection DimWebLinks { get; set; } public virtual ICollection FactContributions { get; set; } + public virtual ICollection FactDimReferencedataFieldOfSciences { get; set; } public virtual ICollection FactFieldValues { get; set; } + + public virtual ICollection DimKeywords { get; set; } + public virtual ICollection DimReferencedata { get; set; } } } diff --git a/aspnetcore/src/api/Models/Ttv/DimResearcherDescription.cs b/aspnetcore/src/api/Models/Ttv/DimResearcherDescription.cs index eb2b2d79..58787f69 100644 --- a/aspnetcore/src/api/Models/Ttv/DimResearcherDescription.cs +++ b/aspnetcore/src/api/Models/Ttv/DimResearcherDescription.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimResearcherDescription diff --git a/aspnetcore/src/api/Models/Ttv/DimResearcherToResearchCommunity.cs b/aspnetcore/src/api/Models/Ttv/DimResearcherToResearchCommunity.cs index 4df28d9c..502fd8a5 100644 --- a/aspnetcore/src/api/Models/Ttv/DimResearcherToResearchCommunity.cs +++ b/aspnetcore/src/api/Models/Ttv/DimResearcherToResearchCommunity.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimResearcherToResearchCommunity diff --git a/aspnetcore/src/api/Models/Ttv/DimSector.cs b/aspnetcore/src/api/Models/Ttv/DimSector.cs index fb797a90..2bc6216e 100644 --- a/aspnetcore/src/api/Models/Ttv/DimSector.cs +++ b/aspnetcore/src/api/Models/Ttv/DimSector.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimSector diff --git a/aspnetcore/src/api/Models/Ttv/DimService.cs b/aspnetcore/src/api/Models/Ttv/DimService.cs index dd91f9cf..4107ebae 100644 --- a/aspnetcore/src/api/Models/Ttv/DimService.cs +++ b/aspnetcore/src/api/Models/Ttv/DimService.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimService diff --git a/aspnetcore/src/api/Models/Ttv/DimServicePoint.cs b/aspnetcore/src/api/Models/Ttv/DimServicePoint.cs index f7ec8811..de216712 100644 --- a/aspnetcore/src/api/Models/Ttv/DimServicePoint.cs +++ b/aspnetcore/src/api/Models/Ttv/DimServicePoint.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimServicePoint diff --git a/aspnetcore/src/api/Models/Ttv/DimTelephoneNumber.cs b/aspnetcore/src/api/Models/Ttv/DimTelephoneNumber.cs index 19b4e5b1..2179203d 100644 --- a/aspnetcore/src/api/Models/Ttv/DimTelephoneNumber.cs +++ b/aspnetcore/src/api/Models/Ttv/DimTelephoneNumber.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimTelephoneNumber diff --git a/aspnetcore/src/api/Models/Ttv/DimTypeOfFunding.cs b/aspnetcore/src/api/Models/Ttv/DimTypeOfFunding.cs index 97a03aa1..82b65423 100644 --- a/aspnetcore/src/api/Models/Ttv/DimTypeOfFunding.cs +++ b/aspnetcore/src/api/Models/Ttv/DimTypeOfFunding.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimTypeOfFunding diff --git a/aspnetcore/src/api/Models/Ttv/DimUserChoice.cs b/aspnetcore/src/api/Models/Ttv/DimUserChoice.cs index 0cbd97cd..736d9f0e 100644 --- a/aspnetcore/src/api/Models/Ttv/DimUserChoice.cs +++ b/aspnetcore/src/api/Models/Ttv/DimUserChoice.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimUserChoice diff --git a/aspnetcore/src/api/Models/Ttv/DimUserProfile.cs b/aspnetcore/src/api/Models/Ttv/DimUserProfile.cs index b1bd2dc0..47ce8e0e 100644 --- a/aspnetcore/src/api/Models/Ttv/DimUserProfile.cs +++ b/aspnetcore/src/api/Models/Ttv/DimUserProfile.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimUserProfile diff --git a/aspnetcore/src/api/Models/Ttv/DimWebLink.cs b/aspnetcore/src/api/Models/Ttv/DimWebLink.cs index a748d23a..489e5df4 100644 --- a/aspnetcore/src/api/Models/Ttv/DimWebLink.cs +++ b/aspnetcore/src/api/Models/Ttv/DimWebLink.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimWebLink @@ -29,11 +27,13 @@ public DimWebLink() public DateTime? Created { get; set; } public DateTime? Modified { get; set; } public int? DimResearchActivityId { get; set; } + public int? DimRegisteredDataSourceId { get; set; } public virtual DimCallProgramme DimCallProgramme { get; set; } public virtual DimFundingDecision DimFundingDecision { get; set; } public virtual DimKnownPerson DimKnownPerson { get; set; } public virtual DimOrganization DimOrganization { get; set; } + public virtual DimRegisteredDataSource DimRegisteredDataSource { get; set; } public virtual DimResearchActivity DimResearchActivity { get; set; } public virtual DimResearchCommunity DimResearchCommunity { get; set; } public virtual DimResearchDataCatalog DimResearchDataCatalog { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/DimWordCluster.cs b/aspnetcore/src/api/Models/Ttv/DimWordCluster.cs index 710f0c96..1d4d57f4 100644 --- a/aspnetcore/src/api/Models/Ttv/DimWordCluster.cs +++ b/aspnetcore/src/api/Models/Ttv/DimWordCluster.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class DimWordCluster diff --git a/aspnetcore/src/api/Models/Ttv/FactContribution.cs b/aspnetcore/src/api/Models/Ttv/FactContribution.cs index 0d17ed49..4418f8dd 100644 --- a/aspnetcore/src/api/Models/Ttv/FactContribution.cs +++ b/aspnetcore/src/api/Models/Ttv/FactContribution.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class FactContribution diff --git a/aspnetcore/src/api/Models/Ttv/FactDimReferencedataFieldOfScience.cs b/aspnetcore/src/api/Models/Ttv/FactDimReferencedataFieldOfScience.cs new file mode 100644 index 00000000..3c9d6219 --- /dev/null +++ b/aspnetcore/src/api/Models/Ttv/FactDimReferencedataFieldOfScience.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; + +namespace api.Models.Ttv +{ + public partial class FactDimReferencedataFieldOfScience + { + public int DimReferencedataId { get; set; } + public int DimResearchDatasetId { get; set; } + public int DimKnownPersonId { get; set; } + public int DimPublicationId { get; set; } + public int DimResearchActivityId { get; set; } + public int DimFundingDecisionId { get; set; } + public int DimInfrastructureId { get; set; } + + public virtual DimFundingDecision DimFundingDecision { get; set; } + public virtual DimInfrastructure DimInfrastructure { get; set; } + public virtual DimKnownPerson DimKnownPerson { get; set; } + public virtual DimPublication DimPublication { get; set; } + public virtual DimReferencedatum DimReferencedata { get; set; } + public virtual DimResearchDataset DimResearchDataset { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Ttv/FactFieldValue.cs b/aspnetcore/src/api/Models/Ttv/FactFieldValue.cs index 049d6903..e693de15 100644 --- a/aspnetcore/src/api/Models/Ttv/FactFieldValue.cs +++ b/aspnetcore/src/api/Models/Ttv/FactFieldValue.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class FactFieldValue @@ -34,9 +32,9 @@ public partial class FactFieldValue public string SourceDescription { get; set; } public DateTime? Created { get; set; } public DateTime? Modified { get; set; } - public int DimFieldOfScienceId { get; set; } public int DimResearchDatasetId { get; set; } public int DimRegisteredDataSourceId { get; set; } + public int DimReferencedataFieldOfScienceId { get; set; } public virtual DimAffiliation DimAffiliation { get; set; } public virtual DimCompetence DimCompetence { get; set; } @@ -44,7 +42,6 @@ public partial class FactFieldValue public virtual DimEmailAddrress DimEmailAddrress { get; set; } public virtual DimEvent DimEvent { get; set; } public virtual DimFieldDisplaySetting DimFieldDisplaySettings { get; set; } - public virtual DimFieldOfScience DimFieldOfScience { get; set; } public virtual DimFundingDecision DimFundingDecision { get; set; } public virtual DimIdentifierlessDatum DimIdentifierlessData { get; set; } public virtual DimKeyword DimKeyword { get; set; } @@ -53,6 +50,7 @@ public partial class FactFieldValue public virtual DimPid DimPid { get; set; } public virtual DimPid DimPidIdOrcidPutCodeNavigation { get; set; } public virtual DimPublication DimPublication { get; set; } + public virtual DimReferencedatum DimReferencedataFieldOfScience { get; set; } public virtual DimRegisteredDataSource DimRegisteredDataSource { get; set; } public virtual DimResearchActivity DimResearchActivity { get; set; } public virtual DimResearchCommunity DimResearchCommunity { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/FactInfraKeyword.cs b/aspnetcore/src/api/Models/Ttv/FactInfraKeyword.cs index b382efa2..b0b46cc9 100644 --- a/aspnetcore/src/api/Models/Ttv/FactInfraKeyword.cs +++ b/aspnetcore/src/api/Models/Ttv/FactInfraKeyword.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class FactInfraKeyword diff --git a/aspnetcore/src/api/Models/Ttv/FactJufoClassCodesForPubChannel.cs b/aspnetcore/src/api/Models/Ttv/FactJufoClassCodesForPubChannel.cs index 27617158..0c90654a 100644 --- a/aspnetcore/src/api/Models/Ttv/FactJufoClassCodesForPubChannel.cs +++ b/aspnetcore/src/api/Models/Ttv/FactJufoClassCodesForPubChannel.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class FactJufoClassCodesForPubChannel diff --git a/aspnetcore/src/api/Models/Ttv/FactUpkeep.cs b/aspnetcore/src/api/Models/Ttv/FactUpkeep.cs index 54b2fa9b..daf829b5 100644 --- a/aspnetcore/src/api/Models/Ttv/FactUpkeep.cs +++ b/aspnetcore/src/api/Models/Ttv/FactUpkeep.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { public partial class FactUpkeep diff --git a/aspnetcore/src/api/Models/Ttv/TempBrFieldOfScienceDimFundingDecision.cs b/aspnetcore/src/api/Models/Ttv/TempBrFieldOfScienceDimFundingDecision.cs new file mode 100644 index 00000000..f5923118 --- /dev/null +++ b/aspnetcore/src/api/Models/Ttv/TempBrFieldOfScienceDimFundingDecision.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; + +namespace api.Models.Ttv +{ + public partial class TempBrFieldOfScienceDimFundingDecision + { + public int DimFieldOfScienceId { get; set; } + public int DimFundingDecisionId { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Ttv/TempBrInfrastructureDimFieldOfScience.cs b/aspnetcore/src/api/Models/Ttv/TempBrInfrastructureDimFieldOfScience.cs new file mode 100644 index 00000000..3b5687e8 --- /dev/null +++ b/aspnetcore/src/api/Models/Ttv/TempBrInfrastructureDimFieldOfScience.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; + +namespace api.Models.Ttv +{ + public partial class TempBrInfrastructureDimFieldOfScience + { + public int DimInfrastructureId { get; set; } + public int DimFieldOfScienceId { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Ttv/TempBrParticipatesInFundingGroup.cs b/aspnetcore/src/api/Models/Ttv/TempBrParticipatesInFundingGroup.cs new file mode 100644 index 00000000..4abcb096 --- /dev/null +++ b/aspnetcore/src/api/Models/Ttv/TempBrParticipatesInFundingGroup.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +namespace api.Models.Ttv +{ + public partial class TempBrParticipatesInFundingGroup + { + public int DimFundingDecisionid { get; set; } + public int DimNameId { get; set; } + public int DimOrganizationId { get; set; } + public string RoleInFundingGroup { get; set; } + public decimal? ShareOfFundingInEur { get; set; } + public string SourceId { get; set; } + public bool? EndOfParticipation { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Ttv/BrResearchDatasetDimFieldOfScience.cs b/aspnetcore/src/api/Models/Ttv/TempBrResearchDatasetDimFieldOfScience.cs similarity index 71% rename from aspnetcore/src/api/Models/Ttv/BrResearchDatasetDimFieldOfScience.cs rename to aspnetcore/src/api/Models/Ttv/TempBrResearchDatasetDimFieldOfScience.cs index 70752591..8e025f3e 100644 --- a/aspnetcore/src/api/Models/Ttv/BrResearchDatasetDimFieldOfScience.cs +++ b/aspnetcore/src/api/Models/Ttv/TempBrResearchDatasetDimFieldOfScience.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; -#nullable disable - namespace api.Models.Ttv { - public partial class BrResearchDatasetDimFieldOfScience + public partial class TempBrResearchDatasetDimFieldOfScience { public int DimResearchDatasetid { get; set; } public int DimFieldOfScienceid { get; set; } diff --git a/aspnetcore/src/api/Models/Ttv/TempDimRegisteredDataSource.cs b/aspnetcore/src/api/Models/Ttv/TempDimRegisteredDataSource.cs new file mode 100644 index 00000000..702e2f21 --- /dev/null +++ b/aspnetcore/src/api/Models/Ttv/TempDimRegisteredDataSource.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +namespace api.Models.Ttv +{ + public partial class TempDimRegisteredDataSource + { + public int Id { get; set; } + public int DimOrganizationId { get; set; } + public string Name { get; set; } + public string SourceId { get; set; } + public string SourceDescription { get; set; } + public DateTime? Modified { get; set; } + public DateTime? Created { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Ttv/TempDimRegisteredDataSource20221121.cs b/aspnetcore/src/api/Models/Ttv/TempDimRegisteredDataSource20221121.cs new file mode 100644 index 00000000..4349023a --- /dev/null +++ b/aspnetcore/src/api/Models/Ttv/TempDimRegisteredDataSource20221121.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +namespace api.Models.Ttv +{ + public partial class TempDimRegisteredDataSource20221121 + { + public int Id { get; set; } + public int DimOrganizationId { get; set; } + public string Name { get; set; } + public string SourceId { get; set; } + public string SourceDescription { get; set; } + public DateTime? Modified { get; set; } + public DateTime? Created { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Ttv/TempFactDimReferencedataFieldOfScience.cs b/aspnetcore/src/api/Models/Ttv/TempFactDimReferencedataFieldOfScience.cs new file mode 100644 index 00000000..f1bd5733 --- /dev/null +++ b/aspnetcore/src/api/Models/Ttv/TempFactDimReferencedataFieldOfScience.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +namespace api.Models.Ttv +{ + public partial class TempFactDimReferencedataFieldOfScience + { + public int DimReferencedataId { get; set; } + public int DimResearchDatasetId { get; set; } + public int DimKnownPersonId { get; set; } + public int DimPublicationId { get; set; } + public int DimResearchActivityId { get; set; } + public int DimFundingDecisionId { get; set; } + public int DimInfrastructureId { get; set; } + } +} diff --git a/aspnetcore/src/api/Models/Ttv/TtvContext.cs b/aspnetcore/src/api/Models/Ttv/TtvContext.cs index f555c944..0a31a918 100644 --- a/aspnetcore/src/api/Models/Ttv/TtvContext.cs +++ b/aspnetcore/src/api/Models/Ttv/TtvContext.cs @@ -1,9 +1,8 @@ using System; +using System.Collections.Generic; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata; -#nullable disable - namespace api.Models.Ttv { public partial class TtvContext : DbContext @@ -17,33 +16,12 @@ public TtvContext(DbContextOptions options) { } - public virtual DbSet BrArtpublicationTypecategories { get; set; } - public virtual DbSet BrCallProgrammeDimCallProgrammes { get; set; } public virtual DbSet BrDatasetDatasetRelationships { get; set; } - public virtual DbSet BrDimReferencedataDimCallProgrammes { get; set; } - public virtual DbSet BrEsfriDimInfrastructures { get; set; } - public virtual DbSet BrFieldDisplaySettingsDimRegisteredDataSources { get; set; } - public virtual DbSet BrFieldOfArtDimPublications { get; set; } - public virtual DbSet BrFieldOfEducationDimPublications { get; set; } - public virtual DbSet BrFieldOfScienceDimFundingDecisions { get; set; } - public virtual DbSet BrFieldOfScienceDimPublications { get; set; } public virtual DbSet BrFundingConsortiumParticipations { get; set; } - public virtual DbSet BrFundingDecisionDimFieldOfArts { get; set; } public virtual DbSet BrGrantedPermissions { get; set; } - public virtual DbSet BrInfrastructureDimFieldOfSciences { get; set; } - public virtual DbSet BrKeywordDimFundingDecisions { get; set; } - public virtual DbSet BrKeywordDimPublications { get; set; } - public virtual DbSet BrLanguageCodesForDatasets { get; set; } - public virtual DbSet BrMerilDimInfrastructures { get; set; } - public virtual DbSet BrOrganizationsFundCallProgrammes { get; set; } public virtual DbSet BrParticipatesInFundingGroups { get; set; } - public virtual DbSet BrPredecessorOrganizations { get; set; } - public virtual DbSet BrPreviousFundingDecisions { get; set; } - public virtual DbSet BrRelatedFundingDecisions { get; set; } - public virtual DbSet BrResearchDatasetDimFieldOfSciences { get; set; } - public virtual DbSet BrResearchDatasetDimKeywords { get; set; } + public virtual DbSet BrParticipatesInFundingGroupBcPoistetuts { get; set; } public virtual DbSet BrServiceSubscriptions { get; set; } - public virtual DbSet BrSuccessorOrganizations { get; set; } public virtual DbSet BrWordClusterDimFundingDecisions { get; set; } public virtual DbSet BrWordsDefineAClusters { get; set; } public virtual DbSet DimAffiliations { get; set; } @@ -59,14 +37,12 @@ public TtvContext(DbContextOptions options) public virtual DbSet DimFieldOfArts { get; set; } public virtual DbSet DimFieldOfEducations { get; set; } public virtual DbSet DimFieldOfSciences { get; set; } - public virtual DbSet DimFieldOfScienceDimResearchActivities { get; set; } public virtual DbSet DimFundingDecisions { get; set; } public virtual DbSet DimGeos { get; set; } public virtual DbSet DimIdentifierlessData { get; set; } public virtual DbSet DimInfrastructures { get; set; } public virtual DbSet DimKeywords { get; set; } public virtual DbSet DimKnownPeople { get; set; } - public virtual DbSet DimKnownPersonDimFieldOfSciences { get; set; } public virtual DbSet DimLocallyReportedPubInfos { get; set; } public virtual DbSet DimMerils { get; set; } public virtual DbSet DimMinedWords { get; set; } @@ -99,10 +75,16 @@ public TtvContext(DbContextOptions options) public virtual DbSet DimWebLinks { get; set; } public virtual DbSet DimWordClusters { get; set; } public virtual DbSet FactContributions { get; set; } + public virtual DbSet FactDimReferencedataFieldOfSciences { get; set; } public virtual DbSet FactFieldValues { get; set; } public virtual DbSet FactInfraKeywords { get; set; } public virtual DbSet FactJufoClassCodesForPubChannels { get; set; } public virtual DbSet FactUpkeeps { get; set; } + public virtual DbSet TempBrInfrastructureDimFieldOfSciences { get; set; } + public virtual DbSet TempBrParticipatesInFundingGroups { get; set; } + public virtual DbSet TempDimRegisteredDataSources { get; set; } + public virtual DbSet TempDimRegisteredDataSource20221121s { get; set; } + public virtual DbSet TempFactDimReferencedataFieldOfSciences { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { @@ -114,60 +96,10 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.HasAnnotation("Relational:Collation", "SQL_Latin1_General_CP1_CI_AS"); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimPublicationId, e.DimReferencedataid }) - .HasName("PK__br_artpu__7AE5307A84CC324E"); - - entity.ToTable("br_artpublication_typecategory"); - - entity.Property(e => e.DimPublicationId).HasColumnName("dim_publication id"); - - entity.Property(e => e.DimReferencedataid).HasColumnName("dim_referencedataid"); - - entity.HasOne(d => d.DimPublication) - .WithMany(p => p.BrArtpublicationTypecategories) - .HasForeignKey(d => d.DimPublicationId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_artpubl464312"); - - entity.HasOne(d => d.DimReferencedata) - .WithMany(p => p.BrArtpublicationTypecategories) - .HasForeignKey(d => d.DimReferencedataid) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_artpubl101187"); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimCallProgrammeId, e.DimCallProgrammeId2 }) - .HasName("PK__br_call___6F0CEDFB46440361"); - - entity.ToTable("br_call_programme_dim_call_programme"); - - entity.Property(e => e.DimCallProgrammeId).HasColumnName("dim_call_programme_id"); - - entity.Property(e => e.DimCallProgrammeId2).HasColumnName("dim_call_programme_id2"); - - entity.HasOne(d => d.DimCallProgramme) - .WithMany(p => p.BrCallProgrammeDimCallProgrammeDimCallProgrammes) - .HasForeignKey(d => d.DimCallProgrammeId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("belongs to / a part of "); - - entity.HasOne(d => d.DimCallProgrammeId2Navigation) - .WithMany(p => p.BrCallProgrammeDimCallProgrammeDimCallProgrammeId2Navigations) - .HasForeignKey(d => d.DimCallProgrammeId2) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_call_pr785575"); - }); - modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimResearchDatasetId, e.DimResearchDatasetId2 }) - .HasName("PK__br_datas__9FEA685AE8535A03"); + .HasName("PK__br_datas__9FEA685A5FEE4AEF"); entity.ToTable("br_dataset_dataset_relationship"); @@ -193,178 +125,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasConstraintName("FKbr_dataset168991"); }); - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimCallProgrammeId, e.DimReferencedataId }) - .HasName("PK__br_dim_r__0A5B885D306ADB18"); - - entity.ToTable("br_dim_referencedata_dim_call_programme"); - - entity.Property(e => e.DimCallProgrammeId).HasColumnName("dim_call_programme_id"); - - entity.Property(e => e.DimReferencedataId).HasColumnName("dim_referencedata_id"); - - entity.HasOne(d => d.DimCallProgramme) - .WithMany(p => p.BrDimReferencedataDimCallProgrammes) - .HasForeignKey(d => d.DimCallProgrammeId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("has disciplines"); - - entity.HasOne(d => d.DimReferencedata) - .WithMany(p => p.BrDimReferencedataDimCallProgrammes) - .HasForeignKey(d => d.DimReferencedataId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_dim_ref172472"); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimEsfriId, e.DimInfrastructureId }) - .HasName("PK__br_esfri__A4A0FE10CAB3657A"); - - entity.ToTable("br_esfri_dim_infrastructure"); - - entity.Property(e => e.DimEsfriId).HasColumnName("dim_esfri_id"); - - entity.Property(e => e.DimInfrastructureId).HasColumnName("dim_infrastructure_id"); - - entity.HasOne(d => d.DimEsfri) - .WithMany(p => p.BrEsfriDimInfrastructures) - .HasForeignKey(d => d.DimEsfriId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_esfri_d559740"); - - entity.HasOne(d => d.DimInfrastructure) - .WithMany(p => p.BrEsfriDimInfrastructures) - .HasForeignKey(d => d.DimInfrastructureId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_esfri_d490989"); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimFieldDisplaySettingsId, e.DimRegisteredDataSourceId }) - .HasName("PK__br_field__6148A772BBC33689"); - - entity.ToTable("br_field_display_settings_dim_registered_data_source"); - - entity.Property(e => e.DimFieldDisplaySettingsId).HasColumnName("dim_field_display_settings_id"); - - entity.Property(e => e.DimRegisteredDataSourceId).HasColumnName("dim_registered_data_source_id"); - - entity.HasOne(d => d.DimFieldDisplaySettings) - .WithMany(p => p.BrFieldDisplaySettingsDimRegisteredDataSources) - .HasForeignKey(d => d.DimFieldDisplaySettingsId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_field_d783303"); - - entity.HasOne(d => d.DimRegisteredDataSource) - .WithMany(p => p.BrFieldDisplaySettingsDimRegisteredDataSources) - .HasForeignKey(d => d.DimRegisteredDataSourceId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_field_d115264"); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimFieldOfArtId, e.DimPublicationId }) - .HasName("PK__br_field__809A87CD97415E08"); - - entity.ToTable("br_field_of_art_dim_publication"); - - entity.Property(e => e.DimFieldOfArtId).HasColumnName("dim_field_of_art_id"); - - entity.Property(e => e.DimPublicationId).HasColumnName("dim_publication_id"); - - entity.HasOne(d => d.DimFieldOfArt) - .WithMany(p => p.BrFieldOfArtDimPublications) - .HasForeignKey(d => d.DimFieldOfArtId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_field_o978876"); - - entity.HasOne(d => d.DimPublication) - .WithMany(p => p.BrFieldOfArtDimPublications) - .HasForeignKey(d => d.DimPublicationId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_field_o505394"); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimFieldOfEducationId, e.DimPublicationId }) - .HasName("PK__br_field__6E377B2C0FF16E01"); - - entity.ToTable("br_field_of_education_dim_publication"); - - entity.Property(e => e.DimFieldOfEducationId).HasColumnName("dim_field_of_education_id"); - - entity.Property(e => e.DimPublicationId).HasColumnName("dim_publication_id"); - - entity.HasOne(d => d.DimFieldOfEducation) - .WithMany(p => p.BrFieldOfEducationDimPublications) - .HasForeignKey(d => d.DimFieldOfEducationId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_field_o983513"); - - entity.HasOne(d => d.DimPublication) - .WithMany(p => p.BrFieldOfEducationDimPublications) - .HasForeignKey(d => d.DimPublicationId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_field_o449658"); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimFieldOfScienceId, e.DimFundingDecisionId }) - .HasName("PK__br_field__1A103AF730AFF80F"); - - entity.ToTable("br_field_of_science_dim_funding_decision"); - - entity.Property(e => e.DimFieldOfScienceId).HasColumnName("dim_field_of_science_id"); - - entity.Property(e => e.DimFundingDecisionId).HasColumnName("dim_funding_decision_id"); - - entity.HasOne(d => d.DimFieldOfScience) - .WithMany(p => p.BrFieldOfScienceDimFundingDecisions) - .HasForeignKey(d => d.DimFieldOfScienceId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_field_o643706"); - - entity.HasOne(d => d.DimFundingDecision) - .WithMany(p => p.BrFieldOfScienceDimFundingDecisions) - .HasForeignKey(d => d.DimFundingDecisionId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_field_o993952"); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimFieldOfScienceId, e.DimPublicationId }) - .HasName("PK__br_field__5088B7766332C590"); - - entity.ToTable("br_field_of_science_dim_publication"); - - entity.Property(e => e.DimFieldOfScienceId).HasColumnName("dim_field_of_science_id"); - - entity.Property(e => e.DimPublicationId).HasColumnName("dim_publication_id"); - - entity.HasOne(d => d.DimFieldOfScience) - .WithMany(p => p.BrFieldOfScienceDimPublications) - .HasForeignKey(d => d.DimFieldOfScienceId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_field_o934749"); - - entity.HasOne(d => d.DimPublication) - .WithMany(p => p.BrFieldOfScienceDimPublications) - .HasForeignKey(d => d.DimPublicationId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_field_o201610"); - }); - modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimFundingDecisionId, e.DimOrganizationid }) - .HasName("PK__br_fundi__3DB567F87ABBFFA9"); + .HasName("PK__br_fundi__3DB567F8F345F076"); entity.ToTable("br_funding_consortium_participation"); @@ -395,34 +159,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasConstraintName("FKbr_funding503907"); }); - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimFundingDecisionId, e.DimFieldOfArtId }) - .HasName("PK__br_fundi__07CB586D20BFF86B"); - - entity.ToTable("br_funding_decision_dim_field_of_art"); - - entity.Property(e => e.DimFundingDecisionId).HasColumnName("dim_funding_decision_id"); - - entity.Property(e => e.DimFieldOfArtId).HasColumnName("dim_field_of_art_id"); - - entity.HasOne(d => d.DimFieldOfArt) - .WithMany(p => p.BrFundingDecisionDimFieldOfArts) - .HasForeignKey(d => d.DimFieldOfArtId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_funding154428"); - - entity.HasOne(d => d.DimFundingDecision) - .WithMany(p => p.BrFundingDecisionDimFieldOfArts) - .HasForeignKey(d => d.DimFundingDecisionId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_funding281737"); - }); - modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimUserProfileId, e.DimExternalServiceId, e.DimPermittedFieldGroup }) - .HasName("PK__br_grant__F51F7BCB9EEFE4B6"); + .HasName("PK__br_grant__F51F7BCB162F9D0B"); entity.ToTable("br_granted_permissions"); @@ -451,142 +191,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasConstraintName("permitted_services"); }); - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimInfrastructureId, e.DimFieldOfScienceId }) - .HasName("PK__br_infra__17B77C16825186C1"); - - entity.ToTable("br_infrastructure_dim_field_of_science"); - - entity.Property(e => e.DimInfrastructureId).HasColumnName("dim_infrastructure_id"); - - entity.Property(e => e.DimFieldOfScienceId).HasColumnName("dim_field_of_science_id"); - - entity.HasOne(d => d.DimFieldOfScience) - .WithMany(p => p.BrInfrastructureDimFieldOfSciences) - .HasForeignKey(d => d.DimFieldOfScienceId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_infrast565984"); - - entity.HasOne(d => d.DimInfrastructure) - .WithMany(p => p.BrInfrastructureDimFieldOfSciences) - .HasForeignKey(d => d.DimInfrastructureId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_infrast156732"); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimKeywordId, e.DimFundingDecisionId }) - .HasName("PK__br_keywo__8C7B929B21F5028A"); - - entity.ToTable("br_keyword_dim_funding_decision"); - - entity.Property(e => e.DimKeywordId).HasColumnName("dim_keyword_id"); - - entity.Property(e => e.DimFundingDecisionId).HasColumnName("dim_funding_decision_id"); - - entity.HasOne(d => d.DimFundingDecision) - .WithMany(p => p.BrKeywordDimFundingDecisions) - .HasForeignKey(d => d.DimFundingDecisionId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_keyword955130"); - - entity.HasOne(d => d.DimKeyword) - .WithMany(p => p.BrKeywordDimFundingDecisions) - .HasForeignKey(d => d.DimKeywordId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_keyword224605"); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimKeywordId, e.DimPublicationId }) - .HasName("PK__br_keywo__C6E31F1ABABE5C19"); - - entity.ToTable("br_keyword_dim_publication"); - - entity.Property(e => e.DimKeywordId).HasColumnName("dim_keyword_id"); - - entity.Property(e => e.DimPublicationId).HasColumnName("dim_publication_id"); - - entity.HasOne(d => d.DimKeyword) - .WithMany(p => p.BrKeywordDimPublications) - .HasForeignKey(d => d.DimKeywordId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_keyword944303"); - - entity.HasOne(d => d.DimPublication) - .WithMany(p => p.BrKeywordDimPublications) - .HasForeignKey(d => d.DimPublicationId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_keyword634640"); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimResearchDatasetId, e.DimReferencedataId }) - .HasName("PK__br_langu__576647BF54CFAC65"); - - entity.ToTable("br_language_codes_for_datasets"); - - entity.Property(e => e.DimResearchDatasetId).HasColumnName("dim_research_dataset_id"); - - entity.Property(e => e.DimReferencedataId).HasColumnName("dim_referencedata_id"); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimMerilId, e.DimInfrastructureId }) - .HasName("PK__br_meril__A30C54DA7582FE32"); - - entity.ToTable("br_meril_dim_infrastructure"); - - entity.Property(e => e.DimMerilId).HasColumnName("dim_meril_id"); - - entity.Property(e => e.DimInfrastructureId).HasColumnName("dim_infrastructure_id"); - - entity.HasOne(d => d.DimInfrastructure) - .WithMany(p => p.BrMerilDimInfrastructures) - .HasForeignKey(d => d.DimInfrastructureId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_meril_d901766"); - - entity.HasOne(d => d.DimMeril) - .WithMany(p => p.BrMerilDimInfrastructures) - .HasForeignKey(d => d.DimMerilId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_meril_d209645"); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimOrganizationid, e.DimCallProgrammeid }) - .HasName("PK__br_organ__10F219BC6E5C0983"); - - entity.ToTable("br_organizations_fund_call_programmes"); - - entity.Property(e => e.DimOrganizationid).HasColumnName("dim_organizationid"); - - entity.Property(e => e.DimCallProgrammeid).HasColumnName("dim_call_programmeid"); - - entity.HasOne(d => d.DimCallProgramme) - .WithMany(p => p.BrOrganizationsFundCallProgrammes) - .HasForeignKey(d => d.DimCallProgrammeid) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_organiz165034"); - - entity.HasOne(d => d.DimOrganization) - .WithMany(p => p.BrOrganizationsFundCallProgrammes) - .HasForeignKey(d => d.DimOrganizationid) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_organiz621686"); - }); - modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimFundingDecisionid, e.DimNameId }) - .HasName("PK__br_parti__5EC9BC64C369D361"); + .HasName("PK__br_parti__5EC9BC64C923D602"); entity.ToTable("br_participates_in_funding_group"); @@ -629,147 +237,136 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasConstraintName("get_funding_in_name_of_org"); }); - modelBuilder.Entity(entity => + modelBuilder.Entity(entity => { - entity.HasKey(e => new { e.DimOrganizationid, e.DimOrganizationid2 }) - .HasName("PK__br_prede__A7CAD2F4CB0E1A8E"); - - entity.ToTable("br_predecessor_organization"); - - entity.Property(e => e.DimOrganizationid).HasColumnName("dim_organizationid"); + entity.HasNoKey(); - entity.Property(e => e.DimOrganizationid2).HasColumnName("dim_organizationid2"); + entity.ToTable("br_participates_in_funding_group_BC_poistetut"); - entity.HasOne(d => d.DimOrganization) - .WithMany(p => p.BrPredecessorOrganizationDimOrganizations) - .HasForeignKey(d => d.DimOrganizationid) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_predece849307"); - - entity.HasOne(d => d.DimOrganizationid2Navigation) - .WithMany(p => p.BrPredecessorOrganizationDimOrganizationid2Navigations) - .HasForeignKey(d => d.DimOrganizationid2) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_predece505451"); - }); + entity.Property(e => e.DimFundingDecisionid).HasColumnName("dim_funding_decisionid"); - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimFundingDecisionFromId, e.DimFundingDecisionToId }) - .HasName("PK__br_previ__90966491D4F0C457"); + entity.Property(e => e.DimNameId).HasColumnName("dim_name_id"); - entity.ToTable("br_previous_funding_decision"); + entity.Property(e => e.DimOrganizationId).HasColumnName("dim_organization_id"); - entity.Property(e => e.DimFundingDecisionFromId).HasColumnName("dim_funding_decision_from_id"); + entity.Property(e => e.EndOfParticipation).HasColumnName("end_of_participation"); - entity.Property(e => e.DimFundingDecisionToId).HasColumnName("dim_funding_decision_to_id"); + entity.Property(e => e.RoleInFundingGroup) + .HasMaxLength(255) + .HasColumnName("role_in_funding_group"); - entity.HasOne(d => d.DimFundingDecisionFrom) - .WithMany(p => p.BrPreviousFundingDecisionDimFundingDecisionFroms) - .HasForeignKey(d => d.DimFundingDecisionFromId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_previou481541"); + entity.Property(e => e.ShareOfFundingInEur) + .HasColumnType("decimal(18, 2)") + .HasColumnName("share_of_funding_in_EUR"); - entity.HasOne(d => d.DimFundingDecisionTo) - .WithMany(p => p.BrPreviousFundingDecisionDimFundingDecisionTos) - .HasForeignKey(d => d.DimFundingDecisionToId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_previou440746"); + entity.Property(e => e.SourceId) + .HasMaxLength(30) + .HasColumnName("source_id"); }); - modelBuilder.Entity(entity => + modelBuilder.Entity(entity => { - entity.HasKey(e => new { e.DimFundingDecisionFromId, e.DimFundingDecisionToId }) - .HasName("PK__br_relat__909664919623E695"); + entity.HasNoKey(); - entity.ToTable("br_related_funding_decision"); + entity.ToTable("br_service_subscription"); - entity.Property(e => e.DimFundingDecisionFromId).HasColumnName("dim_funding_decision_from_id"); + entity.Property(e => e.DimExternalServiceId).HasColumnName("dim_external_service_id"); - entity.Property(e => e.DimFundingDecisionToId).HasColumnName("dim_funding_decision_to_id"); + entity.Property(e => e.DimUserProfileId).HasColumnName("dim_user_profile_id"); - entity.HasOne(d => d.DimFundingDecisionFrom) - .WithMany(p => p.BrRelatedFundingDecisionDimFundingDecisionFroms) - .HasForeignKey(d => d.DimFundingDecisionFromId) + entity.HasOne(d => d.DimExternalService) + .WithMany() + .HasForeignKey(d => d.DimExternalServiceId) .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_related232364"); + .HasConstraintName("FKbr_service763943"); - entity.HasOne(d => d.DimFundingDecisionTo) - .WithMany(p => p.BrRelatedFundingDecisionDimFundingDecisionTos) - .HasForeignKey(d => d.DimFundingDecisionToId) + entity.HasOne(d => d.DimUserProfile) + .WithMany() + .HasForeignKey(d => d.DimUserProfileId) .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_related689923"); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimResearchDatasetid, e.DimFieldOfScienceid }) - .HasName("PK__br_resea__ADD3384BB46E4B15"); - - entity.ToTable("br_research_dataset_dim_field_of_science"); - - entity.Property(e => e.DimResearchDatasetid).HasColumnName("dim_research_datasetid"); - - entity.Property(e => e.DimFieldOfScienceid).HasColumnName("dim_field_of_scienceid"); + .HasConstraintName("permitted services"); }); - modelBuilder.Entity(entity => + modelBuilder.Entity(entity => { - entity.HasKey(e => new { e.DimResearchDatasetId, e.DimKeywordId }) - .HasName("PK__br_resea__4D226DF2A00AACE3"); + entity.HasKey(e => new { e.DimWordClusterId, e.DimFundingDecisionId }) + .HasName("PK__br_word___7D640B5A0254B61D"); - entity.ToTable("br_research_dataset_dim_keyword"); + entity.ToTable("br_word_cluster_dim_funding_decision"); - entity.Property(e => e.DimResearchDatasetId).HasColumnName("dim_research_dataset_id"); + entity.Property(e => e.DimWordClusterId).HasColumnName("dim_word_cluster_id"); - entity.Property(e => e.DimKeywordId).HasColumnName("dim_keyword_id"); - }); + entity.Property(e => e.DimFundingDecisionId).HasColumnName("dim_funding_decision_id"); - modelBuilder.Entity(entity => - { - entity.HasNoKey(); + entity.Property(e => e.Created) + .HasColumnType("datetime") + .HasColumnName("created"); - entity.ToTable("br_service_subscription"); + entity.Property(e => e.Modified) + .HasColumnType("datetime") + .HasColumnName("modified"); - entity.Property(e => e.DimExternalServiceId).HasColumnName("dim_external_service_id"); + entity.Property(e => e.SourceDescription) + .HasMaxLength(255) + .HasColumnName("source_description"); - entity.Property(e => e.DimUserProfileId).HasColumnName("dim_user_profile_id"); + entity.Property(e => e.SourceId) + .IsRequired() + .HasMaxLength(255) + .HasColumnName("source_id"); - entity.HasOne(d => d.DimExternalService) - .WithMany() - .HasForeignKey(d => d.DimExternalServiceId) + entity.HasOne(d => d.DimFundingDecision) + .WithMany(p => p.BrWordClusterDimFundingDecisions) + .HasForeignKey(d => d.DimFundingDecisionId) .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_service763943"); + .HasConstraintName("FKbr_word_cl350721"); - entity.HasOne(d => d.DimUserProfile) - .WithMany() - .HasForeignKey(d => d.DimUserProfileId) + entity.HasOne(d => d.DimWordCluster) + .WithMany(p => p.BrWordClusterDimFundingDecisions) + .HasForeignKey(d => d.DimWordClusterId) .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("permitted services"); + .HasConstraintName("FKbr_word_cl424955"); }); - modelBuilder.Entity(entity => + modelBuilder.Entity(entity => { - entity.HasKey(e => new { e.DimOrganizationid, e.DimOrganizationid2 }) - .HasName("PK__br_succe__A7CAD2F436CC1A1E"); + entity.HasKey(e => new { e.DimMinedWordsId, e.DimWordClusterId }) + .HasName("PK__br_words__0602FA372F9B857F"); - entity.ToTable("br_successor organization"); + entity.ToTable("br_words_define_a_cluster"); - entity.Property(e => e.DimOrganizationid).HasColumnName("dim_organizationid"); + entity.Property(e => e.DimMinedWordsId).HasColumnName("dim_mined_words_id"); - entity.Property(e => e.DimOrganizationid2).HasColumnName("dim_organizationid2"); + entity.Property(e => e.DimWordClusterId).HasColumnName("dim_word_cluster_id"); - entity.HasOne(d => d.DimOrganization) - .WithMany(p => p.BrSuccessorOrganizationDimOrganizations) - .HasForeignKey(d => d.DimOrganizationid) + entity.Property(e => e.Created) + .HasColumnType("datetime") + .HasColumnName("created"); + + entity.Property(e => e.Modified) + .HasColumnType("datetime") + .HasColumnName("modified"); + + entity.Property(e => e.SourceDescription) + .HasMaxLength(255) + .HasColumnName("source_description"); + + entity.Property(e => e.SourceId) + .IsRequired() + .HasMaxLength(255) + .HasColumnName("source_id"); + + entity.HasOne(d => d.DimMinedWords) + .WithMany(p => p.BrWordsDefineAClusters) + .HasForeignKey(d => d.DimMinedWordsId) .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_success452227"); + .HasConstraintName("FKbr_words_d537149"); - entity.HasOne(d => d.DimOrganizationid2Navigation) - .WithMany(p => p.BrSuccessorOrganizationDimOrganizationid2Navigations) - .HasForeignKey(d => d.DimOrganizationid2) + entity.HasOne(d => d.DimWordCluster) + .WithMany(p => p.BrWordsDefineAClusters) + .HasForeignKey(d => d.DimWordClusterId) .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKbr_success902531"); + .HasConstraintName("FKbr_words_d714819"); }); modelBuilder.Entity(entity => @@ -1029,19 +626,70 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .WithMany(p => p.DimCallProgrammeDimDateIdDueNavigations) .HasForeignKey(d => d.DimDateIdDue) .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("start"); + .HasConstraintName("open "); entity.HasOne(d => d.DimDateIdOpenNavigation) .WithMany(p => p.DimCallProgrammeDimDateIdOpenNavigations) .HasForeignKey(d => d.DimDateIdOpen) .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("end"); + .HasConstraintName("close"); entity.HasOne(d => d.DimRegisteredDataSource) .WithMany(p => p.DimCallProgrammes) .HasForeignKey(d => d.DimRegisteredDataSourceId) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("FKdim_call_p102028"); + + entity.HasMany(d => d.DimCallProgrammeId2s) + .WithMany(p => p.DimCallProgrammes) + .UsingEntity>( + "BrCallProgrammeDimCallProgramme", + l => l.HasOne().WithMany().HasForeignKey("DimCallProgrammeId2").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_call_pr785575"), + r => r.HasOne().WithMany().HasForeignKey("DimCallProgrammeId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("belongs to / a part of "), + j => + { + j.HasKey("DimCallProgrammeId", "DimCallProgrammeId2").HasName("PK__br_call___6F0CEDFB5685F50B"); + + j.ToTable("br_call_programme_dim_call_programme"); + + j.IndexerProperty("DimCallProgrammeId").HasColumnName("dim_call_programme_id"); + + j.IndexerProperty("DimCallProgrammeId2").HasColumnName("dim_call_programme_id2"); + }); + + entity.HasMany(d => d.DimCallProgrammes) + .WithMany(p => p.DimCallProgrammeId2s) + .UsingEntity>( + "BrCallProgrammeDimCallProgramme", + l => l.HasOne().WithMany().HasForeignKey("DimCallProgrammeId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("belongs to / a part of "), + r => r.HasOne().WithMany().HasForeignKey("DimCallProgrammeId2").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_call_pr785575"), + j => + { + j.HasKey("DimCallProgrammeId", "DimCallProgrammeId2").HasName("PK__br_call___6F0CEDFB5685F50B"); + + j.ToTable("br_call_programme_dim_call_programme"); + + j.IndexerProperty("DimCallProgrammeId").HasColumnName("dim_call_programme_id"); + + j.IndexerProperty("DimCallProgrammeId2").HasColumnName("dim_call_programme_id2"); + }); + + entity.HasMany(d => d.DimReferencedata) + .WithMany(p => p.DimCallProgrammes) + .UsingEntity>( + "BrDimReferencedataDimCallProgramme", + l => l.HasOne().WithMany().HasForeignKey("DimReferencedataId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_dim_ref172472"), + r => r.HasOne().WithMany().HasForeignKey("DimCallProgrammeId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("has disciplines"), + j => + { + j.HasKey("DimCallProgrammeId", "DimReferencedataId").HasName("PK__br_dim_r__0A5B885D0996EA60"); + + j.ToTable("br_dim_referencedata_dim_call_programme"); + + j.IndexerProperty("DimCallProgrammeId").HasColumnName("dim_call_programme_id"); + + j.IndexerProperty("DimReferencedataId").HasColumnName("dim_referencedata_id"); + }); }); modelBuilder.Entity(entity => @@ -1192,7 +840,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.DimStartDate).HasColumnName("dim_start_date"); - entity.Property(e => e.LocalIdentifier).HasColumnName("local_identifier"); + entity.Property(e => e.LocalIdentifier) + .HasMaxLength(255) + .HasColumnName("local_identifier"); entity.Property(e => e.Modified) .HasColumnType("datetime") @@ -1328,6 +978,23 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .IsRequired() .HasMaxLength(255) .HasColumnName("source_id"); + + entity.HasMany(d => d.DimInfrastructures) + .WithMany(p => p.DimEsfris) + .UsingEntity>( + "BrEsfriDimInfrastructure", + l => l.HasOne().WithMany().HasForeignKey("DimInfrastructureId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_esfri_d490989"), + r => r.HasOne().WithMany().HasForeignKey("DimEsfriId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_esfri_d559740"), + j => + { + j.HasKey("DimEsfriId", "DimInfrastructureId").HasName("PK__br_esfri__A4A0FE10B16F3C75"); + + j.ToTable("br_esfri_dim_infrastructure"); + + j.IndexerProperty("DimEsfriId").HasColumnName("dim_esfri_id"); + + j.IndexerProperty("DimInfrastructureId").HasColumnName("dim_infrastructure_id"); + }); }); modelBuilder.Entity(entity => @@ -1348,22 +1015,24 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.DimRegisteredDataSourceId).HasColumnName("dim_registered_data_source_id"); - entity.Property(e => e.EventLocationText).HasColumnName("event_location_text"); + entity.Property(e => e.EventLocationText) + .HasMaxLength(255) + .HasColumnName("event_location_text"); entity.Property(e => e.Modified) .HasColumnType("datetime") .HasColumnName("modified"); entity.Property(e => e.NameEn) - .HasMaxLength(255) + .HasMaxLength(400) .HasColumnName("name_en"); entity.Property(e => e.NameFi) - .HasMaxLength(255) + .HasMaxLength(400) .HasColumnName("name_fi"); entity.Property(e => e.NameSv) - .HasMaxLength(255) + .HasMaxLength(400) .HasColumnName("name_sv"); entity.Property(e => e.NameUnd) @@ -1387,7 +1056,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.HasOne(d => d.DimDateIdStartDateNavigation) .WithMany(p => p.DimEventDimDateIdStartDateNavigations) .HasForeignKey(d => d.DimDateIdStartDate) - .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("start_date"); entity.HasOne(d => d.DimGeoIdEventCountryNavigation) @@ -1477,6 +1145,23 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey(d => d.DimUserProfileId) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("FKdim_field_653425"); + + entity.HasMany(d => d.DimRegisteredDataSources) + .WithMany(p => p.DimFieldDisplaySettings) + .UsingEntity>( + "BrFieldDisplaySettingsDimRegisteredDataSource", + l => l.HasOne().WithMany().HasForeignKey("DimRegisteredDataSourceId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_field_d115264"), + r => r.HasOne().WithMany().HasForeignKey("DimFieldDisplaySettingsId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_field_d783303"), + j => + { + j.HasKey("DimFieldDisplaySettingsId", "DimRegisteredDataSourceId").HasName("PK__br_field__6148A77275EFD9AC"); + + j.ToTable("br_field_display_settings_dim_registered_data_source"); + + j.IndexerProperty("DimFieldDisplaySettingsId").HasColumnName("dim_field_display_settings_id"); + + j.IndexerProperty("DimRegisteredDataSourceId").HasColumnName("dim_registered_data_source_id"); + }); }); modelBuilder.Entity(entity => @@ -1519,6 +1204,23 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .IsRequired() .HasMaxLength(255) .HasColumnName("source_id"); + + entity.HasMany(d => d.DimPublications) + .WithMany(p => p.DimFieldOfArts) + .UsingEntity>( + "BrFieldOfArtDimPublication", + l => l.HasOne().WithMany().HasForeignKey("DimPublicationId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_field_o505394"), + r => r.HasOne().WithMany().HasForeignKey("DimFieldOfArtId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_field_o978876"), + j => + { + j.HasKey("DimFieldOfArtId", "DimPublicationId").HasName("PK__br_field__809A87CDCEDC37CF"); + + j.ToTable("br_field_of_art_dim_publication"); + + j.IndexerProperty("DimFieldOfArtId").HasColumnName("dim_field_of_art_id"); + + j.IndexerProperty("DimPublicationId").HasColumnName("dim_publication_id"); + }); }); modelBuilder.Entity(entity => @@ -1561,6 +1263,23 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .IsRequired() .HasMaxLength(255) .HasColumnName("source_id"); + + entity.HasMany(d => d.DimPublications) + .WithMany(p => p.DimFieldOfEducations) + .UsingEntity>( + "BrFieldOfEducationDimPublication", + l => l.HasOne().WithMany().HasForeignKey("DimPublicationId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_field_o449658"), + r => r.HasOne().WithMany().HasForeignKey("DimFieldOfEducationId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_field_o983513"), + j => + { + j.HasKey("DimFieldOfEducationId", "DimPublicationId").HasName("PK__br_field__6E377B2C72EE1ED6"); + + j.ToTable("br_field_of_education_dim_publication"); + + j.IndexerProperty("DimFieldOfEducationId").HasColumnName("dim_field_of_education_id"); + + j.IndexerProperty("DimPublicationId").HasColumnName("dim_publication_id"); + }); }); modelBuilder.Entity(entity => @@ -1605,30 +1324,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasColumnName("source_id"); }); - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimFieldOfScienceId, e.DimResearchActivityId }) - .HasName("PK__dim_fiel__D251A5825374B163"); - - entity.ToTable("dim_field_of_science_dim_research_activity"); - - entity.Property(e => e.DimFieldOfScienceId).HasColumnName("dim_field_of_science_id"); - - entity.Property(e => e.DimResearchActivityId).HasColumnName("dim_research_activity_id"); - - entity.HasOne(d => d.DimFieldOfScience) - .WithMany(p => p.DimFieldOfScienceDimResearchActivities) - .HasForeignKey(d => d.DimFieldOfScienceId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKdim_field_235508"); - - entity.HasOne(d => d.DimResearchActivity) - .WithMany(p => p.DimFieldOfScienceDimResearchActivities) - .HasForeignKey(d => d.DimResearchActivityId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKdim_field_982235"); - }); - modelBuilder.Entity(entity => { entity.ToTable("dim_funding_decision"); @@ -1781,6 +1476,91 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey(d => d.DimTypeOfFundingId) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("FKdim_fundin974924"); + + entity.HasMany(d => d.DimFieldOfArts) + .WithMany(p => p.DimFundingDecisions) + .UsingEntity>( + "BrFundingDecisionDimFieldOfArt", + l => l.HasOne().WithMany().HasForeignKey("DimFieldOfArtId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_funding154428"), + r => r.HasOne().WithMany().HasForeignKey("DimFundingDecisionId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_funding281737"), + j => + { + j.HasKey("DimFundingDecisionId", "DimFieldOfArtId").HasName("PK__br_fundi__07CB586D4C093A8E"); + + j.ToTable("br_funding_decision_dim_field_of_art"); + + j.IndexerProperty("DimFundingDecisionId").HasColumnName("dim_funding_decision_id"); + + j.IndexerProperty("DimFieldOfArtId").HasColumnName("dim_field_of_art_id"); + }); + + entity.HasMany(d => d.DimFundingDecisionFroms) + .WithMany(p => p.DimFundingDecisionTos) + .UsingEntity>( + "BrPreviousFundingDecision", + l => l.HasOne().WithMany().HasForeignKey("DimFundingDecisionFromId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_previou481541"), + r => r.HasOne().WithMany().HasForeignKey("DimFundingDecisionToId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_previou440746"), + j => + { + j.HasKey("DimFundingDecisionFromId", "DimFundingDecisionToId").HasName("PK__br_previ__90966491010226C7"); + + j.ToTable("br_previous_funding_decision"); + + j.IndexerProperty("DimFundingDecisionFromId").HasColumnName("dim_funding_decision_from_id"); + + j.IndexerProperty("DimFundingDecisionToId").HasColumnName("dim_funding_decision_to_id"); + }); + + entity.HasMany(d => d.DimFundingDecisionFromsNavigation) + .WithMany(p => p.DimFundingDecisionTosNavigation) + .UsingEntity>( + "BrRelatedFundingDecision", + l => l.HasOne().WithMany().HasForeignKey("DimFundingDecisionFromId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_related232364"), + r => r.HasOne().WithMany().HasForeignKey("DimFundingDecisionToId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_related689923"), + j => + { + j.HasKey("DimFundingDecisionFromId", "DimFundingDecisionToId").HasName("PK__br_relat__909664918926D3B7"); + + j.ToTable("br_related_funding_decision"); + + j.IndexerProperty("DimFundingDecisionFromId").HasColumnName("dim_funding_decision_from_id"); + + j.IndexerProperty("DimFundingDecisionToId").HasColumnName("dim_funding_decision_to_id"); + }); + + entity.HasMany(d => d.DimFundingDecisionTos) + .WithMany(p => p.DimFundingDecisionFroms) + .UsingEntity>( + "BrPreviousFundingDecision", + l => l.HasOne().WithMany().HasForeignKey("DimFundingDecisionToId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_previou440746"), + r => r.HasOne().WithMany().HasForeignKey("DimFundingDecisionFromId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_previou481541"), + j => + { + j.HasKey("DimFundingDecisionFromId", "DimFundingDecisionToId").HasName("PK__br_previ__90966491010226C7"); + + j.ToTable("br_previous_funding_decision"); + + j.IndexerProperty("DimFundingDecisionFromId").HasColumnName("dim_funding_decision_from_id"); + + j.IndexerProperty("DimFundingDecisionToId").HasColumnName("dim_funding_decision_to_id"); + }); + + entity.HasMany(d => d.DimFundingDecisionTosNavigation) + .WithMany(p => p.DimFundingDecisionFromsNavigation) + .UsingEntity>( + "BrRelatedFundingDecision", + l => l.HasOne().WithMany().HasForeignKey("DimFundingDecisionToId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_related689923"), + r => r.HasOne().WithMany().HasForeignKey("DimFundingDecisionFromId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_related232364"), + j => + { + j.HasKey("DimFundingDecisionFromId", "DimFundingDecisionToId").HasName("PK__br_relat__909664918926D3B7"); + + j.ToTable("br_related_funding_decision"); + + j.IndexerProperty("DimFundingDecisionFromId").HasColumnName("dim_funding_decision_from_id"); + + j.IndexerProperty("DimFundingDecisionToId").HasColumnName("dim_funding_decision_to_id"); + }); }); modelBuilder.Entity(entity => @@ -1875,7 +1655,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.SourceId) .IsRequired() - .HasMaxLength(255) + .HasMaxLength(4000) .HasColumnName("source_id"); entity.Property(e => e.Type) @@ -2070,6 +1850,40 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey(d => d.DimRegisteredDataSourceId) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("FKdim_keywor723976"); + + entity.HasMany(d => d.DimFundingDecisions) + .WithMany(p => p.DimKeywords) + .UsingEntity>( + "BrKeywordDimFundingDecision", + l => l.HasOne().WithMany().HasForeignKey("DimFundingDecisionId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_keyword955130"), + r => r.HasOne().WithMany().HasForeignKey("DimKeywordId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_keyword224605"), + j => + { + j.HasKey("DimKeywordId", "DimFundingDecisionId").HasName("PK__br_keywo__8C7B929B63C03C1E"); + + j.ToTable("br_keyword_dim_funding_decision"); + + j.IndexerProperty("DimKeywordId").HasColumnName("dim_keyword_id"); + + j.IndexerProperty("DimFundingDecisionId").HasColumnName("dim_funding_decision_id"); + }); + + entity.HasMany(d => d.DimPublications) + .WithMany(p => p.DimKeywords) + .UsingEntity>( + "BrKeywordDimPublication", + l => l.HasOne().WithMany().HasForeignKey("DimPublicationId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_keyword634640"), + r => r.HasOne().WithMany().HasForeignKey("DimKeywordId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_keyword944303"), + j => + { + j.HasKey("DimKeywordId", "DimPublicationId").HasName("PK__br_keywo__C6E31F1A50941DF8"); + + j.ToTable("br_keyword_dim_publication"); + + j.IndexerProperty("DimKeywordId").HasColumnName("dim_keyword_id"); + + j.IndexerProperty("DimPublicationId").HasColumnName("dim_publication_id"); + }); }); modelBuilder.Entity(entity => @@ -2092,38 +1906,19 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasMaxLength(255) .HasColumnName("source_description"); - entity.Property(e => e.SourceId) - .IsRequired() - .HasMaxLength(255) - .HasColumnName("source_id"); - - entity.Property(e => e.SourceProjectId) - .HasMaxLength(100) - .HasColumnName("source_project_id"); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.DimFieldOfScienceId, e.DimKnownPersonId }) - .HasName("PK__dim_know__493EE0763377A11B"); - - entity.ToTable("dim_known_person_dim_field_of_science"); - - entity.Property(e => e.DimFieldOfScienceId).HasColumnName("dim_field_of_science_id"); - - entity.Property(e => e.DimKnownPersonId).HasColumnName("dim_known_person_id"); + entity.Property(e => e.SourceId) + .IsRequired() + .HasMaxLength(255) + .HasColumnName("source_id"); - entity.HasOne(d => d.DimFieldOfScience) - .WithMany(p => p.DimKnownPersonDimFieldOfSciences) - .HasForeignKey(d => d.DimFieldOfScienceId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKdim_known_609663"); + entity.Property(e => e.SourceProjectId) + .HasMaxLength(100) + .HasColumnName("source_project_id"); - entity.HasOne(d => d.DimKnownPerson) - .WithMany(p => p.DimKnownPersonDimFieldOfSciences) - .HasForeignKey(d => d.DimKnownPersonId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKdim_known_232019"); + entity.HasOne(d => d.DimRegisteredDataSource) + .WithMany(p => p.DimKnownPeople) + .HasForeignKey(d => d.DimRegisteredDataSourceId) + .HasConstraintName("FKdim_knownperson_regdatasource"); }); modelBuilder.Entity(entity => @@ -2214,6 +2009,52 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .IsRequired() .HasMaxLength(255) .HasColumnName("source_id"); + + entity.HasMany(d => d.DimInfrastructures) + .WithMany(p => p.DimMerils) + .UsingEntity>( + "BrMerilDimInfrastructure", + l => l.HasOne().WithMany().HasForeignKey("DimInfrastructureId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_meril_d901766"), + r => r.HasOne().WithMany().HasForeignKey("DimMerilId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_meril_d209645"), + j => + { + j.HasKey("DimMerilId", "DimInfrastructureId").HasName("PK__br_meril__A30C54DAB06A58AC"); + + j.ToTable("br_meril_dim_infrastructure"); + + j.IndexerProperty("DimMerilId").HasColumnName("dim_meril_id"); + + j.IndexerProperty("DimInfrastructureId").HasColumnName("dim_infrastructure_id"); + }); + }); + + modelBuilder.Entity(entity => + { + entity.ToTable("dim_mined_words"); + + entity.Property(e => e.Id).HasColumnName("id"); + + entity.Property(e => e.Created) + .HasColumnType("datetime") + .HasColumnName("created"); + + entity.Property(e => e.Modified) + .HasColumnType("datetime") + .HasColumnName("modified"); + + entity.Property(e => e.SourceDescription) + .HasMaxLength(255) + .HasColumnName("source_description"); + + entity.Property(e => e.SourceId) + .IsRequired() + .HasMaxLength(255) + .HasColumnName("source_id"); + + entity.Property(e => e.Word) + .IsRequired() + .HasMaxLength(255) + .HasColumnName("word"); }); modelBuilder.Entity(entity => @@ -2336,7 +2177,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.Id, e.DimNewsFeedid }) - .HasName("PK__dim_news__B87E6703D18BE696"); + .HasName("PK__dim_news__B87E6703459D631B"); entity.ToTable("dim_news_item"); @@ -2710,6 +2551,91 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey(d => d.DimSectorid) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("FKdim_organi330513"); + + entity.HasMany(d => d.DimCallProgrammes) + .WithMany(p => p.DimOrganizations) + .UsingEntity>( + "BrOrganizationsFundCallProgramme", + l => l.HasOne().WithMany().HasForeignKey("DimCallProgrammeid").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_organiz165034"), + r => r.HasOne().WithMany().HasForeignKey("DimOrganizationid").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_organiz621686"), + j => + { + j.HasKey("DimOrganizationid", "DimCallProgrammeid").HasName("PK__br_organ__10F219BC602A8F57"); + + j.ToTable("br_organizations_fund_call_programmes"); + + j.IndexerProperty("DimOrganizationid").HasColumnName("dim_organizationid"); + + j.IndexerProperty("DimCallProgrammeid").HasColumnName("dim_call_programmeid"); + }); + + entity.HasMany(d => d.DimOrganizationid2s) + .WithMany(p => p.DimOrganizations) + .UsingEntity>( + "BrPredecessorOrganization", + l => l.HasOne().WithMany().HasForeignKey("DimOrganizationid2").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_predece505451"), + r => r.HasOne().WithMany().HasForeignKey("DimOrganizationid").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_predece849307"), + j => + { + j.HasKey("DimOrganizationid", "DimOrganizationid2").HasName("PK__br_prede__A7CAD2F4E27E76D6"); + + j.ToTable("br_predecessor_organization"); + + j.IndexerProperty("DimOrganizationid").HasColumnName("dim_organizationid"); + + j.IndexerProperty("DimOrganizationid2").HasColumnName("dim_organizationid2"); + }); + + entity.HasMany(d => d.DimOrganizationid2sNavigation) + .WithMany(p => p.DimOrganizationsNavigation) + .UsingEntity>( + "BrSuccessorOrganization", + l => l.HasOne().WithMany().HasForeignKey("DimOrganizationid2").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_success902531"), + r => r.HasOne().WithMany().HasForeignKey("DimOrganizationid").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_success452227"), + j => + { + j.HasKey("DimOrganizationid", "DimOrganizationid2").HasName("PK__br_succe__A7CAD2F4C892F51F"); + + j.ToTable("br_successor organization"); + + j.IndexerProperty("DimOrganizationid").HasColumnName("dim_organizationid"); + + j.IndexerProperty("DimOrganizationid2").HasColumnName("dim_organizationid2"); + }); + + entity.HasMany(d => d.DimOrganizations) + .WithMany(p => p.DimOrganizationid2s) + .UsingEntity>( + "BrPredecessorOrganization", + l => l.HasOne().WithMany().HasForeignKey("DimOrganizationid").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_predece849307"), + r => r.HasOne().WithMany().HasForeignKey("DimOrganizationid2").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_predece505451"), + j => + { + j.HasKey("DimOrganizationid", "DimOrganizationid2").HasName("PK__br_prede__A7CAD2F4E27E76D6"); + + j.ToTable("br_predecessor_organization"); + + j.IndexerProperty("DimOrganizationid").HasColumnName("dim_organizationid"); + + j.IndexerProperty("DimOrganizationid2").HasColumnName("dim_organizationid2"); + }); + + entity.HasMany(d => d.DimOrganizationsNavigation) + .WithMany(p => p.DimOrganizationid2sNavigation) + .UsingEntity>( + "BrSuccessorOrganization", + l => l.HasOne().WithMany().HasForeignKey("DimOrganizationid").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_success452227"), + r => r.HasOne().WithMany().HasForeignKey("DimOrganizationid2").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_success902531"), + j => + { + j.HasKey("DimOrganizationid", "DimOrganizationid2").HasName("PK__br_succe__A7CAD2F4C892F51F"); + + j.ToTable("br_successor organization"); + + j.IndexerProperty("DimOrganizationid").HasColumnName("dim_organizationid"); + + j.IndexerProperty("DimOrganizationid2").HasColumnName("dim_organizationid2"); + }); }); modelBuilder.Entity(entity => @@ -2829,6 +2755,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("FKdim_pid400266"); + entity.HasOne(d => d.DimResearchDataset) + .WithMany(p => p.DimPids) + .HasForeignKey(d => d.DimResearchDatasetId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("URN/DOI"); + entity.HasOne(d => d.DimService) .WithMany(p => p.DimPids) .HasForeignKey(d => d.DimServiceId) @@ -3062,6 +2994,23 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .WithMany(p => p.DimPublicationTargetAudienceCodeNavigations) .HasForeignKey(d => d.TargetAudienceCode) .HasConstraintName("target_audience_code"); + + entity.HasMany(d => d.DimReferencedata) + .WithMany(p => p.DimPublications) + .UsingEntity>( + "BrArtpublicationTypecategory", + l => l.HasOne().WithMany().HasForeignKey("DimReferencedataid").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_artpubl101187"), + r => r.HasOne().WithMany().HasForeignKey("DimPublicationId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_artpubl464312"), + j => + { + j.HasKey("DimPublicationId", "DimReferencedataid").HasName("PK__br_artpu__879F18F30B75E11C"); + + j.ToTable("br_artpublication_typecategory"); + + j.IndexerProperty("DimPublicationId").HasColumnName("dim_publication_id"); + + j.IndexerProperty("DimReferencedataid").HasColumnName("dim_referencedataid"); + }); }); modelBuilder.Entity(entity => @@ -3278,15 +3227,15 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasColumnName("modified"); entity.Property(e => e.NameEn) - .HasMaxLength(255) + .HasMaxLength(400) .HasColumnName("name_en"); entity.Property(e => e.NameFi) - .HasMaxLength(255) + .HasMaxLength(400) .HasColumnName("name_fi"); entity.Property(e => e.NameSv) - .HasMaxLength(255) + .HasMaxLength(400) .HasColumnName("name_sv"); entity.Property(e => e.NameUnd) @@ -3346,7 +3295,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimResearchActivityId, e.DimKeywordId }) - .HasName("PK__dim_rese__F7B536BC499552FD"); + .HasName("PK__dim_rese__F7B536BCE39FD625"); entity.ToTable("dim_research_activity_dim_keyword"); @@ -3573,6 +3522,40 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .WithMany(p => p.DimResearchDatasets) .HasForeignKey(d => d.DimResearchDataCatalogId) .HasConstraintName("FKdim_resear753411"); + + entity.HasMany(d => d.DimKeywords) + .WithMany(p => p.DimResearchDatasets) + .UsingEntity>( + "BrResearchDatasetDimKeyword", + l => l.HasOne().WithMany().HasForeignKey("DimKeywordId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_researc961356"), + r => r.HasOne().WithMany().HasForeignKey("DimResearchDatasetId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("dataset-keywords"), + j => + { + j.HasKey("DimResearchDatasetId", "DimKeywordId").HasName("PK__br_resea__4D226DF25851D3CD"); + + j.ToTable("br_research_dataset_dim_keyword"); + + j.IndexerProperty("DimResearchDatasetId").HasColumnName("dim_research_dataset_id"); + + j.IndexerProperty("DimKeywordId").HasColumnName("dim_keyword_id"); + }); + + entity.HasMany(d => d.DimReferencedata) + .WithMany(p => p.DimResearchDatasets) + .UsingEntity>( + "BrLanguageCodesForDataset", + l => l.HasOne().WithMany().HasForeignKey("DimReferencedataId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_languag480770"), + r => r.HasOne().WithMany().HasForeignKey("DimResearchDatasetId").OnDelete(DeleteBehavior.ClientSetNull).HasConstraintName("FKbr_languag34243"), + j => + { + j.HasKey("DimResearchDatasetId", "DimReferencedataId").HasName("PK__br_langu__576647BF83C8131D"); + + j.ToTable("br_language_codes_for_datasets"); + + j.IndexerProperty("DimResearchDatasetId").HasColumnName("dim_research_dataset_id"); + + j.IndexerProperty("DimReferencedataId").HasColumnName("dim_referencedata_id"); + }); }); modelBuilder.Entity(entity => @@ -3960,7 +3943,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { entity.ToTable("dim_type_of_funding"); - entity.HasIndex(e => e.TypeId, "UQ__dim_type__2C00059919EB8A6B") + entity.HasIndex(e => e.TypeId, "UQ__dim_type__2C00059913E5EF78") .IsUnique(); entity.Property(e => e.Id).HasColumnName("id"); @@ -4125,6 +4108,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.DimOrganizationId).HasColumnName("dim_organization_id"); + entity.Property(e => e.DimRegisteredDataSourceId).HasColumnName("dim_registered_data_source_id"); + entity.Property(e => e.DimResearchActivityId).HasColumnName("dim_research_activity_id"); entity.Property(e => e.DimResearchCommunityId).HasColumnName("dim_research_community_id"); @@ -4182,6 +4167,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey(d => d.DimOrganizationId) .HasConstraintName("language specific homepage"); + entity.HasOne(d => d.DimRegisteredDataSource) + .WithMany(p => p.DimWebLinks) + .HasForeignKey(d => d.DimRegisteredDataSourceId) + .HasConstraintName("FKdim_web_regdatasource"); + entity.HasOne(d => d.DimResearchActivity) .WithMany(p => p.DimWebLinks) .HasForeignKey(d => d.DimResearchActivityId) @@ -4232,7 +4222,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimFundingDecisionId, e.DimOrganizationId, e.DimDateId, e.DimNameId, e.DimPublicationId, e.DimGeoId, e.DimInfrastructureId, e.DimNewsFeedId, e.DimResearchDatasetId, e.DimResearchDataCatalogId, e.DimIdentifierlessDataId, e.DimResearchActivityId, e.DimResearchCommunityId, e.DimReferencedataActorRoleId }) - .HasName("PK__fact_con__B7D7E1B5CFA48C5A"); + .HasName("PK__fact_con__B7D7E1B50BA9F19E"); entity.ToTable("fact_contribution"); @@ -4371,10 +4361,68 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasConstraintName("authors, infras, publications, funding decisions"); }); + modelBuilder.Entity(entity => + { + entity.HasKey(e => new { e.DimReferencedataId, e.DimResearchDatasetId, e.DimKnownPersonId, e.DimPublicationId, e.DimResearchActivityId, e.DimFundingDecisionId, e.DimInfrastructureId }) + .HasName("PK__fact_dim__3CB15DD36CF1EA12"); + + entity.ToTable("fact_dim_referencedata_field_of_science"); + + entity.Property(e => e.DimReferencedataId).HasColumnName("dim_referencedata_id"); + + entity.Property(e => e.DimResearchDatasetId).HasColumnName("dim_research_dataset_id"); + + entity.Property(e => e.DimKnownPersonId).HasColumnName("dim_known_person_id"); + + entity.Property(e => e.DimPublicationId).HasColumnName("dim_publication_id"); + + entity.Property(e => e.DimResearchActivityId).HasColumnName("dim_research_activity_id"); + + entity.Property(e => e.DimFundingDecisionId).HasColumnName("dim_funding_decision_id"); + + entity.Property(e => e.DimInfrastructureId).HasColumnName("dim_infrastructure_id"); + + entity.HasOne(d => d.DimFundingDecision) + .WithMany(p => p.FactDimReferencedataFieldOfSciences) + .HasForeignKey(d => d.DimFundingDecisionId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKfact_dim_r41359"); + + entity.HasOne(d => d.DimInfrastructure) + .WithMany(p => p.FactDimReferencedataFieldOfSciences) + .HasForeignKey(d => d.DimInfrastructureId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKfact_dim_r956301"); + + entity.HasOne(d => d.DimKnownPerson) + .WithMany(p => p.FactDimReferencedataFieldOfSciences) + .HasForeignKey(d => d.DimKnownPersonId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName(" "); + + entity.HasOne(d => d.DimPublication) + .WithMany(p => p.FactDimReferencedataFieldOfSciences) + .HasForeignKey(d => d.DimPublicationId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKfact_dim_r127122"); + + entity.HasOne(d => d.DimReferencedata) + .WithMany(p => p.FactDimReferencedataFieldOfSciences) + .HasForeignKey(d => d.DimReferencedataId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKfact_dim_r588766"); + + entity.HasOne(d => d.DimResearchDataset) + .WithMany(p => p.FactDimReferencedataFieldOfSciences) + .HasForeignKey(d => d.DimResearchDatasetId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKfact_dim_r926246"); + }); + modelBuilder.Entity(entity => { - entity.HasKey(e => new { e.DimUserProfileId, e.DimFieldDisplaySettingsId, e.DimNameId, e.DimWebLinkId, e.DimFundingDecisionId, e.DimPublicationId, e.DimPidId, e.DimPidIdOrcidPutCode, e.DimResearchActivityId, e.DimEventId, e.DimEducationId, e.DimCompetenceId, e.DimResearchCommunityId, e.DimTelephoneNumberId, e.DimEmailAddrressId, e.DimResearcherDescriptionId, e.DimIdentifierlessDataId, e.DimOrcidPublicationId, e.DimKeywordId, e.DimAffiliationId, e.DimResearcherToResearchCommunityId, e.DimFieldOfScienceId, e.DimResearchDatasetId, e.DimRegisteredDataSourceId }) - .HasName("PK__fact_fie__A1BF31AA946882DF"); + entity.HasKey(e => new { e.DimUserProfileId, e.DimFieldDisplaySettingsId, e.DimNameId, e.DimWebLinkId, e.DimFundingDecisionId, e.DimPublicationId, e.DimPidId, e.DimPidIdOrcidPutCode, e.DimResearchActivityId, e.DimEventId, e.DimEducationId, e.DimCompetenceId, e.DimResearchCommunityId, e.DimTelephoneNumberId, e.DimEmailAddrressId, e.DimResearcherDescriptionId, e.DimIdentifierlessDataId, e.DimOrcidPublicationId, e.DimKeywordId, e.DimAffiliationId, e.DimResearcherToResearchCommunityId, e.DimResearchDatasetId, e.DimRegisteredDataSourceId, e.DimReferencedataFieldOfScienceId }) + .HasName("PK__fact_fie__93106223BBB1E928"); entity.ToTable("fact_field_values"); @@ -4420,12 +4468,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.DimResearcherToResearchCommunityId).HasColumnName("dim_researcher_to_research_community_id"); - entity.Property(e => e.DimFieldOfScienceId).HasColumnName("dim_field_of_science_id"); - entity.Property(e => e.DimResearchDatasetId).HasColumnName("dim_research_dataset_id"); entity.Property(e => e.DimRegisteredDataSourceId).HasColumnName("dim_registered_data_source_id"); + entity.Property(e => e.DimReferencedataFieldOfScienceId).HasColumnName("dim_referencedata_field_of_science_id"); + entity.Property(e => e.Created) .HasColumnType("datetime") .HasColumnName("created"); @@ -4483,12 +4531,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("field content settings"); - entity.HasOne(d => d.DimFieldOfScience) - .WithMany(p => p.FactFieldValues) - .HasForeignKey(d => d.DimFieldOfScienceId) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("FKfact_field339072"); - entity.HasOne(d => d.DimFundingDecision) .WithMany(p => p.FactFieldValues) .HasForeignKey(d => d.DimFundingDecisionId) @@ -4537,6 +4579,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("displayed publications"); + entity.HasOne(d => d.DimReferencedataFieldOfScience) + .WithMany(p => p.FactFieldValues) + .HasForeignKey(d => d.DimReferencedataFieldOfScienceId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FKfact_field192234"); + entity.HasOne(d => d.DimRegisteredDataSource) .WithMany(p => p.FactFieldValues) .HasForeignKey(d => d.DimRegisteredDataSourceId) @@ -4595,7 +4643,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimKeywordId, e.DimServiceId, e.DimServicePointId, e.DimInfrastructureId }) - .HasName("PK__fact_inf__3C29B680609CD02A"); + .HasName("PK__fact_inf__3C29B680677C35E2"); entity.ToTable("fact_infra_keywords"); @@ -4652,7 +4700,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimPublicationChannelId, e.DimReferencedataId, e.Year }) - .HasName("PK__fact_juf__0E099E4B753B21B6"); + .HasName("PK__fact_juf__0E099E4BB4E9B8B5"); entity.ToTable("fact_jufo_class_codes_for_pub_channels"); @@ -4678,7 +4726,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity(entity => { entity.HasKey(e => new { e.DimOrganizationId, e.DimGeoId, e.DimInfrastructureId, e.DimServiceId, e.DimServicePointId, e.DimDateIdStart, e.DimDateIdEnd }) - .HasName("PK__fact_upk__850A8E30D5F346F4"); + .HasName("PK__fact_upk__850A8E30B5A84163"); entity.ToTable("fact_upkeep"); @@ -4756,6 +4804,135 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasConstraintName("FKfact_upkee415806"); }); + modelBuilder.Entity(entity => + { + entity.HasNoKey(); + + entity.ToTable("temp_br_infrastructure_dim_field_of_science"); + + entity.Property(e => e.DimFieldOfScienceId).HasColumnName("dim_field_of_science_id"); + + entity.Property(e => e.DimInfrastructureId).HasColumnName("dim_infrastructure_id"); + }); + + modelBuilder.Entity(entity => + { + entity.HasNoKey(); + + entity.ToTable("temp_br_participates_in_funding_group"); + + entity.Property(e => e.DimFundingDecisionid).HasColumnName("dim_funding_decisionid"); + + entity.Property(e => e.DimNameId).HasColumnName("dim_name_id"); + + entity.Property(e => e.DimOrganizationId).HasColumnName("dim_organization_id"); + + entity.Property(e => e.EndOfParticipation).HasColumnName("end_of_participation"); + + entity.Property(e => e.RoleInFundingGroup) + .HasMaxLength(255) + .HasColumnName("role_in_funding_group"); + + entity.Property(e => e.ShareOfFundingInEur) + .HasColumnType("decimal(18, 2)") + .HasColumnName("share_of_funding_in_EUR"); + + entity.Property(e => e.SourceId) + .HasMaxLength(30) + .HasColumnName("source_id"); + }); + + modelBuilder.Entity(entity => + { + entity.HasNoKey(); + + entity.ToTable("temp_dim_registered_data_source"); + + entity.Property(e => e.Created) + .HasColumnType("datetime") + .HasColumnName("created"); + + entity.Property(e => e.DimOrganizationId).HasColumnName("dim_organization_id"); + + entity.Property(e => e.Id) + .ValueGeneratedOnAdd() + .HasColumnName("id"); + + entity.Property(e => e.Modified) + .HasColumnType("datetime") + .HasColumnName("modified"); + + entity.Property(e => e.Name) + .IsRequired() + .HasMaxLength(255) + .HasColumnName("name"); + + entity.Property(e => e.SourceDescription) + .HasMaxLength(255) + .HasColumnName("source_description"); + + entity.Property(e => e.SourceId) + .IsRequired() + .HasMaxLength(255) + .HasColumnName("source_id"); + }); + + modelBuilder.Entity(entity => + { + entity.HasNoKey(); + + entity.ToTable("temp_dim_registered_data_source_20221121"); + + entity.Property(e => e.Created) + .HasColumnType("datetime") + .HasColumnName("created"); + + entity.Property(e => e.DimOrganizationId).HasColumnName("dim_organization_id"); + + entity.Property(e => e.Id) + .ValueGeneratedOnAdd() + .HasColumnName("id"); + + entity.Property(e => e.Modified) + .HasColumnType("datetime") + .HasColumnName("modified"); + + entity.Property(e => e.Name) + .IsRequired() + .HasMaxLength(255) + .HasColumnName("name"); + + entity.Property(e => e.SourceDescription) + .HasMaxLength(255) + .HasColumnName("source_description"); + + entity.Property(e => e.SourceId) + .IsRequired() + .HasMaxLength(255) + .HasColumnName("source_id"); + }); + + modelBuilder.Entity(entity => + { + entity.HasNoKey(); + + entity.ToTable("temp_fact_dim_referencedata_field_of_science"); + + entity.Property(e => e.DimFundingDecisionId).HasColumnName("dim_funding_decision_id"); + + entity.Property(e => e.DimInfrastructureId).HasColumnName("dim_infrastructure_id"); + + entity.Property(e => e.DimKnownPersonId).HasColumnName("dim_known_person_id"); + + entity.Property(e => e.DimPublicationId).HasColumnName("dim_publication_id"); + + entity.Property(e => e.DimReferencedataId).HasColumnName("dim_referencedata_id"); + + entity.Property(e => e.DimResearchActivityId).HasColumnName("dim_research_activity_id"); + + entity.Property(e => e.DimResearchDatasetId).HasColumnName("dim_research_dataset_id"); + }); + OnModelCreatingPartial(modelBuilder); } diff --git a/aspnetcore/src/api/Program.cs b/aspnetcore/src/api/Program.cs index 98038a28..25ae9736 100644 --- a/aspnetcore/src/api/Program.cs +++ b/aspnetcore/src/api/Program.cs @@ -12,6 +12,8 @@ public static void Main(string[] args) { var configuration = new ConfigurationBuilder() .AddJsonFile("appsettings.json") + .AddUserSecrets() + .AddEnvironmentVariables() .Build(); Log.Logger = new LoggerConfiguration() diff --git a/aspnetcore/src/api/Services/AdminService.cs b/aspnetcore/src/api/Services/AdminService.cs new file mode 100644 index 00000000..7751dc74 --- /dev/null +++ b/aspnetcore/src/api/Services/AdminService.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Threading.Tasks; +using api.Models.Common; +using api.Models.ProfileEditor; +using api.Models.Ttv; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Nest; + +namespace api.Services +{ + /* + * AdminService implements admin command related functionality. + */ + public class AdminService : IAdminService + { + private readonly TtvContext _ttvContext; + private readonly IOrcidApiService _orcidApiService; + private readonly IUserProfileService _userProfileService; + private readonly ILogger _logger; + private string logPrefix = "AdminService: "; + private const int delayMillisecondsBetweenOrcidApiRequests = 1000; + + public AdminService( + TtvContext ttvContext, + ILogger logger, + IOrcidApiService orcidApiService, + IUserProfileService userProfileService) + { + _ttvContext = ttvContext; + _logger = logger; + _orcidApiService = orcidApiService; + _userProfileService = userProfileService; + } + + /* + * Register ORCID webhook for a single user profile. + */ + public async Task RegisterOrcidWebhookForSingleUserprofile(string webhookOrcidId) + { + // Check that ORCID webhook feature is enabled + if (!_orcidApiService.IsOrcidWebhookEnabled()) + { + _logger.LogInformation($"{logPrefix}ORCID webhook feature disabled in configuration"); + return; + } + + // Check that user profile exists + if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: webhookOrcidId)) + { + _logger.LogError($"{logPrefix}ORCID webhook registration failed for {webhookOrcidId}: user profile not found"); + return; + } + + // Register ORCID webhook. + try + { + bool success = await _orcidApiService.RegisterOrcidWebhook(orcidId: webhookOrcidId); + if (success) + { + _logger.LogInformation($"{logPrefix}ORCID webhook registered for {webhookOrcidId}"); + } + else + { + _logger.LogError($"{logPrefix}ORCID webhook registration failed for {webhookOrcidId}"); + } + } + catch (Exception ex) + { + _logger.LogError($"{logPrefix}ORCID webhook registration failed for {webhookOrcidId}: {ex}"); + } + } + + /* + * Unregister ORCID webhook for a single user profile. + */ + public async Task UnregisterOrcidWebhookForSingleUserprofile(string webhookOrcidId) + { + // Check that ORCID webhook feature is enabled + if (!_orcidApiService.IsOrcidWebhookEnabled()) + { + _logger.LogInformation($"{logPrefix}ORCID webhook feature disabled in configuration"); + return; + } + + // Check that user profile exists + if (!await _userProfileService.UserprofileExistsForOrcidId(orcidId: webhookOrcidId)) + { + _logger.LogError($"{logPrefix}ORCID webhook registration failed for {webhookOrcidId}: user profile not found"); + return; + } + + // UnRegister ORCID webhook + try + { + bool success = await _orcidApiService.UnregisterOrcidWebhook(orcidId: webhookOrcidId); + if (success) + { + _logger.LogInformation($"{logPrefix}ORCID webhook unregistered for {webhookOrcidId}"); + } + else + { + _logger.LogError($"{logPrefix}ORCID webhook unregistration failed for {webhookOrcidId}"); + } + } + catch (Exception ex) + { + _logger.LogError($"{logPrefix}ORCID webhook unregistration failed for {webhookOrcidId}: {ex}"); + } + } + + /* + * Register ORCID webhook for all user profiles. + */ + public async Task RegisterOrcidWebhookForAllUserprofiles() + { + // Check that ORCID webhook feature is enabled + if (!_orcidApiService.IsOrcidWebhookEnabled()) + { + _logger.LogInformation($"{logPrefix}ORCID webhook feature disabled in configuration"); + return; + } + + // Get all user profiles + List dimUserProfiles = await _ttvContext.DimUserProfiles.Where(dup => !string.IsNullOrWhiteSpace(dup.OrcidId)).AsNoTracking().ToListAsync(); + + // Loop user profiles + foreach (DimUserProfile dimUserProfile in dimUserProfiles) + { + // Register ORCID webhook + try + { + bool success = await _orcidApiService.RegisterOrcidWebhook(orcidId: dimUserProfile.OrcidId); + if (success) + { + _logger.LogInformation($"{logPrefix}ORCID webhook registered for {dimUserProfile.OrcidId}"); + } + else + { + _logger.LogError($"{logPrefix}ORCID webhook registration failed for {dimUserProfile.OrcidId}"); + } + } + catch (Exception ex) + { + _logger.LogError($"{logPrefix}ORCID webhook registration failed for {dimUserProfile.OrcidId}: {ex}"); + } + + // Prevent flooding ORCID API + await Task.Delay(delayMillisecondsBetweenOrcidApiRequests); + } + } + + /* + * Unregister ORCID webhook for all user profiles. + */ + public async Task UnregisterOrcidWebhookForAllUserprofiles() + { + // Check that ORCID webhook feature is enabled + if (!_orcidApiService.IsOrcidWebhookEnabled()) + { + _logger.LogInformation($"{logPrefix}ORCID webhook feature disabled in configuration"); + return; + } + + // Get all user profiles + List dimUserProfiles = await _ttvContext.DimUserProfiles.Where(dup => !string.IsNullOrWhiteSpace(dup.OrcidId)).AsNoTracking().ToListAsync(); + + // Loop user profiles + foreach (DimUserProfile dimUserProfile in dimUserProfiles) + { + // Unregister ORCID webhook + try + { + bool success = await _orcidApiService.UnregisterOrcidWebhook(orcidId: dimUserProfile.OrcidId); + if (success) + { + _logger.LogInformation($"{logPrefix}ORCID webhook unregistered for {dimUserProfile.OrcidId}"); + } + else + { + _logger.LogError($"{logPrefix}ORCID webhook unregistration failed for {dimUserProfile.OrcidId}"); + } + } + catch (Exception ex) + { + _logger.LogError($"{logPrefix}ORCID webhook unregistration failed for {dimUserProfile.OrcidId}: {ex}"); + } + + // Prevent flooding ORCID API + await Task.Delay(delayMillisecondsBetweenOrcidApiRequests); + } + } + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/DuplicateHandlerService.cs b/aspnetcore/src/api/Services/DuplicateHandlerService.cs index 070a1e02..816ceaeb 100644 --- a/aspnetcore/src/api/Services/DuplicateHandlerService.cs +++ b/aspnetcore/src/api/Services/DuplicateHandlerService.cs @@ -26,6 +26,20 @@ public bool IsOrcidPublication(ProfileDataFromSql profileData) return profileData.FactFieldValues_DimOrcidPublicationId != -1 && profileData.DimFieldDisplaySettings_FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_PUBLICATION_ORCID; } + /* + * Make sure that publication year contains either a valid year or null. + */ + public int? HandlePublicationYear(int? dimDateYear) + { + if (dimDateYear == null || dimDateYear < 1) + { + return null; + } + else { + return dimDateYear; + } + } + /* * When two publications have the same DOI, they will be considered as DIFFERENT publications if: * - Virta publication code is A3, A4, B2, B3, D2, D3 or E1 @@ -48,6 +62,7 @@ public List AddDataSource(ProfileEditorPublication publicat return publication.DataSources; } + /* * Add publication to publication list. * Handle duplicates by matching DOI. Handle special case in DOI matching regarding Virta publication type codes. @@ -89,9 +104,13 @@ public List AddPublicationToProfileEditorData(ProfileE { PublicationId = profileData.DimPublication_PublicationId, PublicationName = profileData.DimPublication_PublicationName, - PublicationYear = profileData.DimPublication_PublicationYear, + PublicationYear = HandlePublicationYear(profileData.DimPublication_PublicationYear), Doi = profileData.DimPublication_Doi, + AuthorsText = profileData.DimPublication_AuthorsText, TypeCode = profileData.DimPublication_PublicationTypeCode, + JournalName = profileData.DimPublication_JournalName, + ConferenceName = profileData.DimPublication_ConferenceName, + ParentPublicationName = profileData.DimPublication_ParentPublicationName, itemMeta = new ProfileEditorItemMeta() { Id = profileData.FactFieldValues_DimPublicationId, @@ -111,9 +130,13 @@ public List AddPublicationToProfileEditorData(ProfileE { PublicationId = profileData.DimOrcidPublication_PublicationId, PublicationName = profileData.DimOrcidPublication_PublicationName, - PublicationYear = profileData.DimOrcidPublication_PublicationYear, + PublicationYear = HandlePublicationYear(profileData.DimOrcidPublication_PublicationYear), Doi = profileData.DimOrcidPublication_Doi, + AuthorsText = "", TypeCode = "", + JournalName = "", + ConferenceName = "", + ParentPublicationName = "", itemMeta = new ProfileEditorItemMeta() { Id = profileData.FactFieldValues_DimOrcidPublicationId, diff --git a/aspnetcore/src/api/Services/ElasticsearchService.cs b/aspnetcore/src/api/Services/ElasticsearchService.cs index a30ea378..7edf02c6 100644 --- a/aspnetcore/src/api/Services/ElasticsearchService.cs +++ b/aspnetcore/src/api/Services/ElasticsearchService.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using api.Models.Elasticsearch; +using api.Models.Log; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Nest; @@ -15,6 +16,7 @@ public class ElasticsearchService : IElasticsearchService public ElasticClient ESclient; public IConfiguration Configuration { get; } private readonly ILogger _logger; + private readonly string elasticsearchProfileIndexName = "person"; // Check if Elasticsearch synchronization is enabled and related configuration is valid. public bool IsElasticsearchSyncEnabled() @@ -37,17 +39,40 @@ public ElasticsearchService(IConfiguration configuration, ILogger c + .Map(m => m.AutoMap()) + ); + + if (!createIndexResponse.IsValid) + { + _logger.LogError("ElasticsearchService: failed creating index: " + elasticsearchProfileIndexName + " : " + createIndexResponse.ToString()); + } + } + else + { + _logger.LogInformation("ElasticsearchService: required index found: " + elasticsearchProfileIndexName); + } } } // Update entry in Elasticsearch person index // TODO: When 3rd party sharing feature is implemented, check that TTV share is enabled in user profile. - public async Task UpdateEntryInElasticsearchPersonIndex(string orcidId, ElasticsearchPerson person) + public async Task UpdateEntryInElasticsearchPersonIndex(string orcidId, ElasticsearchPerson person, LogUserIdentification logUserIdentification) { if (!IsElasticsearchSyncEnabled()) { @@ -72,13 +97,21 @@ public async Task UpdateEntryInElasticsearchPersonIndex(string orcidId, El { errormessage = asyncIndexResponse.ServerError.Error.Reason; } - _logger.LogError("ElasticsearchService: ERROR updating: " + orcidId + ": " + errormessage); + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_UPDATE, + state: LogContent.ActionState.FAILED, + error: true, + message: errormessage)); + return false; } } // Delete entry from Elasticsearch person index - public async Task DeleteEntryFromElasticsearchPersonIndex(string orcidId) + public async Task DeleteEntryFromElasticsearchPersonIndex(string orcidId, LogUserIdentification logUserIdentification) { if (!IsElasticsearchSyncEnabled()) { @@ -103,7 +136,16 @@ public async Task DeleteEntryFromElasticsearchPersonIndex(string orcidId) { errormessage = asyncDeleteResponse.ServerError.Error.Reason; } - _logger.LogError("ElasticsearchService: ERROR deleting: " + orcidId + ": " + errormessage); + + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.ELASTICSEARCH_DELETE, + state: LogContent.ActionState.FAILED, + error: true, + message: errormessage)); + return false; } } diff --git a/aspnetcore/src/api/Services/IAdminService.cs b/aspnetcore/src/api/Services/IAdminService.cs new file mode 100644 index 00000000..b3d6c8ef --- /dev/null +++ b/aspnetcore/src/api/Services/IAdminService.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; + +namespace api.Services +{ + public interface IAdminService + { + Task RegisterOrcidWebhookForAllUserprofiles(); + Task RegisterOrcidWebhookForSingleUserprofile(string webhookOrcidId); + Task UnregisterOrcidWebhookForAllUserprofiles(); + Task UnregisterOrcidWebhookForSingleUserprofile(string webhookOrcidId); + } +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/IElasticsearchService.cs b/aspnetcore/src/api/Services/IElasticsearchService.cs index 6f7dac77..3373b4a2 100644 --- a/aspnetcore/src/api/Services/IElasticsearchService.cs +++ b/aspnetcore/src/api/Services/IElasticsearchService.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using api.Models.Elasticsearch; +using api.Models.Log; using Microsoft.Extensions.Configuration; namespace api.Services @@ -8,8 +9,8 @@ public interface IElasticsearchService { IConfiguration Configuration { get; } - Task DeleteEntryFromElasticsearchPersonIndex(string orcidId); + Task DeleteEntryFromElasticsearchPersonIndex(string orcidId, LogUserIdentification logUserIdentification); bool IsElasticsearchSyncEnabled(); - Task UpdateEntryInElasticsearchPersonIndex(string orcidId, ElasticsearchPerson person); + Task UpdateEntryInElasticsearchPersonIndex(string orcidId, ElasticsearchPerson person, LogUserIdentification logUserIdentification); } } \ No newline at end of file diff --git a/aspnetcore/src/api/Services/IKeycloakAdminApiService.cs b/aspnetcore/src/api/Services/IKeycloakAdminApiService.cs index 92139d78..dfcb725f 100644 --- a/aspnetcore/src/api/Services/IKeycloakAdminApiService.cs +++ b/aspnetcore/src/api/Services/IKeycloakAdminApiService.cs @@ -1,5 +1,6 @@ using System.IdentityModel.Tokens.Jwt; using System.Threading.Tasks; +using api.Models.Log; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; @@ -12,10 +13,10 @@ public interface IKeycloakAdminApiService string GetAccessTokenFromHttpRequest(HttpRequest httpRequest); string GetKeycloakUserIdFromAccessToken(HttpRequest httpRequest); string GetOrcidIdFromRawKeycloakUserData(string keycloakUserDataRaw); - Task GetRawUserDataFromKeycloakAdminApi(string keycloakUserId); - Task LogoutUser(string tokenStr); - Task RemoveUser(string tokenStr); - Task SetOrcidAttributedInKeycloak(JwtSecurityToken jwtFromUser); - Task SetOrcidIdAsKeycloakUserAttribute(string keycloakUserId, string orcidId); + Task GetRawUserDataFromKeycloakAdminApi(string keycloakUserId, LogUserIdentification logUserIdentification); + Task LogoutUser(string tokenStr, LogUserIdentification logUserIdentification); + Task RemoveUser(string tokenStr, LogUserIdentification logUserIdentification); + Task SetOrcidAttributedInKeycloak(JwtSecurityToken jwtFromUser, LogUserIdentification logUserIdentification); + Task SetOrcidIdAsKeycloakUserAttribute(string keycloakUserId, string orcidId, LogUserIdentification logUserIdentification); } } \ No newline at end of file diff --git a/aspnetcore/src/api/Services/IOrcidApiService.cs b/aspnetcore/src/api/Services/IOrcidApiService.cs index d7218fe6..4a3e2fd0 100644 --- a/aspnetcore/src/api/Services/IOrcidApiService.cs +++ b/aspnetcore/src/api/Services/IOrcidApiService.cs @@ -1,11 +1,21 @@ using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; namespace api.Services { public interface IOrcidApiService { + IConfiguration Configuration { get; } + string GetOrcidRecordPath(string orcidId); - Task GetRecordFromPublicApi(string orcidId); + string GetOrcidWebhookAccessToken(); + string GetOrcidWebhookCallbackUri(string orcidId); + string GetOrcidWebhookRegistrationUri(string orcidId); Task GetRecordFromMemberApi(string orcidId, string orcidAccessToken); + Task GetRecordFromPublicApi(string orcidId); + string GetUrlEncodedString(string uriToEncode); + bool IsOrcidWebhookEnabled(); + Task RegisterOrcidWebhook(string orcidId); + Task UnregisterOrcidWebhook(string orcidId); } } \ No newline at end of file diff --git a/aspnetcore/src/api/Services/ITtvSqlService.cs b/aspnetcore/src/api/Services/ITtvSqlService.cs index bfc62d46..a7ce621b 100644 --- a/aspnetcore/src/api/Services/ITtvSqlService.cs +++ b/aspnetcore/src/api/Services/ITtvSqlService.cs @@ -33,6 +33,15 @@ public interface ITtvSqlService string GetSqlQuery_Delete_DimWebLinks(List dimWebLinkIds); string GetSqlQuery_Delete_FactFieldValues(int userprofileId); string GetSqlQuery_ProfileData(int userprofileId, bool forElasticsearch = false); + string GetSqlQuery_Select_BrParticipatesInFundingGroup(int dimNameId); + string GetSqlQuery_Select_CountPublishedItemsInUserprofile(int dimUserProfileId); + string GetSqlQuery_Select_DimAffiliation(int dimKnownPersonId); + string GetSqlQuery_Select_DimEducation(int dimKnownPersonId); + string GetSqlQuery_Select_DimEmailAddrress(int dimKnownPersonId); + string GetSqlQuery_Select_DimResearcherDescription(int dimKnownPersonId); + string GetSqlQuery_Select_DimTelephoneNumber(int dimKnownPersonId); + string GetSqlQuery_Select_DimWebLink(int dimKnownPersonId); + string GetSqlQuery_Select_FactContribution(int dimNameId); string GetSqlQuery_Select_FactFieldValues(int userprofileId); string GetSqlQuery_Update_FactFieldValues(int dimUserProfileId, ProfileEditorItemMeta profileEditorItemMeta); } diff --git a/aspnetcore/src/api/Services/IUserProfileService.cs b/aspnetcore/src/api/Services/IUserProfileService.cs index 86fad021..18f7793b 100644 --- a/aspnetcore/src/api/Services/IUserProfileService.cs +++ b/aspnetcore/src/api/Services/IUserProfileService.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Threading.Tasks; +using api.Models.Log; using api.Models.Orcid; using api.Models.ProfileEditor.Items; using api.Models.Ttv; @@ -8,27 +10,25 @@ namespace api.Services { public interface IUserProfileService { - void AddDimAffiliationToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile); - void AddDimEducationToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile); - void AddDimEmailAddressItemsToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile); - void AddDimResearcherDescriptionToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile); - void AddDimTelephoneItemsToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile); - void AddFactContributionItemsToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile); Task AddOrUpdateDimEmailAddress(string emailAddress, int dimKnownPersonId, int dimRegisteredDataSourceId); Task AddOrUpdateDimName(string lastName, string firstNames, int dimKnownPersonId, int dimRegisteredDataSourceId); Task AddOrUpdateDimResearcherDescription(string description_fi, string description_en, string description_sv, int dimKnownPersonId, int dimRegisteredDataSourceId); - void AddTtvDataToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile); + Task AddTtvDataToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile, LogUserIdentification logUserIdentification); bool CanDeleteFactFieldValueRelatedData(FactFieldValue ffv); - Task CreateProfile(string orcidId); - Task DeleteProfileDataAsync(int userprofileId); + Task CreateProfile(string orcidId, LogUserIdentification logUserIdentification); + Task DeleteProfileDataAsync(int userprofileId, LogUserIdentification logUserIdentification); Task ExecuteRawSql(string sql); DimOrcidPublication GetEmptyDimOrcidPublication(); DimPid GetEmptyDimPid(); FactFieldValue GetEmptyFactFieldValue(); FactFieldValue GetEmptyFactFieldValueDemo(); List GetFieldIdentifiers(); - Task GetProfileDataAsync2(int userprofileId, bool forElasticsearch = false); + DimKnownPerson GetNewDimKnownPerson(string orcidId, DateTime currentDateTime); + Task GetProfileDataAsync(int userprofileId, LogUserIdentification logUserIdentification, bool forElasticsearch = false); + Task GetUserprofile(string orcidId); + Task GetUserprofileById(int Id); Task GetUserprofileId(string orcidId); + Task IsUserprofilePublished(int dimUserProfileId); Task UpdateOrcidTokensInDimUserProfile(int dimUserProfileId, OrcidTokens orcidTokens); Task UserprofileExistsForOrcidId(string orcidId); } diff --git a/aspnetcore/src/api/Services/IUtilityService.cs b/aspnetcore/src/api/Services/IUtilityService.cs index 23ef5efe..bf695192 100644 --- a/aspnetcore/src/api/Services/IUtilityService.cs +++ b/aspnetcore/src/api/Services/IUtilityService.cs @@ -5,5 +5,8 @@ namespace api.Services public interface IUtilityService { DateTime GetCurrentDateTime(); + string GetDatasourceOrganizationName_ORCID(); + string GetDatasourceOrganizationName_TTV(); + string GetOrganizationId_OKM(); } } \ No newline at end of file diff --git a/aspnetcore/src/api/Services/KeycloakAdminApiService.cs b/aspnetcore/src/api/Services/KeycloakAdminApiService.cs index 4c7a777a..40e42f0f 100644 --- a/aspnetcore/src/api/Services/KeycloakAdminApiService.cs +++ b/aspnetcore/src/api/Services/KeycloakAdminApiService.cs @@ -8,6 +8,7 @@ using System.IdentityModel.Tokens.Jwt; using System.Text; using Microsoft.Extensions.Logging; +using api.Models.Log; namespace api.Services { @@ -51,20 +52,40 @@ public String GetKeycloakUserIdFromAccessToken(HttpRequest httpRequest) * Get user data from Keycloak Admin API. * Use name HttpClient "keycloakClient". */ - public async Task GetRawUserDataFromKeycloakAdminApi(string keycloakUserId) + public async Task GetRawUserDataFromKeycloakAdminApi(string keycloakUserId, LogUserIdentification logUserIdentification) { - //_logger.LogInformation("Get userdata for user id: " + keycloakUserId); + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_GET_RAW_USER_DATA, + state: LogContent.ActionState.START)); + HttpClient keycloakAdminApiHttpClient = _httpClientFactory.CreateClient("keycloakClient"); HttpRequestMessage request = new(method: HttpMethod.Get, requestUri: keycloakUserId); HttpResponseMessage response = await keycloakAdminApiHttpClient.SendAsync(request); try { response.EnsureSuccessStatusCode(); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_GET_RAW_USER_DATA, + state: LogContent.ActionState.COMPLETE)); + return await response.Content.ReadAsStringAsync(); } catch (HttpRequestException) { - _logger.LogError("Keycloak get userdata failed: " + keycloakUserId); + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_GET_RAW_USER_DATA, + state: LogContent.ActionState.FAILED, + error: true)); return ""; } } @@ -94,9 +115,15 @@ public async Task GetRawUserDataFromKeycloakAdminApi(string keycloakUser * Set ORCID ID as Keycloak user attribute using Keycloak Admin API. * Use name HttpClient "keycloakClient". */ - public async Task SetOrcidIdAsKeycloakUserAttribute(string keycloakUserId, string orcidId) + public async Task SetOrcidIdAsKeycloakUserAttribute(string keycloakUserId, string orcidId, LogUserIdentification logUserIdentification) { - //_logger.LogInformation("Set ORCID ID " + orcidId + " as attribute to Keycloak user: " + keycloakUserId); + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_SET_ORCID_ATTRIBUTE, + state: LogContent.ActionState.START)); + HttpClient keycloakAdminApiHttpClient = _httpClientFactory.CreateClient("keycloakClient"); HttpRequestMessage request = new(method: HttpMethod.Put, requestUri: keycloakUserId); string stringPayload = "{\"attributes\": {\"orcid\": [\"" + orcidId + "\"]}}"; @@ -105,11 +132,26 @@ public async Task SetOrcidIdAsKeycloakUserAttribute(string keycloakUserId, try { response.EnsureSuccessStatusCode(); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_SET_ORCID_ATTRIBUTE, + state: LogContent.ActionState.COMPLETE)); + return true; } catch (HttpRequestException) { - _logger.LogError("Keycloak set ORCID ID " + orcidId + " as attribute to Keycloak user failed: " + keycloakUserId); + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_SET_ORCID_ATTRIBUTE, + state: LogContent.ActionState.FAILED, + error: true)); + return false; } } @@ -117,7 +159,7 @@ public async Task SetOrcidIdAsKeycloakUserAttribute(string keycloakUserId, /* * Set attribute "orcid" for Keycloak user. */ - public async Task SetOrcidAttributedInKeycloak(JwtSecurityToken jwtFromUser) + public async Task SetOrcidAttributedInKeycloak(JwtSecurityToken jwtFromUser, LogUserIdentification logUserIdentification) { /* * check is orcid already in token @@ -135,7 +177,7 @@ public async Task SetOrcidAttributedInKeycloak(JwtSecurityToken jwtFromUse string keycloakUserId = _tokenService.GetKeycloakUserIdFromJwt(jwtFromUser); // Get Keycloak user data. - string keycloakUserDataRaw = await this.GetRawUserDataFromKeycloakAdminApi(keycloakUserId); + string keycloakUserDataRaw = await this.GetRawUserDataFromKeycloakAdminApi(keycloakUserId, logUserIdentification); // Stop if Keycloak user data was not received. if (keycloakUserDataRaw == "") @@ -153,7 +195,7 @@ public async Task SetOrcidAttributedInKeycloak(JwtSecurityToken jwtFromUse } // Set orcid id as Keycloak user attribute. - return await this.SetOrcidIdAsKeycloakUserAttribute(keycloakUserId, orcidId); + return await this.SetOrcidIdAsKeycloakUserAttribute(keycloakUserId, orcidId, logUserIdentification); } return true; } @@ -163,8 +205,15 @@ public async Task SetOrcidAttributedInKeycloak(JwtSecurityToken jwtFromUse * Remove all user sessions associated with the user by sending Keycloak admin api message: * POST /{realm}/users/{id}/logout */ - public async Task LogoutUser(String tokenStr) + public async Task LogoutUser(String tokenStr, LogUserIdentification logUserIdentification) { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_USER_LOGOUT, + state: LogContent.ActionState.START)); + // Get jwt from string JwtSecurityToken jwtFromUser = _tokenService.GetJwtFromString(tokenStr); @@ -177,12 +226,26 @@ public async Task LogoutUser(String tokenStr) try { response.EnsureSuccessStatusCode(); - _logger.LogInformation("Keycloak user logout OK: " + keycloakUserId); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_USER_LOGOUT, + state: LogContent.ActionState.COMPLETE)); + return true; } catch (HttpRequestException) { - _logger.LogError("Keycloak user logout failed: " + keycloakUserId); + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_USER_LOGOUT, + error: true, + state: LogContent.ActionState.FAILED)); + return false; } } @@ -191,8 +254,15 @@ public async Task LogoutUser(String tokenStr) * Remove Keycloak user. * DELETE /{realm}/users/{id} */ - public async Task RemoveUser(String tokenStr) + public async Task RemoveUser(String tokenStr, LogUserIdentification logUserIdentification) { + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_USER_DELETE, + state: LogContent.ActionState.START)); + // Get jwt from string JwtSecurityToken jwtFromUser = _tokenService.GetJwtFromString(tokenStr); @@ -205,12 +275,26 @@ public async Task RemoveUser(String tokenStr) try { response.EnsureSuccessStatusCode(); - _logger.LogInformation("Keycloak user delete OK: " + keycloakUserId); + + _logger.LogInformation( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_USER_DELETE, + state: LogContent.ActionState.COMPLETE)); + return true; } catch (HttpRequestException) { - _logger.LogError("Keycloak user delete failed: " + keycloakUserId); + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.KEYCLOAK_USER_DELETE, + error: true, + state: LogContent.ActionState.FAILED)); + return false; } } diff --git a/aspnetcore/src/api/Services/OrcidApiService.cs b/aspnetcore/src/api/Services/OrcidApiService.cs index 577f975e..bc4a06f7 100644 --- a/aspnetcore/src/api/Services/OrcidApiService.cs +++ b/aspnetcore/src/api/Services/OrcidApiService.cs @@ -2,6 +2,11 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; +using System.Web; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Nest; +using Serilog.Core; namespace api.Services { @@ -11,10 +16,17 @@ namespace api.Services public class OrcidApiService : IOrcidApiService { private readonly IHttpClientFactory _httpClientFactory; + public IConfiguration Configuration { get; } + private readonly ILogger _logger; - public OrcidApiService(IHttpClientFactory httpClientFactory) + public OrcidApiService() { } // for unit test + public OrcidApiService(IConfiguration configuration) { Configuration = configuration; } // for unit test + + public OrcidApiService(IHttpClientFactory httpClientFactory, IConfiguration configuration, ILogger logger) { _httpClientFactory = httpClientFactory; + _logger = logger; + Configuration = configuration; } /* @@ -36,6 +48,7 @@ public async Task GetRecordFromPublicApi(String orcidId) string orcidRecordPath = GetOrcidRecordPath(orcidId); // GET request HttpRequestMessage request = new(method: HttpMethod.Get, requestUri: orcidRecordPath); + // Send request HttpResponseMessage response = await orcidPublicApiHttpClient.SendAsync(request); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(); @@ -57,9 +70,124 @@ public async Task GetRecordFromMemberApi(String orcidId, String orcidAcc HttpRequestMessage request = new(method: HttpMethod.Get, requestUri: orcidRecordPath); // Insert ORCID access token into authorization header for each request. request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", orcidAccessToken); + // Send request HttpResponseMessage response = await orcidMemberApiHttpClient.SendAsync(request); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync(); } + + /* + * Check if ORCID webhook is enabled and related configuration is valid. + * Required configuration: + * - ORCID:WEBHOOK:ENABLED + * - ORCID:WEBHOOK:API + * - ORCID:WEBHOOK:ACCESSTOKEN + */ + public bool IsOrcidWebhookEnabled() + { + return !string.IsNullOrWhiteSpace(Configuration["ORCID:WEBHOOK:ENABLED"]) + && Configuration["ORCID:WEBHOOK:ENABLED"] == "true" + && !string.IsNullOrWhiteSpace(Configuration["ORCID:WEBHOOK:API"]) + && Uri.IsWellFormedUriString( + Configuration["ORCID:WEBHOOK:API"], + UriKind.Absolute + ) + && !string.IsNullOrWhiteSpace(Configuration["ORCID:WEBHOOK:ACCESSTOKEN"]); + } + + /* + * Get ORCID webhook callback URL + */ + public string? GetOrcidWebhookCallbackUri(string orcidId) + { + UriBuilder callbackUri = new(Configuration["SERVICEURL"]); + callbackUri.Scheme = "https"; + callbackUri.Path = $"api/webhook/orcid/{orcidId}"; + return callbackUri.Uri.ToString(); + } + + /* + * URL encode URI + */ + public string GetUrlEncodedString(string uriToEncode) + { + return HttpUtility.UrlEncode(uriToEncode); + } + + /* + * Get ORCID webhook registration URI + */ + public string GetOrcidWebhookRegistrationUri(string orcidId) + { + // Get webhook callback uri + string callBackUri = GetOrcidWebhookCallbackUri(orcidId); + // URL encode callback uri + string urlEncodedCallbackUri = GetUrlEncodedString(callBackUri); + + UriBuilder webhookRegistrationUri = new(Configuration["ORCID:WEBHOOK:API"]); + webhookRegistrationUri.Scheme = "https"; + webhookRegistrationUri.Path = $"{orcidId}/webhook/{urlEncodedCallbackUri}"; + return webhookRegistrationUri.Uri.ToString(); + } + + /* + * Get ORCID webhook access token + */ + public string GetOrcidWebhookAccessToken() + { + return Configuration["ORCID:WEBHOOK:ACCESSTOKEN"]; + } + + /* + * Register ORCID webhook. + * https://info.orcid.org/documentation/api-tutorials/api-tutorial-registering-a-notification-webhook/ + */ + public async Task RegisterOrcidWebhook(string orcidId) + { + // Get ORCID webhook API specific http client. + HttpClient orcidWebhookApiHttpClient = _httpClientFactory.CreateClient("ORCID_WEBHOOK_API"); + // PUT request + HttpRequestMessage request = new(method: HttpMethod.Put, requestUri: GetOrcidWebhookRegistrationUri(orcidId)); + // Insert ORCID webhook access token into authorization header for each request. + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", GetOrcidWebhookAccessToken()); + // Send request + try + { + HttpResponseMessage response = await orcidWebhookApiHttpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + } + catch (HttpRequestException ex) + { + _logger.LogError($"OrcidApiService: {ex.ToString()}"); + return false; + } + return true; + } + + /* + * Unregister ORCID webhook. + * https://info.orcid.org/documentation/api-tutorials/api-tutorial-registering-a-notification-webhook/ + */ + public async Task UnregisterOrcidWebhook(string orcidId) + { + // Get ORCID webhook API specific http client. + HttpClient orcidWebhookApiHttpClient = _httpClientFactory.CreateClient("ORCID_WEBHOOK_API"); + // DELETE request + HttpRequestMessage request = new(method: HttpMethod.Delete, requestUri: GetOrcidWebhookRegistrationUri(orcidId)); + // Insert ORCID webhook access token into authorization header for each request. + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", GetOrcidWebhookAccessToken()); + // Send request + try + { + HttpResponseMessage response = await orcidWebhookApiHttpClient.SendAsync(request); + response.EnsureSuccessStatusCode(); + } + catch (HttpRequestException ex) + { + _logger.LogError($"OrcidApiService: {ex.ToString()}"); + return false; + } + return true; + } } } \ No newline at end of file diff --git a/aspnetcore/src/api/Services/OrcidImportService.cs b/aspnetcore/src/api/Services/OrcidImportService.cs index 9eb72c79..14c901a8 100644 --- a/aspnetcore/src/api/Services/OrcidImportService.cs +++ b/aspnetcore/src/api/Services/OrcidImportService.cs @@ -6,6 +6,7 @@ using api.Models.Orcid; using api.Models.Ttv; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; namespace api.Services { @@ -32,10 +33,100 @@ public OrcidImportService(TtvContext ttvContext, IUserProfileService userProfile _dataSourceHelperService = dataSourceHelperService; } + + /* + * Add DimDates + */ + public async Task AddDimDates(string orcidRecordJson, DateTime currentDateTime) + { + // Education DimDates + List educations = _orcidJsonParserService.GetEducations(orcidRecordJson); + foreach (OrcidEducation education in educations) + { + // Start date + DimDate educationStartDate = await _ttvContext.DimDates.FirstOrDefaultAsync(dd => dd.Year == education.StartDate.Year && dd.Month == education.StartDate.Month && dd.Day == education.StartDate.Day); + if (educationStartDate == null) + { + educationStartDate = new DimDate() + { + Year = education.StartDate.Year, + Month = education.StartDate.Month, + Day = education.StartDate.Day, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + Created = currentDateTime, + Modified = currentDateTime + }; + _ttvContext.DimDates.Add(educationStartDate); + await _ttvContext.SaveChangesAsync(); + } + + // End date + DimDate educationEndDate = await _ttvContext.DimDates.FirstOrDefaultAsync(ed => ed.Year == education.EndDate.Year && ed.Month == education.EndDate.Month && ed.Day == education.EndDate.Day); + if (educationEndDate == null) + { + educationEndDate = new DimDate() + { + Year = education.EndDate.Year, + Month = education.EndDate.Month, + Day = education.EndDate.Day, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + Created = currentDateTime, + Modified = currentDateTime + }; + _ttvContext.DimDates.Add(educationEndDate); + await _ttvContext.SaveChangesAsync(); + } + } + + // Employment DimDates + List employments = _orcidJsonParserService.GetEmployments(orcidRecordJson); + foreach (OrcidEmployment employment in employments) + { + // Start date + DimDate employmentStartDate = await _ttvContext.DimDates.FirstOrDefaultAsync(dd => dd.Year == employment.StartDate.Year && dd.Month == employment.StartDate.Month && dd.Day == employment.StartDate.Day); + if (employmentStartDate == null) + { + employmentStartDate = new DimDate() + { + Year = employment.StartDate.Year, + Month = employment.StartDate.Month, + Day = employment.StartDate.Day, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + Created = currentDateTime, + Modified = currentDateTime + }; + _ttvContext.DimDates.Add(employmentStartDate); + await _ttvContext.SaveChangesAsync(); + } + + // End date + DimDate employmentEndDate = await _ttvContext.DimDates.FirstOrDefaultAsync(dd => dd.Year == employment.EndDate.Year && dd.Month == employment.EndDate.Month && dd.Day == employment.EndDate.Day); + if (employmentEndDate == null) + { + employmentEndDate = new DimDate() + { + Year = employment.EndDate.Year, + Month = employment.EndDate.Month, + Day = employment.EndDate.Day, + SourceId = Constants.SourceIdentifiers.PROFILE_API, + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + Created = currentDateTime, + Modified = currentDateTime + }; + _ttvContext.DimDates.Add(employmentEndDate); + await _ttvContext.SaveChangesAsync(); + } + } + } + + /* * Import ORCID record json into user profile. */ - public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, string json) + public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, string orcidRecordJson) { // Get ORCID registered data source id. int orcidRegisteredDataSourceId = _dataSourceHelperService.DimRegisteredDataSourceId_ORCID; @@ -52,8 +143,8 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, .ThenInclude(ffv => ffv.DimWebLink) .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) .ThenInclude(ffv => ffv.DimFundingDecision) - .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) - .ThenInclude(ffv => ffv.DimPublication) + //.Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + // .ThenInclude(ffv => ffv.DimPublication) .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) .ThenInclude(ffv => ffv.DimOrcidPublication) .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) @@ -62,8 +153,8 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, .ThenInclude(ffv => ffv.DimPidIdOrcidPutCodeNavigation) .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) .ThenInclude(ffv => ffv.DimResearchActivity) - .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) - .ThenInclude(ffv => ffv.DimEvent) + //.Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + // .ThenInclude(ffv => ffv.DimEvent) .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) .ThenInclude(ffv => ffv.DimEducation) .ThenInclude(de => de.DimStartDateNavigation) @@ -79,10 +170,10 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) .ThenInclude(ffv => ffv.DimAffiliation) .ThenInclude(da => da.EndDateNavigation) - .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) - .ThenInclude(ffv => ffv.DimCompetence) - .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) - .ThenInclude(ffv => ffv.DimResearchCommunity) + //.Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + // .ThenInclude(ffv => ffv.DimCompetence) + //.Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) + // .ThenInclude(ffv => ffv.DimResearchCommunity) .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) .ThenInclude(ffv => ffv.DimTelephoneNumber) .Include(dup => dup.FactFieldValues.Where(ffv => ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId)) @@ -98,18 +189,21 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, // Get current DateTime DateTime currentDateTime = _utilityService.GetCurrentDateTime(); - // Must use "Constants.SourceIdentifiers.ORCID" as value for "FactFieldValue.SourceId". It is used to identify what data can be deleted when userprofile is deleted. + // Add DimDates. + await AddDimDates(orcidRecordJson, currentDateTime); // Name - DimFieldDisplaySetting dimFieldDisplaySettingsName = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dimFieldDisplaysettingsName => dimFieldDisplaysettingsName.FieldIdentifier == Constants.FieldIdentifiers.PERSON_NAME); + DimFieldDisplaySetting dimFieldDisplaySettingsName = + dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dimFieldDisplaysettingsName => dimFieldDisplaysettingsName.FieldIdentifier == Constants.FieldIdentifiers.PERSON_NAME); // FactFieldValues - FactFieldValue factFieldValuesName = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimFieldDisplaySettings.Id == dimFieldDisplaySettingsName.Id && ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId); + FactFieldValue factFieldValuesName = + dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimFieldDisplaySettings.Id == dimFieldDisplaySettingsName.Id && ffv.DimRegisteredDataSourceId == orcidRegisteredDataSourceId); if (factFieldValuesName != null) { // Update existing DimName DimName dimName = factFieldValuesName.DimName; - dimName.LastName = _orcidJsonParserService.GetFamilyName(json).Value; - dimName.FirstNames = _orcidJsonParserService.GetGivenNames(json).Value; + dimName.LastName = _orcidJsonParserService.GetFamilyName(orcidRecordJson).Value; + dimName.FirstNames = _orcidJsonParserService.GetGivenNames(orcidRecordJson).Value; dimName.Modified = _utilityService.GetCurrentDateTime(); // Update existing FactFieldValue factFieldValuesName.Show = true; // ORCID name is selected by default. @@ -120,8 +214,8 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, // Create new DimName DimName dimName = new() { - LastName = _orcidJsonParserService.GetFamilyName(json).Value, - FirstNames = _orcidJsonParserService.GetGivenNames(json).Value, + LastName = _orcidJsonParserService.GetFamilyName(orcidRecordJson).Value, + FirstNames = _orcidJsonParserService.GetGivenNames(orcidRecordJson).Value, DimKnownPersonIdConfirmedIdentity = dimUserProfile.DimKnownPersonId, DimRegisteredDataSourceId = orcidRegisteredDataSourceId, SourceId = Constants.SourceIdentifiers.PROFILE_API, @@ -142,11 +236,12 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, // Other names - List otherNames = _orcidJsonParserService.GetOtherNames(json); + List otherNames = _orcidJsonParserService.GetOtherNames(orcidRecordJson); foreach (OrcidOtherName otherName in otherNames) { // Check if FactFieldValues contains entry, which points to ORCID put code value in DimPid - FactFieldValue factFieldValuesOtherName = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == otherName.PutCode.Value.ToString()); + FactFieldValue factFieldValuesOtherName = + dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == otherName.PutCode.Value.ToString()); if (factFieldValuesOtherName != null) { @@ -181,7 +276,8 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, _ttvContext.DimPids.Add(dimPidOrcidPutCodeOtherName); // Get DimFieldDisplaySettings for other name - DimFieldDisplaySetting dimFieldDisplaySettingsOtherName = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsWebLink => dfdsWebLink.FieldIdentifier == Constants.FieldIdentifiers.PERSON_OTHER_NAMES); + DimFieldDisplaySetting dimFieldDisplaySettingsOtherName = + dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsWebLink => dfdsWebLink.FieldIdentifier == Constants.FieldIdentifiers.PERSON_OTHER_NAMES); // Create FactFieldValues for other name factFieldValuesOtherName = _userProfileService.GetEmptyFactFieldValue(); @@ -196,11 +292,12 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, // Researcher urls - List researcherUrls = _orcidJsonParserService.GetResearcherUrls(json); + List researcherUrls = _orcidJsonParserService.GetResearcherUrls(orcidRecordJson); foreach (OrcidResearcherUrl researchUrl in researcherUrls) { // Check if FactFieldValues contains entry, which points to ORCID put code value in DimPid - FactFieldValue factFieldValuesWebLink = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == researchUrl.PutCode.Value.ToString()); + FactFieldValue factFieldValuesWebLink = + dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == researchUrl.PutCode.Value.ToString()); if (factFieldValuesWebLink != null) { @@ -240,7 +337,8 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, _ttvContext.DimPids.Add(dimPidOrcidPutCodeWebLink); // Get DimFieldDisplaySettings for weblink - DimFieldDisplaySetting dimFieldDisplaySettingsWebLink = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsWebLink => dfdsWebLink.FieldIdentifier == Constants.FieldIdentifiers.PERSON_WEB_LINK); + DimFieldDisplaySetting dimFieldDisplaySettingsWebLink = + dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsWebLink => dfdsWebLink.FieldIdentifier == Constants.FieldIdentifiers.PERSON_WEB_LINK); // Create FactFieldValues for weblink factFieldValuesWebLink = _userProfileService.GetEmptyFactFieldValue(); @@ -255,19 +353,22 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, // Researcher description - OrcidBiography biography = _orcidJsonParserService.GetBiography(json); + OrcidBiography biography = _orcidJsonParserService.GetBiography(orcidRecordJson); if (biography != null) { DimResearcherDescription dimResearcherDescription = await _userProfileService.AddOrUpdateDimResearcherDescription( "", - _orcidJsonParserService.GetBiography(json).Value, + _orcidJsonParserService.GetBiography(orcidRecordJson).Value, "", dimUserProfile.DimKnownPersonId, orcidRegisteredDataSourceId ); // Researcher description: DimFieldDisplaySettings - DimFieldDisplaySetting dimFieldDisplaySettingsResearcherDescription = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dimFieldDisplaySettingsResearcherDescription => dimFieldDisplaySettingsResearcherDescription.FieldIdentifier == Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION); + DimFieldDisplaySetting dimFieldDisplaySettingsResearcherDescription = + dimUserProfile.DimFieldDisplaySettings.FirstOrDefault( + dimFieldDisplaySettingsResearcherDescription => dimFieldDisplaySettingsResearcherDescription.FieldIdentifier == Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION + ); // Researcher description: FactFieldValues FactFieldValue factFieldValuesResearcherDescription = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimResearcherDescriptionId == dimResearcherDescription.Id); @@ -288,7 +389,7 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, // Email - List emails = _orcidJsonParserService.GetEmails(json); + List emails = _orcidJsonParserService.GetEmails(orcidRecordJson); foreach (OrcidEmail email in emails) { // Email: DimEmailAddrressess @@ -299,7 +400,10 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, ); // Email: DimFieldDisplaySettings - DimFieldDisplaySetting dimFieldDisplaySettingsEmailAddress = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dimFieldDisplaySettingsEmailAddress => dimFieldDisplaySettingsEmailAddress.FieldIdentifier == Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS); + DimFieldDisplaySetting dimFieldDisplaySettingsEmailAddress = + dimUserProfile.DimFieldDisplaySettings.FirstOrDefault( + dimFieldDisplaySettingsEmailAddress => dimFieldDisplaySettingsEmailAddress.FieldIdentifier == Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS + ); // Email: FactFieldValues FactFieldValue factFieldValuesEmailAddress = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimEmailAddrressId == dimEmailAddress.Id); @@ -320,15 +424,17 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, // Keyword - List keywords = _orcidJsonParserService.GetKeywords(json); + List keywords = _orcidJsonParserService.GetKeywords(orcidRecordJson); // Get DimFieldDisplaySettings for keyword - DimFieldDisplaySetting dimFieldDisplaySettingsKeyword = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsKeyword => dfdsKeyword.FieldIdentifier == Constants.FieldIdentifiers.PERSON_KEYWORD); + DimFieldDisplaySetting dimFieldDisplaySettingsKeyword = + dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsKeyword => dfdsKeyword.FieldIdentifier == Constants.FieldIdentifiers.PERSON_KEYWORD); // Collect list of processed FactFieldValues related to keyword. Needed when deleting keywords. List processedKeywordFactFieldValues = new(); foreach (OrcidKeyword keyword in keywords) { // Check if FactFieldValues contains entry, which points to ORCID put code value in DimKeyword - FactFieldValue factFieldValuesKeyword = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == keyword.PutCode.Value.ToString()); + FactFieldValue factFieldValuesKeyword = + dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == keyword.PutCode.Value.ToString()); if (factFieldValuesKeyword != null) { @@ -384,13 +490,15 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, // External identifier (=DimPid) - List externalIdentifiers = _orcidJsonParserService.GetExternalIdentifiers(json); + List externalIdentifiers = _orcidJsonParserService.GetExternalIdentifiers(orcidRecordJson); // Get DimFieldDisplaySettings for keyword - DimFieldDisplaySetting dimFieldDisplaySettingsExternalIdentifier = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsKeyword => dfdsKeyword.FieldIdentifier == Constants.FieldIdentifiers.PERSON_EXTERNAL_IDENTIFIER); + DimFieldDisplaySetting dimFieldDisplaySettingsExternalIdentifier = + dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsKeyword => dfdsKeyword.FieldIdentifier == Constants.FieldIdentifiers.PERSON_EXTERNAL_IDENTIFIER); foreach (OrcidExternalIdentifier externalIdentifier in externalIdentifiers) { // Check if FactFieldValues contains entry, which points to ORCID put code value in DimPid - FactFieldValue factFieldValuesExternalIdentifier = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == externalIdentifier.PutCode.Value.ToString()); + FactFieldValue factFieldValuesExternalIdentifier = + dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == externalIdentifier.PutCode.Value.ToString()); if (factFieldValuesExternalIdentifier != null) { @@ -433,45 +541,20 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, // Education - List educations = _orcidJsonParserService.GetEducations(json); + List educations = _orcidJsonParserService.GetEducations(orcidRecordJson); foreach (OrcidEducation education in educations) { // Check if FactFieldValues contains entry, which points to ORCID put code value in DimEducation - FactFieldValue factFieldValuesEducation = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == education.PutCode.Value.ToString()); + FactFieldValue factFieldValuesEducation = + dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == education.PutCode.Value.ToString()); // Start date - DimDate startDate = await _ttvContext.DimDates.FirstOrDefaultAsync(dd => dd.Year == education.StartDate.Year && dd.Month == education.StartDate.Month && dd.Day == education.StartDate.Day); - if (startDate == null) - { - startDate = new DimDate() - { - Year = education.StartDate.Year, - Month = education.StartDate.Month, - Day = education.StartDate.Day, - SourceId = Constants.SourceIdentifiers.PROFILE_API, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = currentDateTime, - Modified = currentDateTime - }; - _ttvContext.DimDates.Add(startDate); - } + DimDate educationStartDate = await _ttvContext.DimDates + .FirstOrDefaultAsync(dd => dd.Year == education.StartDate.Year && dd.Month == education.StartDate.Month && dd.Day == education.StartDate.Day); // End date - DimDate endDate = await _ttvContext.DimDates.FirstOrDefaultAsync(ed => ed.Year == education.EndDate.Year && ed.Month == education.EndDate.Month && ed.Day == education.EndDate.Day); - if (endDate == null) - { - endDate = new DimDate() - { - Year = education.EndDate.Year, - Month = education.EndDate.Month, - Day = education.EndDate.Day, - SourceId = Constants.SourceIdentifiers.PROFILE_API, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = currentDateTime, - Modified = currentDateTime - }; - _ttvContext.DimDates.Add(endDate); - } + DimDate educationEndDate = await _ttvContext.DimDates + .FirstOrDefaultAsync(ed => ed.Year == education.EndDate.Year && ed.Month == education.EndDate.Month && ed.Day == education.EndDate.Day); if (factFieldValuesEducation != null) { @@ -479,8 +562,8 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, DimEducation dimEducation = factFieldValuesEducation.DimEducation; dimEducation.NameEn = education.RoleTitle; dimEducation.DegreeGrantingInstitutionName = education.OrganizationName; - dimEducation.DimStartDateNavigation = startDate; - dimEducation.DimEndDateNavigation = endDate; + dimEducation.DimStartDateNavigation = educationStartDate; + dimEducation.DimEndDateNavigation = educationEndDate; dimEducation.Modified = currentDateTime; // Update existing FactFieldValue @@ -493,8 +576,8 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, { NameEn = education.RoleTitle, DegreeGrantingInstitutionName = education.OrganizationName, - DimStartDateNavigation = startDate, - DimEndDateNavigation = endDate, + DimStartDateNavigation = educationStartDate, + DimEndDateNavigation = educationEndDate, SourceId = Constants.SourceIdentifiers.PROFILE_API, SourceDescription = Constants.SourceDescriptions.PROFILE_API, DimKnownPersonId = dimUserProfile.DimKnownPersonId, @@ -513,7 +596,8 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, _ttvContext.DimPids.Add(dimPidOrcidPutCodeEducation); // Get DimFieldDisplaySettings for education - DimFieldDisplaySetting dimFieldDisplaySettingsEducation = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsEducation => dfdsEducation.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_EDUCATION); + DimFieldDisplaySetting dimFieldDisplaySettingsEducation = + dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsEducation => dfdsEducation.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_EDUCATION); // Create FactFieldValues for education factFieldValuesEducation = _userProfileService.GetEmptyFactFieldValue(); @@ -529,7 +613,7 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, // Employment (Affiliation in Ttv database) - List employments = _orcidJsonParserService.GetEmployments(json); + List employments = _orcidJsonParserService.GetEmployments(orcidRecordJson); foreach (OrcidEmployment employment in employments) { /* @@ -543,7 +627,8 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, */ // Check if FactFieldValues contains entry, which points to ORCID put code value in DimAffiliation - FactFieldValue factFieldValuesAffiliation = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == employment.PutCode.Value.ToString()); + FactFieldValue factFieldValuesAffiliation = + dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == employment.PutCode.Value.ToString()); // Search organization identifier from DimPid based on ORCID's disambiguated-organization-identifier data. int? dimOrganization_id_affiliation = await _organizationHandlerService.FindOrganizationIdByOrcidDisambiguationIdentifier( @@ -552,38 +637,12 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, ); // Start date - DimDate startDate = await _ttvContext.DimDates.FirstOrDefaultAsync(dd => dd.Year == employment.StartDate.Year && dd.Month == employment.StartDate.Month && dd.Day == employment.StartDate.Day); - if (startDate == null) - { - startDate = new DimDate() - { - Year = employment.StartDate.Year, - Month = employment.StartDate.Month, - Day = employment.StartDate.Day, - SourceId = Constants.SourceIdentifiers.PROFILE_API, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = currentDateTime, - Modified = currentDateTime - }; - _ttvContext.DimDates.Add(startDate); - } + DimDate employmentStartDate = await _ttvContext.DimDates + .FirstOrDefaultAsync(dd => dd.Year == employment.StartDate.Year && dd.Month == employment.StartDate.Month && dd.Day == employment.StartDate.Day); // End date - DimDate endDate = await _ttvContext.DimDates.FirstOrDefaultAsync(dd => dd.Year == employment.EndDate.Year && dd.Month == employment.EndDate.Month && dd.Day == employment.EndDate.Day); - if (endDate == null) - { - endDate = new DimDate() - { - Year = employment.EndDate.Year, - Month = employment.EndDate.Month, - Day = employment.EndDate.Day, - SourceId = Constants.SourceIdentifiers.PROFILE_API, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = currentDateTime, - Modified = currentDateTime - }; - _ttvContext.DimDates.Add(endDate); - } + DimDate employmentEndDate = await _ttvContext.DimDates + .FirstOrDefaultAsync(dd => dd.Year == employment.EndDate.Year && dd.Month == employment.EndDate.Month && dd.Day == employment.EndDate.Day); /* * Check if affiliation already exists in profile. @@ -595,8 +654,8 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, */ DimAffiliation dimAffiliation_existing = factFieldValuesAffiliation.DimAffiliation; dimAffiliation_existing.PositionNameEn = employment.RoleTitle; - dimAffiliation_existing.StartDateNavigation = startDate; - dimAffiliation_existing.EndDateNavigation = endDate; + dimAffiliation_existing.StartDateNavigation = employmentStartDate; + dimAffiliation_existing.EndDateNavigation = employmentEndDate; dimAffiliation_existing.Modified = currentDateTime; /* @@ -635,7 +694,8 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, /* * Create new DimIdentifierlessData for organization name. */ - DimIdentifierlessDatum dimIdentifierlessDatum_organization_name = _organizationHandlerService.CreateIdentifierlessData_OrganizationName(nameFi: "", nameEn: employment.OrganizationName, nameSv: ""); + DimIdentifierlessDatum dimIdentifierlessDatum_organization_name = + _organizationHandlerService.CreateIdentifierlessData_OrganizationName(nameFi: "", nameEn: employment.OrganizationName, nameSv: ""); _ttvContext.DimIdentifierlessData.Add(dimIdentifierlessDatum_organization_name); factFieldValuesAffiliation.DimIdentifierlessData = dimIdentifierlessDatum_organization_name; } @@ -653,8 +713,8 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, DimAffiliation dimAffiliation_new = new() { DimOrganizationId = -1, - StartDateNavigation = startDate, - EndDateNavigation = endDate, + StartDateNavigation = employmentStartDate, + EndDateNavigation = employmentEndDate, PositionNameEn = employment.RoleTitle, AffiliationType = -1, SourceId = Constants.SourceIdentifiers.PROFILE_API, @@ -681,7 +741,8 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, _ttvContext.DimPids.Add(dimPidOrcidPutCodeEmployment); // Get DimFieldDisplaySettings for affiliation - DimFieldDisplaySetting dimFieldDisplaySettingsAffiliation = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsAffiliation => dfdsAffiliation.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_AFFILIATION); + DimFieldDisplaySetting dimFieldDisplaySettingsAffiliation = + dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsAffiliation => dfdsAffiliation.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_AFFILIATION); // Create FactFieldValues for affiliation factFieldValuesAffiliation = _userProfileService.GetEmptyFactFieldValue(); @@ -694,7 +755,8 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, // If organization was not found, add organization_name into DimIdentifierlessData if (dimOrganization_id_affiliation == null || dimOrganization_id_affiliation == -1) { - DimIdentifierlessDatum dimIdentifierlessData_oganizationName = _organizationHandlerService.CreateIdentifierlessData_OrganizationName(nameFi: "", nameEn: employment.OrganizationName, nameSv: ""); + DimIdentifierlessDatum dimIdentifierlessData_oganizationName = + _organizationHandlerService.CreateIdentifierlessData_OrganizationName(nameFi: "", nameEn: employment.OrganizationName, nameSv: ""); _ttvContext.DimIdentifierlessData.Add(dimIdentifierlessData_oganizationName); factFieldValuesAffiliation.DimIdentifierlessData = dimIdentifierlessData_oganizationName; } @@ -773,11 +835,12 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, // Publication - List orcidPublications = _orcidJsonParserService.GetPublications(json); + List orcidPublications = _orcidJsonParserService.GetPublications(orcidRecordJson); foreach (OrcidPublication orcidPublication in orcidPublications) { // Check if FactFieldValues contains entry, which points to ORCID put code value in DimOrcidPublication - FactFieldValue factFieldValuesPublication = dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == orcidPublication.PutCode.Value.ToString()); + FactFieldValue factFieldValuesPublication = + dimUserProfile.FactFieldValues.FirstOrDefault(ffv => ffv.DimPidIdOrcidPutCode > 0 && ffv.DimPidIdOrcidPutCodeNavigation.PidContent == orcidPublication.PutCode.Value.ToString()); if (factFieldValuesPublication != null) { @@ -813,7 +876,8 @@ public async Task ImportOrcidRecordJsonIntoUserProfile(int userprofileId, _ttvContext.DimPids.Add(dimPidOrcidPutCodePublication); // Get DimFieldDisplaySettings for orcid publication - DimFieldDisplaySetting dimFieldDisplaySettingsOrcidPublication = dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsPublication => dfdsPublication.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_PUBLICATION_ORCID); + DimFieldDisplaySetting dimFieldDisplaySettingsOrcidPublication = + dimUserProfile.DimFieldDisplaySettings.FirstOrDefault(dfdsPublication => dfdsPublication.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_PUBLICATION_ORCID); // Create FactFieldValues for orcid publication factFieldValuesPublication = _userProfileService.GetEmptyFactFieldValue(); diff --git a/aspnetcore/src/api/Services/SharingService.cs b/aspnetcore/src/api/Services/SharingService.cs index 2987af9f..4a95544d 100644 --- a/aspnetcore/src/api/Services/SharingService.cs +++ b/aspnetcore/src/api/Services/SharingService.cs @@ -66,6 +66,8 @@ public async Task> GetDefaultSharingPermissionsListFor if (dimReferencedata != null) { + // TODO: Uncomment when sharing permissions feature is enabled. + /* defaultSharingPermissions.Add( new BrGrantedPermission() { @@ -74,6 +76,7 @@ public async Task> GetDefaultSharingPermissionsListFor DimPermittedFieldGroup = dimReferencedata.Id } ); + */ } } return defaultSharingPermissions; diff --git a/aspnetcore/src/api/Services/StartupHelperService.cs b/aspnetcore/src/api/Services/StartupHelperService.cs index 82451bc4..48412caa 100644 --- a/aspnetcore/src/api/Services/StartupHelperService.cs +++ b/aspnetcore/src/api/Services/StartupHelperService.cs @@ -13,11 +13,13 @@ public class StartupHelperService : IStartupHelperService { private readonly TtvContext _ttvContext; private readonly ILogger _logger; + private readonly IUtilityService _utilityService; - public StartupHelperService(TtvContext ttvContext, ILogger logger) + public StartupHelperService(TtvContext ttvContext, ILogger logger, IUtilityService utilityService) { _ttvContext = ttvContext; _logger = logger; + _utilityService = utilityService; } /* @@ -28,15 +30,17 @@ public StartupHelperService(TtvContext ttvContext, ILogger public DimRegisteredDataSource GetDimRegisteredDataSourceId_OnStartup_ORCID() { _logger.LogInformation("Get data source for ORCID in DimRegisteredDataSource"); + // Get data source organization name. + string organizationName_ORCID = _utilityService.GetDatasourceOrganizationName_ORCID(); // Get data source and related organization. DimRegisteredDataSource orcidRegisteredDataSource = _ttvContext.DimRegisteredDataSources .Include(drds => drds.DimOrganization) - .Where(drds => drds.DimOrganization.NameFi == Constants.OrganizationNames.ORCID).AsNoTracking().FirstOrDefault(); + .Where(drds => drds.DimOrganization.NameFi == organizationName_ORCID).AsNoTracking().FirstOrDefault(); // Log error and raise exception on missing ORCID registered data source. The application does not function without this. if (orcidRegisteredDataSource == null) { - string errorMessage = "Registered data source was not found from dim_registered_data_source for organization: " + Constants.OrganizationNames.ORCID; + string errorMessage = "Registered data source was not found from dim_registered_data_source for organization: " + organizationName_ORCID; _logger.LogError(errorMessage); throw new System.Exception(errorMessage); } @@ -52,15 +56,17 @@ public DimRegisteredDataSource GetDimRegisteredDataSourceId_OnStartup_ORCID() public DimRegisteredDataSource GetDimRegisteredDataSourceId_OnStartup_TTV() { _logger.LogInformation("Get data source for TTV in DimRegisteredDataSource"); + // Get data source organization name. + string organizationName_TTV = _utilityService.GetDatasourceOrganizationName_TTV(); // Get data source and related organization. DimRegisteredDataSource ttvRegisteredDataSource = _ttvContext.DimRegisteredDataSources .Include(drds => drds.DimOrganization) - .Where(drds => drds.DimOrganization.NameFi == Constants.OrganizationNames.TTV).AsNoTracking().FirstOrDefault(); + .Where(drds => drds.DimOrganization.NameFi == organizationName_TTV).AsNoTracking().FirstOrDefault(); // Log error and raise exception on missing TTV registered data source. The application does not function without this. if (ttvRegisteredDataSource == null) { - string errorMessage = "Registered data source was not found from dim_registered_data_source for organization: " + Constants.OrganizationNames.TTV; + string errorMessage = "Registered data source was not found from dim_registered_data_source for organization: " + organizationName_TTV; _logger.LogError(errorMessage); throw new System.Exception(errorMessage); } @@ -75,14 +81,17 @@ public DimRegisteredDataSource GetDimRegisteredDataSourceId_OnStartup_TTV() */ public DimPurpose GetDimPurposeId_OnStartup_TTV() { + // Get organization ID. + string organizationId_OKM = _utilityService.GetOrganizationId_OKM(); + DimPurpose dimPurpose = _ttvContext.DimPurposes .Include(dp => dp.DimOrganization) - .Where(dp => dp.DimOrganization.OrganizationId == Constants.OrganizationIds.OKM).AsNoTracking().FirstOrDefault(); + .Where(dp => dp.DimOrganization.OrganizationId == organizationId_OKM).AsNoTracking().FirstOrDefault(); // Log error and raise exception on missing TTV purpose. The application does not function without this. if (dimPurpose == null) { - string errorMessage = "Purpose was not found from dim_purpose for organization_id: " + Constants.OrganizationIds.OKM; + string errorMessage = "Purpose was not found from dim_purpose for organization_id: " + organizationId_OKM; _logger.LogError(errorMessage); throw new System.Exception(errorMessage); } diff --git a/aspnetcore/src/api/Services/TtvSqlService.cs b/aspnetcore/src/api/Services/TtvSqlService.cs index a106f954..bea074b0 100644 --- a/aspnetcore/src/api/Services/TtvSqlService.cs +++ b/aspnetcore/src/api/Services/TtvSqlService.cs @@ -2,6 +2,7 @@ using api.Models.Common; using api.Models.Ttv; using System.Collections.Generic; +using Nest; namespace api.Services { @@ -59,6 +60,9 @@ public string GetFactFieldValuesFKColumnNameFromFieldIdentifier(int fieldIdentif case Constants.FieldIdentifiers.ACTIVITY_RESEARCH_DATASET: fk_column_name = "dim_research_dataset_id"; break; + case Constants.FieldIdentifiers.ACTIVITY_RESEARCH_ACTIVITY: + fk_column_name = "dim_research_activity_id"; + break; default: break; } @@ -114,7 +118,7 @@ public string GetSqlQuery_ProfileData(int userprofileId, bool forElasticsearch = ffv.dim_researcher_description_id AS 'FactFieldValues_DimResearcherDescriptionId', ffv.dim_email_addrress_id AS 'FactFieldValues_DimEmailAddrressId', ffv.dim_telephone_number_id AS 'FactFieldValues_DimTelephoneNumberId', - ffv.dim_field_of_science_id AS ' FactFieldValues_DimFieldOfScienceId', + ffv.dim_referencedata_field_of_science_id AS ' FactFieldValues_DimReferencedataFieldOfScienceId', ffv.dim_keyword_id AS 'FactFieldValues_DimKeywordId', ffv.dim_pid_id AS 'FactFieldValues_DimPidId', ffv.dim_affiliation_id AS 'FactFieldValues_DimAffiliationId', @@ -135,16 +139,28 @@ public string GetSqlQuery_ProfileData(int userprofileId, bool forElasticsearch = dim_researcher_description.research_description_sv AS 'DimResearcherDescription_ResearchDescriptionSv', dim_email_addrress.email AS 'DimEmailAddrress_Email', dim_telephone_number.telephone_number AS 'DimTelephoneNumber_TelephoneNumber', - dim_field_of_science.name_fi AS 'DimFieldOfScience_NameFi', - dim_field_of_science.name_en AS 'DimFieldOfScience_NameEn', - dim_field_of_science.name_sv AS 'DimFieldOfScience_NameSv', dim_keyword.keyword AS 'DimKeyword_Keyword', dim_pid.pid_type AS 'DimPid_PidType', dim_pid.pid_content AS 'DimPid_PidContent', + affiliation_organization.id AS 'DimAffiliation_DimOrganization_Id', + affiliation_organization.organization_id AS 'DimAffiliation_DimOrganization_OrganizationId', affiliation_organization.name_fi AS 'DimAffiliation_DimOrganization_NameFi', affiliation_organization.name_en AS 'DimAffiliation_DimOrganization_NameEn', affiliation_organization.name_sv AS 'DimAffiliation_DimOrganization_NameSv', + affiliation_organization_sector.sector_id AS 'DimAffiliation_DimOrganization_DimSector_SectorId', + affiliation_organization_sector.name_fi AS 'DimAffiliation_DimOrganization_DimSector_NameFi', + affiliation_organization_sector.name_en AS 'DimAffiliation_DimOrganization_DimSector_NameEn', + affiliation_organization_sector.name_sv AS 'DimAffiliation_DimOrganization_DimSector_NameSv', + affiliation_organization_broader.id AS 'DimAffiliation_DimOrganizationBroader_Id', + affiliation_organization_broader.organization_id AS 'DimAffiliation_DimOrganizationBroader_OrganizationId', + affiliation_organization_broader.name_fi AS 'DimAffiliation_DimOrganizationBroader_NameFi', + affiliation_organization_broader.name_en AS 'DimAffiliation_DimOrganizationBroader_NameEn', + affiliation_organization_broader.name_sv AS 'DimAffiliation_DimOrganizationBroader_NameSv', + affiliation_organization_broader_sector.sector_id AS 'DimAffiliation_DimOrganizationBroader_DimSector_SectorId', + affiliation_organization_broader_sector.name_fi AS 'DimAffiliation_DimOrganizationBroader_DimSector_NameFi', + affiliation_organization_broader_sector.name_en AS 'DimAffiliation_DimOrganizationBroader_DimSector_NameEn', + affiliation_organization_broader_sector.name_sv AS 'DimAffiliation_DimOrganizationBroader_DimSector_NameSv', dim_affiliation.position_name_fi AS 'DimAffiliation_PositionNameFi', dim_affiliation.position_name_en AS 'DimAffiliation_PositionNameEn', dim_affiliation.position_name_sv AS 'DimAffiliation_PositionNameSv', @@ -165,6 +181,7 @@ public string GetSqlQuery_ProfileData(int userprofileId, bool forElasticsearch = did_child.value_en AS 'DimIdentifierlessData_Child_ValueEn', did_child.value_sv AS 'DimIdentifierlessData_Child_ValueSv', did_child.unlinked_identifier AS 'DimIdentifierlessData_Child_UnlinkedIdentifier', + dim_education.name_fi AS 'DimEducation_NameFi', dim_education.name_en AS 'DimEducation_NameEn', dim_education.name_sv AS 'DimEducation_NameSv', @@ -179,7 +196,11 @@ public string GetSqlQuery_ProfileData(int userprofileId, bool forElasticsearch = dim_publication.publication_name AS 'DimPublication_PublicationName', dim_publication.publication_year AS 'DimPublication_PublicationYear', dim_publication.doi AS 'DimPublication_Doi', + dim_publication.authors_text AS 'DimPublication_AuthorsText', dim_publication.publication_type_code AS 'DimPublication_PublicationTypeCode', + dim_publication.journal_name AS 'DimPublication_JournalName', + dim_publication.conference_name AS 'DimPublication_ConferenceName', + dim_publication.parent_publication_name AS 'DimPublication_ParentPublicationName', dim_orcid_publication.publication_id AS 'DimOrcidPublication_PublicationId', dim_orcid_publication.publication_name AS 'DimOrcidPublication_PublicationName', dim_orcid_publication.publication_year AS 'DimOrcidPublication_PublicationYear', @@ -197,6 +218,14 @@ public string GetSqlQuery_ProfileData(int userprofileId, bool forElasticsearch = research_activity_end_date.year AS 'DimResearchActivity_EndDate_Year', research_activity_end_date.month AS 'DimResearchActivity_EndDate_Month', research_activity_end_date.day AS 'DimResearchActivity_EndDate_Day', + research_activity_fact_contribution_activity_type_dim_referencedata.code_value AS 'DimResearchActivity_ActivityType_CodeValue', + research_activity_fact_contribution_activity_type_dim_referencedata.name_fi AS 'DimResearchActivity_ActivityType_NameFi', + research_activity_fact_contribution_activity_type_dim_referencedata.name_en AS 'DimResearchActivity_ActivityType_NameEn', + research_activity_fact_contribution_activity_type_dim_referencedata.name_sv AS 'DimResearchActivity_ActivityType_NameSv', + research_activity_fact_contribution_researcher_name_activity_dim_referencedata.code_value AS 'DimResearchActivity_Role_CodeValue', + research_activity_fact_contribution_researcher_name_activity_dim_referencedata.name_fi AS 'DimResearchActivity_Role_NameFi', + research_activity_fact_contribution_researcher_name_activity_dim_referencedata.name_en AS 'DimResearchActivity_Role_NameEn', + research_activity_fact_contribution_researcher_name_activity_dim_referencedata.name_sv AS 'DimResearchActivity_Role_NameSv', dfd.acronym AS 'DimFundingDecision_Acronym', dfd.funder_project_number AS 'DimFundingDecision_FunderProjectNumber', dfd.name_fi AS 'DimFundingDecision_NameFi', @@ -237,16 +266,20 @@ FROM fact_field_values AS ffv JOIN dim_researcher_description ON ffv.dim_researcher_description_id=dim_researcher_description.id JOIN dim_email_addrress ON ffv.dim_email_addrress_id=dim_email_addrress.id JOIN dim_telephone_number ON ffv.dim_telephone_number_id=dim_telephone_number.id - JOIN dim_field_of_science ON ffv.dim_field_of_science_id=dim_field_of_science.id JOIN dim_keyword ON ffv.dim_keyword_id=dim_keyword.id JOIN dim_pid ON ffv.dim_pid_id=dim_pid.id + JOIN dim_affiliation ON ffv.dim_affiliation_id=dim_affiliation.id JOIN dim_organization AS affiliation_organization ON dim_affiliation.dim_organization_id=affiliation_organization.id + LEFT JOIN dim_organization AS affiliation_organization_broader ON affiliation_organization_broader.id=affiliation_organization.dim_organization_broader AND affiliation_organization.dim_organization_broader!=-1 + JOIN dim_sector AS affiliation_organization_sector ON affiliation_organization.dim_sectorid=affiliation_organization_sector.id + LEFT JOIN dim_sector AS affiliation_organization_broader_sector ON affiliation_organization_broader.dim_sectorid=affiliation_organization_broader_sector.id LEFT JOIN dim_date AS affiliation_start_date ON dim_affiliation.start_date=affiliation_start_date.id AND affiliation_start_date.id!=-1 LEFT JOIN dim_date AS affiliation_end_date ON dim_affiliation.end_date=affiliation_end_date.id AND affiliation_end_date.id!=-1 JOIN dim_referencedata AS affiliation_type ON dim_affiliation.affiliation_type=affiliation_type.id JOIN dim_identifierless_data AS did ON ffv.dim_identifierless_data_id=did.id LEFT JOIN dim_identifierless_data AS did_child ON did_child.dim_identifierless_data_id=did.id AND did_child.dim_identifierless_data_id!=-1 + JOIN dim_education ON ffv.dim_education_id=dim_education.id LEFT JOIN dim_date AS education_start_date ON dim_education.dim_start_date=education_start_date.id AND education_start_date.id!=-1 LEFT JOIN dim_date AS education_end_date ON dim_education.dim_end_date=education_end_date.id AND education_end_date.id!=-1 @@ -255,14 +288,28 @@ FROM fact_field_values AS ffv JOIN dim_research_activity ON ffv.dim_research_activity_id=dim_research_activity.id LEFT JOIN dim_date AS research_activity_start_date ON dim_research_activity.dim_start_date=research_activity_start_date.id AND research_activity_start_date.id!=-1 LEFT JOIN dim_date AS research_activity_end_date ON dim_research_activity.dim_end_date=research_activity_end_date.id AND research_activity_end_date.id!=-1 - JOIN dim_funding_decision AS dfd ON ffv.dim_funding_decision_id=dfd.id + + LEFT JOIN fact_contribution AS research_activity_fact_contribution_activity_type ON dim_research_activity.id=research_activity_fact_contribution_activity_type.dim_research_activity_id AND + dim_research_activity.id!=-1 AND + research_activity_fact_contribution_activity_type.contribution_type='activity_type' + LEFT JOIN dim_referencedata AS research_activity_fact_contribution_activity_type_dim_referencedata ON + research_activity_fact_contribution_activity_type.dim_referencedata_actor_role_id=research_activity_fact_contribution_activity_type_dim_referencedata.id AND + research_activity_fact_contribution_activity_type_dim_referencedata.id!=-1 + + LEFT JOIN fact_contribution AS research_activity_fact_contribution_researcher_name_activity ON dim_research_activity.id=research_activity_fact_contribution_researcher_name_activity.dim_research_activity_id AND + dim_research_activity.id!=-1 AND + research_activity_fact_contribution_researcher_name_activity.contribution_type='researcher_name_activity' + LEFT JOIN dim_referencedata AS research_activity_fact_contribution_researcher_name_activity_dim_referencedata ON + research_activity_fact_contribution_researcher_name_activity.dim_referencedata_actor_role_id=research_activity_fact_contribution_researcher_name_activity_dim_referencedata.id AND + research_activity_fact_contribution_researcher_name_activity_dim_referencedata.id!=-1 + + JOIN dim_funding_decision AS dfd ON ffv.dim_funding_decision_id=dfd.id LEFT JOIN dim_date AS funding_decision_start_date ON dfd.dim_date_id_start=funding_decision_start_date.id AND funding_decision_start_date.id!=-1 LEFT JOIN dim_date AS funding_decision_end_date ON dfd.dim_date_id_end=funding_decision_end_date.id AND funding_decision_end_date.id!=-1 LEFT JOIN dim_call_programme ON dim_call_programme.id=dfd.dim_call_programme_id LEFT JOIN dim_type_of_funding ON dim_type_of_funding.id=dfd.dim_type_of_funding_id LEFT JOIN dim_organization AS dfd_organization ON dfd_organization.id=dfd.dim_organization_id_funder JOIN dim_research_dataset ON ffv.dim_research_dataset_id=dim_research_dataset.id - WHERE ffv.dim_user_profile_id={userprofileId} AND {(forElasticsearch ? " ffv.show=1 AND " : "")} ( @@ -271,7 +318,7 @@ FROM fact_field_values AS ffv ffv.dim_researcher_description_id != -1 OR ffv.dim_email_addrress_id != -1 OR ffv.dim_telephone_number_id != -1 OR - ffv.dim_field_of_science_id != -1 OR + ffv.dim_referencedata_field_of_science_id != -1 OR ffv.dim_keyword_id != -1 OR ffv.dim_pid_id != -1 OR ffv.dim_affiliation_id != -1 OR @@ -441,5 +488,82 @@ public string GetSqlQuery_Delete_DimUserProfile(int userprofileId) { return $"DELETE FROM dim_user_profile WHERE id={userprofileId}"; } + + + // Return SQL SELECT statement for dim_email_addrress + public string GetSqlQuery_Select_DimEmailAddrress(int dimKnownPersonId) + { + return $@"SELECT id as 'Id', dim_registered_data_source_id AS 'DimRegisteredDataSourceId' + FROM dim_email_addrress + WHERE dim_known_person_id={dimKnownPersonId} AND id!=-1 AND dim_registered_data_source_id!=-1"; + } + + // Return SQL SELECT statement for dim_researcher_description + public string GetSqlQuery_Select_DimResearcherDescription(int dimKnownPersonId) + { + return $@"SELECT id as 'Id', dim_registered_data_source_id AS 'DimRegisteredDataSourceId' + FROM dim_researcher_description + WHERE dim_known_person_id={dimKnownPersonId} AND id!=-1 AND dim_registered_data_source_id!=-1"; + } + + // Return SQL SELECT statement for dim_web_link + // TODO: IS NOT NULL condition can be removed, when table dim_web_link.dim_registered_data_source_id is modified to disallow NULL. + public string GetSqlQuery_Select_DimWebLink(int dimKnownPersonId) + { + return $@"SELECT id as 'Id', dim_registered_data_source_id AS 'DimRegisteredDataSourceId' + FROM dim_web_link + WHERE dim_known_person_id={dimKnownPersonId} AND id!=-1 AND dim_registered_data_source_id!=-1 AND dim_registered_data_source_id IS NOT NULL"; + } + + // Return SQL SELECT statement for dim_telephone_number + public string GetSqlQuery_Select_DimTelephoneNumber(int dimKnownPersonId) + { + return $@"SELECT id as 'Id', dim_registered_data_source_id AS 'DimRegisteredDataSourceId' + FROM dim_telephone_number + WHERE dim_known_person_id={dimKnownPersonId} AND id!=-1 AND dim_registered_data_source_id!=-1"; + } + + // Return SQL SELECT statement for dim_affiliation + public string GetSqlQuery_Select_DimAffiliation(int dimKnownPersonId) + { + return $@"SELECT id as 'Id', dim_registered_data_source_id AS 'DimRegisteredDataSourceId' + FROM dim_affiliation + WHERE dim_known_person_id={dimKnownPersonId} AND id!=-1 AND dim_registered_data_source_id!=-1"; + } + + // Return SQL SELECT statement for dim_education + public string GetSqlQuery_Select_DimEducation(int dimKnownPersonId) + { + return $@"SELECT id as 'Id', dim_registered_data_source_id AS 'DimRegisteredDataSourceId' + FROM dim_education + WHERE dim_known_person_id={dimKnownPersonId} AND id!=-1 AND dim_registered_data_source_id!=-1"; + } + + // Return SQL SELECT statement for fact_contribution + public string GetSqlQuery_Select_FactContribution(int dimNameId) + { + return $@"SELECT DISTINCT + dim_research_activity_id AS 'DimResearchActivityId', + dim_research_dataset_id AS 'DimResearchDatasetId', + dim_publication_id AS 'DimPublicationId' + FROM fact_contribution WHERE dim_name_id = {dimNameId} AND (dim_research_activity_id!=-1 OR dim_research_dataset_id!=-1 OR dim_publication_id!=-1)"; + } + + // Return SQL SELECT statement for br_participates_in_funding_group + public string GetSqlQuery_Select_BrParticipatesInFundingGroup(int dimNameId) + { + return $@"SELECT dim_funding_decisionid as 'DimFundingDecisionId' + FROM br_participates_in_funding_group + WHERE dim_name_id = {dimNameId}"; + } + + // Return SQL SELECT statement for counting number of published items in userprofile + public string GetSqlQuery_Select_CountPublishedItemsInUserprofile(int dimUserProfileId) + { + return $@"SELECT COUNT(ffv.show) AS 'PublishedCount' + FROM fact_field_values AS ffv + JOIN dim_user_profile AS dup ON ffv.dim_user_profile_id=dup.id + WHERE dup.id={dimUserProfileId} AND ffv.show=1"; + } } } \ No newline at end of file diff --git a/aspnetcore/src/api/Services/UserProfileService.cs b/aspnetcore/src/api/Services/UserProfileService.cs index 37cea94a..00bbbe64 100644 --- a/aspnetcore/src/api/Services/UserProfileService.cs +++ b/aspnetcore/src/api/Services/UserProfileService.cs @@ -8,11 +8,15 @@ using Microsoft.EntityFrameworkCore; using api.Models.Common; using api.Models.Orcid; +using api.Models.Log; using Dapper; using System.Transactions; using api.Controllers; using Microsoft.Extensions.Logging; using api.Models.Elasticsearch; +using Elasticsearch.Net; +using api.Models.Api; +using Serilog; namespace api.Services { @@ -103,6 +107,22 @@ public bool CanDeleteFactFieldValueRelatedData(FactFieldValue ffv) return ffv.DimRegisteredDataSourceId == _dataSourceHelperService.DimRegisteredDataSourceId_ORCID; } + /* + * Get DimUserProfile based on ORCID Id. + */ + public async Task GetUserprofile(string orcidId) + { + return await _ttvContext.DimUserProfiles.Where(dup => dup.OrcidId == orcidId).AsNoTracking().FirstOrDefaultAsync(); + } + + /* + * Get DimUserProfile based on Id. + */ + public async Task GetUserprofileById(int Id) + { + return await _ttvContext.DimUserProfiles.Where(dup => dup.Id == Id).AsNoTracking().FirstOrDefaultAsync(); + } + /* * Get Id of DimUserProfile based on ORCID Id. */ @@ -220,6 +240,23 @@ public async Task AddOrUpdateDimEmailAddress(string emailAddre return dimEmailAddress; } + /* + * Get new DimKnownPerson. + * - ORCID ID must be used as a source_id. + * - Registered data source must point to ORCID. + */ + public DimKnownPerson GetNewDimKnownPerson(string orcidId, DateTime currentDateTime) + { + return new DimKnownPerson() + { + SourceId = orcidId, // ORCID ID must be used in dim_known_person.source_id + SourceDescription = Constants.SourceDescriptions.PROFILE_API, + Created = currentDateTime, + Modified = currentDateTime, + DimRegisteredDataSourceId = _dataSourceHelperService.DimRegisteredDataSourceId_ORCID + }; + } + /* * Get empty FactFieldValue. * Must use -1 in required foreign keys. @@ -249,8 +286,8 @@ public FactFieldValue GetEmptyFactFieldValue() DimKeywordId = -1, DimAffiliationId = -1, DimResearcherToResearchCommunityId = -1, - DimFieldOfScienceId = -1, DimResearchDatasetId = -1, + DimReferencedataFieldOfScienceId = -1, Show = false, PrimaryValue = false, SourceId = Constants.SourceIdentifiers.PROFILE_API, @@ -348,249 +385,335 @@ public DimPid GetEmptyDimPid() }; } - /* - * Search DimAffiliation items from TTV database and link them to user profile. - */ - public void AddDimAffiliationToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile) - { - DimFieldDisplaySetting dimFieldDisplaySetting_affiliation = - dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_AFFILIATION).First(); - - foreach (DimAffiliation dimAffiliation in dimKnownPerson.DimAffiliations.Where(dimAffiliation => dimAffiliation.DimRegisteredDataSourceId != -1)) - { - FactFieldValue factFieldValueAffiliation = this.GetEmptyFactFieldValue(); - factFieldValueAffiliation.DimUserProfileId = dimUserProfile.Id; - factFieldValueAffiliation.DimFieldDisplaySettingsId = dimFieldDisplaySetting_affiliation.Id; - factFieldValueAffiliation.DimAffiliationId = dimAffiliation.Id; - factFieldValueAffiliation.DimRegisteredDataSourceId = dimAffiliation.DimRegisteredDataSourceId; - _ttvContext.FactFieldValues.Add(factFieldValueAffiliation); - } - } - - /* - * Search DimEducation items from TTV database and link them to user profile. - */ - public void AddDimEducationToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile) - { - DimFieldDisplaySetting dimFieldDisplaySetting_education = - dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_EDUCATION).First(); - - foreach (DimEducation dimEducation in dimKnownPerson.DimEducations.Where(dimEducation => dimEducation.DimRegisteredDataSourceId != -1)) - { - FactFieldValue factFieldValueEducation = this.GetEmptyFactFieldValue(); - factFieldValueEducation.DimUserProfileId = dimUserProfile.Id; - factFieldValueEducation.DimFieldDisplaySettingsId = dimFieldDisplaySetting_education.Id; - factFieldValueEducation.DimEducationId = dimEducation.Id; - factFieldValueEducation.DimRegisteredDataSourceId = dimEducation.DimRegisteredDataSourceId; - _ttvContext.FactFieldValues.Add(factFieldValueEducation); - } - } - - /* - * Search DimResearcherDescription items from TTV database and link them to user profile. - */ - public void AddDimResearcherDescriptionToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile) - { - DimFieldDisplaySetting dimFieldDisplaySetting_researcher_description = - dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION).First(); - foreach (DimResearcherDescription dimResearcherDescription in dimKnownPerson.DimResearcherDescriptions.Where(dimResearcherDescription => dimResearcherDescription.DimRegisteredDataSourceId != -1)) - { - FactFieldValue factFieldValueResearcherDescription = this.GetEmptyFactFieldValue(); - factFieldValueResearcherDescription.DimUserProfileId = dimUserProfile.Id; - factFieldValueResearcherDescription.DimFieldDisplaySettingsId = dimFieldDisplaySetting_researcher_description.Id; - factFieldValueResearcherDescription.DimResearcherDescriptionId = dimResearcherDescription.Id; - factFieldValueResearcherDescription.DimRegisteredDataSourceId = dimResearcherDescription.DimRegisteredDataSourceId; - _ttvContext.FactFieldValues.Add(factFieldValueResearcherDescription); - } - } - /* - * Search DimEmailAddress items from TTV database and link them to user profile. - */ - public void AddDimEmailAddressItemsToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile) - { - DimFieldDisplaySetting dimFieldDisplaySetting_emailAddress = - dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS).First(); - - foreach (DimEmailAddrress dimEmailAddress in dimKnownPerson.DimEmailAddrresses.Where(dimEmailAddress => dimEmailAddress.DimRegisteredDataSourceId != -1)) - { - FactFieldValue factFieldValueEmailAddress = this.GetEmptyFactFieldValue(); - factFieldValueEmailAddress.DimUserProfileId = dimUserProfile.Id; - factFieldValueEmailAddress.DimFieldDisplaySettingsId = dimFieldDisplaySetting_emailAddress.Id; - factFieldValueEmailAddress.DimEmailAddrressId = dimEmailAddress.Id; - factFieldValueEmailAddress.DimRegisteredDataSourceId = dimEmailAddress.DimRegisteredDataSourceId; - _ttvContext.FactFieldValues.Add(factFieldValueEmailAddress); - } - } /* - * Search DimTelephoneNumber items from TTV database and link them to user profile. - */ - public void AddDimTelephoneItemsToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile) - { - DimFieldDisplaySetting dimFieldDisplaySetting_telephoneNumber = - dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_TELEPHONE_NUMBER).First(); - - foreach (DimTelephoneNumber dimTelephoneNumber in dimKnownPerson.DimTelephoneNumbers.Where(dimTelephoneNumber => dimTelephoneNumber.DimRegisteredDataSourceId != -1)) - { - FactFieldValue factFieldValueTelephoneNumber = this.GetEmptyFactFieldValue(); - factFieldValueTelephoneNumber.DimUserProfileId = dimUserProfile.Id; - factFieldValueTelephoneNumber.DimFieldDisplaySettingsId = dimFieldDisplaySetting_telephoneNumber.Id; - factFieldValueTelephoneNumber.DimTelephoneNumberId = dimTelephoneNumber.Id; - factFieldValueTelephoneNumber.DimRegisteredDataSourceId = dimTelephoneNumber.DimRegisteredDataSourceId; - _ttvContext.FactFieldValues.Add(factFieldValueTelephoneNumber); - } - } - - /* - * Search FactContribution related items from TTV database and link them to user profile. + * Search and add data from TTV database. + * This is data that is already linked to the ORCID id in DimPid and it's related DimKnownPerson. */ - public void AddFactContributionItemsToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile) + public async Task AddTtvDataToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile, LogUserIdentification logUserIdentification) { - /* - * Loop DimNames, then related FactContributions. - * - * DimKnownPerson - * => DimName - * => FactContribution - * => DimPublication - * => DimFundingDecision - * => DimResearchDataset - * => DimResearchActivity - * - * NOTE! Registered data source must be taken from DimName. - * Skip item if DimName does not have registered data source. - */ - - // DimFieldDisplaySetting for publications - DimFieldDisplaySetting dimFieldDisplaySetting_publication = - dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_PUBLICATION).First(); - - // DimFieldDisplaySetting for funding decisions - DimFieldDisplaySetting dimFieldDisplaySetting_fundingDecision = - dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_FUNDING_DECISION).First(); - - // DimFieldDisplaySetting for research datasets - DimFieldDisplaySetting dimFieldDisplaySetting_researchDataset = - dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_RESEARCH_DATASET).First(); - - // DimFieldDisplaySetting for research activity - DimFieldDisplaySetting dimFieldDisplaySetting_researchActivity = - dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_RESEARCH_ACTIVITY).First(); - - // Loop DimNames, which have valid registered data source - foreach (DimName dimName in dimKnownPerson.DimNames.Where(dimName => dimName.DimRegisteredDataSourceId != -1)) + using (var connection = _ttvContext.Database.GetDbConnection()) { - // Collect entity IDs into lists. - List publicationsIds = new(); - List fundingDecisionIds = new(); - List researchDatasetIds = new(); - List researchActivityIds = new(); - - // Loop FactContributions. Collect "non -1" values only. - foreach ( - FactContribution factContribution in dimName.FactContributions.Where( - fc => fc.DimPublicationId != -1 || fc.DimFundingDecisionId != -1 || fc.DimResearchDatasetId != -1 || fc.DimResearchActivityId != -1 - ) - ) + // email + try { - // FactContribution is linked to DimPublication - if (factContribution.DimPublicationId != -1) + string emailSql = _ttvSqlService.GetSqlQuery_Select_DimEmailAddrress(dimKnownPerson.Id); + List emails = (await connection.QueryAsync(emailSql)).ToList(); + DimFieldDisplaySetting dimFieldDisplaySetting_emailAddress = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_EMAIL_ADDRESS).First(); + foreach (DimTableMinimalDTO email in emails) { - publicationsIds.Add(factContribution.DimPublicationId); + FactFieldValue factFieldValueEmailAddress = this.GetEmptyFactFieldValue(); + factFieldValueEmailAddress.DimUserProfileId = dimUserProfile.Id; + factFieldValueEmailAddress.DimFieldDisplaySettingsId = dimFieldDisplaySetting_emailAddress.Id; + factFieldValueEmailAddress.DimEmailAddrressId = email.Id; + factFieldValueEmailAddress.DimRegisteredDataSourceId = email.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueEmailAddress); } + } + catch (Exception ex) + { + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.PROFILE_CREATE_ADD_TTV_DATA, + state: LogContent.ActionState.FAILED, + error: true, + message: $"email: {ex.ToString()}")); + } - // FactContribution is linked to DimFundingDecision - if (factContribution.DimFundingDecisionId != -1) + // web link + try + { + string webLinkSql = _ttvSqlService.GetSqlQuery_Select_DimWebLink(dimKnownPerson.Id); + List webLinks = (await connection.QueryAsync(webLinkSql)).ToList(); + DimFieldDisplaySetting dimFieldDisplaySetting_webLink = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_WEB_LINK).First(); + foreach (DimTableMinimalDTO webLink in webLinks) { - fundingDecisionIds.Add(factContribution.DimFundingDecisionId); + FactFieldValue factFieldValueWebLink = this.GetEmptyFactFieldValue(); + factFieldValueWebLink.DimUserProfileId = dimUserProfile.Id; + factFieldValueWebLink.DimFieldDisplaySettingsId = dimFieldDisplaySetting_webLink.Id; + factFieldValueWebLink.DimWebLinkId = webLink.Id; + factFieldValueWebLink.DimRegisteredDataSourceId = webLink.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueWebLink); } + } + catch (Exception ex) + { + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.PROFILE_CREATE_ADD_TTV_DATA, + state: LogContent.ActionState.FAILED, + error: true, + message: $"web link: {ex.ToString()}")); + } - // FactContribution is linked to DimResearchDataset - if (factContribution.DimResearchDatasetId != -1) + // telephone number + try + { + string telephoneNumberSql = _ttvSqlService.GetSqlQuery_Select_DimTelephoneNumber(dimKnownPerson.Id); + List telephoneNumbers = (await connection.QueryAsync(telephoneNumberSql)).ToList(); + DimFieldDisplaySetting dimFieldDisplaySetting_telephoneNumber = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_TELEPHONE_NUMBER).First(); + foreach (DimTableMinimalDTO telephoneNumber in telephoneNumbers) { - researchDatasetIds.Add(factContribution.DimResearchDatasetId); + FactFieldValue factFieldValueTelephoneNumber = this.GetEmptyFactFieldValue(); + factFieldValueTelephoneNumber.DimUserProfileId = dimUserProfile.Id; + factFieldValueTelephoneNumber.DimFieldDisplaySettingsId = dimFieldDisplaySetting_telephoneNumber.Id; + factFieldValueTelephoneNumber.DimTelephoneNumberId = telephoneNumber.Id; + factFieldValueTelephoneNumber.DimRegisteredDataSourceId = telephoneNumber.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueTelephoneNumber); } + } + catch (Exception ex) + { + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.PROFILE_CREATE_ADD_TTV_DATA, + state: LogContent.ActionState.FAILED, + error: true, + message: $"telephone number: {ex.ToString()}")); + } - // FactContribution is linked to DimResearchActivity - if (factContribution.DimResearchActivityId != -1) + // researcher description + try + { + string researcherDescriptionSql = _ttvSqlService.GetSqlQuery_Select_DimResearcherDescription(dimKnownPerson.Id); + List researcherDescriptions = (await connection.QueryAsync(researcherDescriptionSql)).ToList(); + DimFieldDisplaySetting dimFieldDisplaySetting_researcherDescription = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_RESEARCHER_DESCRIPTION).First(); + foreach (DimTableMinimalDTO researcherDescription in researcherDescriptions) { - researchActivityIds.Add(factContribution.DimResearchActivityId); + FactFieldValue factFieldValueResearcherDescription = this.GetEmptyFactFieldValue(); + factFieldValueResearcherDescription.DimUserProfileId = dimUserProfile.Id; + factFieldValueResearcherDescription.DimFieldDisplaySettingsId = dimFieldDisplaySetting_researcherDescription.Id; + factFieldValueResearcherDescription.DimResearcherDescriptionId = researcherDescription.Id; + factFieldValueResearcherDescription.DimRegisteredDataSourceId = researcherDescription.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueResearcherDescription); } } - - // Add FactFieldValues for DimPublication. Remove duplicate IDs. - foreach (int publicationId in publicationsIds.Distinct()) + catch (Exception ex) { - FactFieldValue factFieldValuePublication = this.GetEmptyFactFieldValue(); - factFieldValuePublication.DimUserProfileId = dimUserProfile.Id; - factFieldValuePublication.DimFieldDisplaySettingsId = dimFieldDisplaySetting_publication.Id; - factFieldValuePublication.DimPublicationId = publicationId; - factFieldValuePublication.DimRegisteredDataSourceId = dimName.DimRegisteredDataSourceId; - _ttvContext.FactFieldValues.Add(factFieldValuePublication); + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.PROFILE_CREATE_ADD_TTV_DATA, + state: LogContent.ActionState.FAILED, + error: true, + message: $"researcher description: {ex.ToString()}")); } - // Add FactFieldValues for DimFundingDecision. Remove duplicate IDs. - foreach (int fundingDecisionId in fundingDecisionIds.Distinct()) + // affiliation + try { - FactFieldValue factFieldValueFundingDecision = this.GetEmptyFactFieldValue(); - factFieldValueFundingDecision.DimUserProfileId = dimUserProfile.Id; - factFieldValueFundingDecision.DimFieldDisplaySettingsId = dimFieldDisplaySetting_fundingDecision.Id; - factFieldValueFundingDecision.DimFundingDecisionId = fundingDecisionId; - factFieldValueFundingDecision.DimRegisteredDataSourceId = dimName.DimRegisteredDataSourceId; - _ttvContext.FactFieldValues.Add(factFieldValueFundingDecision); + string affiliationSql = _ttvSqlService.GetSqlQuery_Select_DimAffiliation(dimKnownPerson.Id); + List affiliations = (await connection.QueryAsync(affiliationSql)).ToList(); + DimFieldDisplaySetting dimFieldDisplaySetting_affiliation = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_AFFILIATION).First(); + foreach (DimTableMinimalDTO affiliation in affiliations) + { + FactFieldValue factFieldValueAffiliation = this.GetEmptyFactFieldValue(); + factFieldValueAffiliation.DimUserProfileId = dimUserProfile.Id; + factFieldValueAffiliation.DimFieldDisplaySettingsId = dimFieldDisplaySetting_affiliation.Id; + factFieldValueAffiliation.DimAffiliationId = affiliation.Id; + factFieldValueAffiliation.DimRegisteredDataSourceId = affiliation.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueAffiliation); + } } - - // Add FactFieldValues for DimResearchDataset. Remove duplicate IDs. - foreach (int researchDatasetId in researchDatasetIds.Distinct()) + catch (Exception ex) { - FactFieldValue factFieldValueResearchDataset = this.GetEmptyFactFieldValue(); - factFieldValueResearchDataset.DimUserProfileId = dimUserProfile.Id; - factFieldValueResearchDataset.DimFieldDisplaySettingsId = dimFieldDisplaySetting_researchDataset.Id; - factFieldValueResearchDataset.DimResearchDatasetId = researchDatasetId; - factFieldValueResearchDataset.DimRegisteredDataSourceId = dimName.DimRegisteredDataSourceId; - _ttvContext.FactFieldValues.Add(factFieldValueResearchDataset); + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.PROFILE_CREATE_ADD_TTV_DATA, + state: LogContent.ActionState.FAILED, + error: true, + message: $"affiliation: {ex.ToString()}")); } - // Add FactFieldValues for DimResearchActivity. Remove duplicate IDs. - foreach (int researchActivityId in researchActivityIds.Distinct()) + // education + try { - FactFieldValue factFieldValueResearchActivity = this.GetEmptyFactFieldValue(); - factFieldValueResearchActivity.DimUserProfileId = dimUserProfile.Id; - factFieldValueResearchActivity.DimFieldDisplaySettingsId = dimFieldDisplaySetting_researchActivity.Id; - factFieldValueResearchActivity.DimResearchActivityId = researchActivityId; - factFieldValueResearchActivity.DimRegisteredDataSourceId = dimName.DimRegisteredDataSourceId; - _ttvContext.FactFieldValues.Add(factFieldValueResearchActivity); + string educationSql = _ttvSqlService.GetSqlQuery_Select_DimEducation(dimKnownPerson.Id); + List educations = (await connection.QueryAsync(educationSql)).ToList(); + DimFieldDisplaySetting dimFieldDisplaySetting_education = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_EDUCATION).First(); + foreach (DimTableMinimalDTO education in educations) + { + FactFieldValue factFieldValueEducation = this.GetEmptyFactFieldValue(); + factFieldValueEducation.DimUserProfileId = dimUserProfile.Id; + factFieldValueEducation.DimFieldDisplaySettingsId = dimFieldDisplaySetting_education.Id; + factFieldValueEducation.DimEducationId = education.Id; + factFieldValueEducation.DimRegisteredDataSourceId = education.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueEducation); + } + } + catch (Exception ex) + { + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.PROFILE_CREATE_ADD_TTV_DATA, + state: LogContent.ActionState.FAILED, + error: true, + message: $"education: {ex.ToString()}")); } - } - } - /* - * Search and add data from TTV database. - * This is data that is already linked to the ORCID id in DimPid and it's related DimKnownPerson. - */ - public void AddTtvDataToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfile dimUserProfile) - { - // DimEmailAddress - AddDimEmailAddressItemsToUserProfile(dimKnownPerson, dimUserProfile); - // DimTelephoneNumber - AddDimTelephoneItemsToUserProfile(dimKnownPerson, dimUserProfile); - // DimAffiliation - AddDimAffiliationToUserProfile(dimKnownPerson, dimUserProfile); + DimFieldDisplaySetting dimFieldDisplaySetting_name = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_NAME).First(); + DimFieldDisplaySetting dimFieldDisplaySetting_otherNames = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.PERSON_OTHER_NAMES).First(); + DimFieldDisplaySetting dimFieldDisplaySetting_publication = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_PUBLICATION).First(); + DimFieldDisplaySetting dimFieldDisplaySetting_fundingDecision = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_FUNDING_DECISION).First(); + DimFieldDisplaySetting dimFieldDisplaySetting_researchActivity = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_RESEARCH_ACTIVITY).First(); + DimFieldDisplaySetting dimFieldDisplaySetting_researchDataset = + dimUserProfile.DimFieldDisplaySettings.Where(dfds => dfds.FieldIdentifier == Constants.FieldIdentifiers.ACTIVITY_RESEARCH_DATASET).First(); + + // Loop DimNames, which have valid registered data source + foreach (DimName dimName in dimKnownPerson.DimNames.Where(dimName => dimName.DimRegisteredDataSourceId != -1)) + { + // Name + // Exclude DimNames, whose registered data source is any of the following: + // - virta + // - metax + // - sftp_funding + if ( + !( + dimName.DimRegisteredDataSource.Name == "virta" || + dimName.DimRegisteredDataSource.Name == "metax" || + dimName.DimRegisteredDataSource.Name == "sftp_funding" + ) + ) + { + if (!String.IsNullOrWhiteSpace(dimName.FirstNames) && !String.IsNullOrWhiteSpace(dimName.LastName)) + { + // name: first_names & last_name + FactFieldValue factFieldValueName = this.GetEmptyFactFieldValue(); + factFieldValueName.DimUserProfileId = dimUserProfile.Id; + factFieldValueName.DimFieldDisplaySettingsId = dimFieldDisplaySetting_name.Id; + factFieldValueName.DimNameId = dimName.Id; + factFieldValueName.DimRegisteredDataSourceId = dimName.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueName); + } + else if (!String.IsNullOrWhiteSpace(dimName.FullName)) + { + // other name: full_name + FactFieldValue factFieldValueOtherNames = this.GetEmptyFactFieldValue(); + factFieldValueOtherNames.DimUserProfileId = dimUserProfile.Id; + factFieldValueOtherNames.DimFieldDisplaySettingsId = dimFieldDisplaySetting_otherNames.Id; + factFieldValueOtherNames.DimNameId = dimName.Id; + factFieldValueOtherNames.DimRegisteredDataSourceId = dimName.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueOtherNames); + } + } - // DimEducation - AddDimEducationToUserProfile(dimKnownPerson, dimUserProfile); + // fact_contribution + try + { + string factContributionSql = _ttvSqlService.GetSqlQuery_Select_FactContribution(dimName.Id); + List factContributions = (await connection.QueryAsync(factContributionSql)).ToList(); - // DimResearcherDescription - AddDimResearcherDescriptionToUserProfile(dimKnownPerson, dimUserProfile); + // Loop FactContributions related to a DimName + foreach (FactContributionTableMinimalDTO fc in factContributions) + { + // publication + if (fc.DimPublicationId != -1) + { + FactFieldValue factFieldValuePublication = this.GetEmptyFactFieldValue(); + factFieldValuePublication.DimUserProfileId = dimUserProfile.Id; + factFieldValuePublication.DimFieldDisplaySettingsId = dimFieldDisplaySetting_publication.Id; + factFieldValuePublication.DimPublicationId = fc.DimPublicationId; + factFieldValuePublication.DimRegisteredDataSourceId = dimName.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValuePublication); + } - // FactContribution - AddFactContributionItemsToUserProfile(dimKnownPerson, dimUserProfile); + // research activity + if (fc.DimResearchActivityId != -1) + { + FactFieldValue factFieldValueResearchActivity = this.GetEmptyFactFieldValue(); + factFieldValueResearchActivity.DimUserProfileId = dimUserProfile.Id; + factFieldValueResearchActivity.DimFieldDisplaySettingsId = dimFieldDisplaySetting_researchActivity.Id; + factFieldValueResearchActivity.DimResearchActivityId = fc.DimResearchActivityId; + factFieldValueResearchActivity.DimRegisteredDataSourceId = dimName.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueResearchActivity); + } + + // research dataset + if (fc.DimResearchDatasetId != -1) + { + FactFieldValue factFieldValueResearchDataset = this.GetEmptyFactFieldValue(); + factFieldValueResearchDataset.DimUserProfileId = dimUserProfile.Id; + factFieldValueResearchDataset.DimFieldDisplaySettingsId = dimFieldDisplaySetting_researchDataset.Id; + factFieldValueResearchDataset.DimResearchDatasetId = fc.DimResearchDatasetId; + factFieldValueResearchDataset.DimRegisteredDataSourceId = dimName.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueResearchDataset); + } + } + } + catch (Exception ex) + { + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.PROFILE_CREATE_ADD_TTV_DATA, + state: LogContent.ActionState.FAILED, + error: true, + message: $"fact_contribution: {ex.ToString()}")); + } + + // Funding decisions via br_participates_in_funding_group + // fact_contribution + try + { + string brParticipatesInFundingGroupSql = _ttvSqlService.GetSqlQuery_Select_BrParticipatesInFundingGroup(dimName.Id); + List fundingDecisionIds = (await connection.QueryAsync(brParticipatesInFundingGroupSql)).ToList(); + foreach (int fundingDecisionId in fundingDecisionIds) + { + FactFieldValue factFieldValueFundingDecision = this.GetEmptyFactFieldValue(); + factFieldValueFundingDecision.DimUserProfileId = dimUserProfile.Id; + factFieldValueFundingDecision.DimFieldDisplaySettingsId = dimFieldDisplaySetting_fundingDecision.Id; + factFieldValueFundingDecision.DimFundingDecisionId = fundingDecisionId; + factFieldValueFundingDecision.DimRegisteredDataSourceId = dimName.DimRegisteredDataSourceId; + _ttvContext.FactFieldValues.Add(factFieldValueFundingDecision); + } + } + catch (Exception ex) + { + _logger.LogError( + LogContent.MESSAGE_TEMPLATE, + logUserIdentification, + new LogApiInfo( + action: LogContent.Action.PROFILE_CREATE_ADD_TTV_DATA, + state: LogContent.ActionState.FAILED, + error: true, + message: $"br_participates_in_funding_group: {ex.ToString()}")); + } + } + + await _ttvContext.SaveChangesAsync(); + + } } + + /* * Create user profile. * The following entities will be created in the database: @@ -601,10 +724,9 @@ public void AddTtvDataToUserProfile(DimKnownPerson dimKnownPerson, DimUserProfil * - FactFieldValues * - BrGrantedPermissions */ - public async Task CreateProfile(string orcidId) + public async Task CreateProfile(string orcidId, LogUserIdentification logUserIdentification) { // Get DimPid by ORCID ID. - // Also get all related entities, that should be automatically included in profile. DimPid dimPid = await _ttvContext.DimPids // FactContribution .Include(dp => dp.DimKnownPerson) @@ -612,22 +734,8 @@ public async Task CreateProfile(string orcidId) .ThenInclude(dn => dn.FactContributions).AsNoTracking() // DimName .Include(dp => dp.DimKnownPerson) - .ThenInclude(dkp => dkp.DimNames).AsNoTracking() - // DimAffiliation - .Include(dp => dp.DimKnownPerson) - .ThenInclude(dkp => dkp.DimAffiliations).AsNoTracking() - // DimEducation - .Include(dp => dp.DimKnownPerson) - .ThenInclude(dkp => dkp.DimEducations).AsNoTracking() - // DimReseacherDescription - .Include(dp => dp.DimKnownPerson) - .ThenInclude(dkp => dkp.DimResearcherDescriptions).AsNoTracking() - // DimEmailAddress - .Include(dp => dp.DimKnownPerson) - .ThenInclude(dkp => dkp.DimEmailAddrresses).AsNoTracking() - // DimTelephoneNumber - .Include(dp => dp.DimKnownPerson) - .ThenInclude(dkp => dkp.DimTelephoneNumbers).AsNoTracking() + .ThenInclude(dkp => dkp.DimNames) + .ThenInclude(dn => dn.DimRegisteredDataSource).AsNoTracking() // DimUserProfile .Include(dp => dp.DimKnownPerson) .ThenInclude(dkp => dkp.DimUserProfiles) @@ -647,26 +755,13 @@ public async Task CreateProfile(string orcidId) dimPid.SourceId = Constants.SourceIdentifiers.PROFILE_API; // Since new DimPid is added, then new DimKnownPerson must be added. - dimPid.DimKnownPerson = new DimKnownPerson() - { - SourceId = Constants.SourceIdentifiers.PROFILE_API, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = currentDateTime, - Modified = currentDateTime - }; - + dimPid.DimKnownPerson = GetNewDimKnownPerson(orcidId, currentDateTime); _ttvContext.DimPids.Add(dimPid); } else if (dimPid.DimKnownPerson == null || dimPid.DimKnownPersonId == -1) { // DimPid was found but it does not have related DimKnownPerson, add new. - DimKnownPerson kp = new() - { - SourceId = Constants.SourceIdentifiers.PROFILE_API, - SourceDescription = Constants.SourceDescriptions.PROFILE_API, - Created = currentDateTime, - Modified = currentDateTime - }; + DimKnownPerson kp = GetNewDimKnownPerson(orcidId, currentDateTime); _ttvContext.DimKnownPeople.Add(kp); dimPid.DimKnownPerson = kp; } @@ -720,7 +815,12 @@ await _sharingService.GetDefaultSharingPermissionsListForUserProfile(dimUserProf await _ttvContext.SaveChangesAsync(); // FactFieldValues - Search TTV database and add related entries into user profile. - AddTtvDataToUserProfile(dimPid.DimKnownPerson, dimUserProfile); + //AddTtvDataToUserProfile(dimPid.DimKnownPerson, dimUserProfile); + + await AddTtvDataToUserProfile( + dimKnownPerson: dimPid.DimKnownPerson, + dimUserProfile: dimUserProfile, + logUserIdentification: logUserIdentification); // Save FactFieldValues changes. await _ttvContext.SaveChangesAsync(); @@ -732,7 +832,7 @@ await _sharingService.GetDefaultSharingPermissionsListForUserProfile(dimUserProf * Get profile data. New version using data structure, * where each item contains a list of data sources. */ - public async Task GetProfileDataAsync2(int userprofileId, bool forElasticsearch = false) + public async Task GetProfileDataAsync(int userprofileId, LogUserIdentification logUserIdentification, bool forElasticsearch = false) { // Response data ProfileEditorDataResponse profileDataResponse = new() { }; @@ -898,6 +998,7 @@ public async Task GetProfileDataAsync2(int userprofil break; // Field of science + /* case Constants.FieldIdentifiers.PERSON_FIELD_OF_SCIENCE: // Field of science name translation NameTranslation nameTranslationFieldOfScience = _languageService.GetNameTranslation( @@ -923,6 +1024,7 @@ public async Task GetProfileDataAsync2(int userprofil } ); break; + */ // Keyword case Constants.FieldIdentifiers.PERSON_KEYWORD: @@ -963,17 +1065,45 @@ public async Task GetProfileDataAsync2(int userprofil // Affiliation case Constants.FieldIdentifiers.ACTIVITY_AFFILIATION: - // Get affiliation organization name from related DimOrganization (ffv.DimAffiliation.DimOrganization), if exists. - // Otherwise from DimIdentifierlessData (ffv.DimIdentifierlessData). + // Affiliation organization search order: + // 1. DimAffiliation_DimOrganizationBroader_Id + // 2. DimAffiliation_DimOrganization_Id + // 3. DimIdentifierlessData + // + // Name translation service ensures that none of the language fields is empty. NameTranslation nameTranslationAffiliationOrganization = new(); - if (p.DimAffiliation_DimOrganization_Id > 0) + NameTranslation nameTranslationAffiliationOrganizationSector = new(); + NameTranslation nameTranslationAffiliationDepartment = new(); + + // Organization name + if (p.DimAffiliation_DimOrganizationBroader_Id > 0) + { + nameTranslationAffiliationOrganization = _languageService.GetNameTranslation( + nameFi: p.DimAffiliation_DimOrganizationBroader_NameFi, + nameEn: p.DimAffiliation_DimOrganizationBroader_NameEn, + nameSv: p.DimAffiliation_DimOrganizationBroader_NameSv + ); + + nameTranslationAffiliationOrganizationSector = _languageService.GetNameTranslation( + nameFi: p.DimAffiliation_DimOrganizationBroader_DimSector_NameFi, + nameEn: p.DimAffiliation_DimOrganizationBroader_DimSector_NameEn, + nameSv: p.DimAffiliation_DimOrganizationBroader_DimSector_NameSv + ); + } + else if (p.DimAffiliation_DimOrganization_Id > 0) { nameTranslationAffiliationOrganization = _languageService.GetNameTranslation( nameFi: p.DimAffiliation_DimOrganization_NameFi, nameEn: p.DimAffiliation_DimOrganization_NameEn, nameSv: p.DimAffiliation_DimOrganization_NameSv ); + + nameTranslationAffiliationOrganizationSector = _languageService.GetNameTranslation( + nameFi: p.DimAffiliation_DimOrganization_DimSector_NameFi, + nameEn: p.DimAffiliation_DimOrganization_DimSector_NameEn, + nameSv: p.DimAffiliation_DimOrganization_DimSector_NameSv + ); } else if (p.FactFieldValues_DimIdentifierlessDataId > -1 && p.DimIdentifierlessData_Type == Constants.IdentifierlessDataTypes.ORGANIZATION_NAME) @@ -985,16 +1115,17 @@ public async Task GetProfileDataAsync2(int userprofil ); } - // Name translation for position name - NameTranslation nameTranslationPositionName = _languageService.GetNameTranslation( - nameFi: p.DimAffiliation_PositionNameFi, - nameEn: p.DimAffiliation_PositionNameEn, - nameSv: p.DimAffiliation_PositionNameSv - ); - - // Name translation for department name - NameTranslation nameTranslationAffiliationDepartment = new(); - if (p.DimIdentifierlessData_Type != null && p.DimIdentifierlessData_Type == Constants.IdentifierlessDataTypes.ORGANIZATION_UNIT) + // Department name + if (p.DimAffiliation_DimOrganizationBroader_Id > 0) + { + // When DimOrganizationBroader is available, it contains the organization name and DimOrganization contains department name. + nameTranslationAffiliationDepartment = _languageService.GetNameTranslation( + nameFi: p.DimAffiliation_DimOrganization_NameFi, + nameEn: p.DimAffiliation_DimOrganization_NameEn, + nameSv: p.DimAffiliation_DimOrganization_NameSv + ); + } + else if (p.DimIdentifierlessData_Type != null && p.DimIdentifierlessData_Type == Constants.IdentifierlessDataTypes.ORGANIZATION_UNIT) { nameTranslationAffiliationDepartment = _languageService.GetNameTranslation( nameFi: p.DimIdentifierlessData_ValueFi, @@ -1011,41 +1142,71 @@ public async Task GetProfileDataAsync2(int userprofil ); } - profileDataResponse.activity.affiliations.Add( - new() + // Name translation for position name + NameTranslation nameTranslationPositionName = _languageService.GetNameTranslation( + nameFi: p.DimAffiliation_PositionNameFi, + nameEn: p.DimAffiliation_PositionNameEn, + nameSv: p.DimAffiliation_PositionNameSv + ); + + ProfileEditorAffiliation affiliation = new() + { + OrganizationNameFi = nameTranslationAffiliationOrganization.NameFi, + OrganizationNameEn = nameTranslationAffiliationOrganization.NameEn, + OrganizationNameSv = nameTranslationAffiliationOrganization.NameSv, + DepartmentNameFi = nameTranslationAffiliationDepartment.NameFi, + DepartmentNameEn = nameTranslationAffiliationDepartment.NameSv, + DepartmentNameSv = nameTranslationAffiliationDepartment.NameEn, + PositionNameFi = nameTranslationPositionName.NameFi, + PositionNameEn = nameTranslationPositionName.NameEn, + PositionNameSv = nameTranslationPositionName.NameSv, + Type = p.DimAffiliation_DimReferenceData_NameFi, + StartDate = new ProfileEditorDate() { - OrganizationNameFi = nameTranslationAffiliationOrganization.NameFi, - OrganizationNameEn = nameTranslationAffiliationOrganization.NameEn, - OrganizationNameSv = nameTranslationAffiliationOrganization.NameSv, - DepartmentNameFi = nameTranslationAffiliationDepartment.NameFi, - DepartmentNameEn = nameTranslationAffiliationDepartment.NameSv, - DepartmentNameSv = nameTranslationAffiliationDepartment.NameEn, - PositionNameFi = nameTranslationPositionName.NameFi, - PositionNameEn = nameTranslationPositionName.NameEn, - PositionNameSv = nameTranslationPositionName.NameSv, - Type = p.DimAffiliation_DimReferenceData_NameFi, - StartDate = new ProfileEditorDate() - { - Year = p.DimAffiliation_StartDate_Year, - Month = p.DimAffiliation_StartDate_Month, - Day = p.DimAffiliation_StartDate_Day - }, - EndDate = new ProfileEditorDate() - { - Year = p.DimAffiliation_EndDate_Year, - Month = p.DimAffiliation_EndDate_Month, - Day = p.DimAffiliation_EndDate_Day - }, - itemMeta = new ProfileEditorItemMeta() + Year = p.DimAffiliation_StartDate_Year, + Month = p.DimAffiliation_StartDate_Month, + Day = p.DimAffiliation_StartDate_Day + }, + EndDate = new ProfileEditorDate() + { + Year = p.DimAffiliation_EndDate_Year, + Month = p.DimAffiliation_EndDate_Month, + Day = p.DimAffiliation_EndDate_Day + }, + itemMeta = new ProfileEditorItemMeta() + { + Id = p.FactFieldValues_DimAffiliationId, + Type = Constants.FieldIdentifiers.ACTIVITY_AFFILIATION, + Show = p.FactFieldValues_Show, + PrimaryValue = p.FactFieldValues_PrimaryValue + }, + DataSources = new List { profileEditorSource } + }; + + // Add Elasticsearch person index related data. + if (forElasticsearch && !String.IsNullOrWhiteSpace(p.DimAffiliation_DimOrganization_DimSector_SectorId)) + { + affiliation.sector = new List + { + new ProfileEditorSector() { - Id = p.FactFieldValues_DimAffiliationId, - Type = Constants.FieldIdentifiers.ACTIVITY_AFFILIATION, - Show = p.FactFieldValues_Show, - PrimaryValue = p.FactFieldValues_PrimaryValue - }, - DataSources = new List { profileEditorSource } - } - ); + sectorId = p.DimAffiliation_DimOrganization_DimSector_SectorId, + nameFiSector = nameTranslationAffiliationOrganizationSector.NameFi, + nameEnSector = nameTranslationAffiliationOrganizationSector.NameEn, + nameSvSector = nameTranslationAffiliationOrganizationSector.NameSv, + organization = new List() { + new ProfileEditorSectorOrganization() + { + organizationId = p.DimAffiliation_DimOrganization_OrganizationId, + OrganizationNameFi = nameTranslationAffiliationOrganization.NameFi, + OrganizationNameEn = nameTranslationAffiliationOrganization.NameEn, + OrganizationNameSv = nameTranslationAffiliationOrganization.NameSv + } + } + } + }; + } + profileDataResponse.activity.affiliations.Add(affiliation); break; // Education @@ -1120,6 +1281,16 @@ public async Task GetProfileDataAsync2(int userprofil nameEn: p.DimResearchActivity_DescriptionEn, nameSv: p.DimResearchActivity_DescriptionSv ); + NameTranslation nameTraslationResearchActivityTypeName = _languageService.GetNameTranslation( + nameFi: p.DimResearchActivity_ActivityType_NameFi, + nameEn: p.DimResearchActivity_ActivityType_NameEn, + nameSv: p.DimResearchActivity_ActivityType_NameSv + ); + NameTranslation nameTraslationResearchActivityRoleName = _languageService.GetNameTranslation( + nameFi: p.DimResearchActivity_Role_NameFi, + nameEn: p.DimResearchActivity_Role_NameEn, + nameSv: p.DimResearchActivity_Role_NameSv + ); profileDataResponse.activity.activitiesAndRewards.Add( new ProfileEditorActivityAndReward() { @@ -1149,6 +1320,14 @@ public async Task GetProfileDataAsync2(int userprofil Show = p.FactFieldValues_Show, PrimaryValue = p.FactFieldValues_PrimaryValue }, + ActivityTypeCode = p.DimResearchActivity_ActivityType_CodeValue, + ActivityTypeNameFi = nameTraslationResearchActivityTypeName.NameFi, + ActivityTypeNameEn = nameTraslationResearchActivityTypeName.NameEn, + ActivityTypeNameSv = nameTraslationResearchActivityTypeName.NameSv, + RoleCode = p.DimResearchActivity_Role_CodeValue, + RoleNameFi = nameTraslationResearchActivityRoleName.NameFi, + RoleNameEn = nameTraslationResearchActivityRoleName.NameEn, + RoleNameSv = nameTraslationResearchActivityRoleName.NameSv, DataSources = new List { profileEditorSource } } ); @@ -1235,7 +1414,7 @@ public async Task GetProfileDataAsync2(int userprofil nameFi: p.DimResearchDataset_DescriptionFi, nameEn: p.DimResearchDataset_DescriptionEn, nameSv: p.DimResearchDataset_DescriptionSv - ); + ); profileDataResponse.activity.researchDatasets.Add( new ProfileEditorResearchDataset() { @@ -1286,10 +1465,8 @@ public async Task GetProfileDataAsync2(int userprofil * - delete DimUserChoices (co-operation selection) * - delete DimUserProfile */ - public async Task DeleteProfileDataAsync(int userprofileId) + public async Task DeleteProfileDataAsync(int userprofileId, LogUserIdentification logUserIdentification) { - _logger.LogInformation($"Deleting user profile (dim_user_profile.id={userprofileId})"); - using (var connection = _ttvContext.Database.GetDbConnection()) { // Get list of FactFieldValues using Entity Framework, which ensures that model FactFieldValue populates correctly. @@ -1360,7 +1537,7 @@ await connection.ExecuteAsync( if (factFieldValue.DimEducationId != -1) dimEducationIds.Add(factFieldValue.DimEducationId); if (factFieldValue.DimEmailAddrressId != -1) dimEmailAddrressIds.Add(factFieldValue.DimEmailAddrressId); if (factFieldValue.DimEventId != -1) dimEventIds.Add(factFieldValue.DimEventId); - if (factFieldValue.DimFieldOfScienceId != -1) dimFieldOfScienceIds.Add(factFieldValue.DimFieldOfScienceId); + if (factFieldValue.DimReferencedataFieldOfScienceId != -1) dimFieldOfScienceIds.Add(factFieldValue.DimReferencedataFieldOfScienceId); if (factFieldValue.DimFundingDecisionId != -1) dimFundingDecisionIds.Add(factFieldValue.DimFundingDecisionId); if (factFieldValue.DimKeywordId != -1) dimKeywordIds.Add(factFieldValue.DimKeywordId); if (factFieldValue.DimNameId != -1) dimNameIds.Add(factFieldValue.DimNameId); @@ -1572,6 +1749,24 @@ await connection.ExecuteAsync( return true; } + /* + * Check by dim_user_profile.id if user profile is published. + * Logic: User profile is considered as published, if more than 1 item has property show=true. + * Property 'show' is checked from table fact_field_values + * + * In user profile, the name from ORCID has always show=1, hence the requirement "more than 1 item". + */ + public async Task IsUserprofilePublished(int dimUserProfileId) + { + int publishedCount = 0; + using (var connection = _ttvContext.Database.GetDbConnection()) + { + string publishedCountSql = _ttvSqlService.GetSqlQuery_Select_CountPublishedItemsInUserprofile(dimUserProfileId); + publishedCount = (await connection.QueryAsync(publishedCountSql)).First(); + } + return publishedCount > 1; + } + /* * Execute raw sql. */ @@ -1580,4 +1775,4 @@ public async Task ExecuteRawSql(string sql) await _ttvContext.Database.ExecuteSqlRawAsync(sql); } } -} +} \ No newline at end of file diff --git a/aspnetcore/src/api/Services/UtilityService.cs b/aspnetcore/src/api/Services/UtilityService.cs index 8540f29c..cd856666 100644 --- a/aspnetcore/src/api/Services/UtilityService.cs +++ b/aspnetcore/src/api/Services/UtilityService.cs @@ -1,4 +1,5 @@ using System; +using api.Models.Common; namespace api.Services { @@ -16,5 +17,29 @@ public DateTime GetCurrentDateTime() { return DateTime.UtcNow; } + + /* + * Get ORCID data source organization name. + */ + public string GetDatasourceOrganizationName_ORCID() + { + return Constants.OrganizationNames.ORCID; + } + + /* + * Get TTV data source organization name. + */ + public string GetDatasourceOrganizationName_TTV() + { + return Constants.OrganizationNames.TTV; + } + + /* + * Get OKM organization id. + */ + public string GetOrganizationId_OKM() + { + return Constants.OrganizationIds.OKM; + } } } \ No newline at end of file diff --git a/aspnetcore/src/api/Startup.cs b/aspnetcore/src/api/Startup.cs index 9b869ce7..0db887b0 100644 --- a/aspnetcore/src/api/Startup.cs +++ b/aspnetcore/src/api/Startup.cs @@ -153,8 +153,15 @@ public void ConfigureServices(IServiceCollection services) options.AddPolicy("production", builder => { builder.WithOrigins( - "https://*.csc.fi", - "https://*.rahtiapp.fi" + "https://tiedejatutkimus.fi", + "https://forskning.fi", + "https://research.fi", + "https://www.tiedejatutkimus.fi", + "https://www.forskning.fi", + "https://www.research.fi", + "https://researchfi-qa.rahtiapp.fi", + "https://researchfi-qa-sv.rahtiapp.fi", + "https://researchfi-qa-en.rahtiapp.fi" ) .SetIsOriginAllowedToAllowWildcardSubdomains() .AllowAnyHeader() // TODO: check if AllowAnyHeader() should be removed @@ -163,7 +170,7 @@ public void ConfigureServices(IServiceCollection services) }); /* - * HTTP client: ORCID MEMBER API + * HTTP client: ORCID member API */ services.AddHttpClient("ORCID_MEMBER_API", httpClient => { @@ -172,7 +179,7 @@ public void ConfigureServices(IServiceCollection services) }); /* - * HTTP client: ORCID PUBLIC API + * HTTP client: ORCID public API */ services.AddHttpClient("ORCID_PUBLIC_API", httpClient => { @@ -180,6 +187,15 @@ public void ConfigureServices(IServiceCollection services) httpClient.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json"); }); + /* + * HTTP client: ORCID webhook API + */ + services.AddHttpClient("ORCID_WEBHOOK_API", httpClient => + { + httpClient.BaseAddress = new Uri(Configuration["ORCID:WEBHOOK:API"]); + httpClient.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json"); + }); + /* * HTTP client: Keycloak Admin user token management (client credentials flow). * https://identitymodel.readthedocs.io/en/latest/aspnetcore/worker.html @@ -227,6 +243,8 @@ public void ConfigureServices(IServiceCollection services) services.AddResponseCompression(); + + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); @@ -285,7 +303,7 @@ public void Configure(IApplicationBuilder app, IStartupHelperService startupHelp app.UseCors("production"); } - app.UseSerilogRequestLogging(); + //app.UseSerilogRequestLogging(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); @@ -309,7 +327,8 @@ public void SetServiceValuesFromDatabase(IStartupHelperService startupHelperServ { DimRegisteredDataSource dimRegisteredDataSource_ORCID = startupHelperService.GetDimRegisteredDataSourceId_OnStartup_ORCID(); DimRegisteredDataSource dimRegisteredDataSource_TTV = startupHelperService.GetDimRegisteredDataSourceId_OnStartup_TTV(); - DimPurpose dimPurpose_TTV = startupHelperService.GetDimPurposeId_OnStartup_TTV(); + // TODO: Uncomment when sharing permissions feature is enabled. + // DimPurpose dimPurpose_TTV = startupHelperService.GetDimPurposeId_OnStartup_TTV(); dataSourceHelperService.DimRegisteredDataSourceId_ORCID = dimRegisteredDataSource_ORCID.Id; dataSourceHelperService.DimRegisteredDataSourceName_ORCID = dimRegisteredDataSource_ORCID.Name; @@ -325,7 +344,8 @@ public void SetServiceValuesFromDatabase(IStartupHelperService startupHelperServ dataSourceHelperService.DimOrganizationNameEn_TTV = dimRegisteredDataSource_TTV.DimOrganization.NameEn; dataSourceHelperService.DimOrganizationNameSv_TTV = dimRegisteredDataSource_TTV.DimOrganization.NameSv; - dataSourceHelperService.DimPurposeId_TTV = dimPurpose_TTV.Id; + // TODO: Uncomment when sharing permissions feature is enabled. + // dataSourceHelperService.DimPurposeId_TTV = dimPurpose_TTV.Id; } } } diff --git a/aspnetcore/src/api/api.csproj b/aspnetcore/src/api/api.csproj index 7a55c351..5832f23e 100644 --- a/aspnetcore/src/api/api.csproj +++ b/aspnetcore/src/api/api.csproj @@ -28,7 +28,7 @@ - + @@ -36,6 +36,8 @@ + + @@ -50,6 +52,7 @@ + @@ -74,5 +77,8 @@ + + + diff --git a/aspnetcore/src/api/appsettings.json b/aspnetcore/src/api/appsettings.json index 97caed27..e6858d50 100644 --- a/aspnetcore/src/api/appsettings.json +++ b/aspnetcore/src/api/appsettings.json @@ -1,23 +1,33 @@ { "AllowedHosts": "*", + "Serilog": { - "Using": [], + "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.OpenSearch" ], "MinimumLevel": { "Default": "Information", "Override": { "Microsoft": "Warning", - "System": "Warning", - //"Microsoft.EntityFrameworkCore.Database.Command": "Information" + "System": "Warning" } }, - "Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId", "WithThreadId" ], - "WriteTo": [ - { + "WriteTo": { + "ConsoleSink": { "Name": "Console", "Args": { - "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj} {NewLine}{Exception}" + "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}" + } + }, + "OpenSearchSink": { + "Name": "OpenSearch", + "Args": { + "autoRegisterTemplate": true, + "indexFormat": "mydata-api-log-{0:yyyy.MM}" } } - ] + }, + "Enrich": [ "FromLogContext" ], + "Properties": { + "Application": "CSC.MydataApi" + } } } \ No newline at end of file