Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions dbhub.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
# Local PostgreSQL - DSN format (typical Docker/local dev setup)
[[sources]]
id = "local_pg"
description = "Local development database"
dsn = "postgres://postgres:postgres@localhost:5432/myapp"

# Local PostgreSQL - Individual parameters (use when password contains special characters like @, :, /)
Expand Down Expand Up @@ -249,6 +250,11 @@ dsn = "postgres://postgres:postgres@localhost:5432/myapp"
# QUICK REFERENCE
# ============================================================================
#
# Source Fields:
# id = "unique_id" # Required: unique identifier
# description = "..." # Optional: human-readable description
# dsn = "..." # Connection string (or use individual params below)
#
# DSN Formats:
# PostgreSQL: postgres://user:pass@host:5432/database?sslmode=require
# MySQL: mysql://user:pass@host:3306/database
Expand Down
1 change: 1 addition & 0 deletions src/__fixtures__/toml/readonly-maxrows.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

[[sources]]
id = "readonly_limited"
description = "Read-only database for safe queries"
type = "sqlite"
database = ":memory:"

Expand Down
13 changes: 13 additions & 0 deletions src/api/__tests__/sources.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,19 @@ describe('Data Sources API Integration Tests', () => {
expect(sqlParam!.description).toContain('SQL');
});
});

it('should include description when present', async () => {
const response = await fetch(`${BASE_URL}/api/sources`);
const sources = (await response.json()) as DataSource[];

// First source has a description
const readonlySource = sources.find(s => s.id === 'readonly_limited');
expect(readonlySource?.description).toBe('Read-only database for safe queries');

// Other sources don't have descriptions
const writableSource = sources.find(s => s.id === 'writable_limited');
expect(writableSource?.description).toBeUndefined();
});
});

describe('GET /api/sources/{source-id}', () => {
Expand Down
5 changes: 5 additions & 0 deletions src/api/openapi.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ export interface components {
* @example prod_pg
*/
id: string;
/**
* @description Human-readable description of the data source
* @example Production read replica for analytics queries
*/
description?: string;
/**
* @description Database type
* @example postgres
Expand Down
4 changes: 4 additions & 0 deletions src/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ components:
type: string
description: Unique identifier for the data source
example: prod_pg
description:
type: string
description: Human-readable description of the data source
example: Production read replica for analytics queries
type:
type: string
enum: [postgres, mysql, mariadb, sqlserver, sqlite]
Expand Down
5 changes: 5 additions & 0 deletions src/api/sources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ function transformSourceConfig(source: SourceConfig): DataSource {
type: source.type,
};

// Add description if present
if (source.description) {
dataSource.description = source.description;
}

// Add connection details (excluding password)
if (source.host) {
dataSource.host = source.host;
Expand Down
31 changes: 31 additions & 0 deletions src/config/__tests__/toml-loader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,37 @@ dsn = "postgres://user:pass@localhost:5432/testdb"
});
});

describe('description field', () => {
it('should parse description field', () => {
const tomlContent = `
[[sources]]
id = "test_db"
description = "Production read replica for analytics"
dsn = "postgres://user:pass@localhost:5432/testdb"
`;
fs.writeFileSync(path.join(tempDir, 'dbhub.toml'), tomlContent);

const result = loadTomlConfig();

expect(result).toBeTruthy();
expect(result?.sources[0].description).toBe('Production read replica for analytics');
});

it('should work without description (optional field)', () => {
const tomlContent = `
[[sources]]
id = "test_db"
dsn = "postgres://user:pass@localhost:5432/testdb"
`;
fs.writeFileSync(path.join(tempDir, 'dbhub.toml'), tomlContent);

const result = loadTomlConfig();

expect(result).toBeTruthy();
expect(result?.sources[0].description).toBeUndefined();
});
});

describe('sslmode validation', () => {
it('should accept sslmode = "disable"', () => {
const tomlContent = `
Expand Down
1 change: 1 addition & 0 deletions src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface ConnectionParams {
*/
export interface SourceConfig extends ConnectionParams, SSHConfig {
id: string;
description?: string; // Human-readable description of this data source
dsn?: string;
connection_timeout?: number; // Connection timeout in seconds
query_timeout?: number; // Query timeout in seconds (PostgreSQL, MySQL, MariaDB, SQL Server)
Expand Down
Loading