@Test
public void testNotFound() {
Navigation nav = Navigation.get(orchestrator);
- nav.open("/organizations/test-org/quality_profiles/show?key=unknown");
+ nav.open("/organizations/" + ORGANIZATION + "/quality_profiles/show?key=unknown");
+ $(".quality-profile-not-found").should(Condition.visible);
+
+ nav.open("/organizations/" + ORGANIZATION + "/quality_profiles/show?language=xoo&name=unknown");
$(".quality-profile-not-found").should(Condition.visible);
}
@Test
public void testNotFound() {
Navigation nav = Navigation.get(orchestrator);
+
nav.open("/profiles/show?key=unknown");
$(".quality-profile-not-found").should(Condition.visible);
+
+ nav.open("/profiles/show?language=xoo&name=unknown");
+ $(".quality-profile-not-found").should(Condition.visible);
}
@Test
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { stringify } from 'querystring';
import $ from 'jquery';
import { sortBy } from 'lodash';
import Backbone from 'backbone';
});
},
- getProfilePath(profileKey) {
+ getProfilePath(language, name) {
const { organization } = this.options.app;
- const encodedKey = encodeURIComponent(profileKey);
+ const query = stringify({ language, name });
return organization
- ? `${window.baseUrl}/organizations/${organization}/quality_profiles/show?key=${encodedKey}`
- : `${window.baseUrl}/profiles/show?key=${encodedKey}`;
+ ? `${window.baseUrl}/organizations/${organization}/quality_profiles/show?${query}`
+ : `${window.baseUrl}/profiles/show?${query}`;
},
serializeData() {
parameters: this.enhanceParameters(),
templateKey: this.options.rule.get('templateKey'),
isTemplate: this.options.rule.get('isTemplate'),
- profilePath: this.getProfilePath(this.model.get('key')),
+ profilePath: this.getProfilePath(this.model.get('lang'), this.model.get('name')),
parentProfilePath: parent && this.getProfilePath(parent.key)
};
}
const languageName = languageFromStore ? languageFromStore.name : profile.language;
const path = this.props.customOrganizations
- ? getQualityProfileUrl(profile.key, this.props.component.organization)
- : getQualityProfileUrl(profile.key);
+ ? getQualityProfileUrl(profile.name, profile.language, this.props.component.organization)
+ : getQualityProfileUrl(profile.name, profile.language);
const inner = (
<div className="text-ellipsis">
}
handleFromDateChange = (fromDate?: string) => {
- const path = getProfileChangelogPath(this.props.profile.key, this.props.organization, {
- since: fromDate,
- to: this.props.location.query.to
- });
+ const path = getProfileChangelogPath(
+ this.props.profile.name,
+ this.props.profile.language,
+ this.props.organization,
+ {
+ since: fromDate,
+ to: this.props.location.query.to
+ }
+ );
this.context.router.push(path);
};
handleToDateChange = (toDate?: string) => {
- const path = getProfileChangelogPath(this.props.profile.key, this.props.organization, {
- since: this.props.location.query.since,
- to: toDate
- });
+ const path = getProfileChangelogPath(
+ this.props.profile.name,
+ this.props.profile.language,
+ this.props.organization,
+ {
+ since: this.props.location.query.since,
+ to: toDate
+ }
+ );
this.context.router.push(path);
};
handleReset = () => {
- const path = getProfileChangelogPath(this.props.profile.key, this.props.organization);
+ const path = getProfileChangelogPath(
+ this.props.profile.name,
+ this.props.profile.language,
+ this.props.organization
+ );
this.context.router.push(path);
};
}
handleCompare = (withKey: string) => {
- const path = getProfileComparePath(this.props.profile.key, this.props.organization, withKey);
+ const path = getProfileComparePath(
+ this.props.profile.name,
+ this.props.profile.language,
+ this.props.organization,
+ withKey
+ );
this.context.router.push(path);
};
type Props = {
canAdmin: boolean,
+ fromList: boolean,
organization: ?string,
profile: Profile,
updateProfiles: () => Promise<*>
export default class ProfileActions extends React.PureComponent {
props: Props;
+ static defaultProps = {
+ fromList: false
+ };
+
static contextTypes = {
router: React.PropTypes.object
};
handleRenameClick = (e: SyntheticInputEvent) => {
e.preventDefault();
new RenameProfileView({ profile: this.props.profile })
- .on('done', () => this.props.updateProfiles())
+ .on('done', (newName: string) => {
+ this.props.updateProfiles().then(() => {
+ if (!this.props.fromList) {
+ this.context.router.replace(
+ getProfilePath(newName, this.props.profile.language, this.props.organization)
+ );
+ }
+ });
+ })
.render();
};
new CopyProfileView({ profile: this.props.profile })
.on('done', profile => {
this.props.updateProfiles().then(() => {
- this.context.router.push(getProfilePath(profile.key, this.props.organization));
+ this.context.router.push(
+ getProfilePath(profile.name, profile.language, this.props.organization)
+ );
});
})
.render();
</li>
<li>
<Link
- to={getProfileComparePath(profile.key, this.props.organization)}
+ to={getProfileComparePath(profile.name, profile.language, this.props.organization)}
id="quality-profile-compare">
{translate('compare')}
</Link>
type Props = {
canAdmin: boolean,
children: React.Element<*>,
- location: { query: { key: string } },
+ location: {
+ pathname: string,
+ query: { key?: string, language: string, name: string }
+ },
organization: ?string,
profiles: Array<Profile>,
+ router: { replace: () => void },
updateProfiles: () => Promise<*>
};
export default class ProfileContainer extends React.PureComponent {
props: Props;
+ componentDidMount() {
+ const { location, profiles, router } = this.props;
+ if (location.query.key) {
+ // try to find a quality profile with the given key
+ // if managed to find one, redirect to a new version
+ // otherwise do nothing, `render` will show not found page
+ const profile = profiles.find(profile => profile.key === location.query.key);
+ if (profile) {
+ router.replace({
+ pathname: location.pathname,
+ query: { language: profile.language, name: profile.name }
+ });
+ }
+ }
+ }
+
render() {
const { organization, profiles, location, ...other } = this.props;
- const { key } = location.query;
- const profile = profiles.find(profile => profile.key === key);
+ const { key, language, name } = location.query;
+
+ if (key) {
+ // if there is a `key` parameter,
+ // then if we managed to find a quality profile with this key
+ // then we will be redirected in `componentDidMount`
+ // otherwise show `ProfileNotFound`
+ const profile = profiles.find(profile => profile.key === location.query.key);
+ return profile ? null : <ProfileNotFound organization={organization} />;
+ }
+
+ const profile = profiles.find(
+ profile => profile.language === language && profile.name === name
+ );
if (!profile) {
return <ProfileNotFound organization={organization} />;
type Props = {
children?: React.Element<*>,
- organization: ?string,
- profileKey: string
+ language: string,
+ name: string,
+ organization: ?string
};
export default class ProfileLink extends React.PureComponent {
props: Props;
render() {
- const { profileKey, organization, children, ...other } = this.props;
+ const { name, language, organization, children, ...other } = this.props;
return (
<Link
- to={getProfilePath(profileKey, organization)}
+ to={getProfilePath(name, language, organization)}
activeClassName="link-no-underline"
{...other}>
{children}
import { createFakeProfile } from '../../utils';
it('should render ProfileHeader', () => {
- const targetProfile = createFakeProfile({ key: 'profile1' });
- const profiles = [targetProfile, createFakeProfile({ key: 'profile2' })];
+ const targetProfile = createFakeProfile({ language: 'js', name: 'fake' });
+ const profiles = [targetProfile, createFakeProfile({ language: 'js', name: 'another' })];
const updateProfiles = jest.fn();
const output = shallow(
<ProfileContainer
- location={{ query: { key: 'profile1' } }}
+ location={{ query: { language: 'js', name: 'fake' } }}
profiles={profiles}
canAdmin={false}
updateProfiles={updateProfiles}>
});
it('should render ProfileNotFound', () => {
- const profiles = [createFakeProfile({ key: 'profile1' }), createFakeProfile({ key: 'profile2' })];
+ const profiles = [
+ createFakeProfile({ language: 'js', name: 'fake' }),
+ createFakeProfile({ language: 'js', name: 'another' })
+ ];
const output = shallow(
<ProfileContainer
- location={{ query: { key: 'random' } }}
+ location={{ query: { language: 'js', name: 'random' } }}
profiles={profiles}
canAdmin={false}
updateProfiles={() => true}>
});
it('should render Helmet', () => {
- const profiles = [createFakeProfile({ key: 'profile1', name: 'First Profile' })];
+ const profiles = [createFakeProfile({ language: 'js', name: 'First Profile' })];
const updateProfiles = jest.fn();
const output = shallow(
<ProfileContainer
- location={{ query: { key: 'profile1' } }}
+ location={{ query: { language: 'js', name: 'First Profile' } }}
profiles={profiles}
canAdmin={false}
updateProfiles={updateProfiles}>
<h1 className="page-title">
<ProfileLink
- organization={organization}
- profileKey={profile.key}
- className="link-base-color">
+ className="link-base-color"
+ language={profile.language}
+ name={profile.name}
+ organization={organization}>
<span>{profile.name}</span>
</ProfileLink>
</h1>
{this.renderUpdateDate()}
{this.renderUsageDate()}
<li>
- <Link to={getProfileChangelogPath(profile.key, organization)} className="button">
+ <Link
+ to={getProfileChangelogPath(profile.name, profile.language, organization)}
+ className="button">
{translate('changelog')}
</Link>
</li>
updateProfiles: () => Promise<*>
};
+type ProfileInheritanceDetails = {
+ activeRuleCount: number,
+ key: string,
+ language: string,
+ name: string,
+ overridingRuleCount?: number
+};
+
type State = {
- ancestors?: Array<{
- activeRuleCount: number,
- key: string,
- name: string,
- overridingRuleCount?: number
- }>,
- children?: Array<{
- activeRuleCount: number,
- key: string,
- name: string,
- overridingRuleCount?: number
- }>,
+ ancestors?: Array<ProfileInheritanceDetails>,
+ children?: Array<ProfileInheritanceDetails>,
loading: boolean,
- profile?: {
- activeRuleCount: number,
- key: string,
- name: string,
- overridingRuleCount?: number
- }
+ profile?: ProfileInheritanceDetails
};
export default class ProfileInheritance extends React.PureComponent {
className="js-inheritance-ancestor"
depth={index}
key={ancestor.key}
+ language={this.props.profile.language}
organization={this.props.organization}
profile={ancestor}
/>
className={currentClassName}
depth={this.state.ancestors ? this.state.ancestors.length : 0}
displayLink={false}
+ language={this.props.profile.language}
organization={this.props.organization}
profile={this.state.profile}
/>
className="js-inheritance-child"
depth={this.state.ancestors ? this.state.ancestors.length + 1 : 0}
key={child.key}
+ language={this.props.profile.language}
organization={this.props.organization}
profile={child}
/>
className?: string,
depth: number,
displayLink?: boolean,
+ language: string,
organization: ?string,
profile: {
activeRuleCount: number,
key: string,
+ language: string,
name: string,
overridingRuleCount?: number
}
<td>
<div style={{ paddingLeft: offset }}>
{this.props.displayLink
- ? <ProfileLink organization={this.props.organization} profileKey={profile.key}>
+ ? <ProfileLink
+ language={this.props.language}
+ name={profile.name}
+ organization={this.props.organization}>
{profile.name}
</ProfileLink>
: profile.name}
<li key={profile.key} className="spacer-top">
<div className="text-ellipsis">
<ProfileLink
- organization={this.props.organization}
- profileKey={profile.key}
- className="link-no-underline">
+ className="link-no-underline"
+ language={profile.language}
+ name={profile.name}
+ organization={this.props.organization}>
{profile.name}
</ProfileLink>
</div>
<li key={profile.key} className="spacer-top">
<div className="text-ellipsis">
<ProfileLink
- organization={this.props.organization}
- profileKey={profile.key}
- className="link-no-underline">
+ className="link-no-underline"
+ language={profile.language}
+ name={profile.name}
+ organization={this.props.organization}>
{profile.name}
</ProfileLink>
</div>
})
.on('done', profile => {
this.props.updateProfiles().then(() => {
- this.context.router.push(getProfilePath(profile.key, this.props.organization));
+ this.context.router.push(
+ getProfilePath(profile.name, profile.language, this.props.organization)
+ );
});
})
.render();
const offset = 25 * (profile.depth - 1);
return (
<div style={{ paddingLeft: offset }}>
- <ProfileLink organization={this.props.organization} profileKey={profile.key}>
+ <ProfileLink
+ language={profile.language}
+ name={profile.name}
+ organization={this.props.organization}>
{profile.name}
</ProfileLink>
</div>
</button>
<ProfileActions
canAdmin={this.props.canAdmin}
+ fromList={true}
organization={this.props.organization}
profile={this.props.profile}
updateProfiles={this.props.updateProfiles}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { withRouter } from 'react-router';
+
const routes = [
{
getComponent(state, callback) {
{
getComponent(_, callback) {
require.ensure([], require => {
- callback(null, require('./components/ProfileContainer').default);
+ callback(null, withRouter(require('./components/ProfileContainer').default));
});
},
childRoutes: [
organization ? `/organizations/${organization}/quality_profiles` : '/profiles';
export const getProfilesForLanguagePath = (language: string, organization: ?string) => ({
- pathname: organization ? `/organizations/${organization}/quality_profiles` : '/profiles',
+ pathname: getProfilesPath(organization),
query: { language }
});
-export const getProfilePath = (profile: string, organization: ?string) => ({
- pathname: organization
- ? `/organizations/${organization}/quality_profiles/show`
- : '/profiles/show',
- query: { key: profile }
+export const getProfilePath = (name: string, language: string, organization: ?string) => ({
+ pathname: getProfilesPath(organization) + '/show',
+ query: { name, language }
});
-export const getProfileComparePath = (profile: string, organization: ?string, withKey?: string) => {
- const query: Object = { key: profile };
+export const getProfileComparePath = (
+ name: string,
+ language: string,
+ organization: ?string,
+ withKey?: string
+) => {
+ const query: Object = { language, name };
if (withKey) {
Object.assign(query, { withKey });
}
return {
- pathname: organization
- ? `/organizations/${organization}/quality_profiles/compare`
- : '/profiles/compare',
+ pathname: getProfilesPath(organization) + '/compare',
query
};
};
export const getProfileChangelogPath = (
- profile: string,
+ name: string,
+ language: string,
organization: ?string,
filter?: { since?: string, to?: string }
) => {
- const query: Object = { key: profile };
+ const query: Object = { language, name };
if (filter) {
if (filter.since) {
Object.assign(query, { since: filter.since });
}
}
return {
- pathname: organization
- ? `/organizations/${organization}/quality_profiles/changelog`
- : '/profiles/changelog',
+ pathname: getProfilesPath(organization) + '/changelog',
query
};
};
sendRequest() {
const name = this.$('#rename-profile-name').val();
renameProfile(this.options.profile.key, name)
- .then(profile => {
+ .then(() => {
this.destroy();
- this.trigger('done', profile);
+ this.trigger('done', name);
})
.catch(e => {
if (e.response.status === 400) {
/**
* Generate URL for a quality profile
- * @param {string} key
- * @returns {Object}
*/
-export function getQualityProfileUrl(key, organization) {
- return getProfilePath(key, organization);
+export function getQualityProfileUrl(name, language, organization) {
+ return getProfilePath(name, language, organization);
}
/**