diff --git a/file_actions/dependency-reduced-pom.xml b/file_actions/dependency-reduced-pom.xml new file mode 100644 index 00000000..110fd87e --- /dev/null +++ b/file_actions/dependency-reduced-pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + com.testsigma.addons + file_actions + 1.0.5 + + file_actions + + + maven-shade-plugin + 3.2.4 + + + package + + shade + + + + + + maven-source-plugin + ${maven.source.plugin.version} + + + attach-sources + + jar + + + + + + + + + org.junit.jupiter + junit-jupiter-api + 5.8.0-M1 + test + + + apiguardian-api + org.apiguardian + + + opentest4j + org.opentest4j + + + junit-platform-commons + org.junit.platform + + + + + + 1.2.24_cloud + 1.18.30 + 3.2.1 + 11 + 11 + UTF-8 + 5.8.0-M1 + 1.0.0 + + diff --git a/file_actions/src/main/java/com/testsigma/addons/utils/FileHelper.java b/file_actions/src/main/java/com/testsigma/addons/utils/FileHelper.java new file mode 100644 index 00000000..ffe0a29a --- /dev/null +++ b/file_actions/src/main/java/com/testsigma/addons/utils/FileHelper.java @@ -0,0 +1,62 @@ +package com.testsigma.addons.utils; + +import com.testsigma.sdk.Logger; +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +public class FileHelper { + + public static File urlToFileConverter(Logger logger, String fileName, String url) { + try { + if (url.startsWith("https://") || url.startsWith("http://")) { + logger.info("Given is s3 url ...File name:" + fileName); + URL urlObject = new URL(url); + String baseName = fileName; + String extension = ""; + int lastDotIndex = fileName.lastIndexOf('.'); + if (lastDotIndex > 0) { + baseName = fileName.substring(0, lastDotIndex); + extension = fileName.substring(lastDotIndex); + } + File tempFile = File.createTempFile(baseName, extension); + FileUtils.copyURLToFile(urlObject, tempFile); + logger.info("Temp file created with name for s3 file" + tempFile.getName() + + " at path " + tempFile.getAbsolutePath()); + return tempFile; + } else { + logger.info("Given is local file path.."); + return new File(url); + } + } catch (Exception e) { + logger.info("Error while accessing: " + url); + throw new RuntimeException("Unable to access the given file, please check the given inputs."); + } + } + + public static String readFileContent(File file) throws Exception { + byte[] fileBytes = Files.readAllBytes(file.toPath()); + return new String(fileBytes, StandardCharsets.UTF_8); + } + + public static boolean isHtmlFile(String fileName) { + if (fileName == null || fileName.isEmpty()) return false; + String lower = fileName.toLowerCase(); + return lower.endsWith(".html") || lower.endsWith(".htm"); + } + + public static String stripHtmlTags(String htmlContent) { + if (htmlContent == null || htmlContent.isEmpty()) return ""; + return htmlContent.replaceAll("<[^>]+>", " ").replaceAll("\\s+", " ").trim(); + } + + public static String extractFileName(String pathOrUrl) { + if (pathOrUrl == null || pathOrUrl.isEmpty()) return "file"; + String path = pathOrUrl.contains("?") ? pathOrUrl.substring(0, pathOrUrl.indexOf('?')) : pathOrUrl; + int lastSlash = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\')); + return lastSlash >= 0 ? path.substring(lastSlash + 1) : path; + } +} diff --git a/file_actions/src/main/java/com/testsigma/addons/utils/TextExtractionHelper.java b/file_actions/src/main/java/com/testsigma/addons/utils/TextExtractionHelper.java new file mode 100644 index 00000000..5042eafd --- /dev/null +++ b/file_actions/src/main/java/com/testsigma/addons/utils/TextExtractionHelper.java @@ -0,0 +1,135 @@ +package com.testsigma.addons.utils; + +import com.testsigma.sdk.Logger; + +import java.io.File; +import java.util.regex.Pattern; + +public class TextExtractionHelper { + + /** + * Extracts text found between startWord and endWord in the given file or URL. + * Supports local file paths and HTTP/HTTPS URLs. Strips HTML tags for .html/.htm files. + * Boundary search is case-insensitive; returned text preserves original casing. + * Returns null if either word is not found or an error occurs. + */ + public static String extractTextBetweenWords(Logger logger, String filePathOrUrl, String startWord, String endWord) { + try { + String fileName = FileHelper.extractFileName(filePathOrUrl); + File file = FileHelper.urlToFileConverter(logger, fileName, filePathOrUrl); + + if (!file.exists()) { + logger.warn("File does not exist: " + filePathOrUrl); + return null; + } + + String content = FileHelper.readFileContent(file); + + if (FileHelper.isHtmlFile(fileName)) { + logger.debug("HTML file detected, stripping tags before extraction"); + content = FileHelper.stripHtmlTags(content); + } + + String lowerContent = content.toLowerCase(); + int startIndex = lowerContent.indexOf(startWord.toLowerCase()); + if (startIndex == -1) { + logger.warn("Start word '" + startWord + "' not found in file: " + filePathOrUrl); + return null; + } + + int afterStart = startIndex + startWord.length(); + int endIndex = lowerContent.indexOf(endWord.toLowerCase(), afterStart); + if (endIndex == -1) { + logger.warn("End word '" + endWord + "' not found after start word in file: " + filePathOrUrl); + return null; + } + + String extracted = content.substring(afterStart, endIndex).trim(); + logger.info("Extracted text between '" + startWord + "' and '" + endWord + "': " + extracted); + return extracted; + + } catch (Exception e) { + logger.warn("Error extracting text between words from: " + filePathOrUrl+e); + return null; + } + } + + /** + * Splits the file content by the resolved delimiter and returns the token at the given position. + * position=0 returns the first token (before any delimiter); position=N returns the token after + * the Nth occurrence of the delimiter. + * delimiterType accepts: "," | "." | "\t" | "tab" | " " | "space" + */ + public static String extractWordAtDelimiterPosition(Logger logger, String filePathOrUrl, String delimiterType, int position) { + try { + String fileName = FileHelper.extractFileName(filePathOrUrl); + File file = FileHelper.urlToFileConverter(logger, fileName, filePathOrUrl); + + if (!file.exists()) { + logger.warn("File does not exist: " + filePathOrUrl); + return null; + } + + String content = FileHelper.readFileContent(file); + logger.warn("File content read successfully. Length: " + content.length() + " characters"); + logger.info("content " + content); + + if (FileHelper.isHtmlFile(fileName)) { + logger.debug("HTML file detected, stripping tags before extraction"); + content = FileHelper.stripHtmlTags(content); + } + + String delimiter = resolveDelimiter(delimiterType); + if (delimiter == null) { + logger.warn("Unknown delimiter type: '" + delimiterType + "'. Accepted values: , . \\t tab space multi-space"); + return null; + } + + // multi-space is a regex pattern — don't quote it + String splitPattern = isRegexDelimiter(delimiterType) ? delimiter : Pattern.quote(delimiter); + String[] tokens = content.split(splitPattern, -1); + logger.info("Total tokens after splitting by '" + delimiterType + "': " + tokens.length); + + if (position < 0 || position >= tokens.length) { + logger.warn("Position " + position + " is out of range. Total tokens: " + tokens.length); + return null; + } + + String result = tokens[position].trim(); + logger.info("Extracted word at position " + position + ": " + result); + return result; + + } catch (Exception e) { + logger.warn("Error extracting word at delimiter position from: " + filePathOrUrl + e); + return null; + } + } + + public static String resolveDelimiter(String delimiterType) { + if (delimiterType == null) return null; + // Check for literal space before trimming, since trim() would erase it + if (delimiterType.equals(" ")) return " "; + switch (delimiterType.trim().toLowerCase()) { + case "comma": + case ",": return ","; + case "period": + case ".": return "."; + case "\\t": + case "tab": + case "\t": return "\t"; + case "space": return " "; + case "multi-space": + case "multispace": return "\\s{2,}"; + default: return null; + } + } + + public static boolean isRegexDelimiter(String delimiterType) { + if (delimiterType == null) return false; + switch (delimiterType.trim().toLowerCase()) { + case "multi-space": + case "multispace": return true; + default: return false; + } + } +} diff --git a/file_actions/src/main/java/com/testsigma/addons/windowsAdvanced/ExtractTextInBetweenWordsFromFile.java b/file_actions/src/main/java/com/testsigma/addons/windowsAdvanced/ExtractTextInBetweenWordsFromFile.java new file mode 100644 index 00000000..cf10dda5 --- /dev/null +++ b/file_actions/src/main/java/com/testsigma/addons/windowsAdvanced/ExtractTextInBetweenWordsFromFile.java @@ -0,0 +1,83 @@ +package com.testsigma.addons.windowsAdvanced; + +import com.testsigma.addons.utils.TextExtractionHelper; +import com.testsigma.sdk.Result; +import com.testsigma.sdk.WindowsAdvancedAction; +import com.testsigma.sdk.annotation.Action; +import com.testsigma.sdk.annotation.RunTimeData; +import com.testsigma.sdk.annotation.TestData; +import lombok.Data; + + +@Data +@Action(actionText = "Store text in between word start word start-word and end word end-word from file file-path" + + " into a runtime variable variable variable-name", + description = "Extracts text that is located between the specified start and end words in the given file." + + " The extracted text is stored in a variable for later use. Supports various file types including HTML files.", + applicationType = com.testsigma.sdk.ApplicationType.WINDOWS_ADVANCED, + useCustomScreenshot = false, + displayName = "Extract Text In Between Words From File") +public class ExtractTextInBetweenWordsFromFile extends WindowsAdvancedAction { + + @TestData(reference = "start-word") + private com.testsigma.sdk.TestData startWord; + @TestData(reference = "end-word") + private com.testsigma.sdk.TestData endWord; + @TestData(reference = "file-path") + private com.testsigma.sdk.TestData filePath; + @TestData(reference = "variable-name", isRuntimeVariable = true) + private com.testsigma.sdk.TestData variableName; + + @RunTimeData + private com.testsigma.sdk.RunTimeData runTimeData; + + + @Override + protected Result execute() { + Result result = Result.SUCCESS; + + try { + logger.info("Initiating text extraction between words from file"); + logger.debug("Start word: " + startWord.getValue() + + ", End word: " + endWord.getValue() + + ", File path: " + filePath.getValue() + + ", Variable name: " + variableName.getValue()); + + String startWordStr = startWord.getValue().toString(); + String endWordStr = endWord.getValue().toString(); + String filePathStr = filePath.getValue().toString(); + String variableNameStr = variableName.getValue().toString(); + + // Validate inputs + if (startWordStr.isEmpty() || endWordStr.isEmpty() || filePathStr.isEmpty() || variableNameStr.isEmpty()) { + result = Result.FAILED; + setErrorMessage("All input fields must be provided and cannot be empty"); + return result; + } + + // Extract text between start and end words from the file + String extractedText = TextExtractionHelper.extractTextBetweenWords(logger, filePathStr, startWordStr, endWordStr); + + if (extractedText == null) { + result = Result.FAILED; + setErrorMessage("Failed to extract text between the specified words." + + " Please check the file and the provided words."); + return result; + } + + // Store the extracted text in a runtime variable + runTimeData.setKey(variableNameStr); + runTimeData.setValue(extractedText); + logger.info("Extracted text stored in variable '" + variableNameStr + "': " + extractedText); + + } catch (Exception e) { + logger.warn("An error occurred while extracting text between words from file" + e); + result = Result.FAILED; + setErrorMessage("An error occurred: " + e.getMessage()); + } + + return result; + } + + +} diff --git a/file_actions/src/main/java/com/testsigma/addons/windowsAdvanced/ExtractWordByDelimiterPositionFromFile.java b/file_actions/src/main/java/com/testsigma/addons/windowsAdvanced/ExtractWordByDelimiterPositionFromFile.java new file mode 100644 index 00000000..206a5004 --- /dev/null +++ b/file_actions/src/main/java/com/testsigma/addons/windowsAdvanced/ExtractWordByDelimiterPositionFromFile.java @@ -0,0 +1,100 @@ +package com.testsigma.addons.windowsAdvanced; + +import com.testsigma.addons.utils.TextExtractionHelper; +import com.testsigma.sdk.Result; +import com.testsigma.sdk.WindowsAdvancedAction; +import com.testsigma.sdk.annotation.Action; +import com.testsigma.sdk.annotation.RunTimeData; +import com.testsigma.sdk.annotation.TestData; +import lombok.Data; + +@Data +@Action(actionText = "Store the word after position number position-number occurrences of delimiter-type delimiter" + + " from file file-path into a runtime variable variable variable-name", + description = "Extracts the word found after a specified number of delimiter occurrences in the given file." + + " Supported delimiters: comma (,), period (.), tab (\\t or tab), space. " + + "position-number=0 returns the token before any delimiter; position-number=N returns the token after the Nth delimiter.", + applicationType = com.testsigma.sdk.ApplicationType.WINDOWS_ADVANCED, + useCustomScreenshot = false, + displayName = "Extract Word By Delimiter Position From File") +public class ExtractWordByDelimiterPositionFromFile extends WindowsAdvancedAction { + + @TestData(reference = "file-path") + private com.testsigma.sdk.TestData filePath; + + @TestData(reference = "delimiter-type", allowedValues = {"comma", ".", "tab", "space", "multi-space"}, + description = "Accepted values: comma or ,, period or ., tab or \\t, space, multi-space (2+ consecutive spaces — use for fixed-width files)") + private com.testsigma.sdk.TestData delimiterType; + + @TestData(reference = "position-number") + private com.testsigma.sdk.TestData positionNumber; + + @TestData(reference = "variable-name", isRuntimeVariable = true) + private com.testsigma.sdk.TestData variableName; + + @RunTimeData + private com.testsigma.sdk.RunTimeData runTimeData; + + @Override + protected Result execute() { + Result result = Result.SUCCESS; + + try { + logger.info("Initiating word extraction by delimiter position from file"); + + String filePathStr = filePath.getValue().toString().trim(); + String delimiterTypeStr = delimiterType.getValue().toString().trim(); + String positionStr = positionNumber.getValue().toString().trim(); + String variableNameStr = variableName.getValue().toString().trim(); + + if (filePathStr.isEmpty() || delimiterTypeStr.isEmpty() || positionStr.isEmpty() || variableNameStr.isEmpty()) { + setErrorMessage("All input fields must be provided and cannot be empty"); + return Result.FAILED; + } + + int position; + try { + position = Integer.parseInt(positionStr); + } catch (NumberFormatException e) { + setErrorMessage("position-number must be a valid integer, got: " + positionStr); + return Result.FAILED; + } + + if (position < 0) { + setErrorMessage("position-number must be 0 or greater, got: " + position); + return Result.FAILED; + } + + if (TextExtractionHelper.resolveDelimiter(delimiterTypeStr) == null) { + setErrorMessage("Unsupported delimiter-type: '" + delimiterTypeStr + + "'. Accepted values: comma (,) period (.) tab (\\t) space multi-space"); + return Result.FAILED; + } + + logger.debug("File: " + filePathStr + ", delimiter: '" + delimiterTypeStr + + "', position: " + position + ", variable: " + variableNameStr); + + String extractedWord = TextExtractionHelper.extractWordAtDelimiterPosition( + logger, filePathStr, delimiterTypeStr, position); + + if (extractedWord == null) { + setErrorMessage("Could not extract word at position " + position + + " using delimiter '" + delimiterTypeStr + "'. " + + "Check that the file exists and the position is within range."); + return Result.FAILED; + } + + runTimeData.setKey(variableNameStr); + runTimeData.setValue(extractedWord); + logger.info("Stored extracted word '" + extractedWord + "' in variable '" + variableNameStr + "'"); + setSuccessMessage("Successfully extracted word at position " + position + " using delimiter '" + delimiterTypeStr + + "' and stored in variable '" + variableNameStr + "': " + extractedWord); + } catch (Exception e) { + logger.warn("An error occurred while extracting word by delimiter position from file" + e); + result = Result.FAILED; + setErrorMessage("An error occurred: " + e.getMessage()); + } + + return result; + } +}