Plugin File Client Usage Guide
Overview
The PluginFileClient
provides essential file operations designed specifically for plugin developers using MVEL, JavaScript, or Python. It offers simple, secure methods for file I/O, JSON/CSV handling, and directory operations with built-in error handling and sandboxing.
Key Features
- Simplified API: Only essential methods for data processing
- Script-Friendly: Designed for MVEL, JavaScript, and Python developers
- Secure by Design: Sandboxed operations with path validation
- Atomic Writes: Data integrity for configuration and export files
- JSON/CSV Support: Built-in support for common data formats
- Error Handling: Consistent error reporting without exceptions
Core API Methods
File Operations (6 methods)
read(String filePath)
- Read text file contentwrite(String filePath, String content)
- Write text to filewrite(String filePath, String content, boolean append)
- Write or append textappend(String filePath, String content)
- Append to file (convenience method)delete(String filePath)
- Delete a fileexists(String filePath)
- Check if file exists
JSON Operations (3 methods)
readJson(String filePath)
- Read and parse JSON filewriteJson(String filePath, Object data)
- Write object as JSONappendJson(String filePath, Object element)
- Append to JSON array
CSV Operations (3 methods)
readCsv(String filePath)
- Read CSV with headerswriteCsv(String filePath, List headers, List<List> rows, boolean append)
- Write CSV dataappendCsv(String filePath, List<List> rows)
- Append rows to CSV
Directory Operations (2 methods)
createDir(String dirPath)
- Create directory with parentslistFiles(String dirPath)
- List files in directory
Path Utilities (2 methods)
joinPath(String... parts)
- Build platform-independent pathsgetFileName(String filePath)
- Extract filename from path
Basic Usage Examples
Simple File Operations
// MVEL - Read configuration
content = files.read("config.txt")
if (content.isSuccess()) {
configText = content.getAsString()
log.info("Config loaded: " + configText)
}
// Write processing data
result = files.write("output.txt", "Item processed: " + itemId)
if (result.isSuccess()) {
log.info("Data written successfully")
}
// Append to log file
files.append("process.log", "Event: " + eventId + "\n")
// JavaScript - File operations with error handling
const content = files.read("data.txt");
if (content.isSuccess()) {
console.log("File content:", content.getAsString());
} else {
console.error("Read failed:", content.getErrorMessage());
}
// Append to event log
const logEntry = `[${new Date()}] Item ${itemId} processed\n`;
files.append("events.log", logEntry);
# Python - Check and write file
if not files.exists("output.csv"):
# Create new file with headers
headers = ["item_id", "timestamp", "event_count"]
files.writeCsv("output.csv", headers, [], False)
# Append data
files.append("debug.log", f"Processing item {item_id}\n")
JSON Operations
// MVEL - Read configuration
configResult = files.readJson("config.json")
if (configResult.isSuccess()) {
config = configResult.getData()
apiUrl = config.get("api_url")
timeout = config.get("timeout")
}
// Export data
eventData = {
"id": eventId,
"timestamp": System.currentTimeMillis(),
"count": eventCount
}
files.writeJson("events/" + eventId + ".json", eventData)
// Append to event log
event = {
"type": "item_processed",
"item_id": itemId,
"time": System.currentTimeMillis()
}
files.appendJson("events.json", event)
// JavaScript - Configuration and logging
const settings = files.readJson("settings.json");
if (settings.isSuccess()) {
const config = settings.getData();
const network = config.network || "mainnet";
console.log(`Connected to ${network}`);
}
// Log event
const event = {
id: eventData.getId(),
amount: eventData.getValue(),
timestamp: Date.now()
};
files.appendJson("events.json", event);
# Python - JSON data export
summary = {
"period": current_period,
"items_processed": item_count,
"total_events": event_count,
"timestamp": System.currentTimeMillis()
}
files.writeJson(f"summaries/period_{current_period}.json", summary)
# Append to audit log
audit_entry = {
"action": "export_completed",
"period": current_period,
"records": record_count
}
files.appendJson("audit.json", audit_entry)
CSV Operations
// MVEL - Export event data
headers = ["event_id", "item_id", "amount", "fee"]
rows = []
for (event : events) {
row = [
event.getId(),
String.valueOf(itemId),
String.valueOf(event.getAmount()),
String.valueOf(event.getFee())
]
rows.add(row)
}
// Write new CSV file
files.writeCsv("transactions.csv", headers, rows, false)
// Or append to existing CSV
files.appendCsv("all_transactions.csv", rows)
// JavaScript - Read and process CSV
const csvResult = files.readCsv("addresses.csv");
if (csvResult.isSuccess()) {
const records = csvResult.getData();
console.log(`Processing ${records.length} addresses`);
records.forEach(record => {
const address = record.address;
const balance = record.balance;
// Process each address
});
}
// Append new data
const newRows = [
[newAddress, newBalance.toString(), "0"],
[anotherAddress, anotherBalance.toString(), "1"]
];
files.appendCsv("addresses.csv", newRows);
# Python - CSV export with append
headers = ["epoch", "slot", "block_hash", "tx_count"]
# Write headers if new file
if not files.exists("blocks.csv"):
files.writeCsv("blocks.csv", headers, [], False)
# Append block data
row = [
str(epoch_number),
str(slot_number),
block_hash,
str(transaction_count)
]
files.appendCsv("blocks.csv", [row])
Directory Operations
// MVEL - Organize output by date
dateDir = "exports/" + new java.text.SimpleDateFormat("yyyy-MM-dd").format(new java.util.Date())
files.createDir(dateDir)
files.writeJson(dateDir + "/block_" + blockNumber + ".json", blockData)
// List and process files
listing = files.listFiles("imports/")
if (listing.isSuccess()) {
fileList = listing.getFiles()
log.info("Found " + fileList.size() + " files to process")
for (fileInfo : fileList) {
if (fileInfo.getName().endsWith(".json")) {
// Process JSON file
data = files.readJson("imports/" + fileInfo.getName())
// ...
}
}
}
// JavaScript - Batch processing
files.createDir("processed");
const listing = files.listFiles("pending/");
if (listing.isSuccess()) {
const files = listing.getFiles();
const jsonFiles = listing.filterByExtension("json");
console.log(`Processing ${jsonFiles.length} JSON files`);
jsonFiles.forEach(file => {
const result = files.readJson(`pending/${file.getName()}`);
if (result.isSuccess()) {
// Process and move to processed directory
const data = result.getData();
// ... process data ...
files.writeJson(`processed/${file.getName()}`, data);
files.delete(`pending/${file.getName()}`);
}
});
}
# Python - Directory organization
# Create directory structure
files.createDir("data/blocks")
files.createDir("data/transactions")
files.createDir("data/addresses")
# List and count files
listing = files.listFiles("data/blocks/")
if listing.isSuccess():
file_count = len(listing.getFiles())
print(f"Total blocks stored: {file_count}")
Path Operations
// MVEL - Build paths safely
basePath = "exports"
yearMonth = new java.text.SimpleDateFormat("yyyy/MM").format(new java.util.Date())
fileName = "block_" + blockNumber + ".json"
// Cross-platform path construction
fullPath = files.joinPath(basePath, yearMonth, fileName)
files.createDir(files.joinPath(basePath, yearMonth))
files.writeJson(fullPath, blockData)
// JavaScript - Path manipulation
const exportDir = "exports";
const date = new Date().toISOString().split('T')[0];
const filename = `transactions_${date}.csv`;
const fullPath = files.joinPath(exportDir, date, filename);
const dirPath = files.joinPath(exportDir, date);
files.createDir(dirPath);
files.writeCsv(fullPath, headers, rows, false);
// Extract filename for logging
const savedFile = files.getFileName(fullPath);
console.log(`Data saved to: ${savedFile}`);
Best Practices
1. Always Check Operation Results
const result = files.read("important.json");
if (!result.isSuccess()) {
console.error(`Failed to read file: ${result.getErrorMessage()}`);
return; // Handle error appropriately
}
const data = result.getAsString();
2. Use Path Utilities for Cross-Platform Compatibility
// Good - works on all platforms
const logPath = files.joinPath("logs", "2024", "01", "app.log");
// Avoid - platform-specific
// const logPath = "logs/2024/01/app.log"; // Unix only
// const logPath = "logs\\2024\\01\\app.log"; // Windows only
3. Choose Appropriate Write Mode
// For configuration and complete data - use write (overwrites)
files.writeJson("config.json", settings);
// For logs and continuous data - use append
files.append("transaction.log", logEntry);
files.appendJson("events.json", eventData);
files.appendCsv("metrics.csv", newRows);
4. Organize Data with Directories
// Create logical directory structure
files.createDir("data/blocks");
files.createDir("data/transactions");
files.createDir("logs/daily");
// Use date-based organization for time-series data
const dateDir = new Date().toISOString().split('T')[0];
files.createDir(files.joinPath("exports", dateDir));
5. Handle Missing Files Gracefully
// Check existence before reading
if (files.exists("state.json")) {
const state = files.readJson("state.json");
// Resume from saved state
} else {
// Initialize new state
const newState = { startBlock: 0, processed: 0 };
files.writeJson("state.json", newState);
}
Security Features
The file client includes built-in security measures:
- Sandboxing: All file operations are confined to a designated sandbox directory
- Path Validation: Prevents directory traversal attacks
- No Dangerous Operations: No recursive deletion, file movement, or archive extraction
- Safe Defaults: Conservative permissions and error handling
Error Handling
All operations return a FileOperationResult
with consistent error handling:
const result = files.readJson("data.json");
if (result.isSuccess()) {
const data = result.getData(); // For JSON/CSV operations
// or
const text = result.getAsString(); // For text operations
// Process data...
} else {
const error = result.getErrorMessage();
console.error(`Operation failed: ${error}`);
}