@ServerSide
public interface OrganizationAlmBinding {
- void bindOrganization(DbSession dbSession, OrganizationDto organization, String installationId, boolean enableMembersSync);
+ void bindOrganization(DbSession dbSession, OrganizationDto organization, String installationId, boolean isNewOrganization);
}
import { Alert } from '../../../components/ui/Alert';
import { SubmitButton } from '../../../components/ui/buttons';
import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { isGithub } from '../../../helpers/almIntegrations';
interface Props {
almKey: string;
};
render() {
+ const { almKey } = this.props;
const { organization, submitting } = this.state;
return (
<form id="bind-organization-form" onSubmit={this.handleSubmit}>
organization={organization}
organizations={this.props.unboundOrganizations}
/>
- <Alert className="abs-width-400 big-spacer-top" display="block" variant="info">
- {translateWithParameters(
- 'onboarding.import_organization.bind_members_not_sync_info_x',
- translate('organization', this.props.almKey)
- )}
- <Link
- className="spacer-left"
- target="_blank"
- to={{ pathname: '/documentation/organizations/manage-team/' }}>
- {translate('learn_more')}
- </Link>
- </Alert>
+ {isGithub(almKey) && (
+ <Alert className="abs-width-400 big-spacer-top" display="block" variant="info">
+ {translateWithParameters(
+ 'onboarding.import_organization.bind_members_not_sync_info_x',
+ translate('organization', almKey)
+ )}
+ <Link
+ className="spacer-left"
+ target="_blank"
+ to={{ pathname: '/documentation/organizations/manage-team/' }}>
+ {translate('learn_more')}
+ </Link>
+ </Alert>
+ )}
<div className="display-flex-center big-spacer-top">
<SubmitButton disabled={submitting || !organization}>
{translate('onboarding.import_organization.bind')}
import { DeleteButton } from '../../../components/ui/buttons';
import RadioToggle from '../../../components/controls/RadioToggle';
import { bindAlmOrganization } from '../../../api/alm-integration';
-import { sanitizeAlmId, getAlmMembersUrl } from '../../../helpers/almIntegrations';
+import { sanitizeAlmId, getAlmMembersUrl, isGithub } from '../../../helpers/almIntegrations';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { getBaseUrl } from '../../../helpers/urls';
{filter === Filters.Create && (
<OrganizationDetailsForm
infoBlock={
- <Alert className="abs-width-600 big-spacer-top" display="block" variant="info">
- <p>
- {translateWithParameters(
- 'onboarding.import_organization.members_sync_info_x',
- translate('organization', almKey),
- almOrganization.name,
- translate(almKey)
- )}
- </p>
- <a
- href={getAlmMembersUrl(almApplication.key, almOrganization.almUrl)}
- rel="noopener noreferrer"
- target="_blank">
- {translateWithParameters(
- 'organization.members.see_all_members_on_x',
- translate(almKey)
- )}
- </a>
- </Alert>
+ isGithub(almKey) && (
+ <Alert className="abs-width-600 big-spacer-top" display="block" variant="info">
+ <p>
+ {translateWithParameters(
+ 'onboarding.import_organization.members_sync_info_x',
+ translate('organization', almKey),
+ almOrganization.name,
+ translate(almKey)
+ )}
+ </p>
+ <a
+ href={getAlmMembersUrl(almApplication.key, almOrganization.almUrl)}
+ rel="noopener noreferrer"
+ target="_blank">
+ {translateWithParameters(
+ 'organization.members.see_all_members_on_x',
+ translate(almKey)
+ )}
+ </a>
+ </Alert>
+ )
}
onContinue={this.props.handleOrgDetailsFinish}
organization={almOrganization}
expect(onBindOrganization).toHaveBeenCalled();
});
+it('should not show member sync info box for Bitbucket', () => {
+ expect(
+ shallowRender({ almKey: 'bitbucket' })
+ .find('Alert')
+ .exists()
+ ).toBe(false);
+});
+
function shallowRender(props: Partial<AutoOrganizationBind['props']> = {}) {
return shallow(
<AutoOrganizationBind
it('should render prefilled and create org', async () => {
const createOrganization = jest.fn().mockResolvedValue({ key: 'foo' });
const handleOrgDetailsFinish = jest.fn();
- const almApplication = mockAlmApplication({ key: 'github' });
const almOrganization = mockAlmOrganization({ almUrl: 'http://github.com/thing' });
const wrapper = shallowRender({
- almApplication,
almOrganization,
createOrganization,
handleOrgDetailsFinish
expect(onOrgCreated).toHaveBeenCalledWith('foo');
});
+it('should not show member sync info box for Bitbucket', () => {
+ expect(
+ shallowRender({ almApplication: mockAlmApplication({ key: 'bitbucket-cloud' }) })
+ .find('Alert')
+ .exists()
+ ).toBe(false);
+});
+
function shallowRender(props: Partial<AutoOrganizationCreate['props']> = {}) {
return shallow(
<AutoOrganizationCreate
values={
Object {
"avatar": <img
- alt="BitBucket"
+ alt="GitHub"
className="little-spacer-left"
- src="/images/sonarcloud/bitbucket.svg"
+ src="/images/sonarcloud/github.svg"
width={16}
/>,
"name": <strong>
<PlanStep
almApplication={
Object {
- "backgroundColor": "#0052CC",
- "iconPath": "\\"/static/authbitbucket/bitbucket.svg\\"",
- "installationUrl": "https://bitbucket.org/install/app",
- "key": "bitbucket",
- "name": "BitBucket",
+ "backgroundColor": "#444444",
+ "iconPath": "/images/sonarcloud/github-white.svg",
+ "installationUrl": "https://github.com/apps/greg-sonarcloud/installations/new",
+ "key": "github",
+ "name": "GitHub",
}
}
almOrganization={
values={
Object {
"avatar": <img
- alt="BitBucket"
+ alt="GitHub"
className="little-spacer-left"
src="/images/sonarcloud/github.svg"
width={16}
<PlanStep
almApplication={
Object {
- "backgroundColor": "#0052CC",
- "iconPath": "\\"/static/authbitbucket/bitbucket.svg\\"",
- "installationUrl": "https://bitbucket.org/install/app",
+ "backgroundColor": "#444444",
+ "iconPath": "/images/sonarcloud/github-white.svg",
+ "installationUrl": "https://github.com/apps/greg-sonarcloud/installations/new",
"key": "github",
- "name": "BitBucket",
+ "name": "GitHub",
}
}
almOrganization={
import SyncMemberForm from './SyncMemberForm';
import DeferredSpinner from '../../components/common/DeferredSpinner';
import DocTooltip from '../../components/docs/DocTooltip';
-import { sanitizeAlmId } from '../../helpers/almIntegrations';
+import { sanitizeAlmId, isGithub } from '../../helpers/almIntegrations';
import { translate, translateWithParameters } from '../../helpers/l10n';
import { Alert } from '../../components/ui/Alert';
{isAdmin && (
<div className="page-actions text-right">
{almKey &&
+ isGithub(almKey) &&
!showSyncNotif && (
<SyncMemberForm organization={organization} refreshMembers={refreshMembers} />
)}
}}
/>
{almKey &&
+ isGithub(almKey) &&
showSyncNotif && (
<Alert className="spacer-top" display="inline" variant="info">
{translateWithParameters(
import { Alert } from '../../components/ui/Alert';
import { Button } from '../../components/ui/buttons';
import { setOrganizationMemberSync, syncMembers } from '../../api/organizations';
-import { sanitizeAlmId, isGithub } from '../../helpers/almIntegrations';
+import { sanitizeAlmId } from '../../helpers/almIntegrations';
import { translate, translateWithParameters } from '../../helpers/l10n';
import { fetchOrganization } from '../../store/rootActions';
enabled: membersSync
}).then(() => {
this.props.fetchOrganization(organization.key);
- if (membersSync && isGithub(organization.alm && organization.alm.key)) {
+ if (membersSync) {
return this.handleMemberSync();
}
return Promise.resolve();
).toMatchSnapshot();
});
-it('should render for bound organization without sync', () => {
+it('should render for Bitbucket bound organization', () => {
+ const organization = mockOrganizationWithAlm(mockOrganizationWithAdminActions(), {
+ key: 'bitbucket'
+ });
+ expect(shallowRender({ organization })).toMatchSnapshot();
+});
+
+it('should render for GitHub bound organization without sync', () => {
const organization = mockOrganizationWithAlm(mockOrganizationWithAdminActions());
expect(shallowRender({ organization })).toMatchSnapshot();
});
jest.clearAllMocks();
});
-it('should allow to switch to automatic mode with github', async () => {
+it('should allow to switch to automatic mode', async () => {
const fetchOrganization = jest.fn();
const refreshMembers = jest.fn().mockResolvedValue({});
const wrapper = shallowRender({ fetchOrganization, refreshMembers });
expect(refreshMembers).toBeCalled();
});
-it('should allow to switch to automatic mode with bitbucket', async () => {
- const fetchOrganization = jest.fn();
- const wrapper = shallowRender({
- fetchOrganization,
- organization: mockOrganizationWithAlm({}, { key: 'bitbucket' })
- });
- expect(wrapper).toMatchSnapshot();
-
- wrapper.setState({ membersSync: true });
- wrapper.find('ConfirmButton').prop<Function>('onConfirm')();
- expect(setOrganizationMemberSync).toHaveBeenCalledWith({ organization: 'foo', enabled: true });
-
- await waitAndUpdate(wrapper);
- expect(fetchOrganization).toHaveBeenCalledWith('foo');
- expect(syncMembers).not.toHaveBeenCalled();
-});
-
it('should allow to switch to manual mode', async () => {
const fetchOrganization = jest.fn();
const wrapper = shallowRender({
</header>
`;
-exports[`should render for admin 1`] = `
+exports[`should render for Bitbucket bound organization 1`] = `
<header
className="page-header"
>
"actions": Object {
"admin": true,
},
+ "alm": Object {
+ "key": "bitbucket",
+ "membersSync": false,
+ "url": "https://github.com/foo",
+ },
"key": "foo",
"name": "Foo",
}
</header>
`;
-exports[`should render for bound organization without sync 1`] = `
+exports[`should render for GitHub bound organization without sync 1`] = `
<header
className="page-header"
>
</div>
</header>
`;
+
+exports[`should render for admin 1`] = `
+<header
+ className="page-header"
+>
+ <h1
+ className="page-title"
+ >
+ organization.members.page
+ </h1>
+ <DeferredSpinner
+ loading={false}
+ timeout={100}
+ />
+ <div
+ className="page-actions text-right"
+ >
+ <div
+ className="display-inline-block spacer-left spacer-bottom"
+ >
+ <AddMemberForm
+ addMember={[MockFunction]}
+ memberLogins={Array []}
+ organization={
+ Object {
+ "actions": Object {
+ "admin": true,
+ },
+ "key": "foo",
+ "name": "Foo",
+ }
+ }
+ />
+ <DocTooltip
+ className="spacer-left"
+ doc={Promise {}}
+ />
+ </div>
+ </div>
+ <div
+ className="page-description"
+ >
+ <FormattedMessage
+ defaultMessage="organization.members.page.description"
+ id="organization.members.page.description"
+ values={
+ Object {
+ "link": <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ target="_blank"
+ to="/documentation/organizations/manage-team/"
+ >
+ organization.members.manage_a_team
+ </Link>,
+ }
+ }
+ />
+ </div>
+</header>
+`;
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`should allow to switch to automatic mode with bitbucket 1`] = `
-<ConfirmButton
- cancelButtonText="close"
- confirmButtonText="save"
- confirmDisable={true}
- modalBody={
- <div
- className="display-flex-stretch big-spacer-top"
- >
- <RadioCard
- onClick={[Function]}
- selected={true}
- title="organization.members.management.manual"
- >
- <div
- className="spacer-left"
- >
- <ul
- className="big-spacer-left note"
- >
- <li
- className="spacer-bottom"
- >
- organization.members.management.manual.add_members_manually
- </li>
- <li>
- organization.members.management.choose_members_permissions
- </li>
- </ul>
- </div>
- </RadioCard>
- <RadioCard
- onClick={[Function]}
- selected={false}
- title="organization.members.management.automatic.bitbucket"
- >
- <div
- className="spacer-left"
- >
- <ul
- className="big-spacer-left note"
- >
- <React.Fragment>
- <li
- className="spacer-bottom"
- >
- organization.members.management.automatic.synchronized_from_x.organization.bitbucket
- </li>
- <li
- className="spacer-bottom"
- >
- organization.members.management.automatic.members_changes_reflected.bitbucket
- </li>
- </React.Fragment>
- <li>
- organization.members.management.choose_members_permissions
- </li>
- </ul>
- </div>
- <Alert
- className="big-spacer-top"
- variant="warning"
- >
- organization.members.management.automatic.warning
- </Alert>
- </RadioCard>
- </div>
- }
- modalHeader="organization.members.management.title"
- modalHeaderDescription={
- <p
- className="spacer-top"
- >
- organization.members.management.description
- <Link
- className="spacer-left"
- onlyActiveOnIndex={false}
- style={Object {}}
- target="_blank"
- to={
- Object {
- "pathname": "/documentation/organizations/manage-team/",
- }
- }
- >
- learn_more
- </Link>
- </p>
- }
- onConfirm={[Function]}
- size="medium"
->
- <Component />
-</ConfirmButton>
-`;
-
-exports[`should allow to switch to automatic mode with github 1`] = `
+exports[`should allow to switch to automatic mode 1`] = `
<ConfirmButton
cancelButtonText="close"
confirmButtonText="save"
export function mockAlmApplication(overrides: Partial<T.AlmApplication> = {}): T.AlmApplication {
return {
- backgroundColor: '#0052CC',
- iconPath: '"/static/authbitbucket/bitbucket.svg"',
- installationUrl: 'https://bitbucket.org/install/app',
- key: 'bitbucket',
- name: 'BitBucket',
+ backgroundColor: '#444444',
+ iconPath: '/images/sonarcloud/github-white.svg',
+ installationUrl: 'https://github.com/apps/greg-sonarcloud/installations/new',
+ key: 'github',
+ name: 'GitHub',
...overrides
};
}
organization.members.config_synchro=Configure Synchronization
organization.members.auto_sync_with_x=Automatic sync with {0}
organization.members.auto_sync_members_from_org_x=Now your members can be automatically synchronized with your {0}.
-organization.members.auto_sync_total_help.bitbucket=You might not see all members from your Bitbucket team yet, as they need to reconnect to SonarCloud to be members of the organization.
organization.members.auto_sync_total_help.github=You might not see all members from your GitHub organization yet, as they need to connect to SonarCloud at least once to appear in this list.
organization.members.see_all_members_on_x=See all members on {0}
organization.members.management.title=Members Management
organization.members.management.manual.add_members_manually=Admin add members manually from SonarCloud existing users
organization.members.management.automatic=Automatic sync with {0}
organization.members.management.automatic.synchronized_from_x=Members are synchronized automatically from your {0}
-organization.members.management.automatic.members_changes_reflected.bitbucket=Your team members must reconnect to SonarCloud to be automatically added to correct SonarCloud organization
organization.members.management.automatic.members_changes_reflected.github=If you add or remove a member on GitHub, SonarCloud immediately reflects the changes
organization.members.management.automatic.warning=This will override your current Members and Permissions configuration
organization.members.management.choose_members_permissions=Admin manages permissions for each member in SonarCloud