github.com/freiheit-com/kuberpult@v1.24.2-0.20240328135542-315d5630abe6/services/frontend-service/src/ui/utils/Links.tsx (about) 1 /*This file is part of kuberpult. 2 3 Kuberpult is free software: you can redistribute it and/or modify 4 it under the terms of the Expat(MIT) License as published by 5 the Free Software Foundation. 6 7 Kuberpult is distributed in the hope that it will be useful, 8 but WITHOUT ANY WARRANTY; without even the implied warranty of 9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 MIT License for more details. 11 12 You should have received a copy of the MIT License 13 along with kuberpult. If not, see <https://directory.fsf.org/wiki/License:Expat>. 14 15 Copyright 2023 freiheit.com*/ 16 17 import React from 'react'; 18 import { useArgoCdBaseUrl, useSourceRepoUrl, useBranch, useManifestRepoUrl } from './store'; 19 20 export const deriveArgoAppLink = (baseUrl: string | undefined, app: string): string | undefined => { 21 if (baseUrl) { 22 const baseUrlSlash = baseUrl.endsWith('/') ? baseUrl : baseUrl + '/'; 23 return baseUrlSlash + 'applications?search=-' + app; 24 } 25 return undefined; 26 }; 27 28 export const deriveArgoAppEnvLink = ( 29 baseUrl: string | undefined, 30 app: string, 31 env: string, 32 namespace: string 33 ): string | undefined => { 34 if (baseUrl) { 35 const baseUrlSlash = baseUrl.endsWith('/') ? baseUrl : baseUrl + '/'; 36 return `${baseUrlSlash}applications/${namespace}/${env}-${app}`; 37 } 38 return undefined; 39 }; 40 41 export const deriveArgoTeamLink = (baseUrl: string | undefined, team: string): string | undefined => { 42 if (baseUrl) { 43 const baseUrlSlash = baseUrl.endsWith('/') ? baseUrl : baseUrl + '/'; 44 return baseUrlSlash + 'applications?&labels=' + encodeURIComponent('com.freiheit.kuberpult/team=') + team; 45 } 46 return undefined; 47 }; 48 49 export const deriveSourceCommitLink = ( 50 baseUrl: string | undefined, 51 branch: string | undefined, 52 commit: string 53 ): string | undefined => { 54 if (baseUrl && branch) { 55 baseUrl = baseUrl.replace(/{branch}/gi, branch); 56 baseUrl = baseUrl.replace(/{commit}/gi, commit); 57 return baseUrl; 58 } 59 return undefined; 60 }; 61 62 export const deriveReleaseDirLink = ( 63 baseUrl: string | undefined, 64 branch: string | undefined, 65 app: string, 66 version: number 67 ): string | undefined => { 68 if (baseUrl && branch) { 69 baseUrl = baseUrl.replace(/{branch}/gi, branch); 70 baseUrl = baseUrl.replace(/{dir}/gi, 'applications/' + app + '/releases/' + version); 71 return baseUrl; 72 } 73 return undefined; 74 }; 75 76 export const getCommitHistoryLink = (commitId: string | undefined): string | undefined => { 77 if (commitId) { 78 return '/ui/commits/' + commitId; 79 } 80 return undefined; 81 }; 82 83 export const ArgoTeamLink: React.FC<{ team: string | undefined }> = (props): JSX.Element | null => { 84 const { team } = props; 85 const argoBaseUrl = useArgoCdBaseUrl(); 86 if (!team) { 87 return null; 88 } 89 if (!argoBaseUrl) { 90 // just render as text, because we do not have a base url: 91 return <span>{team}</span>; 92 } 93 return ( 94 <span> 95 {' | Team: '} 96 <a title={'Opens the team in ArgoCd'} href={deriveArgoTeamLink(argoBaseUrl, team)}> 97 {team} 98 </a> 99 </span> 100 ); 101 }; 102 103 export const ArgoAppLink: React.FC<{ app: string }> = (props): JSX.Element => { 104 const { app } = props; 105 const argoBaseUrl = useArgoCdBaseUrl(); 106 if (!argoBaseUrl) { 107 // just render as text, because we do not have a base url: 108 return <span>{app}</span>; 109 } 110 return ( 111 <a title={'Opens this app in ArgoCd for all environments'} href={deriveArgoAppLink(argoBaseUrl, app)}> 112 {app} 113 </a> 114 ); 115 }; 116 117 export const ArgoAppEnvLink: React.FC<{ app: string; env: string; namespace: string | undefined }> = ( 118 props 119 ): JSX.Element => { 120 const { app, env, namespace } = props; 121 const argoBaseUrl = useArgoCdBaseUrl(); 122 if (!argoBaseUrl) { 123 // just render as text, because we do not have a base url: 124 return <span>{env}</span>; 125 } 126 return ( 127 <a 128 title={'Opens the app in ArgoCd for this environment'} 129 href={namespace ? deriveArgoAppEnvLink(argoBaseUrl, app, env, namespace) : undefined}> 130 {env} 131 </a> 132 ); 133 }; 134 135 export const DisplaySourceLink: React.FC<{ displayString: string; commitId: string }> = (props): JSX.Element | null => { 136 const { commitId, displayString } = props; 137 const sourceRepo = useSourceRepoUrl(); 138 const branch = useBranch(); 139 const sourceLink = deriveSourceCommitLink(sourceRepo, branch, commitId); 140 if (sourceLink) { 141 return ( 142 <a title={'Opens the commit for this release in the source repository'} href={sourceLink}> 143 {displayString} 144 </a> 145 ); 146 } 147 return null; 148 }; 149 150 export const DisplayManifestLink: React.FC<{ displayString: string; app: string; version: number }> = ( 151 props 152 ): JSX.Element | null => { 153 const { displayString, app, version } = props; 154 const manifestRepo = useManifestRepoUrl(); 155 const branch = useBranch(); 156 const manifestLink = deriveReleaseDirLink(manifestRepo, branch, app, version); 157 if (manifestLink && version) { 158 return ( 159 <a title={'Opens the release directory in the manifest repository for this release'} href={manifestLink}> 160 {displayString} 161 </a> 162 ); 163 } 164 return null; 165 }; 166 167 export const DisplayCommitHistoryLink: React.FC<{ displayString: string; commitId: string }> = ( 168 props 169 ): JSX.Element | null => { 170 const { displayString, commitId } = props; 171 if (commitId) { 172 const listLink = getCommitHistoryLink(commitId); 173 return ( 174 <a title={'Opens the commit history'} href={listLink}> 175 {displayString} 176 </a> 177 ); 178 } 179 180 return null; 181 }; 182 183 type Query = { 184 key: string; 185 value: string | null; 186 }; 187 188 const toQueryString = (queries: Query[]): string => { 189 const str: string[] = []; 190 queries.forEach((q: Query) => { 191 if (q.value) { 192 str.push(encodeURIComponent(q.key) + '=' + encodeURIComponent(q.value)); 193 } 194 }); 195 return str.join('&'); 196 }; 197 198 export const ProductVersionLink: React.FC<{ env: string; groupName: string }> = (props): JSX.Element | null => { 199 const { env, groupName } = props; 200 201 const separator = groupName === '' ? '' : '/'; 202 203 const urlParams = new URLSearchParams(window.location.search); 204 const teams = urlParams.get('teams'); 205 const queryString = toQueryString([ 206 { key: 'env', value: groupName + separator + env }, 207 { key: 'teams', value: teams }, 208 ]); 209 210 const currentLink = window.location.href; 211 const addParam = currentLink.split('?'); 212 return ( 213 <a 214 title={'Opens the release directory in the manifest repository for this release'} 215 href={addParam[0] + '/productVersion?' + queryString}> 216 Display Version for {env} 217 </a> 218 ); 219 }; 220 221 export const KuberpultGitHubLink: React.FC<{ version: string }> = (props): JSX.Element | null => { 222 const { version } = props; 223 return ( 224 <a 225 title={'Opens the Kuberpult Readme for the current version ' + version} 226 href={'https://github.com/freiheit-com/kuberpult/blob/' + version + '/README.md'}> 227 {version} 228 </a> 229 ); 230 }; 231 232 const hideWithoutWarningsParamName = 'hideWithoutWarnings'; 233 const hideWithoutWarningsParamEnabledValue = 'Y'; 234 export const hideWithoutWarnings = (params: URLSearchParams): boolean => { 235 const hideWithoutWarningsParam = params.get(hideWithoutWarningsParamName) || ''; 236 return hideWithoutWarningsParam === hideWithoutWarningsParamEnabledValue; 237 }; 238 export const setHideWithoutWarnings = (params: URLSearchParams, newValue: boolean): void => { 239 if (newValue) { 240 params.set(hideWithoutWarningsParamName, hideWithoutWarningsParamEnabledValue); 241 } else { 242 params.delete(hideWithoutWarningsParamName); 243 } 244 }; 245 246 const envConfigDialogParamName = 'dialog-env-config'; 247 export const getOpenEnvironmentConfigDialog = (params: URLSearchParams): string => 248 params.get(envConfigDialogParamName) || ''; 249 export const setOpenEnvironmentConfigDialog = (params: URLSearchParams, envName: string): void => { 250 if (envName.length > 0) { 251 params.set(envConfigDialogParamName, envName); 252 } else { 253 params.delete(envConfigDialogParamName); 254 } 255 };