]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-14396 Decorate PRs in Bitbucket Cloud
authorDuarte Meneses <duarte.meneses@sonarsource.com>
Fri, 29 Jan 2021 17:42:09 +0000 (11:42 -0600)
committersonartech <sonartech@sonarsource.com>
Mon, 8 Feb 2021 20:07:45 +0000 (20:07 +0000)
12 files changed:
server/sonar-alm-client/src/main/java/org/sonar/alm/client/bitbucket/bitbucketcloud/BitbucketCloudRestClient.java
server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionForm.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormField.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmTab.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmTabRenderer.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/AzureForm.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/GithubForm.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/GitlabForm.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionForm-test.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmTabRenderer-test.tsx
server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionFormField-test.tsx.snap
server/sonar-webserver-webapi/src/test/java/org/sonar/server/almsettings/ws/ValidateActionTest.java

index 1102bd375c7fe27fa273d09e66f48e9f7fe87c51..1f3cc008f6a8872bdd1bf7802cd314e37f9f94f4 100644 (file)
@@ -26,6 +26,7 @@ import java.io.IOException;
 import java.net.HttpURLConnection;
 import java.util.Objects;
 import java.util.function.Function;
+import java.util.function.UnaryOperator;
 import javax.annotation.Nullable;
 import okhttp3.Credentials;
 import okhttp3.FormBody;
@@ -138,8 +139,7 @@ public class BitbucketCloudRestClient {
   }
 
   protected <G> G doGet(String accessToken, HttpUrl url, Function<Response, G> handler) {
-    Request request = prepareRequestWithAccessToken(accessToken, GET, url, null);
-    return doCall(request, handler, false);
+    return doGet(accessToken, url, handler, false);
   }
 
   protected void doPost(String accessToken, HttpUrl url, RequestBody body) {
@@ -191,7 +191,7 @@ public class BitbucketCloudRestClient {
     });
   }
 
-  private static <T> ErrorDetails getErrorDetails(@Nullable ResponseBody body, Function<String, String> parser) throws IOException {
+  private static ErrorDetails getErrorDetails(@Nullable ResponseBody body, UnaryOperator<String> parser) throws IOException {
     if (body == null) {
       return new ErrorDetails("", null);
     }
@@ -200,7 +200,7 @@ public class BitbucketCloudRestClient {
       try {
         return new ErrorDetails(bodyStr, parser.apply(bodyStr));
       } catch (JsonParseException e) {
-        // ignore
+        //ignore
       }
     }
     return new ErrorDetails(bodyStr, null);
index 0cb70f4bbbb81c3bed5c3908bbf704b92aefe5d5..fa0a18865440e39589695fc2d2611a3a731a7374 100644 (file)
@@ -24,16 +24,13 @@ import AlmBindingDefinitionFormModalRenderer from './AlmBindingDefinitionFormMod
 
 export interface AlmBindingDefinitionFormChildrenProps<B> {
   formData: B;
-  hideKeyField?: boolean;
   onFieldChange: (fieldId: keyof B, value: string) => void;
-  readOnly?: boolean;
 }
 
 interface Props<B> {
   bindingDefinition: B;
   children: (props: AlmBindingDefinitionFormChildrenProps<B>) => React.ReactNode;
   help?: React.ReactNode;
-  hideKeyField?: boolean;
   isSecondInstance?: boolean;
   onCancel?: () => void;
   onDelete?: (definitionKey: string) => void;
@@ -95,18 +92,13 @@ export default class AlmBindingDefinitionForm<
   };
 
   canSubmit = () => {
-    const { hideKeyField, optionalFields } = this.props;
+    const { optionalFields } = this.props;
     const { formData, touched } = this.state;
 
-    let values;
-    if (hideKeyField) {
-      values = omit(formData, 'key');
-    } else {
-      values = { ...formData };
-    }
+    let values = { ...formData };
 
     if (optionalFields && optionalFields.length > 0) {
-      values = omit(values, optionalFields);
+      values = omit(values, optionalFields) as B;
     }
 
     return touched && !Object.values(values).some(v => !v);
index b86c8209f8f821be80114acf4920dbd772588f4e..9b4570231456024f5937d746cb84f0a581f7af8f 100644 (file)
@@ -33,7 +33,6 @@ export interface AlmBindingDefinitionFormFieldProps<B extends AlmBindingDefiniti
   optional?: boolean;
   overwriteOnly?: boolean;
   propKey: keyof B;
-  readOnly?: boolean;
   value: string;
 }
 
@@ -49,7 +48,6 @@ export function AlmBindingDefinitionFormField<B extends AlmBindingDefinition>(
     optional,
     overwriteOnly = false,
     propKey,
-    readOnly = false,
     value
   } = props;
   const [showField, setShowField] = React.useState(!overwriteOnly);
@@ -78,7 +76,6 @@ export function AlmBindingDefinitionFormField<B extends AlmBindingDefinition>(
       {showField && isTextArea && (
         <textarea
           className="settings-large-input"
-          disabled={readOnly}
           id={id}
           maxLength={maxLength || 2000}
           onChange={e => props.onFieldChange(propKey, e.currentTarget.value)}
@@ -92,7 +89,6 @@ export function AlmBindingDefinitionFormField<B extends AlmBindingDefinition>(
         <input
           autoFocus={autoFocus}
           className="input-super-large"
-          disabled={readOnly}
           id={id}
           maxLength={maxLength || 100}
           name={id}
index 53a845726959ffd5198f79c145e4edd6a5c08d24..6e54d53897ddf8f97fb3d7b48157a2c0128dc4ba 100644 (file)
@@ -82,9 +82,7 @@ export default class AlmTab<B extends AlmBindingDefinition> extends React.PureCo
   handleSubmit = (config: B, originalKey: string) => {
     const call = originalKey
       ? this.props.updateConfiguration({ newKey: config.key, ...config, key: originalKey })
-      : // If there's no support for multi-ALM binding, the key will be an empty string.
-        // Set a default.
-        this.props.createConfiguration({ ...config, key: config.key || this.props.alm });
+      : this.props.createConfiguration({ ...config });
 
     this.setState({ submitting: true });
     return call
@@ -112,7 +110,6 @@ export default class AlmTab<B extends AlmBindingDefinition> extends React.PureCo
     const {
       alm,
       branchesEnabled,
-      defaultBinding,
       definitions,
       definitionStatus,
       form,
@@ -128,7 +125,6 @@ export default class AlmTab<B extends AlmBindingDefinition> extends React.PureCo
       <AlmTabRenderer
         alm={alm}
         branchesEnabled={branchesEnabled}
-        defaultBinding={defaultBinding}
         definitions={definitions}
         definitionStatus={definitionStatus}
         editedDefinition={editedDefinition}
index e0f13c920db4bbd2a320ce6ad1eca894e35602b4..2e8380d91d4dcf57f01dd1f62bc9d22c54484a3b 100644 (file)
@@ -38,7 +38,6 @@ export interface AlmTabRendererProps<B> {
   branchesEnabled: boolean;
   definitionStatus: T.Dict<AlmSettingsBindingStatus>;
   editedDefinition?: B;
-  defaultBinding: B;
   definitions: B[];
   form: (props: AlmBindingDefinitionFormChildrenProps<B>) => React.ReactNode;
   help: React.ReactNode;
index 1b9338566e3698c2b9faeccde688ccc4a872e0a4..24eacd2886a066203492d6b48e528f5b12c03aed 100644 (file)
@@ -24,27 +24,22 @@ import { AlmBindingDefinitionFormField } from './AlmBindingDefinitionFormField';
 
 export interface AzureFormProps {
   formData: AzureBindingDefinition;
-  hideKeyField?: boolean;
   onFieldChange: (fieldId: keyof AzureBindingDefinition, value: string) => void;
-  readOnly?: boolean;
 }
 
 export default function AzureForm(props: AzureFormProps) {
-  const { formData, hideKeyField, onFieldChange, readOnly } = props;
+  const { formData, onFieldChange } = props;
 
   return (
     <>
-      {!hideKeyField && (
-        <AlmBindingDefinitionFormField
-          autoFocus={true}
-          help={translate('settings.almintegration.form.name.azure.help')}
-          id="name.azure"
-          onFieldChange={onFieldChange}
-          propKey="key"
-          readOnly={readOnly}
-          value={formData.key}
-        />
-      )}
+      <AlmBindingDefinitionFormField
+        autoFocus={true}
+        help={translate('settings.almintegration.form.name.azure.help')}
+        id="name.azure"
+        onFieldChange={onFieldChange}
+        propKey="key"
+        value={formData.key}
+      />
       <AlmBindingDefinitionFormField
         help={
           <>
@@ -62,7 +57,6 @@ export default function AzureForm(props: AzureFormProps) {
         maxLength={2000}
         onFieldChange={onFieldChange}
         propKey="url"
-        readOnly={readOnly}
         value={formData.url || ''}
       />
       <AlmBindingDefinitionFormField
@@ -72,7 +66,6 @@ export default function AzureForm(props: AzureFormProps) {
         onFieldChange={onFieldChange}
         overwriteOnly={Boolean(formData.key)}
         propKey="personalAccessToken"
-        readOnly={readOnly}
         value={formData.personalAccessToken}
       />
     </>
index 87cb47d8652f43914676762c872f1e161b609bd4..1219f0d494cdc68c77c2134919e6176ed2b4cbd0 100644 (file)
@@ -24,27 +24,22 @@ import { AlmBindingDefinitionFormField } from './AlmBindingDefinitionFormField';
 
 export interface GithubFormProps {
   formData: GithubBindingDefinition;
-  hideKeyField?: boolean;
   onFieldChange: (fieldId: keyof GithubBindingDefinition, value: string) => void;
-  readOnly?: boolean;
 }
 
 export default function GithubForm(props: GithubFormProps) {
-  const { formData, hideKeyField, onFieldChange, readOnly } = props;
+  const { formData, onFieldChange } = props;
 
   return (
     <>
-      {!hideKeyField && (
-        <AlmBindingDefinitionFormField
-          autoFocus={true}
-          help={translate('settings.almintegration.form.name.github.help')}
-          id="name.github"
-          onFieldChange={onFieldChange}
-          propKey="key"
-          readOnly={readOnly}
-          value={formData.key}
-        />
-      )}
+      <AlmBindingDefinitionFormField
+        autoFocus={true}
+        help={translate('settings.almintegration.form.name.github.help')}
+        id="name.github"
+        onFieldChange={onFieldChange}
+        propKey="key"
+        value={formData.key}
+      />
       <AlmBindingDefinitionFormField
         help={
           <>
@@ -62,7 +57,6 @@ export default function GithubForm(props: GithubFormProps) {
         maxLength={2000}
         onFieldChange={onFieldChange}
         propKey="url"
-        readOnly={readOnly}
         value={formData.url}
       />
       <AlmBindingDefinitionFormField
@@ -70,7 +64,6 @@ export default function GithubForm(props: GithubFormProps) {
         maxLength={80}
         onFieldChange={onFieldChange}
         propKey="appId"
-        readOnly={readOnly}
         value={formData.appId}
       />
       <AlmBindingDefinitionFormField
@@ -78,7 +71,6 @@ export default function GithubForm(props: GithubFormProps) {
         maxLength={80}
         onFieldChange={onFieldChange}
         propKey="clientId"
-        readOnly={readOnly}
         value={formData.clientId}
       />
       <AlmBindingDefinitionFormField
@@ -87,7 +79,6 @@ export default function GithubForm(props: GithubFormProps) {
         onFieldChange={onFieldChange}
         overwriteOnly={Boolean(formData.key)}
         propKey="clientSecret"
-        readOnly={readOnly}
         value={formData.clientSecret}
       />
       <AlmBindingDefinitionFormField
@@ -96,7 +87,6 @@ export default function GithubForm(props: GithubFormProps) {
         onFieldChange={onFieldChange}
         overwriteOnly={Boolean(formData.key)}
         propKey="privateKey"
-        readOnly={readOnly}
         value={formData.privateKey}
       />
     </>
index 2ef0b83a92a0c485a7b807d04e67adb3d56068d6..62b8daf8db7241d7d957822a582f72e02779c021 100644 (file)
@@ -24,27 +24,22 @@ import { AlmBindingDefinitionFormField } from './AlmBindingDefinitionFormField';
 
 export interface GitlabFormProps {
   formData: GitlabBindingDefinition;
-  hideKeyField?: boolean;
   onFieldChange: (fieldId: keyof GitlabBindingDefinition, value: string) => void;
-  readOnly?: boolean;
 }
 
 export default function GitlabForm(props: GitlabFormProps) {
-  const { formData, hideKeyField, onFieldChange, readOnly } = props;
+  const { formData, onFieldChange } = props;
 
   return (
     <>
-      {!hideKeyField && (
-        <AlmBindingDefinitionFormField
-          autoFocus={true}
-          help={translate('settings.almintegration.form.name.gitlab.help')}
-          id="name.gitlab"
-          onFieldChange={onFieldChange}
-          propKey="key"
-          readOnly={readOnly}
-          value={formData.key}
-        />
-      )}
+      <AlmBindingDefinitionFormField
+        autoFocus={true}
+        help={translate('settings.almintegration.form.name.gitlab.help')}
+        id="name.gitlab"
+        onFieldChange={onFieldChange}
+        propKey="key"
+        value={formData.key}
+      />
       <AlmBindingDefinitionFormField
         help={
           <>
@@ -57,7 +52,6 @@ export default function GitlabForm(props: GitlabFormProps) {
         maxLength={2000}
         onFieldChange={onFieldChange}
         propKey="url"
-        readOnly={readOnly}
         value={formData.url || ''}
       />
       <AlmBindingDefinitionFormField
@@ -67,7 +61,6 @@ export default function GitlabForm(props: GitlabFormProps) {
         onFieldChange={onFieldChange}
         overwriteOnly={Boolean(formData.key)}
         propKey="personalAccessToken"
-        readOnly={readOnly}
         value={formData.personalAccessToken}
       />
     </>
index 64c7673ffa86d2d4f6ea7ce5daaea02cd25926c5..6c2d2acb59f1160a30b17542d3b3fd77fbc41925 100644 (file)
@@ -135,10 +135,6 @@ it('should (dis)allow submit by validating its state', () => {
   wrapper.setState({ formData: mockGithubBindingDefinition(), touched: true });
   expect(wrapper.instance().canSubmit()).toBe(true);
 
-  wrapper.setState({ formData: mockGithubBindingDefinition({ key: '' }), touched: true });
-  wrapper.setProps({ hideKeyField: true });
-  expect(wrapper.instance().canSubmit()).toBe(true);
-
   wrapper.setState({ formData: mockGithubBindingDefinition({ url: '' }), touched: true });
   wrapper.setProps({ optionalFields: ['url'] });
   expect(wrapper.instance().canSubmit()).toBe(true);
index 242e6293cc3bd5d7ba2aa99dc8b71e1cc8698fcb..8d35c9a6bb8415fa2d466c08ba9f6521000ceaf1 100644 (file)
@@ -58,7 +58,6 @@ it('should render correctly for single-ALM binding', () => {
 it('should render correctly with validation', () => {
   const githubProps = {
     alm: AlmKeys.GitHub,
-    defaultBinding: mockGithubBindingDefinition(),
     definitions: [mockGithubBindingDefinition()]
   };
   expect(shallowRender(githubProps)).toMatchSnapshot('default');
@@ -89,7 +88,6 @@ it('should render correctly with validation', () => {
 
 function shallowRenderAzure(props: Partial<AlmTabRendererProps<AzureBindingDefinition>> = {}) {
   return shallowRender({
-    defaultBinding: mockAzureBindingDefinition(),
     definitions: [mockAzureBindingDefinition()],
     ...props
   });
@@ -102,7 +100,6 @@ function shallowRender<B extends AlmBindingDefinition>(
     <AlmTabRenderer
       alm={AlmKeys.Azure}
       branchesEnabled={true}
-      defaultBinding={{} as any}
       definitions={[]}
       definitionStatus={{}}
       form={jest.fn()}
index 246cfcb1745ea2159521ed5c3d11b4eac4c3c98b..ccc7252b6919d88924a9adbc789c1019bd795762 100644 (file)
@@ -17,7 +17,6 @@ exports[`should render correctly: default 1`] = `
   </label>
   <input
     className="input-super-large"
-    disabled={false}
     id="key"
     maxLength={40}
     name="key"
@@ -41,7 +40,6 @@ exports[`should render correctly: optional 1`] = `
   </label>
   <input
     className="input-super-large"
-    disabled={false}
     id="key"
     maxLength={40}
     name="key"
@@ -98,7 +96,6 @@ exports[`should render correctly: textarea 1`] = `
   </label>
   <textarea
     className="settings-large-input"
-    disabled={false}
     id="key"
     maxLength={40}
     onChange={[Function]}
@@ -131,7 +128,6 @@ exports[`should render correctly: with help 1`] = `
   </label>
   <input
     className="input-super-large"
-    disabled={false}
     id="key"
     maxLength={40}
     name="key"
index 676086c4f56338120f6403920220b01c20c04794..946d72f6a035da0efedd035428357ce10009c240 100644 (file)
@@ -38,6 +38,7 @@ import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
 import org.sonar.server.ws.WsActionTester;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -190,6 +191,29 @@ public class ValidateActionTest {
       .execute()).isInstanceOf(IllegalArgumentException.class).hasMessage("Invalid Azure URL or Personal Access Token");
   }
 
+  @Test
+  public void bitbucketcloud_validation_checks() {
+    AlmSettingDto almSetting = insertAlmSetting(db.almSettings().insertBitbucketCloudAlmSetting());
+
+    ws.newRequest()
+      .setParam("key", almSetting.getKey())
+      .execute();
+
+    verify(bitbucketCloudRestClient).validate(almSetting.getClientId(), almSetting.getClientSecret(), almSetting.getAppId());
+  }
+
+  @Test
+  public void bitbucketcloud_validation_check_fails() {
+    AlmSettingDto almSetting = insertAlmSetting(db.almSettings().insertBitbucketCloudAlmSetting());
+
+    doThrow(IllegalArgumentException.class)
+      .when(bitbucketCloudRestClient).validate(almSetting.getClientId(), almSetting.getClientSecret(), almSetting.getAppId());
+
+    TestRequest request = ws.newRequest()
+      .setParam("key", almSetting.getKey());
+    assertThatThrownBy(request::execute).isInstanceOf(IllegalArgumentException.class);
+  }
+
   private AlmSettingDto insertAlmSetting(AlmSettingDto almSettingDto) {
     UserDto user = db.users().insertUser();
     userSession.logIn(user).setSystemAdministrator();