import org.sonar.server.notification.NotificationService;
import org.sonar.server.notification.email.AlertsEmailTemplate;
import org.sonar.server.notification.email.EmailNotificationChannel;
+import org.sonar.server.organization.BillingValidationsProxyImpl;
import org.sonar.server.organization.DefaultOrganizationProviderImpl;
import org.sonar.server.permission.GroupPermissionChanger;
import org.sonar.server.permission.PermissionTemplateService;
ResourceTypes.class,
DefaultResourceTypes.get(),
Periods.class,
+ BillingValidationsProxyImpl.class,
// quality profile
ActiveRuleIndexer.class,
assertThat(picoContainer.getComponentAdapters())
.hasSize(
CONTAINER_ITSELF
- + 73 // level 4
+ + 74 // level 4
+ 4 // content of CeConfigurationModule
+ 5 // content of CeQueueModule
+ 3 // content of CeHttpModule
import org.sonar.server.computation.task.projectanalysis.analysis.Organization;
import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReader;
import org.sonar.server.computation.task.step.ComputationStep;
+import org.sonar.server.organization.BillingValidations;
+import org.sonar.server.organization.BillingValidations.BillingValidationsException;
+import org.sonar.server.organization.BillingValidationsProxy;
import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.qualityprofile.QualityProfile;
private final MutableAnalysisMetadataHolder mutableAnalysisMetadataHolder;
private final DefaultOrganizationProvider defaultOrganizationProvider;
private final DbClient dbClient;
+ private final BillingValidations billingValidations;
public LoadReportAnalysisMetadataHolderStep(CeTask ceTask, BatchReportReader reportReader, MutableAnalysisMetadataHolder mutableAnalysisMetadataHolder,
- DefaultOrganizationProvider defaultOrganizationProvider, DbClient dbClient) {
+ DefaultOrganizationProvider defaultOrganizationProvider, DbClient dbClient, BillingValidationsProxy billingValidations) {
this.ceTask = ceTask;
this.reportReader = reportReader;
this.mutableAnalysisMetadataHolder = mutableAnalysisMetadataHolder;
this.defaultOrganizationProvider = defaultOrganizationProvider;
this.dbClient = dbClient;
+ this.billingValidations = billingValidations;
}
@Override
checkProjectKeyConsistency(reportMetadata);
Organization organization = toOrganization(ceTask.getOrganizationUuid());
checkOrganizationKeyConsistency(reportMetadata, organization);
+ checkOrganizationCanExecuteAnalysis(organization);
checkQualityProfilesConsistency(reportMetadata, organization);
mutableAnalysisMetadataHolder.setRootComponentRef(reportMetadata.getRootComponentRef());
}
}
+ private void checkOrganizationCanExecuteAnalysis(Organization organization) {
+ try {
+ billingValidations.checkOnProjectAnalysis(new BillingValidations.Organization(organization.getKey(), organization.getUuid()));
+ } catch (BillingValidationsException e) {
+ throw MessageException.of(e.getMessage());
+ }
+ }
+
private String resolveReportOrganizationKey(@Nullable String organizationKey) {
if (reportBelongsToDefaultOrganization(organizationKey)) {
return defaultOrganizationProvider.get().getKey();
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.organization;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Available checks that will be done by the billing plugin.
+ * When the billing plugin is not loaded, no check will be done.
+ * This is not the interface that should be implemented by the plugin, but {@link BillingValidationsExtension}
+ */
+public interface BillingValidations {
+
+ /**
+ * @throws BillingValidationsException when projects analysis on organization is not allowed
+ */
+ void checkOnProjectAnalysis(Organization organization);
+
+ class Organization {
+ private final String key;
+ private final String uuid;
+
+ public Organization(String key, String uuid) {
+ this.key = requireNonNull(key, "Organization key cannot be null");
+ this.uuid = requireNonNull(uuid, "Organization uuid cannot be null");
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+ }
+
+ class BillingValidationsException extends RuntimeException {
+ public BillingValidationsException(String message) {
+ super(message);
+ }
+
+ /**
+ * Does not fill in the stack trace
+ *
+ * @see java.lang.Throwable#fillInStackTrace()
+ */
+ @Override
+ public synchronized Throwable fillInStackTrace() {
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return getMessage();
+ }
+
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.organization;
+
+import org.sonar.api.ce.ComputeEngineSide;
+import org.sonar.api.server.ServerSide;
+
+/**
+ * The billing plugin must implement this interface
+ */
+@ServerSide
+@ComputeEngineSide
+public interface BillingValidationsExtension extends BillingValidations {
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.organization;
+
+/**
+ * The goal of this class is to handle the 2 different use case :
+ * - The billing plugin exists, the proxy will redirect method calls to the plugin
+ * - No billing plugin, every methods won't do anything
+ */
+public interface BillingValidationsProxy extends BillingValidations {
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.organization;
+
+public class BillingValidationsProxyImpl implements BillingValidationsProxy {
+
+ private final BillingValidationsExtension billingValidationsExtension;
+
+ public BillingValidationsProxyImpl(BillingValidationsExtension billingValidationsExtension) {
+ this.billingValidationsExtension = billingValidationsExtension;
+ }
+
+ // Used when no plugin is providing the extension
+ public BillingValidationsProxyImpl() {
+ this.billingValidationsExtension = null;
+ }
+
+ @Override
+ public void checkOnProjectAnalysis(Organization organization) {
+ if (billingValidationsExtension == null) {
+ return;
+ }
+ billingValidationsExtension.checkOnProjectAnalysis(organization);
+ }
+}
import org.sonar.server.metric.DefaultMetricFinder;
import org.sonar.server.metric.ws.MetricsWsModule;
import org.sonar.server.notification.NotificationModule;
+import org.sonar.server.organization.BillingValidationsProxyImpl;
import org.sonar.server.organization.OrganizationCreationImpl;
import org.sonar.server.organization.OrganizationValidationImpl;
import org.sonar.server.organization.ws.OrganizationsWsModule;
OrganizationValidationImpl.class,
OrganizationCreationImpl.class,
OrganizationsWsModule.class,
+ BillingValidationsProxyImpl.class,
// quality profile
DefinedQProfileRepositoryImpl.class,
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.mockito.ArgumentCaptor;
import org.sonar.api.utils.MessageException;
import org.sonar.api.utils.System2;
import org.sonar.ce.queue.CeTask;
import org.sonar.server.computation.task.projectanalysis.analysis.Organization;
import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReaderRule;
import org.sonar.server.computation.task.step.ComputationStep;
+import org.sonar.server.organization.BillingValidations;
+import org.sonar.server.organization.BillingValidations.BillingValidationsException;
+import org.sonar.server.organization.BillingValidationsProxy;
import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.organization.TestDefaultOrganizationProvider;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class LoadReportAnalysisMetadataHolderStepTest {
private DbClient dbClient = dbTester.getDbClient();
private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(dbTester);
+ private BillingValidationsProxy billingValidations = mock(BillingValidationsProxy.class);
private ComputationStep underTest;
@Before
underTest.execute();
}
+ @Test
+ public void execute_fails_with_MessageException_when_organization_is_not_allowed_to_execute_analysis() {
+ OrganizationDto organization = dbTester.organizations().insert();
+ reportReader.setMetadata(newBatchReportBuilder()
+ .setOrganizationKey(organization.getKey())
+ .build());
+ ComputationStep underTest = createStep(createCeTask(PROJECT_KEY, organization.getUuid()));
+ doThrow(new BillingValidationsException("This organization cannot execute project analysis")).when(billingValidations)
+ .checkOnProjectAnalysis(any(BillingValidations.Organization.class));
+
+ expectedException.expect(MessageException.class);
+ expectedException.expectMessage("This organization cannot execute project analysis");
+
+ underTest.execute();
+ }
+
+ @Test
+ public void execute_does_no_fails_when_organization_is_allowed_to_execute_analysis() {
+ OrganizationDto organization = dbTester.organizations().insert();
+ reportReader.setMetadata(newBatchReportBuilder()
+ .setOrganizationKey(organization.getKey())
+ .build());
+ ComputationStep underTest = createStep(createCeTask(PROJECT_KEY, organization.getUuid()));
+
+ underTest.execute();
+
+ ArgumentCaptor<BillingValidations.Organization> argumentCaptor = ArgumentCaptor.forClass(BillingValidations.Organization.class);
+ verify(billingValidations).checkOnProjectAnalysis(argumentCaptor.capture());
+ assertThat(argumentCaptor.getValue().getKey()).isEqualTo(organization.getKey());
+ assertThat(argumentCaptor.getValue().getUuid()).isEqualTo(organization.getUuid());
+ }
+
private LoadReportAnalysisMetadataHolderStep createStep(CeTask ceTask) {
- return new LoadReportAnalysisMetadataHolderStep(ceTask, reportReader, analysisMetadataHolder, defaultOrganizationProvider, dbClient);
+ return new LoadReportAnalysisMetadataHolderStep(ceTask, reportReader, analysisMetadataHolder, defaultOrganizationProvider, dbClient, billingValidations);
}
private static ScannerReport.Metadata.Builder newBatchReportBuilder() {
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.organization;
+
+import org.junit.Test;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.sonar.server.organization.BillingValidations.Organization;
+
+public class BillingValidationsProxyTest {
+
+ private static String ORGANIZATION_KEY = "ORGANIZATION_KEY";
+ private static String ORGANIZATION_UUID = "ORGANIZATION_UUID";
+
+ private BillingValidationsExtension billingValidationsExtension = mock(BillingValidationsExtension.class);
+
+ private BillingValidationsProxyImpl underTest;
+
+ @Test
+ public void checkOnProjectAnalysis_calls_extension_when_available() {
+ underTest = new BillingValidationsProxyImpl(billingValidationsExtension);
+
+ Organization organization = new Organization(ORGANIZATION_KEY, ORGANIZATION_UUID);
+ underTest.checkOnProjectAnalysis(organization);
+
+ verify(billingValidationsExtension).checkOnProjectAnalysis(organization);
+ }
+
+ @Test
+ public void checkOnProjectAnalysis_does_nothing_when_no_extension_available() {
+ underTest = new BillingValidationsProxyImpl();
+
+ Organization organization = new Organization(ORGANIZATION_KEY, ORGANIZATION_UUID);
+ underTest.checkOnProjectAnalysis(organization);
+
+ verifyZeroInteractions(billingValidationsExtension);
+ }
+
+}