diff --git a/medium-tests/src/test/java/mediumtest/BindingSuggestionsMediumTests.java b/medium-tests/src/test/java/mediumtest/BindingSuggestionsMediumTests.java index 6be1ca2b1b..c453e865f9 100644 --- a/medium-tests/src/test/java/mediumtest/BindingSuggestionsMediumTests.java +++ b/medium-tests/src/test/java/mediumtest/BindingSuggestionsMediumTests.java @@ -27,6 +27,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; +import java.util.UUID; import java.util.Map; import java.util.concurrent.ExecutionException; import org.eclipse.jgit.api.errors.GitAPIException; @@ -45,7 +46,6 @@ import org.sonarsource.sonarlint.core.rpc.protocol.backend.connection.config.SonarQubeConnectionConfigurationDto; import org.sonarsource.sonarlint.core.rpc.protocol.backend.file.DidUpdateFileSystemParams; import org.sonarsource.sonarlint.core.rpc.protocol.common.ClientFileDto; -import org.sonarsource.sonarlint.core.serverapi.UrlUtils; import org.sonarsource.sonarlint.core.serverapi.proto.sonarqube.ws.Common; import org.sonarsource.sonarlint.core.serverapi.proto.sonarqube.ws.Components; import org.sonarsource.sonarlint.core.test.utils.junit5.SonarLintTest; @@ -53,8 +53,6 @@ import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; -import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; @@ -71,7 +69,7 @@ class BindingSuggestionsMediumTests { public static final String CONFIG_SCOPE_ID = "myProject1"; public static final String SLCORE_PROJECT_KEY = "org.sonarsource.sonarlint:sonarlint-core-parent"; public static final String SLCORE_PROJECT_NAME = "SonarLint Core"; - public static final String PROJECT_ID = "my-project-id"; + public static final String PROJECT_ID = "123e4567-e89b-12d3-a456-426614174000"; public static final String REMOTE_URL = "git@github.com:myorg/myproject.git"; @RegisterExtension @@ -337,9 +335,6 @@ void test_uses_binding_clues_from_shared_configuration_when_updating_fs(SonarLin void should_suggest_binding_by_remote_url_when_no_other_suggestions_found(SonarLintTestHarness harness, @TempDir Path tmp) throws IOException { var gitRepo = tmp.resolve("git-repo"); Files.createDirectory(gitRepo); - var encodedUrl = UrlUtils.urlEncode(REMOTE_URL); - var expectedPath = "/dop-translation/project-bindings?url=" + encodedUrl; - var expectedSearchProjectsPath = "/api/components/search_projects?projectIds=" + PROJECT_ID + "&organization=orgKey"; try (var git = GitUtils.createRepository(gitRepo)) { git.remoteAdd() @@ -356,7 +351,11 @@ void should_suggest_binding_by_remote_url_when_no_other_suggestions_found(SonarL var scServer = harness.newFakeSonarCloudServer() .withOrganization("orgKey", organization -> - organization.withProject(SLCORE_PROJECT_KEY, project -> project.withBranch("main"))) + organization.withProject(SLCORE_PROJECT_KEY, project -> project + .withBranch("main") + .withName(SLCORE_PROJECT_NAME) + .withId(UUID.fromString(PROJECT_ID)) + .withBinding(REMOTE_URL))) .start(); var backend = harness.newBackend() @@ -367,30 +366,6 @@ void should_suggest_binding_by_remote_url_when_no_other_suggestions_found(SonarL .withTelemetryEnabled() .start(fakeClient); - scServer.getMockServer().stubFor(get(urlEqualTo(expectedPath)) - .willReturn(aResponse() - .withStatus(200) - .withHeader("Content-Type", "application/json") - .withBody("{\"bindings\":[{\"projectId\":\"" + PROJECT_ID + "\"}]}"))); - - scServer.getMockServer().stubFor(get(urlEqualTo(expectedSearchProjectsPath)) - .willReturn(aResponse() - .withStatus(200) - .withHeader("Content-Type", "application/json") - .withBody("{\"components\":[{\"key\":\"" + SLCORE_PROJECT_KEY + "\",\"name\":\"" + SLCORE_PROJECT_NAME + "\"}]}\n"))); - - scServer.getMockServer().stubFor(get(urlPathMatching("/api/components/search\\.protobuf")) - .willReturn(aResponse() - .withStatus(200) - .withHeader("Content-Type", "application/x-protobuf") - .withResponseBody(protobufBody(Components.SearchWsResponse.newBuilder() - .addComponents(Components.Component.newBuilder() - .setKey("my-project-key") - .setName("My Project") - .build()) - .setPaging(Common.Paging.newBuilder().setTotal(1).build()) - .build())))); - ArgumentCaptor>> suggestionCaptor = ArgumentCaptor.forClass(Map.class); verify(fakeClient, timeout(5000)).suggestBinding(suggestionCaptor.capture()); @@ -406,8 +381,6 @@ void should_suggest_binding_by_remote_url_when_no_other_suggestions_found(SonarL void should_suggest_binding_by_remote_url_when_no_other_suggestions_found_for_sonarqube_server(SonarLintTestHarness harness, @TempDir Path tmp) throws IOException { var gitRepo = tmp.resolve("git-repo"); Files.createDirectory(gitRepo); - var encodedUrl = UrlUtils.urlEncode(REMOTE_URL); - var expectedPath = "/api/v2/dop-translation/project-bindings?repositoryUrl=" + encodedUrl; try (var git = GitUtils.createRepository(gitRepo)) { git.remoteAdd() @@ -423,7 +396,11 @@ void should_suggest_binding_by_remote_url_when_no_other_suggestions_found_for_so .build(); var sqServer = harness.newFakeSonarQubeServer() - .withProject(SLCORE_PROJECT_KEY, project -> project.withBranch("main")) + .withProject(SLCORE_PROJECT_KEY, project -> project + .withBranch("main") + .withProjectName(SLCORE_PROJECT_NAME) + .withId(UUID.fromString(PROJECT_ID)) + .withBinding(REMOTE_URL)) .start(); var backend = harness.newBackend() @@ -432,25 +409,6 @@ void should_suggest_binding_by_remote_url_when_no_other_suggestions_found_for_so .withTelemetryEnabled() .start(fakeClient); - sqServer.getMockServer().stubFor(get(urlEqualTo(expectedPath)) - .willReturn(aResponse() - .withStatus(200) - .withHeader("Content-Type", "application/json") - .withBody("{\"projectBindings\":[{\"projectId\":\"" + PROJECT_ID + "\",\"projectKey\":\"" + SLCORE_PROJECT_KEY + "\"}]}"))); - - var expectedProjectPath = "/api/components/show.protobuf?component=" + UrlUtils.urlEncode(SLCORE_PROJECT_KEY); - sqServer.getMockServer().stubFor(get(urlEqualTo(expectedProjectPath)) - .willReturn(aResponse() - .withStatus(200) - .withHeader("Content-Type", "application/x-protobuf") - .withResponseBody(protobufBody(Components.ShowWsResponse.newBuilder() - .setComponent(Components.Component.newBuilder() - .setKey(SLCORE_PROJECT_KEY) - .setName(SLCORE_PROJECT_NAME) - .setIsAiCodeFixEnabled(false) - .build()) - .build())))); - ArgumentCaptor>> suggestionCaptor = ArgumentCaptor.forClass(Map.class); verify(fakeClient, timeout(5000)).suggestBinding(suggestionCaptor.capture()); @@ -466,8 +424,6 @@ void should_suggest_binding_by_remote_url_when_no_other_suggestions_found_for_so void should_return_empty_when_sqc_project_bindings_is_null(SonarLintTestHarness harness, @TempDir Path tmp) throws IOException, GitAPIException, URISyntaxException { var gitRepo = tmp.resolve("git-repo"); Files.createDirectory(gitRepo); - var encodedUrl = UrlUtils.urlEncode(REMOTE_URL); - var expectedPath = "/dop-translation/project-bindings?url=" + encodedUrl; try (var git = GitUtils.createRepository(gitRepo)) { git.remoteAdd() @@ -483,6 +439,7 @@ void should_return_empty_when_sqc_project_bindings_is_null(SonarLintTestHarness var scServer = harness.newFakeSonarCloudServer() .withOrganization("orgKey", organization -> organization.withProject(SLCORE_PROJECT_KEY, project -> project.withBranch("main"))) + .withResponseCodes(codes -> codes.withStatusCode(500)) .start(); harness.newBackend() @@ -492,11 +449,6 @@ void should_return_empty_when_sqc_project_bindings_is_null(SonarLintTestHarness .withUnboundConfigScope(CONFIG_SCOPE_ID, "unmatched-project-name") .start(fakeClient); - scServer.getMockServer().stubFor(get(urlEqualTo(expectedPath)) - .willReturn(aResponse() - .withStatus(500) - .withBody("Internal Server Error"))); - await().untilAsserted(() -> assertThat(fakeClient.getLogMessages()).contains("Found 0 suggestions for configuration scope '" + CONFIG_SCOPE_ID + "'")); verify(fakeClient, never()).suggestBinding(any()); } @@ -505,8 +457,6 @@ void should_return_empty_when_sqc_project_bindings_is_null(SonarLintTestHarness void should_return_empty_when_sqs_project_bindings_is_null(SonarLintTestHarness harness, @TempDir Path tmp) throws IOException, GitAPIException, URISyntaxException { var gitRepo = tmp.resolve("git-repo"); Files.createDirectory(gitRepo); - var encodedUrl = UrlUtils.urlEncode(REMOTE_URL); - var expectedPath = "/api/v2/dop-translation/project-bindings?repositoryUrl=" + encodedUrl; try (var git = GitUtils.createRepository(gitRepo)) { git.remoteAdd() @@ -521,6 +471,7 @@ void should_return_empty_when_sqs_project_bindings_is_null(SonarLintTestHarness var sqServer = harness.newFakeSonarQubeServer() .withProject(SLCORE_PROJECT_KEY, project -> project.withBranch("main")) + .withResponseCodes(codes -> codes.withStatusCode(500)) .start(); harness.newBackend() @@ -528,11 +479,6 @@ void should_return_empty_when_sqs_project_bindings_is_null(SonarLintTestHarness .withUnboundConfigScope(CONFIG_SCOPE_ID, "unmatched-project-name") .start(fakeClient); - sqServer.getMockServer().stubFor(get(urlEqualTo(expectedPath)) - .willReturn(aResponse() - .withStatus(500) - .withBody("Internal Server Error"))); - await().untilAsserted(() -> assertThat(fakeClient.getLogMessages()).contains("Found 0 suggestions for configuration scope '" + CONFIG_SCOPE_ID + "'")); verify(fakeClient, never()).suggestBinding(any()); } @@ -541,9 +487,6 @@ void should_return_empty_when_sqs_project_bindings_is_null(SonarLintTestHarness void should_return_empty_when_sqc_search_response_is_null(SonarLintTestHarness harness, @TempDir Path tmp) throws IOException, GitAPIException, URISyntaxException { var gitRepo = tmp.resolve("git-repo"); Files.createDirectory(gitRepo); - var encodedUrl = UrlUtils.urlEncode(REMOTE_URL); - var expectedPath = "/dop-translation/project-bindings?url=" + encodedUrl; - var expectedSearchProjectsPath = "/api/components/search_projects?projectIds=" + PROJECT_ID + "&organization=orgKey"; try (var git = GitUtils.createRepository(gitRepo)) { git.remoteAdd() @@ -558,7 +501,12 @@ void should_return_empty_when_sqc_search_response_is_null(SonarLintTestHarness h var scServer = harness.newFakeSonarCloudServer() .withOrganization("orgKey", organization -> - organization.withProject(SLCORE_PROJECT_KEY, project -> project.withBranch("main"))) + organization.withProject(SLCORE_PROJECT_KEY, project -> project + .withBranch("main") + .withName(SLCORE_PROJECT_NAME) + .withId(UUID.fromString(PROJECT_ID)) + .withBinding(REMOTE_URL))) + .withResponseCodes(codes -> codes.withStatusCode(500)) .start(); harness.newBackend() @@ -568,17 +516,6 @@ void should_return_empty_when_sqc_search_response_is_null(SonarLintTestHarness h .withUnboundConfigScope(CONFIG_SCOPE_ID, "unmatched-project-name") .start(fakeClient); - scServer.getMockServer().stubFor(get(urlEqualTo(expectedPath)) - .willReturn(aResponse() - .withStatus(200) - .withHeader("Content-Type", "application/json") - .withBody("{\"bindings\":[{\"projectId\":\"" + PROJECT_ID + "\"}]}"))); - - scServer.getMockServer().stubFor(get(urlEqualTo(expectedSearchProjectsPath)) - .willReturn(aResponse() - .withStatus(500) - .withBody("Internal Server Error"))); - await().untilAsserted(() -> assertThat(fakeClient.getLogMessages()).contains("Found 0 suggestions for configuration scope '" + CONFIG_SCOPE_ID + "'")); verify(fakeClient, never()).suggestBinding(any()); } @@ -587,9 +524,6 @@ void should_return_empty_when_sqc_search_response_is_null(SonarLintTestHarness h void should_return_empty_when_sqs_server_project_is_not_present(SonarLintTestHarness harness, @TempDir Path tmp) throws IOException, GitAPIException, URISyntaxException { var gitRepo = tmp.resolve("git-repo"); Files.createDirectory(gitRepo); - var encodedUrl = UrlUtils.urlEncode(REMOTE_URL); - var expectedPath = "/api/v2/dop-translation/project-bindings?repositoryUrl=" + encodedUrl; - var expectedProjectPath = "/api/components/show.protobuf?component=" + UrlUtils.urlEncode(SLCORE_PROJECT_KEY); try (var git = GitUtils.createRepository(gitRepo)) { git.remoteAdd() @@ -603,7 +537,8 @@ void should_return_empty_when_sqs_server_project_is_not_present(SonarLintTestHar .build(); var sqServer = harness.newFakeSonarQubeServer() - .withProject(SLCORE_PROJECT_KEY, project -> project.withBranch("main")) + .withDopTranslation(dop -> dop + .withProjectBinding(REMOTE_URL, PROJECT_ID, SLCORE_PROJECT_KEY)) .start(); harness.newBackend() @@ -611,17 +546,6 @@ void should_return_empty_when_sqs_server_project_is_not_present(SonarLintTestHar .withUnboundConfigScope(CONFIG_SCOPE_ID, "unmatched-project-name") .start(fakeClient); - sqServer.getMockServer().stubFor(get(urlEqualTo(expectedPath)) - .willReturn(aResponse() - .withStatus(200) - .withHeader("Content-Type", "application/json") - .withBody("{\"projectBindings\":[{\"projectId\":\"" + PROJECT_ID + "\",\"projectKey\":\"" + SLCORE_PROJECT_KEY + "\"}]}"))); - - sqServer.getMockServer().stubFor(get(urlEqualTo(expectedProjectPath)) - .willReturn(aResponse() - .withStatus(404) - .withBody("Project not found"))); - await().untilAsserted(() -> assertThat(fakeClient.getLogMessages()).contains("Found 0 suggestions for configuration scope '" + CONFIG_SCOPE_ID + "'")); verify(fakeClient, never()).suggestBinding(any()); } diff --git a/medium-tests/src/test/java/mediumtest/SonarLintTestHarnessMediumTests.java b/medium-tests/src/test/java/mediumtest/SonarLintTestHarnessMediumTests.java index c45f83372c..7d8c722e12 100644 --- a/medium-tests/src/test/java/mediumtest/SonarLintTestHarnessMediumTests.java +++ b/medium-tests/src/test/java/mediumtest/SonarLintTestHarnessMediumTests.java @@ -221,7 +221,7 @@ static class TestServer extends ServerFixture.Server { private boolean shutdownCalled = false; public TestServer() { - super(null, null, null, null, null, null, null, null, null, false, null, null, null); + super(null, null, null, null, null, null, null, null, null, false, null, null, null, null); } @Override @@ -238,7 +238,7 @@ static class ThrowingTestServer extends ServerFixture.Server { private final RuntimeException exceptionToThrow; ThrowingTestServer(RuntimeException exceptionToThrow) { - super(null, null, null, null, null, null, null, null, null, false, null, null, null); + super(null, null, null, null, null, null, null, null, null, false, null, null, null, null); this.exceptionToThrow = exceptionToThrow; } diff --git a/test-utils/src/main/java/org/sonarsource/sonarlint/core/test/utils/server/ServerFixture.java b/test-utils/src/main/java/org/sonarsource/sonarlint/core/test/utils/server/ServerFixture.java index 326edd42fd..4c5bd3c2c4 100644 --- a/test-utils/src/main/java/org/sonarsource/sonarlint/core/test/utils/server/ServerFixture.java +++ b/test-utils/src/main/java/org/sonarsource/sonarlint/core/test/utils/server/ServerFixture.java @@ -82,6 +82,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.jsonResponse; import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; @@ -142,6 +143,7 @@ public abstract static class AbstractServerBuilder features = new HashSet<>(); private final List smartNotifications = new ArrayList<>(); private final Map globalSettings = new HashMap<>(); + protected DopTranslationBuilder dopTranslation = new DopTranslationBuilder(); protected AbstractServerBuilder(@Nullable Consumer onStart, ServerKind serverKind, @Nullable String version) { this.onStart = onStart; @@ -185,12 +187,30 @@ public T withSmartNotifications(List projects, String events) { return (T) this; } + public T withDopTranslation(UnaryOperator dopTranslationBuilder) { + this.dopTranslation = dopTranslationBuilder.apply(new DopTranslationBuilder()); + return (T) this; + } + + public static class DopTranslationBuilder { + private final Map projectBindings = new HashMap<>(); + + public DopTranslationBuilder withProjectBinding(String repositoryUrl, String projectId, String projectKey) { + this.projectBindings.put(repositoryUrl, new ProjectBinding(projectId, projectKey)); + return this; + } + + record ProjectBinding(String projectId, String projectKey) { + } + + } + record SmartNotifications(List projects, String events) { } public Server start() { var server = new Server(serverKind, serverStatus, version, organizationsByKey, projectByProjectKey, pluginsByKey, qualityProfilesByKey, responseCodes.build(), - aiCodeFixSupportedRules, serverSentEventsEnabled, features, smartNotifications, globalSettings); + aiCodeFixSupportedRules, serverSentEventsEnabled, features, smartNotifications, globalSettings, dopTranslation); server.start(); if (onStart != null) { onStart.accept(server); @@ -293,13 +313,18 @@ public static class ServerProjectBuilder { private String projectName; private AiCodeFixSuggestionBuilder aiCodeFixSuggestion; private boolean aiCodeFixEnabled; + private final DopTranslationBuilder dopTranslation; + private final String projectKey; + private String projectId; private ServerProjectBuilder() { - this(null); + this(null, null, null); } - private ServerProjectBuilder(@Nullable String organizationKey) { + private ServerProjectBuilder(@Nullable String organizationKey, @Nullable DopTranslationBuilder dopTranslation, @Nullable String projectKey) { this.organizationKey = organizationKey; + this.dopTranslation = dopTranslation; + this.projectKey = projectKey; this.branchesByName.put(mainBranchName, new ServerProjectBranchBuilder()); } @@ -317,12 +342,6 @@ public ServerProjectBuilder withProjectName(String projectName) { return this; } - public ServerProjectBuilder withEmptyBranch(String branchName) { - var builder = new ServerProjectBranchBuilder(); - this.branchesByName.put(branchName, builder); - return this; - } - public ServerProjectBuilder withBranch(@Nullable String branchName, UnaryOperator branchBuilder) { var builder = new ServerProjectBranchBuilder(); this.branchesByName.put(branchName, branchBuilder.apply(builder)); @@ -365,6 +384,19 @@ public ServerProjectBuilder withAiCodeFixEnabled(boolean enabled) { return this; } + public ServerProjectBuilder withId(UUID id) { + this.projectId = id.toString(); + return this; + } + + public ServerProjectBuilder withBinding(String repositoryUrl) { + if (this.projectId == null) { + throw new IllegalStateException("withBinding() requires project id to be set via withId(UUID) beforehand"); + } + this.dopTranslation.withProjectBinding(repositoryUrl, this.projectId, this.projectKey); + return this; + } + public record ServerDependencyRisk( String id, String type, @@ -670,7 +702,7 @@ public SonarQubeServerBuilder withProject(String projectKey) { } public SonarQubeServerBuilder withProject(String projectKey, UnaryOperator projectBuilder) { - var builder = new ServerProjectBuilder(); + var builder = new ServerProjectBuilder(null, this.dopTranslation, projectKey); this.projectByProjectKey.put(projectKey, projectBuilder.apply(builder)); return this; } @@ -703,7 +735,7 @@ public SonarQubeCloudBuilder withOrganization(String organizationKey) { } public SonarQubeCloudBuilder withOrganization(String organizationKey, UnaryOperator organizationBuilder) { - var builder = new SonarQubeCloudOrganizationBuilder(organizationKey, qualityProfilesByKey, projectByProjectKey); + var builder = new SonarQubeCloudOrganizationBuilder(organizationKey, qualityProfilesByKey, projectByProjectKey, this.dopTranslation); this.organizationsByKey.put(organizationKey, organizationBuilder.apply(builder)); return this; } @@ -718,12 +750,14 @@ public static class SonarQubeCloudOrganizationBuilder { private final UUID uuidV4 = UUID.randomUUID(); private AbstractServerBuilder.AiCodeFixFeatureBuilder aiCodeFixFeature = new AiCodeFixFeatureBuilder(); private boolean isCurrentUserMember = true; + private final DopTranslationBuilder dopTranslation; public SonarQubeCloudOrganizationBuilder(String organizationKey, Map qualityProfilesByKey, - Map projectByProjectKey) { + Map projectByProjectKey, DopTranslationBuilder dopTranslation) { this.organizationKey = organizationKey; this.qualityProfilesByKey = qualityProfilesByKey; this.projectByProjectKey = projectByProjectKey; + this.dopTranslation = dopTranslation; } public SonarQubeCloudOrganizationBuilder withQualityProfile(String qualityProfileKey, UnaryOperator qualityProfileBuilder) { @@ -737,7 +771,7 @@ public SonarQubeCloudOrganizationBuilder withProject(String projectKey) { } public SonarQubeCloudOrganizationBuilder withProject(String projectKey, UnaryOperator projectBuilder) { - var builder = new ServerProjectBuilder(organizationKey); + var builder = new ServerProjectBuilder(organizationKey, dopTranslation, projectKey); this.projectByProjectKey.put(projectKey, projectBuilder.apply(builder)); return this; } @@ -758,6 +792,9 @@ public SonarQubeCloudOrganizationBuilder withCurrentUserMember(boolean isCurrent public static class Server { private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ").withZone(ZoneId.from(ZoneOffset.UTC)); + public static final String API_COMPONENTS_SHOW_PROTOBUF_COMPONENT = "/api/components/show.protobuf?component="; + public static final String CONTENT_TYPE = "Content-Type"; + public static final String APPLICATION_JSON = "application/json"; private final WireMockServer mockServer = new WireMockServer(options().dynamicPort()); @@ -774,6 +811,7 @@ public static class Server { private final Map globalSettings; private final Set aiCodeFixSupportedRules; private final boolean serverSentEventsEnabled; + private final AbstractServerBuilder.DopTranslationBuilder dopTranslation; private final Set features; private SSEServer sseServer; @@ -781,7 +819,7 @@ public Server(ServerKind serverKind, ServerStatus serverStatus, @Nullable String Map organizationsByKey, Map projectsByProjectKey, Map pluginsByKey, Map qualityProfilesByKey, AbstractServerBuilder.ResponseCodes responseCodes, Set aiCodeFixSupportedRules, boolean serverSentEventsEnabled, Set features, - List smartNotifications, Map globalSettings) { + List smartNotifications, Map globalSettings, AbstractServerBuilder.DopTranslationBuilder dopTranslation) { this.serverKind = serverKind; this.serverStatus = serverStatus; this.version = version != null ? Version.create(version) : null; @@ -795,6 +833,7 @@ public Server(ServerKind serverKind, ServerStatus serverStatus, @Nullable String this.features = features; this.smartNotifications = smartNotifications; this.globalSettings = globalSettings; + this.dopTranslation = dopTranslation; } public void start() { @@ -826,13 +865,14 @@ private void registerWebApiResponses() { registerPushApiResponses(); registerFeaturesApiResponses(); registerScaApiResponses(); + registerDopTranslationApiResponses(); } } private void registerComponentApiResponses() { projectsByProjectKey.forEach((projectKey, project) -> { if (project.projectName != null) { - mockServer.stubFor(get("/api/components/show.protobuf?component=" + projectKey) + mockServer.stubFor(get(API_COMPONENTS_SHOW_PROTOBUF_COMPONENT + UrlUtils.urlEncode(projectKey)) .willReturn(aResponse().withResponseBody(protobufBody( Components.ShowWsResponse.newBuilder() .setComponent(Components.Component.newBuilder().setKey(projectKey).setName(project.projectName).build()).build())))); @@ -1364,6 +1404,32 @@ private void registerComponentsApiResponses() { registerComponentsSearchApiResponses(); registerComponentsShowApiResponses(); registerComponentsTreeApiResponses(); + + projectsByProjectKey.forEach((projectKey, project) -> { + var organizationKey = project.organizationKey; + if (organizationKey == null) { + return; + } + var projectName = project.name; + if (projectName == null) { + return; + } + + dopTranslation.projectBindings.entrySet().stream() + .filter(e -> projectKey.equals(e.getValue().projectKey())) + .forEach(e -> { + var projectBinding = e.getValue(); + var endpoint = "/api/components/search_projects?projectIds=" + projectBinding.projectId() + "&organization=" + organizationKey; + var body = """ + {"components":[{"key":"%s","name":"%s"}]} + """.formatted(projectKey, projectName); + mockServer.stubFor(get(urlEqualTo(endpoint)) + .willReturn(aResponse() + .withStatus(responseCodes.statusCode) + .withHeader(CONTENT_TYPE, APPLICATION_JSON) + .withBody(body))); + }); + }); } private void registerComponentsSearchApiResponses() { @@ -1395,7 +1461,7 @@ private void registerComponentsSearchApiResponses() { private void registerComponentsShowApiResponses() { projectsByProjectKey.forEach((projectKey, project) -> { - var url = "/api/components/show.protobuf?component=" + projectKey; + var url = API_COMPONENTS_SHOW_PROTOBUF_COMPONENT + projectKey; var projectComponent = projectsByProjectKey.entrySet().stream().filter(e -> e.getKey().equals(projectKey)) .map(entry -> Components.Component.newBuilder() .setKey(entry.getKey()) @@ -1581,5 +1647,32 @@ public String url(String path) { public WireMockServer getMockServer() { return mockServer; } + + private void registerDopTranslationApiResponses() { + dopTranslation.projectBindings.forEach((repositoryUrl, projectBinding) -> { + var encodedUrl = UrlUtils.urlEncode(repositoryUrl); + if (serverKind == ServerKind.SONARCLOUD) { + var endpoint = "/dop-translation/project-bindings?url=" + encodedUrl; + var responseBody = """ + {"bindings":[{"projectId":"%s"}]} + """.formatted(projectBinding.projectId()); + mockServer.stubFor(get(urlEqualTo(endpoint)) + .willReturn(aResponse() + .withStatus(200) + .withHeader(CONTENT_TYPE, APPLICATION_JSON) + .withBody(responseBody))); + } else { + var endpoint = "/api/v2/dop-translation/project-bindings?repositoryUrl=" + encodedUrl; + var responseBody = """ + {"projectBindings":[{"projectId":"%s","projectKey":"%s"}]} + """.formatted(projectBinding.projectId(), projectBinding.projectKey()); + mockServer.stubFor(get(urlEqualTo(endpoint)) + .willReturn(aResponse() + .withStatus(200) + .withHeader(CONTENT_TYPE, APPLICATION_JSON) + .withBody(responseBody))); + } + }); + } } }