-
Notifications
You must be signed in to change notification settings - Fork 16
[CUS-9787] clear cell value from csv file. #286
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| package com.testsigma.addons.web; | ||
|
|
||
|
|
||
| import com.testsigma.sdk.ApplicationType; | ||
| import com.testsigma.sdk.WebAction; | ||
| import com.testsigma.sdk.annotation.Action; | ||
| import com.testsigma.sdk.annotation.TestData; | ||
| import lombok.Data; | ||
| import com.opencsv.CSVReader; | ||
| import com.opencsv.CSVWriter; | ||
| import com.opencsv.exceptions.CsvException; | ||
|
|
||
| import java.io.File; | ||
| import java.io.FileReader; | ||
| import java.io.FileWriter; | ||
| import java.io.IOException; | ||
| import java.io.Reader; | ||
| import java.net.URL; | ||
| import java.util.List; | ||
|
|
||
| import org.apache.commons.io.FileUtils; | ||
| import org.apache.commons.lang3.exception.ExceptionUtils; | ||
| import org.openqa.selenium.NoSuchElementException; | ||
|
|
||
| @Data | ||
| @Action(actionText = "clear cell value from a CSV file file-path where row is row-number and column is column-number", | ||
| description = "Deletes a particular cell from CSV file uses 1-based indexing for row and column numbers.", | ||
| applicationType = ApplicationType.WEB) | ||
| public class DeleteRowContentFromCsv extends WebAction { | ||
|
|
||
| @TestData(reference = "file-path") | ||
| private com.testsigma.sdk.TestData filePathOrUpload; | ||
|
|
||
| @TestData(reference = "row-number") | ||
| private com.testsigma.sdk.TestData rowNumber; | ||
|
|
||
| @TestData(reference = "column-number") | ||
| private com.testsigma.sdk.TestData columnNumber; | ||
|
|
||
| @Override | ||
| public com.testsigma.sdk.Result execute() throws NoSuchElementException { | ||
| logger.info("Initiating execution"); | ||
|
|
||
| com.testsigma.sdk.Result result = com.testsigma.sdk.Result.SUCCESS; | ||
|
|
||
| String filePathString = filePathOrUpload.getValue().toString(); | ||
| int targetRow; | ||
| int targetColumn; | ||
| try { | ||
| targetRow = Integer.parseInt(rowNumber.getValue().toString()); | ||
| targetColumn = Integer.parseInt(columnNumber.getValue().toString()); | ||
| } catch (NumberFormatException e) { | ||
| setErrorMessage("Row number and Column number must be valid integers."); | ||
| return com.testsigma.sdk.Result.FAILED; | ||
| } | ||
|
|
||
| // Validate 1-based input (must be at least 1) | ||
| if (targetRow < 1 || targetColumn < 1) { | ||
| setErrorMessage("Row and Column numbers must be at least 1. Given: row=" + targetRow + ", column=" + targetColumn); | ||
| return com.testsigma.sdk.Result.FAILED; | ||
| } | ||
|
|
||
| // Store original 1-based values for user-friendly messages | ||
| int originalRow = targetRow; | ||
| int originalColumn = targetColumn; | ||
|
|
||
| // Convert to 0-based index (user gives 1,1 → maps to 0,0 in CSV) | ||
| int rowIndex = targetRow - 1; | ||
| int columnIndex = targetColumn - 1; | ||
|
|
||
|
|
||
| String csvFilePath; | ||
| if (filePathString.startsWith("https://") || filePathString.startsWith("http://")) { | ||
| File tempFile = urlToCSVFileConverter("csvFileName", filePathString); | ||
| csvFilePath = tempFile.getAbsolutePath(); | ||
| } else { | ||
| // Use local file path directly | ||
| logger.info("Given is local file path..."); | ||
| csvFilePath = filePathString; | ||
| } | ||
| logger.info("CSV File path: " + csvFilePath); | ||
|
|
||
| try (Reader reader = new FileReader(csvFilePath); | ||
| CSVReader csvReader = new CSVReader(reader)) { | ||
| List<String[]> rows = csvReader.readAll(); | ||
|
|
||
| // Check row bounds using 0-based index | ||
| if (rowIndex >= 0 && rowIndex < rows.size()) { | ||
| logger.info("Accessing row " + originalRow); | ||
| String[] row = rows.get(rowIndex); | ||
|
|
||
| // Check column bounds using 0-based index | ||
| if (columnIndex >= 0 && columnIndex < row.length) { | ||
| row[columnIndex] = ""; | ||
|
|
||
| // Write the modified CSV back to the same file | ||
| try (CSVWriter writer = new CSVWriter(new FileWriter(csvFilePath))) { | ||
| writer.writeAll(rows); | ||
| } catch (IOException e) { | ||
| logger.warn("Error writing to CSV file: " + ExceptionUtils.getStackTrace(e)); | ||
| setErrorMessage("Error writing to CSV file: " + e.getMessage()); | ||
| result = com.testsigma.sdk.Result.FAILED; | ||
| return result; | ||
| } | ||
|
|
||
| logger.info("Content deleted from row " + originalRow + " and column " + originalColumn); | ||
| setSuccessMessage("Content deleted from row " + originalRow + " and column " + originalColumn); | ||
| } else { | ||
| logger.warn("Column number " + originalColumn + " is out of bounds."); | ||
| setErrorMessage("Column number " + originalColumn + " is out of bounds."); | ||
| result = com.testsigma.sdk.Result.FAILED; | ||
| } | ||
| } else { | ||
| logger.warn("Row number " + originalRow + " is out of bounds."); | ||
| setErrorMessage("Row number " + originalRow + " is out of bounds."); | ||
| result = com.testsigma.sdk.Result.FAILED; | ||
| } | ||
| } catch (IOException | CsvException e) { | ||
| logger.warn("Error processing CSV file: " + ExceptionUtils.getStackTrace(e)); | ||
| setErrorMessage("Error processing CSV file: " + e.getMessage()); | ||
| result = com.testsigma.sdk.Result.FAILED; | ||
| return result; | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| public File urlToCSVFileConverter(String fileName, String url) { | ||
| try { | ||
| logger.info("Given is URL... File name: " + fileName); | ||
| URL urlObject = new URL(url); | ||
|
|
||
| // Extract file extension if present | ||
| String baseName = fileName; | ||
| String extension = ".csv"; // default csv format | ||
| 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("CSV file created: " + tempFile.getAbsolutePath()); | ||
| return tempFile; | ||
| } catch (Exception e) { | ||
| logger.info("Error while accessing: " + url); | ||
| throw new RuntimeException("Unable to access or validate the CSV file. Please check the inputs.", e); | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,200 @@ | ||
| package com.testsigma.addons.web; | ||
|
|
||
| import com.opencsv.CSVReader; | ||
| import com.opencsv.CSVWriter; | ||
| import com.opencsv.exceptions.CsvException; | ||
| import com.testsigma.sdk.ApplicationType; | ||
| import com.testsigma.sdk.WebAction; | ||
| import com.testsigma.sdk.annotation.Action; | ||
| import com.testsigma.sdk.annotation.RunTimeData; | ||
| import com.testsigma.sdk.annotation.TestData; | ||
| import lombok.Data; | ||
| import org.apache.commons.io.FileUtils; | ||
| import org.apache.commons.io.FilenameUtils; | ||
| import org.openqa.selenium.NoSuchElementException; | ||
|
|
||
| import java.io.File; | ||
| import java.io.FileReader; | ||
| import java.io.FileWriter; | ||
| import java.io.IOException; | ||
| import java.net.URL; | ||
| import java.nio.file.Files; | ||
| import java.nio.file.StandardCopyOption; | ||
| import java.util.List; | ||
|
|
||
| @Data | ||
| @Action(actionText = "clear cell value from CSV file test_data where row is row-number and column column-number" + | ||
| " and store filepath in runtime variable variable-name (It supports file from upload section)", | ||
| description = "Deletes content from a particular cell in CSV file using 1-based indexing for row " + | ||
| "and column numbers. Can accept local file paths or URLs for the CSV file." + | ||
| " Stores the file path in a runtime variable. It supports file from upload section.", | ||
| applicationType = ApplicationType.WEB) | ||
| public class DeleteRowContentFromCsvAndStorePath extends WebAction { | ||
|
|
||
| @TestData(reference = "row-number") | ||
| private com.testsigma.sdk.TestData rowNumber; | ||
|
|
||
| @TestData(reference = "column-number") | ||
| private com.testsigma.sdk.TestData columnNumber; | ||
|
|
||
| @TestData(reference = "test_data") | ||
| 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 | ||
| public com.testsigma.sdk.Result execute() throws NoSuchElementException { | ||
| logger.info("Initiating execution"); | ||
|
|
||
| com.testsigma.sdk.Result result = com.testsigma.sdk.Result.SUCCESS; | ||
|
|
||
| String filePathString = filePath.getValue().toString(); | ||
|
|
||
| int targetRow; | ||
| int targetColumn; | ||
| try { | ||
| targetRow = Integer.parseInt(rowNumber.getValue().toString()); | ||
| targetColumn = Integer.parseInt(columnNumber.getValue().toString()); | ||
| } catch (NumberFormatException e) { | ||
| setErrorMessage("Row number and Column number must be valid integers."); | ||
| return com.testsigma.sdk.Result.FAILED; | ||
| } | ||
|
|
||
| // Validate 1-based input (must be at least 1) | ||
| if (targetRow < 1 || targetColumn < 1) { | ||
| setErrorMessage("Row and Column numbers must be at least 1. Given: row=" + targetRow + ", column=" + targetColumn); | ||
| return com.testsigma.sdk.Result.FAILED; | ||
| } | ||
|
|
||
| // Store original 1-based values for user-friendly messages | ||
| int originalRow = targetRow; | ||
| int originalColumn = targetColumn; | ||
|
|
||
| File csvFile = null; | ||
| File tempCsvFile = null; | ||
|
|
||
| try { | ||
| csvFile = convertToFile(filePathString); | ||
| logger.info("CSV file path: " + csvFile.getAbsolutePath()); | ||
|
|
||
| if (csvFile == null || !csvFile.exists()) { | ||
| setErrorMessage("CSV File not found or could not be downloaded: " + filePathString); | ||
| return com.testsigma.sdk.Result.FAILED; | ||
| } | ||
|
|
||
| // Create a unique temp file each time | ||
| String uniqueFileName = "updated_" + System.currentTimeMillis() + ".csv"; | ||
| tempCsvFile = new File(csvFile.getParentFile(), uniqueFileName); | ||
| logger.info("Temp file path: " + tempCsvFile.getAbsolutePath()); | ||
| Files.copy(csvFile.toPath(), tempCsvFile.toPath(), StandardCopyOption.REPLACE_EXISTING); | ||
|
|
||
| CSVReader csvReader = null; | ||
| CSVWriter writer = null; | ||
| try { | ||
| csvReader = new CSVReader(new FileReader(tempCsvFile)); | ||
| List<String[]> data = csvReader.readAll(); | ||
|
|
||
| // Convert from 1-based (user input) to 0-based index (user gives 1,1 → maps to 0,0) | ||
| int rowIndex = targetRow - 1; | ||
| int columnIndex = targetColumn - 1; | ||
|
|
||
| // Check row bounds using 0-based index | ||
| if (rowIndex < data.size()) { | ||
| logger.info("Accessing row " + originalRow); | ||
| String[] row = data.get(rowIndex); | ||
|
|
||
| // Check column bounds using 0-based index | ||
| if (columnIndex < row.length) { | ||
| row[columnIndex] = ""; | ||
|
|
||
| writer = new CSVWriter(new FileWriter(tempCsvFile), ',', CSVWriter.NO_QUOTE_CHARACTER, | ||
| CSVWriter.DEFAULT_ESCAPE_CHARACTER, CSVWriter.DEFAULT_LINE_END); | ||
| writer.writeAll(data); | ||
| writer.flush(); | ||
|
|
||
| logger.info("Content deleted from row " + originalRow + " and column " + originalColumn); | ||
| } else { | ||
| setErrorMessage("Column number " + originalColumn + " is out of bounds. Max columns: " + row.length); | ||
| return com.testsigma.sdk.Result.FAILED; | ||
| } | ||
| } else { | ||
| setErrorMessage("Row number " + originalRow + " is out of bounds. Max rows: " + data.size()); | ||
| return com.testsigma.sdk.Result.FAILED; | ||
| } | ||
| } catch (IOException | CsvException e) { | ||
| result = com.testsigma.sdk.Result.FAILED; | ||
| setErrorMessage("Error processing CSV file: " + e.getMessage()); | ||
| logger.warn("Error processing CSV file: " + e); | ||
| return result; | ||
| } finally { | ||
| if (csvReader != null) { | ||
| try { | ||
| csvReader.close(); | ||
| } catch (IOException e) { | ||
| logger.warn("Error closing CSVReader: " + e.getMessage() + e); | ||
| } | ||
| } | ||
| if (writer != null) { | ||
| try { | ||
| writer.close(); | ||
| } catch (IOException e) { | ||
| logger.warn("Error closing CSVWriter: " + e.getMessage() + e); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Copy the temp file back to the original file | ||
| try { | ||
| Files.copy(tempCsvFile.toPath(), csvFile.toPath(), StandardCopyOption.REPLACE_EXISTING); | ||
| logger.info("Successfully copied content to original file: " + csvFile.getAbsolutePath()); | ||
| } catch (IOException ex) { | ||
| logger.warn("Error copying data from temp file to original file: " + ex); | ||
| setErrorMessage("Failed to copy data from temp file to original file: " + ex.getMessage()); | ||
| return com.testsigma.sdk.Result.FAILED; | ||
| } | ||
|
|
||
| // Store the path of the updated file | ||
| runTimeData.setKey(variableName.getValue().toString()); | ||
| runTimeData.setValue(csvFile.getAbsolutePath()); | ||
|
|
||
| setSuccessMessage("Content deleted from row " + originalRow + ", column " + originalColumn | ||
| + ". File path stored in runtime variable: " + variableName.getValue().toString() | ||
| + " = " + csvFile.getAbsolutePath()); | ||
| } catch (Exception e) { | ||
| result = com.testsigma.sdk.Result.FAILED; | ||
| setErrorMessage("Operation Failed: " + e.getMessage()); | ||
| logger.warn("Error during CSV processing: " + e.getMessage() + e); | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| private File convertToFile(String pathOrUrl) throws IOException { | ||
| if (pathOrUrl.startsWith("https://") || pathOrUrl.startsWith("http://")) { | ||
| // Extract the original file name from the URL | ||
| String originalFileName = FilenameUtils.getName(new URL(pathOrUrl).getPath()); | ||
| // Generate a unique file name by appending a timestamp | ||
| String uniqueFileName = "temp_" + System.currentTimeMillis() + "_" + originalFileName; | ||
| logger.info("Given is a URL... Original file name: " + originalFileName + ", Unique file name: " | ||
| + uniqueFileName); | ||
|
|
||
| // Create the full path for the temporary file | ||
| String filePath = String.format("%s%s%s", FileUtils.getTempDirectoryPath(), File.separator, uniqueFileName); | ||
| File tempFile = new File(filePath); | ||
|
|
||
| // Download the file from the URL to the temporary location | ||
| FileUtils.copyURLToFile(new URL(pathOrUrl), tempFile, 10000, 10000); | ||
| logger.info("Temp file created for URL file: " + uniqueFileName + " at path " + filePath); | ||
|
|
||
| return tempFile; | ||
| } else { | ||
| logger.info("Given is a local file path..."); | ||
| return new File(pathOrUrl); | ||
| } | ||
| } | ||
|
Comment on lines
+176
to
+198
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consolidate convertToFile with similar methods and add temp file cleanup. The
🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential data loss if concurrent modifications occur.
When processing a URL-based CSV, the downloaded temp file is modified and written back to the same temp file. For local file paths, writing back to the original file could cause data corruption if multiple actions run concurrently or if the process crashes mid-write.
Consider using the same pattern as
DeleteRowContentFromCsvAndStorePathandWriteCsvFileandStorePath, which create a separate temporary file for the modified content and then copy it back atomically usingFiles.copywithStandardCopyOption.REPLACE_EXISTING.🤖 Prompt for AI Agents