mirror of https://github.com/dapr/go-sdk.git
				
				
				
			Implement distributed scheduler building block (#562)
* feat: add jobs/scheduling api (with validation override) Signed-off-by: mikeee <hey@mike.ee> * chore: fix deps Signed-off-by: mikeee <hey@mike.ee> * fix: use cli fix Signed-off-by: mikeee <hey@mike.ee> * fix: ci artifact path set for cli build Signed-off-by: mikeee <hey@mike.ee> * chore: remove sidecar step Signed-off-by: mikeee <hey@mike.ee> * chore: revert changes to other examples Signed-off-by: mikeee <hey@mike.ee> --------- Signed-off-by: mikeee <hey@mike.ee> Signed-off-by: Mike Nguyen <hey@mike.ee>
This commit is contained in:
		
							parent
							
								
									01c0f3154f
								
							
						
					
					
						commit
						967570515b
					
				| 
						 | 
				
			
			@ -23,6 +23,8 @@ jobs:
 | 
			
		|||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
        with:
 | 
			
		||||
          submodules: recursive
 | 
			
		||||
 | 
			
		||||
      - name: Setup
 | 
			
		||||
        uses: actions/setup-go@v5
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,10 +39,12 @@ jobs:
 | 
			
		|||
    outputs:
 | 
			
		||||
      DAPR_INSTALL_URL: ${{ env.DAPR_INSTALL_URL }}
 | 
			
		||||
      DAPR_CLI_VER: ${{ steps.outputs.outputs.DAPR_CLI_VER }}
 | 
			
		||||
      DAPR_RUNTIME_VER: ${{ steps.outputs.outputs.DAPR_RUNTIME_VER }}
 | 
			
		||||
      DAPR_CLI_REF: ${{ steps.outputs.outputs.DAPR_CLI_REF }}
 | 
			
		||||
      DAPR_RUNTIME_VER: 1.14.0-rc.2
 | 
			
		||||
      CHECKOUT_REPO: ${{ steps.outputs.outputs.CHECKOUT_REPO }}
 | 
			
		||||
      CHECKOUT_REF: ${{ steps.outputs.outputs.CHECKOUT_REF }}
 | 
			
		||||
      DAPR_REF: ${{ steps.outputs.outputs.DAPR_REF }}
 | 
			
		||||
      GITHUB_SHA: ${{ steps.outputs.outputs.GITHUB_SHA }}
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Parse repository_dispatch payload
 | 
			
		||||
        if: github.event_name == 'repository_dispatch'
 | 
			
		||||
| 
						 | 
				
			
			@ -79,9 +81,6 @@ jobs:
 | 
			
		|||
          echo "DAPR_CLI_VER=$CLI_VERSION" >> $GITHUB_ENV
 | 
			
		||||
          echo "Found $CLI_VERSION"
 | 
			
		||||
 | 
			
		||||
      - name: Set up Dapr CLI
 | 
			
		||||
        run: wget -q ${{ env.DAPR_INSTALL_URL }} -O - | /bin/bash -s ${{ env.DAPR_CLI_VER }}
 | 
			
		||||
 | 
			
		||||
      - name: Checkout Dapr CLI repo to override dapr command.
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
        if: env.DAPR_CLI_REF != ''
 | 
			
		||||
| 
						 | 
				
			
			@ -105,17 +104,19 @@ jobs:
 | 
			
		|||
          cd cli
 | 
			
		||||
          make
 | 
			
		||||
          mkdir -p $HOME/artifacts/$GITHUB_SHA/
 | 
			
		||||
          sudo cp dist/linux_amd64/release/dapr $HOME/artifacts/$GITHUB_SHA/dapr
 | 
			
		||||
          sudo cp dist/linux_amd64/release/dapr ~/artifacts/$GITHUB_SHA/dapr
 | 
			
		||||
          echo "artifactPath=~/artifacts/$GITHUB_SHA/" >> $GITHUB_ENV
 | 
			
		||||
          echo "DAPR_CLI_REF=$DAPR_CLI_REF" >> $GITHUB_ENV
 | 
			
		||||
 | 
			
		||||
      - name: Build daprd and placement with referenced commit.
 | 
			
		||||
      - name: Build dapr
 | 
			
		||||
        if: env.DAPR_REF != ''
 | 
			
		||||
        run: |
 | 
			
		||||
          echo "artifactPath=~/artifacts/$GITHUB_SHA/" >> $GITHUB_ENV
 | 
			
		||||
          cd dapr_runtime
 | 
			
		||||
          make
 | 
			
		||||
          mkdir -p $HOME/artifacts/$GITHUB_SHA/
 | 
			
		||||
          cp dist/linux_amd64/release/daprd $HOME/artifacts/$GITHUB_SHA/daprd
 | 
			
		||||
          cp dist/linux_amd64/release/placement $HOME/artifacts/$GITHUB_SHA/placement
 | 
			
		||||
          echo "artifactPath=~/artifacts/$GITHUB_SHA/" >> $GITHUB_ENV
 | 
			
		||||
          cp ./dist/linux_amd64/release/* ~/artifacts/$GITHUB_SHA/
 | 
			
		||||
 | 
			
		||||
      - name: Upload dapr-artifacts
 | 
			
		||||
        uses: actions/upload-artifact@v4
 | 
			
		||||
| 
						 | 
				
			
			@ -132,10 +133,12 @@ jobs:
 | 
			
		|||
        run: |
 | 
			
		||||
          echo "DAPR_INSTALL_URL=$DAPR_INSTALL_URL"
 | 
			
		||||
          echo "DAPR_CLI_VER=$DAPR_CLI_VER" >> "$GITHUB_OUTPUT"
 | 
			
		||||
          echo "DAPR_CLI_REF=$DAPR_CLI_REF" >> "$GITHUB_OUTPUT"
 | 
			
		||||
          echo "DAPR_RUNTIME_VER=$DAPR_RUNTIME_VER" >> "$GITHUB_OUTPUT"
 | 
			
		||||
          echo "CHECKOUT_REPO=$CHECKOUT_REPO" >> "$GITHUB_OUTPUT"
 | 
			
		||||
          echo "CHECKOUT_REF=$CHECKOUT_REF" >> "$GITHUB_OUTPUT"
 | 
			
		||||
          echo "DAPR_REF=$DAPR_REF" >> "$GITHUB_OUTPUT"
 | 
			
		||||
          echo "GITHUB_SHA=$GITHUB_SHA" >> "$GITHUB_OUTPUT"
 | 
			
		||||
 | 
			
		||||
  validate-example:
 | 
			
		||||
    needs: setup
 | 
			
		||||
| 
						 | 
				
			
			@ -148,10 +151,11 @@ jobs:
 | 
			
		|||
      DAPR_INSTALL_URL: ${{ needs.setup.outputs.DAPR_INSTALL_URL }}
 | 
			
		||||
      DAPR_CLI_VER: ${{ needs.setup.outputs.DAPR_CLI_VER }}
 | 
			
		||||
      DAPR_RUNTIME_VER: 1.14.0-rc.2
 | 
			
		||||
      DAPR_CLI_REF: ${{ github.event.inputs.daprcli_commit }}
 | 
			
		||||
      DAPR_REF: ${{ github.event.inputs.daprdapr_commit }}
 | 
			
		||||
      DAPR_CLI_REF: ${{ needs.setup.outputs.DAPR_CLI_REF }}
 | 
			
		||||
      DAPR_REF: ${{ needs.setup.outputs.DAPR_REF }}
 | 
			
		||||
      CHECKOUT_REPO: ${{ needs.setup.outputs.CHECKOUT_REPO }}
 | 
			
		||||
      CHECKOUT_REF: ${{ needs.setup.outputs.CHECKOUT_REF }}
 | 
			
		||||
      GITHUB_SHA: ${{ needs.setup.outputs.GITHUB_SHA }}
 | 
			
		||||
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
| 
						 | 
				
			
			@ -161,6 +165,7 @@ jobs:
 | 
			
		|||
            "actor",
 | 
			
		||||
            "configuration",
 | 
			
		||||
            "crypto",
 | 
			
		||||
            "dist-scheduler",
 | 
			
		||||
            "grpc-service",
 | 
			
		||||
            "hello-world",
 | 
			
		||||
            "pubsub",
 | 
			
		||||
| 
						 | 
				
			
			@ -187,7 +192,11 @@ jobs:
 | 
			
		|||
        uses: actions/download-artifact@v4
 | 
			
		||||
        with:
 | 
			
		||||
          name: dapr-artifacts
 | 
			
		||||
          path: $HOME/artifacts/$GITHUB_SHA/
 | 
			
		||||
          path: ~/artifacts/${{ env.GITHUB_SHA }}/
 | 
			
		||||
 | 
			
		||||
      - name: Display artifacts downloaded
 | 
			
		||||
        if: env.DAPR_CLI_REF != '' || env.DAPR_REF != ''
 | 
			
		||||
        run: ls ~/artifacts/$GITHUB_SHA/
 | 
			
		||||
 | 
			
		||||
      - name: Set up Go
 | 
			
		||||
        id: setup-go
 | 
			
		||||
| 
						 | 
				
			
			@ -196,6 +205,7 @@ jobs:
 | 
			
		|||
          go-version-file: "go.mod"
 | 
			
		||||
 | 
			
		||||
      - name: Set up Dapr CLI
 | 
			
		||||
        if: env.DAPR_CLI_VER != ''
 | 
			
		||||
        run: wget -q ${{ env.DAPR_INSTALL_URL }} -O - | /bin/bash -s ${{ env.DAPR_CLI_VER }}
 | 
			
		||||
 | 
			
		||||
      - name: Override dapr cli with referenced commit.
 | 
			
		||||
| 
						 | 
				
			
			@ -208,13 +218,15 @@ jobs:
 | 
			
		|||
          dapr uninstall --all
 | 
			
		||||
          dapr init --runtime-version ${{ env.DAPR_RUNTIME_VER }}
 | 
			
		||||
 | 
			
		||||
      - name: Override daprd and placement service with referenced commit.
 | 
			
		||||
      - name: Print scheduler logs
 | 
			
		||||
        run: |
 | 
			
		||||
          docker logs dapr_scheduler
 | 
			
		||||
 | 
			
		||||
      - name: Override daprd with referenced commit.
 | 
			
		||||
        if: env.DAPR_REF != ''
 | 
			
		||||
        run: |
 | 
			
		||||
          mkdir -p $HOME/.dapr/bin/
 | 
			
		||||
          cp $HOME/artifacts/$GITHUB_SHA/daprd $HOME/.dapr/bin/daprd
 | 
			
		||||
          docker stop dapr_placement
 | 
			
		||||
          $HOME/artifacts/$GITHUB_SHA/placement --healthz-port 9091 &
 | 
			
		||||
 | 
			
		||||
      - name: Set up Python ${{ env.PYTHON_VER }}
 | 
			
		||||
        uses: actions/setup-python@v5
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -250,6 +250,15 @@ type Client interface {
 | 
			
		|||
	// RaiseEventWorkflowBeta1 raises an event for a workflow.
 | 
			
		||||
	RaiseEventWorkflowBeta1(ctx context.Context, req *RaiseEventWorkflowRequest) error
 | 
			
		||||
 | 
			
		||||
	// ScheduleJobAlpha1 creates and schedules a job.
 | 
			
		||||
	ScheduleJobAlpha1(ctx context.Context, req *Job) error
 | 
			
		||||
 | 
			
		||||
	// GetJobAlpha1 returns a scheduled job.
 | 
			
		||||
	GetJobAlpha1(ctx context.Context, name string) (*Job, error)
 | 
			
		||||
 | 
			
		||||
	// DeleteJobAlpha1 deletes a scheduled job.
 | 
			
		||||
	DeleteJobAlpha1(ctx context.Context, name string) error
 | 
			
		||||
 | 
			
		||||
	// GrpcClient returns the base grpc client if grpc is used and nil otherwise
 | 
			
		||||
	GrpcClient() pb.DaprClient
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -557,6 +557,33 @@ func (s *testDaprServer) RaiseEventWorkflowBeta1(ctx context.Context, in *pb.Rai
 | 
			
		|||
	return &emptypb.Empty{}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *testDaprServer) ScheduleJobAlpha1(ctx context.Context, in *pb.ScheduleJobRequest) (*pb.ScheduleJobResponse, error) {
 | 
			
		||||
	return &pb.ScheduleJobResponse{}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *testDaprServer) GetJobAlpha1(ctx context.Context, in *pb.GetJobRequest) (*pb.GetJobResponse, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		schedule        = "@every 10s"
 | 
			
		||||
		dueTime         = "10s"
 | 
			
		||||
		repeats  uint32 = 4
 | 
			
		||||
		ttl             = "10s"
 | 
			
		||||
	)
 | 
			
		||||
	return &pb.GetJobResponse{
 | 
			
		||||
		Job: &pb.Job{
 | 
			
		||||
			Name:     "name",
 | 
			
		||||
			Schedule: &schedule,
 | 
			
		||||
			Repeats:  &repeats,
 | 
			
		||||
			DueTime:  &dueTime,
 | 
			
		||||
			Ttl:      &ttl,
 | 
			
		||||
			Data:     nil,
 | 
			
		||||
		},
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *testDaprServer) DeleteJobAlpha1(ctx context.Context, in *pb.DeleteJobRequest) (*pb.DeleteJobResponse, error) {
 | 
			
		||||
	return &pb.DeleteJobResponse{}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGrpcClient(t *testing.T) {
 | 
			
		||||
	protoClient := pb.NewDaprClient(nil)
 | 
			
		||||
	client := &GRPCClient{protoClient: protoClient}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,91 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2021 The Dapr Authors
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package client
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"log"
 | 
			
		||||
 | 
			
		||||
	"google.golang.org/protobuf/types/known/anypb"
 | 
			
		||||
 | 
			
		||||
	pb "github.com/dapr/dapr/pkg/proto/runtime/v1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Job struct {
 | 
			
		||||
	Name     string
 | 
			
		||||
	Schedule string
 | 
			
		||||
	Repeats  uint32 // Optional
 | 
			
		||||
	DueTime  string // Optional
 | 
			
		||||
	TTL      string // Optional
 | 
			
		||||
	Data     *anypb.Any
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ScheduleJobAlpha1 raises and schedules a job.
 | 
			
		||||
func (c *GRPCClient) ScheduleJobAlpha1(ctx context.Context, job *Job) error {
 | 
			
		||||
	// TODO: Assert job fields are defined: Name, Schedule, Data
 | 
			
		||||
	jobRequest := &pb.Job{
 | 
			
		||||
		Name:     job.Name,
 | 
			
		||||
		Schedule: &job.Schedule,
 | 
			
		||||
		Data:     job.Data,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if job.Schedule != "" {
 | 
			
		||||
		jobRequest.Schedule = &job.Schedule
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if job.Repeats != 0 {
 | 
			
		||||
		jobRequest.Repeats = &job.Repeats
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if job.DueTime != "" {
 | 
			
		||||
		jobRequest.DueTime = &job.DueTime
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if job.TTL != "" {
 | 
			
		||||
		jobRequest.Ttl = &job.TTL
 | 
			
		||||
	}
 | 
			
		||||
	_, err := c.protoClient.ScheduleJobAlpha1(ctx, &pb.ScheduleJobRequest{
 | 
			
		||||
		Job: jobRequest,
 | 
			
		||||
	})
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetJobAlpha1 retrieves a scheduled job.
 | 
			
		||||
func (c *GRPCClient) GetJobAlpha1(ctx context.Context, name string) (*Job, error) {
 | 
			
		||||
	// TODO: Name validation
 | 
			
		||||
	resp, err := c.protoClient.GetJobAlpha1(ctx, &pb.GetJobRequest{
 | 
			
		||||
		Name: name,
 | 
			
		||||
	})
 | 
			
		||||
	log.Println(resp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &Job{
 | 
			
		||||
		Name:     resp.GetJob().GetName(),
 | 
			
		||||
		Schedule: resp.GetJob().GetSchedule(),
 | 
			
		||||
		Repeats:  resp.GetJob().GetRepeats(),
 | 
			
		||||
		DueTime:  resp.GetJob().GetDueTime(),
 | 
			
		||||
		TTL:      resp.GetJob().GetTtl(),
 | 
			
		||||
		Data:     resp.GetJob().GetData(),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeleteJobAlpha1 deletes a scheduled job.
 | 
			
		||||
func (c *GRPCClient) DeleteJobAlpha1(ctx context.Context, name string) error {
 | 
			
		||||
	// TODO: Name validation
 | 
			
		||||
	_, err := c.protoClient.DeleteJobAlpha1(ctx, &pb.DeleteJobRequest{
 | 
			
		||||
		Name: name,
 | 
			
		||||
	})
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright 2021 The Dapr Authors
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package client
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
	"google.golang.org/protobuf/types/known/anypb"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestSchedulingAlpha1(t *testing.T) {
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
 | 
			
		||||
	t.Run("schedule job - valid", func(t *testing.T) {
 | 
			
		||||
		err := testClient.ScheduleJobAlpha1(ctx, &Job{
 | 
			
		||||
			Name:     "test",
 | 
			
		||||
			Schedule: "test",
 | 
			
		||||
			Data:     &anypb.Any{},
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t.Run("get job - valid", func(t *testing.T) {
 | 
			
		||||
		expected := &Job{
 | 
			
		||||
			Name:     "name",
 | 
			
		||||
			Schedule: "@every 10s",
 | 
			
		||||
			Repeats:  4,
 | 
			
		||||
			DueTime:  "10s",
 | 
			
		||||
			TTL:      "10s",
 | 
			
		||||
			Data:     nil,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		resp, err := testClient.GetJobAlpha1(ctx, "name")
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
		assert.Equal(t, expected, resp)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	t.Run("delete job - valid", func(t *testing.T) {
 | 
			
		||||
		err := testClient.DeleteJobAlpha1(ctx, "name")
 | 
			
		||||
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
# Dapr Distributed Scheduler Example with go-sdk
 | 
			
		||||
 | 
			
		||||
## Steps
 | 
			
		||||
 | 
			
		||||
### Prepare
 | 
			
		||||
 | 
			
		||||
- Dapr installed (v1.14 or higher)
 | 
			
		||||
 | 
			
		||||
### Run Distributed Scheduling Example
 | 
			
		||||
 | 
			
		||||
<!-- STEP
 | 
			
		||||
name: Run Distributed Scheduling Example
 | 
			
		||||
output_match_mode: substring
 | 
			
		||||
expected_stdout_lines:
 | 
			
		||||
  - 'Scheduler stream connected'
 | 
			
		||||
  - 'schedulejob - success'
 | 
			
		||||
  - 'job 0 received'
 | 
			
		||||
  - 'extracted payload: {db-backup {my-prod-db /backup-dir}}'
 | 
			
		||||
  - 'job 1 received'
 | 
			
		||||
  - 'extracted payload: {db-backup {my-prod-db /backup-dir}}'
 | 
			
		||||
  - 'job 2 received'
 | 
			
		||||
  - 'extracted payload: {db-backup {my-prod-db /backup-dir}}'
 | 
			
		||||
  - 'getjob - resp: &{prod-db-backup @every 1s 10   value:"{\"task\":\"db-backup\",\"metadata\":{\"db_name\":\"my-prod-db\",\"backup_location\":\"/backup-dir\"}}"}'
 | 
			
		||||
  - 'deletejob - success'
 | 
			
		||||
 | 
			
		||||
background: true
 | 
			
		||||
sleep: 30
 | 
			
		||||
 | 
			
		||||
-->
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
         dapr run --app-id=distributed-scheduler \
 | 
			
		||||
                --metrics-port=9091 \
 | 
			
		||||
                --scheduler-host-address=localhost:50006 \
 | 
			
		||||
                --dapr-grpc-port 50001 \
 | 
			
		||||
                --app-port 50070 \
 | 
			
		||||
                --app-protocol grpc \
 | 
			
		||||
                --log-level debug \
 | 
			
		||||
                go run ./main.go
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
<!-- END_STEP -->
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
package api
 | 
			
		||||
 | 
			
		||||
type Metadata struct {
 | 
			
		||||
	DBName         string `json:"db_name"`
 | 
			
		||||
	BackupLocation string `json:"backup_location"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type DBBackup struct {
 | 
			
		||||
	Task     string   `json:"task"`
 | 
			
		||||
	Metadata Metadata `json:"metadata"`
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,114 @@
 | 
			
		|||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"google.golang.org/protobuf/types/known/anypb"
 | 
			
		||||
 | 
			
		||||
	daprc "github.com/dapr/go-sdk/client"
 | 
			
		||||
	"github.com/dapr/go-sdk/examples/dist-scheduler/api"
 | 
			
		||||
	"github.com/dapr/go-sdk/service/common"
 | 
			
		||||
	daprs "github.com/dapr/go-sdk/service/grpc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	server, err := daprs.NewService(":50070")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("failed to start the server: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = server.AddJobEventHandler("prod-db-backup", prodDBBackupHandler); err != nil {
 | 
			
		||||
		log.Fatalf("failed to register job event handler: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Println("starting server")
 | 
			
		||||
	go func() {
 | 
			
		||||
		if err = server.Start(); err != nil {
 | 
			
		||||
			log.Fatalf("failed to start server: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	// Brief intermission to allow for the server to initialize.
 | 
			
		||||
	time.Sleep(10 * time.Second)
 | 
			
		||||
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
 | 
			
		||||
	jobData, err := json.Marshal(&api.DBBackup{
 | 
			
		||||
		Task: "db-backup",
 | 
			
		||||
		Metadata: api.Metadata{
 | 
			
		||||
			DBName:         "my-prod-db",
 | 
			
		||||
			BackupLocation: "/backup-dir",
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
	)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	job := daprc.Job{
 | 
			
		||||
		Name:     "prod-db-backup",
 | 
			
		||||
		Schedule: "@every 1s",
 | 
			
		||||
		Repeats:  10,
 | 
			
		||||
		Data: &anypb.Any{
 | 
			
		||||
			Value: jobData,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// create the client
 | 
			
		||||
	client, err := daprc.NewClient()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	defer client.Close()
 | 
			
		||||
 | 
			
		||||
	err = client.ScheduleJobAlpha1(ctx, &job)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Println("schedulejob - success")
 | 
			
		||||
 | 
			
		||||
	time.Sleep(3 * time.Second)
 | 
			
		||||
 | 
			
		||||
	resp, err := client.GetJobAlpha1(ctx, "prod-db-backup")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		panic(err)
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("getjob - resp: %v\n", resp) // parse
 | 
			
		||||
 | 
			
		||||
	err = client.DeleteJobAlpha1(ctx, "prod-db-backup")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("job deletion error: %v\n", err)
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Println("deletejob - success")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = server.Stop(); err != nil {
 | 
			
		||||
		log.Fatalf("failed to stop server: %v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var jobCount = 0
 | 
			
		||||
 | 
			
		||||
func prodDBBackupHandler(ctx context.Context, job *common.JobEvent) error {
 | 
			
		||||
	var jobData common.Job
 | 
			
		||||
	if err := json.Unmarshal(job.Data, &jobData); err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to unmarshal job: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	decodedPayload, err := base64.StdEncoding.DecodeString(jobData.Value)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to decode job payload: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	var jobPayload api.DBBackup
 | 
			
		||||
	if err := json.Unmarshal(decodedPayload, &jobPayload); err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to unmarshal payload: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("job %d received:\n type: %v \n typeurl: %v\n value: %v\n extracted payload: %v\n", jobCount, job.JobType, jobData.TypeURL, jobData.Value, jobPayload)
 | 
			
		||||
	jobCount++
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
module github.com/dapr/go-sdk/examples
 | 
			
		||||
 | 
			
		||||
go 1.22.4
 | 
			
		||||
go 1.22.5
 | 
			
		||||
 | 
			
		||||
replace github.com/dapr/go-sdk => ../
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -10,14 +10,15 @@ require (
 | 
			
		|||
	github.com/go-redis/redis/v8 v8.11.5
 | 
			
		||||
	github.com/google/uuid v1.6.0
 | 
			
		||||
	google.golang.org/grpc v1.65.0
 | 
			
		||||
	google.golang.org/grpc/examples v0.0.0-20240529235625-24e902437554
 | 
			
		||||
	google.golang.org/grpc/examples v0.0.0-20240516203910-e22436abb809
 | 
			
		||||
	google.golang.org/protobuf v1.34.2
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
 | 
			
		||||
	github.com/cenkalti/backoff/v4 v4.3.0 // indirect
 | 
			
		||||
	github.com/cespare/xxhash/v2 v2.3.0 // indirect
 | 
			
		||||
	github.com/dapr/dapr v1.14.0-rc.1 // indirect
 | 
			
		||||
	github.com/dapr/dapr v1.14.0-rc.2 // indirect
 | 
			
		||||
	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
 | 
			
		||||
	github.com/go-chi/chi/v5 v5.1.0 // indirect
 | 
			
		||||
	github.com/go-logr/logr v1.4.2 // indirect
 | 
			
		||||
| 
						 | 
				
			
			@ -34,6 +35,5 @@ require (
 | 
			
		|||
	golang.org/x/sys v0.21.0 // indirect
 | 
			
		||||
	golang.org/x/text v0.16.0 // indirect
 | 
			
		||||
	google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d // indirect
 | 
			
		||||
	google.golang.org/protobuf v1.34.2 // indirect
 | 
			
		||||
	gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,8 +7,8 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
 | 
			
		|||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 | 
			
		||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 | 
			
		||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 | 
			
		||||
github.com/dapr/dapr v1.14.0-rc.1 h1:4P376+PIU66hMtLz5TiF41IJ6Lh5FNY1DiwaNNYZv/8=
 | 
			
		||||
github.com/dapr/dapr v1.14.0-rc.1/go.mod h1:uZMuD9K7y+LKSsQUoSAvv1Yn8Cim9X/9ZQ9XuTobyP8=
 | 
			
		||||
github.com/dapr/dapr v1.14.0-rc.2 h1:wuXninZLTyokeztCinVIVAc9mpVYJS8QyxecPCLdlY8=
 | 
			
		||||
github.com/dapr/dapr v1.14.0-rc.2/go.mod h1:uZMuD9K7y+LKSsQUoSAvv1Yn8Cim9X/9ZQ9XuTobyP8=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
| 
						 | 
				
			
			@ -77,8 +77,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d h1:
 | 
			
		|||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
 | 
			
		||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
 | 
			
		||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
 | 
			
		||||
google.golang.org/grpc/examples v0.0.0-20240529235625-24e902437554 h1:w4+xIPcgnCPx2fCAcl+9XZ4wA7tpurRvJ+KBSgAig4A=
 | 
			
		||||
google.golang.org/grpc/examples v0.0.0-20240529235625-24e902437554/go.mod h1:zVJi9R2NecTook3ihvrayAmkYWKtOfRrvL+2eRtKdMk=
 | 
			
		||||
google.golang.org/grpc/examples v0.0.0-20240516203910-e22436abb809 h1:f96Rv5C5Y2CWlbKK6KhKDdyFgGOjPHPEMsdyaxE9k0c=
 | 
			
		||||
google.golang.org/grpc/examples v0.0.0-20240516203910-e22436abb809/go.mod h1:uaPEAc5V00jjG3DPhGFLXGT290RUV3+aNQigs1W50/8=
 | 
			
		||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
 | 
			
		||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										6
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										6
									
								
								go.mod
								
								
								
								
							| 
						 | 
				
			
			@ -1,11 +1,9 @@
 | 
			
		|||
module github.com/dapr/go-sdk
 | 
			
		||||
 | 
			
		||||
go 1.22.4
 | 
			
		||||
 | 
			
		||||
toolchain go1.22.5
 | 
			
		||||
go 1.22.5
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/dapr/dapr v1.14.0-rc.1
 | 
			
		||||
	github.com/dapr/dapr v1.14.0-rc.2
 | 
			
		||||
	github.com/go-chi/chi/v5 v5.1.0
 | 
			
		||||
	github.com/golang/mock v1.6.0
 | 
			
		||||
	github.com/google/uuid v1.6.0
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										4
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										4
									
								
								go.sum
								
								
								
								
							| 
						 | 
				
			
			@ -1,8 +1,8 @@
 | 
			
		|||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
 | 
			
		||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
 | 
			
		||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 | 
			
		||||
github.com/dapr/dapr v1.14.0-rc.1 h1:4P376+PIU66hMtLz5TiF41IJ6Lh5FNY1DiwaNNYZv/8=
 | 
			
		||||
github.com/dapr/dapr v1.14.0-rc.1/go.mod h1:uZMuD9K7y+LKSsQUoSAvv1Yn8Cim9X/9ZQ9XuTobyP8=
 | 
			
		||||
github.com/dapr/dapr v1.14.0-rc.2 h1:wuXninZLTyokeztCinVIVAc9mpVYJS8QyxecPCLdlY8=
 | 
			
		||||
github.com/dapr/dapr v1.14.0-rc.2/go.mod h1:uZMuD9K7y+LKSsQUoSAvv1Yn8Cim9X/9ZQ9XuTobyP8=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,6 +42,9 @@ type Service interface {
 | 
			
		|||
	RegisterActorImplFactory(f actor.Factory, opts ...config.Option)
 | 
			
		||||
	// RegisterActorImplFactoryContext Register a new actor to actor runtime of go sdk
 | 
			
		||||
	RegisterActorImplFactoryContext(f actor.FactoryContext, opts ...config.Option)
 | 
			
		||||
	// AddJobEventHandler appends a provided job event handler with its name to
 | 
			
		||||
	// the service.
 | 
			
		||||
	AddJobEventHandler(name string, fn JobEventHandler) error
 | 
			
		||||
	// Start starts service.
 | 
			
		||||
	Start() error
 | 
			
		||||
	// Stop stops the previously started service.
 | 
			
		||||
| 
						 | 
				
			
			@ -54,5 +57,6 @@ type (
 | 
			
		|||
	ServiceInvocationHandler func(ctx context.Context, in *InvocationEvent) (out *Content, err error)
 | 
			
		||||
	TopicEventHandler        func(ctx context.Context, e *TopicEvent) (retry bool, err error)
 | 
			
		||||
	BindingInvocationHandler func(ctx context.Context, in *BindingEvent) (out []byte, err error)
 | 
			
		||||
	JobEventHandler          func(ctx context.Context, in *JobEvent) error
 | 
			
		||||
	HealthCheckHandler       func(context.Context) error
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -120,3 +120,13 @@ const (
 | 
			
		|||
type SubscriptionResponse struct {
 | 
			
		||||
	Status SubscriptionResponseStatus `json:"status"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type JobEvent struct {
 | 
			
		||||
	JobType string `json:"job_type"`
 | 
			
		||||
	Data    []byte `json:"data"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Job struct {
 | 
			
		||||
	TypeURL string `json:"type_url"`
 | 
			
		||||
	Value   string `json:"value"`
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
package grpc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	runtimepb "github.com/dapr/dapr/pkg/proto/runtime/v1"
 | 
			
		||||
	"github.com/dapr/go-sdk/service/common"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AddJobEventHandler registers a job handler
 | 
			
		||||
func (s *Server) AddJobEventHandler(name string, fn common.JobEventHandler) error {
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		return errors.New("job event name cannot be empty")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if fn == nil {
 | 
			
		||||
		return errors.New("job event handler not supplied")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s.jobEventHandlers[name] = fn
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OnJobEvent is invoked by the sidecar following a scheduled job registered in
 | 
			
		||||
// the scheduler
 | 
			
		||||
func (s *Server) OnJobEventAlpha1(ctx context.Context, in *runtimepb.JobEventRequest) (*runtimepb.JobEventResponse, error) {
 | 
			
		||||
	// parse the job type from the method or name
 | 
			
		||||
	jobType, found := strings.CutPrefix(in.GetMethod(), "job/")
 | 
			
		||||
	if !found {
 | 
			
		||||
		if in.GetName() == "" {
 | 
			
		||||
			return &runtimepb.JobEventResponse{}, errors.New("unsupported invocation")
 | 
			
		||||
		}
 | 
			
		||||
		jobType = in.GetName()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if fn, ok := s.jobEventHandlers[jobType]; ok {
 | 
			
		||||
		e := &common.JobEvent{
 | 
			
		||||
			JobType: jobType,
 | 
			
		||||
			Data:    in.GetData().GetValue(),
 | 
			
		||||
		}
 | 
			
		||||
		if err := fn(ctx, e); err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("error executing %s binding: %w", in.GetName(), err)
 | 
			
		||||
		}
 | 
			
		||||
		return &runtimepb.JobEventResponse{}, nil
 | 
			
		||||
	}
 | 
			
		||||
	return &runtimepb.JobEventResponse{}, errors.New("job event handler not found")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -59,6 +59,7 @@ func newService(lis net.Listener, grpcServer *grpc.Server, opts ...grpc.ServerOp
 | 
			
		|||
		invokeHandlers:   make(map[string]common.ServiceInvocationHandler),
 | 
			
		||||
		topicRegistrar:   make(internal.TopicRegistrar),
 | 
			
		||||
		bindingHandlers:  make(map[string]common.BindingInvocationHandler),
 | 
			
		||||
		jobEventHandlers: make(map[string]common.JobEventHandler),
 | 
			
		||||
		authToken:        os.Getenv(common.AppAPITokenEnvVar),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -67,6 +68,7 @@ func newService(lis net.Listener, grpcServer *grpc.Server, opts ...grpc.ServerOp
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	pb.RegisterAppCallbackServer(grpcServer, s)
 | 
			
		||||
	pb.RegisterAppCallbackAlphaServer(grpcServer, s)
 | 
			
		||||
	pb.RegisterAppCallbackHealthCheckServer(grpcServer, s)
 | 
			
		||||
	s.grpcServer = grpcServer
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -81,6 +83,7 @@ type Server struct {
 | 
			
		|||
	invokeHandlers     map[string]common.ServiceInvocationHandler
 | 
			
		||||
	topicRegistrar     internal.TopicRegistrar
 | 
			
		||||
	bindingHandlers    map[string]common.BindingInvocationHandler
 | 
			
		||||
	jobEventHandlers   map[string]common.JobEventHandler
 | 
			
		||||
	healthCheckHandler common.HealthCheckHandler
 | 
			
		||||
	authToken          string
 | 
			
		||||
	grpcServer         *grpc.Server
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -169,3 +169,7 @@ func getCustomMetadataFromContext(ctx context.Context) map[string]string {
 | 
			
		|||
	}
 | 
			
		||||
	return md
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Server) OnBulkTopicEventAlpha1(ctx context.Context, in *runtimev1pb.TopicEventBulkRequest) (*runtimev1pb.TopicEventBulkResponse, error) {
 | 
			
		||||
	panic("This API callback is not supported.")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
package http
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/dapr/go-sdk/service/common"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (s *Server) AddJobEventHandler(name string, fn common.JobEventHandler) error {
 | 
			
		||||
	if name == "" {
 | 
			
		||||
		return fmt.Errorf("job event name required")
 | 
			
		||||
	}
 | 
			
		||||
	if fn == nil {
 | 
			
		||||
		return fmt.Errorf("job event handler required")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return errors.New("handling http scheduling requests has not been implemented in this sdk")
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue