create-svc 0.1.62 → 0.1.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -6
- package/package.json +2 -1
- package/src/cli.test.ts +1 -0
- package/src/cli.ts +17 -6
- package/src/naming.test.ts +6 -0
- package/src/naming.ts +1 -1
- package/src/scaffold.test.ts +19 -3
- package/src/scaffold.ts +10 -0
- package/src/service-runtime/authctl.ts +32 -0
- package/src/service-runtime/cloudrun/bootstrap.ts +3 -4
- package/src/service-runtime/cloudrun/cleanup.ts +6 -1
- package/src/service-runtime/cloudrun/cli.ts +52 -12
- package/src/service-runtime/cloudrun/config.ts +3 -0
- package/src/service-runtime/cloudrun/deploy-args.ts +17 -0
- package/src/service-runtime/cloudrun/deploy.ts +25 -0
- package/src/service-runtime/cloudrun/lib.test.ts +12 -1
- package/src/service-runtime/cloudrun/lib.ts +55 -15
- package/src/service-runtime/cloudrun/temporal-config.test.ts +66 -0
- package/src/service-runtime/cloudrun/temporal-config.ts +84 -0
- package/src/service-runtime/workers/cli.ts +88 -0
- package/src/service.test.ts +15 -2
- package/src/service.ts +31 -1
- package/templates/shared/.env.example +1 -1
- package/templates/shared/README.md +41 -9
- package/templates/shared/scripts/dev.ts +37 -5
- package/templates/shared/service.jsonc +8 -2
- package/templates/shared/service.yaml +4 -1
- package/templates/targets/workers/.github/workflows/deploy.yml +3 -0
- package/templates/targets/workers/.github/workflows/preview.yml +3 -0
- package/templates/targets/workers/README.md +28 -0
- package/templates/targets/workers/package.json +6 -1
- package/templates/targets/workers/src/index.ts +36 -25
- package/templates/targets/workers/src/trigger.ts +81 -0
- package/templates/targets/workers/test/app.test.ts +46 -1
- package/templates/targets/workers/trigger/waitlist-follow-up.ts +24 -0
- package/templates/targets/workers/trigger.config.ts +24 -0
- package/templates/targets/workers/tsconfig.json +1 -1
- package/templates/targets/workers/wrangler.toml +2 -0
- package/templates/variants/bun-connectrpc/package.json +1 -1
- package/templates/variants/bun-connectrpc/src/index.ts +2 -6
- package/templates/variants/bun-connectrpc/src/temporal/client.ts +28 -0
- package/templates/variants/bun-connectrpc/src/temporal.ts +56 -0
- package/templates/variants/bun-connectrpc/src/waitlist/service.ts +16 -1
- package/templates/variants/bun-connectrpc/src/worker.ts +21 -0
- package/templates/variants/bun-connectrpc/test/app.test.ts +22 -0
- package/templates/variants/bun-hono/package.json +1 -1
- package/templates/variants/bun-hono/src/index.ts +2 -6
- package/templates/variants/bun-hono/src/temporal/client.ts +28 -0
- package/templates/variants/bun-hono/src/temporal.ts +56 -0
- package/templates/variants/bun-hono/src/waitlist/service.ts +10 -1
- package/templates/variants/bun-hono/src/worker.ts +21 -0
- package/templates/variants/go-chi/Dockerfile +2 -0
- package/templates/variants/go-chi/Makefile +1 -1
- package/templates/variants/go-chi/cmd/server/main.go +5 -4
- package/templates/variants/go-chi/cmd/worker/main.go +56 -0
- package/templates/variants/go-chi/internal/app/service.go +35 -3
- package/templates/variants/go-chi/internal/config/config.go +34 -3
- package/templates/variants/go-chi/internal/config/config_test.go +63 -0
- package/templates/variants/go-chi/internal/temporal/client.go +55 -0
- package/templates/variants/go-connectrpc/Dockerfile +2 -0
- package/templates/variants/go-connectrpc/Makefile +1 -1
- package/templates/variants/go-connectrpc/cmd/server/main.go +5 -4
- package/templates/variants/go-connectrpc/cmd/worker/main.go +56 -0
- package/templates/variants/go-connectrpc/internal/app/service.go +35 -3
- package/templates/variants/go-connectrpc/internal/config/config.go +34 -3
- package/templates/variants/go-connectrpc/internal/config/config_test.go +63 -0
- package/templates/variants/go-connectrpc/internal/temporal/client.go +55 -0
|
@@ -24,9 +24,9 @@ func Load() (Config, error) {
|
|
|
24
24
|
cfg := Config{
|
|
25
25
|
Port: envOr("PORT", "8080"),
|
|
26
26
|
DatabaseURL: strings.TrimSpace(os.Getenv("DATABASE_URL")),
|
|
27
|
-
TemporalEnabled:
|
|
28
|
-
TemporalAddress:
|
|
29
|
-
TemporalNamespace:
|
|
27
|
+
TemporalEnabled: envBoolDefault("TEMPORAL_ENABLED", true),
|
|
28
|
+
TemporalAddress: envOrRuntime("TEMPORAL_ADDRESS", "localhost:7233"),
|
|
29
|
+
TemporalNamespace: envOrRuntime("TEMPORAL_NAMESPACE", "default"),
|
|
30
30
|
TemporalTaskQueue: envOr("TEMPORAL_TASK_QUEUE", "{{SERVICE_NAME}}"),
|
|
31
31
|
TemporalAPIKey: strings.TrimSpace(os.Getenv("TEMPORAL_API_KEY")),
|
|
32
32
|
AuthEnabled: envBool("AUTH_ENABLED"),
|
|
@@ -37,6 +37,18 @@ func Load() (Config, error) {
|
|
|
37
37
|
if cfg.DatabaseURL == "" {
|
|
38
38
|
return Config{}, errors.New("DATABASE_URL is required")
|
|
39
39
|
}
|
|
40
|
+
if cfg.TemporalEnabled {
|
|
41
|
+
missing := make([]string, 0, 2)
|
|
42
|
+
if cfg.TemporalAddress == "" {
|
|
43
|
+
missing = append(missing, "TEMPORAL_ADDRESS")
|
|
44
|
+
}
|
|
45
|
+
if cfg.TemporalNamespace == "" {
|
|
46
|
+
missing = append(missing, "TEMPORAL_NAMESPACE")
|
|
47
|
+
}
|
|
48
|
+
if len(missing) > 0 {
|
|
49
|
+
return Config{}, errors.New(strings.Join(missing, " and ") + " required when Temporal is enabled")
|
|
50
|
+
}
|
|
51
|
+
}
|
|
40
52
|
return cfg, nil
|
|
41
53
|
}
|
|
42
54
|
|
|
@@ -45,6 +57,14 @@ func envBool(key string) bool {
|
|
|
45
57
|
return value == "1" || value == "true" || value == "yes" || value == "on"
|
|
46
58
|
}
|
|
47
59
|
|
|
60
|
+
func envBoolDefault(key string, fallback bool) bool {
|
|
61
|
+
value := strings.TrimSpace(strings.ToLower(os.Getenv(key)))
|
|
62
|
+
if value == "" {
|
|
63
|
+
return fallback
|
|
64
|
+
}
|
|
65
|
+
return value == "1" || value == "true" || value == "yes" || value == "on"
|
|
66
|
+
}
|
|
67
|
+
|
|
48
68
|
func envOr(key string, fallback string) string {
|
|
49
69
|
value := os.Getenv(key)
|
|
50
70
|
if value != "" {
|
|
@@ -52,3 +72,14 @@ func envOr(key string, fallback string) string {
|
|
|
52
72
|
}
|
|
53
73
|
return fallback
|
|
54
74
|
}
|
|
75
|
+
|
|
76
|
+
func envOrRuntime(key string, localFallback string) string {
|
|
77
|
+
value := os.Getenv(key)
|
|
78
|
+
if value != "" {
|
|
79
|
+
return value
|
|
80
|
+
}
|
|
81
|
+
if strings.TrimSpace(os.Getenv("K_SERVICE")) != "" {
|
|
82
|
+
return ""
|
|
83
|
+
}
|
|
84
|
+
return localFallback
|
|
85
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
package config
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"strings"
|
|
5
|
+
"testing"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
func TestLoadTemporalDefaultsToLocalDevelopment(t *testing.T) {
|
|
9
|
+
setRequiredEnv(t)
|
|
10
|
+
|
|
11
|
+
cfg, err := Load()
|
|
12
|
+
if err != nil {
|
|
13
|
+
t.Fatal(err)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if !cfg.TemporalEnabled {
|
|
17
|
+
t.Fatal("Temporal should default to enabled")
|
|
18
|
+
}
|
|
19
|
+
if cfg.TemporalAddress != "localhost:7233" {
|
|
20
|
+
t.Fatalf("unexpected Temporal address: %s", cfg.TemporalAddress)
|
|
21
|
+
}
|
|
22
|
+
if cfg.TemporalNamespace != "default" {
|
|
23
|
+
t.Fatalf("unexpected Temporal namespace: %s", cfg.TemporalNamespace)
|
|
24
|
+
}
|
|
25
|
+
if cfg.TemporalTaskQueue != "{{SERVICE_NAME}}" {
|
|
26
|
+
t.Fatalf("unexpected Temporal task queue: %s", cfg.TemporalTaskQueue)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
func TestLoadTemporalOptOut(t *testing.T) {
|
|
31
|
+
setRequiredEnv(t)
|
|
32
|
+
t.Setenv("K_SERVICE", "svc")
|
|
33
|
+
t.Setenv("TEMPORAL_ENABLED", "false")
|
|
34
|
+
|
|
35
|
+
cfg, err := Load()
|
|
36
|
+
if err != nil {
|
|
37
|
+
t.Fatal(err)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if cfg.TemporalEnabled {
|
|
41
|
+
t.Fatal("Temporal should be disabled")
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
func TestLoadTemporalCloudRunFailsWithoutConnectionSettings(t *testing.T) {
|
|
46
|
+
setRequiredEnv(t)
|
|
47
|
+
t.Setenv("K_SERVICE", "svc")
|
|
48
|
+
|
|
49
|
+
_, err := Load()
|
|
50
|
+
if err == nil {
|
|
51
|
+
t.Fatal("expected Temporal configuration error")
|
|
52
|
+
}
|
|
53
|
+
if !strings.Contains(err.Error(), "TEMPORAL_ADDRESS and TEMPORAL_NAMESPACE") {
|
|
54
|
+
t.Fatalf("unexpected error: %v", err)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
func setRequiredEnv(t *testing.T) {
|
|
59
|
+
t.Helper()
|
|
60
|
+
t.Setenv("DATABASE_URL", "postgres://postgres:postgres@127.0.0.1:5432/app")
|
|
61
|
+
t.Setenv("ATTACHMENT_BUCKET", "bucket")
|
|
62
|
+
t.Setenv("ATTACHMENT_PUBLIC_BASE_URL", "https://storage.test")
|
|
63
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
package temporalapp
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
|
|
6
|
+
"{{MODULE_PATH}}/internal/app"
|
|
7
|
+
|
|
8
|
+
"go.temporal.io/sdk/client"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
type TriggerDispatcher struct {
|
|
12
|
+
client client.Client
|
|
13
|
+
taskQueue string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
func NewTriggerDispatcher(cfg WorkerConfig) (*TriggerDispatcher, error) {
|
|
17
|
+
options := client.Options{
|
|
18
|
+
HostPort: cfg.Address,
|
|
19
|
+
Namespace: cfg.Namespace,
|
|
20
|
+
}
|
|
21
|
+
if cfg.APIKey != "" {
|
|
22
|
+
options.Credentials = client.NewAPIKeyStaticCredentials(cfg.APIKey)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
temporalClient, err := client.Dial(options)
|
|
26
|
+
if err != nil {
|
|
27
|
+
return nil, err
|
|
28
|
+
}
|
|
29
|
+
return &TriggerDispatcher{client: temporalClient, taskQueue: cfg.TaskQueue}, nil
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func (d *TriggerDispatcher) Close() {
|
|
33
|
+
d.client.Close()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
func (d *TriggerDispatcher) DispatchWaitlistFollowUp(ctx context.Context, trigger app.WaitlistTrigger) error {
|
|
37
|
+
_, err := d.client.ExecuteWorkflow(ctx, client.StartWorkflowOptions{
|
|
38
|
+
ID: "waitlist-follow-up-" + trigger.ID,
|
|
39
|
+
TaskQueue: d.taskQueue,
|
|
40
|
+
}, WaitlistFollowUpWorkflow, WaitlistFollowUpInput{
|
|
41
|
+
TriggerID: trigger.ID,
|
|
42
|
+
Email: triggerEmail(trigger.Payload),
|
|
43
|
+
Type: trigger.Type,
|
|
44
|
+
})
|
|
45
|
+
return err
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
func triggerEmail(payload any) string {
|
|
49
|
+
values, ok := payload.(map[string]any)
|
|
50
|
+
if !ok {
|
|
51
|
+
return ""
|
|
52
|
+
}
|
|
53
|
+
email, _ := values["email"].(string)
|
|
54
|
+
return email
|
|
55
|
+
}
|