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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,5 @@ go.work.sum
# .vscode/
.DS_Store
*.db
*.db-shm
*.db-wal
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,34 +38,39 @@ func main() {
log.Fatal(err)
}

plan := entities.Plan{
plan := model.Plan{
Name: "example-plan",
Output: entities.Output{
Output: model.Output{
DatasetName: "books",
Verbose: true,
},
Connectors: []entities.Connector{
Connectors: []model.Connector{
{
Name: "local",
Type: "INTERNAL",
Type: "internal",
},
},
Datasets: []entities.Dataset{
Datasets: []model.Dataset{
{
Name: "books",
ConnectorName: "local",
Type: "JSON",
Type: "json",
Data: []map[string]any{
{"id": 1, "title": "Dune"},
{"id": 2, "title": "Neuromancer"},
},
Options: entities.JSONDatasetOptions{
Timeout: &timeout,
Options: &model.JSONDatasetOptions{
Timeout: 30,
},
},
},
}

resp, err := rt.ExecutePlan(context.Background(), entities.ExecutePlanRequest{
if err := plan.Validate(); err != nil {
log.Fatalf("invalid plan: %v", err)
}

resp, err := rt.ExecutePlan(context.Background(), model.ExecutePlanRequest{
Plan: plan,
})
if err != nil {
Expand Down Expand Up @@ -147,8 +152,7 @@ type ExecutePlanResponse struct {
- Enrich local datasets with external APIs
- Query federated datasets using SQL



------------------------------------------------------------------------

## 🎯 Philosophy

Expand Down
54 changes: 0 additions & 54 deletions entities/plan.go

This file was deleted.

46 changes: 25 additions & 21 deletions examples/crypto_portfolio/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"fmt"
"log"

"github.com/kvatch-hub/kvatch-runtime/entities"
"github.com/kvatch-hub/kvatch-runtime/model"
"github.com/kvatch-hub/kvatch-runtime/pkg/runtime"
)

Expand All @@ -15,44 +15,44 @@ func main() {
log.Fatalf("failed to create runtime: %v", err)
}

plan := entities.Plan{
plan := model.Plan{
Name: "crypto_portfolio",
Connectors: []entities.Connector{
Connectors: []model.Connector{
{
Name: "coin_gecko",
Type: "api",
Connection: entities.APIConnectorConfig{
Connection: model.APIConnectorConfig{
BaseURL: "https://api.coingecko.com/api/v3",
DefaultHeaders: map[string]string{
"Accept": "application/json",
},
Auth: &entities.APIAuthConfig{
Type: entities.APIAuthNone,
Auth: &model.APIAuthConfig{
Type: model.APIAuthNone,
},
RateLimit: &entities.APIRateLimitConfig{
RateLimit: &model.APIRateLimitConfig{
RequestsPerMinute: 60,
},
Cache: &entities.APICacheConfig{
Cache: &model.APICacheConfig{
TTL: "1m",
},
},
},
{
Name: "holdings_sheet",
Type: "googlesheet",
Connection: entities.GoogleSheetConnectorConfig{
Connection: &model.GoogleSheetConnectorConfig{
SpreadsheetID: "1DYEHzASo9D8GHKpTCL_6ia2vDVoTaMSQiLhR2y7TGRQ",
ReadRange: "holdings",
},
},
},
Datasets: []entities.Dataset{
Datasets: []model.Dataset{
{
Name: "holdings",
Type: "googlesheet",
ConnectorName: "holdings_sheet",
Query: "holdings",
Options: entities.GoogleSheetDatasetOptions{
Options: &model.GoogleSheetDatasetOptions{
HeaderRowNo: 1,
EnableStreaming: false,
},
Expand All @@ -62,13 +62,13 @@ func main() {
Name: "prices",
Type: "api",
ConnectorName: "coin_gecko",
Options: entities.APIDatasetOptions{
Options: &model.APIDatasetOptions{
InjectTimestamp: true,
TimestampField: "fetched_at",
Vars: map[string]string{
"symbols": "bitcoin,ethereum",
},
Request: entities.APIRequestSpec{
Request: model.APIRequestSpec{
Method: "GET",
Path: "/simple/price",
Params: map[string]string{
Expand All @@ -78,13 +78,13 @@ func main() {
Headers: map[string]string{},
Body: nil,
},
Pagination: entities.APIPaginationConfig{
Type: entities.APIPaginationNone,
Pagination: model.APIPaginationConfig{
Type: model.APIPaginationNone,
},
Response: entities.APIResponseSpec{
Response: model.APIResponseSpec{
Format: "json",
Extract: "$",
Normalize: entities.APINormalizeConfig{
Normalize: model.APINormalizeConfig{
Enabled: true,
KeyField: "coin",
ValuePrefix: "price_",
Expand All @@ -97,7 +97,7 @@ func main() {
Name: "portfolio_value",
Type: "sql",
ConnectorName: "federated",
Options: entities.SQLDatasetOptions{
Options: &model.SQLDatasetOptions{
Query: `SELECT
h.coin,
h.holding,
Expand All @@ -108,19 +108,23 @@ func main() {
Vars: map[string]string{},
Dedupe: []string{"coin"},
},
Children: []entities.DatasetChild{
Children: []model.DatasetChild{
{DatasetName: "holdings"},
{DatasetName: "prices"},
},
},
},
Output: entities.Output{
Output: model.Output{
DatasetName: "portfolio_value",
Verbose: true,
},
}

resp, err := rt.ExecutePlan(context.Background(), entities.ExecutePlanRequest{
if err := plan.Validate(); err != nil {
log.Fatalf("invalid plan: %v", err)
}

resp, err := rt.ExecutePlan(context.Background(), model.ExecutePlanRequest{
Plan: plan,
})
if err != nil {
Expand Down
13 changes: 13 additions & 0 deletions examples/postgres/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Books database
BOOKS_POSTGRES_HOST=localhost
BOOKS_POSTGRES_PORT=5432
BOOKS_POSTGRES_DATABASE=books
BOOKS_POSTGRES_USERNAME=username
BOOKS_POSTGRES_PASSWORD=password

# Authors database
AUTHORS_POSTGRES_HOST=localhost
AUTHORS_POSTGRES_PORT=5432
AUTHORS_POSTGRES_DATABASE=authors
AUTHORS_POSTGRES_USERNAME=username
AUTHORS_POSTGRES_PASSWORD=password
110 changes: 110 additions & 0 deletions examples/postgres/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Postgres Federated Example

This example demonstrates how to use Kvatch Runtime to:

- Query data from two separate Postgres databases
- Load both datasets into the runtime
- Join them using the built-in `federated` connector
- Return a combined result set

---

## What it does

The example reads:

- `books` from a Postgres database
- `authors` from a different Postgres database

It then performs a federated SQL join:

```sql
SELECT a.name, b.title as title, b.review
FROM authors a
JOIN books b ON a.slug = b.author
```

This shows that Kvatch can join data across completely separate data sources.

---

## Prerequisites

You need access to two Postgres databases:

- A `books` database containing a `books` table
- An `authors` database containing an `authors` table

---

## Setup

1. Copy the example environment file:

```bash
cp examples/postgres/.env.example .env
```

2. Update `.env` with your database credentials.

Example:

```bash
# Books database
BOOKS_POSTGRES_HOST=localhost
BOOKS_POSTGRES_PORT=5432
BOOKS_POSTGRES_DATABASE=books
BOOKS_POSTGRES_USERNAME=root
BOOKS_POSTGRES_PASSWORD=root

# Authors database
AUTHORS_POSTGRES_HOST=localhost
AUTHORS_POSTGRES_PORT=5432
AUTHORS_POSTGRES_DATABASE=authors
AUTHORS_POSTGRES_USERNAME=root
AUTHORS_POSTGRES_PASSWORD=root
```

---

## Run the example

From the root of the repository:

```bash
go run ./examples/postgres
```

---

## Example output

```text
Columns:
- name (TEXT)
- title (TEXT)
- review (TEXT)

Rows:
map[name:Frank Herbert title:Dune review:Classic sci-fi]
map[name:William Gibson title:Neuromancer review:Cyberpunk essential]
```

---

## Notes

- Credentials are loaded from environment variables using `.env`
- The `federated` connector is handled internally by the runtime
- Each database has its own independent configuration
- This example is designed for local experimentation and learning

---

## Why this example matters

This demonstrates a core Kvatch capability:

> Query and join data across multiple independent systems using a single runtime.

No ETL. No duplication. Just declarative data access.
Loading
Loading