From: Stas Vilchik Date: Thu, 30 Mar 2017 12:09:03 +0000 (+0200) Subject: SONAR-8927 Change format of quality profile permalinks X-Git-Tag: 6.4-RC1~477 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=2c890d852dd6c355a64679e1d603c17bfac29bc5;p=sonarqube.git SONAR-8927 Change format of quality profile permalinks --- diff --git a/it/it-tests/src/test/java/it/organization/OrganizationQualityProfilesPageTest.java b/it/it-tests/src/test/java/it/organization/OrganizationQualityProfilesPageTest.java index 3cabbe545d0..f214c76456f 100644 --- a/it/it-tests/src/test/java/it/organization/OrganizationQualityProfilesPageTest.java +++ b/it/it-tests/src/test/java/it/organization/OrganizationQualityProfilesPageTest.java @@ -100,7 +100,10 @@ public class OrganizationQualityProfilesPageTest { @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); } diff --git a/it/it-tests/src/test/java/it/qualityProfile/QualityProfilesPageTest.java b/it/it-tests/src/test/java/it/qualityProfile/QualityProfilesPageTest.java index cd41ebf2e29..73e9902e4b5 100644 --- a/it/it-tests/src/test/java/it/qualityProfile/QualityProfilesPageTest.java +++ b/it/it-tests/src/test/java/it/qualityProfile/QualityProfilesPageTest.java @@ -85,8 +85,12 @@ public class QualityProfilesPageTest { @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 diff --git a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profile-view.js b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profile-view.js index 8120484ae41..1f03259a1a8 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profile-view.js +++ b/server/sonar-web/src/main/js/apps/coding-rules/rule/rule-profile-view.js @@ -17,6 +17,7 @@ * 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'; @@ -150,12 +151,12 @@ export default Marionette.ItemView.extend({ }); }, - 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() { @@ -168,7 +169,7 @@ export default Marionette.ItemView.extend({ 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) }; } diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js b/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js index fb5c176df85..13484c3ca0a 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js +++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js @@ -85,8 +85,8 @@ class MetaQualityProfiles extends React.Component { 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 = (
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.js index a1fe68f50af..119c4bd52be 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.js @@ -125,23 +125,37 @@ export default class ChangelogContainer extends React.PureComponent { } 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); }; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.js index 6428c060684..24fba98b452 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.js @@ -93,7 +93,12 @@ export default class ComparisonContainer extends React.PureComponent { } 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); }; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js index d1398b7f90e..aacbe90c428 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js @@ -31,6 +31,7 @@ import type { Profile } from '../propTypes'; type Props = { canAdmin: boolean, + fromList: boolean, organization: ?string, profile: Profile, updateProfiles: () => Promise<*> @@ -39,6 +40,10 @@ type Props = { export default class ProfileActions extends React.PureComponent { props: Props; + static defaultProps = { + fromList: false + }; + static contextTypes = { router: React.PropTypes.object }; @@ -46,7 +51,15 @@ export default class ProfileActions extends React.PureComponent { 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(); }; @@ -55,7 +68,9 @@ export default class ProfileActions extends React.PureComponent { 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(); @@ -107,7 +122,7 @@ export default class ProfileActions extends React.PureComponent {
  • {translate('compare')} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js index d55f57dd8fe..0ee269b6a25 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js @@ -28,19 +28,51 @@ import type { Profile } from '../propTypes'; 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, + 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 : ; + } + + const profile = profiles.find( + profile => profile.language === language && profile.name === name + ); if (!profile) { return ; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.js index a60c37ab9b1..0eb92cb0105 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.js @@ -24,18 +24,19 @@ import { getProfilePath } from '../utils'; 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 ( {children} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileContainer-test.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileContainer-test.js index c4073232a95..08aa98c6b32 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileContainer-test.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileContainer-test.js @@ -26,12 +26,12 @@ import ProfileHeader from '../../details/ProfileHeader'; 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( @@ -46,10 +46,13 @@ it('should render ProfileHeader', () => { }); 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( true}> @@ -60,11 +63,11 @@ it('should render ProfileNotFound', () => { }); 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( diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js index 3a06c63e472..5c66dfcfcac 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js @@ -108,9 +108,10 @@ export default class ProfileHeader extends React.PureComponent {

    + className="link-base-color" + language={profile.language} + name={profile.name} + organization={organization}> {profile.name}

    @@ -120,7 +121,9 @@ export default class ProfileHeader extends React.PureComponent { {this.renderUpdateDate()} {this.renderUsageDate()}
  • - + {translate('changelog')}
  • diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.js index f6760f674f5..ea38598bd65 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.js @@ -34,26 +34,19 @@ type Props = { 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, + children?: Array, loading: boolean, - profile?: { - activeRuleCount: number, - key: string, - name: string, - overridingRuleCount?: number - } + profile?: ProfileInheritanceDetails }; export default class ProfileInheritance extends React.PureComponent { @@ -129,6 +122,7 @@ 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} /> @@ -138,6 +132,7 @@ export default class ProfileInheritance extends React.PureComponent { 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} /> @@ -148,6 +143,7 @@ export default class ProfileInheritance extends React.PureComponent { 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} /> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritanceBox.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritanceBox.js index 5af9d91b207..6ae591d571c 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritanceBox.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritanceBox.js @@ -26,10 +26,12 @@ type Props = { className?: string, depth: number, displayLink?: boolean, + language: string, organization: ?string, profile: { activeRuleCount: number, key: string, + language: string, name: string, overridingRuleCount?: number } @@ -51,7 +53,10 @@ export default class ProfileInheritanceBox extends React.PureComponent {
    {this.props.displayLink - ? + ? {profile.name} : profile.name} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js index 89d9d87beab..037c1e9f16f 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js @@ -61,9 +61,10 @@ export default class EvolutionDeprecated extends React.PureComponent {
  • + className="link-no-underline" + language={profile.language} + name={profile.name} + organization={this.props.organization}> {profile.name}
    diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.js index 29679aca1ae..63f0ff7bdef 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.js @@ -55,9 +55,10 @@ export default class EvolutionStagnant extends React.PureComponent {
  • + className="link-no-underline" + language={profile.language} + name={profile.name} + organization={this.props.organization}> {profile.name}
    diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js index 48ff0383758..df6191a8300 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js @@ -73,7 +73,9 @@ export default class PageHeader extends React.PureComponent { }) .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(); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js index a38a5e9954f..8f3e21c793e 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js @@ -48,7 +48,10 @@ export default class ProfilesListRow extends React.PureComponent { const offset = 25 * (profile.depth - 1); return (
    - + {profile.name}
    @@ -161,6 +164,7 @@ export default class ProfilesListRow extends React.PureComponent { { - callback(null, require('./components/ProfileContainer').default); + callback(null, withRouter(require('./components/ProfileContainer').default)); }); }, childRoutes: [ diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/utils.js b/server/sonar-web/src/main/js/apps/quality-profiles/utils.js index 64bb0ea07d6..97ff96be524 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/utils.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/utils.js @@ -70,36 +70,38 @@ export const getProfilesPath = (organization: ?string) => 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 }); @@ -109,9 +111,7 @@ export const getProfileChangelogPath = ( } } return { - pathname: organization - ? `/organizations/${organization}/quality_profiles/changelog` - : '/profiles/changelog', + pathname: getProfilesPath(organization) + '/changelog', query }; }; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/RenameProfileView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/RenameProfileView.js index 308efa7c143..57e3e5c5e67 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/views/RenameProfileView.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/views/RenameProfileView.js @@ -33,9 +33,9 @@ export default ModalFormView.extend({ 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) { diff --git a/server/sonar-web/src/main/js/helpers/urls.js b/server/sonar-web/src/main/js/helpers/urls.js index 1464e336fb7..1d4097f95c7 100644 --- a/server/sonar-web/src/main/js/helpers/urls.js +++ b/server/sonar-web/src/main/js/helpers/urls.js @@ -91,11 +91,9 @@ export function getComponentPermissionsUrl(componentKey) { /** * 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); } /**