feat(security): multiple encryption options, API tokens, easier setup (#125)

* (wip) encryption

* feat: api tokens

* chore: add api token generation command

* fix: e2e tests

* chore: set timeout for e2e job

* fix: e2e tests, remove client-side certs

* chore: address PR review comments

* fix: token tests

* chore: address review comments and fix tests
This commit is contained in:
abelanger5
2024-01-26 12:38:36 -08:00
committed by GitHub
parent 13315cdd9e
commit 78685d0098
105 changed files with 3904 additions and 1111 deletions
+8 -4
View File
@@ -102,6 +102,7 @@ jobs:
e2e:
runs-on: ubuntu-latest
timeout-minutes: 5
env:
DATABASE_URL: postgresql://hatchet:hatchet@127.0.0.1:5431/hatchet
@@ -153,14 +154,17 @@ jobs:
. .env
set +a
go run ./cmd/hatchet-admin seed
go run ./cmd/hatchet-admin quickstart
go run ./cmd/hatchet-engine &
go run ./cmd/hatchet-api &
go run ./cmd/hatchet-engine --config ./generated/ &
go run ./cmd/hatchet-api --config ./generated/ &
sleep 30
- name: Test
run: go test -tags e2e ./... -p 1 -v -failfast
run: |
export HATCHET_CLIENT_TOKEN="$(go run ./cmd/hatchet-admin token create --config ./generated/ --tenant-id 707d0855-80ab-4e1f-a156-f1c4546cbf52)"
go test -tags e2e ./... -p 1 -v -failfast
- name: Teardown
run: docker compose down
+2
View File
@@ -85,3 +85,5 @@ tmp
postgres-data
rabbitmq.conf
*encryption-keys
+7
View File
@@ -0,0 +1,7 @@
repos:
- repo: https://github.com/gitguardian/ggshield
rev: v1.23.0
hooks:
- id: ggshield
language_version: python3
stages: [commit]
+69 -7
View File
@@ -8,7 +8,7 @@
- `docker-compose`
- [`Taskfile`](https://taskfile.dev/installation/)
- The following additional devtools:
- `protoc`: `brew install protobuf@25`
- `protoc`: `brew install protobuf@25`
- `caddy` and `nss`: `brew install caddy nss`
### Setup
@@ -19,7 +19,9 @@
3. Generate certificates needed for communicating between the Hatchet client and engine: `task generate-certs`
4. Create environment variables:
4. Generate keysets for encryption: `task generate-local-encryption-keys`
5. Create environment variables:
```sh
alias randstring='f() { openssl rand -base64 69 | tr -d "\n" | tr -d "=+/" | cut -c1-$1 };f'
@@ -30,6 +32,10 @@ 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_ENCRYPTION_MASTER_KEYSET_FILE=./hack/dev/encryption-keys/master.key
SERVER_ENCRYPTION_JWT_PRIVATE_KEYSET_FILE=./hack/dev/encryption-keys/private_ec256.key
SERVER_ENCRYPTION_JWT_PUBLIC_KEYSET_FILE=./hack/dev/encryption-keys/public_ec256.key
SERVER_PORT=8080
SERVER_URL=https://app.dev.hatchet-tools.com
@@ -37,22 +43,44 @@ SERVER_AUTH_COOKIE_SECRETS="$(randstring 16) $(randstring 16)"
SERVER_AUTH_COOKIE_DOMAIN=app.dev.hatchet-tools.com
SERVER_AUTH_COOKIE_INSECURE=false
SERVER_AUTH_SET_EMAIL_VERIFIED=true
SERVER_LOGGER_LEVEL=debug
SERVER_LOGGER_FORMAT=console
DATABASE_LOGGER_LEVEL=debug
DATABASE_LOGGER_FORMAT=console
EOF
```
5. Migrate the database: `task prisma-migrate`
6. Migrate the database: `task prisma-migrate`
6. Generate all files: `task generate`
7. Generate all files: `task generate`
7. Seed the database: `task seed-dev`
8. Seed the database: `task seed-dev`
8. Start the Hatchet engine, API server, dashboard, and Prisma studio:
9. Start the Hatchet engine, API server, dashboard, and Prisma studio:
```sh
task start-dev
```
10. To create and test workflows, run the examples in the `./examples` directory. You will need to add the tenant (output from the `task seed-dev` command) to the `.env` file in each example directory.
### Creating and testing workflows
To create and test workflows, run the examples in the `./examples` directory.
You will need to add the tenant (output from the `task seed-dev` command) to the `.env` file in each example directory. An example `.env` file for the `./examples/simple` directory can be generated via:
```sh
alias get_token='go run ./cmd/hatchet-admin token create --name local --tenant-id 707d0855-80ab-4e1f-a156-f1c4546cbf52'
cat > ./examples/simple/.env <<EOF
HATCHET_CLIENT_TENANT_ID=707d0855-80ab-4e1f-a156-f1c4546cbf52
HATCHET_CLIENT_TLS_ROOT_CA_FILE=../../hack/dev/certs/ca.cert
HATCHET_CLIENT_TLS_SERVER_NAME=cluster
HATCHET_CLIENT_TOKEN="$(get_token)"
EOF
```
This example can then be run via `go run main.go` from the `./examples/simple` directory.
### Logging
@@ -83,3 +111,37 @@ OTEL_EXPORTER_OTLP_HEADERS=<optional-headers>
# optional
OTEL_EXPORTER_OTLP_ENDPOINT=<collector-url>
```
### CloudKMS
CloudKMS can be used to generate master encryption keys:
```
gcloud kms keyrings create "development" --location "global"
gcloud kms keys create "development" --location "global" --keyring "development" --purpose "encryption"
gcloud kms keys list --location "global" --keyring "development"
```
From the last step, copy the Key URI and set the following environment variable:
```
SERVER_ENCRYPTION_CLOUDKMS_KEY_URI=gcp-kms://projects/<PROJECT>/locations/global/keyRings/development/cryptoKeys/development
```
Generate a service account in GCP which can encrypt/decrypt on CloudKMS, then download a service account JSON file and set it via:
```
SERVER_ENCRYPTION_CLOUDKMS_CREDENTIALS_JSON='{...}'
```
## Issues
### Query engine leakage
Sometimes the spawned query engines from Prisma don't get killed when hot reloading. You can run `task kill-query-engines` on OSX to kill the query engines.
Make sure you call `.Disconnect` on the database config object when writing CLI commands which interact with the database. If you don't, and you try to wrap these CLI commands in a new command, it will never exit, for example:
```
export HATCHET_CLIENT_TOKEN="$(go run ./cmd/hatchet-admin token create --tenant-id <tenant>)"
```
+7 -4
View File
@@ -1,9 +1,6 @@
version: "3"
tasks:
write-default-env:
cmds:
- sh ./hack/dev/write-default-env.sh
set-etc-hosts:
cmds:
- sudo sh ./hack/dev/manage-hosts.sh add 127.0.0.1 app.dev.hatchet-tools.com
@@ -46,7 +43,13 @@ tasks:
- task: generate-api-client
generate-certs:
cmds:
- sh ./hack/dev/generate-temporal-certs.sh ./hack/dev/certs
- sh ./hack/dev/generate-x509-certs.sh ./hack/dev/certs
generate-local-encryption-keys:
cmds:
- sh ./hack/dev/generate-local-encryption-keys.sh ./hack/dev/encryption-keys
generate-dev-api-token:
cmds:
- sh ./hack/dev/generate-dev-api-token.sh
generate-api-server:
cmds:
- sh ./hack/oas/generate-server.sh
+14 -26
View File
@@ -15,17 +15,14 @@ service Dispatcher {
}
message WorkerRegisterRequest {
// the tenant id
string tenantId = 1;
// the name of the worker
string workerName = 2;
string workerName = 1;
// a list of actions that this worker can run
repeated string actions = 3;
repeated string actions = 2;
// (optional) the services for this worker
repeated string services = 4;
repeated string services = 3;
}
message WorkerRegisterResponse {
@@ -74,19 +71,13 @@ message AssignedAction {
}
message WorkerListenRequest {
// the tenant id
string tenantId = 1;
// the id of the worker
string workerId = 2;
string workerId = 1;
}
message WorkerUnsubscribeRequest {
// the tenant id to unsubscribe from
string tenantId = 1;
// the id of the worker
string workerId = 2;
string workerId = 1;
}
message WorkerUnsubscribeResponse {
@@ -105,34 +96,31 @@ enum ActionEventType {
}
message ActionEvent {
// the tenant id
string tenantId = 1;
// the id of the worker
string workerId = 2;
string workerId = 1;
// the id of the job
string jobId = 3;
string jobId = 2;
// the job run id
string jobRunId = 4;
string jobRunId = 3;
// the id of the step
string stepId = 5;
string stepId = 4;
// the step run id
string stepRunId = 6;
string stepRunId = 5;
// the action id
string actionId = 7;
string actionId = 6;
google.protobuf.Timestamp eventTimestamp = 8;
google.protobuf.Timestamp eventTimestamp = 7;
// the step event type
ActionEventType eventType = 9;
ActionEventType eventType = 8;
// the event payload
string eventPayload = 10;
string eventPayload = 9;
}
message ActionEventResponse {
+6 -15
View File
@@ -30,28 +30,22 @@ message Event {
}
message PushEventRequest {
// the tenant id
string tenantId = 1;
// the key for the event
string key = 2;
string key = 1;
// the payload for the event
string payload = 3;
string payload = 2;
// when the event was generated
google.protobuf.Timestamp eventTimestamp = 4;
google.protobuf.Timestamp eventTimestamp = 3;
}
message ListEventRequest {
// (required) the tenant id
string tenantId = 1;
// (optional) the number of events to skip
int32 offset = 2;
int32 offset = 1;
// (optional) the key for the event
string key = 3;
string key = 2;
}
message ListEventResponse {
@@ -60,9 +54,6 @@ message ListEventResponse {
}
message ReplayEventRequest {
// the tenant id
string tenantId = 1;
// the event id to replay
string eventId = 2;
string eventId = 1;
}
@@ -108,3 +108,11 @@ WorkerList:
$ref: "./worker.yaml#/WorkerList"
Worker:
$ref: "./worker.yaml#/Worker"
APIToken:
$ref: "./api_tokens.yaml#/APIToken"
CreateAPITokenRequest:
$ref: "./api_tokens.yaml#/CreateAPITokenRequest"
CreateAPITokenResponse:
$ref: "./api_tokens.yaml#/CreateAPITokenResponse"
ListAPITokensResponse:
$ref: "./api_tokens.yaml#/ListAPITokensResponse"
@@ -0,0 +1,45 @@
APIToken:
type: object
properties:
metadata:
$ref: "./metadata.yaml#/APIResourceMeta"
name:
type: string
description: The name of the API token.
maxLength: 255
expiresAt:
type: string
format: date-time
description: When the API token expires.
required:
- metadata
- name
- expiresAt
CreateAPITokenRequest:
type: object
properties:
name:
type: string
description: A name for the API token.
maxLength: 255
required:
- name
CreateAPITokenResponse:
type: object
properties:
token:
type: string
description: The API token.
required:
- token
ListAPITokensResponse:
properties:
pagination:
$ref: "./metadata.yaml#/PaginationResponse"
rows:
items:
$ref: "#/APIToken"
type: array
+4
View File
@@ -48,6 +48,10 @@ paths:
$ref: "./paths/tenant/tenant.yaml#/invites"
/api/v1/tenants/{tenant}/invites/{tenant-invite}:
$ref: "./paths/tenant/tenant.yaml#/inviteScoped"
/api/v1/tenants/{tenant}/api-tokens:
$ref: "./paths/api-tokens/api_tokens.yaml#/withTenant"
/api/v1/api-tokens/{api-token}:
$ref: "./paths/api-tokens/api_tokens.yaml#/revoke"
/api/v1/tenants/{tenant}/events:
$ref: "./paths/event/event.yaml#/withTenant"
/api/v1/tenants/{tenant}/events/replay:
@@ -0,0 +1,111 @@
withTenant:
post:
x-resources: ["tenant"]
description: Create an API token for a tenant
operationId: api-token:create
parameters:
- description: The tenant id
in: path
name: tenant
required: true
schema:
type: string
format: uuid
minLength: 36
maxLength: 36
requestBody:
content:
application/json:
schema:
$ref: "../../components/schemas/_index.yaml#/CreateAPITokenRequest"
responses:
"200":
content:
application/json:
schema:
$ref: "../../components/schemas/_index.yaml#/CreateAPITokenResponse"
description: Successfully retrieved the workflows
"400":
content:
application/json:
schema:
$ref: "../../components/schemas/_index.yaml#/APIErrors"
description: A malformed or bad request
"403":
content:
application/json:
schema:
$ref: "../../components/schemas/_index.yaml#/APIErrors"
description: Forbidden
summary: Create API Token
tags:
- API Token
get:
x-resources: ["tenant"]
description: List API tokens for a tenant
operationId: api-token:list
parameters:
- description: The tenant id
in: path
name: tenant
required: true
schema:
type: string
format: uuid
minLength: 36
maxLength: 36
responses:
"200":
content:
application/json:
schema:
$ref: "../../components/schemas/_index.yaml#/ListAPITokensResponse"
description: Successfully retrieved the workflows
"400":
content:
application/json:
schema:
$ref: "../../components/schemas/_index.yaml#/APIErrors"
description: A malformed or bad request
"403":
content:
application/json:
schema:
$ref: "../../components/schemas/_index.yaml#/APIErrors"
description: Forbidden
summary: List API Tokens
tags:
- API Token
revoke:
post:
x-resources: ["tenant", "api-token"]
description: Revoke an API token for a tenant
operationId: api-token:update:revoke
parameters:
- description: The API token
in: path
name: api-token
required: true
schema:
type: string
format: uuid
minLength: 36
maxLength: 36
responses:
"204":
description: Successfully revoked the token
"400":
content:
application/json:
schema:
$ref: "../../components/schemas/_index.yaml#/APIErrors"
description: A malformed or bad request
"403":
content:
application/json:
schema:
$ref: "../../components/schemas/_index.yaml#/APIErrors"
description: Forbidden
summary: Revoke API Token
tags:
- API Token
+8 -15
View File
@@ -16,8 +16,7 @@ service WorkflowService {
}
message PutWorkflowRequest {
string tenant_id = 1;
CreateWorkflowVersionOpts opts = 2;
CreateWorkflowVersionOpts opts = 1;
}
// CreateWorkflowVersionOpts represents options to create a workflow version.
@@ -49,17 +48,14 @@ message CreateWorkflowStepOpts {
}
// ListWorkflowsRequest is the request for ListWorkflows.
message ListWorkflowsRequest {
string tenant_id = 1;
}
message ListWorkflowsRequest {}
message ScheduleWorkflowRequest {
string tenant_id = 1;
string workflow_id = 2;
repeated google.protobuf.Timestamp schedules = 3;
string workflow_id = 1;
repeated google.protobuf.Timestamp schedules = 2;
// (optional) the input data for the workflow
string input = 4;
string input = 3;
}
// ListWorkflowsResponse is the response for ListWorkflows.
@@ -69,8 +65,7 @@ message ListWorkflowsResponse {
// ListWorkflowsForEventRequest is the request for ListWorkflowsForEvent.
message ListWorkflowsForEventRequest {
string tenant_id = 1;
string event_key = 2;
string event_key = 1;
}
// Workflow represents the Workflow model.
@@ -147,11 +142,9 @@ message Step {
}
message DeleteWorkflowRequest {
string tenant_id = 1;
string workflow_id = 2;
string workflow_id = 1;
}
message GetWorkflowByNameRequest {
string tenant_id = 1;
string name = 2;
string name = 1;
}
+63 -2
View File
@@ -1,13 +1,16 @@
package authn
import (
"fmt"
"net/http"
"strings"
"github.com/labstack/echo/v4"
"github.com/rs/zerolog"
"github.com/hatchet-dev/hatchet/api/v1/server/middleware"
"github.com/hatchet-dev/hatchet/internal/config/server"
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
)
type AuthN struct {
@@ -51,14 +54,20 @@ func (a *AuthN) authenticate(c echo.Context, r *middleware.RouteInfo) error {
if r.Security.CookieAuth() {
err = a.handleCookieAuth(c)
c.Set("auth_strategy", "cookie")
}
if err != nil {
if err != nil && !r.Security.BearerAuth() {
return err
}
if err != nil && r.Security.BearerAuth() {
err = a.handleBearerAuth(c)
c.Set("auth_strategy", "bearer")
if err == nil {
return nil
}
}
return err
@@ -136,5 +145,57 @@ func (a *AuthN) handleCookieAuth(c echo.Context) error {
}
func (a *AuthN) handleBearerAuth(c echo.Context) error {
panic("implement me")
forbidden := echo.NewHTTPError(http.StatusForbidden, "Please provide valid credentials")
// a tenant id must exist in the context in order for the bearer auth to succeed, since
// these tokens are tenant-scoped
queriedTenant, ok := c.Get("tenant").(*db.TenantModel)
if !ok {
a.l.Debug().Msgf("tenant not found in context")
return forbidden
}
token, err := getBearerTokenFromRequest(c.Request())
if err != nil {
a.l.Debug().Err(err).Msg("error getting bearer token from request")
return forbidden
}
// Validate the token.
tenantId, err := a.config.Auth.JWTManager.ValidateTenantToken(token)
if err != nil {
a.l.Debug().Err(err).Msg("error validating tenant token")
return forbidden
}
// Verify that the tenant id which exists in the context is the same as the tenant id
// in the token.
if queriedTenant.ID != tenantId {
a.l.Debug().Msgf("tenant id in token does not match tenant id in context")
return forbidden
}
return nil
}
var errInvalidAuthHeader = fmt.Errorf("invalid authorization header in request")
func getBearerTokenFromRequest(r *http.Request) (string, error) {
reqToken := r.Header.Get("Authorization")
splitToken := strings.Split(reqToken, "Bearer")
if len(splitToken) != 2 {
return "", errInvalidAuthHeader
}
reqToken = strings.TrimSpace(splitToken[1])
return reqToken, nil
}
+36
View File
@@ -41,6 +41,21 @@ func (a *AuthZ) authorize(c echo.Context, r *middleware.RouteInfo) error {
return nil
}
var err error
switch c.Get("auth_strategy").(string) {
case "cookie":
err = a.handleCookieAuth(c, r)
case "bearer":
err = a.handleBearerAuth(c, r)
default:
return echo.NewHTTPError(http.StatusInternalServerError, "No authorization strategy was checked")
}
return err
}
func (a *AuthZ) handleCookieAuth(c echo.Context, r *middleware.RouteInfo) error {
unauthorized := echo.NewHTTPError(http.StatusUnauthorized, "Not authorized to view this resource")
if err := a.ensureVerifiedEmail(c, r); err != nil {
@@ -87,6 +102,23 @@ func (a *AuthZ) authorize(c echo.Context, r *middleware.RouteInfo) error {
return nil
}
var restrictedWithBearerToken = []string{
// bearer tokens cannot read, list, or write other bearer tokens
"ApiTokenList",
"ApiTokenCreate",
"ApiTokenUpdateRevoke",
}
// At the moment, there's no further bearer auth because bearer tokens are admin-scoped
// and we check that the bearer token has access to the tenant in the authn step.
func (a *AuthZ) handleBearerAuth(c echo.Context, r *middleware.RouteInfo) error {
if operationIn(r.OperationID, restrictedWithBearerToken) {
return echo.NewHTTPError(http.StatusUnauthorized, "Not authorized to perform this operation")
}
return nil
}
var permittedWithUnverifiedEmail = []string{
"UserGetCurrent",
"UserUpdateLogout",
@@ -116,6 +148,10 @@ var adminAndOwnerOnly = []string{
"TenantInviteUpdate",
"TenantInviteDelete",
"TenantMemberList",
// members cannot create API tokens for a tenant, because they have admin permissions
"ApiTokenList",
"ApiTokenCreate",
"ApiTokenUpdateRevoke",
}
func (a *AuthZ) authorizeTenantOperations(tenant *db.TenantModel, tenantMember *db.TenantMemberModel, r *middleware.RouteInfo) error {
@@ -0,0 +1,30 @@
package apitokens
import (
"github.com/labstack/echo/v4"
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
)
func (a *APITokenService) ApiTokenCreate(ctx echo.Context, request gen.ApiTokenCreateRequestObject) (gen.ApiTokenCreateResponseObject, error) {
tenant := ctx.Get("tenant").(*db.TenantModel)
// validate the request
if apiErrors, err := a.config.Validator.ValidateAPI(request.Body); err != nil {
return nil, err
} else if apiErrors != nil {
return gen.ApiTokenCreate400JSONResponse(*apiErrors), nil
}
token, err := a.config.Auth.JWTManager.GenerateTenantToken(tenant.ID, request.Body.Name)
if err != nil {
return nil, err
}
// This is the only time the token is sent over the API
return gen.ApiTokenCreate200JSONResponse{
Token: token,
}, nil
}
+31
View File
@@ -0,0 +1,31 @@
package apitokens
import (
"github.com/labstack/echo/v4"
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
"github.com/hatchet-dev/hatchet/api/v1/server/oas/transformers"
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
)
func (a *APITokenService) ApiTokenList(ctx echo.Context, request gen.ApiTokenListRequestObject) (gen.ApiTokenListResponseObject, error) {
tenant := ctx.Get("tenant").(*db.TenantModel)
tokens, err := a.config.Repository.APIToken().ListAPITokensByTenant(tenant.ID)
if err != nil {
return nil, err
}
rows := make([]gen.APIToken, len(tokens))
for i := range tokens {
rows[i] = *transformers.ToAPIToken(&tokens[i])
}
return gen.ApiTokenList200JSONResponse(
gen.ListAPITokensResponse{
Rows: &rows,
},
), nil
}
@@ -0,0 +1,20 @@
package apitokens
import (
"github.com/labstack/echo/v4"
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
)
func (a *APITokenService) ApiTokenUpdateRevoke(ctx echo.Context, request gen.ApiTokenUpdateRevokeRequestObject) (gen.ApiTokenUpdateRevokeResponseObject, error) {
apiToken := ctx.Get("api-token").(*db.APITokenModel)
err := a.config.Repository.APIToken().RevokeAPIToken(apiToken.ID)
if err != nil {
return nil, err
}
return gen.ApiTokenUpdateRevoke204Response{}, nil
}
@@ -0,0 +1,15 @@
package apitokens
import (
"github.com/hatchet-dev/hatchet/internal/config/server"
)
type APITokenService struct {
config *server.ServerConfig
}
func NewAPITokenService(config *server.ServerConfig) *APITokenService {
return &APITokenService{
config: config,
}
}
@@ -66,11 +66,24 @@ func (u *UserService) upsertGoogleUserFromToken(config *server.ServerConfig, tok
expiresAt := tok.Expiry
// use the encryption service to encrypt the access and refresh token
accessTokenEncrypted, err := config.Encryption.Encrypt([]byte(tok.AccessToken), "google_access_token")
if err != nil {
return nil, fmt.Errorf("failed to encrypt access token: %s", err.Error())
}
refreshTokenEncrypted, err := config.Encryption.Encrypt([]byte(tok.RefreshToken), "google_refresh_token")
if err != nil {
return nil, fmt.Errorf("failed to encrypt refresh token: %s", err.Error())
}
oauthOpts := &repository.OAuthOpts{
Provider: "google",
ProviderUserId: gInfo.Sub,
AccessToken: tok.AccessToken,
RefreshToken: repository.StringPtr(tok.RefreshToken),
AccessToken: accessTokenEncrypted,
RefreshToken: &refreshTokenEncrypted,
ExpiresAt: &expiresAt,
}
+373 -74
View File
@@ -116,11 +116,33 @@ type APIResourceMeta struct {
UpdatedAt time.Time `json:"updatedAt"`
}
// APIToken defines model for APIToken.
type APIToken struct {
// ExpiresAt When the API token expires.
ExpiresAt time.Time `json:"expiresAt"`
Metadata APIResourceMeta `json:"metadata"`
// Name The name of the API token.
Name string `json:"name"`
}
// AcceptInviteRequest defines model for AcceptInviteRequest.
type AcceptInviteRequest struct {
Invite string `json:"invite" validate:"required,uuid"`
}
// CreateAPITokenRequest defines model for CreateAPITokenRequest.
type CreateAPITokenRequest struct {
// Name A name for the API token.
Name string `json:"name"`
}
// CreateAPITokenResponse defines model for CreateAPITokenResponse.
type CreateAPITokenResponse struct {
// Token The API token.
Token string `json:"token"`
}
// CreateTenantInviteRequest defines model for CreateTenantInviteRequest.
type CreateTenantInviteRequest struct {
// Email The email of the user to invite.
@@ -231,6 +253,12 @@ type JobRun struct {
// JobRunStatus defines model for JobRunStatus.
type JobRunStatus string
// ListAPITokensResponse defines model for ListAPITokensResponse.
type ListAPITokensResponse struct {
Pagination *PaginationResponse `json:"pagination,omitempty"`
Rows *[]APIToken `json:"rows,omitempty"`
}
// PaginationResponse defines model for PaginationResponse.
type PaginationResponse struct {
// CurrentPage the current page
@@ -600,6 +628,9 @@ type WorkflowVersionGetDefinitionParams struct {
// TenantCreateJSONRequestBody defines body for TenantCreate for application/json ContentType.
type TenantCreateJSONRequestBody = CreateTenantRequest
// ApiTokenCreateJSONRequestBody defines body for ApiTokenCreate for application/json ContentType.
type ApiTokenCreateJSONRequestBody = CreateAPITokenRequest
// EventUpdateReplayJSONRequestBody defines body for EventUpdateReplay for application/json ContentType.
type EventUpdateReplayJSONRequestBody = ReplayEventRequest
@@ -623,6 +654,9 @@ type UserCreateJSONRequestBody = UserRegisterRequest
// ServerInterface represents all server handlers.
type ServerInterface interface {
// Revoke API Token
// (POST /api/v1/api-tokens/{api-token})
ApiTokenUpdateRevoke(ctx echo.Context, apiToken openapi_types.UUID) error
// Get event data
// (GET /api/v1/events/{event}/data)
EventDataGet(ctx echo.Context, event openapi_types.UUID) error
@@ -632,6 +666,12 @@ type ServerInterface interface {
// Create tenant
// (POST /api/v1/tenants)
TenantCreate(ctx echo.Context) error
// List API Tokens
// (GET /api/v1/tenants/{tenant}/api-tokens)
ApiTokenList(ctx echo.Context, tenant openapi_types.UUID) error
// Create API Token
// (POST /api/v1/tenants/{tenant}/api-tokens)
ApiTokenCreate(ctx echo.Context, tenant openapi_types.UUID) error
// List events
// (GET /api/v1/tenants/{tenant}/events)
EventList(ctx echo.Context, tenant openapi_types.UUID, params EventListParams) error
@@ -717,6 +757,26 @@ type ServerInterfaceWrapper struct {
Handler ServerInterface
}
// ApiTokenUpdateRevoke converts echo context to params.
func (w *ServerInterfaceWrapper) ApiTokenUpdateRevoke(ctx echo.Context) error {
var err error
// ------------- Path parameter "api-token" -------------
var apiToken openapi_types.UUID
err = runtime.BindStyledParameterWithLocation("simple", false, "api-token", runtime.ParamLocationPath, ctx.Param("api-token"), &apiToken)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter api-token: %s", err))
}
ctx.Set(BearerAuthScopes, []string{})
ctx.Set(CookieAuthScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.ApiTokenUpdateRevoke(ctx, apiToken)
return err
}
// EventDataGet converts echo context to params.
func (w *ServerInterfaceWrapper) EventDataGet(ctx echo.Context) error {
var err error
@@ -759,6 +819,46 @@ func (w *ServerInterfaceWrapper) TenantCreate(ctx echo.Context) error {
return err
}
// ApiTokenList converts echo context to params.
func (w *ServerInterfaceWrapper) ApiTokenList(ctx echo.Context) error {
var err error
// ------------- Path parameter "tenant" -------------
var tenant openapi_types.UUID
err = runtime.BindStyledParameterWithLocation("simple", false, "tenant", runtime.ParamLocationPath, ctx.Param("tenant"), &tenant)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter tenant: %s", err))
}
ctx.Set(BearerAuthScopes, []string{})
ctx.Set(CookieAuthScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.ApiTokenList(ctx, tenant)
return err
}
// ApiTokenCreate converts echo context to params.
func (w *ServerInterfaceWrapper) ApiTokenCreate(ctx echo.Context) error {
var err error
// ------------- Path parameter "tenant" -------------
var tenant openapi_types.UUID
err = runtime.BindStyledParameterWithLocation("simple", false, "tenant", runtime.ParamLocationPath, ctx.Param("tenant"), &tenant)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter tenant: %s", err))
}
ctx.Set(BearerAuthScopes, []string{})
ctx.Set(CookieAuthScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.ApiTokenCreate(ctx, tenant)
return err
}
// EventList converts echo context to params.
func (w *ServerInterfaceWrapper) EventList(ctx echo.Context) error {
var err error
@@ -1336,9 +1436,12 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
Handler: si,
}
router.POST(baseURL+"/api/v1/api-tokens/:api-token", wrapper.ApiTokenUpdateRevoke)
router.GET(baseURL+"/api/v1/events/:event/data", wrapper.EventDataGet)
router.GET(baseURL+"/api/v1/meta", wrapper.MetadataGet)
router.POST(baseURL+"/api/v1/tenants", wrapper.TenantCreate)
router.GET(baseURL+"/api/v1/tenants/:tenant/api-tokens", wrapper.ApiTokenList)
router.POST(baseURL+"/api/v1/tenants/:tenant/api-tokens", wrapper.ApiTokenCreate)
router.GET(baseURL+"/api/v1/tenants/:tenant/events", wrapper.EventList)
router.GET(baseURL+"/api/v1/tenants/:tenant/events/keys", wrapper.EventKeyList)
router.POST(baseURL+"/api/v1/tenants/:tenant/events/replay", wrapper.EventUpdateReplay)
@@ -1368,6 +1471,40 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
}
type ApiTokenUpdateRevokeRequestObject struct {
ApiToken openapi_types.UUID `json:"api-token"`
}
type ApiTokenUpdateRevokeResponseObject interface {
VisitApiTokenUpdateRevokeResponse(w http.ResponseWriter) error
}
type ApiTokenUpdateRevoke204Response struct {
}
func (response ApiTokenUpdateRevoke204Response) VisitApiTokenUpdateRevokeResponse(w http.ResponseWriter) error {
w.WriteHeader(204)
return nil
}
type ApiTokenUpdateRevoke400JSONResponse APIErrors
func (response ApiTokenUpdateRevoke400JSONResponse) VisitApiTokenUpdateRevokeResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(400)
return json.NewEncoder(w).Encode(response)
}
type ApiTokenUpdateRevoke403JSONResponse APIErrors
func (response ApiTokenUpdateRevoke403JSONResponse) VisitApiTokenUpdateRevokeResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(403)
return json.NewEncoder(w).Encode(response)
}
type EventDataGetRequestObject struct {
Event openapi_types.UUID `json:"event"`
}
@@ -1463,6 +1600,77 @@ func (response TenantCreate403JSONResponse) VisitTenantCreateResponse(w http.Res
return json.NewEncoder(w).Encode(response)
}
type ApiTokenListRequestObject struct {
Tenant openapi_types.UUID `json:"tenant"`
}
type ApiTokenListResponseObject interface {
VisitApiTokenListResponse(w http.ResponseWriter) error
}
type ApiTokenList200JSONResponse ListAPITokensResponse
func (response ApiTokenList200JSONResponse) VisitApiTokenListResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
return json.NewEncoder(w).Encode(response)
}
type ApiTokenList400JSONResponse APIErrors
func (response ApiTokenList400JSONResponse) VisitApiTokenListResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(400)
return json.NewEncoder(w).Encode(response)
}
type ApiTokenList403JSONResponse APIErrors
func (response ApiTokenList403JSONResponse) VisitApiTokenListResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(403)
return json.NewEncoder(w).Encode(response)
}
type ApiTokenCreateRequestObject struct {
Tenant openapi_types.UUID `json:"tenant"`
Body *ApiTokenCreateJSONRequestBody
}
type ApiTokenCreateResponseObject interface {
VisitApiTokenCreateResponse(w http.ResponseWriter) error
}
type ApiTokenCreate200JSONResponse CreateAPITokenResponse
func (response ApiTokenCreate200JSONResponse) VisitApiTokenCreateResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
return json.NewEncoder(w).Encode(response)
}
type ApiTokenCreate400JSONResponse APIErrors
func (response ApiTokenCreate400JSONResponse) VisitApiTokenCreateResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(400)
return json.NewEncoder(w).Encode(response)
}
type ApiTokenCreate403JSONResponse APIErrors
func (response ApiTokenCreate403JSONResponse) VisitApiTokenCreateResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(403)
return json.NewEncoder(w).Encode(response)
}
type EventListRequestObject struct {
Tenant openapi_types.UUID `json:"tenant"`
Params EventListParams
@@ -2386,12 +2594,18 @@ func (response WorkflowVersionGetDefinition404JSONResponse) VisitWorkflowVersion
}
type StrictServerInterface interface {
ApiTokenUpdateRevoke(ctx echo.Context, request ApiTokenUpdateRevokeRequestObject) (ApiTokenUpdateRevokeResponseObject, error)
EventDataGet(ctx echo.Context, request EventDataGetRequestObject) (EventDataGetResponseObject, error)
MetadataGet(ctx echo.Context, request MetadataGetRequestObject) (MetadataGetResponseObject, error)
TenantCreate(ctx echo.Context, request TenantCreateRequestObject) (TenantCreateResponseObject, error)
ApiTokenList(ctx echo.Context, request ApiTokenListRequestObject) (ApiTokenListResponseObject, error)
ApiTokenCreate(ctx echo.Context, request ApiTokenCreateRequestObject) (ApiTokenCreateResponseObject, error)
EventList(ctx echo.Context, request EventListRequestObject) (EventListResponseObject, error)
EventKeyList(ctx echo.Context, request EventKeyListRequestObject) (EventKeyListResponseObject, error)
@@ -2456,6 +2670,31 @@ type strictHandler struct {
middlewares []StrictMiddlewareFunc
}
// ApiTokenUpdateRevoke operation middleware
func (sh *strictHandler) ApiTokenUpdateRevoke(ctx echo.Context, apiToken openapi_types.UUID) error {
var request ApiTokenUpdateRevokeRequestObject
request.ApiToken = apiToken
handler := func(ctx echo.Context, request interface{}) (interface{}, error) {
return sh.ssi.ApiTokenUpdateRevoke(ctx, request.(ApiTokenUpdateRevokeRequestObject))
}
for _, middleware := range sh.middlewares {
handler = middleware(handler, "ApiTokenUpdateRevoke")
}
response, err := handler(ctx, request)
if err != nil {
return err
} else if validResponse, ok := response.(ApiTokenUpdateRevokeResponseObject); ok {
return validResponse.VisitApiTokenUpdateRevokeResponse(ctx.Response())
} else if response != nil {
return fmt.Errorf("Unexpected response type: %T", response)
}
return nil
}
// EventDataGet operation middleware
func (sh *strictHandler) EventDataGet(ctx echo.Context, event openapi_types.UUID) error {
var request EventDataGetRequestObject
@@ -2533,6 +2772,62 @@ func (sh *strictHandler) TenantCreate(ctx echo.Context) error {
return nil
}
// ApiTokenList operation middleware
func (sh *strictHandler) ApiTokenList(ctx echo.Context, tenant openapi_types.UUID) error {
var request ApiTokenListRequestObject
request.Tenant = tenant
handler := func(ctx echo.Context, request interface{}) (interface{}, error) {
return sh.ssi.ApiTokenList(ctx, request.(ApiTokenListRequestObject))
}
for _, middleware := range sh.middlewares {
handler = middleware(handler, "ApiTokenList")
}
response, err := handler(ctx, request)
if err != nil {
return err
} else if validResponse, ok := response.(ApiTokenListResponseObject); ok {
return validResponse.VisitApiTokenListResponse(ctx.Response())
} else if response != nil {
return fmt.Errorf("Unexpected response type: %T", response)
}
return nil
}
// ApiTokenCreate operation middleware
func (sh *strictHandler) ApiTokenCreate(ctx echo.Context, tenant openapi_types.UUID) error {
var request ApiTokenCreateRequestObject
request.Tenant = tenant
var body ApiTokenCreateJSONRequestBody
if err := ctx.Bind(&body); err != nil {
return err
}
request.Body = &body
handler := func(ctx echo.Context, request interface{}) (interface{}, error) {
return sh.ssi.ApiTokenCreate(ctx, request.(ApiTokenCreateRequestObject))
}
for _, middleware := range sh.middlewares {
handler = middleware(handler, "ApiTokenCreate")
}
response, err := handler(ctx, request)
if err != nil {
return err
} else if validResponse, ok := response.(ApiTokenCreateResponseObject); ok {
return validResponse.VisitApiTokenCreateResponse(ctx.Response())
} else if response != nil {
return fmt.Errorf("Unexpected response type: %T", response)
}
return nil
}
// EventList operation middleware
func (sh *strictHandler) EventList(ctx echo.Context, tenant openapi_types.UUID, params EventListParams) error {
var request EventListRequestObject
@@ -3215,80 +3510,84 @@ func (sh *strictHandler) WorkflowVersionGetDefinition(ctx echo.Context, workflow
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
"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",
"L3KShMdhOL+f/38AAAD//1fM3PiCpwAA",
}
// GetSwagger returns the content of the embedded swagger specification file
@@ -0,0 +1,22 @@
package transformers
import (
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
)
func ToAPIToken(token *db.APITokenModel) *gen.APIToken {
res := &gen.APIToken{
Metadata: *toAPIMetadata(token.ID, token.CreatedAt, token.UpdatedAt),
}
if expiresAt, ok := token.ExpiresAt(); ok {
res.ExpiresAt = expiresAt
}
if name, ok := token.Name(); ok {
res.Name = name
}
return res
}
+1 -1
View File
@@ -20,7 +20,7 @@ func ToWorker(worker *db.WorkerModel) *gen.Worker {
apiActions := make([]string, len(actions))
for i, action := range actions {
apiActions[i] = action.ID
apiActions[i] = action.ActionID
}
res.Actions = &apiActions
+22
View File
@@ -9,6 +9,7 @@ import (
"github.com/hatchet-dev/hatchet/api/v1/server/authn"
"github.com/hatchet-dev/hatchet/api/v1/server/authz"
apitokens "github.com/hatchet-dev/hatchet/api/v1/server/handlers/api-tokens"
"github.com/hatchet-dev/hatchet/api/v1/server/handlers/events"
"github.com/hatchet-dev/hatchet/api/v1/server/handlers/metadata"
"github.com/hatchet-dev/hatchet/api/v1/server/handlers/tenants"
@@ -29,6 +30,7 @@ type apiService struct {
*workflows.WorkflowService
*workers.WorkerService
*metadata.MetadataService
*apitokens.APITokenService
}
func newAPIService(config *server.ServerConfig) *apiService {
@@ -39,6 +41,7 @@ func newAPIService(config *server.ServerConfig) *apiService {
WorkflowService: workflows.NewWorkflowService(config),
WorkerService: workers.NewWorkerService(config),
MetadataService: metadata.NewMetadataService(config),
APITokenService: apitokens.NewAPITokenService(config),
}
}
@@ -75,6 +78,25 @@ func (t *APIServer) Run(ctx context.Context) error {
return tenant, "", nil
})
populatorMW.RegisterGetter("api-token", func(config *server.ServerConfig, parentId, id string) (result interface{}, uniqueParentId string, err error) {
apiToken, err := config.Repository.APIToken().GetAPITokenById(id)
if err != nil {
return nil, "", err
}
// at the moment, API tokens should have a tenant id, because there are no other types of
// API tokens. If we add other types of API tokens, we'll need to pass in a parent id to query
// for.
tenantId, ok := apiToken.TenantID()
if !ok {
return nil, "", fmt.Errorf("api token has no tenant id")
}
return apiToken, tenantId, nil
})
populatorMW.RegisterGetter("tenant-invite", func(config *server.ServerConfig, parentId, id string) (result interface{}, uniqueParentId string, err error) {
tenantInvite, err := config.Repository.TenantInvite().GetTenantInvite(id)
+144
View File
@@ -0,0 +1,144 @@
package cli
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/hatchet-dev/hatchet/internal/encryption"
)
var (
encryptionKeyDir string
cloudKMSCredentialsPath string
cloudKMSKeyURI string
)
var keysetCmd = &cobra.Command{
Use: "keyset",
Short: "command for managing keysets.",
}
var keysetCreateLocalKeysetsCmd = &cobra.Command{
Use: "create-local-keys",
Short: "create a new local master keyset and JWT public/private keyset.",
Run: func(cmd *cobra.Command, args []string) {
err := runCreateLocalKeysets()
if err != nil {
fmt.Printf("Fatal: could not run [keyset create-local-keys] command: %v\n", err)
os.Exit(1)
}
},
}
var keysetCreateCloudKMSJWTCmd = &cobra.Command{
Use: "create-cloudkms-jwt",
Short: "create a new JWT keyset encrypted by a remote CloudKMS repository.",
Run: func(cmd *cobra.Command, args []string) {
err := runCreateCloudKMSJWTKeyset()
if err != nil {
fmt.Printf("Fatal: could not run [keyset create-cloudkms-jwt] command: %v\n", err)
os.Exit(1)
}
},
}
func init() {
rootCmd.AddCommand(keysetCmd)
keysetCmd.AddCommand(keysetCreateLocalKeysetsCmd)
keysetCmd.AddCommand(keysetCreateCloudKMSJWTCmd)
keysetCmd.PersistentFlags().StringVar(
&encryptionKeyDir,
"key-dir",
"",
"if storing keys on disk, path to the directory where encryption keys should be stored",
)
keysetCreateCloudKMSJWTCmd.PersistentFlags().StringVar(
&cloudKMSCredentialsPath,
"credentials",
"",
"path to the JSON credentials file for the CloudKMS repository",
)
keysetCreateCloudKMSJWTCmd.PersistentFlags().StringVar(
&cloudKMSKeyURI,
"key-uri",
"",
"URI of the key in the CloudKMS repository",
)
}
func runCreateLocalKeysets() error {
masterKeyBytes, privateEc256, publicEc256, err := encryption.GenerateLocalKeys()
if err != nil {
return err
}
if encryptionKeyDir != "" {
// we write these as .key files so that they're gitignored by default
err = os.WriteFile(encryptionKeyDir+"/master.key", masterKeyBytes, 0600)
if err != nil {
return err
}
err = os.WriteFile(encryptionKeyDir+"/private_ec256.key", privateEc256, 0600)
if err != nil {
return err
}
err = os.WriteFile(encryptionKeyDir+"/public_ec256.key", publicEc256, 0600)
if err != nil {
return err
}
} else {
fmt.Println("Master Key Bytes:")
fmt.Println(string(masterKeyBytes))
fmt.Println("Private EC256 Keyset:")
fmt.Println(string(privateEc256))
fmt.Println("Public EC256 Keyset:")
fmt.Println(string(publicEc256))
}
return nil
}
func runCreateCloudKMSJWTKeyset() error {
if cloudKMSCredentialsPath == "" {
return fmt.Errorf("missing required flag --credentials")
}
if cloudKMSKeyURI == "" {
return fmt.Errorf("missing required flag --key-uri")
}
credentials, err := os.ReadFile(cloudKMSCredentialsPath)
if err != nil {
return err
}
privateEc256, publicEc256, err := encryption.GenerateJWTKeysetsFromCloudKMS(cloudKMSKeyURI, credentials)
if err != nil {
return err
}
fmt.Println("Private EC256 Keyset:")
fmt.Println(string(privateEc256))
fmt.Println("Public EC256 Keyset:")
fmt.Println(string(publicEc256))
return nil
}
+33
View File
@@ -229,6 +229,39 @@ func generateKeys(generated *generatedConfigFiles) error {
generated.sc.Auth.Cookie.Secrets = fmt.Sprintf("%s %s", cookieHashKey, cookieBlockKey)
}
// if using local keys, generate master key
if !generated.sc.Encryption.CloudKMS.Enabled {
masterKeyBytes, privateEc256, publicEc256, err := encryption.GenerateLocalKeys()
if err != nil {
return err
}
if overwrite || (generated.sc.Encryption.MasterKeyset == "") {
generated.sc.Encryption.MasterKeyset = string(masterKeyBytes)
}
if overwrite || (generated.sc.Encryption.JWT.PublicJWTKeyset == "") || (generated.sc.Encryption.JWT.PrivateJWTKeyset == "") {
generated.sc.Encryption.JWT.PrivateJWTKeyset = string(privateEc256)
generated.sc.Encryption.JWT.PublicJWTKeyset = string(publicEc256)
}
}
// generate jwt keys
if generated.sc.Encryption.CloudKMS.Enabled && (overwrite || (generated.sc.Encryption.JWT.PublicJWTKeyset == "") || (generated.sc.Encryption.JWT.PrivateJWTKeyset == "")) {
privateEc256, publicEc256, err := encryption.GenerateJWTKeysetsFromCloudKMS(
generated.sc.Encryption.CloudKMS.KeyURI,
[]byte(generated.sc.Encryption.CloudKMS.CredentialsJSON),
)
if err != nil {
return err
}
generated.sc.Encryption.JWT.PrivateJWTKeyset = string(privateEc256)
generated.sc.Encryption.JWT.PublicJWTKeyset = string(publicEc256)
}
return nil
}
+2
View File
@@ -41,6 +41,8 @@ func runSeed(cf *loader.ConfigLoader) error {
panic(err)
}
defer dc.Disconnect() // nolint: errcheck
shouldSeedUser := dc.Seed.AdminEmail != "" && dc.Seed.AdminPassword != ""
var userId string
+76
View File
@@ -0,0 +1,76 @@
package cli
import (
"fmt"
"github.com/spf13/cobra"
"github.com/hatchet-dev/hatchet/internal/config/loader"
)
var (
tokenTenantId string
tokenName string
)
var tokenCmd = &cobra.Command{
Use: "token",
Short: "command for generating tokens.",
}
var tokenCreateAPICmd = &cobra.Command{
Use: "create",
Short: "create a new API token.",
Run: func(cmd *cobra.Command, args []string) {
err := runCreateAPIToken()
if err != nil {
fmt.Printf("Fatal: could not run [token create] command: %v\n", err)
}
},
}
func init() {
rootCmd.AddCommand(tokenCmd)
tokenCmd.AddCommand(tokenCreateAPICmd)
tokenCreateAPICmd.PersistentFlags().StringVar(
&tokenTenantId,
"tenant-id",
"",
"the tenant ID to associate with the token",
)
// require the tenant ID
tokenCreateAPICmd.MarkPersistentFlagRequired("tenant-id") // nolint: errcheck
tokenCreateAPICmd.PersistentFlags().StringVar(
&tokenName,
"name",
"default",
"the name of the token",
)
}
func runCreateAPIToken() error {
// read in the local config
configLoader := loader.NewConfigLoader(configDirectory)
serverConf, err := configLoader.LoadServerConfig()
if err != nil {
return err
}
defer serverConf.Disconnect() // nolint: errcheck
defaultTok, err := serverConf.Auth.JWTManager.GenerateTenantToken(tokenTenantId, tokenName)
if err != nil {
return err
}
fmt.Println(defaultTok)
return nil
}
+1
View File
@@ -134,6 +134,7 @@ func startEngineOrDie(cf *loader.ConfigLoader, interruptCh <-chan interface{}) {
}
grpcOpts := []grpc.ServerOpt{
grpc.WithConfig(sc),
grpc.WithIngestor(ei),
grpc.WithDispatcher(d),
grpc.WithAdmin(adminSvc),
+4 -1
View File
@@ -6,10 +6,11 @@ import (
"log"
"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"
"github.com/joho/godotenv"
)
type userCreateEvent struct {
@@ -141,4 +142,6 @@ func run(ch <-chan interface{}, events chan<- string) error {
time.Sleep(time.Second)
}
}
return nil
}
+11 -36
View File
@@ -6,6 +6,7 @@ import yaml from 'react-syntax-highlighter/dist/esm/languages/hljs/yaml';
import json from 'react-syntax-highlighter/dist/esm/languages/hljs/json';
import { anOldHope } from 'react-syntax-highlighter/dist/esm/styles/hljs';
import CopyToClipboard from './copy-to-clipboard';
SyntaxHighlighter.registerLanguage('typescript', typescript);
SyntaxHighlighter.registerLanguage('yaml', yaml);
@@ -16,18 +17,24 @@ export function Code({
language,
className,
maxHeight,
maxWidth,
copy,
wrapLines = true,
}: {
children: string;
language: string;
className?: string;
maxHeight?: string;
maxWidth?: string;
copy?: boolean;
wrapLines?: boolean;
}) {
return (
<div className={cn('text-xs', className)}>
<div className={cn('text-xs flex flex-col gap-4 justify-end', className)}>
<SyntaxHighlighter
language={language}
style={anOldHope}
wrapLines={true}
wrapLines={wrapLines}
lineProps={{
style: { wordBreak: 'break-all', whiteSpace: 'pre-wrap' },
}}
@@ -35,46 +42,14 @@ export function Code({
background: 'hsl(var(--muted) / 0.5)',
borderRadius: '0.5rem',
maxHeight: maxHeight,
maxWidth: maxWidth,
fontFamily:
"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace",
}}
>
{children.trim()}
</SyntaxHighlighter>
{copy && <CopyToClipboard text={children.trim()} />}
</div>
);
}
// import { Fragment } from "react";
// import { Highlight, themes } from "prism-react-renderer";
// const theme = themes.nightOwl;
// export function Code({
// children,
// language,
// }: {
// children: string;
// language: string;
// }) {
// return (
// <Highlight code={children.trimEnd()} language={language} theme={theme}>
// {({ className, style, tokens, getTokenProps }) => (
// <pre className={className} style={style}>
// <code>
// {tokens.map((line, lineIndex) => (
// <Fragment key={lineIndex}>
// {line
// .filter((token) => !token.empty)
// .map((token, tokenIndex) => (
// <span key={tokenIndex} {...getTokenProps({ token })} />
// ))}
// {"\n"}
// </Fragment>
// ))}
// </code>
// </pre>
// )}
// </Highlight>
// );
// }
@@ -0,0 +1,35 @@
import React, { useState } from 'react';
import { Button } from './button';
import { CopyIcon } from '@radix-ui/react-icons';
import { CheckCircleIcon } from '@heroicons/react/24/outline';
type Props = {
text: string;
};
const CopyToClipboard: React.FC<Props> = ({ text }) => {
const [successCopy, setSuccessCopy] = useState(false);
return (
<Button
className="max-w-fit"
onClick={() => {
navigator.clipboard.writeText(text);
setSuccessCopy(true);
setTimeout(() => {
setSuccessCopy(false);
}, 2000);
}}
>
{successCopy ? (
<CheckCircleIcon className="w-4 h-4 mr-2" />
) : (
<CopyIcon className="w-4 h-4 mr-2" />
)}
{successCopy ? 'Copied!' : 'Copy to clipboard'}
</Button>
);
};
export default CopyToClipboard;
+55
View File
@@ -14,6 +14,8 @@ import {
APIErrors,
APIMeta,
AcceptInviteRequest,
CreateAPITokenRequest,
CreateAPITokenResponse,
CreateTenantInviteRequest,
CreateTenantRequest,
EventData,
@@ -23,6 +25,7 @@ import {
EventOrderByDirection,
EventOrderByField,
EventSearch,
ListAPITokensResponse,
RejectInviteRequest,
ReplayEventRequest,
Tenant,
@@ -322,6 +325,58 @@ export class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType
format: "json",
...params,
});
/**
* @description Create an API token for a tenant
*
* @tags API Token
* @name ApiTokenCreate
* @summary Create API Token
* @request POST:/api/v1/tenants/{tenant}/api-tokens
* @secure
*/
apiTokenCreate = (tenant: string, data: CreateAPITokenRequest, params: RequestParams = {}) =>
this.request<CreateAPITokenResponse, APIErrors>({
path: `/api/v1/tenants/${tenant}/api-tokens`,
method: "POST",
body: data,
secure: true,
type: ContentType.Json,
format: "json",
...params,
});
/**
* @description List API tokens for a tenant
*
* @tags API Token
* @name ApiTokenList
* @summary List API Tokens
* @request GET:/api/v1/tenants/{tenant}/api-tokens
* @secure
*/
apiTokenList = (tenant: string, params: RequestParams = {}) =>
this.request<ListAPITokensResponse, APIErrors>({
path: `/api/v1/tenants/${tenant}/api-tokens`,
method: "GET",
secure: true,
format: "json",
...params,
});
/**
* @description Revoke an API token for a tenant
*
* @tags API Token
* @name ApiTokenUpdateRevoke
* @summary Revoke API Token
* @request POST:/api/v1/api-tokens/{api-token}
* @secure
*/
apiTokenUpdateRevoke = (apiToken: string, params: RequestParams = {}) =>
this.request<void, APIErrors>({
path: `/api/v1/api-tokens/${apiToken}`,
method: "POST",
secure: true,
...params,
});
/**
* @description Lists all events for a tenant.
*
@@ -533,3 +533,35 @@ export interface Worker {
/** The recent step runs for this worker. */
recentStepRuns?: StepRun[];
}
export interface APIToken {
metadata: APIResourceMeta;
/**
* The name of the API token.
* @maxLength 255
*/
name: string;
/**
* When the API token expires.
* @format date-time
*/
expiresAt: string;
}
export interface CreateAPITokenRequest {
/**
* A name for the API token.
* @maxLength 255
*/
name: string;
}
export interface CreateAPITokenResponse {
/** The API token. */
token: string;
}
export interface ListAPITokensResponse {
pagination?: PaginationResponse;
rows?: APIToken[];
}
+6
View File
@@ -26,6 +26,12 @@ export const queries = createQueryKeyStore({
queryFn: async () => (await api.tenantMemberList(tenant)).data,
}),
},
tokens: {
list: (tenant: string) => ({
queryKey: ['api-token:list', tenant],
queryFn: async () => (await api.apiTokenList(tenant)).data,
}),
},
invites: {
list: (tenant: string) => ({
queryKey: ['tenant-invite:list', tenant],
@@ -0,0 +1,55 @@
import { ColumnDef } from '@tanstack/react-table';
import { DataTableColumnHeader } from '../../../../components/molecules/data-table/data-table-column-header';
import { APIToken } from '@/lib/api';
import { DataTableRowActions } from '@/components/molecules/data-table/data-table-row-actions';
import { relativeDate } from '@/lib/utils';
export const columns = ({
onRevokeClick,
}: {
onRevokeClick: (row: APIToken) => void;
}): ColumnDef<APIToken>[] => {
return [
{
accessorKey: 'name',
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Name" />
),
cell: ({ row }) => <div>{row.getValue('name')}</div>,
enableSorting: false,
enableHiding: false,
},
{
accessorKey: 'created',
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Created" />
),
cell: ({ row }) => (
<div>{relativeDate(row.original.metadata.createdAt)}</div>
),
},
{
accessorKey: 'Expires',
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Expires" />
),
cell: ({ row }) => {
return <div>{relativeDate(row.original.expiresAt)}</div>;
},
},
{
id: 'actions',
cell: ({ row }) => (
<DataTableRowActions
row={row}
actions={[
{
label: 'Revoke',
onClick: () => onRevokeClick(row.original),
},
]}
/>
),
},
];
};
@@ -0,0 +1,103 @@
import {
DialogContent,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { Code } from '@/components/ui/code';
import { Button } from '@/components/ui/button';
import { z } from 'zod';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { Label } from '@/components/ui/label';
import { Input } from '@/components/ui/input';
import { cn } from '@/lib/utils';
import { Spinner } from '@/components/ui/loading';
const schema = z.object({
name: z.string().min(1).max(255),
});
interface CreateTokenDialogProps {
className?: string;
token?: string;
onSubmit: (opts: z.infer<typeof schema>) => void;
isLoading: boolean;
fieldErrors?: Record<string, string>;
}
export function CreateTokenDialog({
className,
token,
...props
}: CreateTokenDialogProps) {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<z.infer<typeof schema>>({
resolver: zodResolver(schema),
});
const nameError = errors.name?.message?.toString() || props.fieldErrors?.name;
if (token) {
return (
<DialogContent className="w-fit max-w-[700px]">
<DialogHeader>
<DialogTitle>Keep it secret, keep it safe</DialogTitle>
</DialogHeader>
<p className="text-sm">
This is the only time we will show you this token. Make sure to copy
it somewhere safe.
</p>
<Code
language="typescript"
className="text-sm"
wrapLines={false}
maxWidth={'calc(700px - 4rem)'}
copy
>
{token}
</Code>
</DialogContent>
);
}
// TODO: add a name for the token
return (
<DialogContent className="w-fit max-w-[80%] min-w-[500px]">
<DialogHeader>
<DialogTitle>Create a new API token</DialogTitle>
</DialogHeader>
<div className={cn('grid gap-6', className)}>
<form
onSubmit={handleSubmit((d) => {
props.onSubmit(d);
})}
>
<div className="grid gap-4">
<div className="grid gap-2">
<Label htmlFor="email">Name</Label>
<Input
{...register('name')}
id="api-token-name"
placeholder="My Token"
autoCapitalize="none"
autoCorrect="off"
disabled={props.isLoading}
/>
{nameError && (
<div className="text-sm text-red-500">{nameError}</div>
)}
</div>
<Button disabled={props.isLoading}>
{props.isLoading && <Spinner />}
Generate token
</Button>
</div>
</form>
</div>
</DialogContent>
);
}
@@ -43,7 +43,6 @@ export const columns = ({
<DataTableColumnHeader column={column} title="Expires" />
),
cell: ({ row }) => {
console.log(row.original.expires);
return <div>{relativeDate(row.original.expires)}</div>;
},
},
@@ -0,0 +1,52 @@
import { Button } from '@/components/ui/button';
import { Spinner } from '@/components/ui/loading.tsx';
import {
DialogContent,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { APIToken } from '@/lib/api';
interface RevokeTokenFormProps {
className?: string;
onSubmit: (apiToken: APIToken) => void;
onCancel: () => void;
apiToken: APIToken;
isLoading: boolean;
}
export function RevokeTokenForm({ className, ...props }: RevokeTokenFormProps) {
return (
<DialogContent className="w-fit max-w-[80%] min-w-[500px]">
<DialogHeader>
<DialogTitle>Delete invite</DialogTitle>
</DialogHeader>
<div>
<div className="text-sm text-foreground mb-4">
Are you sure you want to revoke the API token {props.apiToken.name}?
This action will immediately prevent any services running with this
token from dispatching events or executing steps.
</div>
<div className="flex flex-row gap-4">
<Button
variant="ghost"
onClick={() => {
props.onCancel();
}}
>
Cancel
</Button>
<Button
variant="destructive"
onClick={() => {
props.onSubmit(props.apiToken);
}}
>
{props.isLoading && <Spinner />}
Revoke API token
</Button>
</div>
</div>
</DialogContent>
);
}
@@ -7,6 +7,8 @@ import { CreateInviteForm } from './components/create-invite-form';
import { useApiError } from '@/lib/hooks';
import { useMutation, useQuery } from '@tanstack/react-query';
import api, {
APIToken,
CreateAPITokenRequest,
CreateTenantInviteRequest,
TenantInvite,
UpdateTenantInviteRequest,
@@ -16,8 +18,11 @@ import { Dialog } from '@/components/ui/dialog';
import { DataTable } from '@/components/molecules/data-table/data-table';
import { columns } from './components/invites-columns';
import { columns as membersColumns } from './components/members-columns';
import { columns as apiTokensColumns } from './components/api-tokens-columns';
import { UpdateInviteForm } from './components/update-invite-form';
import { DeleteInviteForm } from './components/delete-invite-form';
import { CreateTokenDialog } from './components/create-token-dialog';
import { RevokeTokenForm } from './components/revoke-token-form';
export default function TenantSettings() {
const { tenant } = useOutletContext<TenantContextType>();
@@ -32,6 +37,8 @@ export default function TenantSettings() {
<MembersList />
<Separator className="my-4" />
<InvitesList />
<Separator className="my-4" />
<TokensList />
</div>
</div>
);
@@ -247,3 +254,137 @@ function DeleteInvite({
</Dialog>
);
}
function TokensList() {
const { tenant } = useOutletContext<TenantContextType>();
const [showTokenDialog, setShowTokenDialog] = useState(false);
const [revokeToken, setRevokeToken] = useState<APIToken | null>(null);
const listTokensQuery = useQuery({
...queries.tokens.list(tenant.metadata.id),
});
const cols = apiTokensColumns({
onRevokeClick: (row) => {
setRevokeToken(row);
},
});
return (
<div>
<div className="flex flex-row justify-between items-center">
<h3 className="text-xl font-semibold leading-tight text-foreground">
API Tokens
</h3>
<Button key="create-api-token" onClick={() => setShowTokenDialog(true)}>
Create API Token
</Button>
</div>
<Separator className="my-4" />
<DataTable
isLoading={listTokensQuery.isLoading}
columns={cols}
data={listTokensQuery.data?.rows || []}
filters={[]}
getRowId={(row) => row.metadata.id}
/>
{showTokenDialog && (
<CreateToken
tenant={tenant.metadata.id}
showTokenDialog={showTokenDialog}
setShowTokenDialog={setShowTokenDialog}
onSuccess={() => {
listTokensQuery.refetch();
}}
/>
)}
{revokeToken && (
<RevokeToken
tenant={tenant.metadata.id}
apiToken={revokeToken}
setShowTokenRevoke={() => setRevokeToken(null)}
onSuccess={() => {
setRevokeToken(null);
listTokensQuery.refetch();
}}
/>
)}
</div>
);
}
function CreateToken({
tenant,
showTokenDialog,
setShowTokenDialog,
onSuccess,
}: {
tenant: string;
onSuccess: () => void;
showTokenDialog: boolean;
setShowTokenDialog: (show: boolean) => void;
}) {
const [generatedToken, setGeneratedToken] = useState<string | undefined>();
const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});
const { handleApiError } = useApiError({
setFieldErrors: setFieldErrors,
});
const createTokenMutation = useMutation({
mutationKey: ['api-token:create', tenant],
mutationFn: async (data: CreateAPITokenRequest) => {
const res = await api.apiTokenCreate(tenant, data);
return res.data;
},
onSuccess: (data) => {
setGeneratedToken(data.token);
onSuccess();
},
onError: handleApiError,
});
return (
<Dialog open={showTokenDialog} onOpenChange={setShowTokenDialog}>
<CreateTokenDialog
isLoading={createTokenMutation.isPending}
onSubmit={createTokenMutation.mutate}
token={generatedToken}
fieldErrors={fieldErrors}
/>
</Dialog>
);
}
function RevokeToken({
tenant,
apiToken,
setShowTokenRevoke,
onSuccess,
}: {
tenant: string;
apiToken: APIToken;
setShowTokenRevoke: (show: boolean) => void;
onSuccess: () => void;
}) {
const { handleApiError } = useApiError({});
const revokeMutation = useMutation({
mutationKey: ['api-token:revoke', tenant, apiToken],
mutationFn: async () => {
await api.apiTokenUpdateRevoke(apiToken.metadata.id);
},
onSuccess: onSuccess,
onError: handleApiError,
});
return (
<Dialog open={!!apiToken} onOpenChange={setShowTokenRevoke}>
<RevokeTokenForm
apiToken={apiToken}
isLoading={revokeMutation.isPending}
onSubmit={() => revokeMutation.mutate()}
onCancel={() => setShowTokenRevoke(false)}
/>
</Dialog>
);
}
+20 -7
View File
@@ -20,17 +20,23 @@ require (
github.com/spf13/cobra v1.8.0
github.com/spf13/viper v1.16.0
github.com/steebchen/prisma-client-go v0.32.1
github.com/tink-crypto/tink-go v0.0.0-20230613075026-d6de17e3f164
github.com/tink-crypto/tink-go-gcpkms v0.0.0-20230602082706-31d0d09ccc8d
go.opentelemetry.io/otel v1.21.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0
go.opentelemetry.io/otel/sdk v1.21.0
go.opentelemetry.io/otel/trace v1.21.0
google.golang.org/api v0.157.0
sigs.k8s.io/yaml v1.4.0
)
require (
cloud.google.com/go/compute v1.23.3 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/go-logr/logr v1.3.0 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
@@ -39,6 +45,10 @@ require (
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 // indirect
@@ -57,12 +67,14 @@ require (
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect
go.opentelemetry.io/otel/metric v1.21.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect
)
require (
@@ -75,6 +87,7 @@ require (
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.5.0
github.com/gorilla/schema v1.2.1
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
@@ -96,12 +109,12 @@ require (
golang.org/x/crypto v0.18.0
golang.org/x/net v0.20.0 // indirect
golang.org/x/oauth2 v0.16.0
golang.org/x/sync v0.4.0
golang.org/x/sync v0.6.0
golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
google.golang.org/grpc v1.59.0
google.golang.org/protobuf v1.31.0
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect
google.golang.org/grpc v1.60.1
google.golang.org/protobuf v1.32.0
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1
)
+55 -23
View File
@@ -23,6 +23,10 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
@@ -71,6 +75,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
@@ -118,6 +124,8 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
@@ -140,6 +148,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -152,6 +161,7 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
@@ -174,11 +184,17 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
@@ -191,6 +207,8 @@ github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8L
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1 h1:HcUWd006luQPljE73d5sk+/VgYPGUReEVz2y1/qylwY=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1/go.mod h1:w9Y7gY31krpLmrVU5ZPG9H7l9fZuRu5/3R3S3FMtVQ4=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 h1:6UKoz5ujsI55KNpsJH3UwCq3T8kKbZwNZBNPuTTje8U=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1/go.mod h1:YvJ2f6MplWDhfxiUC3KpyTy76kYUZA4W3pTv/wdKQ9Y=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
@@ -301,8 +319,6 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=
github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/steebchen/prisma-client-go v0.31.4-0.20231228102837-d2b2373128a2 h1:8vofR6qaIWoTi2AGymk1MlipkUWLjBNjKjYA8DYaHps=
github.com/steebchen/prisma-client-go v0.31.4-0.20231228102837-d2b2373128a2/go.mod h1:ksKELgUZSn56rbAv1jlF8D7o8V6lis0Tc2LEgv2qNbs=
github.com/steebchen/prisma-client-go v0.32.1 h1:7jUke4XVSzUkV2TN4VhYSwjU7qizM9w475gpudkLCNI=
github.com/steebchen/prisma-client-go v0.32.1/go.mod h1:shY2GTQyv15WYTE4p2zffr01ratTzX0zXtBWnDHiLpo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -321,6 +337,10 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tink-crypto/tink-go v0.0.0-20230613075026-d6de17e3f164 h1:yhVO0Yhq84FjdcotvFFvDJRNHJ7mO743G12VdcW4Evc=
github.com/tink-crypto/tink-go v0.0.0-20230613075026-d6de17e3f164/go.mod h1:HhtDVdE/PRZFRia834tkmcwuscnaAzda1RJUW9Pr3Rg=
github.com/tink-crypto/tink-go-gcpkms v0.0.0-20230602082706-31d0d09ccc8d h1:+In5BwTMe2nF3FC6LrYqg71jDyaOOMZ4EQBFUhFq23g=
github.com/tink-crypto/tink-go-gcpkms v0.0.0-20230602082706-31d0d09ccc8d/go.mod h1:TXKMH7TDt0h7QXtI9TdYPyly6xZL+ooPpbw30qekmEc=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
@@ -332,12 +352,17 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo=
go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw=
@@ -361,9 +386,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -401,6 +425,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -429,12 +454,12 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -458,8 +483,9 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -498,15 +524,16 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -515,13 +542,14 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -569,6 +597,7 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -592,14 +621,17 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.157.0 h1:ORAeqmbrrozeyw5NjnMxh7peHO0UzV4wWYSwZeCUb20=
google.golang.org/api v0.157.0/go.mod h1:+z4v4ufbZ1WEpld6yMGHyggs+PmAHiaLNj5ytP3N01g=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -636,12 +668,12 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f h1:2yNACc1O40tTnrsbk9Cv6oxiW8pxI/pXj0wRtdlYmgY=
google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f/go.mod h1:Uy9bTZJqmfrw2rIBxgGLnamc78euZULUBrLZ9XTITKI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc=
google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg=
google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0=
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM=
google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -658,8 +690,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU=
google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -672,8 +704,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
# This scripts generates a local API token.
set -eux
set -a
. .env
set +a
go run ./cmd/hatchet-admin token create --name "local" --tenant-id 707d0855-80ab-4e1f-a156-f1c4546cbf52
@@ -0,0 +1,17 @@
#!/bin/bash
# This scripts generates local encryption keys for development.
set -eux
ENCRYPTION_KEYS_DIR=./encryption-keys
# Read CERTS_DIR from args if exists
if [ -n "$1" ]; then
ENCRYPTION_KEYS_DIR=$1
fi
mkdir -p $ENCRYPTION_KEYS_DIR
# Generate a master encryption key
go run ./cmd/hatchet-admin keyset create-local-keys --key-dir $ENCRYPTION_KEYS_DIR
-9
View File
@@ -1,9 +0,0 @@
#!/bin/bash
set -eux
set -a
. .env
set +a
go run ./cmd/temporal-server
-33
View File
@@ -1,33 +0,0 @@
#!/bin/bash
cat > .env <<EOF
TEMPORAL_CLIENT_TLS_ROOT_CA_FILE=./hack/dev/certs/ca.cert
TEMPORAL_CLIENT_TLS_CERT_FILE=./hack/dev/certs/client-worker.pem
TEMPORAL_CLIENT_TLS_KEY_FILE=./hack/dev/certs/client-worker.key
TEMPORAL_CLIENT_TLS_SERVER_NAME=cluster
TEMPORAL_SQLITE_PATH=./temporal.db
TEMPORAL_LOG_LEVEL=error
TEMPORAL_NAMESPACES=default
TEMPORAL_FRONTEND_TLS_SERVER_NAME=cluster
TEMPORAL_FRONTEND_TLS_CERT_FILE=./hack/dev/certs/cluster.pem
TEMPORAL_FRONTEND_TLS_KEY_FILE=./hack/dev/certs/cluster.key
TEMPORAL_FRONTEND_TLS_ROOT_CA_FILE=./hack/dev/certs/ca.cert
TEMPORAL_WORKER_TLS_SERVER_NAME=cluster
TEMPORAL_WORKER_TLS_CERT_FILE=./hack/dev/certs/cluster.pem
TEMPORAL_WORKER_TLS_KEY_FILE=./hack/dev/certs/cluster.key
TEMPORAL_WORKER_TLS_ROOT_CA_FILE=./hack/dev/certs/ca.cert
TEMPORAL_INTERNODE_TLS_SERVER_NAME=cluster
TEMPORAL_INTERNODE_TLS_CERT_FILE=./hack/dev/certs/cluster.pem
TEMPORAL_INTERNODE_TLS_KEY_FILE=./hack/dev/certs/cluster.key
TEMPORAL_INTERNODE_TLS_ROOT_CA_FILE=./hack/dev/certs/ca.cert
TEMPORAL_UI_TLS_ROOT_CA_FILE=./hack/dev/certs/ca.cert
TEMPORAL_UI_TLS_CERT_FILE=./hack/dev/certs/client-internal-admin.pem
TEMPORAL_UI_TLS_KEY_FILE=./hack/dev/certs/client-internal-admin.key
TEMPORAL_UI_TLS_SERVER_NAME=cluster
EOF
+163
View File
@@ -0,0 +1,163 @@
package token
import (
"fmt"
"time"
"github.com/google/uuid"
"github.com/tink-crypto/tink-go/jwt"
"github.com/hatchet-dev/hatchet/internal/encryption"
"github.com/hatchet-dev/hatchet/internal/repository"
)
type JWTManager interface {
GenerateTenantToken(tenantId, name string) (string, error)
ValidateTenantToken(token string) (string, error)
}
type TokenOpts struct {
Issuer string
Audience string
}
type jwtManagerImpl struct {
encryption encryption.EncryptionService
opts *TokenOpts
tokenRepo repository.APITokenRepository
verifier jwt.Verifier
}
func NewJWTManager(encryptionSvc encryption.EncryptionService, tokenRepo repository.APITokenRepository, opts *TokenOpts) (JWTManager, error) {
verifier, err := jwt.NewVerifier(encryptionSvc.GetPublicJWTHandle())
if err != nil {
return nil, fmt.Errorf("failed to create JWT Verifier: %v", err)
}
return &jwtManagerImpl{
encryption: encryptionSvc,
opts: opts,
tokenRepo: tokenRepo,
verifier: verifier,
}, nil
}
func (j *jwtManagerImpl) GenerateTenantToken(tenantId, name string) (string, error) {
// Retrieve the JWT Signer primitive from privateKeysetHandle.
signer, err := jwt.NewSigner(j.encryption.GetPrivateJWTHandle())
if err != nil {
return "", fmt.Errorf("failed to create JWT Signer: %v", err)
}
tokenId, expiresAt, opts := j.getJWTOptionsForTenant(tenantId)
rawJWT, err := jwt.NewRawJWT(opts)
if err != nil {
return "", fmt.Errorf("failed to create raw JWT: %v", err)
}
token, err := signer.SignAndEncode(rawJWT)
if err != nil {
return "", fmt.Errorf("failed to sign and encode JWT: %v", err)
}
// write the token to the database
_, err = j.tokenRepo.CreateAPIToken(&repository.CreateAPITokenOpts{
ID: tokenId,
ExpiresAt: expiresAt,
TenantId: &tenantId,
Name: &name,
})
if err != nil {
return "", fmt.Errorf("failed to write token to database: %v", err)
}
return token, nil
}
func (j *jwtManagerImpl) ValidateTenantToken(token string) (tenantId string, err error) {
// Verify the signed token.
audience := j.opts.Audience
validator, err := jwt.NewValidator(&jwt.ValidatorOpts{
ExpectedAudience: &audience,
ExpectedIssuer: &j.opts.Issuer,
FixedNow: time.Now(),
ExpectIssuedInThePast: true,
})
if err != nil {
return "", fmt.Errorf("failed to create JWT Validator: %v", err)
}
verifiedJwt, err := j.verifier.VerifyAndDecode(token, validator)
if err != nil {
return "", fmt.Errorf("failed to verify and decode JWT: %v", err)
}
// Read the token from the database and make sure it's not revoked
if hasTokenId := verifiedJwt.HasStringClaim("token_id"); !hasTokenId {
return "", fmt.Errorf("token does not have token_id claim")
}
tokenId, err := verifiedJwt.StringClaim("token_id")
if err != nil {
return "", fmt.Errorf("failed to read token_id claim: %v", err)
}
// read the token from the database
dbToken, err := j.tokenRepo.GetAPITokenById(tokenId)
if err != nil {
return "", fmt.Errorf("failed to read token from database: %v", err)
}
if dbToken.Revoked {
return "", fmt.Errorf("token has been revoked")
}
if expiresAt, ok := dbToken.ExpiresAt(); ok && expiresAt.Before(time.Now()) {
return "", fmt.Errorf("token has expired")
}
// ensure the subject of the token matches the tenantId
if hasSubject := verifiedJwt.HasSubject(); !hasSubject {
return "", fmt.Errorf("token does not have subject claim")
}
subject, err := verifiedJwt.Subject()
if err != nil {
return "", fmt.Errorf("failed to read subject claim: %v", err)
}
return subject, nil
}
func (j *jwtManagerImpl) getJWTOptionsForTenant(tenantId string) (tokenId string, expiresAt time.Time, opts *jwt.RawJWTOptions) {
expiresAt = time.Now().Add(90 * 24 * time.Hour)
iAt := time.Now()
audience := j.opts.Audience
subject := tenantId
issuer := j.opts.Issuer
tokenId = uuid.New().String()
opts = &jwt.RawJWTOptions{
IssuedAt: &iAt,
Audience: &audience,
Subject: &subject,
ExpiresAt: &expiresAt,
Issuer: &issuer,
CustomClaims: map[string]interface{}{
"token_id": tokenId,
},
}
return
}
+142
View File
@@ -0,0 +1,142 @@
//go:build integration
package token_test
import (
"fmt"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/hatchet-dev/hatchet/internal/auth/token"
"github.com/hatchet-dev/hatchet/internal/config/database"
"github.com/hatchet-dev/hatchet/internal/encryption"
"github.com/hatchet-dev/hatchet/internal/repository"
"github.com/hatchet-dev/hatchet/internal/testutils"
)
func TestCreateTenantToken(t *testing.T) {
testutils.RunTestWithDatabase(t, func(conf *database.Config) error {
jwtManager := getJWTManager(t, conf)
tenantId := uuid.New().String()
// create the tenant
slugSuffix, err := encryption.GenerateRandomBytes(8)
if err != nil {
t.Fatal(err.Error())
}
_, err = conf.Repository.Tenant().CreateTenant(&repository.CreateTenantOpts{
ID: &tenantId,
Name: "test-tenant",
Slug: fmt.Sprintf("test-tenant-%s", slugSuffix),
})
if err != nil {
t.Fatal(err.Error())
}
token, err := jwtManager.GenerateTenantToken(tenantId, "test token")
if err != nil {
t.Fatal(err.Error())
}
// validate the token
newTenantId, err := jwtManager.ValidateTenantToken(token)
assert.NoError(t, err)
assert.Equal(t, tenantId, newTenantId)
return nil
})
}
func TestRevokeTenantToken(t *testing.T) {
testutils.RunTestWithDatabase(t, func(conf *database.Config) error {
jwtManager := getJWTManager(t, conf)
tenantId := uuid.New().String()
// create the tenant
slugSuffix, err := encryption.GenerateRandomBytes(8)
if err != nil {
t.Fatal(err.Error())
}
_, err = conf.Repository.Tenant().CreateTenant(&repository.CreateTenantOpts{
ID: &tenantId,
Name: "test-tenant",
Slug: fmt.Sprintf("test-tenant-%s", slugSuffix),
})
if err != nil {
t.Fatal(err.Error())
}
token, err := jwtManager.GenerateTenantToken(tenantId, "test token")
if err != nil {
t.Fatal(err.Error())
}
// validate the token
_, err = jwtManager.ValidateTenantToken(token)
assert.NoError(t, err)
// revoke the token
apiTokens, err := conf.Repository.APIToken().ListAPITokensByTenant(tenantId)
if err != nil {
t.Fatal(err.Error())
}
assert.Len(t, apiTokens, 1)
err = conf.Repository.APIToken().RevokeAPIToken(apiTokens[0].ID)
if err != nil {
t.Fatal(err.Error())
}
// validate the token again
_, err = jwtManager.ValidateTenantToken(token)
assert.Error(t, err)
return nil
})
}
func getJWTManager(t *testing.T, conf *database.Config) token.JWTManager {
t.Helper()
masterKeyBytes, privateJWTBytes, publicJWTBytes, err := encryption.GenerateLocalKeys()
if err != nil {
t.Fatal(err.Error())
}
encryptionService, err := encryption.NewLocalEncryption(masterKeyBytes, privateJWTBytes, publicJWTBytes)
if err != nil {
t.Fatal(err.Error())
}
tokenRepo := conf.Repository.APIToken()
jwtManager, err := token.NewJWTManager(encryptionService, tokenRepo, &token.TokenOpts{
Issuer: "hatchet",
Audience: "hatchet",
})
if err != nil {
t.Fatal(err.Error())
}
return jwtManager
}
+8
View File
@@ -11,6 +11,10 @@ import (
type ClientConfigFile struct {
TenantId string `mapstructure:"tenantId" json:"tenantId,omitempty"`
Token string `mapstructure:"token" json:"token,omitempty"`
HostPort string `mapstructure:"hostPort" json:"hostPort,omitempty"`
TLS ClientTLSConfigFile `mapstructure:"tls" json:"tls,omitempty"`
}
@@ -22,14 +26,18 @@ type ClientTLSConfigFile struct {
type ClientConfig struct {
TenantId string
Token string
TLSConfig *tls.Config
}
func BindAllEnv(v *viper.Viper) {
_ = v.BindEnv("tenantId", "HATCHET_CLIENT_TENANT_ID")
_ = v.BindEnv("token", "HATCHET_CLIENT_TOKEN")
_ = v.BindEnv("hostPort", "HATCHET_CLIENT_HOST_PORT")
// tls options
_ = v.BindEnv("tls.base.tlsStrategy", "HATCHET_CLIENT_TLS_STRATEGY")
_ = v.BindEnv("tls.base.tlsCertFile", "HATCHET_CLIENT_TLS_CERT_FILE")
_ = v.BindEnv("tls.base.tlsKeyFile", "HATCHET_CLIENT_TLS_KEY_FILE")
_ = v.BindEnv("tls.base.tlsRootCAFile", "HATCHET_CLIENT_TLS_ROOT_CA_FILE")
+108 -1
View File
@@ -5,6 +5,7 @@ package loader
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
@@ -12,9 +13,11 @@ import (
"github.com/hatchet-dev/hatchet/internal/auth/cookie"
"github.com/hatchet-dev/hatchet/internal/auth/oauth"
"github.com/hatchet-dev/hatchet/internal/auth/token"
"github.com/hatchet-dev/hatchet/internal/config/database"
"github.com/hatchet-dev/hatchet/internal/config/loader/loaderutils"
"github.com/hatchet-dev/hatchet/internal/config/server"
"github.com/hatchet-dev/hatchet/internal/encryption"
"github.com/hatchet-dev/hatchet/internal/logger"
"github.com/hatchet-dev/hatchet/internal/repository/prisma"
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
@@ -106,7 +109,7 @@ func GetDatabaseConfigFromConfigFile(cf *database.ConfigFile) (res *database.Con
cf.PostgresSSLMode,
)
// os.Setenv("DATABASE_URL", databaseUrl)
os.Setenv("DATABASE_URL", databaseUrl)
client := db.NewClient(
// db.WithDatasourceURL(databaseUrl),
@@ -196,9 +199,26 @@ func GetServerConfigFromConfigfile(dc *database.Config, cf *server.ServerConfigF
auth.GoogleOAuthConfig = gClient
}
encryptionSvc, err := loadEncryptionSvc(cf)
if err != nil {
return nil, fmt.Errorf("could not load encryption service: %w", err)
}
// create a new JWT manager
auth.JWTManager, err = token.NewJWTManager(encryptionSvc, dc.Repository.APIToken(), &token.TokenOpts{
Issuer: cf.Runtime.ServerURL,
Audience: cf.Runtime.ServerURL,
})
if err != nil {
return nil, fmt.Errorf("could not create JWT manager: %w", err)
}
return &server.ServerConfig{
Runtime: cf.Runtime,
Auth: auth,
Encryption: encryptionSvc,
Config: dc,
TaskQueue: tq,
Services: cf.Services,
@@ -214,3 +234,90 @@ func GetServerConfigFromConfigfile(dc *database.Config, cf *server.ServerConfigF
func getStrArr(v string) []string {
return strings.Split(v, " ")
}
func loadEncryptionSvc(cf *server.ServerConfigFile) (encryption.EncryptionService, error) {
var err error
hasLocalMasterKeyset := cf.Encryption.MasterKeyset != "" || cf.Encryption.MasterKeysetFile != ""
isCloudKMSEnabled := cf.Encryption.CloudKMS.Enabled
if !hasLocalMasterKeyset && !isCloudKMSEnabled {
return nil, fmt.Errorf("encryption is required")
}
if hasLocalMasterKeyset && isCloudKMSEnabled {
return nil, fmt.Errorf("cannot use both encryption and cloud kms")
}
hasJWTKeys := (cf.Encryption.JWT.PublicJWTKeyset != "" || cf.Encryption.JWT.PublicJWTKeysetFile != "") &&
(cf.Encryption.JWT.PrivateJWTKeyset != "" || cf.Encryption.JWT.PrivateJWTKeysetFile != "")
if !hasJWTKeys {
return nil, fmt.Errorf("jwt encryption is required")
}
privateJWT := cf.Encryption.JWT.PrivateJWTKeyset
if cf.Encryption.JWT.PrivateJWTKeysetFile != "" {
privateJWTBytes, err := loaderutils.GetFileBytes(cf.Encryption.JWT.PrivateJWTKeysetFile)
if err != nil {
return nil, fmt.Errorf("could not load private jwt keyset file: %w", err)
}
privateJWT = string(privateJWTBytes)
}
publicJWT := cf.Encryption.JWT.PublicJWTKeyset
if cf.Encryption.JWT.PublicJWTKeysetFile != "" {
publicJWTBytes, err := loaderutils.GetFileBytes(cf.Encryption.JWT.PublicJWTKeysetFile)
if err != nil {
return nil, fmt.Errorf("could not load public jwt keyset file: %w", err)
}
publicJWT = string(publicJWTBytes)
}
var encryptionSvc encryption.EncryptionService
if hasLocalMasterKeyset {
masterKeyset := cf.Encryption.MasterKeyset
if cf.Encryption.MasterKeysetFile != "" {
masterKeysetBytes, err := loaderutils.GetFileBytes(cf.Encryption.MasterKeysetFile)
if err != nil {
return nil, fmt.Errorf("could not load master keyset file: %w", err)
}
masterKeyset = string(masterKeysetBytes)
}
encryptionSvc, err = encryption.NewLocalEncryption(
[]byte(masterKeyset),
[]byte(privateJWT),
[]byte(publicJWT),
)
if err != nil {
return nil, fmt.Errorf("could not create raw keyset encryption service: %w", err)
}
}
if isCloudKMSEnabled {
encryptionSvc, err = encryption.NewCloudKMSEncryption(
cf.Encryption.CloudKMS.KeyURI,
[]byte(cf.Encryption.CloudKMS.CredentialsJSON),
[]byte(privateJWT),
[]byte(publicJWT),
)
if err != nil {
return nil, fmt.Errorf("could not create CloudKMS encryption service: %w", err)
}
}
return encryptionSvc, nil
}
@@ -21,6 +21,20 @@ func GetConfigBytes(configFilePath string) ([][]byte, error) {
return configFileBytes, nil
}
func GetFileBytes(filename string) ([]byte, error) {
if fileExists(filename) {
fileBytes, err := os.ReadFile(filename) // #nosec G304 -- config files are meant to be read from user-supplied directory
if err != nil {
return nil, fmt.Errorf("could not read config file at path %s: %w", filename, err)
}
return fileBytes, nil
}
return nil, fmt.Errorf("could not read config file at path %s", filename)
}
func fileExists(filename string) bool {
info, err := os.Stat(filename)
if err != nil && os.IsNotExist(err) {
+47 -16
View File
@@ -10,15 +10,26 @@ import (
"github.com/hatchet-dev/hatchet/internal/config/shared"
)
func LoadClientTLSConfig(tlsConfig *client.ClientTLSConfigFile) (*tls.Config, error) {
func LoadClientTLSConfig(tlsConfig *client.ClientTLSConfigFile, serverName string) (*tls.Config, error) {
res, ca, err := LoadBaseTLSConfig(&tlsConfig.Base)
if err != nil {
return nil, err
}
res.ServerName = tlsConfig.TLSServerName
res.RootCAs = ca
res.ServerName = serverName
switch tlsConfig.Base.TLSStrategy {
case "tls":
if ca != nil {
res.RootCAs = ca
}
case "mtls":
res.ServerName = tlsConfig.TLSServerName
res.RootCAs = ca
default:
return nil, fmt.Errorf("invalid TLS strategy: %s", tlsConfig.Base.TLSStrategy)
}
return res, nil
}
@@ -30,8 +41,23 @@ func LoadServerTLSConfig(tlsConfig *shared.TLSConfigFile) (*tls.Config, error) {
return nil, err
}
res.ClientAuth = tls.RequireAndVerifyClientCert
res.ClientCAs = ca
switch tlsConfig.TLSStrategy {
case "tls":
res.ClientAuth = tls.VerifyClientCertIfGiven
if ca != nil {
res.ClientCAs = ca
}
case "mtls":
if ca == nil {
return nil, fmt.Errorf("Client CA is required for mTLS")
}
res.ClientAuth = tls.RequireAndVerifyClientCert
res.ClientCAs = ca
default:
return nil, fmt.Errorf("invalid TLS strategy: %s", tlsConfig.TLSStrategy)
}
return res, nil
}
@@ -45,8 +71,6 @@ func LoadBaseTLSConfig(tlsConfig *shared.TLSConfigFile) (*tls.Config, *x509.Cert
x509Cert, err = tls.X509KeyPair([]byte(tlsConfig.TLSCert), []byte(tlsConfig.TLSKey))
case tlsConfig.TLSCertFile != "" && tlsConfig.TLSKeyFile != "":
x509Cert, err = tls.LoadX509KeyPair(tlsConfig.TLSCertFile, tlsConfig.TLSKeyFile)
default:
return nil, nil, fmt.Errorf("no cert or key provided")
}
var caBytes []byte
@@ -56,18 +80,25 @@ func LoadBaseTLSConfig(tlsConfig *shared.TLSConfigFile) (*tls.Config, *x509.Cert
caBytes = []byte(tlsConfig.TLSRootCA)
case tlsConfig.TLSRootCAFile != "":
caBytes, err = os.ReadFile(tlsConfig.TLSRootCAFile)
default:
return nil, nil, fmt.Errorf("no root CA provided")
}
ca := x509.NewCertPool()
var ca *x509.CertPool
if ok := ca.AppendCertsFromPEM(caBytes); !ok {
return nil, nil, fmt.Errorf("could not append root CA to cert pool: %w", err)
if len(caBytes) != 0 {
ca = x509.NewCertPool()
if ok := ca.AppendCertsFromPEM(caBytes); !ok {
return nil, nil, fmt.Errorf("could not append root CA to cert pool: %w", err)
}
}
return &tls.Config{
Certificates: []tls.Certificate{x509Cert},
MinVersion: tls.VersionTLS13,
}, ca, nil
res := &tls.Config{
MinVersion: tls.VersionTLS13,
}
if len(x509Cert.Certificate) != 0 {
res.Certificates = []tls.Certificate{x509Cert}
}
return res, ca, nil
}
+63
View File
@@ -8,8 +8,10 @@ import (
"golang.org/x/oauth2"
"github.com/hatchet-dev/hatchet/internal/auth/cookie"
"github.com/hatchet-dev/hatchet/internal/auth/token"
"github.com/hatchet-dev/hatchet/internal/config/database"
"github.com/hatchet-dev/hatchet/internal/config/shared"
"github.com/hatchet-dev/hatchet/internal/encryption"
"github.com/hatchet-dev/hatchet/internal/services/ingestor"
"github.com/hatchet-dev/hatchet/internal/taskqueue"
"github.com/hatchet-dev/hatchet/internal/validator"
@@ -18,6 +20,8 @@ import (
type ServerConfigFile struct {
Auth ConfigFileAuth `mapstructure:"auth" json:"auth,omitempty"`
Encryption EncryptionConfigFile `mapstructure:"encryption" json:"encryption,omitempty"`
Runtime ConfigFileRuntime `mapstructure:"runtime" json:"runtime,omitempty"`
TaskQueue TaskQueueConfigFile `mapstructure:"taskQueue" json:"taskQueue,omitempty"`
@@ -49,6 +53,49 @@ type ConfigFileRuntime struct {
GRPCInsecure bool `mapstructure:"grpcInsecure" json:"grpcInsecure,omitempty" default:"false"`
}
// Encryption options
type EncryptionConfigFile struct {
// MasterKeyset is the raw master keyset for the instance. This should be a base64-encoded JSON string. You must set
// either MasterKeyset, MasterKeysetFile or cloudKms.enabled with CloudKMS credentials
MasterKeyset string `mapstructure:"masterKeyset" json:"masterKeyset,omitempty"`
// MasterKeysetFile is the path to the master keyset file for the instance.
MasterKeysetFile string `mapstructure:"masterKeysetFile" json:"masterKeysetFile,omitempty"`
JWT EncryptionConfigFileJWT `mapstructure:"jwt" json:"jwt,omitempty"`
// CloudKMS is the configuration for Google Cloud KMS. You must set either MasterKeyset or cloudKms.enabled.
CloudKMS EncryptionConfigFileCloudKMS `mapstructure:"cloudKms" json:"cloudKms,omitempty"`
}
type EncryptionConfigFileJWT struct {
// PublicJWTKeyset is a base64-encoded JSON string containing the public keyset which has been encrypted
// by the master key.
PublicJWTKeyset string `mapstructure:"publicJWTKeyset" json:"publicJWTKeyset,omitempty"`
// PublicJWTKeysetFile is the path to the public keyset file for the instance.
PublicJWTKeysetFile string `mapstructure:"publicJWTKeysetFile" json:"publicJWTKeysetFile,omitempty"`
// PrivateJWTKeyset is a base64-encoded JSON string containing the private keyset which has been encrypted
// by the master key.
PrivateJWTKeyset string `mapstructure:"privateJWTKeyset" json:"privateJWTKeyset,omitempty"`
// PrivateJWTKeysetFile is the path to the private keyset file for the instance.
PrivateJWTKeysetFile string `mapstructure:"privateJWTKeysetFile" json:"privateJWTKeysetFile,omitempty"`
}
type EncryptionConfigFileCloudKMS struct {
// Enabled controls whether the Cloud KMS service is enabled for this Hatchet instance.
Enabled bool `mapstructure:"enabled" json:"enabled,omitempty" default:"false"`
// KeyURI is the URI of the key in Google Cloud KMS. This should be in the format of
// gcp-kms://...
KeyURI string `mapstructure:"keyURI" json:"keyURI,omitempty"`
// CredentialsJSON is the JSON credentials for the Google Cloud KMS service account.
CredentialsJSON string `mapstructure:"credentialsJSON" json:"credentialsJSON,omitempty"`
}
type ConfigFileAuth struct {
// RestrictedEmailDomains sets the restricted email domains for the instance.
RestrictedEmailDomains []string `mapstructure:"restrictedEmailDomains" json:"restrictedEmailDomains,omitempty"`
@@ -95,6 +142,8 @@ type AuthConfig struct {
ConfigFile ConfigFileAuth
GoogleOAuthConfig *oauth2.Config
JWTManager token.JWTManager
}
type ServerConfig struct {
@@ -102,6 +151,8 @@ type ServerConfig struct {
Auth AuthConfig
Encryption encryption.EncryptionService
Runtime ConfigFileRuntime
Services []string
@@ -142,6 +193,17 @@ func BindAllEnv(v *viper.Viper) {
_ = v.BindEnv("runtime.grpcInsecure", "SERVER_GRPC_INSECURE")
_ = v.BindEnv("services", "SERVER_SERVICES")
// encryption options
_ = v.BindEnv("encryption.masterKeyset", "SERVER_ENCRYPTION_MASTER_KEYSET")
_ = v.BindEnv("encryption.masterKeysetFile", "SERVER_ENCRYPTION_MASTER_KEYSET_FILE")
_ = v.BindEnv("encryption.jwt.publicJWTKeyset", "SERVER_ENCRYPTION_JWT_PUBLIC_KEYSET")
_ = v.BindEnv("encryption.jwt.publicJWTKeysetFile", "SERVER_ENCRYPTION_JWT_PUBLIC_KEYSET_FILE")
_ = v.BindEnv("encryption.jwt.privateJWTKeyset", "SERVER_ENCRYPTION_JWT_PRIVATE_KEYSET")
_ = v.BindEnv("encryption.jwt.privateJWTKeysetFile", "SERVER_ENCRYPTION_JWT_PRIVATE_KEYSET_FILE")
_ = v.BindEnv("encryption.cloudKms.enabled", "SERVER_ENCRYPTION_CLOUDKMS_ENABLED")
_ = v.BindEnv("encryption.cloudKms.keyURI", "SERVER_ENCRYPTION_CLOUDKMS_KEY_URI")
_ = v.BindEnv("encryption.cloudKms.credentialsJSON", "SERVER_ENCRYPTION_CLOUDKMS_CREDENTIALS_JSON")
// auth options
_ = v.BindEnv("auth.restrictedEmailDomains", "SERVER_AUTH_RESTRICTED_EMAIL_DOMAINS")
_ = v.BindEnv("auth.basicAuthEnabled", "SERVER_AUTH_BASIC_AUTH_ENABLED")
@@ -160,6 +222,7 @@ func BindAllEnv(v *viper.Viper) {
_ = v.BindEnv("taskQueue.rabbitmq.url", "SERVER_TASKQUEUE_RABBITMQ_URL")
// tls options
_ = v.BindEnv("tls.tlsStrategy", "SERVER_TLS_STRATEGY")
_ = v.BindEnv("tls.tlsCert", "SERVER_TLS_CERT")
_ = v.BindEnv("tls.tlsCertFile", "SERVER_TLS_CERT_FILE")
_ = v.BindEnv("tls.tlsKey", "SERVER_TLS_KEY")
+3
View File
@@ -1,6 +1,9 @@
package shared
type TLSConfigFile struct {
// TLSStrategy can be "tls" or "mtls"
TLSStrategy string `mapstructure:"tlsStrategy" json:"tlsStrategy,omitempty" default:"tls"`
TLSCert string `mapstructure:"tlsCert" json:"tlsCert,omitempty"`
TLSCertFile string `mapstructure:"tlsCertFile" json:"tlsCertFile,omitempty"`
TLSKey string `mapstructure:"tlsKey" json:"tlsKey,omitempty"`
-70
View File
@@ -1,70 +0,0 @@
package encryption
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"errors"
"io"
)
// This file is copied from: https://github.com/gtank/cryptopasta
// NewEncryptionKey generates a random 256-bit key for Encrypt() and
// Decrypt(). It panics if the source of randomness fails.
func NewEncryptionKey() *[32]byte {
key := [32]byte{}
_, err := io.ReadFull(rand.Reader, key[:])
if err != nil {
panic(err)
}
return &key
}
// Encrypt encrypts data using 256-bit AES-GCM. This both hides the content of
// the data and provides a check that it hasn't been altered. Output takes the
// form nonce|ciphertext|tag where '|' indicates concatenation.
func Encrypt(plaintext []byte, key *[32]byte) (ciphertext []byte, err error) {
block, err := aes.NewCipher(key[:])
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonce := make([]byte, gcm.NonceSize())
_, err = io.ReadFull(rand.Reader, nonce)
if err != nil {
return nil, err
}
return gcm.Seal(nonce, nonce, plaintext, nil), nil
}
// Decrypt decrypts data using 256-bit AES-GCM. This both hides the content of
// the data and provides a check that it hasn't been altered. Expects input
// form nonce|ciphertext|tag where '|' indicates concatenation.
func Decrypt(ciphertext []byte, key *[32]byte) (plaintext []byte, err error) {
block, err := aes.NewCipher(key[:])
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
if len(ciphertext) < gcm.NonceSize() {
return nil, errors.New("malformed ciphertext")
}
return gcm.Open(nil,
ciphertext[:gcm.NonceSize()],
ciphertext[gcm.NonceSize():],
nil,
)
}
+109
View File
@@ -0,0 +1,109 @@
package encryption
import (
"context"
"fmt"
"github.com/tink-crypto/tink-go-gcpkms/integration/gcpkms"
"github.com/tink-crypto/tink-go/aead"
"github.com/tink-crypto/tink-go/core/registry"
"github.com/tink-crypto/tink-go/keyset"
"google.golang.org/api/option"
)
type cloudkmsEncryptionService struct {
key *aead.KMSEnvelopeAEAD
privateEc256Handle *keyset.Handle
publicEc256Handle *keyset.Handle
}
// NewCloudKMSEncryption creates a GCP CloudKMS-backed encryption service.
func NewCloudKMSEncryption(keyUri string, credentialsJSON, privateEc256, publicEc256 []byte) (*cloudkmsEncryptionService, error) {
client, err := gcpkms.NewClientWithOptions(context.Background(), keyUri, option.WithCredentialsJSON(credentialsJSON))
if err != nil {
return nil, err
}
return newWithClient(client, keyUri, privateEc256, publicEc256)
}
func GenerateJWTKeysetsFromCloudKMS(keyUri string, credentialsJSON []byte) (privateEc256 []byte, publicEc256 []byte, err error) {
client, err := gcpkms.NewClientWithOptions(context.Background(), keyUri, option.WithCredentialsJSON(credentialsJSON))
if err != nil {
return nil, nil, err
}
return generateJWTKeysetsWithClient(keyUri, client)
}
func generateJWTKeysetsWithClient(keyUri string, client registry.KMSClient) (privateEc256 []byte, publicEc256 []byte, err error) {
registry.RegisterKMSClient(client)
remote, err := client.GetAEAD(keyUri)
if err != nil {
return nil, nil, err
}
return generateJWTKeysets(remote)
}
func newWithClient(client registry.KMSClient, keyUri string, privateEc256, publicEc256 []byte) (*cloudkmsEncryptionService, error) {
registry.RegisterKMSClient(client)
dek := aead.AES128CTRHMACSHA256KeyTemplate()
template, err := aead.CreateKMSEnvelopeAEADKeyTemplate(keyUri, dek)
if err != nil {
return nil, err
}
// get the remote KEK from the client
remote, err := client.GetAEAD(keyUri)
if err != nil {
return nil, err
}
envelope := aead.NewKMSEnvelopeAEAD2(template, remote)
if envelope == nil {
return nil, fmt.Errorf("failed to create envelope")
}
privateEc256Handle, err := handleFromBytes(privateEc256, remote)
if err != nil {
return nil, err
}
publicEc256Handle, err := handleFromBytes(publicEc256, remote)
if err != nil {
return nil, err
}
return &cloudkmsEncryptionService{
key: envelope,
privateEc256Handle: privateEc256Handle,
publicEc256Handle: publicEc256Handle,
}, nil
}
func (svc *cloudkmsEncryptionService) Encrypt(plaintext []byte, dataId string) ([]byte, error) {
return encrypt(svc.key, plaintext, dataId)
}
func (svc *cloudkmsEncryptionService) Decrypt(ciphertext []byte, dataId string) ([]byte, error) {
return decrypt(svc.key, ciphertext, dataId)
}
func (svc *cloudkmsEncryptionService) GetPrivateJWTHandle() *keyset.Handle {
return svc.privateEc256Handle
}
func (svc *cloudkmsEncryptionService) GetPublicJWTHandle() *keyset.Handle {
return svc.publicEc256Handle
}
+104
View File
@@ -0,0 +1,104 @@
package encryption
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/tink-crypto/tink-go/testing/fakekms"
)
var (
fakeKeyURI = "fake-kms://CM2b3_MDElQKSAowdHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuY3J5cHRvLnRpbmsuQWVzR2NtS2V5EhIaEIK75t5L-adlUwVhWvRuWUwYARABGM2b3_MDIAE"
fakeCredentialsJSON = []byte(`{}`)
)
func TestNewCloudKMSEncryptionValid(t *testing.T) {
// Using fake KMS client for testing
client, err := fakekms.NewClient(fakeKeyURI)
assert.NoError(t, err)
// generate JWT keysets
privateEc256, publicEc256, err := generateJWTKeysetsWithClient(fakeKeyURI, client)
if err != nil {
t.Fatal(err)
}
// Create encryption service with valid key URI and credentials
svc, err := newWithClient(client, fakeKeyURI, privateEc256, publicEc256)
assert.NoError(t, err)
assert.NotNil(t, svc)
}
func TestNewCloudKMSEncryptionInvalidKeyUri(t *testing.T) {
// Create encryption service with invalid key URI
_, err := NewCloudKMSEncryption("invalid-key-uri", fakeCredentialsJSON, nil, nil)
assert.Error(t, err)
}
func TestNewCloudKMSEncryptionInvalidCredentials(t *testing.T) {
// Create encryption service with invalid credentials
_, err := NewCloudKMSEncryption(fakeKeyURI, []byte("invalid credentials"), nil, nil)
assert.Error(t, err)
}
func TestEncryptDecryptCloudKMS(t *testing.T) {
// Using fake KMS client for testing
client, err := fakekms.NewClient(fakeKeyURI)
assert.NoError(t, err)
// generate JWT keysets
privateEc256, publicEc256, err := generateJWTKeysetsWithClient(fakeKeyURI, client)
if err != nil {
t.Fatal(err)
}
// Create encryption service with valid key URI and credentials
svc, err := newWithClient(client, fakeKeyURI, privateEc256, publicEc256)
if err != nil {
t.Fatal(err)
}
plaintext := []byte("test message")
dataID := "123"
// Encrypt
ciphertext, err := svc.Encrypt(plaintext, dataID)
assert.NoError(t, err)
// Decrypt
decryptedText, err := svc.Decrypt(ciphertext, dataID)
assert.NoError(t, err)
// Check if decrypted text matches original plaintext
assert.Equal(t, plaintext, decryptedText)
}
func TestEncryptDecryptCloudKMSWithEmptyDataID(t *testing.T) {
// Using fake KMS client for testing
client, err := fakekms.NewClient(fakeKeyURI)
assert.NoError(t, err)
// generate JWT keysets
privateEc256, publicEc256, err := generateJWTKeysetsWithClient(fakeKeyURI, client)
if err != nil {
t.Fatal(err)
}
// Create encryption service with valid key URI and credentials
svc, err := newWithClient(client, fakeKeyURI, privateEc256, publicEc256)
if err != nil {
t.Fatal(err)
}
plaintext := []byte("test message")
emptyDataID := ""
// Encrypt with empty data ID
_, err = svc.Encrypt(plaintext, emptyDataID)
assert.Error(t, err)
}
+31
View File
@@ -0,0 +1,31 @@
package encryption
import (
"fmt"
"github.com/tink-crypto/tink-go/tink"
)
func encrypt(key tink.AEAD, plaintext []byte, dataId string) ([]byte, error) {
// validate data id is not empty
if len(dataId) == 0 {
return nil, fmt.Errorf("data id cannot be empty")
}
associatedData := []byte(dataId)
// encrypt the data
return key.Encrypt(plaintext, associatedData)
}
func decrypt(key tink.AEAD, ciphertext []byte, dataId string) ([]byte, error) {
// validate data id is not empty
if len(dataId) == 0 {
return nil, fmt.Errorf("data id cannot be empty")
}
associatedData := []byte(dataId)
// decrypt the data
return key.Decrypt(ciphertext, associatedData)
}
+221
View File
@@ -0,0 +1,221 @@
package encryption
import (
"bytes"
"encoding/base64"
"fmt"
"github.com/tink-crypto/tink-go/aead"
"github.com/tink-crypto/tink-go/insecurecleartextkeyset"
"github.com/tink-crypto/tink-go/jwt"
"github.com/tink-crypto/tink-go/keyset"
"github.com/tink-crypto/tink-go/tink"
)
type localEncryptionService struct {
key *aead.KMSEnvelopeAEAD
privateEc256Handle *keyset.Handle
publicEc256Handle *keyset.Handle
}
// NewLocalEncryption creates a new local encryption service. keysetBytes is the raw keyset in
// base64-encoded JSON format. This can be generated by calling hatchet-admin keyset create-local.
func NewLocalEncryption(masterKey []byte, privateEc256 []byte, publicEc256 []byte) (*localEncryptionService, error) {
// get the master keyset handle
aes256GcmHandle, err := insecureHandleFromBytes(masterKey)
if err != nil {
return nil, err
}
a, err := aead.New(aes256GcmHandle)
if err != nil {
return nil, err
}
privateEc256Handle, err := handleFromBytes(privateEc256, a)
if err != nil {
return nil, err
}
publicEc256Handle, err := handleFromBytes(publicEc256, a)
if err != nil {
return nil, err
}
envelope := aead.NewKMSEnvelopeAEAD2(aead.AES128GCMKeyTemplate(), a)
if envelope == nil {
return nil, fmt.Errorf("failed to create envelope")
}
return &localEncryptionService{
key: envelope,
privateEc256Handle: privateEc256Handle,
publicEc256Handle: publicEc256Handle,
}, nil
}
func GenerateLocalKeys() (masterKey []byte, privateEc256 []byte, publicEc256 []byte, err error) {
masterKey, masterHandle, err := generateLocalMasterKey()
if err != nil {
return nil, nil, nil, err
}
a, err := aead.New(masterHandle)
if err != nil {
return nil, nil, nil, err
}
privateEc256, publicEc256, err = generateJWTKeysets(a)
if err != nil {
return nil, nil, nil, err
}
return masterKey, privateEc256, publicEc256, nil
}
func generateLocalMasterKey() ([]byte, *keyset.Handle, error) {
aeadTemplate := aead.AES256GCMKeyTemplate()
aes256GcmHandle, err := keyset.NewHandle(aeadTemplate)
if err != nil {
return nil, nil, fmt.Errorf("failed to create new keyset handle with AES256GCM template: %w", err)
}
bytes, err := insecureBytesFromHandle(aes256GcmHandle)
if err != nil {
return nil, nil, fmt.Errorf("failed to get bytes from handle: %w", err)
}
return bytes, aes256GcmHandle, nil
}
// generateJWTKeysets creates the keysets for JWT signing and verification encrypted with the
// masterKey. The masterKey can be from a remote KMS service or a local keyset.
func generateJWTKeysets(masterKey tink.AEAD) (privateEc256 []byte, publicEc256 []byte, err error) {
privateHandle, err := keyset.NewHandle(jwt.ES256Template())
if err != nil {
err = fmt.Errorf("failed to create new keyset handle with ES256 template: %w", err)
return
}
privateEc256, err = bytesFromHandle(privateHandle, masterKey)
if err != nil {
return
}
publicHandle, err := privateHandle.Public()
if err != nil {
err = fmt.Errorf("failed to get public keyset: %w", err)
return
}
publicEc256, err = bytesFromHandle(publicHandle, masterKey)
if err != nil {
return
}
return
}
// bytesFromHandle returns the encrypted keyset in base64-encoded JSON format, encrypted with the
// masterKey
func bytesFromHandle(kh *keyset.Handle, masterKey tink.AEAD) ([]byte, error) {
buf := new(bytes.Buffer)
writer := keyset.NewJSONWriter(buf)
err := kh.Write(writer, masterKey)
if err != nil {
return nil, fmt.Errorf("failed to write keyset: %w", err)
}
// base64-encode bytes
keysetBytes := make([]byte, base64.RawStdEncoding.EncodedLen(len(buf.Bytes())))
base64.RawStdEncoding.Encode(keysetBytes, buf.Bytes())
return keysetBytes, nil
}
// insecureBytesFromHandle returns the raw (unencrypted) keyset in base64-encoded JSON format.
func insecureBytesFromHandle(kh *keyset.Handle) ([]byte, error) {
buf := new(bytes.Buffer)
writer := keyset.NewJSONWriter(buf)
err := insecurecleartextkeyset.Write(kh, writer)
if err != nil {
return nil, fmt.Errorf("failed to write keyset: %w", err)
}
// base64-encode bytes
keysetBytes := make([]byte, base64.RawStdEncoding.EncodedLen(len(buf.Bytes())))
base64.RawStdEncoding.Encode(keysetBytes, buf.Bytes())
return keysetBytes, nil
}
func handleFromBytes(keysetBytes []byte, masterKey tink.AEAD) (*keyset.Handle, error) {
// base64-decode bytes
keysetJsonBytes := make([]byte, base64.RawStdEncoding.DecodedLen(len(keysetBytes)))
_, err := base64.RawStdEncoding.Decode(keysetJsonBytes, keysetBytes)
if err != nil {
return nil, fmt.Errorf("failed to decode keyset bytes: %w", err)
}
// read keyset
handle, err := keyset.Read(keyset.NewJSONReader(bytes.NewReader(keysetJsonBytes)), masterKey)
if err != nil {
return nil, fmt.Errorf("failed to read keyset: %w", err)
}
return handle, nil
}
func insecureHandleFromBytes(keysetBytes []byte) (*keyset.Handle, error) {
// base64-decode bytes
keysetJsonBytes := make([]byte, base64.RawStdEncoding.DecodedLen(len(keysetBytes)))
_, err := base64.RawStdEncoding.Decode(keysetJsonBytes, keysetBytes)
if err != nil {
return nil, fmt.Errorf("failed to decode keyset bytes: %w", err)
}
// read keyset
handle, err := insecurecleartextkeyset.Read(keyset.NewJSONReader(bytes.NewReader(keysetJsonBytes)))
if err != nil {
return nil, fmt.Errorf("failed to read keyset: %w", err)
}
return handle, nil
}
func (svc *localEncryptionService) Encrypt(plaintext []byte, dataId string) ([]byte, error) {
return encrypt(svc.key, plaintext, dataId)
}
func (svc *localEncryptionService) Decrypt(ciphertext []byte, dataId string) ([]byte, error) {
return decrypt(svc.key, ciphertext, dataId)
}
func (svc *localEncryptionService) GetPrivateJWTHandle() *keyset.Handle {
return svc.privateEc256Handle
}
func (svc *localEncryptionService) GetPublicJWTHandle() *keyset.Handle {
return svc.publicEc256Handle
}
+79
View File
@@ -0,0 +1,79 @@
package encryption
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewLocalEncryptionValidKeyset(t *testing.T) {
// Generate a new keyset
aes256Gcm, privateEc256, publicEc256, err := GenerateLocalKeys()
assert.NoError(t, err)
// Create encryption service
_, err = NewLocalEncryption(aes256Gcm, privateEc256, publicEc256)
assert.NoError(t, err)
}
func TestNewLocalEncryptionInvalidKeyset(t *testing.T) {
invalidKeysetBytes := []byte("invalid keyset")
// Create encryption service with invalid keyset
_, err := NewLocalEncryption(invalidKeysetBytes, invalidKeysetBytes, invalidKeysetBytes)
assert.Error(t, err)
}
func TestEncryptDecrypt(t *testing.T) {
aes256Gcm, privateEc256, publicEc256, _ := GenerateLocalKeys()
svc, _ := NewLocalEncryption(aes256Gcm, privateEc256, publicEc256)
plaintext := []byte("test message")
dataID := "123"
// Encrypt
ciphertext, err := svc.Encrypt(plaintext, dataID)
assert.NoError(t, err)
// Decrypt
decryptedText, err := svc.Decrypt(ciphertext, dataID)
assert.NoError(t, err)
// Check if decrypted text matches original plaintext
assert.Equal(t, plaintext, decryptedText)
}
func TestDecryptWithInvalidKey(t *testing.T) {
aes256Gcm, privateEc256, publicEc256, _ := GenerateLocalKeys()
svc, _ := NewLocalEncryption(aes256Gcm, privateEc256, publicEc256)
plaintext := []byte("test message")
dataID := "123"
// Encrypt
ciphertext, _ := svc.Encrypt(plaintext, dataID)
// Generate a new keyset for decryption
aes256Gcm, privateEc256, publicEc256, _ = GenerateLocalKeys()
newSvc, _ := NewLocalEncryption(aes256Gcm, privateEc256, publicEc256)
// Attempt to decrypt with a different key
_, err := newSvc.Decrypt(ciphertext, dataID)
assert.Error(t, err)
}
func TestEncryptDecryptWithEmptyDataID(t *testing.T) {
aes256Gcm, privateEc256, publicEc256, _ := GenerateLocalKeys()
svc, _ := NewLocalEncryption(aes256Gcm, privateEc256, publicEc256)
plaintext := []byte("test message")
emptyDataID := ""
// Encrypt with empty data ID
_, err := svc.Encrypt(plaintext, emptyDataID)
assert.Error(t, err)
// Decrypt with empty data ID
_, err = svc.Decrypt(plaintext, emptyDataID)
assert.Error(t, err)
}
+21
View File
@@ -0,0 +1,21 @@
package encryption
import "github.com/tink-crypto/tink-go/keyset"
type EncryptionService interface {
// Encrypt encrypts the given plaintext with the given data id. The data id is used to
// associate the ciphertext with the data in the database.
// For more information, see: https://developers.google.com/tink/client-side-encryption#kms_envelope_aead
Encrypt(plaintext []byte, dataId string) ([]byte, error)
// Decrypt decrypts the given ciphertext with the given data id. The data id is used to
// associate the ciphertext with the data in the database.
// For more information, see: https://developers.google.com/tink/client-side-encryption#kms_envelope_aead
Decrypt(ciphertext []byte, dataId string) ([]byte, error)
// GetPrivateJWTHandle returns a private JWT handle. This is used to sign JWTs.
GetPrivateJWTHandle() *keyset.Handle
// GetPublicJWTHandle returns a public JWT handle. This is used to verify JWTs.
GetPublicJWTHandle() *keyset.Handle
}
+28
View File
@@ -0,0 +1,28 @@
package repository
import (
"time"
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
)
type CreateAPITokenOpts struct {
// The id of the token
ID string `validate:"required,uuid"`
// When the token expires
ExpiresAt time.Time
// (optional) A tenant ID for this API token
TenantId *string `validate:"omitempty,uuid"`
// (optional) A name for this API token
Name *string `validate:"omitempty,max=255"`
}
type APITokenRepository interface {
GetAPITokenById(id string) (*db.APITokenModel, error)
CreateAPIToken(opts *CreateAPITokenOpts) (*db.APITokenModel, error)
RevokeAPIToken(id string) error
ListAPITokensByTenant(tenantId string) ([]db.APITokenModel, error)
}
+71
View File
@@ -0,0 +1,71 @@
package prisma
import (
"context"
"time"
"github.com/hatchet-dev/hatchet/internal/repository"
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
"github.com/hatchet-dev/hatchet/internal/validator"
)
type apiTokenRepository struct {
client *db.PrismaClient
v validator.Validator
}
func NewAPITokenRepository(client *db.PrismaClient, v validator.Validator) repository.APITokenRepository {
return &apiTokenRepository{
client: client,
v: v,
}
}
func (a *apiTokenRepository) GetAPITokenById(id string) (*db.APITokenModel, error) {
return a.client.APIToken.FindUnique(
db.APIToken.ID.Equals(id),
).Exec(context.Background())
}
func (a *apiTokenRepository) CreateAPIToken(opts *repository.CreateAPITokenOpts) (*db.APITokenModel, error) {
if err := a.v.Validate(opts); err != nil {
return nil, err
}
optionals := []db.APITokenSetParam{
db.APIToken.ID.Set(opts.ID),
db.APIToken.ExpiresAt.Set(opts.ExpiresAt),
}
if opts.TenantId != nil {
optionals = append(optionals, db.APIToken.Tenant.Link(
db.Tenant.ID.Equals(*opts.TenantId),
))
}
if opts.Name != nil {
optionals = append(optionals, db.APIToken.Name.Set(*opts.Name))
}
return a.client.APIToken.CreateOne(
optionals...,
).Exec(context.Background())
}
func (a *apiTokenRepository) RevokeAPIToken(id string) error {
_, err := a.client.APIToken.FindUnique(
db.APIToken.ID.Equals(id),
).Update(
db.APIToken.ExpiresAt.Set(time.Now().Add(-1*time.Second)),
db.APIToken.Revoked.Set(true),
).Exec(context.Background())
return err
}
func (a *apiTokenRepository) ListAPITokensByTenant(tenantId string) ([]db.APITokenModel, error) {
return a.client.APIToken.FindMany(
db.APIToken.TenantID.Equals(tenantId),
db.APIToken.Revoked.Equals(false),
).Exec(context.Background())
}
+15 -4
View File
@@ -275,14 +275,25 @@ func (ns NullWorkflowRunStatus) Value() (driver.Value, error) {
return string(ns.WorkflowRunStatus), nil
}
type APIToken struct {
ID pgtype.UUID `json:"id"`
CreatedAt pgtype.Timestamp `json:"createdAt"`
UpdatedAt pgtype.Timestamp `json:"updatedAt"`
ExpiresAt pgtype.Timestamp `json:"expiresAt"`
Revoked bool `json:"revoked"`
Name pgtype.Text `json:"name"`
TenantId pgtype.UUID `json:"tenantId"`
}
type Action struct {
ID string `json:"id"`
ID pgtype.UUID `json:"id"`
ActionId string `json:"actionId"`
Description pgtype.Text `json:"description"`
TenantId pgtype.UUID `json:"tenantId"`
}
type ActionToWorker struct {
A string `json:"A"`
A pgtype.UUID `json:"A"`
B pgtype.UUID `json:"B"`
}
@@ -464,8 +475,8 @@ type UserOAuth struct {
UserId pgtype.UUID `json:"userId"`
Provider string `json:"provider"`
ProviderUserId string `json:"providerUserId"`
AccessToken string `json:"accessToken"`
RefreshToken pgtype.Text `json:"refreshToken"`
AccessToken []byte `json:"accessToken"`
RefreshToken []byte `json:"refreshToken"`
ExpiresAt pgtype.Timestamp `json:"expiresAt"`
}
+29 -6
View File
@@ -16,9 +16,23 @@ CREATE TYPE "WorkerStatus" AS ENUM ('ACTIVE', 'INACTIVE');
-- CreateEnum
CREATE TYPE "WorkflowRunStatus" AS ENUM ('PENDING', 'RUNNING', 'SUCCEEDED', 'FAILED');
-- CreateTable
CREATE TABLE "APIToken" (
"id" UUID NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"expiresAt" TIMESTAMP(3),
"revoked" BOOLEAN NOT NULL DEFAULT false,
"name" TEXT,
"tenantId" UUID,
CONSTRAINT "APIToken_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Action" (
"id" TEXT NOT NULL,
"id" UUID NOT NULL,
"actionId" TEXT NOT NULL,
"description" TEXT,
"tenantId" UUID NOT NULL,
@@ -228,8 +242,8 @@ CREATE TABLE "UserOAuth" (
"userId" UUID NOT NULL,
"provider" TEXT NOT NULL,
"providerUserId" TEXT NOT NULL,
"accessToken" TEXT NOT NULL,
"refreshToken" TEXT,
"accessToken" BYTEA NOT NULL,
"refreshToken" BYTEA,
"expiresAt" TIMESTAMP(3),
CONSTRAINT "UserOAuth_pkey" PRIMARY KEY ("id")
@@ -376,7 +390,7 @@ CREATE TABLE "WorkflowVersion" (
-- CreateTable
CREATE TABLE "_ActionToWorker" (
"A" TEXT NOT NULL,
"A" UUID NOT NULL,
"B" UUID NOT NULL
);
@@ -405,7 +419,13 @@ CREATE TABLE "_WorkflowToWorkflowTag" (
);
-- CreateIndex
CREATE UNIQUE INDEX "Action_tenantId_id_key" ON "Action"("tenantId" ASC, "id" ASC);
CREATE UNIQUE INDEX "APIToken_id_key" ON "APIToken"("id" ASC);
-- CreateIndex
CREATE UNIQUE INDEX "Action_id_key" ON "Action"("id" ASC);
-- CreateIndex
CREATE UNIQUE INDEX "Action_tenantId_actionId_key" ON "Action"("tenantId" ASC, "actionId" ASC);
-- CreateIndex
CREATE UNIQUE INDEX "Dispatcher_id_key" ON "Dispatcher"("id" ASC);
@@ -566,6 +586,9 @@ CREATE UNIQUE INDEX "_WorkflowToWorkflowTag_AB_unique" ON "_WorkflowToWorkflowTa
-- CreateIndex
CREATE INDEX "_WorkflowToWorkflowTag_B_index" ON "_WorkflowToWorkflowTag"("B" ASC);
-- AddForeignKey
ALTER TABLE "APIToken" ADD CONSTRAINT "APIToken_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Action" ADD CONSTRAINT "Action_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -603,7 +626,7 @@ ALTER TABLE "JobRunLookupData" ADD CONSTRAINT "JobRunLookupData_tenantId_fkey" F
ALTER TABLE "Service" ADD CONSTRAINT "Service_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Step" ADD CONSTRAINT "Step_actionId_tenantId_fkey" FOREIGN KEY ("actionId", "tenantId") REFERENCES "Action"("id", "tenantId") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "Step" ADD CONSTRAINT "Step_actionId_tenantId_fkey" FOREIGN KEY ("actionId", "tenantId") REFERENCES "Action"("actionId", "tenantId") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Step" ADD CONSTRAINT "Step_jobId_fkey" FOREIGN KEY ("jobId") REFERENCES "Job"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -222,17 +222,19 @@ JOIN
-- name: UpsertAction :exec
INSERT INTO "Action" (
"id",
"actionId",
"tenantId"
)
VALUES (
gen_random_uuid(),
@action::text,
@tenantId::uuid
)
ON CONFLICT ("tenantId", "id") DO UPDATE
ON CONFLICT ("tenantId", "actionId") DO UPDATE
SET
"tenantId" = EXCLUDED."tenantId"
WHERE
"Action"."tenantId" = @tenantId AND "Action"."id" = @action;
"Action"."tenantId" = @tenantId AND "Action"."actionId" = @action::text;
-- name: UpsertWorkflowTag :exec
INSERT INTO "WorkflowTag" (
@@ -628,17 +628,19 @@ func (q *Queries) ListWorkflowsLatestRuns(ctx context.Context, db DBTX, arg List
const upsertAction = `-- name: UpsertAction :exec
INSERT INTO "Action" (
"id",
"actionId",
"tenantId"
)
VALUES (
gen_random_uuid(),
$1::text,
$2::uuid
)
ON CONFLICT ("tenantId", "id") DO UPDATE
ON CONFLICT ("tenantId", "actionId") DO UPDATE
SET
"tenantId" = EXCLUDED."tenantId"
WHERE
"Action"."tenantId" = $2 AND "Action"."id" = $1
"Action"."tenantId" = $2 AND "Action"."actionId" = $1::text
`
type UpsertActionParams struct {
+6
View File
@@ -10,6 +10,7 @@ import (
)
type prismaRepository struct {
apiToken repository.APITokenRepository
event repository.EventRepository
tenant repository.TenantRepository
tenantInvite repository.TenantInviteRepository
@@ -61,6 +62,7 @@ func NewPrismaRepository(client *db.PrismaClient, pool *pgxpool.Pool, fs ...Pris
opts.l = &newLogger
return &prismaRepository{
apiToken: NewAPITokenRepository(client, opts.v),
event: NewEventRepository(client, pool, opts.v, opts.l),
tenant: NewTenantRepository(client, opts.v),
tenantInvite: NewTenantInviteRepository(client, opts.v),
@@ -77,6 +79,10 @@ func NewPrismaRepository(client *db.PrismaClient, pool *pgxpool.Pool, fs ...Pris
}
}
func (r *prismaRepository) APIToken() repository.APITokenRepository {
return r.apiToken
}
func (r *prismaRepository) Event() repository.EventRepository {
return r.event
}
+11 -11
View File
@@ -68,7 +68,7 @@ func (w *workerRepository) ListWorkers(tenantId string, opts *repository.ListWor
if opts.Action != nil {
queryParams = append(queryParams, db.Worker.Actions.Some(
db.Action.TenantID.Equals(tenantId),
db.Action.ID.Equals(*opts.Action),
db.Action.ActionID.Equals(*opts.Action),
))
}
@@ -203,12 +203,12 @@ func (w *workerRepository) CreateNewWorker(tenantId string, opts *repository.Cre
if len(opts.Actions) > 0 {
for _, action := range opts.Actions {
txs = append(txs, w.client.Action.UpsertOne(
db.Action.TenantIDID(
db.Action.TenantIDActionID(
db.Action.TenantID.Equals(tenantId),
db.Action.ID.Equals(action),
db.Action.ActionID.Equals(action),
),
).Create(
db.Action.ID.Set(action),
db.Action.ActionID.Set(action),
db.Action.Tenant.Link(
db.Tenant.ID.Equals(tenantId),
),
@@ -221,9 +221,9 @@ func (w *workerRepository) CreateNewWorker(tenantId string, opts *repository.Cre
db.Worker.ID.Equals(workerId),
).Update(
db.Worker.Actions.Link(
db.Action.TenantIDID(
db.Action.TenantIDActionID(
db.Action.TenantID.Equals(tenantId),
db.Action.ID.Equals(action),
db.Action.ActionID.Equals(action),
),
),
).Tx())
@@ -265,12 +265,12 @@ func (w *workerRepository) UpdateWorker(tenantId, workerId string, opts *reposit
if len(opts.Actions) > 0 {
for _, action := range opts.Actions {
txs = append(txs, w.client.Action.UpsertOne(
db.Action.TenantIDID(
db.Action.TenantIDActionID(
db.Action.TenantID.Equals(tenantId),
db.Action.ID.Equals(action),
db.Action.ActionID.Equals(action),
),
).Create(
db.Action.ID.Set(action),
db.Action.ActionID.Set(action),
db.Action.Tenant.Link(
db.Tenant.ID.Equals(tenantId),
),
@@ -283,9 +283,9 @@ func (w *workerRepository) UpdateWorker(tenantId, workerId string, opts *reposit
db.Worker.ID.Equals(workerId),
).Update(
db.Worker.Actions.Link(
db.Action.TenantIDID(
db.Action.TenantIDActionID(
db.Action.TenantID.Equals(tenantId),
db.Action.ID.Equals(action),
db.Action.ActionID.Equals(action),
),
),
).Tx())
+1
View File
@@ -1,6 +1,7 @@
package repository
type Repository interface {
APIToken() APITokenRepository
Event() EventRepository
Tenant() TenantRepository
TenantInvite() TenantInviteRepository
+2 -2
View File
@@ -22,8 +22,8 @@ type CreateUserOpts struct {
type OAuthOpts struct {
Provider string `validate:"required,oneof=google"`
ProviderUserId string `validate:"required,min=1"`
AccessToken string `validate:"required,min=1"`
RefreshToken *string // optional
AccessToken []byte `validate:"required,min=1"`
RefreshToken *[]byte // optional
ExpiresAt *time.Time // optional
}
+210 -269
View File
@@ -27,8 +27,7 @@ type PutWorkflowRequest struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
TenantId string `protobuf:"bytes,1,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"`
Opts *CreateWorkflowVersionOpts `protobuf:"bytes,2,opt,name=opts,proto3" json:"opts,omitempty"`
Opts *CreateWorkflowVersionOpts `protobuf:"bytes,1,opt,name=opts,proto3" json:"opts,omitempty"`
}
func (x *PutWorkflowRequest) Reset() {
@@ -63,13 +62,6 @@ func (*PutWorkflowRequest) Descriptor() ([]byte, []int) {
return file_workflows_proto_rawDescGZIP(), []int{0}
}
func (x *PutWorkflowRequest) GetTenantId() string {
if x != nil {
return x.TenantId
}
return ""
}
func (x *PutWorkflowRequest) GetOpts() *CreateWorkflowVersionOpts {
if x != nil {
return x.Opts
@@ -330,8 +322,6 @@ type ListWorkflowsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
TenantId string `protobuf:"bytes,1,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"`
}
func (x *ListWorkflowsRequest) Reset() {
@@ -366,23 +356,15 @@ func (*ListWorkflowsRequest) Descriptor() ([]byte, []int) {
return file_workflows_proto_rawDescGZIP(), []int{4}
}
func (x *ListWorkflowsRequest) GetTenantId() string {
if x != nil {
return x.TenantId
}
return ""
}
type ScheduleWorkflowRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
TenantId string `protobuf:"bytes,1,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"`
WorkflowId string `protobuf:"bytes,2,opt,name=workflow_id,json=workflowId,proto3" json:"workflow_id,omitempty"`
Schedules []*timestamppb.Timestamp `protobuf:"bytes,3,rep,name=schedules,proto3" json:"schedules,omitempty"`
WorkflowId string `protobuf:"bytes,1,opt,name=workflow_id,json=workflowId,proto3" json:"workflow_id,omitempty"`
Schedules []*timestamppb.Timestamp `protobuf:"bytes,2,rep,name=schedules,proto3" json:"schedules,omitempty"`
// (optional) the input data for the workflow
Input string `protobuf:"bytes,4,opt,name=input,proto3" json:"input,omitempty"`
Input string `protobuf:"bytes,3,opt,name=input,proto3" json:"input,omitempty"`
}
func (x *ScheduleWorkflowRequest) Reset() {
@@ -417,13 +399,6 @@ func (*ScheduleWorkflowRequest) Descriptor() ([]byte, []int) {
return file_workflows_proto_rawDescGZIP(), []int{5}
}
func (x *ScheduleWorkflowRequest) GetTenantId() string {
if x != nil {
return x.TenantId
}
return ""
}
func (x *ScheduleWorkflowRequest) GetWorkflowId() string {
if x != nil {
return x.WorkflowId
@@ -499,8 +474,7 @@ type ListWorkflowsForEventRequest struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
TenantId string `protobuf:"bytes,1,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"`
EventKey string `protobuf:"bytes,2,opt,name=event_key,json=eventKey,proto3" json:"event_key,omitempty"`
EventKey string `protobuf:"bytes,1,opt,name=event_key,json=eventKey,proto3" json:"event_key,omitempty"`
}
func (x *ListWorkflowsForEventRequest) Reset() {
@@ -535,13 +509,6 @@ func (*ListWorkflowsForEventRequest) Descriptor() ([]byte, []int) {
return file_workflows_proto_rawDescGZIP(), []int{7}
}
func (x *ListWorkflowsForEventRequest) GetTenantId() string {
if x != nil {
return x.TenantId
}
return ""
}
func (x *ListWorkflowsForEventRequest) GetEventKey() string {
if x != nil {
return x.EventKey
@@ -1194,8 +1161,7 @@ type DeleteWorkflowRequest struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
TenantId string `protobuf:"bytes,1,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"`
WorkflowId string `protobuf:"bytes,2,opt,name=workflow_id,json=workflowId,proto3" json:"workflow_id,omitempty"`
WorkflowId string `protobuf:"bytes,1,opt,name=workflow_id,json=workflowId,proto3" json:"workflow_id,omitempty"`
}
func (x *DeleteWorkflowRequest) Reset() {
@@ -1230,13 +1196,6 @@ func (*DeleteWorkflowRequest) Descriptor() ([]byte, []int) {
return file_workflows_proto_rawDescGZIP(), []int{15}
}
func (x *DeleteWorkflowRequest) GetTenantId() string {
if x != nil {
return x.TenantId
}
return ""
}
func (x *DeleteWorkflowRequest) GetWorkflowId() string {
if x != nil {
return x.WorkflowId
@@ -1249,8 +1208,7 @@ type GetWorkflowByNameRequest struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
TenantId string `protobuf:"bytes,1,opt,name=tenant_id,json=tenantId,proto3" json:"tenant_id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
}
func (x *GetWorkflowByNameRequest) Reset() {
@@ -1285,13 +1243,6 @@ func (*GetWorkflowByNameRequest) Descriptor() ([]byte, []int) {
return file_workflows_proto_rawDescGZIP(), []int{16}
}
func (x *GetWorkflowByNameRequest) GetTenantId() string {
if x != nil {
return x.TenantId
}
return ""
}
func (x *GetWorkflowByNameRequest) GetName() string {
if x != nil {
return x.Name
@@ -1307,145 +1258,108 @@ var file_workflows_proto_rawDesc = []byte{
0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x22, 0x61, 0x0a, 0x12, 0x50, 0x75, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61,
0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e,
0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x6f, 0x70, 0x74, 0x73, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b,
0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x73, 0x52,
0x04, 0x6f, 0x70, 0x74, 0x73, 0x22, 0xae, 0x02, 0x0a, 0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4f,
0x74, 0x6f, 0x22, 0x44, 0x0a, 0x12, 0x50, 0x75, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6f, 0x70, 0x74, 0x73,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57,
0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4f, 0x70,
0x74, 0x73, 0x52, 0x04, 0x6f, 0x70, 0x74, 0x73, 0x22, 0xae, 0x02, 0x0a, 0x19, 0x43, 0x72, 0x65,
0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f,
0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d,
0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x12, 0x23, 0x0a,
0x0d, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x18, 0x05,
0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x72, 0x6f, 0x6e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65,
0x72, 0x73, 0x12, 0x49, 0x0a, 0x12, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f,
0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x11, 0x73, 0x63, 0x68, 0x65,
0x64, 0x75, 0x6c, 0x65, 0x64, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a,
0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x43, 0x72,
0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4a, 0x6f, 0x62, 0x4f,
0x70, 0x74, 0x73, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x22, 0x96, 0x01, 0x0a, 0x15, 0x43, 0x72,
0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4a, 0x6f, 0x62, 0x4f,
0x70, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72,
0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x72, 0x69,
0x67, 0x67, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x76, 0x65,
0x6e, 0x74, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x72,
0x6f, 0x6e, 0x5f, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28,
0x09, 0x52, 0x0c, 0x63, 0x72, 0x6f, 0x6e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x12,
0x49, 0x0a, 0x12, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x64, 0x5f, 0x74, 0x72, 0x69,
0x67, 0x67, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x11, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c,
0x65, 0x64, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x04, 0x6a, 0x6f,
0x62, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4a, 0x6f, 0x62, 0x4f, 0x70, 0x74, 0x73,
0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x22, 0x96, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4a, 0x6f, 0x62, 0x4f, 0x70, 0x74, 0x73,
0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72,
0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d,
0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65,
0x6f, 0x75, 0x74, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x04, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x17, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66,
0x6c, 0x6f, 0x77, 0x53, 0x74, 0x65, 0x70, 0x4f, 0x70, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x65,
0x70, 0x73, 0x22, 0x9d, 0x01, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72,
0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x65, 0x70, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x1f, 0x0a,
0x0b, 0x72, 0x65, 0x61, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x61, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x16,
0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75,
0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74,
0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x17, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
0x53, 0x74, 0x65, 0x70, 0x4f, 0x70, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x22,
0x9d, 0x01, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x6f, 0x77, 0x53, 0x74, 0x65, 0x70, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65,
0x61, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0a, 0x72, 0x65, 0x61, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x61,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03,
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x0a,
0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69,
0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73,
0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x22,
0x33, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e,
0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61,
0x6e, 0x74, 0x49, 0x64, 0x22, 0xa7, 0x01, 0x0a, 0x17, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c,
0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1f, 0x0a,
0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x64, 0x12, 0x38,
0x0a, 0x09, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73,
0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75,
0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0x40,
0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x66,
0x6c, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x57, 0x6f, 0x72,
0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73,
0x22, 0x58, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
0x73, 0x46, 0x6f, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1b, 0x0a,
0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x22, 0xaf, 0x02, 0x0a, 0x08, 0x57,
0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74,
0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
0x12, 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x72, 0x65,
0x6e, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e,
0x74, 0x73, 0x22, 0x16, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x6f, 0x77, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8a, 0x01, 0x0a, 0x17, 0x53,
0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x6f, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72,
0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x73, 0x63, 0x68, 0x65, 0x64,
0x75, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65,
0x73, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0x40, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x57,
0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x27, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x09,
0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x22, 0x3b, 0x0a, 0x1c, 0x4c, 0x69, 0x73,
0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x46, 0x6f, 0x72, 0x45, 0x76, 0x65,
0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x76, 0x65,
0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x76,
0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x22, 0xaf, 0x02, 0x0a, 0x08, 0x57, 0x6f, 0x72, 0x6b, 0x66,
0x6c, 0x6f, 0x77, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61,
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39,
0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09,
0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e,
0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65,
0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0b, 0x64, 0x65,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x64,
0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x08, 0x76, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x57,
0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xb1, 0x02, 0x0a, 0x0f, 0x57, 0x6f, 0x72,
0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02,
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0a,
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72,
0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74,
0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64,
0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1b, 0x0a,
0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3e,
0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75,
0x65, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c,
0x0a, 0x08, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x10, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x52, 0x08, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xb1, 0x02, 0x0a,
0x0f, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64,
0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75,
0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64,
0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
0x12, 0x14, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52,
0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x6f, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72,
0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x08, 0x74, 0x72, 0x69, 0x67, 0x67,
0x65, 0x72, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x57, 0x6f, 0x72, 0x6b,
0x66, 0x6c, 0x6f, 0x77, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x52, 0x08, 0x74, 0x72,
0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x09,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x04, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73,
0x22, 0xc6, 0x02, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x72, 0x69,
0x67, 0x67, 0x65, 0x72, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64,
0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65,
0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74,
0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x77,
0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x74,
0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e,
0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66,
0x6c, 0x6f, 0x77, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52,
0x65, 0x66, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2d, 0x0a, 0x05, 0x63, 0x72,
0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x57, 0x6f, 0x72, 0x6b,
0x66, 0x6c, 0x6f, 0x77, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x72, 0x6f, 0x6e, 0x52,
0x65, 0x66, 0x52, 0x05, 0x63, 0x72, 0x6f, 0x6e, 0x73, 0x22, 0x53, 0x0a, 0x17, 0x57, 0x6f, 0x72,
0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e,
0x74, 0x52, 0x65, 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49,
0x64, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x49,
0x0a, 0x16, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65,
0x72, 0x43, 0x72, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65,
0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x72,
0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x22, 0x81, 0x03, 0x0a, 0x03, 0x4a, 0x6f,
0x62, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64,
0x41, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05,
0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6f, 0x72, 0x64,
0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x69,
0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
0x77, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x08, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x18,
0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x73, 0x52, 0x08, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65,
0x72, 0x73, 0x12, 0x18, 0x0a, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x04, 0x2e, 0x4a, 0x6f, 0x62, 0x52, 0x04, 0x6a, 0x6f, 0x62, 0x73, 0x22, 0xc6, 0x02, 0x0a,
0x10, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72,
0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69,
0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
@@ -1453,86 +1367,113 @@ var file_workflows_proto_rawDesc = []byte{
0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70,
0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e,
0x74, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61,
0x6e, 0x74, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28,
0x09, 0x52, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63,
0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e,
0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x66,
0x6c, 0x6f, 0x77, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05,
0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e,
0x74, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61,
0x6e, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x07,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54,
0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x66, 0x52, 0x06,
0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2d, 0x0a, 0x05, 0x63, 0x72, 0x6f, 0x6e, 0x73, 0x18,
0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x72, 0x6f, 0x6e, 0x52, 0x65, 0x66, 0x52, 0x05,
0x63, 0x72, 0x6f, 0x6e, 0x73, 0x22, 0x53, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
0x77, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x66,
0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1b, 0x0a,
0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x49, 0x0a, 0x16, 0x57, 0x6f,
0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x72, 0x6f,
0x6e, 0x52, 0x65, 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49,
0x64, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x63, 0x72, 0x6f, 0x6e, 0x22, 0x81, 0x03, 0x0a, 0x03, 0x4a, 0x6f, 0x62, 0x12, 0x0e, 0x0a,
0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a,
0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63,
0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61,
0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
0x64, 0x41, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64,
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64,
0x12, 0x2e, 0x0a, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x76, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x77,
0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64,
0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69,
0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x09, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x53, 0x74, 0x65, 0x70, 0x52, 0x05, 0x73, 0x74, 0x65, 0x70,
0x73, 0x12, 0x36, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0a, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65,
0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0x85, 0x03, 0x0a, 0x04, 0x53, 0x74,
0x65, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,
0x69, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a,
0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75,
0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x3d, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x64,
0x61, 0x62, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x64, 0x65, 0x73,
0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x70,
0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x53, 0x74, 0x65, 0x70, 0x52, 0x05,
0x73, 0x74, 0x65, 0x70, 0x73, 0x12, 0x36, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74,
0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56,
0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0x85, 0x03,
0x0a, 0x04, 0x53, 0x74, 0x65, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65,
0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41,
0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18,
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x3d, 0x0a, 0x0b,
0x72, 0x65, 0x61, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52,
0x0a, 0x72, 0x65, 0x61, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x74,
0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f,
0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12,
0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52,
0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f,
0x75, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e,
0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12,
0x18, 0x0a, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09,
0x52, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x69,
0x6c, 0x64, 0x72, 0x65, 0x6e, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x63, 0x68, 0x69,
0x6c, 0x64, 0x72, 0x65, 0x6e, 0x22, 0x55, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57,
0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b,
0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x77,
0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x64, 0x22, 0x4b, 0x0a, 0x18,
0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x42, 0x79, 0x4e, 0x61, 0x6d,
0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61,
0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e,
0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x32, 0x87, 0x03, 0x0a, 0x0f, 0x57, 0x6f,
0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3e, 0x0a,
0x0d, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x12, 0x15,
0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b,
0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x34, 0x0a,
0x0b, 0x50, 0x75, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x13, 0x2e, 0x50,
0x75, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x10, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x10, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x57,
0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x18, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75,
0x6c, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x10, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x6f, 0x77, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f,
0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x09, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x4e,
0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x46,
0x6f, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f,
0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x46, 0x6f, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72,
0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33,
0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
0x12, 0x16, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x09, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66,
0x6c, 0x6f, 0x77, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x68, 0x61, 0x74, 0x63, 0x68, 0x65, 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x68, 0x61,
0x74, 0x63, 0x68, 0x65, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2f, 0x63, 0x6f,
0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x72, 0x65, 0x61,
0x64, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e,
0x74, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61,
0x6e, 0x74, 0x49, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x07,
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x61,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x09,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c,
0x75, 0x65, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70,
0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61,
0x72, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65,
0x6e, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x72, 0x65,
0x6e, 0x22, 0x38, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66,
0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f,
0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x64, 0x22, 0x2e, 0x0a, 0x18, 0x47,
0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x32, 0x87, 0x03, 0x0a, 0x0f,
0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
0x3e, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73,
0x12, 0x15, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f,
0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x34, 0x0a, 0x0b, 0x50, 0x75, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x13,
0x2e, 0x50, 0x75, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x10, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c,
0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x18, 0x2e, 0x53, 0x63, 0x68, 0x65,
0x64, 0x75, 0x6c, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b,
0x66, 0x6c, 0x6f, 0x77, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x2e, 0x47, 0x65, 0x74,
0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x42, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x09, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
0x12, 0x4e, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
0x73, 0x46, 0x6f, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x2e, 0x4c, 0x69, 0x73, 0x74,
0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x46, 0x6f, 0x72, 0x45, 0x76, 0x65, 0x6e,
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x57,
0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x33, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x6f, 0x77, 0x12, 0x16, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66,
0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x09, 0x2e, 0x57, 0x6f, 0x72,
0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x74, 0x63, 0x68, 0x65, 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f,
0x68, 0x61, 0x74, 0x63, 0x68, 0x65, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2f,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var (
+18 -10
View File
@@ -22,8 +22,10 @@ import (
)
func (a *AdminServiceImpl) GetWorkflowByName(ctx context.Context, req *contracts.GetWorkflowByNameRequest) (*contracts.Workflow, error) {
tenant := ctx.Value("tenant").(*db.TenantModel)
workflow, err := a.repo.Workflow().GetWorkflowByName(
req.TenantId,
tenant.ID,
req.Name,
)
@@ -44,7 +46,7 @@ func (a *AdminServiceImpl) GetWorkflowByName(ctx context.Context, req *contracts
}
func (a *AdminServiceImpl) PutWorkflow(ctx context.Context, req *contracts.PutWorkflowRequest) (*contracts.WorkflowVersion, error) {
// TODO: authorize request
tenant := ctx.Value("tenant").(*db.TenantModel)
createOpts, err := getCreateWorkflowOpts(req)
@@ -57,7 +59,7 @@ func (a *AdminServiceImpl) PutWorkflow(ctx context.Context, req *contracts.PutWo
var oldWorkflowVersion *db.WorkflowVersionModel
currWorkflow, err := a.repo.Workflow().GetWorkflowByName(
req.TenantId,
tenant.ID,
req.Opts.Name,
)
@@ -68,7 +70,7 @@ func (a *AdminServiceImpl) PutWorkflow(ctx context.Context, req *contracts.PutWo
// workflow does not exist, create it
workflowVersion, err = a.repo.Workflow().CreateNewWorkflow(
req.TenantId,
tenant.ID,
createOpts,
)
@@ -80,7 +82,7 @@ func (a *AdminServiceImpl) PutWorkflow(ctx context.Context, req *contracts.PutWo
// workflow exists, create a new version
workflowVersion, err = a.repo.Workflow().CreateWorkflowVersion(
req.TenantId,
tenant.ID,
createOpts,
)
@@ -295,7 +297,7 @@ func (a *AdminServiceImpl) PutWorkflow(ctx context.Context, req *contracts.PutWo
}
func (a *AdminServiceImpl) ScheduleWorkflow(ctx context.Context, req *contracts.ScheduleWorkflowRequest) (*contracts.WorkflowVersion, error) {
// TODO: authorize request
tenant := ctx.Value("tenant").(*db.TenantModel)
currWorkflow, err := a.repo.Workflow().GetWorkflowById(
req.WorkflowId,
@@ -336,7 +338,7 @@ func (a *AdminServiceImpl) ScheduleWorkflow(ctx context.Context, req *contracts.
}
schedules, err := a.repo.Workflow().CreateSchedules(
req.TenantId,
tenant.ID,
workflowVersion.ID,
&repository.CreateWorkflowSchedulesOpts{
ScheduledTriggers: dbSchedules,
@@ -416,8 +418,10 @@ func (a *AdminServiceImpl) ScheduleWorkflow(ctx context.Context, req *contracts.
}
func (a *AdminServiceImpl) DeleteWorkflow(ctx context.Context, req *contracts.DeleteWorkflowRequest) (*contracts.Workflow, error) {
tenant := ctx.Value("tenant").(*db.TenantModel)
workflow, err := a.repo.Workflow().DeleteWorkflow(
req.TenantId,
tenant.ID,
req.WorkflowId,
)
@@ -434,8 +438,10 @@ func (a *AdminServiceImpl) ListWorkflows(
ctx context.Context,
req *contracts.ListWorkflowsRequest,
) (*contracts.ListWorkflowsResponse, error) {
tenant := ctx.Value("tenant").(*db.TenantModel)
listResp, err := a.repo.Workflow().ListWorkflows(
req.TenantId,
tenant.ID,
&repository.ListWorkflowsOpts{},
)
@@ -458,8 +464,10 @@ func (a *AdminServiceImpl) ListWorkflowsForEvent(
ctx context.Context,
req *contracts.ListWorkflowsForEventRequest,
) (*contracts.ListWorkflowsResponse, error) {
tenant := ctx.Value("tenant").(*db.TenantModel)
listResp, err := a.repo.Workflow().ListWorkflows(
req.TenantId,
tenant.ID,
&repository.ListWorkflowsOpts{
EventKey: &req.EventKey,
},
@@ -124,14 +124,12 @@ type WorkerRegisterRequest struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// the tenant id
TenantId string `protobuf:"bytes,1,opt,name=tenantId,proto3" json:"tenantId,omitempty"`
// the name of the worker
WorkerName string `protobuf:"bytes,2,opt,name=workerName,proto3" json:"workerName,omitempty"`
WorkerName string `protobuf:"bytes,1,opt,name=workerName,proto3" json:"workerName,omitempty"`
// a list of actions that this worker can run
Actions []string `protobuf:"bytes,3,rep,name=actions,proto3" json:"actions,omitempty"`
Actions []string `protobuf:"bytes,2,rep,name=actions,proto3" json:"actions,omitempty"`
// (optional) the services for this worker
Services []string `protobuf:"bytes,4,rep,name=services,proto3" json:"services,omitempty"`
Services []string `protobuf:"bytes,3,rep,name=services,proto3" json:"services,omitempty"`
}
func (x *WorkerRegisterRequest) Reset() {
@@ -166,13 +164,6 @@ func (*WorkerRegisterRequest) Descriptor() ([]byte, []int) {
return file_dispatcher_proto_rawDescGZIP(), []int{0}
}
func (x *WorkerRegisterRequest) GetTenantId() string {
if x != nil {
return x.TenantId
}
return ""
}
func (x *WorkerRegisterRequest) GetWorkerName() string {
if x != nil {
return x.WorkerName
@@ -385,10 +376,8 @@ type WorkerListenRequest struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// the tenant id
TenantId string `protobuf:"bytes,1,opt,name=tenantId,proto3" json:"tenantId,omitempty"`
// the id of the worker
WorkerId string `protobuf:"bytes,2,opt,name=workerId,proto3" json:"workerId,omitempty"`
WorkerId string `protobuf:"bytes,1,opt,name=workerId,proto3" json:"workerId,omitempty"`
}
func (x *WorkerListenRequest) Reset() {
@@ -423,13 +412,6 @@ func (*WorkerListenRequest) Descriptor() ([]byte, []int) {
return file_dispatcher_proto_rawDescGZIP(), []int{3}
}
func (x *WorkerListenRequest) GetTenantId() string {
if x != nil {
return x.TenantId
}
return ""
}
func (x *WorkerListenRequest) GetWorkerId() string {
if x != nil {
return x.WorkerId
@@ -442,10 +424,8 @@ type WorkerUnsubscribeRequest struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// the tenant id to unsubscribe from
TenantId string `protobuf:"bytes,1,opt,name=tenantId,proto3" json:"tenantId,omitempty"`
// the id of the worker
WorkerId string `protobuf:"bytes,2,opt,name=workerId,proto3" json:"workerId,omitempty"`
WorkerId string `protobuf:"bytes,1,opt,name=workerId,proto3" json:"workerId,omitempty"`
}
func (x *WorkerUnsubscribeRequest) Reset() {
@@ -480,13 +460,6 @@ func (*WorkerUnsubscribeRequest) Descriptor() ([]byte, []int) {
return file_dispatcher_proto_rawDescGZIP(), []int{4}
}
func (x *WorkerUnsubscribeRequest) GetTenantId() string {
if x != nil {
return x.TenantId
}
return ""
}
func (x *WorkerUnsubscribeRequest) GetWorkerId() string {
if x != nil {
return x.WorkerId
@@ -556,25 +529,23 @@ type ActionEvent struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// the tenant id
TenantId string `protobuf:"bytes,1,opt,name=tenantId,proto3" json:"tenantId,omitempty"`
// the id of the worker
WorkerId string `protobuf:"bytes,2,opt,name=workerId,proto3" json:"workerId,omitempty"`
WorkerId string `protobuf:"bytes,1,opt,name=workerId,proto3" json:"workerId,omitempty"`
// the id of the job
JobId string `protobuf:"bytes,3,opt,name=jobId,proto3" json:"jobId,omitempty"`
JobId string `protobuf:"bytes,2,opt,name=jobId,proto3" json:"jobId,omitempty"`
// the job run id
JobRunId string `protobuf:"bytes,4,opt,name=jobRunId,proto3" json:"jobRunId,omitempty"`
JobRunId string `protobuf:"bytes,3,opt,name=jobRunId,proto3" json:"jobRunId,omitempty"`
// the id of the step
StepId string `protobuf:"bytes,5,opt,name=stepId,proto3" json:"stepId,omitempty"`
StepId string `protobuf:"bytes,4,opt,name=stepId,proto3" json:"stepId,omitempty"`
// the step run id
StepRunId string `protobuf:"bytes,6,opt,name=stepRunId,proto3" json:"stepRunId,omitempty"`
StepRunId string `protobuf:"bytes,5,opt,name=stepRunId,proto3" json:"stepRunId,omitempty"`
// the action id
ActionId string `protobuf:"bytes,7,opt,name=actionId,proto3" json:"actionId,omitempty"`
EventTimestamp *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=eventTimestamp,proto3" json:"eventTimestamp,omitempty"`
ActionId string `protobuf:"bytes,6,opt,name=actionId,proto3" json:"actionId,omitempty"`
EventTimestamp *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=eventTimestamp,proto3" json:"eventTimestamp,omitempty"`
// the step event type
EventType ActionEventType `protobuf:"varint,9,opt,name=eventType,proto3,enum=ActionEventType" json:"eventType,omitempty"`
EventType ActionEventType `protobuf:"varint,8,opt,name=eventType,proto3,enum=ActionEventType" json:"eventType,omitempty"`
// the event payload
EventPayload string `protobuf:"bytes,10,opt,name=eventPayload,proto3" json:"eventPayload,omitempty"`
EventPayload string `protobuf:"bytes,9,opt,name=eventPayload,proto3" json:"eventPayload,omitempty"`
}
func (x *ActionEvent) Reset() {
@@ -609,13 +580,6 @@ func (*ActionEvent) Descriptor() ([]byte, []int) {
return file_dispatcher_proto_rawDescGZIP(), []int{6}
}
func (x *ActionEvent) GetTenantId() string {
if x != nil {
return x.TenantId
}
return ""
}
func (x *ActionEvent) GetWorkerId() string {
if x != nil {
return x.WorkerId
@@ -742,117 +706,109 @@ var file_dispatcher_proto_rawDesc = []byte{
0x0a, 0x10, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x22, 0x89, 0x01, 0x0a, 0x15, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65,
0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a,
0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x77, 0x6f, 0x72,
0x6b, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77,
0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18,
0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x22,
0x70, 0x0a, 0x16, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65,
0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6e,
0x61, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e,
0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49,
0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49,
0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4e, 0x61, 0x6d,
0x65, 0x22, 0x9d, 0x02, 0x0a, 0x0e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x41, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64,
0x12, 0x14, 0x0a, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d,
0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e, 0x61, 0x6d, 0x65,
0x12, 0x1a, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x18, 0x04, 0x20, 0x01,
0x28, 0x09, 0x52, 0x08, 0x6a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06,
0x73, 0x74, 0x65, 0x70, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74,
0x65, 0x70, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x65, 0x70, 0x52, 0x75, 0x6e, 0x49,
0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x65, 0x70, 0x52, 0x75, 0x6e,
0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x07,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2b,
0x0a, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01,
0x28, 0x0e, 0x32, 0x0b, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x52,
0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x61,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x09, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0d, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61,
0x64, 0x22, 0x4d, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x65,
0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6e, 0x61,
0x6e, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61,
0x6e, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64,
0x22, 0x52, 0x0a, 0x18, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73,
0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08,
0x6f, 0x74, 0x6f, 0x22, 0x6d, 0x0a, 0x15, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x67,
0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a,
0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07,
0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x61,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x73, 0x22, 0x70, 0x0a, 0x16, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69,
0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08,
0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b,
0x65, 0x72, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b,
0x65, 0x72, 0x49, 0x64, 0x22, 0x53, 0x0a, 0x19, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x55, 0x6e,
0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a,
0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x22, 0xe1, 0x02, 0x0a, 0x0b, 0x41, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6e,
0x61, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e,
0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49,
0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49,
0x64, 0x12, 0x14, 0x0a, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x52, 0x75,
0x6e, 0x49, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x6f, 0x62, 0x52, 0x75,
0x6e, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x65, 0x70, 0x49, 0x64, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x65, 0x70, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73,
0x74, 0x65, 0x70, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
0x73, 0x74, 0x65, 0x70, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x42, 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74,
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2e, 0x0a, 0x09, 0x65, 0x76, 0x65,
0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x41,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09,
0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x65, 0x76, 0x65,
0x6e, 0x74, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x4d, 0x0a,
0x13, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64,
0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x2a, 0x35, 0x0a, 0x0a,
0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54,
0x41, 0x52, 0x54, 0x5f, 0x53, 0x54, 0x45, 0x50, 0x5f, 0x52, 0x55, 0x4e, 0x10, 0x00, 0x12, 0x13,
0x0a, 0x0f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x5f, 0x53, 0x54, 0x45, 0x50, 0x5f, 0x52, 0x55,
0x4e, 0x10, 0x01, 0x2a, 0x86, 0x01, 0x0a, 0x0f, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76,
0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x54, 0x45, 0x50, 0x5f,
0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f,
0x57, 0x4e, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x54, 0x45, 0x50, 0x5f, 0x45, 0x56, 0x45,
0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10,
0x01, 0x12, 0x1d, 0x0a, 0x19, 0x53, 0x54, 0x45, 0x50, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f,
0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x02,
0x12, 0x1a, 0x0a, 0x16, 0x53, 0x54, 0x45, 0x50, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54,
0x59, 0x50, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x32, 0x81, 0x02, 0x0a,
0x0a, 0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x12, 0x3d, 0x0a, 0x08, 0x52,
0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x16, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72,
0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x17, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x06, 0x4c, 0x69,
0x73, 0x74, 0x65, 0x6e, 0x12, 0x14, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x69, 0x73,
0x74, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x41, 0x73, 0x73,
0x69, 0x67, 0x6e, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x30, 0x01, 0x12,
0x37, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65,
0x6e, 0x74, 0x12, 0x0c, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74,
0x1a, 0x14, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x0b, 0x55, 0x6e, 0x73, 0x75,
0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x19, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72,
0x55, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x55, 0x6e, 0x73, 0x75, 0x62,
0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
0x42, 0x47, 0x5a, 0x45, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68,
0x61, 0x74, 0x63, 0x68, 0x65, 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x68, 0x61, 0x74, 0x63, 0x68,
0x65, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x73, 0x2f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2f,
0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
0x65, 0x72, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4e, 0x61,
0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72,
0x4e, 0x61, 0x6d, 0x65, 0x22, 0x9d, 0x02, 0x0a, 0x0e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65,
0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e,
0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e,
0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6a, 0x6f, 0x62,
0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x6f, 0x62, 0x4e,
0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x18,
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x6f, 0x62, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x12,
0x16, 0x0a, 0x06, 0x73, 0x74, 0x65, 0x70, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52,
0x06, 0x73, 0x74, 0x65, 0x70, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x65, 0x70, 0x52,
0x75, 0x6e, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x65, 0x70,
0x52, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49,
0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49,
0x64, 0x12, 0x2b, 0x0a, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x18,
0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0b, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79,
0x70, 0x65, 0x52, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24,
0x0a, 0x0d, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18,
0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79,
0x6c, 0x6f, 0x61, 0x64, 0x22, 0x31, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x69,
0x73, 0x74, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77,
0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77,
0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x22, 0x36, 0x0a, 0x18, 0x57, 0x6f, 0x72, 0x6b, 0x65,
0x72, 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x22,
0x53, 0x0a, 0x19, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73, 0x63,
0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08,
0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b,
0x65, 0x72, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b,
0x65, 0x72, 0x49, 0x64, 0x22, 0xc5, 0x02, 0x0a, 0x0b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45,
0x76, 0x65, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64,
0x12, 0x14, 0x0a, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6a, 0x6f, 0x62, 0x52, 0x75, 0x6e,
0x49, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6a, 0x6f, 0x62, 0x52, 0x75, 0x6e,
0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x65, 0x70, 0x49, 0x64, 0x18, 0x04, 0x20, 0x01,
0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x65, 0x70, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74,
0x65, 0x70, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73,
0x74, 0x65, 0x70, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x61, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x49, 0x64, 0x12, 0x42, 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d,
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2e, 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e,
0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x41, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65,
0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x65, 0x76, 0x65, 0x6e,
0x74, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
0x65, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x4d, 0x0a, 0x13,
0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x12,
0x1a, 0x0a, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x2a, 0x35, 0x0a, 0x0a, 0x41,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41,
0x52, 0x54, 0x5f, 0x53, 0x54, 0x45, 0x50, 0x5f, 0x52, 0x55, 0x4e, 0x10, 0x00, 0x12, 0x13, 0x0a,
0x0f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x5f, 0x53, 0x54, 0x45, 0x50, 0x5f, 0x52, 0x55, 0x4e,
0x10, 0x01, 0x2a, 0x86, 0x01, 0x0a, 0x0f, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65,
0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x54, 0x45, 0x50, 0x5f, 0x45,
0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57,
0x4e, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x54, 0x45, 0x50, 0x5f, 0x45, 0x56, 0x45, 0x4e,
0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x45, 0x44, 0x10, 0x01,
0x12, 0x1d, 0x0a, 0x19, 0x53, 0x54, 0x45, 0x50, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54,
0x59, 0x50, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12,
0x1a, 0x0a, 0x16, 0x53, 0x54, 0x45, 0x50, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x54, 0x59,
0x50, 0x45, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x32, 0x81, 0x02, 0x0a, 0x0a,
0x44, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x12, 0x3d, 0x0a, 0x08, 0x52, 0x65,
0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x16, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52,
0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17,
0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x06, 0x4c, 0x69, 0x73,
0x74, 0x65, 0x6e, 0x12, 0x14, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x69, 0x73, 0x74,
0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0f, 0x2e, 0x41, 0x73, 0x73, 0x69,
0x67, 0x6e, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x30, 0x01, 0x12, 0x37,
0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e,
0x74, 0x12, 0x0c, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x1a,
0x14, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x0b, 0x55, 0x6e, 0x73, 0x75, 0x62,
0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x19, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x55,
0x6e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x1a, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x55, 0x6e, 0x73, 0x75, 0x62, 0x73,
0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42,
0x47, 0x5a, 0x45, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61,
0x74, 0x63, 0x68, 0x65, 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x68, 0x61, 0x74, 0x63, 0x68, 0x65,
0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x73, 0x2f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2f, 0x63,
0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
+24 -15
View File
@@ -90,7 +90,7 @@ func (worker *subscribedWorker) CancelStepRun(
}
func (s *DispatcherImpl) Register(ctx context.Context, request *contracts.WorkerRegisterRequest) (*contracts.WorkerRegisterResponse, error) {
// TODO: auth checks to make sure the worker is allowed to register for this tenant
tenant := ctx.Value("tenant").(*db.TenantModel)
s.l.Debug().Msgf("Received register request from ID %s with actions %v", request.WorkerName, request.Actions)
@@ -101,7 +101,7 @@ func (s *DispatcherImpl) Register(ctx context.Context, request *contracts.Worker
}
// create a worker in the database
worker, err := s.repo.Worker().CreateNewWorker(request.TenantId, &repository.CreateWorkerOpts{
worker, err := s.repo.Worker().CreateNewWorker(tenant.ID, &repository.CreateWorkerOpts{
DispatcherId: s.dispatcherId,
Name: request.WorkerName,
Actions: request.Actions,
@@ -109,7 +109,7 @@ func (s *DispatcherImpl) Register(ctx context.Context, request *contracts.Worker
})
if err != nil {
s.l.Error().Err(err).Msgf("could not create worker for tenant %s", request.TenantId)
s.l.Error().Err(err).Msgf("could not create worker for tenant %s", tenant.ID)
return nil, err
}
@@ -125,6 +125,8 @@ func (s *DispatcherImpl) Register(ctx context.Context, request *contracts.Worker
// Subscribe handles a subscribe request from a client
func (s *DispatcherImpl) Listen(request *contracts.WorkerListenRequest, stream contracts.Dispatcher_ListenServer) error {
tenant := stream.Context().Value("tenant").(*db.TenantModel)
s.l.Debug().Msgf("Received subscribe request from ID: %s", request.WorkerId)
worker, err := s.repo.Worker().GetWorkerById(request.WorkerId)
@@ -136,7 +138,7 @@ func (s *DispatcherImpl) Listen(request *contracts.WorkerListenRequest, stream c
// check the worker's dispatcher against the current dispatcher. if they don't match, then update the worker
if worker.DispatcherID != s.dispatcherId {
_, err = s.repo.Worker().UpdateWorker(request.TenantId, request.WorkerId, &repository.UpdateWorkerOpts{
_, err = s.repo.Worker().UpdateWorker(tenant.ID, request.WorkerId, &repository.UpdateWorkerOpts{
DispatcherId: &s.dispatcherId,
})
@@ -155,7 +157,7 @@ func (s *DispatcherImpl) Listen(request *contracts.WorkerListenRequest, stream c
inactive := db.WorkerStatusInactive
_, err := s.repo.Worker().UpdateWorker(request.TenantId, request.WorkerId, &repository.UpdateWorkerOpts{
_, err := s.repo.Worker().UpdateWorker(tenant.ID, request.WorkerId, &repository.UpdateWorkerOpts{
Status: &inactive,
})
@@ -183,7 +185,7 @@ func (s *DispatcherImpl) Listen(request *contracts.WorkerListenRequest, stream c
if now := time.Now().UTC(); lastHeartbeat.Add(5 * time.Second).Before(now) {
s.l.Debug().Msgf("updating worker %s heartbeat", request.WorkerId)
_, err := s.repo.Worker().UpdateWorker(request.TenantId, request.WorkerId, &repository.UpdateWorkerOpts{
_, err := s.repo.Worker().UpdateWorker(tenant.ID, request.WorkerId, &repository.UpdateWorkerOpts{
LastHeartbeatAt: &now,
})
@@ -227,23 +229,26 @@ func (s *DispatcherImpl) SendActionEvent(ctx context.Context, request *contracts
}
func (s *DispatcherImpl) Unsubscribe(ctx context.Context, request *contracts.WorkerUnsubscribeRequest) (*contracts.WorkerUnsubscribeResponse, error) {
// TODO: auth checks to make sure the worker is allowed to unsubscribe for this tenant
tenant := ctx.Value("tenant").(*db.TenantModel)
// no matter what, remove the worker from the connection pool
defer s.workers.Delete(request.WorkerId)
err := s.repo.Worker().DeleteWorker(request.TenantId, request.WorkerId)
err := s.repo.Worker().DeleteWorker(tenant.ID, request.WorkerId)
if err != nil {
return nil, err
}
return &contracts.WorkerUnsubscribeResponse{
TenantId: request.TenantId,
TenantId: tenant.ID,
WorkerId: request.WorkerId,
}, nil
}
func (s *DispatcherImpl) handleStepRunStarted(ctx context.Context, request *contracts.ActionEvent) (*contracts.ActionEventResponse, error) {
tenant := ctx.Value("tenant").(*db.TenantModel)
s.l.Debug().Msgf("Received step started event for step run %s", request.StepRunId)
startedAt := request.EventTimestamp.AsTime()
@@ -254,7 +259,7 @@ func (s *DispatcherImpl) handleStepRunStarted(ctx context.Context, request *cont
})
metadata, _ := datautils.ToJSONMap(tasktypes.StepRunStartedTaskMetadata{
TenantId: request.TenantId,
TenantId: tenant.ID,
})
// send the event to the jobs queue
@@ -270,12 +275,14 @@ func (s *DispatcherImpl) handleStepRunStarted(ctx context.Context, request *cont
}
return &contracts.ActionEventResponse{
TenantId: request.TenantId,
TenantId: tenant.ID,
WorkerId: request.WorkerId,
}, nil
}
func (s *DispatcherImpl) handleStepRunCompleted(ctx context.Context, request *contracts.ActionEvent) (*contracts.ActionEventResponse, error) {
tenant := ctx.Value("tenant").(*db.TenantModel)
s.l.Debug().Msgf("Received step completed event for step run %s", request.StepRunId)
finishedAt := request.EventTimestamp.AsTime()
@@ -287,7 +294,7 @@ func (s *DispatcherImpl) handleStepRunCompleted(ctx context.Context, request *co
})
metadata, _ := datautils.ToJSONMap(tasktypes.StepRunFinishedTaskMetadata{
TenantId: request.TenantId,
TenantId: tenant.ID,
})
// send the event to the jobs queue
@@ -303,12 +310,14 @@ func (s *DispatcherImpl) handleStepRunCompleted(ctx context.Context, request *co
}
return &contracts.ActionEventResponse{
TenantId: request.TenantId,
TenantId: tenant.ID,
WorkerId: request.WorkerId,
}, nil
}
func (s *DispatcherImpl) handleStepRunFailed(ctx context.Context, request *contracts.ActionEvent) (*contracts.ActionEventResponse, error) {
tenant := ctx.Value("tenant").(*db.TenantModel)
s.l.Debug().Msgf("Received step failed event for step run %s", request.StepRunId)
failedAt := request.EventTimestamp.AsTime()
@@ -320,7 +329,7 @@ func (s *DispatcherImpl) handleStepRunFailed(ctx context.Context, request *contr
})
metadata, _ := datautils.ToJSONMap(tasktypes.StepRunFailedTaskMetadata{
TenantId: request.TenantId,
TenantId: tenant.ID,
})
// send the event to the jobs queue
@@ -336,7 +345,7 @@ func (s *DispatcherImpl) handleStepRunFailed(ctx context.Context, request *contr
}
return &contracts.ActionEventResponse{
TenantId: request.TenantId,
TenantId: tenant.ID,
WorkerId: request.WorkerId,
}, nil
}
+53
View File
@@ -0,0 +1,53 @@
package middleware
import (
"context"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
"github.com/rs/zerolog"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/hatchet-dev/hatchet/internal/config/server"
)
type GRPCAuthN struct {
config *server.ServerConfig
l *zerolog.Logger
}
func NewAuthN(config *server.ServerConfig) *GRPCAuthN {
return &GRPCAuthN{
config: config,
l: config.Logger,
}
}
func (a *GRPCAuthN) Middleware(ctx context.Context) (context.Context, error) {
forbidden := status.Errorf(codes.Unauthenticated, "invalid auth token")
token, err := auth.AuthFromMD(ctx, "bearer")
if err != nil {
a.l.Debug().Err(err).Msg("error getting bearer token from request")
return nil, forbidden
}
tenantId, err := a.config.Auth.JWTManager.ValidateTenantToken(token)
if err != nil {
a.l.Debug().Err(err).Msg("error validating tenant token")
return nil, forbidden
}
// get the tenant id
queriedTenant, err := a.config.Repository.Tenant().GetTenantByID(tenantId)
if err != nil {
a.l.Debug().Err(err).Msg("error getting tenant by id")
return nil, forbidden
}
return context.WithValue(ctx, "tenant", queriedTenant), nil
}
+26
View File
@@ -6,16 +6,19 @@ import (
"fmt"
"net"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
"github.com/rs/zerolog"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"github.com/hatchet-dev/hatchet/internal/config/server"
"github.com/hatchet-dev/hatchet/internal/logger"
"github.com/hatchet-dev/hatchet/internal/services/admin"
admincontracts "github.com/hatchet-dev/hatchet/internal/services/admin/contracts"
"github.com/hatchet-dev/hatchet/internal/services/dispatcher"
dispatchercontracts "github.com/hatchet-dev/hatchet/internal/services/dispatcher/contracts"
"github.com/hatchet-dev/hatchet/internal/services/grpc/middleware"
"github.com/hatchet-dev/hatchet/internal/services/ingestor"
eventcontracts "github.com/hatchet-dev/hatchet/internal/services/ingestor/contracts"
)
@@ -29,6 +32,7 @@ type Server struct {
port int
bindAddress string
config *server.ServerConfig
ingestor ingestor.Ingestor
dispatcher dispatcher.Dispatcher
admin admin.AdminService
@@ -39,6 +43,7 @@ type Server struct {
type ServerOpt func(*ServerOpts)
type ServerOpts struct {
config *server.ServerConfig
l *zerolog.Logger
port int
bindAddress string
@@ -84,6 +89,12 @@ func WithIngestor(i ingestor.Ingestor) ServerOpt {
}
}
func WithConfig(config *server.ServerConfig) ServerOpt {
return func(opts *ServerOpts) {
opts.config = config
}
}
func WithTLSConfig(tls *tls.Config) ServerOpt {
return func(opts *ServerOpts) {
opts.tls = tls
@@ -115,6 +126,10 @@ func NewServer(fs ...ServerOpt) (*Server, error) {
f(opts)
}
if opts.config == nil {
return nil, fmt.Errorf("config is required. use WithConfig")
}
if opts.tls == nil {
return nil, fmt.Errorf("tls config is required. use WithTLSConfig")
}
@@ -124,6 +139,7 @@ func NewServer(fs ...ServerOpt) (*Server, error) {
return &Server{
l: opts.l,
config: opts.config,
port: opts.port,
bindAddress: opts.bindAddress,
ingestor: opts.ingestor,
@@ -155,6 +171,16 @@ func (s *Server) startGRPC(ctx context.Context) error {
serverOpts = append(serverOpts, grpc.Creds(credentials.NewTLS(s.tls)))
}
authMiddleware := middleware.NewAuthN(s.config)
serverOpts = append(serverOpts, grpc.StreamInterceptor(
auth.StreamServerInterceptor(authMiddleware.Middleware),
))
serverOpts = append(serverOpts, grpc.UnaryInterceptor(
auth.UnaryServerInterceptor(authMiddleware.Middleware),
))
grpcServer := grpc.NewServer(serverOpts...)
if s.ingestor != nil {
@@ -110,14 +110,12 @@ type PushEventRequest struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// the tenant id
TenantId string `protobuf:"bytes,1,opt,name=tenantId,proto3" json:"tenantId,omitempty"`
// the key for the event
Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
// the payload for the event
Payload string `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"`
Payload string `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"`
// when the event was generated
EventTimestamp *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=eventTimestamp,proto3" json:"eventTimestamp,omitempty"`
EventTimestamp *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=eventTimestamp,proto3" json:"eventTimestamp,omitempty"`
}
func (x *PushEventRequest) Reset() {
@@ -152,13 +150,6 @@ func (*PushEventRequest) Descriptor() ([]byte, []int) {
return file_events_proto_rawDescGZIP(), []int{1}
}
func (x *PushEventRequest) GetTenantId() string {
if x != nil {
return x.TenantId
}
return ""
}
func (x *PushEventRequest) GetKey() string {
if x != nil {
return x.Key
@@ -185,12 +176,10 @@ type ListEventRequest struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// (required) the tenant id
TenantId string `protobuf:"bytes,1,opt,name=tenantId,proto3" json:"tenantId,omitempty"`
// (optional) the number of events to skip
Offset int32 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"`
Offset int32 `protobuf:"varint,1,opt,name=offset,proto3" json:"offset,omitempty"`
// (optional) the key for the event
Key string `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"`
Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
}
func (x *ListEventRequest) Reset() {
@@ -225,13 +214,6 @@ func (*ListEventRequest) Descriptor() ([]byte, []int) {
return file_events_proto_rawDescGZIP(), []int{2}
}
func (x *ListEventRequest) GetTenantId() string {
if x != nil {
return x.TenantId
}
return ""
}
func (x *ListEventRequest) GetOffset() int32 {
if x != nil {
return x.Offset
@@ -299,10 +281,8 @@ type ReplayEventRequest struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// the tenant id
TenantId string `protobuf:"bytes,1,opt,name=tenantId,proto3" json:"tenantId,omitempty"`
// the event id to replay
EventId string `protobuf:"bytes,2,opt,name=eventId,proto3" json:"eventId,omitempty"`
EventId string `protobuf:"bytes,1,opt,name=eventId,proto3" json:"eventId,omitempty"`
}
func (x *ReplayEventRequest) Reset() {
@@ -337,13 +317,6 @@ func (*ReplayEventRequest) Descriptor() ([]byte, []int) {
return file_events_proto_rawDescGZIP(), []int{4}
}
func (x *ReplayEventRequest) GetTenantId() string {
if x != nil {
return x.TenantId
}
return ""
}
func (x *ReplayEventRequest) GetEventId() string {
if x != nil {
return x.EventId
@@ -368,45 +341,40 @@ var file_events_proto_rawDesc = []byte{
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22,
0x9e, 0x01, 0x0a, 0x10, 0x50, 0x75, 0x73, 0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64,
0x82, 0x01, 0x0a, 0x10, 0x50, 0x75, 0x73, 0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61,
0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64,
0x12, 0x42, 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x22, 0x3c, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x76, 0x65, 0x6e,
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73,
0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74,
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x42, 0x0a, 0x0e,
0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x52, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x22, 0x58, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64,
0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05,
0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x33, 0x0a, 0x11, 0x4c, 0x69,
0x73, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x1e, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x06, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22,
0x4a, 0x0a, 0x12, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49,
0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x07, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x32, 0x99, 0x01, 0x0a, 0x0d,
0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x23, 0x0a,
0x04, 0x50, 0x75, 0x73, 0x68, 0x12, 0x11, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x45, 0x76, 0x65, 0x6e,
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x06, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74,
0x22, 0x00, 0x12, 0x2f, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x11, 0x2e, 0x4c, 0x69, 0x73,
0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e,
0x4c, 0x69, 0x73, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x00, 0x12, 0x32, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x53, 0x69, 0x6e,
0x67, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x13, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x61,
0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x06, 0x2e,
0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x00, 0x42, 0x47, 0x5a, 0x45, 0x67, 0x69, 0x74, 0x68, 0x75,
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x74, 0x63, 0x68, 0x65, 0x74, 0x2d, 0x64, 0x65,
0x76, 0x2f, 0x68, 0x61, 0x74, 0x63, 0x68, 0x65, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x64, 0x69, 0x73, 0x70,
0x61, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x65, 0x79, 0x22, 0x33, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74,
0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x06, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52,
0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x2e, 0x0a, 0x12, 0x52, 0x65, 0x70, 0x6c, 0x61,
0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a,
0x07, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
0x65, 0x76, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x32, 0x99, 0x01, 0x0a, 0x0d, 0x45, 0x76, 0x65, 0x6e,
0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x04, 0x50, 0x75, 0x73,
0x68, 0x12, 0x11, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x06, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x00, 0x12, 0x2f,
0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x11, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x45, 0x76, 0x65,
0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x4c, 0x69, 0x73, 0x74,
0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
0x32, 0x0a, 0x11, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x45,
0x76, 0x65, 0x6e, 0x74, 0x12, 0x13, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x79, 0x45, 0x76, 0x65,
0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x06, 0x2e, 0x45, 0x76, 0x65, 0x6e,
0x74, 0x22, 0x00, 0x42, 0x47, 0x5a, 0x45, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x68, 0x61, 0x74, 0x63, 0x68, 0x65, 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x68, 0x61,
0x74, 0x63, 0x68, 0x65, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x64, 0x69, 0x73, 0x70, 0x61, 0x74, 0x63, 0x68,
0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var (
+9 -3
View File
@@ -16,6 +16,8 @@ import (
)
func (i *IngestorImpl) Push(ctx context.Context, req *contracts.PushEventRequest) (*contracts.Event, error) {
tenant := ctx.Value("tenant").(*db.TenantModel)
eventDataMap := map[string]interface{}{}
err := json.Unmarshal([]byte(req.Payload), &eventDataMap)
@@ -24,7 +26,7 @@ func (i *IngestorImpl) Push(ctx context.Context, req *contracts.PushEventRequest
return nil, err
}
event, err := i.IngestEvent(ctx, req.TenantId, req.Key, eventDataMap)
event, err := i.IngestEvent(ctx, tenant.ID, req.Key, eventDataMap)
if err != nil {
return nil, err
@@ -40,6 +42,8 @@ func (i *IngestorImpl) Push(ctx context.Context, req *contracts.PushEventRequest
}
func (i *IngestorImpl) List(ctx context.Context, req *contracts.ListEventRequest) (*contracts.ListEventResponse, error) {
tenant := ctx.Value("tenant").(*db.TenantModel)
offset := int(req.Offset)
var keys []string
@@ -47,7 +51,7 @@ func (i *IngestorImpl) List(ctx context.Context, req *contracts.ListEventRequest
keys = []string{req.Key}
}
listResult, err := i.eventRepository.ListEvents(req.TenantId, &repository.ListEventOpts{
listResult, err := i.eventRepository.ListEvents(tenant.ID, &repository.ListEventOpts{
Keys: keys,
Offset: &offset,
})
@@ -74,13 +78,15 @@ func (i *IngestorImpl) List(ctx context.Context, req *contracts.ListEventRequest
}
func (i *IngestorImpl) ReplaySingleEvent(ctx context.Context, req *contracts.ReplayEventRequest) (*contracts.Event, error) {
tenant := ctx.Value("tenant").(*db.TenantModel)
oldEvent, err := i.eventRepository.GetEventById(req.EventId)
if err != nil {
return nil, err
}
newEvent, err := i.IngestReplayedEvent(ctx, req.TenantId, oldEvent)
newEvent, err := i.IngestReplayedEvent(ctx, tenant.ID, oldEvent)
if err != nil {
return nil, err
-2
View File
@@ -11,7 +11,5 @@ func Prepare(t *testing.T) {
_ = os.Setenv("HATCHET_CLIENT_TENANT_ID", "707d0855-80ab-4e1f-a156-f1c4546cbf52")
_ = os.Setenv("DATABASE_URL", "postgresql://hatchet:hatchet@127.0.0.1:5431/hatchet")
_ = os.Setenv("HATCHET_CLIENT_TLS_ROOT_CA_FILE", "../../hack/dev/certs/ca.cert")
_ = os.Setenv("HATCHET_CLIENT_TLS_CERT_FILE", "../../hack/dev/certs/client-worker.pem")
_ = os.Setenv("HATCHET_CLIENT_TLS_KEY_FILE", "../../hack/dev/certs/client-worker.key")
_ = os.Setenv("HATCHET_CLIENT_TLS_SERVER_NAME", "cluster")
}
+10 -11
View File
@@ -31,6 +31,8 @@ type adminClientImpl struct {
l *zerolog.Logger
v validator.Validator
ctx *contextLoader
}
func newAdmin(conn *grpc.ClientConn, opts *sharedClientOpts) AdminClient {
@@ -39,6 +41,7 @@ func newAdmin(conn *grpc.ClientConn, opts *sharedClientOpts) AdminClient {
tenantId: opts.tenantId,
l: opts.l,
v: opts.v,
ctx: opts.ctxLoader,
}
}
@@ -75,9 +78,8 @@ func (a *adminClientImpl) PutWorkflow(workflow *types.Workflow, fs ...PutOptFunc
return fmt.Errorf("could not get put opts: %w", err)
}
apiWorkflow, err := a.client.GetWorkflowByName(context.Background(), &admincontracts.GetWorkflowByNameRequest{
TenantId: a.tenantId,
Name: req.Opts.Name,
apiWorkflow, err := a.client.GetWorkflowByName(a.ctx.newContext(context.Background()), &admincontracts.GetWorkflowByNameRequest{
Name: req.Opts.Name,
})
shouldPut := opts.autoVersion
@@ -114,7 +116,7 @@ func (a *adminClientImpl) PutWorkflow(workflow *types.Workflow, fs ...PutOptFunc
}
if shouldPut {
_, err = a.client.PutWorkflow(context.Background(), req)
_, err = a.client.PutWorkflow(a.ctx.newContext(context.Background()), req)
if err != nil {
return fmt.Errorf("could not create workflow: %w", err)
@@ -159,9 +161,8 @@ func (a *adminClientImpl) ScheduleWorkflow(workflowName string, fs ...ScheduleOp
}
// get the workflow id from the name
workflow, err := a.client.GetWorkflowByName(context.Background(), &admincontracts.GetWorkflowByNameRequest{
TenantId: a.tenantId,
Name: workflowName,
workflow, err := a.client.GetWorkflowByName(a.ctx.newContext(context.Background()), &admincontracts.GetWorkflowByNameRequest{
Name: workflowName,
})
if err != nil {
@@ -180,8 +181,7 @@ func (a *adminClientImpl) ScheduleWorkflow(workflowName string, fs ...ScheduleOp
return err
}
_, err = a.client.ScheduleWorkflow(context.Background(), &admincontracts.ScheduleWorkflowRequest{
TenantId: a.tenantId,
_, err = a.client.ScheduleWorkflow(a.ctx.newContext(context.Background()), &admincontracts.ScheduleWorkflowRequest{
WorkflowId: workflow.Id,
Schedules: pbSchedules,
Input: string(inputBytes),
@@ -246,8 +246,7 @@ func (a *adminClientImpl) getPutRequest(workflow *types.Workflow) (*admincontrac
opts.Jobs = jobOpts
return &admincontracts.PutWorkflowRequest{
TenantId: a.tenantId,
Opts: opts,
Opts: opts,
}, nil
}
+14 -6
View File
@@ -45,6 +45,7 @@ type ClientOpts struct {
v validator.Validator
tls *tls.Config
hostPort string
token string
filesLoader filesLoaderFunc
initWorkflows bool
@@ -64,6 +65,7 @@ func defaultClientOpts() *ClientOpts {
return &ClientOpts{
tenantId: clientConfig.TenantId,
token: clientConfig.Token,
l: &logger,
v: validator.NewDefaultValidator(),
tls: clientConfig.TLSConfig,
@@ -101,9 +103,10 @@ func WithWorkflows(files []*types.Workflow) ClientOpt {
}
type sharedClientOpts struct {
tenantId string
l *zerolog.Logger
v validator.Validator
tenantId string
l *zerolog.Logger
v validator.Validator
ctxLoader *contextLoader
}
// New creates a new client instance.
@@ -119,6 +122,10 @@ func New(fs ...ClientOpt) (Client, error) {
return nil, fmt.Errorf("tls config is required")
}
if opts.token == "" {
return nil, fmt.Errorf("token is required")
}
opts.l.Debug().Msgf("connecting to %s with TLS server name %s", opts.hostPort, opts.tls.ServerName)
conn, err := grpc.Dial(
@@ -131,9 +138,10 @@ func New(fs ...ClientOpt) (Client, error) {
}
shared := &sharedClientOpts{
tenantId: opts.tenantId,
l: opts.l,
v: opts.v,
tenantId: opts.tenantId,
l: opts.l,
v: opts.v,
ctxLoader: newContextLoader(opts.token),
}
admin := newAdmin(conn, shared)
+26
View File
@@ -0,0 +1,26 @@
package client
import (
grpcMetadata "google.golang.org/grpc/metadata"
"context"
)
type contextLoader struct {
// The token
Token string
}
func newContextLoader(token string) *contextLoader {
return &contextLoader{
Token: token,
}
}
func (c *contextLoader) newContext(ctx context.Context) context.Context {
md := grpcMetadata.New(map[string]string{
"authorization": "Bearer " + c.Token,
})
return grpcMetadata.NewOutgoingContext(ctx, md)
}
+11 -10
View File
@@ -123,6 +123,8 @@ type dispatcherClientImpl struct {
l *zerolog.Logger
v validator.Validator
ctx *contextLoader
}
func newDispatcher(conn *grpc.ClientConn, opts *sharedClientOpts) DispatcherClient {
@@ -131,6 +133,7 @@ func newDispatcher(conn *grpc.ClientConn, opts *sharedClientOpts) DispatcherClie
tenantId: opts.tenantId,
l: opts.l,
v: opts.v,
ctx: opts.ctxLoader,
}
}
@@ -146,6 +149,8 @@ type actionListenerImpl struct {
l *zerolog.Logger
v validator.Validator
ctx *contextLoader
}
func (d *dispatcherClientImpl) newActionListener(ctx context.Context, req *GetActionListenerRequest) (*actionListenerImpl, error) {
@@ -155,8 +160,7 @@ func (d *dispatcherClientImpl) newActionListener(ctx context.Context, req *GetAc
}
// register the worker
resp, err := d.client.Register(ctx, &dispatchercontracts.WorkerRegisterRequest{
TenantId: d.tenantId,
resp, err := d.client.Register(d.ctx.newContext(ctx), &dispatchercontracts.WorkerRegisterRequest{
WorkerName: req.WorkerName,
Actions: req.Actions,
Services: req.Services,
@@ -169,8 +173,7 @@ func (d *dispatcherClientImpl) newActionListener(ctx context.Context, req *GetAc
d.l.Debug().Msgf("Registered worker with id: %s", resp.WorkerId)
// subscribe to the worker
listener, err := d.client.Listen(ctx, &dispatchercontracts.WorkerListenRequest{
TenantId: d.tenantId,
listener, err := d.client.Listen(d.ctx.newContext(ctx), &dispatchercontracts.WorkerListenRequest{
WorkerId: resp.WorkerId,
})
@@ -185,6 +188,7 @@ func (d *dispatcherClientImpl) newActionListener(ctx context.Context, req *GetAc
l: d.l,
v: d.v,
tenantId: d.tenantId,
ctx: d.ctx,
}, nil
}
@@ -279,8 +283,7 @@ func (a *actionListenerImpl) retrySubscribe(ctx context.Context) error {
for retries < DefaultActionListenerRetryCount {
time.Sleep(DefaultActionListenerRetryInterval)
listenClient, err := a.client.Listen(ctx, &dispatchercontracts.WorkerListenRequest{
TenantId: a.tenantId,
listenClient, err := a.client.Listen(a.ctx.newContext(ctx), &dispatchercontracts.WorkerListenRequest{
WorkerId: a.workerId,
})
@@ -300,9 +303,8 @@ func (a *actionListenerImpl) retrySubscribe(ctx context.Context) error {
func (a *actionListenerImpl) Unregister() error {
_, err := a.client.Unsubscribe(
context.Background(),
a.ctx.newContext(context.Background()),
&dispatchercontracts.WorkerUnsubscribeRequest{
TenantId: a.tenantId,
WorkerId: a.workerId,
},
)
@@ -343,8 +345,7 @@ func (d *dispatcherClientImpl) SendActionEvent(ctx context.Context, in *ActionEv
actionEventType = dispatchercontracts.ActionEventType_STEP_EVENT_TYPE_UNKNOWN
}
resp, err := d.client.SendActionEvent(ctx, &dispatchercontracts.ActionEvent{
TenantId: d.tenantId,
resp, err := d.client.SendActionEvent(d.ctx.newContext(ctx), &dispatchercontracts.ActionEvent{
WorkerId: in.WorkerId,
JobId: in.JobId,
JobRunId: in.JobRunId,
+4 -2
View File
@@ -24,6 +24,8 @@ type eventClientImpl struct {
l *zerolog.Logger
v validator.Validator
ctx *contextLoader
}
func newEvent(conn *grpc.ClientConn, opts *sharedClientOpts) EventClient {
@@ -32,6 +34,7 @@ func newEvent(conn *grpc.ClientConn, opts *sharedClientOpts) EventClient {
tenantId: opts.tenantId,
l: opts.l,
v: opts.v,
ctx: opts.ctxLoader,
}
}
@@ -42,8 +45,7 @@ func (a *eventClientImpl) Push(ctx context.Context, eventKey string, payload int
return err
}
_, err = a.client.Push(ctx, &eventcontracts.PushEventRequest{
TenantId: a.tenantId,
_, err = a.client.Push(a.ctx.newContext(ctx), &eventcontracts.PushEventRequest{
Key: eventKey,
Payload: string(payloadBytes),
EventTimestamp: timestamppb.Now(),
+25 -1
View File
@@ -2,7 +2,9 @@ package loader
import (
"fmt"
"net/url"
"path/filepath"
"strings"
"github.com/hatchet-dev/hatchet/internal/config/client"
"github.com/hatchet-dev/hatchet/internal/config/loader/loaderutils"
@@ -41,7 +43,21 @@ func LoadClientConfigFile(files ...[]byte) (*client.ClientConfigFile, error) {
}
func GetClientConfigFromConfigFile(cf *client.ClientConfigFile) (res *client.ClientConfig, err error) {
tlsConf, err := loaderutils.LoadClientTLSConfig(&cf.TLS)
tlsServerName := cf.TLS.TLSServerName
// if the tls server name is empty, parse the domain from the host:port
if tlsServerName == "" {
// parse the domain from the host:port
domain, err := parseDomain(cf.HostPort)
if err != nil {
return nil, fmt.Errorf("could not parse domain: %w", err)
}
tlsServerName = domain.Hostname()
}
tlsConf, err := loaderutils.LoadClientTLSConfig(&cf.TLS, tlsServerName)
if err != nil {
return nil, fmt.Errorf("could not load TLS config: %w", err)
@@ -50,5 +66,13 @@ func GetClientConfigFromConfigFile(cf *client.ClientConfigFile) (res *client.Cli
return &client.ClientConfig{
TenantId: cf.TenantId,
TLSConfig: tlsConf,
Token: cf.Token,
}, nil
}
func parseDomain(domain string) (*url.URL, error) {
if !strings.HasPrefix(domain, "http://") && !strings.HasPrefix(domain, "https://") {
domain = "https://" + domain
}
return url.Parse(domain)
}
@@ -0,0 +1,72 @@
/*
Warnings:
- The primary key for the `Action` table will be changed. If it partially fails, the table could be left without primary key constraint.
- The `refreshToken` column on the `UserOAuth` table would be dropped and recreated. This will lead to data loss if there is data in the column.
- A unique constraint covering the columns `[id]` on the table `Action` will be added. If there are existing duplicate values, this will fail.
- A unique constraint covering the columns `[tenantId,actionId]` on the table `Action` will be added. If there are existing duplicate values, this will fail.
- Added the required column `actionId` to the `Action` table without a default value. This is not possible if the table is not empty.
- Changed the type of `id` on the `Action` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required.
- Changed the type of `accessToken` on the `UserOAuth` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required.
- Changed the type of `A` on the `_ActionToWorker` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required.
*/
-- DropForeignKey
ALTER TABLE "Step" DROP CONSTRAINT "Step_actionId_tenantId_fkey";
-- DropForeignKey
ALTER TABLE "_ActionToWorker" DROP CONSTRAINT "_ActionToWorker_A_fkey";
-- DropIndex
DROP INDEX "Action_tenantId_id_key";
-- AlterTable
ALTER TABLE "Action" DROP CONSTRAINT "Action_pkey",
ADD COLUMN "actionId" TEXT NOT NULL,
DROP COLUMN "id",
ADD COLUMN "id" UUID NOT NULL,
ADD CONSTRAINT "Action_pkey" PRIMARY KEY ("id");
-- AlterTable
ALTER TABLE "UserOAuth" DROP COLUMN "accessToken",
ADD COLUMN "accessToken" BYTEA NOT NULL,
DROP COLUMN "refreshToken",
ADD COLUMN "refreshToken" BYTEA;
-- AlterTable
ALTER TABLE "_ActionToWorker" DROP COLUMN "A",
ADD COLUMN "A" UUID NOT NULL;
-- CreateTable
CREATE TABLE "APIToken" (
"id" UUID NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"expiresAt" TIMESTAMP(3),
"revoked" BOOLEAN NOT NULL DEFAULT false,
"name" TEXT,
"tenantId" UUID,
CONSTRAINT "APIToken_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "APIToken_id_key" ON "APIToken"("id");
-- CreateIndex
CREATE UNIQUE INDEX "Action_id_key" ON "Action"("id");
-- CreateIndex
CREATE UNIQUE INDEX "Action_tenantId_actionId_key" ON "Action"("tenantId", "actionId");
-- CreateIndex
CREATE UNIQUE INDEX "_ActionToWorker_AB_unique" ON "_ActionToWorker"("A", "B");
-- AddForeignKey
ALTER TABLE "APIToken" ADD CONSTRAINT "APIToken_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Step" ADD CONSTRAINT "Step_actionId_tenantId_fkey" FOREIGN KEY ("actionId", "tenantId") REFERENCES "Action"("actionId", "tenantId") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_ActionToWorker" ADD CONSTRAINT "_ActionToWorker_A_fkey" FOREIGN KEY ("A") REFERENCES "Action"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+27 -5
View File
@@ -53,10 +53,10 @@ model UserOAuth {
providerUserId String
// the oauth provider's access token
accessToken String
accessToken Bytes @db.ByteA
// the oauth provider's refresh token
refreshToken String?
refreshToken Bytes? @db.ByteA
// the oauth provider's expiry time
expiresAt DateTime?
@@ -117,6 +117,7 @@ model Tenant {
actions Action[]
services Service[]
invites TenantInviteLink[]
apiTokens APIToken[]
}
enum TenantMemberRole {
@@ -169,6 +170,25 @@ model TenantInviteLink {
role TenantMemberRole @default(OWNER)
}
model APIToken {
// base fields
id String @id @unique @default(uuid()) @db.Uuid
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt
// when it expires
expiresAt DateTime?
// whether the token has been revoked
revoked Boolean @default(false)
// an optional name for the token
name String?
tenant Tenant? @relation(fields: [tenantId], references: [id], onDelete: Cascade, onUpdate: Cascade)
tenantId String? @db.Uuid
}
// Event represents an event in the database.
model Event {
// base fields
@@ -385,7 +405,9 @@ model Job {
model Action {
// base fields
id String @id
id String @id @unique @default(uuid()) @db.Uuid
actionId String
// the action description
description String?
@@ -400,7 +422,7 @@ model Action {
workers Worker[]
// actions are unique per tenant
@@unique([tenantId, id])
@@unique([tenantId, actionId])
}
model Step {
@@ -422,7 +444,7 @@ model Step {
jobId String @db.Uuid
// an action id for the step
action Action @relation(fields: [actionId, tenantId], references: [id, tenantId])
action Action @relation(fields: [actionId, tenantId], references: [actionId, tenantId])
actionId String
timeout String?
+3 -3
View File
@@ -2,6 +2,6 @@
#
# Builds python auto-generated protobuf files
poetry run python -m grpc_tools.protoc --proto_path=../api-contracts/dispatcher --python_out=./hatchet --pyi_out=./hatchet --grpc_python_out=./hatchet dispatcher.proto
poetry run python -m grpc_tools.protoc --proto_path=../api-contracts/events --python_out=./hatchet --pyi_out=./hatchet --grpc_python_out=./hatchet events.proto
poetry run python -m grpc_tools.protoc --proto_path=../api-contracts/workflows --python_out=./hatchet --pyi_out=./hatchet --grpc_python_out=./hatchet workflows.proto
poetry run python -m grpc_tools.protoc --proto_path=../api-contracts/dispatcher --python_out=./hatchet_sdk --pyi_out=./hatchet_sdk --grpc_python_out=./hatchet_sdk dispatcher.proto
poetry run python -m grpc_tools.protoc --proto_path=../api-contracts/events --python_out=./hatchet_sdk --pyi_out=./hatchet_sdk --grpc_python_out=./hatchet_sdk events.proto
poetry run python -m grpc_tools.protoc --proto_path=../api-contracts/workflows --python_out=./hatchet_sdk --pyi_out=./hatchet_sdk --grpc_python_out=./hatchet_sdk workflows.proto
+19 -5
View File
@@ -1,4 +1,5 @@
# relative imports
from typing import Any
from .clients.admin import AdminClientImpl, new_admin
from .clients.events import EventClientImpl, new_event
from .clients.dispatcher import DispatcherClientImpl, new_dispatcher
@@ -53,14 +54,27 @@ def new_client(*opts_functions):
if config.host_port is None:
raise ValueError("Host and port are required")
root = open(config.tls_config.ca_file, "rb").read()
private_key = open(config.tls_config.key_file, "rb").read()
certificate_chain = open(config.tls_config.cert_file, "rb").read()
credentials : grpc.ChannelCredentials | None = None
# load channel credentials
if config.tls_config.tls_strategy == 'tls':
root : Any | None = None
if config.tls_config.ca_file:
root = open(config.tls_config.ca_file, "rb").read()
credentials = grpc.ssl_channel_credentials(root_certificates=root)
elif config.tls_config.tls_strategy == 'mtls':
root = open(config.tls_config.ca_file, "rb").read()
private_key = open(config.tls_config.key_file, "rb").read()
certificate_chain = open(config.tls_config.cert_file, "rb").read()
credentials = grpc.ssl_channel_credentials(root_certificates=root, private_key=private_key, certificate_chain=certificate_chain)
conn = grpc.secure_channel(
target=config.host_port,
credentials=grpc.ssl_channel_credentials(root_certificates=root, private_key=private_key, certificate_chain=certificate_chain),
credentials=credentials,
options=[('grpc.ssl_target_name_override', config.tls_config.server_name)],
)
+10 -12
View File
@@ -5,21 +5,19 @@ from ..workflows_pb2_grpc import WorkflowServiceStub
from ..workflows_pb2 import CreateWorkflowVersionOpts, ScheduleWorkflowRequest, PutWorkflowRequest, GetWorkflowByNameRequest, Workflow
from ..loader import ClientConfig
from ..semver import bump_minor_version
from ..metadata import get_metadata
def new_admin(conn, config: ClientConfig):
return AdminClientImpl(
client=WorkflowServiceStub(conn),
tenant_id=config.tenant_id,
# logger=shared_opts['logger'],
# validator=shared_opts['validator'],
token=config.token,
)
class AdminClientImpl:
def __init__(self, client : WorkflowServiceStub, tenant_id):
def __init__(self, client : WorkflowServiceStub, token):
self.client = client
self.tenant_id = tenant_id
# self.logger = logger
# self.validator = validator
self.token = token
def put_workflow(self, workflow: CreateWorkflowVersionOpts, auto_version: bool = False):
if workflow.version == "" and not auto_version:
@@ -31,9 +29,9 @@ class AdminClientImpl:
try:
existing_workflow : Workflow = self.client.GetWorkflowByName(
GetWorkflowByNameRequest(
tenant_id=self.tenant_id,
name=workflow.name,
)
),
metadata=get_metadata(self.token),
)
except grpc.RpcError as e:
if e.code() == grpc.StatusCode.NOT_FOUND:
@@ -63,9 +61,9 @@ class AdminClientImpl:
try:
self.client.PutWorkflow(
PutWorkflowRequest(
tenant_id=self.tenant_id,
opts=workflow,
)
),
metadata=get_metadata(self.token),
)
except grpc.RpcError as e:
raise ValueError(f"Could not create/update workflow: {e}")
@@ -76,6 +74,6 @@ class AdminClientImpl:
tenant_id=self.tenant_id,
workflow_id=workflow_id,
schedules=schedules,
))
), metadata=get_metadata(self.token))
except grpc.RpcError as e:
raise ValueError(f"gRPC error: {e}")
+15 -14
View File
@@ -8,13 +8,13 @@ from ..logger import logger
import json
import grpc
from typing import Callable, List, Union
from ..metadata import get_metadata
def new_dispatcher(conn, config: ClientConfig):
return DispatcherClientImpl(
client=DispatcherStub(conn),
tenant_id=config.tenant_id,
# logger=shared_opts['logger'],
# validator=shared_opts['validator'],
token=config.token,
)
class DispatcherClient:
@@ -60,9 +60,9 @@ START_STEP_RUN = 0
CANCEL_STEP_RUN = 1
class ActionListenerImpl(WorkerActionListener):
def __init__(self, client : DispatcherStub, tenant_id, worker_id):
def __init__(self, client : DispatcherStub, token, worker_id):
self.client = client
self.tenant_id = tenant_id
self.token = token
self.worker_id = worker_id
self.retries = 0
@@ -145,42 +145,43 @@ class ActionListenerImpl(WorkerActionListener):
time.sleep(DEFAULT_ACTION_LISTENER_RETRY_INTERVAL)
return self.client.Listen(WorkerListenRequest(
tenantId=self.tenant_id,
workerId=self.worker_id
), timeout=DEFAULT_ACTION_TIMEOUT)
),
timeout=DEFAULT_ACTION_TIMEOUT,
metadata=get_metadata(self.token),
)
def unregister(self):
try:
self.client.Unsubscribe(
WorkerUnsubscribeRequest(
tenantId=self.tenant_id,
workerId=self.worker_id
),
timeout=DEFAULT_REGISTER_TIMEOUT,
metadata=get_metadata(self.token),
)
except grpc.RpcError as e:
raise Exception(f"Failed to unsubscribe: {e}")
class DispatcherClientImpl(DispatcherClient):
def __init__(self, client : DispatcherStub, tenant_id):
def __init__(self, client : DispatcherStub, token):
self.client = client
self.tenant_id = tenant_id
self.token = token
# self.logger = logger
# self.validator = validator
def get_action_listener(self, req: GetActionListenerRequest) -> ActionListenerImpl:
# Register the worker
response : WorkerRegisterResponse = self.client.Register(WorkerRegisterRequest(
tenantId=self.tenant_id,
workerName=req.worker_name,
actions=req.actions,
services=req.services
), timeout=DEFAULT_REGISTER_TIMEOUT)
), timeout=DEFAULT_REGISTER_TIMEOUT, metadata=get_metadata(self.token))
return ActionListenerImpl(self.client, self.tenant_id, response.workerId)
return ActionListenerImpl(self.client, self.token, response.workerId)
def send_action_event(self, in_: ActionEvent):
response : ActionEventResponse = self.client.SendActionEvent(in_)
response : ActionEventResponse = self.client.SendActionEvent(in_, metadata=get_metadata(self.token),)
return response
+5 -9
View File
@@ -6,21 +6,18 @@ from ..loader import ClientConfig
import json
import grpc
from google.protobuf import timestamp_pb2
from ..metadata import get_metadata
def new_event(conn, config: ClientConfig):
return EventClientImpl(
client=EventsServiceStub(conn),
tenant_id=config.tenant_id,
# logger=shared_opts['logger'],
# validator=shared_opts['validator'],
token=config.token,
)
class EventClientImpl:
def __init__(self, client, tenant_id):
def __init__(self, client, token):
self.client = client
self.tenant_id = tenant_id
# self.logger = logger
# self.validator = validator
self.token = token
def push(self, event_key, payload):
try:
@@ -29,13 +26,12 @@ class EventClientImpl:
raise ValueError(f"Error encoding payload: {e}")
request = PushEventRequest(
tenantId=self.tenant_id,
key=event_key,
payload=payload_bytes,
eventTimestamp=timestamp_pb2.Timestamp().FromDatetime(datetime.datetime.now()),
)
try:
self.client.Push(request)
self.client.Push(request, metadata=get_metadata(self.token))
except grpc.RpcError as e:
raise ValueError(f"gRPC error: {e}")
+22 -22
View File
@@ -15,7 +15,7 @@ _sym_db = _symbol_database.Default()
from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10\x64ispatcher.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"`\n\x15WorkerRegisterRequest\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x12\n\nworkerName\x18\x02 \x01(\t\x12\x0f\n\x07\x61\x63tions\x18\x03 \x03(\t\x12\x10\n\x08services\x18\x04 \x03(\t\"P\n\x16WorkerRegisterResponse\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x10\n\x08workerId\x18\x02 \x01(\t\x12\x12\n\nworkerName\x18\x03 \x01(\t\"\xc1\x01\n\x0e\x41ssignedAction\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\r\n\x05jobId\x18\x02 \x01(\t\x12\x0f\n\x07jobName\x18\x03 \x01(\t\x12\x10\n\x08jobRunId\x18\x04 \x01(\t\x12\x0e\n\x06stepId\x18\x05 \x01(\t\x12\x11\n\tstepRunId\x18\x06 \x01(\t\x12\x10\n\x08\x61\x63tionId\x18\x07 \x01(\t\x12\x1f\n\nactionType\x18\x08 \x01(\x0e\x32\x0b.ActionType\x12\x15\n\ractionPayload\x18\t \x01(\t\"9\n\x13WorkerListenRequest\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x10\n\x08workerId\x18\x02 \x01(\t\">\n\x18WorkerUnsubscribeRequest\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x10\n\x08workerId\x18\x02 \x01(\t\"?\n\x19WorkerUnsubscribeResponse\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x10\n\x08workerId\x18\x02 \x01(\t\"\xf6\x01\n\x0b\x41\x63tionEvent\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x10\n\x08workerId\x18\x02 \x01(\t\x12\r\n\x05jobId\x18\x03 \x01(\t\x12\x10\n\x08jobRunId\x18\x04 \x01(\t\x12\x0e\n\x06stepId\x18\x05 \x01(\t\x12\x11\n\tstepRunId\x18\x06 \x01(\t\x12\x10\n\x08\x61\x63tionId\x18\x07 \x01(\t\x12\x32\n\x0e\x65ventTimestamp\x18\x08 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12#\n\teventType\x18\t \x01(\x0e\x32\x10.ActionEventType\x12\x14\n\x0c\x65ventPayload\x18\n \x01(\t\"9\n\x13\x41\x63tionEventResponse\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x10\n\x08workerId\x18\x02 \x01(\t*5\n\nActionType\x12\x12\n\x0eSTART_STEP_RUN\x10\x00\x12\x13\n\x0f\x43\x41NCEL_STEP_RUN\x10\x01*\x86\x01\n\x0f\x41\x63tionEventType\x12\x1b\n\x17STEP_EVENT_TYPE_UNKNOWN\x10\x00\x12\x1b\n\x17STEP_EVENT_TYPE_STARTED\x10\x01\x12\x1d\n\x19STEP_EVENT_TYPE_COMPLETED\x10\x02\x12\x1a\n\x16STEP_EVENT_TYPE_FAILED\x10\x03\x32\x81\x02\n\nDispatcher\x12=\n\x08Register\x12\x16.WorkerRegisterRequest\x1a\x17.WorkerRegisterResponse\"\x00\x12\x33\n\x06Listen\x12\x14.WorkerListenRequest\x1a\x0f.AssignedAction\"\x00\x30\x01\x12\x37\n\x0fSendActionEvent\x12\x0c.ActionEvent\x1a\x14.ActionEventResponse\"\x00\x12\x46\n\x0bUnsubscribe\x12\x19.WorkerUnsubscribeRequest\x1a\x1a.WorkerUnsubscribeResponse\"\x00\x42GZEgithub.com/hatchet-dev/hatchet/internal/services/dispatcher/contractsb\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10\x64ispatcher.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"N\n\x15WorkerRegisterRequest\x12\x12\n\nworkerName\x18\x01 \x01(\t\x12\x0f\n\x07\x61\x63tions\x18\x02 \x03(\t\x12\x10\n\x08services\x18\x03 \x03(\t\"P\n\x16WorkerRegisterResponse\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x10\n\x08workerId\x18\x02 \x01(\t\x12\x12\n\nworkerName\x18\x03 \x01(\t\"\xc1\x01\n\x0e\x41ssignedAction\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\r\n\x05jobId\x18\x02 \x01(\t\x12\x0f\n\x07jobName\x18\x03 \x01(\t\x12\x10\n\x08jobRunId\x18\x04 \x01(\t\x12\x0e\n\x06stepId\x18\x05 \x01(\t\x12\x11\n\tstepRunId\x18\x06 \x01(\t\x12\x10\n\x08\x61\x63tionId\x18\x07 \x01(\t\x12\x1f\n\nactionType\x18\x08 \x01(\x0e\x32\x0b.ActionType\x12\x15\n\ractionPayload\x18\t \x01(\t\"\'\n\x13WorkerListenRequest\x12\x10\n\x08workerId\x18\x01 \x01(\t\",\n\x18WorkerUnsubscribeRequest\x12\x10\n\x08workerId\x18\x01 \x01(\t\"?\n\x19WorkerUnsubscribeResponse\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x10\n\x08workerId\x18\x02 \x01(\t\"\xe4\x01\n\x0b\x41\x63tionEvent\x12\x10\n\x08workerId\x18\x01 \x01(\t\x12\r\n\x05jobId\x18\x02 \x01(\t\x12\x10\n\x08jobRunId\x18\x03 \x01(\t\x12\x0e\n\x06stepId\x18\x04 \x01(\t\x12\x11\n\tstepRunId\x18\x05 \x01(\t\x12\x10\n\x08\x61\x63tionId\x18\x06 \x01(\t\x12\x32\n\x0e\x65ventTimestamp\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12#\n\teventType\x18\x08 \x01(\x0e\x32\x10.ActionEventType\x12\x14\n\x0c\x65ventPayload\x18\t \x01(\t\"9\n\x13\x41\x63tionEventResponse\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x10\n\x08workerId\x18\x02 \x01(\t*5\n\nActionType\x12\x12\n\x0eSTART_STEP_RUN\x10\x00\x12\x13\n\x0f\x43\x41NCEL_STEP_RUN\x10\x01*\x86\x01\n\x0f\x41\x63tionEventType\x12\x1b\n\x17STEP_EVENT_TYPE_UNKNOWN\x10\x00\x12\x1b\n\x17STEP_EVENT_TYPE_STARTED\x10\x01\x12\x1d\n\x19STEP_EVENT_TYPE_COMPLETED\x10\x02\x12\x1a\n\x16STEP_EVENT_TYPE_FAILED\x10\x03\x32\x81\x02\n\nDispatcher\x12=\n\x08Register\x12\x16.WorkerRegisterRequest\x1a\x17.WorkerRegisterResponse\"\x00\x12\x33\n\x06Listen\x12\x14.WorkerListenRequest\x1a\x0f.AssignedAction\"\x00\x30\x01\x12\x37\n\x0fSendActionEvent\x12\x0c.ActionEvent\x1a\x14.ActionEventResponse\"\x00\x12\x46\n\x0bUnsubscribe\x12\x19.WorkerUnsubscribeRequest\x1a\x1a.WorkerUnsubscribeResponse\"\x00\x42GZEgithub.com/hatchet-dev/hatchet/internal/services/dispatcher/contractsb\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -23,26 +23,26 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'dispatcher_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals['DESCRIPTOR']._options = None
_globals['DESCRIPTOR']._serialized_options = b'ZEgithub.com/hatchet-dev/hatchet/internal/services/dispatcher/contracts'
_globals['_ACTIONTYPE']._serialized_start=925
_globals['_ACTIONTYPE']._serialized_end=978
_globals['_ACTIONEVENTTYPE']._serialized_start=981
_globals['_ACTIONEVENTTYPE']._serialized_end=1115
_globals['_ACTIONTYPE']._serialized_start=853
_globals['_ACTIONTYPE']._serialized_end=906
_globals['_ACTIONEVENTTYPE']._serialized_start=909
_globals['_ACTIONEVENTTYPE']._serialized_end=1043
_globals['_WORKERREGISTERREQUEST']._serialized_start=53
_globals['_WORKERREGISTERREQUEST']._serialized_end=149
_globals['_WORKERREGISTERRESPONSE']._serialized_start=151
_globals['_WORKERREGISTERRESPONSE']._serialized_end=231
_globals['_ASSIGNEDACTION']._serialized_start=234
_globals['_ASSIGNEDACTION']._serialized_end=427
_globals['_WORKERLISTENREQUEST']._serialized_start=429
_globals['_WORKERLISTENREQUEST']._serialized_end=486
_globals['_WORKERUNSUBSCRIBEREQUEST']._serialized_start=488
_globals['_WORKERUNSUBSCRIBEREQUEST']._serialized_end=550
_globals['_WORKERUNSUBSCRIBERESPONSE']._serialized_start=552
_globals['_WORKERUNSUBSCRIBERESPONSE']._serialized_end=615
_globals['_ACTIONEVENT']._serialized_start=618
_globals['_ACTIONEVENT']._serialized_end=864
_globals['_ACTIONEVENTRESPONSE']._serialized_start=866
_globals['_ACTIONEVENTRESPONSE']._serialized_end=923
_globals['_DISPATCHER']._serialized_start=1118
_globals['_DISPATCHER']._serialized_end=1375
_globals['_WORKERREGISTERREQUEST']._serialized_end=131
_globals['_WORKERREGISTERRESPONSE']._serialized_start=133
_globals['_WORKERREGISTERRESPONSE']._serialized_end=213
_globals['_ASSIGNEDACTION']._serialized_start=216
_globals['_ASSIGNEDACTION']._serialized_end=409
_globals['_WORKERLISTENREQUEST']._serialized_start=411
_globals['_WORKERLISTENREQUEST']._serialized_end=450
_globals['_WORKERUNSUBSCRIBEREQUEST']._serialized_start=452
_globals['_WORKERUNSUBSCRIBEREQUEST']._serialized_end=496
_globals['_WORKERUNSUBSCRIBERESPONSE']._serialized_start=498
_globals['_WORKERUNSUBSCRIBERESPONSE']._serialized_end=561
_globals['_ACTIONEVENT']._serialized_start=564
_globals['_ACTIONEVENT']._serialized_end=792
_globals['_ACTIONEVENTRESPONSE']._serialized_start=794
_globals['_ACTIONEVENTRESPONSE']._serialized_end=851
_globals['_DISPATCHER']._serialized_start=1046
_globals['_DISPATCHER']._serialized_end=1303
# @@protoc_insertion_point(module_scope)
+8 -16
View File
@@ -26,16 +26,14 @@ STEP_EVENT_TYPE_COMPLETED: ActionEventType
STEP_EVENT_TYPE_FAILED: ActionEventType
class WorkerRegisterRequest(_message.Message):
__slots__ = ("tenantId", "workerName", "actions", "services")
TENANTID_FIELD_NUMBER: _ClassVar[int]
__slots__ = ("workerName", "actions", "services")
WORKERNAME_FIELD_NUMBER: _ClassVar[int]
ACTIONS_FIELD_NUMBER: _ClassVar[int]
SERVICES_FIELD_NUMBER: _ClassVar[int]
tenantId: str
workerName: str
actions: _containers.RepeatedScalarFieldContainer[str]
services: _containers.RepeatedScalarFieldContainer[str]
def __init__(self, tenantId: _Optional[str] = ..., workerName: _Optional[str] = ..., actions: _Optional[_Iterable[str]] = ..., services: _Optional[_Iterable[str]] = ...) -> None: ...
def __init__(self, workerName: _Optional[str] = ..., actions: _Optional[_Iterable[str]] = ..., services: _Optional[_Iterable[str]] = ...) -> None: ...
class WorkerRegisterResponse(_message.Message):
__slots__ = ("tenantId", "workerId", "workerName")
@@ -70,20 +68,16 @@ class AssignedAction(_message.Message):
def __init__(self, tenantId: _Optional[str] = ..., jobId: _Optional[str] = ..., jobName: _Optional[str] = ..., jobRunId: _Optional[str] = ..., stepId: _Optional[str] = ..., stepRunId: _Optional[str] = ..., actionId: _Optional[str] = ..., actionType: _Optional[_Union[ActionType, str]] = ..., actionPayload: _Optional[str] = ...) -> None: ...
class WorkerListenRequest(_message.Message):
__slots__ = ("tenantId", "workerId")
TENANTID_FIELD_NUMBER: _ClassVar[int]
__slots__ = ("workerId",)
WORKERID_FIELD_NUMBER: _ClassVar[int]
tenantId: str
workerId: str
def __init__(self, tenantId: _Optional[str] = ..., workerId: _Optional[str] = ...) -> None: ...
def __init__(self, workerId: _Optional[str] = ...) -> None: ...
class WorkerUnsubscribeRequest(_message.Message):
__slots__ = ("tenantId", "workerId")
TENANTID_FIELD_NUMBER: _ClassVar[int]
__slots__ = ("workerId",)
WORKERID_FIELD_NUMBER: _ClassVar[int]
tenantId: str
workerId: str
def __init__(self, tenantId: _Optional[str] = ..., workerId: _Optional[str] = ...) -> None: ...
def __init__(self, workerId: _Optional[str] = ...) -> None: ...
class WorkerUnsubscribeResponse(_message.Message):
__slots__ = ("tenantId", "workerId")
@@ -94,8 +88,7 @@ class WorkerUnsubscribeResponse(_message.Message):
def __init__(self, tenantId: _Optional[str] = ..., workerId: _Optional[str] = ...) -> None: ...
class ActionEvent(_message.Message):
__slots__ = ("tenantId", "workerId", "jobId", "jobRunId", "stepId", "stepRunId", "actionId", "eventTimestamp", "eventType", "eventPayload")
TENANTID_FIELD_NUMBER: _ClassVar[int]
__slots__ = ("workerId", "jobId", "jobRunId", "stepId", "stepRunId", "actionId", "eventTimestamp", "eventType", "eventPayload")
WORKERID_FIELD_NUMBER: _ClassVar[int]
JOBID_FIELD_NUMBER: _ClassVar[int]
JOBRUNID_FIELD_NUMBER: _ClassVar[int]
@@ -105,7 +98,6 @@ class ActionEvent(_message.Message):
EVENTTIMESTAMP_FIELD_NUMBER: _ClassVar[int]
EVENTTYPE_FIELD_NUMBER: _ClassVar[int]
EVENTPAYLOAD_FIELD_NUMBER: _ClassVar[int]
tenantId: str
workerId: str
jobId: str
jobRunId: str
@@ -115,7 +107,7 @@ class ActionEvent(_message.Message):
eventTimestamp: _timestamp_pb2.Timestamp
eventType: ActionEventType
eventPayload: str
def __init__(self, tenantId: _Optional[str] = ..., workerId: _Optional[str] = ..., jobId: _Optional[str] = ..., jobRunId: _Optional[str] = ..., stepId: _Optional[str] = ..., stepRunId: _Optional[str] = ..., actionId: _Optional[str] = ..., eventTimestamp: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., eventType: _Optional[_Union[ActionEventType, str]] = ..., eventPayload: _Optional[str] = ...) -> None: ...
def __init__(self, workerId: _Optional[str] = ..., jobId: _Optional[str] = ..., jobRunId: _Optional[str] = ..., stepId: _Optional[str] = ..., stepRunId: _Optional[str] = ..., actionId: _Optional[str] = ..., eventTimestamp: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., eventType: _Optional[_Union[ActionEventType, str]] = ..., eventPayload: _Optional[str] = ...) -> None: ...
class ActionEventResponse(_message.Message):
__slots__ = ("tenantId", "workerId")
@@ -2,7 +2,6 @@
"""Client and server classes corresponding to protobuf-defined services."""
import grpc
# import dispatcher_pb2 as dispatcher__pb2
from . import dispatcher_pb2 as dispatcher__pb2
class DispatcherStub(object):
+10 -10
View File
@@ -15,7 +15,7 @@ _sym_db = _symbol_database.Default()
from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0c\x65vents.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"|\n\x05\x45vent\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x0f\n\x07\x65ventId\x18\x02 \x01(\t\x12\x0b\n\x03key\x18\x03 \x01(\t\x12\x0f\n\x07payload\x18\x04 \x01(\t\x12\x32\n\x0e\x65ventTimestamp\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"v\n\x10PushEventRequest\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\t\x12\x0f\n\x07payload\x18\x03 \x01(\t\x12\x32\n\x0e\x65ventTimestamp\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"A\n\x10ListEventRequest\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x0e\n\x06offset\x18\x02 \x01(\x05\x12\x0b\n\x03key\x18\x03 \x01(\t\"+\n\x11ListEventResponse\x12\x16\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x06.Event\"7\n\x12ReplayEventRequest\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x0f\n\x07\x65ventId\x18\x02 \x01(\t2\x99\x01\n\rEventsService\x12#\n\x04Push\x12\x11.PushEventRequest\x1a\x06.Event\"\x00\x12/\n\x04List\x12\x11.ListEventRequest\x1a\x12.ListEventResponse\"\x00\x12\x32\n\x11ReplaySingleEvent\x12\x13.ReplayEventRequest\x1a\x06.Event\"\x00\x42GZEgithub.com/hatchet-dev/hatchet/internal/services/dispatcher/contractsb\x06proto3')
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0c\x65vents.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"|\n\x05\x45vent\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x0f\n\x07\x65ventId\x18\x02 \x01(\t\x12\x0b\n\x03key\x18\x03 \x01(\t\x12\x0f\n\x07payload\x18\x04 \x01(\t\x12\x32\n\x0e\x65ventTimestamp\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"d\n\x10PushEventRequest\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x0f\n\x07payload\x18\x02 \x01(\t\x12\x32\n\x0e\x65ventTimestamp\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"/\n\x10ListEventRequest\x12\x0e\n\x06offset\x18\x01 \x01(\x05\x12\x0b\n\x03key\x18\x02 \x01(\t\"+\n\x11ListEventResponse\x12\x16\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x06.Event\"%\n\x12ReplayEventRequest\x12\x0f\n\x07\x65ventId\x18\x01 \x01(\t2\x99\x01\n\rEventsService\x12#\n\x04Push\x12\x11.PushEventRequest\x1a\x06.Event\"\x00\x12/\n\x04List\x12\x11.ListEventRequest\x1a\x12.ListEventResponse\"\x00\x12\x32\n\x11ReplaySingleEvent\x12\x13.ReplayEventRequest\x1a\x06.Event\"\x00\x42GZEgithub.com/hatchet-dev/hatchet/internal/services/dispatcher/contractsb\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -26,13 +26,13 @@ if _descriptor._USE_C_DESCRIPTORS == False:
_globals['_EVENT']._serialized_start=49
_globals['_EVENT']._serialized_end=173
_globals['_PUSHEVENTREQUEST']._serialized_start=175
_globals['_PUSHEVENTREQUEST']._serialized_end=293
_globals['_LISTEVENTREQUEST']._serialized_start=295
_globals['_LISTEVENTREQUEST']._serialized_end=360
_globals['_LISTEVENTRESPONSE']._serialized_start=362
_globals['_LISTEVENTRESPONSE']._serialized_end=405
_globals['_REPLAYEVENTREQUEST']._serialized_start=407
_globals['_REPLAYEVENTREQUEST']._serialized_end=462
_globals['_EVENTSSERVICE']._serialized_start=465
_globals['_EVENTSSERVICE']._serialized_end=618
_globals['_PUSHEVENTREQUEST']._serialized_end=275
_globals['_LISTEVENTREQUEST']._serialized_start=277
_globals['_LISTEVENTREQUEST']._serialized_end=324
_globals['_LISTEVENTRESPONSE']._serialized_start=326
_globals['_LISTEVENTRESPONSE']._serialized_end=369
_globals['_REPLAYEVENTREQUEST']._serialized_start=371
_globals['_REPLAYEVENTREQUEST']._serialized_end=408
_globals['_EVENTSSERVICE']._serialized_start=411
_globals['_EVENTSSERVICE']._serialized_end=564
# @@protoc_insertion_point(module_scope)
+6 -12
View File
@@ -21,26 +21,22 @@ class Event(_message.Message):
def __init__(self, tenantId: _Optional[str] = ..., eventId: _Optional[str] = ..., key: _Optional[str] = ..., payload: _Optional[str] = ..., eventTimestamp: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ...
class PushEventRequest(_message.Message):
__slots__ = ("tenantId", "key", "payload", "eventTimestamp")
TENANTID_FIELD_NUMBER: _ClassVar[int]
__slots__ = ("key", "payload", "eventTimestamp")
KEY_FIELD_NUMBER: _ClassVar[int]
PAYLOAD_FIELD_NUMBER: _ClassVar[int]
EVENTTIMESTAMP_FIELD_NUMBER: _ClassVar[int]
tenantId: str
key: str
payload: str
eventTimestamp: _timestamp_pb2.Timestamp
def __init__(self, tenantId: _Optional[str] = ..., key: _Optional[str] = ..., payload: _Optional[str] = ..., eventTimestamp: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ...
def __init__(self, key: _Optional[str] = ..., payload: _Optional[str] = ..., eventTimestamp: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ...
class ListEventRequest(_message.Message):
__slots__ = ("tenantId", "offset", "key")
TENANTID_FIELD_NUMBER: _ClassVar[int]
__slots__ = ("offset", "key")
OFFSET_FIELD_NUMBER: _ClassVar[int]
KEY_FIELD_NUMBER: _ClassVar[int]
tenantId: str
offset: int
key: str
def __init__(self, tenantId: _Optional[str] = ..., offset: _Optional[int] = ..., key: _Optional[str] = ...) -> None: ...
def __init__(self, offset: _Optional[int] = ..., key: _Optional[str] = ...) -> None: ...
class ListEventResponse(_message.Message):
__slots__ = ("events",)
@@ -49,9 +45,7 @@ class ListEventResponse(_message.Message):
def __init__(self, events: _Optional[_Iterable[_Union[Event, _Mapping]]] = ...) -> None: ...
class ReplayEventRequest(_message.Message):
__slots__ = ("tenantId", "eventId")
TENANTID_FIELD_NUMBER: _ClassVar[int]
__slots__ = ("eventId",)
EVENTID_FIELD_NUMBER: _ClassVar[int]
tenantId: str
eventId: str
def __init__(self, tenantId: _Optional[str] = ..., eventId: _Optional[str] = ...) -> None: ...
def __init__(self, eventId: _Optional[str] = ...) -> None: ...
@@ -2,7 +2,6 @@
"""Client and server classes corresponding to protobuf-defined services."""
import grpc
# import events_pb2 as events__pb2
from . import events_pb2 as events__pb2
class EventsServiceStub(object):
+20 -7
View File
@@ -3,17 +3,19 @@ import yaml
from typing import Any, Optional, Dict
class ClientTLSConfig:
def __init__(self, cert_file: str, key_file: str, ca_file: str, server_name: str):
def __init__(self, tls_strategy: str, cert_file: str, key_file: str, ca_file: str, server_name: str):
self.tls_strategy = tls_strategy
self.cert_file = cert_file
self.key_file = key_file
self.ca_file = ca_file
self.server_name = server_name
class ClientConfig:
def __init__(self, tenant_id: str, tls_config: ClientTLSConfig, host_port: str="localhost:7070"):
def __init__(self, tenant_id: str, tls_config: ClientTLSConfig, token: str, host_port: str="localhost:7070"):
self.tenant_id = tenant_id
self.tls_config = tls_config
self.host_port = host_port
self.token = token
class ConfigLoader:
def __init__(self, directory: str):
@@ -30,20 +32,31 @@ class ConfigLoader:
if os.path.exists(config_file_path):
with open(config_file_path, 'r') as file:
config_data = yaml.safe_load(file)
tls_config = self._load_tls_config(config_data['tls'])
tenant_id = config_data['tenantId'] if 'tenantId' in config_data else self._get_env_var('HATCHET_CLIENT_TENANT_ID')
host_port = config_data['hostPort'] if 'hostPort' in config_data else self._get_env_var('HATCHET_CLIENT_HOST_PORT')
token = config_data['token'] if 'token' in config_data else self._get_env_var('HATCHET_CLIENT_TOKEN')
tls_config = self._load_tls_config(config_data['tls'], host_port)
return ClientConfig(tenant_id, tls_config, host_port)
return ClientConfig(tenant_id, tls_config, token, host_port)
def _load_tls_config(self, tls_data: Dict, host_port) -> ClientTLSConfig:
tls_strategy = tls_data['tlsStrategy'] if 'tlsStrategy' in tls_data else self._get_env_var('HATCHET_CLIENT_TLS_STRATEGY')
if not tls_strategy:
tls_strategy = 'tls'
def _load_tls_config(self, tls_data: Dict) -> ClientTLSConfig:
cert_file = tls_data['tlsCertFile'] if 'tlsCertFile' in tls_data else self._get_env_var('HATCHET_CLIENT_TLS_CERT_FILE')
key_file = tls_data['tlsKeyFile'] if 'tlsKeyFile' in tls_data else self._get_env_var('HATCHET_CLIENT_TLS_KEY_FILE')
ca_file = tls_data['tlsRootCAFile'] if 'tlsRootCAFile' in tls_data else self._get_env_var('HATCHET_CLIENT_TLS_ROOT_CA_FILE')
server_name = tls_data['tlsServerName'] if 'tlsServerName' in tls_data else self._get_env_var('HATCHET_CLIENT_TLS_SERVER_NAME')
return ClientTLSConfig(cert_file, key_file, ca_file, server_name)
# if server_name is not set, use the host from the host_port
if not server_name:
server_name = host_port.split(':')[0]
return ClientTLSConfig(tls_strategy, cert_file, key_file, ca_file, server_name)
@staticmethod
def _get_env_var(env_var: str, default: Optional[str] = None) -> str:

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