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
49 changes: 39 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ go get github.com/astronomer/epoch
package main

import (
"log"

"github.com/astronomer/epoch/epoch"
"github.com/gin-gonic/gin"
)
Expand All @@ -52,14 +54,17 @@ func main() {
v1, _ := epoch.NewSemverVersion("1.0.0")
v2, _ := epoch.NewSemverVersion("2.0.0")

migration := epoch.NewVersionChangeBuilder(v1, v2).
migration, err := epoch.NewVersionChangeBuilder(v1, v2).
Description("Add email to User").
ForType(User{}).
RequestToNextVersion().
AddField("email", "user@example.com").
ResponseToPreviousVersion().
RemoveField("email").
Build()
if err != nil {
log.Fatal(err)
}

// Setup Epoch
epochInstance, err := epoch.NewEpoch().
Expand All @@ -69,7 +74,7 @@ func main() {
Build()

if err != nil {
panic(err)
log.Fatal(err)
}

// Add to Gin
Expand Down Expand Up @@ -122,27 +127,33 @@ The new framework uses **flow-based operations** that match the actual migration
When a v1 client sends a request, it needs to be migrated TO the HEAD version:

```go
migration := epoch.NewVersionChangeBuilder(v1, v2).
migration, err := epoch.NewVersionChangeBuilder(v1, v2).
ForType(User{}).
RequestToNextVersion().
AddField("email", "default@example.com"). // Add field for old clients
RemoveField("deprecated_field"). // Remove deprecated field
RenameField("name", "full_name"). // Rename old field to new
Build()
if err != nil {
// handle err
}
```

### Response Operations (HEAD → Client)

When returning to a v1 client, response needs to be migrated FROM HEAD to v1:

```go
migration := epoch.NewVersionChangeBuilder(v1, v2).
migration, err := epoch.NewVersionChangeBuilder(v1, v2).
ForType(User{}).
ResponseToPreviousVersion().
RemoveField("email"). // Remove new fields
AddField("old_field", "default"). // Restore old fields
RenameField("full_name", "name"). // Rename back to old name
Build()
if err != nil {
// handle err
}
```

### Available Operations
Expand Down Expand Up @@ -191,7 +202,7 @@ r.GET("/users",
You can migrate multiple types together:

```go
migration := epoch.NewVersionChangeBuilder(v2, v3).
migration, err := epoch.NewVersionChangeBuilder(v2, v3).
Description("Update User and Product").
ForType(User{}).
ResponseToPreviousVersion().
Expand All @@ -200,14 +211,17 @@ migration := epoch.NewVersionChangeBuilder(v2, v3).
ResponseToPreviousVersion().
RemoveField("currency").
Build()
if err != nil {
// handle err
}
```

## Custom Transformations

Mix declarative operations with custom logic:

```go
migration := epoch.NewVersionChangeBuilder(v1, v2).
migration, err := epoch.NewVersionChangeBuilder(v1, v2).
ForType(User{}).
RequestToNextVersion().
AddField("email", "default@example.com").
Expand All @@ -219,14 +233,17 @@ migration := epoch.NewVersionChangeBuilder(v1, v2).
return nil
}).
Build()
if err != nil {
// handle err
}
```

## Global Transformers

Apply transformations to all types:

```go
migration := epoch.NewVersionChangeBuilder(v1, v2).
migration, err := epoch.NewVersionChangeBuilder(v1, v2).
CustomRequest(func(req *epoch.RequestInfo) error {
// Applies to ALL request types
return nil
Expand All @@ -239,6 +256,9 @@ migration := epoch.NewVersionChangeBuilder(v1, v2).
ResponseToPreviousVersion().
RemoveField("email").
Build()
if err != nil {
// handle err
}
```

## Helper Methods
Expand Down Expand Up @@ -385,15 +405,21 @@ Keep migrations focused on single types:

```go
// ✅ Good - separate migrations per type
userChange := epoch.NewVersionChangeBuilder(v1, v2).
userChange, err := epoch.NewVersionChangeBuilder(v1, v2).
ForType(User{}).
ResponseToPreviousVersion().RemoveField("email").
Build()
if err != nil {
// handle err
}

productChange := epoch.NewVersionChangeBuilder(v1, v2).
productChange, err := epoch.NewVersionChangeBuilder(v1, v2).
ForType(Product{}).
ResponseToPreviousVersion().RemoveField("sku").
Build()
if err != nil {
// handle err
}

// ❌ Avoid - mixing types in operations can be confusing
```
Expand All @@ -404,13 +430,16 @@ Use operations that match the actual migration direction:

```go
// ✅ Good - clear flow direction
migration := epoch.NewVersionChangeBuilder(v1, v2).
migration, err := epoch.NewVersionChangeBuilder(v1, v2).
ForType(User{}).
RequestToNextVersion(). // Client → HEAD
AddField("email", "default").
ResponseToPreviousVersion(). // HEAD → Client
RemoveField("email").
Build()
if err != nil {
// handle err
}
```

## Testing
Expand Down
Loading
Loading