import React from 'react'; import { makeStyles } from '@material-ui/core/styles'; import Container from '@material-ui/core/Container'; import Typography from '@material-ui/core/Typography'; import Tabs from '@material-ui/core/Tabs'; import Tab from '@material-ui/core/Tab'; import Box from '@material-ui/core/Box'; import { Link } from '@material-ui/core'; import { Link as RouterLink } from 'react-router-dom'; import { useHistory } from "react-router-dom"; import { useRuleCoverage } from './utils/useRuleCoverage'; import { useFetch } from './utils/useFetch'; import { RuleMetadata } from './types'; const useStyles = makeStyles((theme) => ({ ruleBar: { borderBottom: '1px solid lightgrey', }, ruleid: { textAlign: 'center', marginTop: theme.spacing(3), marginBottom: theme.spacing(3), }, ruleidLink: { color: 'inherit', }, title: { textAlign: 'justify', marginTop: theme.spacing(4), marginBottom: theme.spacing(4), }, coverage: { marginBottom: theme.spacing(3), }, description: { textAlign: 'justify', // marginBottom: theme.spacing(3), }, // style used to center the tabs when there too few of them to fill the container tabRoot: { justifyContent: "center" }, tabScroller: { flexGrow: 0 }, unimplemented: { color: 'red' }, tab: { display: 'flex', "&::before": { content: '""', display: 'block', width: theme.spacing(1), height: theme.spacing(1), marginRight: theme.spacing(1), borderRadius: theme.spacing(1), }, '& > .MuiTab-wrapper': { width: 'auto', } }, tabCovered: { "&::before": { backgroundColor: '#4c9bd6', } }, tabTargeted: { "&::before": { backgroundColor: '#fd6a00', } }, })); const languageToJiraProject = new Map(Object.entries({ "PYTHON": "SONARPY", "ABAP": "SONARABAP", "CFAMILY": "CPP", "JAVA": "SONARJAVA", "COBOL": "SONARCOBOL", "FLEX": "SONARFLEX", "HTML": "SONARHTML", "PHP": "SONARPHP", "PLI": "SONARPLI", "PLSQL": "SONARPLSQL", "RPG": "SONARRPG", "APEX": "SONARSLANG", "RUBY": "SONARSLANG", "KOTLIN": "SONARKT", "SCALA": "SONARSLANG", "GO": "SONARSLANG", "SECRETS": "SECRETS", "SWIFT": "SONARSWIFT", "TSQL": "SONARTSQL", "VB6": "SONARVBSIX", "XML": "SONARXML", "CLOUDFORMATION": "SONARIAC", "TERRAFORM": "SONARIAC", })); const languageToGithubProject = new Map(Object.entries({ "ABAP": "sonar-abap", "CSHARP": "sonar-dotnet", "VBNET": "sonar-dotnet", "JAVASCRIPT": "SonarJS", "TYPESCRIPT": "SonarJS", "SWIFT": "sonar-swift", "KOTLIN": "sonar-kotlin", "GO": "slang-enterprise", "SCALA": "slang-enterprise", "RUBY": "slang-enterprise", "APEX": "slang-enterprise", "HTML": "sonar-html", "COBOL": "sonar-cobol", "VB6": "sonar-vb", "JAVA": "sonar-java", "PLI": "sonar-pli", "CFAMILY": "sonar-cpp", "CSS": "sonar-css", "FLEX": "sonar-flex", "PHP": "sonar-php", "PLSQL": "sonar-plsql", "PYTHON": "sonar-python", "RPG": "sonar-rpg", "TSQL": "sonar-tsql", "XML": "sonar-xml", "CLOUDFORMATION": "sonar-iac", "TERRAFORM": "sonar-iac", "SECRETS": "sonar-secrets", })); function ticketsAndImplementationPRsLinks(ruleNumber: string, title: string, language?: string) { if (language) { const upperCaseLanguage = language.toUpperCase(); const jiraProject = languageToJiraProject.get(upperCaseLanguage); const githubProject = languageToGithubProject.get(upperCaseLanguage); const titleWihoutQuotes = title.replaceAll('"',''); const implementationPRsLink = ( Implementation Pull Requests ); if (jiraProject !== undefined) { const ticketsLink = ( Implementation tickets on Jira ); return {ticketsLink, implementationPRsLink}; } else { const ticketsLink = ( Implementation issues on GitHub ); return {ticketsLink, implementationPRsLink}; } } else { const ticketsLink = (
Select a language to see the implementation tickets
); const implementationPRsLink = (
Select a language to see the implementation pull requests
); return {ticketsLink, implementationPRsLink}; } } export function RulePage(props: any) { const ruleid = props.match.params.ruleid; // language can be absent const language = props.match.params.language; document.title = ruleid; const history = useHistory(); function handleLanguageChange(event: any, lang: string) { history.push(`/${ruleid}/${lang}`); } const classes = useStyles(); let branch = 'master' let descUrl = process.env.PUBLIC_URL + '/rules/' + ruleid + "/" + (language ?? "default") + "-description.html"; let metadataUrl = process.env.PUBLIC_URL + '/rules/' + ruleid + "/" + (language ?? "default") + "-metadata.json"; let [descHTML, descError, descIsLoading] = useFetch(descUrl, false); let [metadataJSON, metadataError, metadataIsLoading] = useFetch(metadataUrl); const {ruleCoverage, allLangsRuleCoverage, isLanguageCovered} = useRuleCoverage(); let coverage: any = "Loading..."; let title = "Loading..." let metadataJSONString; let languagesTabs = null; let prUrl: string | undefined = undefined; if (metadataJSON && !metadataIsLoading && !metadataError) { title = metadataJSON.title; if ('prUrl' in metadataJSON) { prUrl = metadataJSON.prUrl; } branch = metadataJSON.branch; metadataJSON.all_languages.sort(); languagesTabs = metadataJSON.all_languages.map(lang => { const isImplemented = isLanguageCovered(lang, metadataJSON!.allKeys); const classNames = classes.tab + ' ' + (isImplemented ? classes.tabCovered : classes.tabTargeted); return ; }); metadataJSONString = JSON.stringify(metadataJSON, null, 2); const coverageMapper = (key: any, range: any) => { if (typeof range === "string") { return (
  • {key}: {range}
  • ); } else { return (
  • Not covered for {key} anymore. Was covered from {range['since']} to {range['until']}.
  • ); } }; if (language) { coverage = ruleCoverage(language, metadataJSON.allKeys, coverageMapper); } else { coverage = allLangsRuleCoverage(metadataJSON.allKeys, coverageMapper); } } if (coverage !== "Not Covered") { prUrl = undefined; branch = 'master'; } let editOnGithubUrl = 'https://github.com/SonarSource/rspec/blob/' + branch + '/rules/' + ruleid + (language ? '/' + language : ''); let description =
    Loading...
    ; if (descHTML !== null && !descIsLoading && !descError) { description =

    Edit on Github

    {metadataJSONString}
    ; } let prLink = <>; if (prUrl) { prLink =
    Not implemented (see PR)
    } const ruleNumber = ruleid.substring(1); const specificationPRsLink = ( Specification Pull Requests ); const {ticketsLink, implementationPRsLink} = ticketsAndImplementationPRsLinks(ruleNumber, title, language); const tabsValue = language ? {'value' : language} : {'value': false}; return (
    {ruleid} {prLink} {languagesTabs}
    {title} Covered Since
      {coverage}
    Related Tickets and Pull Requests
      {specificationPRsLink}
      {implementationPRsLink}
      {ticketsLink}
    Description {description}
    ); }