From: Julien Lancelot Date: Thu, 7 Dec 2017 13:42:27 +0000 (+0100) Subject: SONAR-10134 Add organization parameter in api/qualitygates/get_by_project X-Git-Tag: 7.0-RC1~126 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=a5e7a3e58d9459cf96d569835aaaf76ca40bff23;p=sonarqube.git SONAR-10134 Add organization parameter in api/qualitygates/get_by_project --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/DeselectAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/DeselectAction.java index 699df6cd581..c8d4504066b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/DeselectAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/DeselectAction.java @@ -31,9 +31,7 @@ import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.server.component.ComponentFinder; -import org.sonar.server.exceptions.NotFoundException; -import static java.lang.String.format; import static org.sonar.server.qualitygate.QualityGateUpdater.SONAR_QUALITYGATE_PROPERTY; import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PROJECT_ID; import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PROJECT_KEY; @@ -97,10 +95,8 @@ public class DeselectAction implements QualityGatesWsAction { private ComponentDto getProject(DbSession dbSession, OrganizationDto organization, @Nullable String projectId, @Nullable String projectKey) { ComponentDto project = selectProjectById(dbSession, projectId) .orElseGet(() -> componentFinder.getByUuidOrKey(dbSession, projectId, projectKey, ComponentFinder.ParamNames.PROJECT_ID_AND_KEY)); - if (project.getOrganizationUuid().equals(organization.getUuid())) { - return project; - } - throw new NotFoundException(format("Project '%s' doesn't exist in organization '%s'", project.getKey(), organization.getKey())); + wsSupport.checkProjectBelongsToOrganization(organization, project); + return project; } private Optional selectProjectById(DbSession dbSession, @Nullable String projectId) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/GetByProjectAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/GetByProjectAction.java index 1d69b4a4c9b..1762a640c24 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/GetByProjectAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/GetByProjectAction.java @@ -24,10 +24,10 @@ import org.sonar.api.server.ws.Change; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; -import org.sonar.api.web.UserRole; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; +import org.sonar.db.organization.OrganizationDto; import org.sonar.db.qualitygate.QualityGateDto; import org.sonar.server.component.ComponentFinder; import org.sonar.server.qualitygate.QualityGateFinder; @@ -35,10 +35,12 @@ import org.sonar.server.qualitygate.QualityGateFinder.QualityGateData; import org.sonar.server.user.UserSession; import org.sonarqube.ws.Qualitygates.GetByProjectResponse; +import static org.sonar.api.web.UserRole.ADMIN; +import static org.sonar.api.web.UserRole.USER; +import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.ACTION_GET_BY_PROJECT; import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; import static org.sonar.server.ws.WsUtils.writeProtobuf; -import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.ACTION_GET_BY_PROJECT; public class GetByProjectAction implements QualityGatesWsAction { private static final String PARAM_PROJECT = "project"; @@ -47,12 +49,14 @@ public class GetByProjectAction implements QualityGatesWsAction { private final DbClient dbClient; private final ComponentFinder componentFinder; private final QualityGateFinder qualityGateFinder; + private final QualityGatesWsSupport wsSupport; - public GetByProjectAction(UserSession userSession, DbClient dbClient, ComponentFinder componentFinder, QualityGateFinder qualityGateFinder) { + public GetByProjectAction(UserSession userSession, DbClient dbClient, ComponentFinder componentFinder, QualityGateFinder qualityGateFinder, QualityGatesWsSupport wsSupport) { this.userSession = userSession; this.dbClient = dbClient; this.componentFinder = componentFinder; this.qualityGateFinder = qualityGateFinder; + this.wsSupport = wsSupport; } @Override @@ -79,15 +83,20 @@ public class GetByProjectAction implements QualityGatesWsAction { .setDescription("Project key") .setExampleValue(KEY_PROJECT_EXAMPLE_001) .setRequired(true); + + wsSupport.createOrganizationParam(action); } @Override public void handle(Request request, Response response) throws Exception { try (DbSession dbSession = dbClient.openSession(false)) { + OrganizationDto organization = wsSupport.getOrganization(dbSession, request); ComponentDto project = componentFinder.getByKey(dbSession, request.mandatoryParam(PARAM_PROJECT)); + // As ComponentFinder doesn't handle organization yet, we only check here that the project belongs to the organization + wsSupport.checkProjectBelongsToOrganization(organization, project); - if (!userSession.hasComponentPermission(UserRole.USER, project) && - !userSession.hasComponentPermission(UserRole.ADMIN, project)) { + if (!userSession.hasComponentPermission(USER, project) && + !userSession.hasComponentPermission(ADMIN, project)) { throw insufficientPrivilegesException(); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsSupport.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsSupport.java index 5ff27149dba..efade3cdd90 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsSupport.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/QualityGatesWsSupport.java @@ -32,11 +32,13 @@ import org.sonar.db.organization.OrganizationDto; import org.sonar.db.qualitygate.QGateWithOrgDto; import org.sonar.db.qualitygate.QualityGateConditionDto; import org.sonar.db.qualitygate.QualityGateDto; +import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.user.UserSession; import org.sonarqube.ws.Qualitygates; import static com.google.common.base.Preconditions.checkArgument; +import static java.lang.String.format; import static org.sonar.api.web.UserRole.ADMIN; import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_GATES; import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_ORGANIZATION; @@ -118,6 +120,13 @@ public class QualityGatesWsSupport { throw insufficientPrivilegesException(); } + void checkProjectBelongsToOrganization(OrganizationDto organization, ComponentDto project){ + if (project.getOrganizationUuid().equals(organization.getUuid())) { + return; + } + throw new NotFoundException(format("Project '%s' doesn't exist in organization '%s'", project.getKey(), organization.getKey())); + } + private static void checkNotBuiltIn(QualityGateDto qualityGate) { checkArgument(!qualityGate.isBuiltIn(), "Operation forbidden for built-in Quality Gate '%s'", qualityGate.getName()); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/SelectAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/SelectAction.java index 138510d5cfc..79dde6012a1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/SelectAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ws/SelectAction.java @@ -33,10 +33,8 @@ import org.sonar.db.property.PropertyDto; import org.sonar.db.qualitygate.QGateWithOrgDto; import org.sonar.server.component.ComponentFinder; import org.sonar.server.component.ComponentFinder.ParamNames; -import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.qualitygate.QualityGateFinder; -import static java.lang.String.format; import static org.sonar.server.qualitygate.QualityGates.SONAR_QUALITYGATE_PROPERTY; import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.ACTION_SELECT; import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_GATE_ID; @@ -112,10 +110,8 @@ public class SelectAction implements QualityGatesWsAction { private ComponentDto getProject(DbSession dbSession, OrganizationDto organization, @Nullable String projectId, @Nullable String projectKey) { ComponentDto project = selectProjectById(dbSession, projectId) .orElseGet(() -> componentFinder.getByUuidOrKey(dbSession, projectId, projectKey, ParamNames.PROJECT_ID_AND_KEY)); - if (project.getOrganizationUuid().equals(organization.getUuid())) { - return project; - } - throw new NotFoundException(format("Project '%s' doesn't exist in organization '%s'", project.getKey(), organization.getKey())); + wsSupport.checkProjectBelongsToOrganization(organization, project); + return project; } private Optional selectProjectById(DbSession dbSession, @Nullable String projectId) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/GetByProjectActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/GetByProjectActionTest.java index 5a7da83aa67..c053edc7e8c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/GetByProjectActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/ws/GetByProjectActionTest.java @@ -19,7 +19,6 @@ */ package org.sonar.server.qualitygate.ws; -import javax.annotation.Nullable; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -27,22 +26,20 @@ import org.sonar.api.server.ws.Change; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.System2; import org.sonar.api.web.UserRole; -import org.sonar.core.util.Uuids; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.DbTester; -import org.sonar.db.component.ComponentDbTester; import org.sonar.db.component.ComponentDto; -import org.sonar.db.component.ComponentTesting; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.property.PropertyDto; +import org.sonar.db.qualitygate.QGateWithOrgDto; import org.sonar.db.qualitygate.QualityGateDto; import org.sonar.server.component.TestComponentFinder; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.organization.TestDefaultOrganizationProvider; import org.sonar.server.qualitygate.QualityGateFinder; import org.sonar.server.tester.UserSessionRule; -import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.WsActionTester; import org.sonarqube.ws.Qualitygates; import org.sonarqube.ws.Qualitygates.GetByProjectResponse; @@ -60,41 +57,43 @@ public class GetByProjectActionTest { @Rule public DbTester db = DbTester.create(System2.INSTANCE); - private ComponentDbTester componentDb = new ComponentDbTester(db); private DbClient dbClient = db.getDbClient(); private DbSession dbSession = db.getSession(); private WsActionTester ws = new WsActionTester( - new GetByProjectAction(userSession, dbClient, TestComponentFinder.from(db), new QualityGateFinder(dbClient))); + new GetByProjectAction(userSession, dbClient, TestComponentFinder.from(db), new QualityGateFinder(dbClient), + new QualityGatesWsSupport(db.getDbClient(), userSession, TestDefaultOrganizationProvider.from(db)))); @Test public void definition() { - WebService.Action def = ws.getDef(); - assertThat(def.description()).isNotEmpty(); - assertThat(def.isInternal()).isFalse(); - assertThat(def.since()).isEqualTo("6.1"); - assertThat(def.changelog()).extracting(Change::getVersion, Change::getDescription).containsExactlyInAnyOrder( + WebService.Action action = ws.getDef(); + assertThat(action.description()).isNotEmpty(); + assertThat(action.isInternal()).isFalse(); + assertThat(action.since()).isEqualTo("6.1"); + assertThat(action.changelog()).extracting(Change::getVersion, Change::getDescription).containsExactlyInAnyOrder( tuple("6.6", "The parameter 'projectId' has been removed"), tuple("6.6", "The parameter 'projectKey' has been renamed to 'project'"), - tuple("6.6", "This webservice is now part of the public API") - ); - assertThat(def.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("project"); - - WebService.Param projectKey = def.param("project"); - assertThat(projectKey.description()).isNotEmpty(); - assertThat(projectKey.isRequired()).isTrue(); - assertThat(projectKey.exampleValue()).isNotEmpty(); + tuple("6.6", "This webservice is now part of the public API")); + assertThat(action.params()) + .extracting(WebService.Param::key, WebService.Param::isRequired) + .containsExactlyInAnyOrder( + tuple("project", true), + tuple("organization", false)); } @Test public void json_example() { - OrganizationDto organizationDto = db.organizations().insert(); - ComponentDto project = componentDb.insertComponent(ComponentTesting.newPrivateProjectDto(organizationDto)); - QualityGateDto qualityGate = insertQualityGate("My team QG"); + OrganizationDto organization = db.organizations().insert(); + ComponentDto project = db.components().insertPrivateProject(organization); + QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization, qg -> qg.setName("My team QG")); associateProjectToQualityGate(project.getId(), qualityGate.getId()); logInAsProjectUser(project); - String result = ws.newRequest().setParam("project", project.getKey()).execute().getInput(); + String result = ws.newRequest() + .setParam("project", project.getKey()) + .setParam("organization", organization.getKey()) + .execute() + .getInput(); assertJson(result) .ignoreFields("id") @@ -103,23 +102,31 @@ public class GetByProjectActionTest { @Test public void empty_response() { - ComponentDto project = componentDb.insertPrivateProject(); - insertQualityGate("Another QG"); + OrganizationDto organization = db.organizations().insert(); + ComponentDto project = db.components().insertPrivateProject(organization); + db.qualityGates().insertQualityGate(organization); logInAsProjectUser(project); - String result = ws.newRequest().setParam("project", project.getKey()).execute().getInput(); + String result = ws.newRequest() + .setParam("project", project.getKey()) + .setParam("organization", organization.getKey()) + .execute().getInput(); assertThat(result).isEqualToIgnoringWhitespace("{}"); } @Test public void default_quality_gate() { - ComponentDto project = componentDb.insertComponent(ComponentTesting.newPrivateProjectDto(db.organizations().insert())); - QualityGateDto dbQualityGate = insertQualityGate("Sonar way"); + OrganizationDto organization = db.organizations().insert(); + ComponentDto project = db.components().insertPrivateProject(organization); + QGateWithOrgDto dbQualityGate = db.qualityGates().insertQualityGate(organization); setDefaultQualityGate(dbQualityGate.getId()); logInAsProjectUser(project); - GetByProjectResponse result = callByKey(project.getKey()); + GetByProjectResponse result = ws.newRequest() + .setParam("project", project.getKey()) + .setParam("organization", organization.getKey()) + .executeProtobuf(GetByProjectResponse.class); Qualitygates.QualityGate qualityGate = result.getQualityGate(); assertThat(Long.valueOf(qualityGate.getId())).isEqualTo(dbQualityGate.getId()); @@ -129,80 +136,143 @@ public class GetByProjectActionTest { @Test public void project_quality_gate_over_default() { - ComponentDto project = componentDb.insertComponent(ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization())); - QualityGateDto defaultDbQualityGate = insertQualityGate("Sonar way"); - QualityGateDto dbQualityGate = insertQualityGate("My team QG"); + OrganizationDto organization = db.organizations().insert(); + ComponentDto project = db.components().insertPrivateProject(organization); + QGateWithOrgDto defaultDbQualityGate = db.qualityGates().insertQualityGate(organization); setDefaultQualityGate(defaultDbQualityGate.getId()); - associateProjectToQualityGate(project.getId(), dbQualityGate.getId()); + QualityGateDto qualityGate = db.qualityGates().insertQualityGate(organization); + associateProjectToQualityGate(project.getId(), qualityGate.getId()); logInAsProjectUser(project); - GetByProjectResponse result = callByKey(project.getKey()); + GetByProjectResponse result = ws.newRequest() + .setParam("project", project.getKey()) + .setParam("organization", organization.getKey()) + .executeProtobuf(GetByProjectResponse.class); - Qualitygates.QualityGate qualityGate = result.getQualityGate(); - assertThat(qualityGate.getName()).isEqualTo(dbQualityGate.getName()); - assertThat(qualityGate.getDefault()).isFalse(); + Qualitygates.QualityGate reloaded = result.getQualityGate(); + assertThat(reloaded.getName()).isEqualTo(reloaded.getName()); + assertThat(reloaded.getDefault()).isFalse(); } @Test public void get_by_project_key() { - ComponentDto project = componentDb.insertComponent(ComponentTesting.newPrivateProjectDto(db.organizations().insert())); - QualityGateDto dbQualityGate = insertQualityGate("My team QG"); - associateProjectToQualityGate(project.getId(), dbQualityGate.getId()); + OrganizationDto organization = db.organizations().insert(); + ComponentDto project = db.components().insertPrivateProject(organization); + QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); + associateProjectToQualityGate(project.getId(), qualityGate.getId()); logInAsProjectUser(project); - GetByProjectResponse result = callByKey(project.getDbKey()); + GetByProjectResponse result = ws.newRequest() + .setParam("project", project.getKey()) + .setParam("organization", organization.getKey()) + .executeProtobuf(GetByProjectResponse.class); - assertThat(result.getQualityGate().getName()).isEqualTo(dbQualityGate.getName()); + assertThat(result.getQualityGate().getName()).isEqualTo(qualityGate.getName()); } @Test public void get_with_project_admin_permission() { - ComponentDto project = componentDb.insertPrivateProject(); + OrganizationDto organization = db.organizations().insert(); + ComponentDto project = db.components().insertPrivateProject(organization); userSession.logIn().addProjectPermission(UserRole.ADMIN, project); - QualityGateDto dbQualityGate = insertQualityGate("Sonar way"); - setDefaultQualityGate(dbQualityGate.getId()); + QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); + associateProjectToQualityGate(project.getId(), qualityGate.getId()); - GetByProjectResponse result = callByKey(project.getKey()); + GetByProjectResponse result = ws.newRequest() + .setParam("project", project.getKey()) + .setParam("organization", organization.getKey()) + .executeProtobuf(GetByProjectResponse.class); - assertThat(result.getQualityGate().getName()).isEqualTo(dbQualityGate.getName()); + assertThat(result.getQualityGate().getName()).isEqualTo(qualityGate.getName()); } @Test public void get_with_project_user_permission() { - ComponentDto project = componentDb.insertPrivateProject(); + OrganizationDto organization = db.organizations().insert(); + ComponentDto project = db.components().insertPrivateProject(organization); userSession.logIn().addProjectPermission(UserRole.USER, project); - QualityGateDto dbQualityGate = insertQualityGate("Sonar way"); - setDefaultQualityGate(dbQualityGate.getId()); + QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); + associateProjectToQualityGate(project.getId(), qualityGate.getId()); - GetByProjectResponse result = callByKey(project.getKey()); + GetByProjectResponse result = ws.newRequest() + .setParam("project", project.getKey()) + .setParam("organization", organization.getKey()) + .executeProtobuf(GetByProjectResponse.class); - assertThat(result.getQualityGate().getName()).isEqualTo(dbQualityGate.getName()); + assertThat(result.getQualityGate().getName()).isEqualTo(qualityGate.getName()); + } + + @Test + public void default_organization_is_used_when_no_organization_parameter() { + OrganizationDto organization = db.getDefaultOrganization(); + QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); + ComponentDto project = db.components().insertPrivateProject(organization); + userSession.logIn().addProjectPermission(UserRole.USER, project); + associateProjectToQualityGate(project.getId(), qualityGate.getId()); + + GetByProjectResponse result = ws.newRequest() + .setParam("project", project.getKey()) + .executeProtobuf(GetByProjectResponse.class); + + assertThat(result.getQualityGate().getName()).isEqualTo(qualityGate.getName()); + } + + @Test + public void fail_when_project_does_not_not_belong_to_organization() { + OrganizationDto organization = db.organizations().insert(); + OrganizationDto otherOrganization = db.organizations().insert(); + ComponentDto project = db.components().insertPrivateProject(otherOrganization); + userSession.logIn().addProjectPermission(UserRole.USER, project); + QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); + associateProjectToQualityGate(project.getId(), qualityGate.getId()); + + expectedException.expect(NotFoundException.class); + expectedException.expectMessage(String.format("Project '%s' doesn't exist in organization '%s'", project.getKey(), organization.getKey())); + + ws.newRequest() + .setParam("project", project.getKey()) + .setParam("organization", organization.getKey()) + .execute(); } @Test public void fail_when_insufficient_permission() { - ComponentDto project = componentDb.insertComponent(ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization())); + OrganizationDto organization = db.organizations().insert(); + ComponentDto project = db.components().insertPrivateProject(organization); + QGateWithOrgDto qualityGate = db.qualityGates().insertQualityGate(organization); + associateProjectToQualityGate(project.getId(), qualityGate.getId()); userSession.logIn(); - QualityGateDto dbQualityGate = insertQualityGate("Sonar way"); - setDefaultQualityGate(dbQualityGate.getId()); expectedException.expect(ForbiddenException.class); - callByKey(project.getKey()); + ws.newRequest() + .setParam("project", project.getKey()) + .setParam("organization", organization.getKey()) + .execute(); } @Test public void fail_when_project_does_not_exist() { + OrganizationDto organization = db.organizations().insert(); + expectedException.expect(NotFoundException.class); - callByKey("Unknown"); + ws.newRequest() + .setParam("project", "Unknown") + .setParam("organization", organization.getKey()) + .execute(); } @Test - public void fail_when_no_parameter() { + public void fail_when_missing_project_parameter() { + OrganizationDto organization = db.organizations().insert(); + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("The 'project' parameter is missing"); - call(null); + ws.newRequest() + .setParam("organization", organization.getKey()) + .execute(); } @Test @@ -215,25 +285,10 @@ public class GetByProjectActionTest { expectedException.expect(NotFoundException.class); expectedException.expectMessage(format("Component key '%s' not found", branch.getDbKey())); - call(branch.getDbKey()); - } - - private GetByProjectResponse callByKey(String projectKey) { - return call(projectKey); - } - - private GetByProjectResponse call(@Nullable String projectKey) { - TestRequest request = ws.newRequest(); - if (projectKey != null) { - request.setParam("project", projectKey); - } - return request.executeProtobuf(GetByProjectResponse.class); - } - - private QualityGateDto insertQualityGate(String name) { - QualityGateDto qualityGate = dbClient.qualityGateDao().insert(dbSession, new QualityGateDto().setName(name).setUuid(Uuids.createFast())); - db.commit(); - return qualityGate; + ws.newRequest() + .setParam("project", branch.getDbKey()) + .setParam("organization", organization.getKey()) + .execute(); } private void associateProjectToQualityGate(long componentId, long qualityGateId) {