Noundry.Jobs

A .NET library and CLI tool for creating scheduled automation jobs. Provides APIs for database operations, file management, HTTP requests, email sending, and heartbeat monitoring.

# Install library and CLI
$ dotnet add package Noundry.Jobs
$ dotnet tool install --global Noundry.Jobs.Tool

Quick Start

Write a script that queries a database, processes data, and sends an email report -- all scheduled via cron or Task Scheduler.

using Noundry.Jobs.Database;
using Noundry.Jobs.Email;
using Noundry.Jobs.Files;

// Connect to database
var db = new JobsDb("Host=localhost;Database=mydb;Username=user;Password=pass;",
 DatabaseType.PostgreSql);

// Query data
var orders = await db.QueryAsync<Order>(
 "SELECT * FROM orders WHERE created_at >= @StartDate",
 new { StartDate = DateTime.UtcNow.AddDays(-1) });

// Write report
var report = $"Daily Orders: {orders.Count()}";
await JobsFile.WriteAllTextAsync("report.txt", report);

// Send email
var email = new JobsEmail("smtp.example.com", 587, "user", "pass");
await email.SendAsync(new EmailMessage
{
 From = "jobs@company.com",
 To = "team@company.com",
 Subject = "Daily Order Report",
 Body = report
});

Core APIs

JobsDb - Database

Query, insert, update, delete across SQL Server, PostgreSQL, MySQL, and SQLite. Powered by Noundry.Tuxedo ORM.

JobsFile - Files

Static API for file read/write, copy, move, delete, directory listing, and recursive operations.

JobsApi - HTTP

Fluent HTTP client with GET/POST/PUT/PATCH/DELETE, authentication, file upload/download, and typed responses.

JobsEmail - Email

Send emails via SMTP with HTML bodies and file attachments. Built on MailKit.

Database API (JobsDb)

Supports SQL Server, PostgreSQL, MySQL, and SQLite with parameterized queries and transactions.

// Initialize for different databases
var pg = new JobsDb("Host=localhost;Database=mydb;...", DatabaseType.PostgreSql);
var sql = new JobsDb("Server=localhost;Database=mydb;...");
var lite = new JobsDb("Data Source=local.db", DatabaseType.Sqlite);

// Query operations
var users = await db.QueryAsync<User>("SELECT * FROM users WHERE active = @Active",
 new { Active = true });

var user = await db.QueryFirstOrDefaultAsync<User>(
 "SELECT * FROM users WHERE id = @Id", new { Id = 123 });

var count = await db.ExecuteScalarAsync<long>("SELECT COUNT(*) FROM users");

// Execute with parameters
await db.ExecuteAsync("DELETE FROM logs WHERE created_at < @CutoffDate",
 new { CutoffDate = DateTime.UtcNow.AddDays(-90) });

// Transaction
await db.ExecuteInTransactionAsync(async conn =>
{
 await db.ExecuteAsync("INSERT INTO orders ...", new { ... });
 await db.ExecuteAsync("UPDATE inventory ...", new { ... });
 // Auto-rollback on exception, auto-commit on success
});

File API (JobsFile)

All methods are static. Covers read, write, copy, move, delete, and directory operations.

// Read/Write
var content = await JobsFile.ReadAllTextAsync("config.json");
await JobsFile.WriteAllTextAsync("output.txt", "Hello, World!");
JobsFile.WriteAllText("log.txt", logEntry, append: true);

// Copy/Move/Delete
await JobsFile.CopyAsync("source.txt", "backup/source.txt");
JobsFile.Move("temp.txt", "final.txt");
JobsFile.Delete("temp.txt"); // safe - no error if missing
int deleted = JobsFile.DeleteMany("/logs", "*.log");

// Directory operations
JobsFile.CreateDirectory("/data/output/reports");
var files = JobsFile.List("/data", "*.csv", recursive: true);
JobsFile.CopyDirectory("/source", "/backup");

HTTP API (JobsApi)

Fluent HTTP client with built-in authentication, typed responses, and file operations.

var api = new JobsApi()
 .WithBaseUrl("https://api.example.com")
 .WithBearerToken("sk-abc123")
 .WithTimeout(TimeSpan.FromSeconds(30));

// Typed GET request
var users = await api.GetAsync<List<User>>("/users");

// POST with payload
var result = await api.PostAsync<OrderResponse>("/orders", new { ProductId = 1, Qty = 5 });

// Download/Upload files
await api.DownloadFileAsync("/reports/daily.pdf", "report.pdf");
await api.UploadFileAsync("/uploads", "data.csv");

Authentication Methods

Method Description
WithBearerToken(token)Set Bearer token auth header
WithBasicAuth(user, pass)Set Basic auth header
WithApiKey(header, key)Set custom API key header
WithHeader(name, value)Set any custom header

Email API (JobsEmail)

var email = new JobsEmail("smtp.office365.com", 587, "user@company.com", "password");

await email.SendAsync(new EmailMessage
{
 From = "jobs@company.com",
 To = "team@company.com",
 Subject = "Daily Report",
 Body = "<h1>Report</h1><p>All systems operational.</p>",
 IsHtml = true,
 Attachments = new List<string> { "report.csv", "chart.png" }
});

CLI Tool (njobs)

Schedule and run jobs from the command line using .csx script files.

# Run a job script
$ njobs run daily-report.csx

# Schedule with cron (Linux)
$ njobs schedule --script daily-report.csx --cron "0 8 * * *"

# List scheduled jobs
$ njobs list

Heartbeat API (JobsHeartbeat)

Send heartbeat signals to Noundry Jobs Monitor for job monitoring. Alerts you when scheduled jobs fail to run.

var heartbeat = new JobsHeartbeat("https://jobs.noundry.com", "your-api-key");

// Signal job start
await heartbeat.StartAsync("daily-report");

// ... run job ...

// Signal job complete
await heartbeat.CompleteAsync("daily-report");

// Or signal failure
await heartbeat.FailAsync("daily-report", "Database connection timeout");

Complete Example: File Processing Pipeline

A practical example combining database, file, and email APIs to process CSV files, generate a report, and notify your team.

using Noundry.Jobs.Database;
using Noundry.Jobs.Files;
using Noundry.Jobs.Email;
using Noundry.Jobs.Heartbeat;

var heartbeat = new JobsHeartbeat("https://jobs.noundry.com", "key");
await heartbeat.StartAsync("csv-pipeline");

try
{
 var inputDir = "/data/input";
 var archiveDir = "/data/archive";
 JobsFile.CreateDirectory(archiveDir);

 // Process all CSV files in input directory
 foreach (var file in JobsFile.List(inputDir, "*.csv"))
 {
 var content = await JobsFile.ReadAllTextAsync(file);
 // ... process content ...

 // Archive with timestamp
 var name = $"{Path.GetFileNameWithoutExtension(file)}_{DateTime.UtcNow:yyyyMMdd}.csv";
 JobsFile.Move(file, Path.Combine(archiveDir, name));
 }

 // Cleanup old archives (older than 90 days)
 var db = new JobsDb(connStr, DatabaseType.PostgreSql);
 await db.ExecuteAsync("DELETE FROM logs WHERE created < @Cut",
 new { Cut = DateTime.UtcNow.AddDays(-90) });

 await heartbeat.CompleteAsync("csv-pipeline");
}
catch (Exception ex)
{
 await heartbeat.FailAsync("csv-pipeline", ex.Message);
 throw;
}

Supported Databases

Database Enum Value Connection String Example
SQL ServerDatabaseType.SqlServerServer=localhost;Database=mydb;...
PostgreSQLDatabaseType.PostgreSqlHost=localhost;Database=mydb;...
MySQLDatabaseType.MySqlServer=localhost;Database=mydb;...
SQLiteDatabaseType.SqliteData Source=local.db

Default is DatabaseType.SqlServer when no type is specified. All operations support parameterized queries to prevent SQL injection.

Related Packages