Fe overhaul docs (#1640)

* api changes

* doc changes

* move docs

* generated

* generate

* pkg

* backmerge main

* revert to main

* revert main

* race?

* remove go tests
This commit is contained in:
Gabe Ruttner
2025-04-30 17:10:09 -04:00
committed by GitHub
parent 799b5d0dcf
commit 8e80faf2d6
1503 changed files with 36645 additions and 1235 deletions

View File

@@ -9,3 +9,22 @@ updates:
directory: "/"
schedule:
interval: "daily"
# // We need to update the quickstart examples for typescript, go, and python
- package-ecosystem: "npm"
directory: "/examples/typescript"
schedule:
interval: "daily"
- package-ecosystem: "gomod"
directory: "/examples/go/quickstart"
schedule:
interval: "daily"
# TODO python quickstart
- package-ecosystem: "pip"
directory: "/examples/python/quickstart"
schedule:
interval: "daily"

View File

@@ -0,0 +1,29 @@
name: Sync Go Quickstart
on:
push:
branches:
- main
paths:
- 'examples/go/quickstart/**'
workflow_dispatch:
inputs:
manual_trigger:
description: 'Manually trigger the sync'
default: 'true'
required: false
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Run sync script
env:
SYNC_TOKEN: ${{ secrets.SYNC_TOKEN }}
run: |
./hack/sync-quickstart.sh --source-dir "examples/go/quickstart" --target-repo "hatchet-dev/hatchet-go-quickstart"

View File

@@ -0,0 +1,29 @@
name: Sync Python Quickstart
on:
push:
branches:
- main
paths:
- 'examples/python/quickstart/**'
workflow_dispatch:
inputs:
manual_trigger:
description: 'Manually trigger the sync'
default: 'true'
required: false
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Run sync script
env:
SYNC_TOKEN: ${{ secrets.SYNC_TOKEN }}
run: |
./hack/sync-quickstart.sh --source-dir "examples/python/quickstart" --target-repo "hatchet-dev/hatchet-python-quickstart"

View File

@@ -0,0 +1,29 @@
name: Sync TypeScript Quickstart
on:
push:
branches:
- main
paths:
- 'examples/typescript/quickstart/**'
workflow_dispatch:
inputs:
manual_trigger:
description: 'Manually trigger the sync'
default: 'true'
required: false
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Run sync script
env:
SYNC_TOKEN: ${{ secrets.SYNC_TOKEN }}
run: |
./hack/sync-typescript-quickstart.sh --source-dir "examples/typescript/quickstart" --target-repo "grutt/hatchet-typescript-quickstart"

View File

@@ -68,7 +68,7 @@ jobs:
run: go generate ./...
- name: Test
run: go test ./... -v -failfast
run: go test $(go list ./... | grep -v "quickstart") -v -failfast
integration:
runs-on: ubuntu-latest
@@ -109,7 +109,7 @@ jobs:
task generate-local-encryption-keys
- name: Test
run: go test -tags integration ./... -v -failfast
run: go test -tags integration $(go list ./... | grep -v "quickstart") -v -failfast
- name: Teardown
run: docker compose down
@@ -293,11 +293,8 @@ jobs:
load:
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
migrate-strategy: ["latest", "penultimate"]
rabbitmq-enabled: ["true", "false"]
pg-version: ["17-alpine", "16-alpine", "15-alpine"]
env:
DATABASE_URL: postgresql://hatchet:hatchet@127.0.0.1:5431/hatchet?sslmode=disable
steps:
- uses: actions/checkout@v4
@@ -315,13 +312,131 @@ jobs:
version: 9.15.4
run_install: false
- name: Install Atlas
run: |
curl -sSf https://atlasgo.sh | sh
- name: Compose
run: docker compose up -d
- name: Go deps
run: go mod download
- name: Prepare
run: |
cat > .env <<EOF
DATABASE_URL='postgresql://hatchet:hatchet@127.0.0.1:5431/hatchet'
SERVER_TLS_CERT_FILE=./hack/dev/certs/cluster.pem
SERVER_TLS_KEY_FILE=./hack/dev/certs/cluster.key
SERVER_TLS_ROOT_CA_FILE=./hack/dev/certs/ca.cert
SERVER_PORT=8080
SERVER_URL=http://localhost:8080
SERVER_AUTH_COOKIE_SECRETS="something something"
SERVER_AUTH_COOKIE_DOMAIN=app.dev.hatchet-tools.com
SERVER_AUTH_COOKIE_INSECURE=false
SERVER_AUTH_SET_EMAIL_VERIFIED=true
SERVER_LOGGER_LEVEL=warn
SERVER_LOGGER_FORMAT=console
DATABASE_LOGGER_LEVEL=warn
DATABASE_LOGGER_FORMAT=console
EOF
- name: Generate
run: |
go run ./cmd/hatchet-migrate
task generate-go
task generate-certs
task generate-local-encryption-keys
- name: Setup
run: |
set -a
. .env
set +a
go run ./cmd/hatchet-admin quickstart --generated-config-dir ./generated/
- name: Test
run: |
export HATCHET_CLIENT_TOKEN="$(go run ./cmd/hatchet-admin token create --config ./generated/ --tenant-id 707d0855-80ab-4e1f-a156-f1c4546cbf52)"
go test -tags load ./... -p 1 -v -race -failfast
env:
TESTING_MATRIX_MIGRATE: ${{ matrix.migrate-strategy }}
TESTING_MATRIX_RABBITMQ_ENABLED: ${{ matrix.rabbitmq-enabled }}
TESTING_MATRIX_PG_VERSION: ${{ matrix.pg-version }}
- name: Teardown
run: docker compose down
load-pgmq:
runs-on: ubuntu-latest
timeout-minutes: 30
env:
DATABASE_URL: postgresql://hatchet:hatchet@127.0.0.1:5431/hatchet?sslmode=disable
steps:
- uses: actions/checkout@v4
- name: Install Task
uses: arduino/setup-task@v2
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.22"
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9.15.4
run_install: false
- name: Install Atlas
run: |
curl -sSf https://atlasgo.sh | sh
- name: Compose
run: docker compose up -d
- name: Go deps
run: go mod download
- name: Prepare
run: |
cat > .env <<EOF
DATABASE_URL='postgresql://hatchet:hatchet@127.0.0.1:5431/hatchet'
SERVER_TLS_CERT_FILE=./hack/dev/certs/cluster.pem
SERVER_TLS_KEY_FILE=./hack/dev/certs/cluster.key
SERVER_TLS_ROOT_CA_FILE=./hack/dev/certs/ca.cert
SERVER_PORT=8080
SERVER_URL=http://localhost:8080
SERVER_AUTH_COOKIE_SECRETS="something something"
SERVER_AUTH_COOKIE_DOMAIN=app.dev.hatchet-tools.com
SERVER_AUTH_COOKIE_INSECURE=false
SERVER_AUTH_SET_EMAIL_VERIFIED=true
SERVER_LOGGER_LEVEL=warn
SERVER_LOGGER_FORMAT=console
DATABASE_LOGGER_LEVEL=warn
DATABASE_LOGGER_FORMAT=console
SERVER_TASKQUEUE_KIND=postgres
EOF
- name: Generate
run: |
go run ./cmd/hatchet-migrate
task generate-go
task generate-certs
task generate-local-encryption-keys
- name: Setup
run: |
set -a
. .env
set +a
go run ./cmd/hatchet-admin quickstart --generated-config-dir ./generated/
- name: Test
run: |
export HATCHET_CLIENT_TOKEN="$(go run ./cmd/hatchet-admin token create --config ./generated/ --tenant-id 707d0855-80ab-4e1f-a156-f1c4546cbf52)"
RAMP_UP_DURATION_TIMEOUT=20s go test -tags load ./... -p 1 -v -race -failfast
- name: Teardown
run: docker compose down

View File

@@ -56,6 +56,7 @@ tasks:
pre:
cmds:
# FIXME: Remove this once we have a better way to handle pre-commit reliably
- cd frontend/snips/ && pnpm i && pnpm generate && pnpm run copy:all
- pre-commit run --all-files
- pre-commit run --all-files
- pre-commit run --all-files

View File

@@ -10,7 +10,7 @@ RUN go mod download
COPY /pkg ./pkg
COPY /internal ./internal
COPY /api ./api
COPY /examples/loadtest/cli ./cli
COPY /examples/go/z_v0/loadtest/cli ./cli
# Go build environment
# --------------------

28
examples/README.md Normal file
View File

@@ -0,0 +1,28 @@
## Hatchet Examples
### Contributing
These examples are generated from SDK source where they include, to contribute updates to these docs please make these changes in the sdk source:
- Typescript: ../sdks/typescript/src/v1/examples
- Python: ../sdks/python/examples
- Go: ../pkg/examples
#### Comment Markup
We use a special comment markup to generate code snips for our docs. Markups support single line comments in typescript/go `//` or python `#`.
##### Blocks
```
// > Block Title
BLOCK CONTENT
// !!
```
##### Highlights
```
HH-name 1 hello
hello world
```
Where 1 is the number of lines to highlight and hello are strings to specifically highlight.

View File

@@ -0,0 +1,233 @@
package migration_guides
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/hatchet-dev/hatchet/pkg/client/create"
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
"github.com/hatchet-dev/hatchet/pkg/v1/factory"
v1worker "github.com/hatchet-dev/hatchet/pkg/v1/worker"
"github.com/hatchet-dev/hatchet/pkg/v1/workflow"
"github.com/hatchet-dev/hatchet/pkg/worker"
)
// ProcessImage simulates image processing
func ProcessImage(imageURL string, filters []string) (map[string]interface{}, error) {
// Do some image processing
return map[string]interface{}{
"url": imageURL,
"size": 100,
"format": "png",
}, nil
}
// > Before (Mergent)
type MergentRequest struct {
ImageURL string `json:"image_url"`
Filters []string `json:"filters"`
}
type MergentResponse struct {
Success bool `json:"success"`
ProcessedURL string `json:"processed_url"`
}
func ProcessImageMergent(req MergentRequest) (*MergentResponse, error) {
result, err := ProcessImage(req.ImageURL, req.Filters)
if err != nil {
return nil, err
}
return &MergentResponse{
Success: true,
ProcessedURL: result["url"].(string),
}, nil
}
// > After (Hatchet)
type ImageProcessInput struct {
ImageURL string `json:"image_url"`
Filters []string `json:"filters"`
}
type ImageProcessOutput struct {
ProcessedURL string `json:"processed_url"`
Metadata struct {
Size int `json:"size"`
Format string `json:"format"`
AppliedFilters []string `json:"applied_filters"`
} `json:"metadata"`
}
func ImageProcessor(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[ImageProcessInput, ImageProcessOutput] {
processor := factory.NewTask(
create.StandaloneTask{
Name: "image-processor",
},
func(ctx worker.HatchetContext, input ImageProcessInput) (*ImageProcessOutput, error) {
result, err := ProcessImage(input.ImageURL, input.Filters)
if err != nil {
return nil, fmt.Errorf("processing image: %w", err)
}
if result["url"] == "" {
return nil, fmt.Errorf("processing failed to generate URL")
}
output := &ImageProcessOutput{
ProcessedURL: result["url"].(string),
Metadata: struct {
Size int `json:"size"`
Format string `json:"format"`
AppliedFilters []string `json:"applied_filters"`
}{
Size: result["size"].(int),
Format: result["format"].(string),
AppliedFilters: input.Filters,
},
}
return output, nil
},
hatchet,
)
// Example of running a task
_ = func() error {
// > Running a task
result, err := processor.Run(context.Background(), ImageProcessInput{
ImageURL: "https://example.com/image.png",
Filters: []string{"blur"},
})
if err != nil {
return err
}
fmt.Printf("Result: %+v\n", result)
return nil
}
// Example of registering a task on a worker
_ = func() error {
// > Declaring a Worker
w, err := hatchet.Worker(v1worker.WorkerOpts{
Name: "image-processor-worker",
Workflows: []workflow.WorkflowBase{
processor,
},
})
if err != nil {
return err
}
err = w.StartBlocking(context.Background())
if err != nil {
return err
}
return nil
}
return processor
}
func RunMergentTask() error {
return nil
}
func RunningTasks(hatchet v1.HatchetClient) error {
// > Running a task (Mergent)
task := struct {
Request struct {
URL string `json:"url"`
Body string `json:"body"`
Headers map[string]string `json:"headers"`
} `json:"request"`
Name string `json:"name"`
Queue string `json:"queue"`
}{
Request: struct {
URL string `json:"url"`
Body string `json:"body"`
Headers map[string]string `json:"headers"`
}{
URL: "https://example.com",
Headers: map[string]string{
"Authorization": "fake-secret-token",
"Content-Type": "application/json",
},
Body: "Hello, world!",
},
Name: "4cf95241-fa19-47ef-8a67-71e483747649",
Queue: "default",
}
taskJSON, err := json.Marshal(task)
if err != nil {
return fmt.Errorf("marshaling task: %w", err)
}
req, err := http.NewRequest(http.MethodPost, "https://api.mergent.co/v2/tasks", bytes.NewBuffer(taskJSON))
if err != nil {
return fmt.Errorf("creating request: %w", err)
}
req.Header.Add("Authorization", "Bearer <API_KEY>")
req.Header.Add("Content-Type", "application/json")
client := &http.Client{}
res, err := client.Do(req)
if err != nil {
return fmt.Errorf("sending request: %w", err)
}
defer res.Body.Close()
fmt.Printf("Mergent task created with status: %d\n", res.StatusCode)
// > Running a task (Hatchet)
processor := ImageProcessor(hatchet)
result, err := processor.Run(context.Background(), ImageProcessInput{
ImageURL: "https://example.com/image.png",
Filters: []string{"blur"},
})
if err != nil {
return err
}
fmt.Printf("Result: %+v\n", result)
// > Scheduling tasks (Hatchet)
// Schedule the task to run at a specific time
scheduleRef, err := processor.Schedule(
context.Background(),
time.Now().Add(time.Second*10),
ImageProcessInput{
ImageURL: "https://example.com/image.png",
Filters: []string{"blur"},
},
)
if err != nil {
return err
}
// or schedule to run every hour
cronRef, err := processor.Cron(
context.Background(),
"run-hourly",
"0 * * * *",
ImageProcessInput{
ImageURL: "https://example.com/image.png",
Filters: []string{"blur"},
},
)
if err != nil {
return err
}
fmt.Printf("Scheduled tasks with refs: %+v, %+v\n", scheduleRef, cronRef)
return nil
}

View File

@@ -0,0 +1,48 @@
# Hatchet First Workflow Example
This is an example project demonstrating how to use Hatchet with Go. For detailed setup instructions, see the [Hatchet Setup Guide](https://docs.hatchet.run/home/setup).
## Prerequisites
Before running this project, make sure you have the following:
1. [Go v1.22 or higher](https://go.dev/doc/install)
## Setup
1. Clone the repository:
```bash
git clone https://github.com/hatchet-dev/hatchet-go-quickstart.git
cd hatchet-go-quickstart
```
2. Set the required environment variable `HATCHET_CLIENT_TOKEN` created in the [Getting Started Guide](https://docs.hatchet.run/home/hatchet-cloud-quickstart).
```bash
export HATCHET_CLIENT_TOKEN=<token>
```
> Note: If you're self hosting you may need to set `HATCHET_CLIENT_TLS_STRATEGY=none` to disable TLS
3. Install the project dependencies:
```bash
go mod tidy
```
### Running an example
1. Start a Hatchet worker:
```bash
go run cmd/worker/main.go
```
2. In a new terminal, run the example task:
```bash
go run cmd/run/main.go
```
This will trigger the task on the worker running in the first terminal and print the output to the second terminal.

View File

@@ -0,0 +1,32 @@
package main
import (
"context"
"fmt"
hatchet_client "github.com/hatchet-dev/hatchet/pkg/examples/quickstart/hatchet_client"
workflows "github.com/hatchet-dev/hatchet/pkg/examples/quickstart/workflows"
)
func main() {
hatchet, err := hatchet_client.HatchetClient()
if err != nil {
panic(err)
}
simple := workflows.FirstTask(hatchet)
result, err := simple.Run(context.Background(), workflows.SimpleInput{
Message: "Hello, World!",
})
if err != nil {
panic(err)
}
fmt.Println(
"Finished running task, and got the transformed message! The transformed message is:",
result.ToLower.TransformedMessage,
)
}

View File

@@ -0,0 +1,44 @@
package main
import (
hatchet_client "github.com/hatchet-dev/hatchet/pkg/examples/quickstart/hatchet_client"
workflows "github.com/hatchet-dev/hatchet/pkg/examples/quickstart/workflows"
"github.com/hatchet-dev/hatchet/pkg/cmdutils"
"github.com/hatchet-dev/hatchet/pkg/v1/worker"
"github.com/hatchet-dev/hatchet/pkg/v1/workflow"
)
func main() {
hatchet, err := hatchet_client.HatchetClient()
if err != nil {
panic(err)
}
worker, err := hatchet.Worker(
worker.WorkerOpts{
Name: "first-worker",
Workflows: []workflow.WorkflowBase{
workflows.FirstTask(hatchet),
},
},
)
if err != nil {
panic(err)
}
// we construct an interrupt context to handle Ctrl+C
// you can pass in your own context.Context here to the worker
interruptCtx, cancel := cmdutils.NewInterruptContext()
defer cancel()
err = worker.StartBlocking(interruptCtx)
if err != nil {
panic(err)
}
}

View File

@@ -0,0 +1,14 @@
package hatchet_client
import (
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
"github.com/joho/godotenv"
)
func HatchetClient() (v1.HatchetClient, error) {
err := godotenv.Load()
if err != nil {
return nil, err
}
return v1.NewHatchetClient()
}

View File

@@ -0,0 +1,47 @@
package workflows
import (
"fmt"
"strings"
"github.com/hatchet-dev/hatchet/pkg/client/create"
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
"github.com/hatchet-dev/hatchet/pkg/v1/factory"
"github.com/hatchet-dev/hatchet/pkg/v1/workflow"
"github.com/hatchet-dev/hatchet/pkg/worker"
)
type SimpleInput struct {
Message string `json:"message"`
}
type LowerOutput struct {
TransformedMessage string `json:"transformed_message"`
}
type SimpleResult struct {
ToLower LowerOutput
}
func FirstTask(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[SimpleInput, SimpleResult] {
simple := factory.NewWorkflow[SimpleInput, SimpleResult](
create.WorkflowCreateOpts[SimpleInput]{
Name: "first-task",
},
hatchet,
)
simple.Task(
create.WorkflowTask[SimpleInput, SimpleResult]{
Name: "first-task",
},
func(ctx worker.HatchetContext, input SimpleInput) (any, error) {
fmt.Println("first-task task called")
return &LowerOutput{
TransformedMessage: strings.ToLower(input.Message),
}, nil
},
)
return simple
}

View File

@@ -8,7 +8,7 @@ import (
"time"
"github.com/google/uuid"
v1_workflows "github.com/hatchet-dev/hatchet/examples/v1/workflows"
v1_workflows "github.com/hatchet-dev/hatchet/examples/go/workflows"
"github.com/hatchet-dev/hatchet/pkg/client"
"github.com/hatchet-dev/hatchet/pkg/client/rest"
v1 "github.com/hatchet-dev/hatchet/pkg/v1"

41
examples/go/run/bulk.go Normal file
View File

@@ -0,0 +1,41 @@
package main
import (
"context"
"fmt"
v1_workflows "github.com/hatchet-dev/hatchet/examples/go/workflows"
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
"github.com/joho/godotenv"
)
func bulk() {
err := godotenv.Load()
if err != nil {
panic(err)
}
hatchet, err := v1.NewHatchetClient()
if err != nil {
panic(err)
}
ctx := context.Background()
// > Bulk Run Tasks
simple := v1_workflows.Simple(hatchet)
bulkRunIds, err := simple.RunBulkNoWait(ctx, []v1_workflows.SimpleInput{
{
Message: "Hello, World!",
},
{
Message: "Hello, Moon!",
},
})
if err != nil {
panic(err)
}
fmt.Println(bulkRunIds)
}

56
examples/go/run/cron.go Normal file
View File

@@ -0,0 +1,56 @@
package main
import (
"context"
"fmt"
v1_workflows "github.com/hatchet-dev/hatchet/examples/go/workflows"
"github.com/hatchet-dev/hatchet/pkg/client/rest"
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
"github.com/joho/godotenv"
)
func cron() {
err := godotenv.Load()
if err != nil {
panic(err)
}
hatchet, err := v1.NewHatchetClient()
if err != nil {
panic(err)
}
// > Create
simple := v1_workflows.Simple(hatchet)
ctx := context.Background()
result, err := simple.Cron(
ctx,
"daily-run",
"0 0 * * *",
v1_workflows.SimpleInput{
Message: "Hello, World!",
},
)
if err != nil {
panic(err)
}
// it may be useful to save the cron id for later
fmt.Println(result.Metadata.Id)
// > Delete
hatchet.Crons().Delete(ctx, result.Metadata.Id)
// > List
crons, err := hatchet.Crons().List(ctx, rest.CronWorkflowListParams{
AdditionalMetadata: &[]string{"user:daily-run"},
})
if err != nil {
panic(err)
}
fmt.Println(crons)
}

34
examples/go/run/event.go Normal file
View File

@@ -0,0 +1,34 @@
package main
import (
"context"
v1_workflows "github.com/hatchet-dev/hatchet/examples/go/workflows"
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
"github.com/joho/godotenv"
)
func event() {
err := godotenv.Load()
if err != nil {
panic(err)
}
hatchet, err := v1.NewHatchetClient()
if err != nil {
panic(err)
}
// > Pushing an Event
err = hatchet.Events().Push(
context.Background(),
"simple-event:create",
v1_workflows.SimpleInput{
Message: "Hello, World!",
},
)
if err != nil {
panic(err)
}
}

View File

@@ -0,0 +1,58 @@
package main
import (
"context"
"fmt"
"time"
v1_workflows "github.com/hatchet-dev/hatchet/examples/go/workflows"
"github.com/hatchet-dev/hatchet/pkg/client"
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
"github.com/joho/godotenv"
)
func priority() {
err := godotenv.Load()
if err != nil {
panic(err)
}
hatchet, err := v1.NewHatchetClient()
if err != nil {
panic(err)
}
ctx := context.Background()
priorityWorkflow := v1_workflows.Priority(hatchet)
// > Running a Task with Priority
priority := int32(3)
runId, err := priorityWorkflow.RunNoWait(ctx, v1_workflows.PriorityInput{
UserId: "1234",
}, client.WithPriority(priority))
if err != nil {
panic(err)
}
fmt.Println(runId)
// > Schedule and cron
schedulePriority := int32(3)
runAt := time.Now().Add(time.Minute)
scheduledRunId, _ := priorityWorkflow.Schedule(ctx, runAt, v1_workflows.PriorityInput{
UserId: "1234",
}, client.WithPriority(schedulePriority))
cronId, _ := priorityWorkflow.Cron(ctx, "my-cron", "* * * * *", v1_workflows.PriorityInput{
UserId: "1234",
}, client.WithPriority(schedulePriority))
fmt.Println(scheduledRunId)
fmt.Println(cronId)
}

109
examples/go/run/simple.go Normal file
View File

@@ -0,0 +1,109 @@
package main
import (
"context"
"fmt"
"sync"
v1_workflows "github.com/hatchet-dev/hatchet/examples/go/workflows"
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
"github.com/joho/godotenv"
)
func simple() {
err := godotenv.Load()
if err != nil {
panic(err)
}
hatchet, err := v1.NewHatchetClient()
if err != nil {
panic(err)
}
ctx := context.Background()
// > Running a Task
simple := v1_workflows.Simple(hatchet)
result, err := simple.Run(ctx, v1_workflows.SimpleInput{
Message: "Hello, World!",
})
if err != nil {
panic(err)
}
fmt.Println(result.TransformedMessage)
// > Running Multiple Tasks
var results []string
var resultsMutex sync.Mutex
var errs []error
var errsMutex sync.Mutex
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
result, err := simple.Run(ctx, v1_workflows.SimpleInput{
Message: "Hello, World!",
})
if err != nil {
errsMutex.Lock()
errs = append(errs, err)
errsMutex.Unlock()
return
}
resultsMutex.Lock()
results = append(results, result.TransformedMessage)
resultsMutex.Unlock()
}()
go func() {
defer wg.Done()
result, err := simple.Run(ctx, v1_workflows.SimpleInput{
Message: "Hello, Moon!",
})
if err != nil {
errsMutex.Lock()
errs = append(errs, err)
errsMutex.Unlock()
return
}
resultsMutex.Lock()
results = append(results, result.TransformedMessage)
resultsMutex.Unlock()
}()
wg.Wait()
// > Running a Task Without Waiting
simple = v1_workflows.Simple(hatchet)
runRef, err := simple.RunNoWait(ctx, v1_workflows.SimpleInput{
Message: "Hello, World!",
})
if err != nil {
panic(err)
}
// The Run Ref Exposes an ID that can be used to wait for the task to complete
// or check on the status of the task
runId := runRef.RunId()
fmt.Println(runId)
// > Subscribing to results
// finally, we can wait for the task to complete and get the result
finalResult, err := runRef.Result()
if err != nil {
panic(err)
}
fmt.Println(finalResult)
}

View File

@@ -5,7 +5,7 @@ import (
"os"
"time"
v1_workflows "github.com/hatchet-dev/hatchet/examples/v1/workflows"
v1_workflows "github.com/hatchet-dev/hatchet/examples/go/workflows"
"github.com/hatchet-dev/hatchet/pkg/cmdutils"
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
"github.com/hatchet-dev/hatchet/pkg/v1/worker"

View File

@@ -0,0 +1,46 @@
package v1_workflows
import (
"errors"
"time"
"github.com/hatchet-dev/hatchet/pkg/client/create"
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
"github.com/hatchet-dev/hatchet/pkg/v1/factory"
"github.com/hatchet-dev/hatchet/pkg/v1/workflow"
"github.com/hatchet-dev/hatchet/pkg/worker"
)
type CancellationInput struct{}
type CancellationResult struct {
Completed bool
}
func Cancellation(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[CancellationInput, CancellationResult] {
// > Cancelled task
// Create a task that sleeps for 10 seconds and checks if it was cancelled
cancellation := factory.NewTask(
create.StandaloneTask{
Name: "cancellation-task",
}, func(ctx worker.HatchetContext, input CancellationInput) (*CancellationResult, error) {
// Sleep for 10 seconds
time.Sleep(10 * time.Second)
// Check if the context was cancelled
select {
case <-ctx.Done():
return nil, errors.New("Task was cancelled")
default:
// Continue execution
}
return &CancellationResult{
Completed: true,
}, nil
},
hatchet,
)
return cancellation
}

View File

@@ -0,0 +1,200 @@
package v1_workflows
import (
"math/rand"
"time"
"github.com/hatchet-dev/hatchet/pkg/client/create"
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
"github.com/hatchet-dev/hatchet/pkg/v1/factory"
"github.com/hatchet-dev/hatchet/pkg/v1/workflow"
"github.com/hatchet-dev/hatchet/pkg/worker"
"github.com/hatchet-dev/hatchet/pkg/worker/condition"
)
// StepOutput represents the output of most tasks in this workflow
type StepOutput struct {
RandomNumber int `json:"randomNumber"`
}
// RandomSum represents the output of the sum task
type RandomSum struct {
Sum int `json:"sum"`
}
// TaskConditionWorkflowResult represents the aggregate output of all tasks
type TaskConditionWorkflowResult struct {
Start StepOutput `json:"start"`
WaitForSleep StepOutput `json:"waitForSleep"`
WaitForEvent StepOutput `json:"waitForEvent"`
SkipOnEvent StepOutput `json:"skipOnEvent"`
LeftBranch StepOutput `json:"leftBranch"`
RightBranch StepOutput `json:"rightBranch"`
Sum RandomSum `json:"sum"`
}
// taskOpts is a type alias for workflow task options
type taskOpts = create.WorkflowTask[struct{}, TaskConditionWorkflowResult]
func TaskConditionWorkflow(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[struct{}, TaskConditionWorkflowResult] {
// > Create a workflow
wf := factory.NewWorkflow[struct{}, TaskConditionWorkflowResult](
create.WorkflowCreateOpts[struct{}]{
Name: "TaskConditionWorkflow",
},
hatchet,
)
// > Add base task
start := wf.Task(
taskOpts{
Name: "start",
},
func(ctx worker.HatchetContext, _ struct{}) (interface{}, error) {
return &StepOutput{
RandomNumber: rand.Intn(100) + 1,
}, nil
},
)
// > Add wait for sleep
waitForSleep := wf.Task(
taskOpts{
Name: "waitForSleep",
Parents: []create.NamedTask{start},
WaitFor: condition.SleepCondition(time.Second * 10),
},
func(ctx worker.HatchetContext, _ struct{}) (interface{}, error) {
return &StepOutput{
RandomNumber: rand.Intn(100) + 1,
}, nil
},
)
// > Add skip on event
skipOnEvent := wf.Task(
taskOpts{
Name: "skipOnEvent",
Parents: []create.NamedTask{start},
WaitFor: condition.SleepCondition(time.Second * 30),
SkipIf: condition.UserEventCondition("skip_on_event:skip", "true"),
},
func(ctx worker.HatchetContext, _ struct{}) (interface{}, error) {
return &StepOutput{
RandomNumber: rand.Intn(100) + 1,
}, nil
},
)
// > Add branching
leftBranch := wf.Task(
taskOpts{
Name: "leftBranch",
Parents: []create.NamedTask{waitForSleep},
SkipIf: condition.ParentCondition(waitForSleep, "output.randomNumber > 50"),
},
func(ctx worker.HatchetContext, _ struct{}) (interface{}, error) {
return &StepOutput{
RandomNumber: rand.Intn(100) + 1,
}, nil
},
)
rightBranch := wf.Task(
taskOpts{
Name: "rightBranch",
Parents: []create.NamedTask{waitForSleep},
SkipIf: condition.ParentCondition(waitForSleep, "output.randomNumber <= 50"),
},
func(ctx worker.HatchetContext, _ struct{}) (interface{}, error) {
return &StepOutput{
RandomNumber: rand.Intn(100) + 1,
}, nil
},
)
// > Add wait for event
waitForEvent := wf.Task(
taskOpts{
Name: "waitForEvent",
Parents: []create.NamedTask{start},
WaitFor: condition.Or(
condition.SleepCondition(time.Minute),
condition.UserEventCondition("wait_for_event:start", "true"),
),
},
func(ctx worker.HatchetContext, _ struct{}) (interface{}, error) {
return &StepOutput{
RandomNumber: rand.Intn(100) + 1,
}, nil
},
)
// > Add sum
wf.Task(
taskOpts{
Name: "sum",
Parents: []create.NamedTask{
start,
waitForSleep,
waitForEvent,
skipOnEvent,
leftBranch,
rightBranch,
},
},
func(ctx worker.HatchetContext, _ struct{}) (interface{}, error) {
var startOutput StepOutput
if err := ctx.ParentOutput(start, &startOutput); err != nil {
return nil, err
}
var waitForSleepOutput StepOutput
if err := ctx.ParentOutput(waitForSleep, &waitForSleepOutput); err != nil {
return nil, err
}
var waitForEventOutput StepOutput
ctx.ParentOutput(waitForEvent, &waitForEventOutput)
// Handle potentially skipped tasks
var skipOnEventOutput StepOutput
var four int
err := ctx.ParentOutput(skipOnEvent, &skipOnEventOutput)
if err != nil {
four = 0
} else {
four = skipOnEventOutput.RandomNumber
}
var leftBranchOutput StepOutput
var five int
err = ctx.ParentOutput(leftBranch, leftBranchOutput)
if err != nil {
five = 0
} else {
five = leftBranchOutput.RandomNumber
}
var rightBranchOutput StepOutput
var six int
err = ctx.ParentOutput(rightBranch, rightBranchOutput)
if err != nil {
six = 0
} else {
six = rightBranchOutput.RandomNumber
}
return &RandomSum{
Sum: startOutput.RandomNumber + waitForEventOutput.RandomNumber +
waitForSleepOutput.RandomNumber + four + five + six,
}, nil
},
)
return wf
}

View File

@@ -0,0 +1,86 @@
package v1_workflows
import (
"math/rand"
"time"
"github.com/hatchet-dev/hatchet/pkg/client/create"
"github.com/hatchet-dev/hatchet/pkg/client/types"
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
"github.com/hatchet-dev/hatchet/pkg/v1/factory"
"github.com/hatchet-dev/hatchet/pkg/v1/workflow"
"github.com/hatchet-dev/hatchet/pkg/worker"
)
type ConcurrencyInput struct {
Message string
Tier string
Account string
}
type TransformedOutput struct {
TransformedMessage string
}
func ConcurrencyRoundRobin(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[ConcurrencyInput, TransformedOutput] {
// > Concurrency Strategy With Key
var maxRuns int32 = 1
strategy := types.GroupRoundRobin
concurrency := factory.NewTask(
create.StandaloneTask{
Name: "simple-concurrency",
Concurrency: []*types.Concurrency{
{
Expression: "input.GroupKey",
MaxRuns: &maxRuns,
LimitStrategy: &strategy,
},
},
}, func(ctx worker.HatchetContext, input ConcurrencyInput) (*TransformedOutput, error) {
// Random sleep between 200ms and 1000ms
time.Sleep(time.Duration(200+rand.Intn(800)) * time.Millisecond)
return &TransformedOutput{
TransformedMessage: input.Message,
}, nil
},
hatchet,
)
return concurrency
}
func MultipleConcurrencyKeys(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[ConcurrencyInput, TransformedOutput] {
// > Multiple Concurrency Keys
strategy := types.GroupRoundRobin
var maxRuns int32 = 20
concurrency := factory.NewTask(
create.StandaloneTask{
Name: "simple-concurrency",
Concurrency: []*types.Concurrency{
{
Expression: "input.Tier",
MaxRuns: &maxRuns,
LimitStrategy: &strategy,
},
{
Expression: "input.Account",
MaxRuns: &maxRuns,
LimitStrategy: &strategy,
},
},
}, func(ctx worker.HatchetContext, input ConcurrencyInput) (*TransformedOutput, error) {
// Random sleep between 200ms and 1000ms
time.Sleep(time.Duration(200+rand.Intn(800)) * time.Millisecond)
return &TransformedOutput{
TransformedMessage: input.Message,
}, nil
},
hatchet,
)
return concurrency
}

View File

@@ -22,16 +22,15 @@ type DagResult struct {
}
func DagWorkflow(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[DagInput, DagResult] {
// Declaring a Workflow
// > Declaring a Workflow
simple := factory.NewWorkflow[DagInput, DagResult](
create.WorkflowCreateOpts[DagInput]{
Name: "simple-dag",
},
hatchet,
)
// ‼️
// Defining a Task
// > Defining a Task
simple.Task(
create.WorkflowTask[DagInput, DagResult]{
Name: "step",
@@ -41,9 +40,8 @@ func DagWorkflow(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[DagInput
}, nil
},
)
// ‼️
// Adding a Task with a parent
// > Adding a Task with a parent
step1 := simple.Task(
create.WorkflowTask[DagInput, DagResult]{
Name: "step-1",
@@ -73,7 +71,6 @@ func DagWorkflow(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[DagInput
}, nil
},
)
// ‼️
return simple
}

View File

@@ -0,0 +1,77 @@
package v1_workflows
import (
"github.com/hatchet-dev/hatchet/pkg/client/create"
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
"github.com/hatchet-dev/hatchet/pkg/v1/factory"
"github.com/hatchet-dev/hatchet/pkg/v1/workflow"
"github.com/hatchet-dev/hatchet/pkg/worker"
)
type DurableEventInput struct {
Message string
}
type EventData struct {
Message string
}
type DurableEventOutput struct {
Data EventData
}
func DurableEvent(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[DurableEventInput, DurableEventOutput] {
// > Durable Event
durableEventTask := factory.NewDurableTask(
create.StandaloneTask{
Name: "durable-event",
},
func(ctx worker.DurableHatchetContext, input DurableEventInput) (*DurableEventOutput, error) {
eventData, err := ctx.WaitForEvent("user:update", "")
if err != nil {
return nil, err
}
v := EventData{}
err = eventData.Unmarshal(&v)
if err != nil {
return nil, err
}
return &DurableEventOutput{
Data: v,
}, nil
},
hatchet,
)
factory.NewDurableTask(
create.StandaloneTask{
Name: "durable-event",
},
func(ctx worker.DurableHatchetContext, input DurableEventInput) (*DurableEventOutput, error) {
// > Durable Event With Filter
eventData, err := ctx.WaitForEvent("user:update", "input.user_id == '1234'")
if err != nil {
return nil, err
}
v := EventData{}
err = eventData.Unmarshal(&v)
if err != nil {
return nil, err
}
return &DurableEventOutput{
Data: v,
}, nil
},
hatchet,
)
return durableEventTask
}

View File

@@ -0,0 +1,43 @@
package v1_workflows
import (
"strings"
"time"
"github.com/hatchet-dev/hatchet/pkg/client/create"
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
"github.com/hatchet-dev/hatchet/pkg/v1/factory"
"github.com/hatchet-dev/hatchet/pkg/v1/workflow"
"github.com/hatchet-dev/hatchet/pkg/worker"
)
type DurableSleepInput struct {
Message string
}
type DurableSleepOutput struct {
TransformedMessage string
}
func DurableSleep(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[DurableSleepInput, DurableSleepOutput] {
// > Durable Sleep
simple := factory.NewDurableTask(
create.StandaloneTask{
Name: "durable-sleep",
},
func(ctx worker.DurableHatchetContext, input DurableSleepInput) (*DurableSleepOutput, error) {
_, err := ctx.SleepFor(10 * time.Second)
if err != nil {
return nil, err
}
return &DurableSleepOutput{
TransformedMessage: strings.ToLower(input.Message),
}, nil
},
hatchet,
)
return simple
}

View File

@@ -15,7 +15,7 @@ type NonRetryableResult struct{}
// NonRetryableError returns a workflow which throws a non-retryable error
func NonRetryableError(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[NonRetryableInput, NonRetryableResult] {
// Non Retryable Error
// > Non Retryable Error
retries := factory.NewTask(
create.StandaloneTask{
Name: "non-retryable-task",
@@ -25,7 +25,6 @@ func NonRetryableError(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[No
},
hatchet,
)
// ‼️
return retries
}

View File

@@ -0,0 +1,45 @@
package v1_workflows
import (
"strings"
"github.com/hatchet-dev/hatchet/pkg/client/create"
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
"github.com/hatchet-dev/hatchet/pkg/v1/factory"
"github.com/hatchet-dev/hatchet/pkg/v1/workflow"
"github.com/hatchet-dev/hatchet/pkg/worker"
)
type OnCronInput struct {
Message string `json:"Message"`
}
type JobResult struct {
TransformedMessage string `json:"TransformedMessage"`
}
type OnCronOutput struct {
Job JobResult `json:"job"`
}
// > Workflow Definition Cron Trigger
func OnCron(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[OnCronInput, OnCronOutput] {
// Create a standalone task that transforms a message
cronTask := factory.NewTask(
create.StandaloneTask{
Name: "on-cron-task",
// 👀 add a cron expression
OnCron: []string{"0 0 * * *"}, // Run every day at midnight
},
func(ctx worker.HatchetContext, input OnCronInput) (*OnCronOutput, error) {
return &OnCronOutput{
Job: JobResult{
TransformedMessage: strings.ToLower(input.Message),
},
}, nil
},
hatchet,
)
return cronTask
}

View File

@@ -0,0 +1,58 @@
package v1_workflows
import (
"strings"
"github.com/hatchet-dev/hatchet/pkg/client/create"
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
"github.com/hatchet-dev/hatchet/pkg/v1/factory"
"github.com/hatchet-dev/hatchet/pkg/v1/workflow"
"github.com/hatchet-dev/hatchet/pkg/worker"
)
type EventInput struct {
Message string
}
type LowerTaskOutput struct {
TransformedMessage string
}
type UpperTaskOutput struct {
TransformedMessage string
}
// > Run workflow on event
const SimpleEvent = "simple-event:create"
func Lower(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[EventInput, LowerTaskOutput] {
return factory.NewTask(
create.StandaloneTask{
Name: "lower",
// 👀 Declare the event that will trigger the workflow
OnEvents: []string{SimpleEvent},
}, func(ctx worker.HatchetContext, input EventInput) (*LowerTaskOutput, error) {
// Transform the input message to lowercase
return &LowerTaskOutput{
TransformedMessage: strings.ToLower(input.Message),
}, nil
},
hatchet,
)
}
func Upper(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[EventInput, UpperTaskOutput] {
return factory.NewTask(
create.StandaloneTask{
Name: "upper",
OnEvents: []string{SimpleEvent},
},
func(ctx worker.HatchetContext, input EventInput) (*UpperTaskOutput, error) {
return &UpperTaskOutput{
TransformedMessage: strings.ToUpper(input.Message),
}, nil
},
hatchet,
)
}

View File

@@ -0,0 +1,51 @@
package v1_workflows
import (
"time"
"github.com/hatchet-dev/hatchet/pkg/client/create"
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
"github.com/hatchet-dev/hatchet/pkg/v1/factory"
"github.com/hatchet-dev/hatchet/pkg/v1/workflow"
"github.com/hatchet-dev/hatchet/pkg/worker"
)
type PriorityInput struct {
UserId string `json:"userId"`
}
type PriorityOutput struct {
TransformedMessage string `json:"TransformedMessage"`
}
type Result struct {
Step PriorityOutput
}
func Priority(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[PriorityInput, Result] {
// Create a standalone task that transforms a message
// > Default priority
defaultPriority := int32(1)
workflow := factory.NewWorkflow[PriorityInput, Result](
create.WorkflowCreateOpts[PriorityInput]{
Name: "priority",
DefaultPriority: &defaultPriority,
},
hatchet,
)
// > Defining a Task
workflow.Task(
create.WorkflowTask[PriorityInput, Result]{
Name: "step",
}, func(ctx worker.HatchetContext, input PriorityInput) (interface{}, error) {
time.Sleep(time.Second * 5)
return &PriorityOutput{
TransformedMessage: input.UserId,
}, nil
},
)
return workflow
}

View File

@@ -0,0 +1,95 @@
package v1_workflows
import (
"strings"
"github.com/hatchet-dev/hatchet/pkg/client/create"
"github.com/hatchet-dev/hatchet/pkg/client/types"
v1 "github.com/hatchet-dev/hatchet/pkg/v1"
"github.com/hatchet-dev/hatchet/pkg/v1/factory"
"github.com/hatchet-dev/hatchet/pkg/v1/features"
"github.com/hatchet-dev/hatchet/pkg/v1/workflow"
"github.com/hatchet-dev/hatchet/pkg/worker"
)
type RateLimitInput struct {
UserId string `json:"userId"`
}
type RateLimitOutput struct {
TransformedMessage string `json:"TransformedMessage"`
}
func upsertRateLimit(hatchet v1.HatchetClient) {
// > Upsert Rate Limit
hatchet.RateLimits().Upsert(
features.CreateRatelimitOpts{
Key: "api-service-rate-limit",
Limit: 10,
Duration: types.Second,
},
)
}
// > Static Rate Limit
func StaticRateLimit(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[RateLimitInput, RateLimitOutput] {
// Create a standalone task that transforms a message
// define the parameters for the rate limit
rateLimitKey := "api-service-rate-limit"
units := 1
rateLimitTask := factory.NewTask(
create.StandaloneTask{
Name: "rate-limit-task",
// 👀 add a static rate limit
RateLimits: []*types.RateLimit{
{
Key: rateLimitKey,
Units: &units,
},
},
},
func(ctx worker.HatchetContext, input RateLimitInput) (*RateLimitOutput, error) {
return &RateLimitOutput{
TransformedMessage: strings.ToLower(input.UserId),
}, nil
},
hatchet,
)
return rateLimitTask
}
// > Dynamic Rate Limit
func RateLimit(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[RateLimitInput, RateLimitOutput] {
// Create a standalone task that transforms a message
// define the parameters for the rate limit
expression := "input.userId"
units := 1
duration := types.Second
rateLimitTask := factory.NewTask(
create.StandaloneTask{
Name: "rate-limit-task",
// 👀 add a dynamic rate limit
RateLimits: []*types.RateLimit{
{
KeyExpr: &expression,
Units: &units,
Duration: &duration,
},
},
},
func(ctx worker.HatchetContext, input RateLimitInput) (*RateLimitOutput, error) {
return &RateLimitOutput{
TransformedMessage: strings.ToLower(input.UserId),
}, nil
},
hatchet,
)
return rateLimitTask
}

View File

@@ -16,7 +16,7 @@ type RetriesResult struct{}
// Simple retries example that always fails
func Retries(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[RetriesInput, RetriesResult] {
// Simple Step Retries
// > Simple Step Retries
retries := factory.NewTask(
create.StandaloneTask{
Name: "retries-task",
@@ -26,7 +26,6 @@ func Retries(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[RetriesInput
},
hatchet,
)
// ‼️
return retries
}
@@ -38,7 +37,7 @@ type RetriesWithCountResult struct {
// Retries example that succeeds after a certain number of retries
func RetriesWithCount(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[RetriesWithCountInput, RetriesWithCountResult] {
// Retries with Count
// > Retries with Count
retriesWithCount := factory.NewTask(
create.StandaloneTask{
Name: "fail-twice-task",
@@ -59,7 +58,6 @@ func RetriesWithCount(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[Ret
},
hatchet,
)
// ‼️
return retriesWithCount
}
@@ -69,7 +67,7 @@ type BackoffResult struct{}
// Retries example with simple backoff (no configuration in this API version)
func WithBackoff(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[BackoffInput, BackoffResult] {
// Retries with Backoff
// > Retries with Backoff
withBackoff := factory.NewTask(
create.StandaloneTask{
Name: "with-backoff-task",
@@ -85,7 +83,6 @@ func WithBackoff(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[BackoffI
},
hatchet,
)
// ‼️
return withBackoff
}

View File

@@ -25,7 +25,7 @@ func Simple(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[SimpleInput,
// Create a simple standalone task using the task factory
// Note the use of typed generics for both input and output
// Declaring a Task
// > Declaring a Task
simple := factory.NewTask(
create.StandaloneTask{
Name: "simple-task",
@@ -37,23 +37,21 @@ func Simple(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[SimpleInput,
},
hatchet,
)
// ‼️
// Example of running a task
_ = func() error {
// Running a Task
// > Running a Task
result, err := simple.Run(context.Background(), SimpleInput{Message: "Hello, World!"})
if err != nil {
return err
}
fmt.Println(result.TransformedMessage)
// ‼️
return nil
}
// Example of registering a task on a worker
_ = func() error {
// Declaring a Worker
// > Declaring a Worker
w, err := hatchet.Worker(v1worker.WorkerOpts{
Name: "simple-worker",
Workflows: []workflow.WorkflowBase{
@@ -67,7 +65,6 @@ func Simple(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[SimpleInput,
if err != nil {
return err
}
// ‼️
return nil
}
@@ -76,7 +73,7 @@ func Simple(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[SimpleInput,
func ParentTask(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[SimpleInput, SimpleResult] {
// Spawning Tasks from within a Task
// > Spawning Tasks from within a Task
simple := Simple(hatchet)
parent := factory.NewTask(
@@ -97,7 +94,6 @@ func ParentTask(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[SimpleInp
},
hatchet,
)
// ‼️
return parent
}

View File

@@ -18,6 +18,7 @@ type TimeoutResult struct {
func Timeout(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[TimeoutInput, TimeoutResult] {
// > Execution Timeout
// Create a task with a timeout of 3 seconds that tries to sleep for 10 seconds
timeout := factory.NewTask(
create.StandaloneTask{
@@ -30,7 +31,40 @@ func Timeout(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[TimeoutInput
// Check if the context was cancelled due to timeout
select {
case <-ctx.Done():
return nil, errors.New("Task timed out")
return nil, errors.New("TASK TIMED OUT")
default:
// Continue execution
}
return &TimeoutResult{
Completed: true,
}, nil
},
hatchet,
)
return timeout
}
func RefreshTimeout(hatchet v1.HatchetClient) workflow.WorkflowDeclaration[TimeoutInput, TimeoutResult] {
// > Refresh Timeout
timeout := factory.NewTask(
create.StandaloneTask{
Name: "timeout-task",
ExecutionTimeout: 3 * time.Second, // Task will timeout after 3 seconds
}, func(ctx worker.HatchetContext, input TimeoutInput) (*TimeoutResult, error) {
// Refresh the timeout by 10 seconds (new timeout will be 13 seconds)
ctx.RefreshTimeout("10s")
// Sleep for 10 seconds
time.Sleep(10 * time.Second)
// Check if the context was cancelled due to timeout
select {
case <-ctx.Done():
return nil, errors.New("TASK TIMED OUT")
default:
// Continue execution
}

View File

@@ -26,7 +26,7 @@ func run() (func() error, error) {
return nil, fmt.Errorf("error creating worker: %w", err)
}
// StickyWorker
// > StickyWorker
err = w.RegisterWorkflow(
&worker.WorkflowJob{
@@ -66,13 +66,12 @@ func run() (func() error, error) {
},
)
// ‼️
if err != nil {
return nil, fmt.Errorf("error registering workflow: %w", err)
}
// StickyChild
// > StickyChild
err = w.RegisterWorkflow(
&worker.WorkflowJob{
@@ -89,7 +88,6 @@ func run() (func() error, error) {
},
)
// ‼️
if err != nil {
return nil, fmt.Errorf("error registering workflow: %w", err)

View File

@@ -0,0 +1,143 @@
package main
import (
"context"
"fmt"
"github.com/joho/godotenv"
"github.com/hatchet-dev/hatchet/pkg/client"
"github.com/hatchet-dev/hatchet/pkg/cmdutils"
"github.com/hatchet-dev/hatchet/pkg/worker"
)
// > Create
// ... normal workflow definition
type printOutput struct{}
func print(ctx context.Context) (result *printOutput, err error) {
fmt.Println("called print:print")
return &printOutput{}, nil
}
// ,
func main() {
// ... initialize client, worker and workflow
err := godotenv.Load()
if err != nil {
panic(err)
}
c, err := client.New()
if err != nil {
panic(err)
}
w, err := worker.NewWorker(
worker.WithClient(
c,
),
)
if err != nil {
panic(err)
}
err = w.RegisterWorkflow(
&worker.WorkflowJob{
On: worker.NoTrigger(),
Name: "cron-workflow",
Description: "Demonstrates a simple cron workflow",
Steps: []*worker.WorkflowStep{
worker.Fn(print),
},
},
)
if err != nil {
panic(err)
}
interrupt := cmdutils.InterruptChan()
cleanup, err := w.Start()
if err != nil {
panic(err)
}
// ,
go func() {
// 👀 define the cron expression to run every minute
cron, err := c.Cron().Create(
context.Background(),
"cron-workflow",
&client.CronOpts{
Name: "every-minute",
Expression: "* * * * *",
Input: map[string]interface{}{
"message": "Hello, world!",
},
AdditionalMetadata: map[string]string{},
},
)
if err != nil {
panic(err)
}
fmt.Println(*cron.Name, cron.Cron)
}()
// ... wait for interrupt signal
<-interrupt
if err := cleanup(); err != nil {
panic(fmt.Errorf("error cleaning up: %w", err))
}
// ,
}
func ListCrons() {
c, err := client.New()
if err != nil {
panic(err)
}
// > List
crons, err := c.Cron().List(context.Background())
if err != nil {
panic(err)
}
for _, cron := range *crons.Rows {
fmt.Println(cron.Cron, *cron.Name)
}
}
func DeleteCron(id string) {
c, err := client.New()
if err != nil {
panic(err)
}
// > Delete
// 👀 id is the cron's metadata id, can get it via cron.Metadata.Id
err = c.Cron().Delete(context.Background(), id)
if err != nil {
panic(err)
}
}

View File

@@ -0,0 +1,83 @@
package main
import (
"context"
"fmt"
"github.com/joho/godotenv"
"github.com/hatchet-dev/hatchet/pkg/client"
"github.com/hatchet-dev/hatchet/pkg/cmdutils"
"github.com/hatchet-dev/hatchet/pkg/worker"
)
// > Workflow Definition Cron Trigger
// ... normal workflow definition
type printOutput struct{}
func print(ctx context.Context) (result *printOutput, err error) {
fmt.Println("called print:print")
return &printOutput{}, nil
}
// ,
func main() {
// ... initialize client and worker
err := godotenv.Load()
if err != nil {
panic(err)
}
client, err := client.New()
if err != nil {
panic(err)
}
w, err := worker.NewWorker(
worker.WithClient(
client,
),
)
if err != nil {
panic(err)
}
// ,
err = w.RegisterWorkflow(
&worker.WorkflowJob{
// 👀 define the cron expression to run every minute
On: worker.Cron("* * * * *"),
Name: "cron-workflow",
Description: "Demonstrates a simple cron workflow",
Steps: []*worker.WorkflowStep{
worker.Fn(print),
},
},
)
if err != nil {
panic(err)
}
// ... start worker
interrupt := cmdutils.InterruptChan()
cleanup, err := w.Start()
if err != nil {
panic(err)
}
<-interrupt
if err := cleanup(); err != nil {
panic(fmt.Errorf("error cleaning up: %w", err))
}
// ,
}

View File

@@ -77,10 +77,6 @@ func do(duration time.Duration, startEventsPerSecond, amount int, increase, dela
time.Sleep(after)
close(scheduled)
close(executed)
close(hook)
log.Printf("✅ success")
return nil

View File

@@ -30,11 +30,6 @@ func emit(ctx context.Context, startEventsPerSecond, amount int, increase, durat
var eventsPerSecond int
go func() {
took := <-hook
if took == 0 {
return
}
panic(fmt.Errorf("event took too long to schedule: %s at %d events/s", took, eventsPerSecond))
}()
for {

View File

@@ -0,0 +1,107 @@
package main
import (
"fmt"
"time"
"github.com/joho/godotenv"
"github.com/hatchet-dev/hatchet/pkg/client"
"github.com/hatchet-dev/hatchet/pkg/cmdutils"
"github.com/hatchet-dev/hatchet/pkg/worker"
)
type stepOneOutput struct {
Message string `json:"message"`
}
// > OnFailure Step
// This workflow will fail because the step will throw an error
// we define an onFailure step to handle this case
func StepOne(ctx worker.HatchetContext) (result *stepOneOutput, err error) {
// 👀 this step will always raise an exception
return nil, fmt.Errorf("test on failure")
}
func OnFailure(ctx worker.HatchetContext) (result *stepOneOutput, err error) {
// run cleanup code or notifications here
// 👀 you can access the error from the failed step(s) like this
fmt.Println(ctx.StepRunErrors())
return &stepOneOutput{
Message: "Failure!",
}, nil
}
func main() {
// ...
err := godotenv.Load()
if err != nil {
panic(err)
}
c, err := client.New()
if err != nil {
panic(err)
}
w, err := worker.NewWorker(
worker.WithClient(
c,
),
)
if err != nil {
panic(err)
}
// 👀 we define an onFailure step to handle this case
err = w.On(
worker.NoTrigger(),
&worker.WorkflowJob{
Name: "on-failure-workflow",
Description: "This runs at a scheduled time.",
Steps: []*worker.WorkflowStep{
worker.Fn(StepOne).SetName("step-one"),
},
OnFailure: &worker.WorkflowJob{
Name: "scheduled-workflow-failure",
Description: "This runs when the scheduled workflow fails.",
Steps: []*worker.WorkflowStep{
worker.Fn(OnFailure).SetName("on-failure"),
},
},
},
)
// ...
if err != nil {
panic(err)
}
interruptCtx, cancel := cmdutils.InterruptContextFromChan(cmdutils.InterruptChan())
defer cancel()
cleanup, err := w.Start()
if err != nil {
panic(fmt.Errorf("error cleaning up: %w", err))
}
for {
select {
case <-interruptCtx.Done():
if err := cleanup(); err != nil {
panic(fmt.Errorf("error cleaning up: %w", err))
}
return
default:
time.Sleep(time.Second)
}
}
// ,
}

View File

@@ -0,0 +1,97 @@
package main
import (
"fmt"
"github.com/joho/godotenv"
"github.com/hatchet-dev/hatchet/pkg/client"
"github.com/hatchet-dev/hatchet/pkg/cmdutils"
"github.com/hatchet-dev/hatchet/pkg/worker"
)
type stepOneOutput struct {
Message string `json:"message"`
}
// > Backoff
// ... normal function definition
func StepOne(ctx worker.HatchetContext) (result *stepOneOutput, err error) {
if ctx.RetryCount() < 3 {
return nil, fmt.Errorf("failure")
}
return &stepOneOutput{
Message: "Success!",
}, nil
}
// ,
func main() {
// ...
err := godotenv.Load()
if err != nil {
panic(err)
}
c, err := client.New()
if err != nil {
panic(err)
}
w, err := worker.NewWorker(
worker.WithClient(
c,
),
)
if err != nil {
panic(err)
}
// ,
err = w.RegisterWorkflow(
&worker.WorkflowJob{
Name: "retry-with-backoff-workflow",
On: worker.NoTrigger(),
Description: "Demonstrates retry with exponential backoff.",
Steps: []*worker.WorkflowStep{
worker.Fn(StepOne).SetName("with-backoff").
SetRetries(10).
// 👀 Backoff configuration
// 👀 Maximum number of seconds to wait between retries
SetRetryBackoffFactor(2.0).
// 👀 Factor to increase the wait time between retries.
// This sequence will be 2s, 4s, 8s, 16s, 32s, 60s... due to the maxSeconds limit
SetRetryMaxBackoffSeconds(60),
},
},
)
// ...
if err != nil {
panic(err)
}
interruptCtx, cancel := cmdutils.InterruptContextFromChan(cmdutils.InterruptChan())
defer cancel()
cleanup, err := w.Start()
if err != nil {
panic(fmt.Errorf("error cleaning up: %w", err))
}
<-interruptCtx.Done()
if err := cleanup(); err != nil {
panic(fmt.Errorf("error cleaning up: %w", err))
}
// ,
}

View File

@@ -0,0 +1,142 @@
package main
import (
"context"
"fmt"
"time"
"github.com/joho/godotenv"
"github.com/hatchet-dev/hatchet/pkg/client"
"github.com/hatchet-dev/hatchet/pkg/cmdutils"
"github.com/hatchet-dev/hatchet/pkg/worker"
)
// > Create
// ... normal workflow definition
type printOutput struct{}
func print(ctx context.Context) (result *printOutput, err error) {
fmt.Println("called print:print")
return &printOutput{}, nil
}
// ,
func main() {
// ... initialize client, worker and workflow
err := godotenv.Load()
if err != nil {
panic(err)
}
c, err := client.New()
if err != nil {
panic(err)
}
w, err := worker.NewWorker(
worker.WithClient(
c,
),
)
if err != nil {
panic(err)
}
err = w.RegisterWorkflow(
&worker.WorkflowJob{
On: worker.NoTrigger(),
Name: "schedule-workflow",
Description: "Demonstrates a simple scheduled workflow",
Steps: []*worker.WorkflowStep{
worker.Fn(print),
},
},
)
if err != nil {
panic(err)
}
interrupt := cmdutils.InterruptChan()
cleanup, err := w.Start()
if err != nil {
panic(err)
}
// ,
go func() {
// 👀 define the scheduled workflow to run in a minute
schedule, err := c.Schedule().Create(
context.Background(),
"schedule-workflow",
&client.ScheduleOpts{
// 👀 define the time to run the scheduled workflow, in UTC
TriggerAt: time.Now().UTC().Add(time.Minute),
Input: map[string]interface{}{
"message": "Hello, world!",
},
AdditionalMetadata: map[string]string{},
},
)
if err != nil {
panic(err)
}
fmt.Println(schedule.TriggerAt, schedule.WorkflowName)
}()
// ... wait for interrupt signal
<-interrupt
if err := cleanup(); err != nil {
panic(fmt.Errorf("error cleaning up: %w", err))
}
// ,
}
func ListScheduledWorkflows() {
c, err := client.New()
if err != nil {
panic(err)
}
// > List
schedules, err := c.Schedule().List(context.Background())
if err != nil {
panic(err)
}
for _, schedule := range *schedules.Rows {
fmt.Println(schedule.TriggerAt, schedule.WorkflowName)
}
}
func DeleteScheduledWorkflow(id string) {
c, err := client.New()
if err != nil {
panic(err)
}
// > Delete
// 👀 id is the schedule's metadata id, can get it via schedule.Metadata.Id
err = c.Schedule().Delete(context.Background(), id)
if err != nil {
panic(err)
}
}

View File

@@ -27,7 +27,7 @@ func main() {
events := make(chan string, 50)
// TimeoutStep
// > TimeoutStep
cleanup, err := run(events, worker.WorkflowJob{
Name: "timeout",
Description: "timeout",
@@ -38,7 +38,6 @@ func main() {
}).SetName("step-one").SetTimeout("10s"),
},
})
// ‼️
if err != nil {
panic(err)
}

View File

View File

@@ -0,0 +1,6 @@
from examples.affinity_workers.worker import affinity_worker_workflow
from hatchet_sdk import TriggerWorkflowOptions
affinity_worker_workflow.run(
options=TriggerWorkflowOptions(additional_metadata={"hello": "moon"}),
)

Some files were not shown because too many files have changed in this diff Show More