2022-12-01 08:50:42 +03:00
import fs from "fs" ;
import { runCheckOverChangedFiles } from "./utils/changedFilesValidator" ;
import { ExitCode } from "./utils/exitCode" ;
2023-01-04 09:21:23 +03:00
import { GetPRDetails } from "./utils/gitWrapper" ;
2022-12-01 08:50:42 +03:00
import * as logger from "./utils/logger" ;
export async function ValidateHyperlinks ( filePath : string ) : Promise < ExitCode >
{
let splitPath = filePath . split ( "/" )
if ( splitPath [ 0 ] === "Solutions" )
{
let dataFolderName = splitPath [ 2 ] === "Data" || splitPath [ 2 ] === "data" ? splitPath [ 2 ] : null
let dataConnectorFolderName = splitPath [ 2 ] === "DataConnectors" || splitPath [ 2 ] === "Data Connectors" ? splitPath [ 2 ] : null
2022-12-05 14:02:38 +03:00
if ( dataFolderName == null && dataConnectorFolderName == null )
2022-12-01 08:50:42 +03:00
{
console . log ( ` Skipping Hyperlink validation for file path : ' ${ filePath } ' as change is not in 'Data' and/or 'Data Connectors' folder ` )
return ExitCode . SUCCESS ;
}
2022-12-05 13:50:46 +03:00
2022-12-05 14:02:38 +03:00
//IGNORE BELOW FILES
if ( filePath . includes ( "azuredeploy" ) || filePath . includes ( "host.json" ) || filePath . includes ( "proxies.json" ) || filePath . includes ( "function.json" ) || filePath . includes ( "requirements.txt" ) || filePath . includes ( ".py" ) || filePath . includes ( ".ps1" ) )
{
console . log ( ` Skipping Hyperlink validation for file path : ' ${ filePath } ' ` )
return ExitCode . SUCCESS ;
}
2022-12-01 08:50:42 +03:00
const content = fs . readFileSync ( filePath , "utf8" ) ;
const links = content . match ( /(http|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])+/g ) ;
if ( links )
{
var invalidLinks = new Array ( ) ;
for ( var link of links )
{
link = link . replace ( /["']/g , "" )
//check if the link is valid
const isValid = await isValidLink ( link ) ;
2023-01-04 09:21:23 +03:00
if ( ! isValid )
{
2023-01-03 11:26:05 +03:00
// CHECK IF LINK IS A GITHUB LINK
2023-01-04 09:21:23 +03:00
var isGithubLink = false ;
if ( link . includes ( 'https://raw.githubusercontent.com' ) || link . includes ( 'https://github.com' ) )
2023-01-03 11:26:05 +03:00
{
2023-01-17 09:14:16 +03:00
isGithubLink = true ;
}
if ( link . includes ( '&&sudo' ) )
{
//IGNORE HYPERLINKS WHICH HAS &&SUDO IN IT
isGithubLink = false ;
2023-01-03 11:26:05 +03:00
}
2023-01-04 09:21:23 +03:00
if ( isGithubLink )
2023-01-03 11:26:05 +03:00
{
2023-01-04 09:21:23 +03:00
const pr = await GetPRDetails ( ) ;
if ( typeof pr === "undefined" )
{
console . log ( "Azure DevOps CI for a Pull Request wasn't found. If issue persists - please open an issue" ) ;
return ExitCode . ERROR ;
}
const changedFiles = await pr . diff ( ) ;
const imageIndex = link . lastIndexOf ( '/' ) ;
const imageName = link . substring ( imageIndex + 1 ) ;
const searchedFiles = changedFiles . map ( change = > change . path ) . filter ( changedFilePath = > changedFilePath . indexOf ( imageName ) > 0 ) ;
var searchedFilesLength = searchedFiles . length ;
if ( searchedFilesLength <= 0 )
2023-01-03 11:26:05 +03:00
{
invalidLinks . push ( link ) ;
}
2023-01-04 09:21:23 +03:00
else
{
console . log ( ` Skipping Hyperlink validation for ' ${ link } ' in file path : ' ${ filePath } ' ` ) ;
}
2023-01-03 11:26:05 +03:00
}
2023-01-16 13:36:16 +03:00
else
{
console . log ( ` Skipping Hyperlink validation for ' ${ link } ' in file path : ' ${ filePath } ' ` ) ;
}
2022-12-01 08:50:42 +03:00
}
}
if ( invalidLinks . length > 0 )
{
2023-01-06 13:54:56 +03:00
var errorMessage = ` File ' ${ filePath } ' has a total of ' ${ invalidLinks . length } ' broken hyperlinks. Please review and rectify the following hyperlinks: \ n ${ invalidLinks } `
2023-01-04 09:21:23 +03:00
throw new Error ( errorMessage . replace ( "," , "\n" ) ) ;
2022-12-01 08:50:42 +03:00
}
}
return ExitCode . SUCCESS
}
else
{
console . log ( ` Skipping Hyperlink validation for file path : ' ${ filePath } ' as change is not in 'Solutions' folder ` )
return ExitCode . SUCCESS
}
//create a function to check if the link is valid
async function isValidLink ( link : string ) : Promise < boolean >
{
try
{
//import XMLHttpRequest from "xmlhttprequest"
const XMLHttpRequest = require ( "xmlhttprequest" ) . XMLHttpRequest ;
const request = new XMLHttpRequest ( ) ;
request . open ( "GET" , link , false ) ;
request . setRequestHeader ( "X-Requested-With" , "XMLHttpRequest" ) ;
request . timeout = 5000 ;
request . ontimeout = function ( ) { return false ; }
request . send ( ) ;
if ( request . status == 404 )
{
return false ;
}
else if ( request . status == 302 )
{
var redirectResponse = request . getResponseHeader ( "Location" )
return ( redirectResponse . includes ( "www.google.com" ) || redirectResponse . includes ( "www.bing.com" ) || redirectResponse . includes ( "404 - Page not found" ) ) ? false : true ;
}
else if ( request . status == 0 )
{
// TIMEOUT STATUS IS 0
return false ;
}
else
{
var responseContent = request . responseText
if ( responseContent != null && ( responseContent . includes ( "404! Not Found!" ) || responseContent . includes ( "404 Not Found" ) || responseContent . includes ( "404 error" ) || responseContent . includes ( "404 - Page not found" ) ) ) {
return false ;
}
}
return true ;
} catch ( error )
{
console . log ( error ) ;
return false ;
}
}
}
let fileTypeSuffixes = [ "json" ] ;
let filePathFolderPrefixes = [ "DataConnectors" , "Data Connectors" , "Solutions" ] ;
let fileKinds = [ "Added" , "Modified" ] ;
let CheckOptions = {
onCheckFile : ( filePath : string ) = > {
return ValidateHyperlinks ( filePath )
} ,
2023-01-04 09:21:23 +03:00
onExecError : async ( e : any ) = > {
logger . logError ( ` ${ e } ` ) ;
2022-12-01 08:50:42 +03:00
} ,
onFinalFailed : async ( ) = > {
logger . logError ( "An error occurred, please open an issue" ) ;
} ,
} ;
runCheckOverChangedFiles ( CheckOptions , fileKinds , fileTypeSuffixes , filePathFolderPrefixes ) ;