├── .github ├── CODEOWNERS ├── dependabot.yml └── workflows │ ├── conformance.yaml │ ├── go-build.yaml │ ├── go-format.yaml │ ├── go-lint.yaml │ ├── go-unit-test.yaml │ ├── inclusive.yaml │ ├── integration.yaml │ ├── observability.yaml │ ├── release.yaml │ └── update-dependencies.yml ├── .gitignore ├── .gitmodules ├── .golangci.yaml ├── CONTRIBUTING.md ├── LICENSE ├── MAINTAINERS.md ├── README.md ├── RELEASING.md ├── binding └── format │ └── protobuf │ └── v2 │ ├── datacodec.go │ ├── go.mod │ ├── go.sum │ ├── pb │ ├── cloudevent.pb.go │ ├── cloudevent.proto │ └── gen.go │ ├── protobuf.go │ └── protobuf_test.go ├── docs ├── Gemfile ├── Gemfile.lock ├── _config.yml ├── concepts.md ├── event_data_structure.md ├── images │ ├── Makefile │ ├── forwarder.dot │ ├── forwarder.svg │ ├── mutator.dot │ ├── mutator.svg │ ├── receiver.dot │ ├── receiver.svg │ ├── sender.dot │ ├── sender.svg │ ├── stack.dot │ └── stack.svg ├── index.md └── protocol_implementations.md ├── hack ├── boilerplate │ ├── add-boilerplate.sh │ ├── boilerplate.go.txt │ └── boilerplate.sh.txt ├── build-test.sh ├── conformance-test.sh ├── go-mod-tidy.sh ├── integration-test.sh ├── observability-test.sh ├── run-integration-services.sh ├── tag-release.sh ├── unit-test.sh └── update-deps.sh ├── maintainer_guidelines.md ├── observability ├── opencensus │ └── v2 │ │ ├── client │ │ ├── client.go │ │ ├── client_test.go │ │ ├── observability_service.go │ │ ├── observable.go │ │ ├── reporter.go │ │ └── trace_attributes.go │ │ ├── go.mod │ │ ├── go.sum │ │ └── http │ │ └── http.go └── opentelemetry │ └── v2 │ ├── README.md │ ├── client │ ├── client.go │ ├── cloudevents_carrier.go │ ├── otel_observability_service.go │ └── otel_options.go │ ├── go.mod │ ├── go.sum │ └── http │ └── http.go ├── pr_guidelines.md ├── protocol ├── amqp │ └── v2 │ │ ├── doc.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── message.go │ │ ├── message_test.go │ │ ├── options.go │ │ ├── protocol.go │ │ ├── receiver.go │ │ ├── sender.go │ │ ├── types.go │ │ └── write_message.go ├── kafka_confluent │ └── v2 │ │ ├── go.mod │ │ ├── go.sum │ │ ├── message.go │ │ ├── message_test.go │ │ ├── option.go │ │ ├── protocol.go │ │ ├── protocol_test.go │ │ ├── write_producer_message.go │ │ └── write_producer_message_test.go ├── kafka_sarama │ └── v2 │ │ ├── doc.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── message.go │ │ ├── message_benchmark_test.go │ │ ├── message_test.go │ │ ├── option.go │ │ ├── protocol.go │ │ ├── receiver.go │ │ ├── sender.go │ │ ├── sender_test.go │ │ ├── write_producer_message.go │ │ ├── write_producer_message_benchmark_test.go │ │ └── write_producer_message_test.go ├── mqtt_paho │ └── v2 │ │ ├── go.mod │ │ ├── go.sum │ │ ├── message.go │ │ ├── message_test.go │ │ ├── option.go │ │ ├── protocol.go │ │ ├── write_message.go │ │ └── write_message_test.go ├── nats │ └── v2 │ │ ├── doc.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── message.go │ │ ├── options.go │ │ ├── options_test.go │ │ ├── protocol.go │ │ ├── receiver.go │ │ ├── sender.go │ │ ├── subscriber.go │ │ └── write_message.go ├── nats_jetstream │ ├── v2 │ │ ├── doc.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── message.go │ │ ├── message_test.go │ │ ├── options.go │ │ ├── options_test.go │ │ ├── protocol.go │ │ ├── receiver.go │ │ ├── sender.go │ │ ├── subscriber.go │ │ └── write_message.go │ └── v3 │ │ ├── go.mod │ │ ├── go.sum │ │ ├── message.go │ │ ├── message_test.go │ │ ├── options.go │ │ ├── options_test.go │ │ ├── protocol.go │ │ ├── protocol_test.go │ │ ├── validation.go │ │ ├── validation_test.go │ │ ├── write_message.go │ │ └── write_message_test.go ├── pubsub │ └── v2 │ │ ├── attributes.go │ │ ├── context │ │ └── context.go │ │ ├── doc.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── internal │ │ ├── connection.go │ │ └── connection_test.go │ │ ├── message.go │ │ ├── message_test.go │ │ ├── options.go │ │ ├── protocol.go │ │ ├── protocol_test.go │ │ └── write_pubsub_message.go ├── stan │ └── v2 │ │ ├── context.go │ │ ├── context_test.go │ │ ├── doc.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── message.go │ │ ├── options.go │ │ ├── options_test.go │ │ ├── protocol.go │ │ ├── receiver.go │ │ ├── sender.go │ │ ├── subscriber.go │ │ └── write_message.go └── ws │ └── v2 │ ├── client_protocol.go │ ├── client_protocol_test.go │ ├── context.go │ ├── doc.go │ ├── go.mod │ ├── go.sum │ ├── subprotocols.go │ └── subprotocols_test.go ├── samples ├── README.md ├── amqp │ ├── README.md │ ├── go.mod │ ├── go.sum │ ├── receiver │ │ └── main.go │ └── sender │ │ └── main.go ├── gochan │ ├── go.mod │ ├── go.sum │ └── main.go ├── http │ ├── go.mod │ ├── go.sum │ ├── receiver-direct │ │ └── main.go │ ├── receiver-gin │ │ ├── README.md │ │ ├── event.json │ │ └── main.go │ ├── receiver-gorilla │ │ └── main.go │ ├── receiver-protected │ │ └── main.go │ ├── receiver-protobuf │ │ ├── main.go │ │ ├── sample.pb.go │ │ └── sample.proto │ ├── receiver-result │ │ └── main.go │ ├── receiver-sleepy │ │ └── main.go │ ├── receiver-traced │ │ └── main.go │ ├── receiver │ │ └── main.go │ ├── requester-with-custom-client │ │ └── main.go │ ├── requester │ │ └── main.go │ ├── responder │ │ └── main.go │ ├── sender-protobuf │ │ ├── main.go │ │ ├── sample.pb.go │ │ └── sample.proto │ ├── sender-retry │ │ └── main.go │ └── sender │ │ └── main.go ├── kafka │ ├── README.md │ ├── go.mod │ ├── go.sum │ ├── message-handle-non-cloudevents │ │ └── main.go │ ├── receiver │ │ └── main.go │ ├── sender-receiver │ │ └── main.go │ └── sender │ │ └── main.go ├── kafka_confluent │ ├── README.md │ ├── go.mod │ ├── go.sum │ ├── receiver-assign │ │ └── main.go │ ├── receiver │ │ └── main.go │ └── sender │ │ └── main.go ├── mqtt │ ├── README.md │ ├── go.mod │ ├── go.sum │ ├── receiver │ │ └── main.go │ └── sender │ │ └── main.go ├── nats │ ├── go.mod │ ├── go.sum │ ├── message-interoperability │ │ └── main.go │ ├── receiver │ │ └── main.go │ └── sender │ │ └── main.go ├── nats_jetstream │ ├── go.mod │ ├── go.sum │ ├── message-interoperability │ │ └── main.go │ ├── receiver │ │ └── main.go │ ├── sender │ │ └── main.go │ └── v3 │ │ ├── go.mod │ │ ├── go.sum │ │ ├── receiver │ │ └── main.go │ │ └── sender │ │ └── main.go ├── pubsub │ ├── go.mod │ ├── go.sum │ ├── multireceiver │ │ └── main.go │ ├── multisender │ │ └── main.go │ ├── receiver │ │ └── main.go │ └── sender │ │ └── main.go ├── stan │ ├── go.mod │ ├── go.sum │ ├── receiver │ │ └── main.go │ ├── sender-receiver │ │ └── main.go │ └── sender │ │ └── main.go └── ws │ ├── client │ └── main.go │ ├── go.mod │ └── go.sum ├── sql └── v2 │ ├── CESQLLexer.g4 │ ├── CESQLParser.g4 │ ├── Makefile │ ├── README.md │ ├── errors │ └── errors.go │ ├── expression.go │ ├── expression │ ├── base_expressions.go │ ├── comparison_expressions.go │ ├── exists_expression.go │ ├── function_invocation_expression.go │ ├── identifier_expression.go │ ├── in_expression.go │ ├── integer_comparison_expressions.go │ ├── like_expression.go │ ├── literal_expression.go │ ├── logic_expressions.go │ ├── math_expressions.go │ ├── negate_expression.go │ └── not_expression.go │ ├── function.go │ ├── function │ ├── casting_functions.go │ ├── function.go │ ├── integer_functions.go │ └── string_functions.go │ ├── gen │ ├── CESQLParser.interp │ ├── CESQLParser.tokens │ ├── CESQLParserLexer.interp │ ├── CESQLParserLexer.tokens │ ├── cesqlparser_base_visitor.go │ ├── cesqlparser_lexer.go │ ├── cesqlparser_parser.go │ └── cesqlparser_visitor.go │ ├── go.mod │ ├── go.sum │ ├── parser │ ├── case_changing_stream.go │ ├── expression_visitor.go │ └── parser.go │ ├── runtime │ ├── functions_resolver.go │ └── test │ │ ├── tck │ │ └── user_defined_functions.yaml │ │ └── user_defined_functions_test.go │ ├── test │ ├── tck │ │ ├── README.md │ │ ├── binary_comparison_operators.yaml │ │ ├── binary_logical_operators.yaml │ │ ├── binary_math_operators.yaml │ │ ├── case_sensitivity.yaml │ │ ├── casting_functions.yaml │ │ ├── context_attributes_access.yaml │ │ ├── exists_expression.yaml │ │ ├── in_expression.yaml │ │ ├── integer_builtin_functions.yaml │ │ ├── like_expression.yaml │ │ ├── literals.yaml │ │ ├── negate_operator.yaml │ │ ├── not_operator.yaml │ │ ├── parse_errors.yaml │ │ ├── spec_examples.yaml │ │ ├── string_builtin_functions.yaml │ │ ├── sub_expression.yaml │ │ └── subscriptions_api_recreations.yaml │ └── tck_test.go │ ├── types.go │ └── utils │ ├── casting.go │ └── event.go ├── test ├── benchmark │ ├── buffering_vs_to_event │ │ ├── http_transformers_benchmark_test.go │ │ └── transformers_benchmark_test.go │ ├── e2e │ │ ├── benchmark_case.go │ │ ├── benchmark_result.go │ │ ├── doc.go │ │ └── http │ │ │ ├── 3d_plot_allocs.gnuplot │ │ │ ├── 3d_plot_ns.gnuplot │ │ │ ├── README.md │ │ │ ├── http_mock.go │ │ │ ├── main.go │ │ │ ├── plot_output_senders_allocs.gnuplot │ │ │ ├── plot_output_senders_ns.gnuplot │ │ │ ├── plot_parallelism_allocs.gnuplot │ │ │ ├── plot_parallelism_ns.gnuplot │ │ │ └── run_revision.sh │ ├── go.mod │ ├── go.sum │ ├── http_request_to_kafka_sarama_encode │ │ └── benchmark_test.go │ └── kafka_sarama_to_http_request_encode │ │ └── benchmark_test.go ├── conformance │ ├── cloudevents_steps_test.go │ ├── go.mod │ ├── go.sum │ ├── http_steps_test.go │ ├── kafka_steps_test.go │ └── run_test.go ├── integration │ ├── amqp │ │ └── amqp_test.go │ ├── amqp_binding │ │ └── amqp_test.go │ ├── go.mod │ ├── go.sum │ ├── http │ │ ├── blocking_test.go │ │ ├── direct.go │ │ ├── direct_v1_test.go │ │ ├── doc.go │ │ ├── loopback.go │ │ ├── loopback_setters_test.go │ │ ├── loopback_v03_test.go │ │ ├── loopback_v1_test.go │ │ ├── middleware.go │ │ ├── middleware_v1_test.go │ │ ├── receiver.go │ │ ├── receiver_v1_test.go │ │ ├── responder_v1_test.go │ │ ├── tap_handler.go │ │ └── validation.go │ ├── http_binding │ │ └── http_test.go │ ├── kafka_confluent │ │ └── kafka_test.go │ ├── kafka_sarama │ │ └── kafka_test.go │ ├── kafka_sarama_binding │ │ └── kafka_test.go │ ├── mqtt_paho │ │ ├── concurrent_test.go │ │ └── mqtt_test.go │ ├── mqtt_paho_binding │ │ └── mqtt_test.go │ ├── nats │ │ └── nats_test.go │ ├── nats_jetstream │ │ ├── nats_test.go │ │ └── v3 │ │ │ └── nats_test.go │ └── stan │ │ └── stan_test.go ├── observability │ ├── go.mod │ ├── go.sum │ └── opentelemetry │ │ ├── client_test.go │ │ ├── cloudevents_carrier_test.go │ │ ├── doc.go │ │ └── otel_observability_service_test.go └── sql │ ├── go.mod │ ├── go.sum │ └── shortcircuit_test.go ├── tools ├── go.mod ├── go.sum └── http │ └── raw │ └── main.go └── v2 ├── alias.go ├── binding ├── binary_writer.go ├── buffering │ ├── acks_before_finish_message.go │ ├── acks_before_finish_message_test.go │ ├── binary_buffer_message.go │ ├── copy_message.go │ ├── copy_message_benchmark_test.go │ ├── copy_message_test.go │ ├── doc.go │ └── struct_buffer_message.go ├── doc.go ├── encoding.go ├── event_message.go ├── event_message_test.go ├── finish_message.go ├── finish_message_test.go ├── format │ ├── doc.go │ ├── format.go │ └── format_test.go ├── message.go ├── spec │ ├── attributes.go │ ├── attributes_test.go │ ├── doc.go │ ├── match_exact_version.go │ ├── match_exact_version_test.go │ ├── spec.go │ └── spec_test.go ├── structured_writer.go ├── test │ ├── doc.go │ ├── mock_binary_message.go │ ├── mock_structured_message.go │ ├── mock_transformer.go │ ├── mock_unknown_message.go │ └── transformer.go ├── to_event.go ├── to_event_test.go ├── transformer.go ├── transformer │ ├── add_metadata.go │ ├── add_metadata_test.go │ ├── default_metadata.go │ ├── default_metadata_test.go │ ├── delete_metadata.go │ ├── delete_metadata_test.go │ ├── doc.go │ ├── set_metadata.go │ ├── set_metadata_test.go │ ├── version.go │ └── version_test.go ├── utils │ ├── structured_message.go │ ├── structured_message_test.go │ ├── write_structured_message.go │ └── write_structured_message_test.go └── write.go ├── client ├── client.go ├── client_http.go ├── client_observed.go ├── client_test.go ├── defaulters.go ├── defaulters_test.go ├── doc.go ├── http_receiver.go ├── http_receiver_test.go ├── invoker.go ├── observability.go ├── options.go ├── options_test.go ├── receiver.go ├── receiver_test.go └── test │ ├── mock_client.go │ ├── mock_client_test.go │ └── test.go ├── context ├── context.go ├── context_test.go ├── delegating.go ├── delegating_test.go ├── doc.go ├── logger.go ├── logger_test.go ├── retry.go └── retry_test.go ├── event ├── content_type.go ├── content_type_test.go ├── data_content_encoding.go ├── data_content_encoding_test.go ├── datacodec │ ├── codec.go │ ├── codec_test.go │ ├── doc.go │ ├── json │ │ ├── data.go │ │ ├── data_test.go │ │ └── doc.go │ ├── text │ │ ├── data.go │ │ ├── data_test.go │ │ └── doc.go │ └── xml │ │ ├── data.go │ │ ├── data_test.go │ │ └── doc.go ├── doc.go ├── event.go ├── event_data.go ├── event_data_test.go ├── event_interface.go ├── event_marshal.go ├── event_marshal_benchmark_test.go ├── event_marshal_roundtrip_test.go ├── event_marshal_test.go ├── event_reader.go ├── event_reader_writer_test.go ├── event_test.go ├── event_unmarshal.go ├── event_unmarshal_benchmark_test.go ├── event_unmarshal_test.go ├── event_validation.go ├── event_writer.go ├── eventcontext.go ├── eventcontext_test.go ├── eventcontext_v03.go ├── eventcontext_v03_reader.go ├── eventcontext_v03_test.go ├── eventcontext_v03_writer.go ├── eventcontext_v1.go ├── eventcontext_v1_reader.go ├── eventcontext_v1_test.go ├── eventcontext_v1_writer.go ├── extensions.go └── extensions_test.go ├── extensions ├── dataref_extension.go ├── dataref_extension_test.go ├── distributed_tracing_extension.go ├── distributed_tracing_extension_test.go └── doc.go ├── go.mod ├── go.sum ├── observability ├── doc.go └── keys.go ├── protocol ├── doc.go ├── error.go ├── gochan │ ├── doc.go │ ├── protocol.go │ ├── protocol_test.go │ ├── receiver.go │ ├── requester.go │ ├── responder.go │ └── sender.go ├── http │ ├── abuse_protection.go │ ├── context.go │ ├── context_test.go │ ├── doc.go │ ├── headers.go │ ├── headers_test.go │ ├── message.go │ ├── message_test.go │ ├── options.go │ ├── options_test.go │ ├── protocol.go │ ├── protocol_lifecycle.go │ ├── protocol_rate.go │ ├── protocol_retry.go │ ├── protocol_retry_test.go │ ├── protocol_test.go │ ├── result.go │ ├── result_test.go │ ├── retries_result.go │ ├── retries_result_test.go │ ├── utility.go │ ├── utility_test.go │ ├── write_request.go │ ├── write_responsewriter.go │ └── write_responsewriter_test.go ├── inbound.go ├── lifecycle.go ├── outbound.go ├── result.go ├── result_test.go └── test │ ├── benchmark.go │ ├── doc.go │ ├── test.go │ └── test_test.go ├── staticcheck.conf ├── test ├── doc.go ├── event_asserts.go ├── event_asserts_test.go ├── event_matchers.go ├── event_matchers_test.go ├── event_mocks.go └── helpers.go └── types ├── allocate.go ├── allocate_test.go ├── doc.go ├── timestamp.go ├── timestamp_test.go ├── uri.go ├── uri_test.go ├── uriref.go ├── uriref_test.go ├── value.go └── value_test.go /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @cloudevents/sdk-go-maintainers 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" -------------------------------------------------------------------------------- /.github/workflows/conformance.yaml: -------------------------------------------------------------------------------- 1 | name: Conformance 2 | 3 | on: 4 | push: 5 | branches: [ 'main', 'release-*' ] 6 | pull_request: 7 | branches: [ 'main', 'release-*' ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | 14 | conformance: 15 | name: CloudEvents Conformance 16 | strategy: 17 | matrix: 18 | go-version: [1.23] 19 | platform: [ubuntu-latest] 20 | 21 | runs-on: ${{ matrix.platform }} 22 | 23 | steps: 24 | - name: Checkout code 25 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 26 | 27 | - name: Setup Go ${{ matrix.go-version }} 28 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 29 | with: 30 | go-version: ${{ matrix.go-version }} 31 | cache-dependency-path: v2/go.sum 32 | id: go 33 | 34 | - name: Update git submodule 35 | run: git submodule sync && git submodule update --init 36 | 37 | - name: Build 38 | run: ./hack/conformance-test.sh 39 | 40 | -------------------------------------------------------------------------------- /.github/workflows/go-build.yaml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ 'main', 'release-*' ] 6 | pull_request: 7 | branches: [ 'main', 'release-*' ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | 14 | build: 15 | name: Build 16 | strategy: 17 | matrix: 18 | go-version: [1.23] 19 | platform: [ubuntu-latest] 20 | 21 | runs-on: ${{ matrix.platform }} 22 | 23 | steps: 24 | - name: Checkout code 25 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 26 | 27 | - name: Setup Go ${{ matrix.go-version }} 28 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 29 | with: 30 | go-version: ${{ matrix.go-version }} 31 | cache-dependency-path: v2/go.sum 32 | id: go 33 | 34 | - name: Build 35 | run: ./hack/build-test.sh 36 | 37 | -------------------------------------------------------------------------------- /.github/workflows/go-format.yaml: -------------------------------------------------------------------------------- 1 | name: Go Format 2 | 3 | on: 4 | push: 5 | branches: [ 'main', 'release-*' ] 6 | pull_request: 7 | branches: [ 'main', 'release-*' ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | 14 | format: 15 | name: Format 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout code 20 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 21 | 22 | - name: Setup Go 23 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 24 | with: 25 | go-version: 1.23 26 | cache-dependency-path: v2/go.sum 27 | id: go 28 | 29 | - name: Go Format 30 | shell: bash 31 | run: | 32 | gofmt -s -w $(find -type f -name '*.go' -print) 33 | 34 | - name: Verify 35 | shell: bash 36 | run: | 37 | if [[ $(git diff-index --name-only HEAD --) ]]; then 38 | echo "Found diffs in:" 39 | git diff-index --name-only HEAD -- 40 | echo "${{ github.repository }} is out of style. Please run go fmt." 41 | exit 1 42 | fi 43 | echo "${{ github.repository }} is formatted correctly." 44 | -------------------------------------------------------------------------------- /.github/workflows/go-lint.yaml: -------------------------------------------------------------------------------- 1 | name: Code Style 2 | 3 | on: 4 | push: 5 | branches: [ 'main', 'release-*' ] 6 | pull_request: 7 | branches: [ 'main', 'release-*' ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | 14 | lint: 15 | name: Lint 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout code 20 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 21 | 22 | - name: Setup Go 23 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 24 | with: 25 | go-version: 1.23 26 | cache-dependency-path: v2/go.sum 27 | id: go 28 | 29 | - id: golangci_configuration 30 | uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0 31 | with: 32 | files: .golangci.yaml 33 | 34 | - name: Go Lint on ./v2 35 | if: steps.golangci_configuration.outputs.files_exists == 'true' 36 | uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0 37 | with: 38 | version: v2.1.6 39 | working-directory: v2 40 | -------------------------------------------------------------------------------- /.github/workflows/go-unit-test.yaml: -------------------------------------------------------------------------------- 1 | name: Unit Test 2 | 3 | on: 4 | push: 5 | branches: [ 'main', 'release-*' ] 6 | pull_request: 7 | branches: [ 'main', 'release-*' ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | 14 | test: 15 | name: Unit Test 16 | strategy: 17 | matrix: 18 | go-version: [1.23] 19 | platform: [ubuntu-latest] 20 | 21 | runs-on: ${{ matrix.platform }} 22 | 23 | steps: 24 | - name: Checkout code 25 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 26 | 27 | - name: Setup Go ${{ matrix.go-version }} 28 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 29 | with: 30 | go-version: ${{ matrix.go-version }} 31 | cache-dependency-path: v2/go.sum 32 | id: go 33 | 34 | - name: Test 35 | run: ./hack/unit-test.sh 36 | -------------------------------------------------------------------------------- /.github/workflows/inclusive.yaml: -------------------------------------------------------------------------------- 1 | name: Inclusive 2 | 3 | on: 4 | pull_request: 5 | branches: [ 'main', 'release-*' ] 6 | 7 | permissions: 8 | contents: read 9 | 10 | jobs: 11 | 12 | language: 13 | name: Language 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | 18 | - name: Checkout code 19 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 20 | 21 | - name: Woke 22 | uses: get-woke/woke-action-reviewdog@d71fd0115146a01c3181439ce714e21a69d75e31 # v0 23 | with: 24 | github-token: ${{ secrets.GITHUB_TOKEN }} 25 | reporter: github-pr-review -------------------------------------------------------------------------------- /.github/workflows/observability.yaml: -------------------------------------------------------------------------------- 1 | name: Observability 2 | 3 | on: 4 | push: 5 | branches: [ 'main', 'release-*' ] 6 | pull_request: 7 | branches: [ 'main', 'release-*' ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | 14 | observability: 15 | name: CloudEvents Observability 16 | strategy: 17 | matrix: 18 | go-version: [1.23] 19 | platform: [ubuntu-latest] 20 | 21 | runs-on: ${{ matrix.platform }} 22 | 23 | steps: 24 | - name: Checkout code 25 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 26 | 27 | - name: Setup Go ${{ matrix.go-version }} 28 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 29 | with: 30 | go-version: ${{ matrix.go-version }} 31 | cache-dependency-path: v2/go.sum 32 | id: go 33 | 34 | - name: Update git submodule 35 | run: git submodule sync && git submodule update --init 36 | 37 | - name: Build 38 | run: ./hack/observability-test.sh 39 | -------------------------------------------------------------------------------- /.github/workflows/update-dependencies.yml: -------------------------------------------------------------------------------- 1 | name: Update Dependencies 2 | 3 | on: 4 | schedule: 5 | # Runs every Monday at 3:00 AM UTC 6 | - cron: '0 3 * * 1' 7 | # Allow manual triggering 8 | workflow_dispatch: 9 | 10 | jobs: 11 | update-dependencies: 12 | permissions: 13 | contents: write 14 | pull-requests: write 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout code 18 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 19 | 20 | - name: Setup Go 21 | uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 22 | with: 23 | go-version: 1.23 24 | cache-dependency-path: v2/go.sum 25 | 26 | - name: Run update dependencies script 27 | run: | 28 | chmod +x ./hack/update-deps.sh 29 | ./hack/update-deps.sh 30 | 31 | - name: Create Pull Request 32 | uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e 33 | with: 34 | base: main 35 | commit-message: "chore: update dependencies" 36 | title: "chore: update dependencies" 37 | body: | 38 | This PR updates Go dependencies. 39 | 40 | This is an automated PR created by the weekly dependency update workflow. 41 | branch: automated-dependency-updates 42 | delete-branch: true 43 | labels: dependencies 44 | reviewers: sdk-go-maintainers 45 | signoff: true 46 | sign-commits: true 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Operating system temporary files 2 | .DS_Store 3 | 4 | # Editor/IDE specific settings 5 | .idea 6 | .vscode/ 7 | 8 | # Temporary output of build tools 9 | bazel-* 10 | 11 | # Binaries for programs and plugins 12 | *.exe 13 | *.exe~ 14 | *.dll 15 | *.so 16 | *.dylib 17 | 18 | # Test binary, build with `go test -c` 19 | *.test 20 | 21 | # Output of the go coverage tool 22 | *.out 23 | coverage.txt 24 | 25 | # Jekyll stuff 26 | _site/ 27 | .sass-cache/ 28 | .jekyll-cache/ 29 | .jekyll-metadata 30 | 31 | # Others 32 | ## IDE-specific 33 | *.iml 34 | test/benchmark/e2e/http/results/ 35 | tmp/ 36 | test/coverage.tmp 37 | 38 | vendor/ 39 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "conformance"] 2 | path = conformance 3 | url = https://github.com/cloudevents/conformance.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /.golangci.yaml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | run: 3 | timeout: 5m 4 | 5 | build-tags: 6 | - conformance 7 | 8 | linters: 9 | enable: 10 | - unconvert 11 | - prealloc 12 | disable: 13 | - errcheck 14 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to CloudEvents sdk-go 2 | 3 | :+1::tada: First off, thanks for taking the time to contribute! :tada::+1: 4 | 5 | We welcome contributions from the community! Please take some time to become 6 | acquainted with the process before submitting a pull request. There are just 7 | a few things to keep in mind. 8 | 9 | ## Pull Requests 10 | 11 | Typically a pull request should relate to an existing issue. If you have 12 | found a bug, want to add an improvement, or suggest an API change, please 13 | create an issue before proceeding with a pull request. For very minor changes 14 | such as typos in the documentation this isn't really necessary. 15 | 16 | For step by step help with managing your pull request, have a look at our 17 | [PR Guidelines](pr_guidelines.md) document. 18 | 19 | ### Sign your work 20 | 21 | Each PR must be signed. Be sure your `git` `user.name` and `user.email` are configured 22 | then use the `--signoff` flag for your commits. 23 | 24 | ```console 25 | git commit --signoff 26 | ``` 27 | 28 | ### Style Guide 29 | 30 | Code style for this module is maintained using [`go fmt`](https://golang.org/cmd/go/#hdr-Gofmt__reformat__package_sources). 31 | 32 | ### Maintainers 33 | 34 | If you are a maintainer of this repository, [here](maintainers_guide.pr) is a brief 35 | guide with helpful tips. 36 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # Maintainers 2 | 3 | Current active maintainers of this SDK: 4 | 5 | - [Michael Gasch](https://github.com/embano1) 6 | - [Lionel Villard](https://github.com/lionelvillard) 7 | - [Doug Davis](https://github.com/duglin) 8 | - [Sasha Tkachev](https://github.com/sasha-tkachev) 9 | -------------------------------------------------------------------------------- /binding/format/protobuf/v2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/binding/format/protobuf/v2 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | require ( 8 | github.com/cloudevents/sdk-go/v2 v2.16.0 9 | github.com/stretchr/testify v1.10.0 10 | google.golang.org/protobuf v1.36.6 11 | ) 12 | 13 | require ( 14 | github.com/davecgh/go-spew v1.1.1 // indirect 15 | github.com/google/uuid v1.6.0 // indirect 16 | github.com/json-iterator/go v1.1.12 // indirect 17 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 18 | github.com/modern-go/reflect2 v1.0.2 // indirect 19 | github.com/pmezard/go-difflib v1.0.0 // indirect 20 | go.uber.org/multierr v1.11.0 // indirect 21 | go.uber.org/zap v1.27.0 // indirect 22 | gopkg.in/yaml.v3 v3.0.1 // indirect 23 | ) 24 | 25 | replace github.com/cloudevents/sdk-go/v2 => ../../../../v2 26 | -------------------------------------------------------------------------------- /binding/format/protobuf/v2/pb/gen.go: -------------------------------------------------------------------------------- 1 | package pb 2 | 3 | //go:generate protoc --go_out=. --go_opt=paths=source_relative cloudevent.proto 4 | -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | # Hello! This is where you manage which Jekyll version is used to run. 3 | # When you want to use a different version, change it below, save the 4 | # file and run `bundle install`. Run Jekyll with `bundle exec`, like so: 5 | # 6 | # bundle exec jekyll serve 7 | # 8 | # This will help ensure the proper Jekyll version is running. 9 | # Happy Jekylling! 10 | 11 | gem "jekyll", "~> 3.8.7" 12 | gem "github-pages", group: :jekyll_plugins 13 | 14 | gem "just-the-docs" 15 | 16 | # If you have any plugins, put them here! 17 | group :jekyll_plugins do 18 | gem "jekyll-feed", "~> 0.13" 19 | end 20 | 21 | # Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem 22 | # and associated library. 23 | install_if -> { RUBY_PLATFORM =~ %r!mingw|mswin|java! } do 24 | gem "tzinfo", "~> 1.2" 25 | gem "tzinfo-data" 26 | end 27 | 28 | # Performance-booster for watching directories on Windows 29 | gem "wdm", "~> 0.1.1", :install_if => Gem.win_platform? 30 | 31 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | # To configure the environment locally to test the website, check out 2 | # https://help.github.com/en/github/working-with-github-pages/testing-your-github-pages-site-locally-with-jekyll 3 | 4 | title: Golang SDK for CloudEvents 5 | remote_theme: pmarsceill/just-the-docs 6 | plugins: 7 | - jemoji 8 | 9 | aux_links: 10 | "GitHub Repository": 11 | - "https://github.com/cloudevents/sdk-go" 12 | "CloudEvents home": 13 | - "https://cloudevents.io/" 14 | -------------------------------------------------------------------------------- /docs/images/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | all: $(patsubst %.dot,%.svg,$(wildcard *.dot)) 4 | 5 | %.svg: %.dot 6 | dot -Tsvg $< -o $@ -------------------------------------------------------------------------------- /docs/images/forwarder.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | rankdir=LR; 3 | 4 | Forwarder[shape=box] 5 | 6 | downstream -> Forwarder; 7 | 8 | Forwarder -> upstream; 9 | } -------------------------------------------------------------------------------- /docs/images/mutator.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | rankdir=LR; 3 | 4 | Mutator[shape=box] 5 | 6 | Mutator -> upstream[dir=both]; 7 | } -------------------------------------------------------------------------------- /docs/images/mutator.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | 14 | Mutator 15 | 16 | Mutator 17 | 18 | 19 | 20 | upstream 21 | 22 | upstream 23 | 24 | 25 | 26 | Mutator->upstream 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /docs/images/receiver.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | rankdir=LR; 3 | 4 | Receiver[shape=box] 5 | downstream -> Receiver; 6 | } -------------------------------------------------------------------------------- /docs/images/receiver.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | 14 | Receiver 15 | 16 | Receiver 17 | 18 | 19 | 20 | downstream 21 | 22 | downstream 23 | 24 | 25 | 26 | downstream->Receiver 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /docs/images/sender.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | rankdir=LR; 3 | 4 | Sender[shape=box] 5 | 6 | Sender -> upstream; 7 | } -------------------------------------------------------------------------------- /docs/images/sender.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | 14 | Sender 15 | 16 | Sender 17 | 18 | 19 | 20 | upstream 21 | 22 | upstream 23 | 24 | 25 | 26 | Sender->upstream 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /docs/images/stack.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | rankdir=LR; 3 | 4 | Client -> ProtocolBinding [label="[Event]"] 5 | ProtocolBinding -> ProtocolBindingImpl [label="[Event, Message]"] 6 | ProtocolBindingImpl -> Protocol [label="[Message]"] 7 | } -------------------------------------------------------------------------------- /hack/boilerplate/add-boilerplate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2021 The CloudEvents Authors 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | USAGE=$(cat <.txt to all . files missing it in a directory. 8 | 9 | Usage: (from repository root) 10 | ./hack/boilerplate/add-boilerplate.sh 11 | 12 | Example: (from repository root) 13 | ./hack/boilerplate/add-boilerplate.sh go cmd 14 | EOF 15 | ) 16 | 17 | set -e 18 | 19 | if [[ -z $1 || -z $2 ]]; then 20 | echo "${USAGE}" 21 | exit 1 22 | fi 23 | 24 | grep --recursive --files-without-match --extended-regexp --regexp="Copyright \d+ The CloudEvents Authors" $2 \ 25 | | grep --regexp="\.$1\$" \ 26 | | xargs -I {} sh -c \ 27 | "cat hack/boilerplate/boilerplate.$1.txt {} > /tmp/boilerplate && mv /tmp/boilerplate {}" 28 | -------------------------------------------------------------------------------- /hack/boilerplate/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | -------------------------------------------------------------------------------- /hack/boilerplate/boilerplate.sh.txt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2021 The CloudEvents Authors 4 | # SPDX-License-Identifier: Apache-2.0 5 | -------------------------------------------------------------------------------- /hack/build-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2021 The CloudEvents Authors 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | set -o errexit 7 | set -o nounset 8 | 9 | for gomodule in $(find . | grep "go\.mod" | awk '{gsub(/\/go.mod/,""); print $0}' | grep -v "./test" | grep -v "./conformance") 10 | do 11 | echo 12 | echo --- Building $gomodule --- 13 | echo 14 | pushd $gomodule 15 | 16 | tags="$(grep -I -r '// +build' . | cut -f3 -d' ' | sort | uniq | grep -v '^!' | tr '\n' ' ')" 17 | 18 | echo "Building with tags: ${tags}" 19 | go test -vet=off -tags "${tags}" -run=^$ ./... | grep -v "no test" || true 20 | 21 | popd 22 | done 23 | -------------------------------------------------------------------------------- /hack/conformance-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2021 The CloudEvents Authors 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | set -o errexit 7 | set -o nounset 8 | set -o pipefail 9 | 10 | # test/conformance only 11 | pushd ./test/conformance 12 | 13 | go test --tags=conformance -v -timeout 15s 14 | 15 | # Remove test only deps. 16 | go mod tidy 17 | popd 18 | -------------------------------------------------------------------------------- /hack/go-mod-tidy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2022 The CloudEvents Authors 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | set -o errexit 7 | set -o nounset 8 | set -o pipefail 9 | 10 | for gomodule in $(find . | grep "go\.mod" | awk '{gsub(/\/go.mod/,""); print $0}' | grep -v "./conformance") 11 | do 12 | pushd $gomodule 13 | go mod tidy 14 | popd 15 | done 16 | -------------------------------------------------------------------------------- /hack/integration-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2021 The CloudEvents Authors 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | set -o errexit 7 | set -o nounset 8 | set -o pipefail 9 | 10 | COVERAGE="`pwd`/coverage.txt" 11 | 12 | # ./test/integration only 13 | pushd ./test/integration 14 | 15 | # Run integration tests not in parallel 16 | # Prepare coverage file only if not exists 17 | if [ ! -f $COVERAGE ]; then 18 | touch ./coverage.tmp 19 | echo 'mode: atomic' > $COVERAGE 20 | fi 21 | COVERPKG=$(go list ./... | grep -v /vendor | tr "\n" ",") 22 | for gomodule in $(go list ./... | grep -v /cmd | grep -v /vendor) 23 | do 24 | go test -v -parallel 1 -timeout 10m -race -covermode=atomic -coverprofile=coverage.tmp -coverpkg "$COVERPKG" "$gomodule" 2>&1 | sed 's/ of statements in.*//; /warning: no packages being tested depend on matches for pattern /d' 25 | tail -n +2 coverage.tmp >> $COVERAGE 26 | done 27 | rm coverage.tmp 28 | 29 | # Remove test only deps. 30 | go mod tidy 31 | 32 | popd 33 | -------------------------------------------------------------------------------- /hack/observability-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2021 The CloudEvents Authors 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | set -o errexit 7 | set -o nounset 8 | set -o pipefail 9 | 10 | COVERAGE="`pwd`/coverage.txt" 11 | 12 | # test/observability only 13 | pushd ./test/observability 14 | 15 | # Prepare coverage file only if not exists 16 | if [ ! -f $COVERAGE ]; then 17 | touch ./coverage.tmp 18 | echo 'mode: atomic' > $COVERAGE 19 | fi 20 | COVERPKG="github.com/cloudevents/sdk-go/observability/opentelemetry/v2/..." 21 | for gomodule in $(go list ./... | grep -v /cmd | grep -v /vendor) 22 | do 23 | go test -v -timeout 30s -race -covermode=atomic -coverprofile=coverage.tmp -coverpkg "$COVERPKG" "$gomodule" 2>&1 | sed 's/ of statements in.*//; /warning: no packages being tested depend on matches for pattern /d' 24 | tail -n +2 coverage.tmp >> $COVERAGE 25 | done 26 | rm coverage.tmp 27 | 28 | # Remove test only deps. 29 | go mod tidy 30 | 31 | popd 32 | -------------------------------------------------------------------------------- /hack/run-integration-services.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Run the services needed by the integration test in our local docker install 4 | 5 | if [[ "$1" == "stop" ]]; then 6 | docker rm -f kafka nats amqp mqtt 7 | exit 0 8 | fi 9 | 10 | # Kafka 11 | docker run --name kafka -dti -e ADV_HOST=localhost -p 9091:9091 -p 9092:9092 \ 12 | lensesio/fast-data-dev 13 | 14 | # NATS 15 | docker run --name nats -dti -p 4222:4222 nats-streaming:0.22.1 16 | 17 | # AMQP 18 | docker run --name amqp -dti -e QDROUTERD_CONFIG_OPTIONS=' 19 | router { 20 | mode: standalone 21 | id: ZTg2NDQ0N2Q1YjU1OGE1N2NkNzY4NDFk 22 | workerThreads: 4 23 | } 24 | log { 25 | module: DEFAULT 26 | enable: trace+ 27 | timestamp: true 28 | } 29 | listener { 30 | role: normal 31 | host: 0.0.0.0 32 | port: amqp 33 | saslMechanisms: ANONYMOUS 34 | }' -p 5672:5672 scholzj/qpid-dispatch 35 | 36 | # MQTT 37 | docker run --name mqtt -dti -p 1883:1883 eclipse-mosquitto:1.6 38 | 39 | -------------------------------------------------------------------------------- /hack/unit-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2021 The CloudEvents Authors 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | set -o errexit 7 | set -o nounset 8 | set -o pipefail 9 | 10 | COVERAGE="`pwd`/coverage.txt" 11 | echo 'mode: atomic' > $COVERAGE 12 | 13 | for gomodule in $(find . | grep "go\.mod" | awk '{gsub(/\/go.mod/,""); print $0}' | grep -v "./test" | grep -v "./conformance") 14 | do 15 | echo 16 | echo --- Testing $gomodule --- 17 | echo 18 | 19 | pushd $gomodule 20 | touch ./coverage.tmp 21 | COVERPKG=$(go list ./... | grep -v /vendor | grep -v /test | tr "\n" ",") 22 | 23 | go test -v -timeout 30s -race -covermode=atomic -coverprofile=coverage.tmp -coverpkg "$COVERPKG" ./... 2>&1 | sed 's/ of statements in.*//; /warning: no packages being tested depend on matches for pattern /d' 24 | tail -n +2 coverage.tmp >> $COVERAGE 25 | 26 | rm coverage.tmp 27 | # Remove test only deps. 28 | go mod tidy 29 | popd 30 | done 31 | -------------------------------------------------------------------------------- /hack/update-deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2022 The CloudEvents Authors 3 | # SPDX-License-Identifier: Apache-2.0 4 | # update-deps.sh - Updates Go dependencies in all directories with go.mod files 5 | # 6 | # This script: 7 | # 1. Finds all directories containing go.mod files 8 | # 2. Goes into each directory and runs go get -u to update dependencies 9 | # 3. Runs go mod tidy to clean up the go.mod and go.sum files 10 | 11 | set -euo pipefail 12 | 13 | echo "=====================================" 14 | echo "Go Dependencies Update Script" 15 | echo "=====================================" 16 | 17 | echo "Finding all directories with go.mod files..." 18 | DIRS=$(find . -name "go.mod" -exec dirname {} \;) 19 | if [ -z "$DIRS" ]; then 20 | echo "No go.mod files found!" 21 | exit 0 22 | fi 23 | 24 | DIR_COUNT=$(echo "$DIRS" | wc -l | tr -d ' ') 25 | echo "Found $DIR_COUNT directories with go.mod files" 26 | echo 27 | 28 | COUNTER=1 29 | for DIR in $DIRS; do 30 | echo "[$COUNTER/$DIR_COUNT] Processing $DIR" 31 | 32 | pushd "$DIR" >/dev/null 33 | 34 | echo " - Updating dependencies..." 35 | go get -u -t ./... 36 | 37 | echo " - Running go mod tidy..." 38 | go mod tidy 39 | 40 | popd >/dev/null 41 | 42 | echo " - Done" 43 | echo 44 | 45 | COUNTER=$((COUNTER + 1)) 46 | done 47 | 48 | echo "All dependencies updated successfully!" 49 | -------------------------------------------------------------------------------- /observability/opencensus/v2/client/client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package client 7 | 8 | import ( 9 | obshttp "github.com/cloudevents/sdk-go/observability/opencensus/v2/http" 10 | "github.com/cloudevents/sdk-go/v2/client" 11 | "github.com/cloudevents/sdk-go/v2/protocol/http" 12 | ) 13 | 14 | func NewClientHTTP(topt []http.Option, copt []client.Option) (client.Client, error) { 15 | t, err := obshttp.NewObservedHTTP(topt...) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | copt = append(copt, client.WithTimeNow(), client.WithUUIDs(), client.WithObservabilityService(New())) 21 | 22 | c, err := client.New(t, copt...) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | return c, nil 28 | } 29 | -------------------------------------------------------------------------------- /observability/opencensus/v2/client/trace_attributes.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package client 7 | 8 | import ( 9 | "go.opencensus.io/trace" 10 | 11 | "github.com/cloudevents/sdk-go/v2/event" 12 | "github.com/cloudevents/sdk-go/v2/observability" 13 | ) 14 | 15 | func EventTraceAttributes(e event.EventReader) []trace.Attribute { 16 | as := []trace.Attribute{ 17 | trace.StringAttribute(observability.SpecversionAttr, e.SpecVersion()), 18 | trace.StringAttribute(observability.IdAttr, e.ID()), 19 | trace.StringAttribute(observability.TypeAttr, e.Type()), 20 | trace.StringAttribute(observability.SourceAttr, e.Source()), 21 | } 22 | if sub := e.Subject(); sub != "" { 23 | as = append(as, trace.StringAttribute(observability.SubjectAttr, sub)) 24 | } 25 | if dct := e.DataContentType(); dct != "" { 26 | as = append(as, trace.StringAttribute(observability.DatacontenttypeAttr, dct)) 27 | } 28 | return as 29 | } 30 | -------------------------------------------------------------------------------- /observability/opencensus/v2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/observability/opencensus/v2 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | require ( 8 | github.com/cloudevents/sdk-go/v2 v2.16.0 9 | github.com/lightstep/tracecontext.go v0.0.0-20181129014701-1757c391b1ac 10 | github.com/stretchr/testify v1.10.0 11 | go.opencensus.io v0.24.0 12 | ) 13 | 14 | require ( 15 | github.com/davecgh/go-spew v1.1.1 // indirect 16 | github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect 17 | github.com/google/uuid v1.6.0 // indirect 18 | github.com/json-iterator/go v1.1.12 // indirect 19 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 20 | github.com/modern-go/reflect2 v1.0.2 // indirect 21 | github.com/onsi/ginkgo v1.14.2 // indirect 22 | github.com/onsi/gomega v1.10.4 // indirect 23 | github.com/pmezard/go-difflib v1.0.0 // indirect 24 | go.uber.org/multierr v1.11.0 // indirect 25 | go.uber.org/zap v1.27.0 // indirect 26 | golang.org/x/net v0.38.0 // indirect 27 | gopkg.in/yaml.v3 v3.0.1 // indirect 28 | ) 29 | 30 | replace github.com/cloudevents/sdk-go/v2 => ../../../v2 31 | -------------------------------------------------------------------------------- /observability/opencensus/v2/http/http.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package http 7 | 8 | import ( 9 | "net/http" 10 | 11 | "go.opencensus.io/plugin/ochttp" 12 | "go.opencensus.io/plugin/ochttp/propagation/tracecontext" 13 | 14 | cehttp "github.com/cloudevents/sdk-go/v2/protocol/http" 15 | ) 16 | 17 | func roundtripperDecorator(roundTripper http.RoundTripper) http.RoundTripper { 18 | return &ochttp.Transport{ 19 | Propagation: &tracecontext.HTTPFormat{}, 20 | Base: roundTripper, 21 | FormatSpanName: formatSpanName, 22 | } 23 | } 24 | 25 | func formatSpanName(r *http.Request) string { 26 | return "cloudevents.http." + r.URL.Path 27 | } 28 | 29 | func tracecontextMiddleware(h http.Handler) http.Handler { 30 | return &ochttp.Handler{ 31 | Propagation: &tracecontext.HTTPFormat{}, 32 | Handler: h, 33 | FormatSpanName: formatSpanName, 34 | } 35 | } 36 | 37 | // NewObservedHTTP creates an HTTP protocol with trace propagating middleware. 38 | func NewObservedHTTP(opts ...cehttp.Option) (*cehttp.Protocol, error) { 39 | return cehttp.New(append( 40 | []cehttp.Option{ 41 | cehttp.WithRoundTripperDecorator(roundtripperDecorator), 42 | cehttp.WithMiddleware(tracecontextMiddleware), 43 | }, 44 | opts..., 45 | )...) 46 | } 47 | -------------------------------------------------------------------------------- /observability/opentelemetry/v2/client/client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package client 7 | 8 | import ( 9 | obshttp "github.com/cloudevents/sdk-go/observability/opentelemetry/v2/http" 10 | "github.com/cloudevents/sdk-go/v2/client" 11 | cehttp "github.com/cloudevents/sdk-go/v2/protocol/http" 12 | ) 13 | 14 | // NewClientHTTP produces a new client instrumented with OpenTelemetry. 15 | func NewClientHTTP(topt []cehttp.Option, copt []client.Option, obsOpts ...OTelObservabilityServiceOption) (client.Client, error) { 16 | t, err := obshttp.NewObservedHTTP(topt...) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | copt = append( 22 | copt, 23 | client.WithTimeNow(), 24 | client.WithUUIDs(), 25 | client.WithObservabilityService(NewOTelObservabilityService(obsOpts...)), 26 | ) 27 | 28 | c, err := client.New(t, copt...) 29 | if err != nil { 30 | return nil, err 31 | } 32 | 33 | return c, nil 34 | } 35 | -------------------------------------------------------------------------------- /observability/opentelemetry/v2/client/otel_options.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package client 7 | 8 | import ( 9 | "go.opentelemetry.io/otel/attribute" 10 | 11 | cloudevents "github.com/cloudevents/sdk-go/v2" 12 | "github.com/cloudevents/sdk-go/v2/observability" 13 | ) 14 | 15 | const ( 16 | // The value for the `otel.library.name` span attribute 17 | instrumentationName = "github.com/cloudevents/sdk-go/observability/opentelemetry/v2" 18 | ) 19 | 20 | type OTelObservabilityServiceOption func(*OTelObservabilityService) 21 | 22 | // WithSpanAttributesGetter appends the returned attributes from the function to the span. 23 | func WithSpanAttributesGetter(attrGetter func(cloudevents.Event) []attribute.KeyValue) OTelObservabilityServiceOption { 24 | return func(os *OTelObservabilityService) { 25 | if attrGetter != nil { 26 | os.spanAttributesGetter = attrGetter 27 | } 28 | } 29 | } 30 | 31 | // WithSpanNameFormatter replaces the default span name with the string returned from the function 32 | func WithSpanNameFormatter(nameFormatter func(cloudevents.Event) string) OTelObservabilityServiceOption { 33 | return func(os *OTelObservabilityService) { 34 | if nameFormatter != nil { 35 | os.spanNameFormatter = nameFormatter 36 | } 37 | } 38 | } 39 | 40 | var defaultSpanNameFormatter func(cloudevents.Event) string = func(e cloudevents.Event) string { 41 | return observability.ClientSpanName + "." + e.Context.GetType() 42 | } 43 | -------------------------------------------------------------------------------- /observability/opentelemetry/v2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/observability/opentelemetry/v2 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | require ( 8 | github.com/cloudevents/sdk-go/v2 v2.16.0 9 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 10 | go.opentelemetry.io/otel v1.36.0 11 | go.opentelemetry.io/otel/trace v1.36.0 12 | ) 13 | 14 | require ( 15 | github.com/felixge/httpsnoop v1.0.4 // indirect 16 | github.com/go-logr/logr v1.4.3 // indirect 17 | github.com/go-logr/stdr v1.2.2 // indirect 18 | github.com/google/uuid v1.6.0 // indirect 19 | github.com/json-iterator/go v1.1.12 // indirect 20 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 21 | github.com/modern-go/reflect2 v1.0.2 // indirect 22 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect 23 | go.opentelemetry.io/otel/metric v1.36.0 // indirect 24 | go.uber.org/multierr v1.11.0 // indirect 25 | go.uber.org/zap v1.27.0 // indirect 26 | ) 27 | 28 | replace github.com/cloudevents/sdk-go/v2 => ../../../v2 29 | -------------------------------------------------------------------------------- /observability/opentelemetry/v2/http/http.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package http 7 | 8 | import ( 9 | "net/http" 10 | 11 | "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" 12 | 13 | cehttp "github.com/cloudevents/sdk-go/v2/protocol/http" 14 | ) 15 | 16 | // NewObservedHTTP creates an HTTP protocol with OTel trace propagating middleware. 17 | func NewObservedHTTP(opts ...cehttp.Option) (*cehttp.Protocol, error) { 18 | // appends the OpenTelemetry Http transport + Middleware wrapper 19 | // to properly trace outgoing and incoming requests from the client using this protocol 20 | return cehttp.New(append( 21 | []cehttp.Option{ 22 | cehttp.WithRoundTripper(otelhttp.NewTransport(http.DefaultTransport)), 23 | cehttp.WithMiddleware(func(next http.Handler) http.Handler { 24 | return otelhttp.NewHandler(next, "cloudevents.http.receiver") 25 | }), 26 | }, 27 | opts..., 28 | )...) 29 | } 30 | -------------------------------------------------------------------------------- /protocol/amqp/v2/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package amqp implements an AMQP binding using pack.ag/amqp module 8 | */ 9 | package amqp 10 | -------------------------------------------------------------------------------- /protocol/amqp/v2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/protocol/amqp/v2 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | replace github.com/Azure/go-amqp => github.com/Azure/go-amqp v0.17.0 8 | 9 | replace github.com/cloudevents/sdk-go/v2 => ../../../v2 10 | 11 | require ( 12 | github.com/Azure/go-amqp v1.4.0 13 | github.com/cloudevents/sdk-go/v2 v2.16.0 14 | github.com/stretchr/testify v1.10.0 15 | ) 16 | 17 | require ( 18 | github.com/davecgh/go-spew v1.1.1 // indirect 19 | github.com/google/go-cmp v0.7.0 // indirect 20 | github.com/json-iterator/go v1.1.12 // indirect 21 | github.com/kr/text v0.2.0 // indirect 22 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 23 | github.com/modern-go/reflect2 v1.0.2 // indirect 24 | github.com/pmezard/go-difflib v1.0.0 // indirect 25 | gopkg.in/yaml.v3 v3.0.1 // indirect 26 | ) 27 | -------------------------------------------------------------------------------- /protocol/amqp/v2/options.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package amqp 7 | 8 | import ( 9 | "github.com/Azure/go-amqp" 10 | ) 11 | 12 | // Option is the function signature required to be considered an amqp.Option. 13 | type Option func(*Protocol) error 14 | 15 | // WithConnOpt sets a connection option for amqp 16 | func WithConnOpt(opt amqp.ConnOption) Option { 17 | return func(t *Protocol) error { 18 | t.connOpts = append(t.connOpts, opt) 19 | return nil 20 | } 21 | } 22 | 23 | // WithConnSASLPlain sets SASLPlain connection option for amqp 24 | func WithConnSASLPlain(username, password string) Option { 25 | return WithConnOpt(amqp.ConnSASLPlain(username, password)) 26 | } 27 | 28 | // WithSessionOpt sets a session option for amqp 29 | func WithSessionOpt(opt amqp.SessionOption) Option { 30 | return func(t *Protocol) error { 31 | t.sessionOpts = append(t.sessionOpts, opt) 32 | return nil 33 | } 34 | } 35 | 36 | // WithSenderLinkOption sets a link option for amqp 37 | func WithSenderLinkOption(opt amqp.LinkOption) Option { 38 | return func(t *Protocol) error { 39 | t.senderLinkOpts = append(t.senderLinkOpts, opt) 40 | return nil 41 | } 42 | } 43 | 44 | // WithReceiverLinkOption sets a link option for amqp 45 | func WithReceiverLinkOption(opt amqp.LinkOption) Option { 46 | return func(t *Protocol) error { 47 | t.receiverLinkOpts = append(t.receiverLinkOpts, opt) 48 | return nil 49 | } 50 | } 51 | 52 | // SenderOptionFunc is the type of amqp.Sender options 53 | type SenderOptionFunc func(sender *sender) 54 | -------------------------------------------------------------------------------- /protocol/amqp/v2/receiver.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package amqp 7 | 8 | import ( 9 | "context" 10 | "io" 11 | "strings" 12 | 13 | "github.com/Azure/go-amqp" 14 | 15 | "github.com/cloudevents/sdk-go/v2/binding" 16 | "github.com/cloudevents/sdk-go/v2/protocol" 17 | ) 18 | 19 | const serverDown = "session ended by server" 20 | 21 | // receiver wraps an amqp.Receiver as a binding.Receiver 22 | type receiver struct{ amqp *amqp.Receiver } 23 | 24 | func (r *receiver) Receive(ctx context.Context) (binding.Message, error) { 25 | m, err := r.amqp.Receive(ctx) 26 | if err != nil { 27 | if err == ctx.Err() { 28 | return nil, io.EOF 29 | } 30 | // handle case when server goes down 31 | if strings.HasPrefix(err.Error(), serverDown) { 32 | return nil, io.EOF 33 | } 34 | return nil, err 35 | } 36 | 37 | return NewMessage(m, r.amqp), nil 38 | } 39 | 40 | // NewReceiver create a new Receiver which wraps an amqp.Receiver in a binding.Receiver 41 | func NewReceiver(amqp *amqp.Receiver) protocol.Receiver { 42 | return &receiver{amqp: amqp} 43 | } 44 | -------------------------------------------------------------------------------- /protocol/amqp/v2/sender.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package amqp 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/Azure/go-amqp" 12 | 13 | "github.com/cloudevents/sdk-go/v2/binding" 14 | "github.com/cloudevents/sdk-go/v2/protocol" 15 | ) 16 | 17 | // sender wraps an amqp.Sender as a binding.Sender 18 | type sender struct { 19 | amqp *amqp.Sender 20 | } 21 | 22 | func (s *sender) Send(ctx context.Context, in binding.Message, transformers ...binding.Transformer) error { 23 | var err error 24 | defer func() { _ = in.Finish(err) }() 25 | if m, ok := in.(*Message); ok { // Already an AMQP message. 26 | err = s.amqp.Send(ctx, m.AMQP) 27 | return err 28 | } 29 | 30 | var amqpMessage amqp.Message 31 | err = WriteMessage(ctx, in, &amqpMessage, transformers...) 32 | if err != nil { 33 | return err 34 | } 35 | 36 | err = s.amqp.Send(ctx, &amqpMessage) 37 | return err 38 | } 39 | 40 | // NewSender creates a new Sender which wraps an amqp.Sender in a binding.Sender 41 | func NewSender(amqpSender *amqp.Sender, options ...SenderOptionFunc) protocol.Sender { 42 | s := &sender{amqp: amqpSender} 43 | for _, o := range options { 44 | o(s) 45 | } 46 | return s 47 | } 48 | -------------------------------------------------------------------------------- /protocol/amqp/v2/types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package amqp 7 | 8 | import "github.com/cloudevents/sdk-go/v2/types" 9 | 10 | func safeAMQPPropertiesUnwrap(val interface{}) (interface{}, error) { 11 | v, err := types.Validate(val) 12 | if err != nil { 13 | return nil, err 14 | } 15 | switch t := v.(type) { 16 | case types.URI: // Use string form of URLs. 17 | v = t.String() 18 | case types.URIRef: // Use string form of URLs. 19 | v = t.String() 20 | case types.Timestamp: // Use string form of URLs. 21 | v = t.Time 22 | case int32: // Use AMQP long for Integer as per CE spec. 23 | v = int64(t) 24 | } 25 | 26 | return v, nil 27 | } 28 | -------------------------------------------------------------------------------- /protocol/kafka_confluent/v2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/protocol/kafka_confluent/v2 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | replace github.com/cloudevents/sdk-go/v2 => ../../../v2 8 | 9 | require ( 10 | github.com/cloudevents/sdk-go/v2 v2.16.0 11 | github.com/confluentinc/confluent-kafka-go/v2 v2.10.0 12 | github.com/stretchr/testify v1.10.0 13 | ) 14 | 15 | require ( 16 | github.com/davecgh/go-spew v1.1.1 // indirect 17 | github.com/google/go-cmp v0.7.0 // indirect 18 | github.com/google/uuid v1.6.0 // indirect 19 | github.com/json-iterator/go v1.1.12 // indirect 20 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 21 | github.com/modern-go/reflect2 v1.0.2 // indirect 22 | github.com/pmezard/go-difflib v1.0.0 // indirect 23 | go.uber.org/multierr v1.11.0 // indirect 24 | go.uber.org/zap v1.27.0 // indirect 25 | gopkg.in/yaml.v3 v3.0.1 // indirect 26 | ) 27 | -------------------------------------------------------------------------------- /protocol/kafka_sarama/v2/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package kafka_sarama implements a Kafka binding using github.com/IBM/sarama module 8 | */ 9 | package kafka_sarama 10 | -------------------------------------------------------------------------------- /protocol/kafka_sarama/v2/message_benchmark_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package kafka_sarama_test 7 | 8 | import ( 9 | "context" 10 | "testing" 11 | 12 | "github.com/cloudevents/sdk-go/protocol/kafka_sarama/v2" 13 | "github.com/cloudevents/sdk-go/v2/binding" 14 | "github.com/cloudevents/sdk-go/v2/event" 15 | ) 16 | 17 | // Avoid DCE 18 | var M binding.Message 19 | var Event *event.Event 20 | var Err error 21 | 22 | func BenchmarkNewStructuredMessage(b *testing.B) { 23 | for i := 0; i < b.N; i++ { 24 | M = kafka_sarama.NewMessageFromConsumerMessage(structuredConsumerMessage) 25 | } 26 | } 27 | 28 | func BenchmarkNewBinaryMessage(b *testing.B) { 29 | for i := 0; i < b.N; i++ { 30 | M = kafka_sarama.NewMessageFromConsumerMessage(binaryConsumerMessage) 31 | } 32 | } 33 | 34 | func BenchmarkNewStructuredMessageToEvent(b *testing.B) { 35 | for i := 0; i < b.N; i++ { 36 | M = kafka_sarama.NewMessageFromConsumerMessage(structuredConsumerMessage) 37 | Event, Err = binding.ToEvent(context.TODO(), M) 38 | } 39 | } 40 | 41 | func BenchmarkNewBinaryMessageToEvent(b *testing.B) { 42 | for i := 0; i < b.N; i++ { 43 | M = kafka_sarama.NewMessageFromConsumerMessage(binaryConsumerMessage) 44 | Event, Err = binding.ToEvent(context.TODO(), M) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /protocol/kafka_sarama/v2/option.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package kafka_sarama 7 | 8 | import ( 9 | "context" 10 | ) 11 | 12 | // SenderOptionFunc is the type of kafka_sarama.Sender options 13 | type SenderOptionFunc func(sender *Sender) 14 | 15 | // ProtocolOptionFunc is the type of kafka_sarama.Protocol options 16 | type ProtocolOptionFunc func(protocol *Protocol) 17 | 18 | func WithReceiverGroupId(groupId string) ProtocolOptionFunc { 19 | return func(protocol *Protocol) { 20 | protocol.receiverGroupId = groupId 21 | } 22 | } 23 | 24 | func WithSenderContextDecorators(decorator func(context.Context) context.Context) ProtocolOptionFunc { 25 | return func(protocol *Protocol) { 26 | protocol.SenderContextDecorators = append(protocol.SenderContextDecorators, decorator) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /protocol/mqtt_paho/v2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/protocol/mqtt_paho/v2 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | replace github.com/cloudevents/sdk-go/v2 => ../../../v2 8 | 9 | require ( 10 | github.com/cloudevents/sdk-go/v2 v2.16.0 11 | github.com/eclipse/paho.golang v0.22.0 12 | github.com/stretchr/testify v1.10.0 13 | ) 14 | 15 | require ( 16 | github.com/davecgh/go-spew v1.1.1 // indirect 17 | github.com/google/go-cmp v0.7.0 // indirect 18 | github.com/json-iterator/go v1.1.12 // indirect 19 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 20 | github.com/modern-go/reflect2 v1.0.2 // indirect 21 | github.com/pmezard/go-difflib v1.0.0 // indirect 22 | go.uber.org/multierr v1.11.0 // indirect 23 | go.uber.org/zap v1.27.0 // indirect 24 | gopkg.in/yaml.v3 v3.0.1 // indirect 25 | ) 26 | -------------------------------------------------------------------------------- /protocol/mqtt_paho/v2/option.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package mqtt_paho 7 | 8 | import ( 9 | "fmt" 10 | 11 | "github.com/eclipse/paho.golang/paho" 12 | ) 13 | 14 | // Option is the function signature required to be considered an mqtt_paho.Option. 15 | type Option func(*Protocol) error 16 | 17 | // WithConnect sets the paho.Connect configuration for the client. This option is not required. 18 | func WithConnect(connOpt *paho.Connect) Option { 19 | return func(p *Protocol) error { 20 | if connOpt == nil { 21 | return fmt.Errorf("the paho.Connect option must not be nil") 22 | } 23 | p.connOption = connOpt 24 | return nil 25 | } 26 | } 27 | 28 | // WithPublish sets the paho.Publish configuration for the client. This option is required if you want to send messages. 29 | func WithPublish(publishOpt *paho.Publish) Option { 30 | return func(p *Protocol) error { 31 | if publishOpt == nil { 32 | return fmt.Errorf("the paho.Publish option must not be nil") 33 | } 34 | p.publishOption = publishOpt 35 | return nil 36 | } 37 | } 38 | 39 | // WithSubscribe sets the paho.Subscribe configuration for the client. This option is required if you want to receive messages. 40 | func WithSubscribe(subscribeOpt *paho.Subscribe) Option { 41 | return func(p *Protocol) error { 42 | if subscribeOpt == nil { 43 | return fmt.Errorf("the paho.Subscribe option must not be nil") 44 | } 45 | p.subscribeOption = subscribeOpt 46 | return nil 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /protocol/nats/v2/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package nats implements the CloudEvent transport implementation using NATS. 8 | */ 9 | package nats 10 | -------------------------------------------------------------------------------- /protocol/nats/v2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/protocol/nats/v2 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | replace github.com/cloudevents/sdk-go/v2 => ../../../v2 8 | 9 | require ( 10 | github.com/cloudevents/sdk-go/v2 v2.16.0 11 | github.com/nats-io/nats.go v1.42.0 12 | ) 13 | 14 | require ( 15 | github.com/json-iterator/go v1.1.12 // indirect 16 | github.com/klauspost/compress v1.18.0 // indirect 17 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 18 | github.com/modern-go/reflect2 v1.0.2 // indirect 19 | github.com/nats-io/nkeys v0.4.11 // indirect 20 | github.com/nats-io/nuid v1.0.1 // indirect 21 | golang.org/x/crypto v0.38.0 // indirect 22 | golang.org/x/sys v0.33.0 // indirect 23 | ) 24 | -------------------------------------------------------------------------------- /protocol/nats/v2/message.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package nats 7 | 8 | import ( 9 | "bytes" 10 | "context" 11 | 12 | "github.com/cloudevents/sdk-go/v2/binding" 13 | "github.com/cloudevents/sdk-go/v2/binding/format" 14 | "github.com/nats-io/nats.go" 15 | ) 16 | 17 | // Message implements binding.Message by wrapping an *nats.Msg. 18 | // This message *can* be read several times safely 19 | type Message struct { 20 | Msg *nats.Msg 21 | encoding binding.Encoding 22 | } 23 | 24 | // NewMessage wraps an *nats.Msg in a binding.Message. 25 | // The returned message *can* be read several times safely 26 | func NewMessage(msg *nats.Msg) *Message { 27 | return &Message{Msg: msg, encoding: binding.EncodingStructured} 28 | } 29 | 30 | var _ binding.Message = (*Message)(nil) 31 | 32 | func (m *Message) ReadEncoding() binding.Encoding { 33 | return m.encoding 34 | } 35 | 36 | func (m *Message) ReadStructured(ctx context.Context, encoder binding.StructuredWriter) error { 37 | return encoder.SetStructuredEvent(ctx, format.JSON, bytes.NewReader(m.Msg.Data)) 38 | } 39 | 40 | func (m *Message) ReadBinary(ctx context.Context, encoder binding.BinaryWriter) error { 41 | return binding.ErrNotBinary 42 | } 43 | 44 | func (m *Message) Finish(err error) error { 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /protocol/nats/v2/options.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package nats 7 | 8 | import ( 9 | "errors" 10 | 11 | "github.com/nats-io/nats.go" 12 | ) 13 | 14 | var ErrInvalidQueueName = errors.New("invalid queue name for QueueSubscriber") 15 | 16 | // NatsOptions is a helper function to group a variadic nats.ProtocolOption into 17 | // []nats.Option that can be used by either Sender, Consumer or Protocol 18 | func NatsOptions(opts ...nats.Option) []nats.Option { 19 | return opts 20 | } 21 | 22 | // ProtocolOption is the function signature required to be considered an nats.ProtocolOption. 23 | type ProtocolOption func(*Protocol) error 24 | 25 | func WithConsumerOptions(opts ...ConsumerOption) ProtocolOption { 26 | return func(p *Protocol) error { 27 | p.consumerOptions = opts 28 | return nil 29 | } 30 | } 31 | 32 | func WithSenderOptions(opts ...SenderOption) ProtocolOption { 33 | return func(p *Protocol) error { 34 | p.senderOptions = opts 35 | return nil 36 | } 37 | } 38 | 39 | type SenderOption func(*Sender) error 40 | 41 | type ConsumerOption func(*Consumer) error 42 | 43 | // WithQueueSubscriber configures the Consumer to join a queue group when subscribing 44 | func WithQueueSubscriber(queue string) ConsumerOption { 45 | return func(c *Consumer) error { 46 | if queue == "" { 47 | return ErrInvalidQueueName 48 | } 49 | c.Subscriber = &QueueSubscriber{Queue: queue} 50 | return nil 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /protocol/nats/v2/options_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package nats 7 | 8 | import ( 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func TestWithQueueSubscriber(t *testing.T) { 14 | type args struct { 15 | consumer *Consumer 16 | queue string 17 | } 18 | type wants struct { 19 | err error 20 | consumer *Consumer 21 | } 22 | tests := []struct { 23 | name string 24 | args args 25 | wants wants 26 | }{ 27 | { 28 | name: "valid queue", 29 | args: args{ 30 | consumer: &Consumer{}, 31 | queue: "my-queue", 32 | }, 33 | wants: wants{ 34 | err: nil, 35 | consumer: &Consumer{ 36 | Subscriber: &QueueSubscriber{Queue: "my-queue"}, 37 | }, 38 | }, 39 | }, 40 | { 41 | name: "invalid queue", 42 | args: args{ 43 | consumer: &Consumer{}, 44 | queue: "", 45 | }, 46 | wants: wants{ 47 | err: ErrInvalidQueueName, 48 | consumer: &Consumer{}, 49 | }, 50 | }, 51 | } 52 | for _, tt := range tests { 53 | t.Run(tt.name, func(t *testing.T) { 54 | gotErr := tt.args.consumer.applyOptions(WithQueueSubscriber(tt.args.queue)) 55 | if gotErr != tt.wants.err { 56 | t.Errorf("applyOptions(WithQueueSubscriber()) = %v, want %v", gotErr, tt.wants.err) 57 | } 58 | 59 | if !reflect.DeepEqual(tt.args.consumer, tt.wants.consumer) { 60 | t.Errorf("p = %v, want %v", tt.args.consumer, tt.wants.consumer) 61 | } 62 | }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /protocol/nats/v2/subscriber.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package nats 7 | 8 | import ( 9 | "github.com/nats-io/nats.go" 10 | ) 11 | 12 | // The Subscriber interface allows us to configure how the subscription is created 13 | type Subscriber interface { 14 | Subscribe(conn *nats.Conn, subject string, cb nats.MsgHandler) (*nats.Subscription, error) 15 | } 16 | 17 | // RegularSubscriber creates regular subscriptions 18 | type RegularSubscriber struct { 19 | } 20 | 21 | // Subscribe implements Subscriber.Subscribe 22 | func (s *RegularSubscriber) Subscribe(conn *nats.Conn, subject string, cb nats.MsgHandler) (*nats.Subscription, error) { 23 | return conn.Subscribe(subject, cb) 24 | } 25 | 26 | var _ Subscriber = (*RegularSubscriber)(nil) 27 | 28 | // QueueSubscriber creates queue subscriptions 29 | type QueueSubscriber struct { 30 | Queue string 31 | } 32 | 33 | // Subscribe implements Subscriber.Subscribe 34 | func (s *QueueSubscriber) Subscribe(conn *nats.Conn, subject string, cb nats.MsgHandler) (*nats.Subscription, error) { 35 | return conn.QueueSubscribe(subject, s.Queue, cb) 36 | } 37 | 38 | var _ Subscriber = (*QueueSubscriber)(nil) 39 | -------------------------------------------------------------------------------- /protocol/nats/v2/write_message.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package nats 7 | 8 | import ( 9 | "context" 10 | "github.com/cloudevents/sdk-go/v2/binding" 11 | "github.com/cloudevents/sdk-go/v2/binding/format" 12 | "io" 13 | ) 14 | 15 | // WriteMsg fills the provided writer with the bindings.Message m. 16 | // Using context you can tweak the encoding processing (more details on binding.Write documentation). 17 | func WriteMsg(ctx context.Context, m binding.Message, writer io.ReaderFrom, transformers ...binding.Transformer) error { 18 | structuredWriter := &natsMessageWriter{writer} 19 | 20 | _, err := binding.Write( 21 | ctx, 22 | m, 23 | structuredWriter, 24 | nil, 25 | transformers..., 26 | ) 27 | return err 28 | } 29 | 30 | type natsMessageWriter struct { 31 | io.ReaderFrom 32 | } 33 | 34 | func (w *natsMessageWriter) SetStructuredEvent(_ context.Context, _ format.Format, event io.Reader) error { 35 | if _, err := w.ReadFrom(event); err != nil { 36 | return err 37 | } 38 | 39 | return nil 40 | } 41 | 42 | var _ binding.StructuredWriter = (*natsMessageWriter)(nil) // Test it conforms to the interface 43 | -------------------------------------------------------------------------------- /protocol/nats_jetstream/v2/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package nats_jetstream implements the CloudEvent transport implementation using NATS JetStream. 8 | */ 9 | package nats_jetstream 10 | -------------------------------------------------------------------------------- /protocol/nats_jetstream/v2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/protocol/nats_jetstream/v2 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | replace github.com/cloudevents/sdk-go/v2 => ../../../v2 8 | 9 | require ( 10 | github.com/cloudevents/sdk-go/v2 v2.16.0 11 | github.com/nats-io/nats.go v1.42.0 12 | ) 13 | 14 | require ( 15 | github.com/davecgh/go-spew v1.1.1 // indirect 16 | github.com/google/go-cmp v0.7.0 // indirect 17 | github.com/json-iterator/go v1.1.12 // indirect 18 | github.com/klauspost/compress v1.18.0 // indirect 19 | github.com/kr/text v0.2.0 // indirect 20 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 21 | github.com/modern-go/reflect2 v1.0.2 // indirect 22 | github.com/nats-io/nkeys v0.4.11 // indirect 23 | github.com/nats-io/nuid v1.0.1 // indirect 24 | github.com/pmezard/go-difflib v1.0.0 // indirect 25 | github.com/stretchr/testify v1.10.0 // indirect 26 | golang.org/x/crypto v0.38.0 // indirect 27 | golang.org/x/sys v0.33.0 // indirect 28 | gopkg.in/yaml.v3 v3.0.1 // indirect 29 | ) 30 | -------------------------------------------------------------------------------- /protocol/nats_jetstream/v2/options.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package nats_jetstream 7 | 8 | import ( 9 | "errors" 10 | 11 | "github.com/nats-io/nats.go" 12 | ) 13 | 14 | var ErrInvalidQueueName = errors.New("invalid queue name for QueueSubscriber") 15 | 16 | // NatsOptions is a helper function to group a variadic nats.ProtocolOption into 17 | // []nats.Option that can be used by either Sender, Consumer or Protocol 18 | func NatsOptions(opts ...nats.Option) []nats.Option { 19 | return opts 20 | } 21 | 22 | // ProtocolOption is the function signature required to be considered an nats.ProtocolOption. 23 | type ProtocolOption func(*Protocol) error 24 | 25 | func WithConsumerOptions(opts ...ConsumerOption) ProtocolOption { 26 | return func(p *Protocol) error { 27 | p.consumerOptions = opts 28 | return nil 29 | } 30 | } 31 | 32 | func WithSenderOptions(opts ...SenderOption) ProtocolOption { 33 | return func(p *Protocol) error { 34 | p.senderOptions = opts 35 | return nil 36 | } 37 | } 38 | 39 | type SenderOption func(*Sender) error 40 | 41 | type ConsumerOption func(*Consumer) error 42 | 43 | // WithQueueSubscriber configures the Consumer to join a queue group when subscribing 44 | func WithQueueSubscriber(queue string) ConsumerOption { 45 | return func(c *Consumer) error { 46 | if queue == "" { 47 | return ErrInvalidQueueName 48 | } 49 | c.Subscriber = &QueueSubscriber{Queue: queue} 50 | return nil 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /protocol/nats_jetstream/v2/options_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package nats_jetstream 7 | 8 | import ( 9 | "reflect" 10 | "testing" 11 | ) 12 | 13 | func TestWithQueueSubscriber(t *testing.T) { 14 | type args struct { 15 | consumer *Consumer 16 | queue string 17 | } 18 | type wants struct { 19 | err error 20 | consumer *Consumer 21 | } 22 | tests := []struct { 23 | name string 24 | args args 25 | wants wants 26 | }{ 27 | { 28 | name: "valid queue", 29 | args: args{ 30 | consumer: &Consumer{}, 31 | queue: "my-queue", 32 | }, 33 | wants: wants{ 34 | err: nil, 35 | consumer: &Consumer{ 36 | Subscriber: &QueueSubscriber{Queue: "my-queue"}, 37 | }, 38 | }, 39 | }, 40 | { 41 | name: "invalid queue", 42 | args: args{ 43 | consumer: &Consumer{}, 44 | queue: "", 45 | }, 46 | wants: wants{ 47 | err: ErrInvalidQueueName, 48 | consumer: &Consumer{}, 49 | }, 50 | }, 51 | } 52 | for _, tt := range tests { 53 | t.Run(tt.name, func(t *testing.T) { 54 | gotErr := tt.args.consumer.applyOptions(WithQueueSubscriber(tt.args.queue)) 55 | if gotErr != tt.wants.err { 56 | t.Errorf("applyOptions(WithQueueSubscriber()) = %v, want %v", gotErr, tt.wants.err) 57 | } 58 | 59 | if !reflect.DeepEqual(tt.args.consumer, tt.wants.consumer) { 60 | t.Errorf("p = %v, want %v", tt.args.consumer, tt.wants.consumer) 61 | } 62 | }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /protocol/nats_jetstream/v2/subscriber.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package nats_jetstream 7 | 8 | import ( 9 | "github.com/nats-io/nats.go" 10 | ) 11 | 12 | // The Subscriber interface allows us to configure how the subscription is created 13 | type Subscriber interface { 14 | Subscribe(jsm nats.JetStreamContext, subject string, cb nats.MsgHandler, opts ...nats.SubOpt) (*nats.Subscription, error) 15 | } 16 | 17 | // RegularSubscriber creates regular subscriptions 18 | type RegularSubscriber struct { 19 | } 20 | 21 | // Subscribe implements Subscriber.Subscribe 22 | func (s *RegularSubscriber) Subscribe(jsm nats.JetStreamContext, subject string, cb nats.MsgHandler, opts ...nats.SubOpt) (*nats.Subscription, error) { 23 | return jsm.Subscribe(subject, cb, opts...) 24 | } 25 | 26 | var _ Subscriber = (*RegularSubscriber)(nil) 27 | 28 | // QueueSubscriber creates queue subscriptions 29 | type QueueSubscriber struct { 30 | Queue string 31 | } 32 | 33 | // Subscribe implements Subscriber.Subscribe 34 | func (s *QueueSubscriber) Subscribe(jsm nats.JetStreamContext, subject string, cb nats.MsgHandler, opts ...nats.SubOpt) (*nats.Subscription, error) { 35 | return jsm.QueueSubscribe(subject, s.Queue, cb, opts...) 36 | } 37 | 38 | var _ Subscriber = (*QueueSubscriber)(nil) 39 | -------------------------------------------------------------------------------- /protocol/nats_jetstream/v3/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/protocol/nats_jetstream/v3 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | replace github.com/cloudevents/sdk-go/v2 => ../../../v2 8 | 9 | require ( 10 | github.com/cloudevents/sdk-go/v2 v2.16.0 11 | github.com/nats-io/nats.go v1.42.0 12 | ) 13 | 14 | require ( 15 | github.com/davecgh/go-spew v1.1.1 // indirect 16 | github.com/google/go-cmp v0.7.0 // indirect 17 | github.com/json-iterator/go v1.1.12 // indirect 18 | github.com/klauspost/compress v1.18.0 // indirect 19 | github.com/kr/text v0.2.0 // indirect 20 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 21 | github.com/modern-go/reflect2 v1.0.2 // indirect 22 | github.com/nats-io/nkeys v0.4.11 // indirect 23 | github.com/nats-io/nuid v1.0.1 // indirect 24 | github.com/pmezard/go-difflib v1.0.0 // indirect 25 | github.com/stretchr/testify v1.10.0 // indirect 26 | golang.org/x/crypto v0.38.0 // indirect 27 | golang.org/x/sys v0.33.0 // indirect 28 | gopkg.in/yaml.v3 v3.0.1 // indirect 29 | ) 30 | -------------------------------------------------------------------------------- /protocol/pubsub/v2/attributes.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package pubsub 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/cloudevents/sdk-go/v2/binding" 12 | ) 13 | 14 | type withCustomAttributes struct{} 15 | 16 | func AttributesFrom(ctx context.Context) map[string]string { 17 | return binding.GetOrDefaultFromCtx(ctx, withCustomAttributes{}, make(map[string]string)).(map[string]string) 18 | } 19 | 20 | // WithCustomAttributes sets Message Attributes without any CloudEvent logic. 21 | // Note that this function is not intended for CloudEvent Extensions or any `ce-`-prefixed Attributes. 22 | // For these please see `Event` and `Event.SetExtension`. 23 | func WithCustomAttributes(ctx context.Context, attrs map[string]string) context.Context { 24 | return context.WithValue(ctx, withCustomAttributes{}, attrs) 25 | } 26 | -------------------------------------------------------------------------------- /protocol/pubsub/v2/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package pubsub implements a Pub/Sub binding using google.cloud.com/go/pubsub module 8 | 9 | PubSub Messages can be modified beyond what CloudEvents cover by using `WithOrderingKey` 10 | or `WithCustomAttributes`. See function docs for more details. 11 | */ 12 | package pubsub 13 | -------------------------------------------------------------------------------- /protocol/stan/v2/context.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package stan 7 | 8 | import ( 9 | "context" 10 | "github.com/cloudevents/sdk-go/v2/binding" 11 | ) 12 | 13 | // MsgMetadata holds metadata of a received *stan.Msg. This information is kept in a separate struct so that users 14 | // cannot interact with the underlying message outside of the SDK. 15 | type MsgMetadata struct { 16 | Sequence uint64 17 | Redelivered bool 18 | RedeliveryCount uint32 19 | } 20 | 21 | type msgKeyType struct{} 22 | 23 | var msgKey msgKeyType 24 | 25 | // MetadataContextDecorator returns an inbound context decorator which adds STAN message metadata to 26 | // the current context. If the inbound message is not a *stan.Message then this decorator is a no-op. 27 | func MetadataContextDecorator() func(context.Context, binding.Message) context.Context { 28 | return func(ctx context.Context, m binding.Message) context.Context { 29 | if msg, ok := m.(*Message); ok { 30 | return context.WithValue(ctx, msgKey, MsgMetadata{ 31 | Sequence: msg.Msg.Sequence, 32 | Redelivered: msg.Msg.Redelivered, 33 | RedeliveryCount: msg.Msg.RedeliveryCount, 34 | }) 35 | } 36 | 37 | return ctx 38 | } 39 | } 40 | 41 | // MessageMetadataFrom extracts the STAN message metadata from the provided ctx. The bool return parameter is true if 42 | // the metadata was set on the context, or false otherwise. 43 | func MessageMetadataFrom(ctx context.Context) (MsgMetadata, bool) { 44 | if v, ok := ctx.Value(msgKey).(MsgMetadata); ok { 45 | return v, true 46 | } 47 | 48 | return MsgMetadata{}, false 49 | } 50 | -------------------------------------------------------------------------------- /protocol/stan/v2/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package stan implements the CloudEvent transport implementation using NATS Streaming. 8 | */ 9 | package stan 10 | -------------------------------------------------------------------------------- /protocol/stan/v2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/protocol/stan/v2 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | replace github.com/cloudevents/sdk-go/v2 => ../../../v2 8 | 9 | require ( 10 | github.com/cloudevents/sdk-go/v2 v2.16.0 11 | github.com/nats-io/stan.go v0.10.4 12 | ) 13 | 14 | require ( 15 | github.com/gogo/protobuf v1.3.2 // indirect 16 | github.com/json-iterator/go v1.1.12 // indirect 17 | github.com/klauspost/compress v1.18.0 // indirect 18 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 19 | github.com/modern-go/reflect2 v1.0.2 // indirect 20 | github.com/nats-io/nats-server/v2 v2.10.27 // indirect 21 | github.com/nats-io/nats-streaming-server v0.25.6 // indirect 22 | github.com/nats-io/nats.go v1.42.0 // indirect 23 | github.com/nats-io/nkeys v0.4.11 // indirect 24 | github.com/nats-io/nuid v1.0.1 // indirect 25 | golang.org/x/crypto v0.38.0 // indirect 26 | golang.org/x/sys v0.33.0 // indirect 27 | ) 28 | -------------------------------------------------------------------------------- /protocol/stan/v2/subscriber.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package stan 7 | 8 | import "github.com/nats-io/stan.go" 9 | 10 | // The Subscriber interface allows us to configure how the subscription is created 11 | type Subscriber interface { 12 | Subscribe(conn stan.Conn, subject string, cb stan.MsgHandler, 13 | opts ...stan.SubscriptionOption) (stan.Subscription, error) 14 | } 15 | 16 | // RegularSubscriber creates regular subscriptions 17 | type RegularSubscriber struct { 18 | } 19 | 20 | // Subscribe implements Subscriber.Subscribe 21 | func (s *RegularSubscriber) Subscribe(conn stan.Conn, subject string, cb stan.MsgHandler, 22 | opts ...stan.SubscriptionOption) (stan.Subscription, error) { 23 | return conn.Subscribe(subject, cb, opts...) 24 | } 25 | 26 | var _ Subscriber = (*RegularSubscriber)(nil) 27 | 28 | // QueueSubscriber creates queue subscriptions 29 | type QueueSubscriber struct { 30 | QueueGroup string 31 | } 32 | 33 | // Subscribe implements Subscriber.Subscribe 34 | func (s *QueueSubscriber) Subscribe(conn stan.Conn, subject string, cb stan.MsgHandler, 35 | opts ...stan.SubscriptionOption) (stan.Subscription, error) { 36 | return conn.QueueSubscribe(subject, s.QueueGroup, cb, opts...) 37 | } 38 | 39 | var _ Subscriber = (*QueueSubscriber)(nil) 40 | -------------------------------------------------------------------------------- /protocol/stan/v2/write_message.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package stan 7 | 8 | import ( 9 | "context" 10 | "github.com/cloudevents/sdk-go/v2/binding" 11 | "github.com/cloudevents/sdk-go/v2/binding/format" 12 | "io" 13 | ) 14 | 15 | // WriteMsg fills the provided writer with the bindings.Message m. 16 | // Using context you can tweak the encoding processing (more details on binding.Write documentation). 17 | func WriteMsg(ctx context.Context, m binding.Message, writer io.ReaderFrom, transformers ...binding.Transformer) error { 18 | structuredWriter := &stanMessageWriter{writer} 19 | 20 | _, err := binding.Write( 21 | ctx, 22 | m, 23 | structuredWriter, 24 | nil, 25 | transformers..., 26 | ) 27 | return err 28 | } 29 | 30 | type stanMessageWriter struct { 31 | io.ReaderFrom 32 | } 33 | 34 | func (w *stanMessageWriter) SetStructuredEvent(_ context.Context, _ format.Format, event io.Reader) error { 35 | if _, err := w.ReadFrom(event); err != nil { 36 | return err 37 | } 38 | 39 | return nil 40 | } 41 | 42 | var _ binding.StructuredWriter = (*stanMessageWriter)(nil) // Test it conforms to the interface 43 | -------------------------------------------------------------------------------- /protocol/ws/v2/context.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package v2 7 | 8 | import ( 9 | "context" 10 | 11 | "nhooyr.io/websocket" 12 | ) 13 | 14 | type codeKey struct{} 15 | 16 | type reasonKey struct{} 17 | 18 | func WithCloseReason(ctx context.Context, code websocket.StatusCode, reason string) context.Context { 19 | return context.WithValue(context.WithValue(ctx, codeKey{}, code), reasonKey{}, reason) 20 | } 21 | -------------------------------------------------------------------------------- /protocol/ws/v2/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package ws implements the Websocket protocol binding 8 | */ 9 | package v2 10 | -------------------------------------------------------------------------------- /protocol/ws/v2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/protocol/ws/v2 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | replace github.com/cloudevents/sdk-go/v2 => ../../../v2 8 | 9 | require ( 10 | github.com/cloudevents/sdk-go/v2 v2.16.0 11 | github.com/stretchr/testify v1.10.0 12 | nhooyr.io/websocket v1.8.17 13 | ) 14 | 15 | require ( 16 | github.com/davecgh/go-spew v1.1.1 // indirect 17 | github.com/google/go-cmp v0.7.0 // indirect 18 | github.com/google/uuid v1.6.0 // indirect 19 | github.com/json-iterator/go v1.1.12 // indirect 20 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 21 | github.com/modern-go/reflect2 v1.0.2 // indirect 22 | github.com/pmezard/go-difflib v1.0.0 // indirect 23 | go.uber.org/multierr v1.11.0 // indirect 24 | go.uber.org/zap v1.27.0 // indirect 25 | gopkg.in/yaml.v3 v3.0.1 // indirect 26 | ) 27 | -------------------------------------------------------------------------------- /protocol/ws/v2/subprotocols.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package v2 7 | 8 | import ( 9 | "fmt" 10 | 11 | "nhooyr.io/websocket" 12 | 13 | "github.com/cloudevents/sdk-go/v2/binding/format" 14 | ) 15 | 16 | const JsonSubprotocol = "cloudevents.json" 17 | 18 | var SupportedSubprotocols = []string{JsonSubprotocol} 19 | 20 | func resolveFormat(subprotocol string) (format.Format, websocket.MessageType, error) { 21 | switch subprotocol { 22 | case "cloudevents.json": 23 | return format.JSON, websocket.MessageText, nil 24 | default: 25 | return nil, websocket.MessageText, fmt.Errorf("subprotocol not supported: %s", subprotocol) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /protocol/ws/v2/subprotocols_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package v2 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/stretchr/testify/require" 12 | "nhooyr.io/websocket" 13 | 14 | "github.com/cloudevents/sdk-go/v2/binding/format" 15 | ) 16 | 17 | func TestResolveFormat(t *testing.T) { 18 | tests := []struct { 19 | name string 20 | subprotocol string 21 | wantFormat format.Format 22 | wantMessageType websocket.MessageType 23 | }{{ 24 | name: "JSON subprotocol", 25 | subprotocol: JsonSubprotocol, 26 | wantFormat: format.JSON, 27 | wantMessageType: websocket.MessageText, 28 | }} 29 | for _, tt := range tests { 30 | t.Run(tt.name, func(t *testing.T) { 31 | fmt, messageType, err := resolveFormat(tt.subprotocol) 32 | require.NoError(t, err) 33 | require.Equal(t, tt.wantFormat, fmt) 34 | require.Equal(t, tt.wantMessageType, messageType) 35 | }) 36 | } 37 | } 38 | 39 | func TestResolveFormatError(t *testing.T) { 40 | _, _, err := resolveFormat("lalala") 41 | require.Error(t, err, "subprotocol not supported: lalala") 42 | } 43 | -------------------------------------------------------------------------------- /samples/amqp/README.md: -------------------------------------------------------------------------------- 1 | # AMQP Samples 2 | 3 | These samples require an AMQP 1.0 broker or router to be running. 4 | 5 | One option is http://qpid.apache.org/components/dispatch-router/index.html 6 | It can be installed via dnf or apt, or from source: https://qpid.apache.org/packages.html 7 | Run `qdrouterd` and the samples will work without any additional configuration. 8 | 9 | ## Sample configuration 10 | 11 | The environment variable AMQP_URL can be set to indicate the location of the broker 12 | and the AMQP node address. It has this form: 13 | 14 | amqp://user:pass@host:port/node 15 | 16 | The default if AMQP_URL is not set is: 17 | 18 | amqp://localhost:5672/test 19 | 20 | *Note*: setting `user:pass` in a URL is not recommended in production, 21 | it is done here to simplify using the samples. 22 | 23 | 24 | -------------------------------------------------------------------------------- /samples/amqp/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/samples/amqp 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | require ( 8 | github.com/Azure/go-amqp v1.4.0 9 | github.com/cloudevents/sdk-go/protocol/amqp/v2 v2.16.0 10 | github.com/cloudevents/sdk-go/v2 v2.16.0 11 | github.com/google/uuid v1.6.0 12 | ) 13 | 14 | require ( 15 | github.com/json-iterator/go v1.1.12 // indirect 16 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 17 | github.com/modern-go/reflect2 v1.0.2 // indirect 18 | go.uber.org/multierr v1.11.0 // indirect 19 | go.uber.org/zap v1.27.0 // indirect 20 | ) 21 | 22 | replace github.com/cloudevents/sdk-go/v2 => ../../v2 23 | 24 | replace github.com/cloudevents/sdk-go/protocol/amqp/v2 => ../../protocol/amqp/v2 25 | 26 | replace github.com/Azure/go-amqp => github.com/Azure/go-amqp v0.17.0 27 | -------------------------------------------------------------------------------- /samples/gochan/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/samples/gochan 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | require github.com/cloudevents/sdk-go/v2 v2.16.0 8 | 9 | require ( 10 | github.com/google/uuid v1.6.0 // indirect 11 | github.com/json-iterator/go v1.1.12 // indirect 12 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 13 | github.com/modern-go/reflect2 v1.0.2 // indirect 14 | go.uber.org/multierr v1.11.0 // indirect 15 | go.uber.org/zap v1.27.0 // indirect 16 | ) 17 | 18 | replace github.com/cloudevents/sdk-go/v2 => ../../v2 19 | -------------------------------------------------------------------------------- /samples/http/receiver-direct/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package main 7 | 8 | import ( 9 | "context" 10 | "fmt" 11 | "log" 12 | "net/http" 13 | 14 | cloudevents "github.com/cloudevents/sdk-go/v2" 15 | ) 16 | 17 | func main() { 18 | ctx := context.Background() 19 | p, err := cloudevents.NewHTTP() 20 | if err != nil { 21 | log.Fatalf("failed to create protocol: %s", err.Error()) 22 | } 23 | 24 | h, err := cloudevents.NewHTTPReceiveHandler(ctx, p, receive) 25 | if err != nil { 26 | log.Fatalf("failed to create handler: %s", err.Error()) 27 | } 28 | 29 | log.Printf("will listen on :8080\n") 30 | if err := http.ListenAndServe(":8080", h); err != nil { 31 | log.Fatalf("unable to start http server, %s", err) 32 | } 33 | } 34 | 35 | func receive(ctx context.Context, event cloudevents.Event) { 36 | fmt.Printf("%s", event) 37 | } 38 | -------------------------------------------------------------------------------- /samples/http/receiver-gin/README.md: -------------------------------------------------------------------------------- 1 | Gin Receiver for CloudEvents 2 | ---------------------------------- 3 | 4 | An example of a Gin webframework CloudEvents receiver with a [TektonEvent](https://tekton.dev/docs/pipelines/events/) 5 | 6 | # Steps 7 | 8 | Get dependencies 9 | ```shell 10 | cd samples/ 11 | go get 12 | 13 | ``` 14 | 15 | Run the app 16 | ```shell 17 | go run main.go 18 | ``` 19 | 20 | Send a CloudEvent 21 | ```shell 22 | curl -v \ 23 | -H "Ce-Id: e7d95c20-6eb4-4614-946d-27b0ce41c7ff" \ 24 | -H "Ce-Source: /apis/namespaces/dimitar/clone-build-n4qhgl" \ 25 | -H "Ce-Subject: clone-build-n4qhgl" \ 26 | -H "Ce-Specversion: 1.0" \ 27 | -H "Ce-Type: dev.tekton.event.pipelinerun.started.v1" \ 28 | -H "Content-Type: application/json" \ 29 | -d @event.json http://localhost:8080 30 | 31 | ... 32 | < HTTP/1.1 200 OK 33 | ... 34 | ``` 35 | 36 | Logs output 37 | ```shell 38 | Got an Event: Context Attributes, 39 | specversion: 1.0 40 | type: dev.tekton.event.pipelinerun.started.v1 41 | source: /apis/namespaces/dimitar/clone-build-n4qhgl 42 | subject: clone-build-n4qhgl 43 | id: e7d95c20-6eb4-4614-946d-27b0ce41c7ff 44 | datacontenttype: application/json 45 | Data, 46 | { 47 | "pipelineRun": { 48 | "metadata": { 49 | "name": "clone-build-n4qhgl", 50 | "namespace": "dimitar", 51 | "uid": "44ef2940-b2d9-4ecb-ad12-808a69972f02", 52 | 53 | ..... 54 | } 55 | [GIN] 2023/02/13 - 14:16:51 | 200 | 639.6µs | 127.0.0.1 | POST "/" 56 | ``` 57 | -------------------------------------------------------------------------------- /samples/http/receiver-gin/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/rs/zerolog/log" 8 | 9 | cloudevents "github.com/cloudevents/sdk-go/v2" 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | func receive(event cloudevents.Event) { 14 | fmt.Printf("Got an Event: %s", event) 15 | } 16 | 17 | func index(c *gin.Context) { 18 | c.JSON(http.StatusOK, "Welcome to CloudEvents") 19 | } 20 | 21 | func healthz(c *gin.Context) { 22 | c.String(http.StatusOK, "OK") 23 | } 24 | 25 | func cloudEventsHandler() gin.HandlerFunc { 26 | return func(c *gin.Context) { 27 | p, err := cloudevents.NewHTTP() 28 | if err != nil { 29 | log.Fatal(). 30 | Err(err). 31 | Msg("Failed to create protocol") 32 | } 33 | 34 | ceh, err := cloudevents.NewHTTPReceiveHandler(c, p, receive) 35 | if err != nil { 36 | log.Fatal(). 37 | Err(err). 38 | Msg("failed to create handler") 39 | } 40 | 41 | ceh.ServeHTTP(c.Writer, c.Request) 42 | } 43 | } 44 | 45 | func main() { 46 | r := gin.Default() 47 | r.SetTrustedProxies(nil) 48 | 49 | r.GET("/", index) 50 | r.GET("/healthz", healthz) 51 | r.POST("/", cloudEventsHandler()) 52 | 53 | log.Fatal(). 54 | Err(http.ListenAndServe(":8080", r)) 55 | } 56 | -------------------------------------------------------------------------------- /samples/http/receiver-gorilla/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package main 7 | 8 | import ( 9 | "context" 10 | "encoding/json" 11 | "fmt" 12 | "log" 13 | "net/http" 14 | 15 | "github.com/gorilla/mux" 16 | 17 | cloudevents "github.com/cloudevents/sdk-go/v2" 18 | ) 19 | 20 | func main() { 21 | ctx := context.Background() 22 | p, err := cloudevents.NewHTTP() 23 | if err != nil { 24 | log.Fatalf("failed to create protocol: %s", err.Error()) 25 | } 26 | 27 | h, err := cloudevents.NewHTTPReceiveHandler(ctx, p, receive) 28 | if err != nil { 29 | log.Fatalf("failed to create handler: %s", err.Error()) 30 | } 31 | 32 | // Use a gorilla mux implementation for the overall http handler. 33 | router := mux.NewRouter() 34 | 35 | router.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { 36 | // an example API handler 37 | json.NewEncoder(w).Encode(map[string]bool{"ok": true}) 38 | }) 39 | 40 | router.Handle("/", h) 41 | 42 | log.Printf("will listen on :8080\n") 43 | if err := http.ListenAndServe(":8080", router); err != nil { 44 | log.Fatalf("unable to start http server, %s", err) 45 | } 46 | } 47 | 48 | func receive(ctx context.Context, event cloudevents.Event) { 49 | fmt.Printf("Got an Event: %s", event) 50 | } 51 | -------------------------------------------------------------------------------- /samples/http/receiver-protected/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package main 7 | 8 | import ( 9 | "context" 10 | "fmt" 11 | "log" 12 | 13 | cloudevents "github.com/cloudevents/sdk-go/v2" 14 | ) 15 | 16 | func main() { 17 | ctx := context.Background() 18 | p, err := cloudevents.NewHTTP( 19 | cloudevents.WithDefaultOptionsHandlerFunc([]string{"POST", "OPTIONS"}, 100, []string{"http://localhost:8181"}, true), 20 | ) 21 | if err != nil { 22 | log.Fatalf("failed to create protocol: %s", err.Error()) 23 | } 24 | 25 | c, err := cloudevents.NewClient(p) 26 | if err != nil { 27 | log.Fatalf("failed to create client, %v", err) 28 | } 29 | 30 | log.Printf("will listen on :8080\n") 31 | log.Fatalf("failed to start receiver: %s", c.StartReceiver(ctx, receive)) 32 | } 33 | 34 | func receive(ctx context.Context, event cloudevents.Event) { 35 | fmt.Printf("%s", event) 36 | } 37 | 38 | // 39 | // Testing with: 40 | // 41 | // cd ./tools; PORT=8181 go run ./http/raw/ 42 | // 43 | // curl http://localhost:8080 -v -X OPTIONS -H "Origin: http://example.com" -H "WebHook-Request-Origin: http://example.com" -H "WebHook-Request-Callback: http://localhost:8181/do-this?now=true" 44 | // 45 | -------------------------------------------------------------------------------- /samples/http/receiver-protobuf/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package main 7 | 8 | import ( 9 | "context" 10 | "log" 11 | 12 | _ "github.com/cloudevents/sdk-go/binding/format/protobuf/v2" 13 | cloudevents "github.com/cloudevents/sdk-go/v2" 14 | ) 15 | 16 | func main() { 17 | ctx := context.Background() 18 | p, err := cloudevents.NewHTTP() 19 | if err != nil { 20 | log.Fatalf("failed to create protocol: %s", err.Error()) 21 | } 22 | 23 | c, err := cloudevents.NewClient(p) 24 | if err != nil { 25 | log.Fatalf("failed to create client, %v", err) 26 | } 27 | 28 | log.Printf("will listen on :8080\n") 29 | log.Fatalf("failed to start receiver: %s", c.StartReceiver(ctx, receive)) 30 | } 31 | 32 | func receive(ctx context.Context, event cloudevents.Event) { 33 | log.Printf("%s", event) 34 | payload := &Sample{} 35 | if err := event.DataAs(payload); err != nil { 36 | log.Printf("failed to decode protobuf data: %s", err) 37 | return 38 | } 39 | log.Printf("decoded protobuf: %s", payload.Value) 40 | } 41 | -------------------------------------------------------------------------------- /samples/http/receiver-protobuf/sample.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package pb; 4 | 5 | option go_package = "/;main"; 6 | 7 | message Sample { string value = 1; } 8 | -------------------------------------------------------------------------------- /samples/http/receiver-result/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package main 7 | 8 | import ( 9 | "context" 10 | "fmt" 11 | "log" 12 | "net/http" 13 | 14 | cloudevents "github.com/cloudevents/sdk-go/v2" 15 | ) 16 | 17 | func main() { 18 | ctx := context.Background() 19 | p, err := cloudevents.NewHTTP() 20 | if err != nil { 21 | log.Fatalf("failed to create protocol: %s", err.Error()) 22 | } 23 | 24 | c, err := cloudevents.NewClient(p) 25 | if err != nil { 26 | log.Fatalf("failed to create client, %v", err) 27 | } 28 | 29 | log.Printf("will listen on :8080\n") 30 | log.Fatalf("failed to start receiver: %s", c.StartReceiver(ctx, receive)) 31 | } 32 | 33 | func receive(ctx context.Context, event cloudevents.Event) cloudevents.Result { 34 | fmt.Printf("%s", event) 35 | if event.Type() != "com.cloudevents.sample.sent" { 36 | return cloudevents.NewHTTPResult(http.StatusBadRequest, "invalid type of %s", event.Type()) 37 | } 38 | return cloudevents.NewHTTPResult(http.StatusOK, "") 39 | } 40 | -------------------------------------------------------------------------------- /samples/http/receiver-traced/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package main 7 | 8 | import ( 9 | "context" 10 | "fmt" 11 | "log" 12 | "net/http" 13 | 14 | cloudevents "github.com/cloudevents/sdk-go/v2" 15 | "go.opencensus.io/plugin/ochttp" 16 | ) 17 | 18 | func main() { 19 | ctx := context.Background() 20 | p, err := cloudevents.NewHTTP(cloudevents.WithMiddleware(func(next http.Handler) http.Handler { 21 | return &ochttp.Handler{Handler: next} 22 | })) 23 | if err != nil { 24 | log.Fatalf("failed to create protocol: %s", err.Error()) 25 | } 26 | 27 | c, err := cloudevents.NewClient(p) 28 | if err != nil { 29 | log.Fatalf("failed to create client, %v", err) 30 | } 31 | 32 | log.Printf("will listen on :8080\n") 33 | log.Fatalf("failed to start receiver: %s", c.StartReceiver(ctx, receive)) 34 | } 35 | 36 | func receive(ctx context.Context, e cloudevents.Event) { 37 | fmt.Printf("%s", e) 38 | } 39 | -------------------------------------------------------------------------------- /samples/http/receiver/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package main 7 | 8 | import ( 9 | "context" 10 | "fmt" 11 | "log" 12 | 13 | cloudevents "github.com/cloudevents/sdk-go/v2" 14 | ) 15 | 16 | func main() { 17 | ctx := context.Background() 18 | p, err := cloudevents.NewHTTP() 19 | if err != nil { 20 | log.Fatalf("failed to create protocol: %s", err.Error()) 21 | } 22 | 23 | c, err := cloudevents.NewClient(p) 24 | if err != nil { 25 | log.Fatalf("failed to create client, %v", err) 26 | } 27 | 28 | log.Printf("will listen on :8080\n") 29 | log.Fatalf("failed to start receiver: %s", c.StartReceiver(ctx, receive)) 30 | } 31 | 32 | func receive(ctx context.Context, event cloudevents.Event) { 33 | fmt.Printf("%s", event) 34 | } 35 | -------------------------------------------------------------------------------- /samples/http/sender-protobuf/main.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright 2021 The CloudEvents Authors 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | 6 | package main 7 | 8 | import ( 9 | "context" 10 | "log" 11 | 12 | pbcloudevents "github.com/cloudevents/sdk-go/binding/format/protobuf/v2" 13 | cloudevents "github.com/cloudevents/sdk-go/v2" 14 | cehttp "github.com/cloudevents/sdk-go/v2/protocol/http" 15 | ) 16 | 17 | func main() { 18 | ctx := cloudevents.ContextWithTarget(context.Background(), "http://localhost:8080/") 19 | 20 | p, err := cloudevents.NewHTTP() 21 | if err != nil { 22 | log.Fatalf("failed to create protocol: %s", err.Error()) 23 | } 24 | 25 | c, err := cloudevents.NewClient(p, cloudevents.WithTimeNow(), cloudevents.WithUUIDs()) 26 | if err != nil { 27 | log.Fatalf("failed to create client, %v", err) 28 | } 29 | 30 | for i := 0; i < 10; i++ { 31 | data := &Sample{Value: "sample"} 32 | e := cloudevents.NewEvent() 33 | e.SetType("com.cloudevents.sample.sent") 34 | e.SetSource("https://github.com/cloudevents/sdk-go/v2/samples/http/sender-protobuf") 35 | e.SetDataSchema("my-schema-registry://" + string(data.ProtoReflect().Descriptor().FullName())) 36 | _ = e.SetData(pbcloudevents.ContentTypeProtobuf, data) 37 | 38 | res := c.Send(ctx, e) 39 | if cloudevents.IsUndelivered(res) { 40 | log.Printf("Failed to send: %v", res) 41 | } else { 42 | var httpResult *cehttp.Result 43 | if cloudevents.ResultAs(res, &httpResult) { 44 | log.Printf("Sent %d with status code %d", i, httpResult.StatusCode) 45 | } else { 46 | log.Printf("Send did not return an HTTP response: %s", res) 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /samples/http/sender-protobuf/sample.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package pb; 4 | 5 | option go_package = "/;main"; 6 | 7 | message Sample { string value = 1; } 8 | -------------------------------------------------------------------------------- /samples/kafka/README.md: -------------------------------------------------------------------------------- 1 | # Kafka samples 2 | 3 | To run the samples, you need a running Kafka cluster. 4 | 5 | To run a sample Kafka cluster using docker: 6 | 7 | ``` 8 | docker run --rm --net=host -e ADV_HOST=localhost -e SAMPLEDATA=0 lensesio/fast-data-dev 9 | ``` -------------------------------------------------------------------------------- /samples/kafka/receiver/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package main 7 | 8 | import ( 9 | "context" 10 | "fmt" 11 | "log" 12 | 13 | "github.com/IBM/sarama" 14 | 15 | "github.com/cloudevents/sdk-go/protocol/kafka_sarama/v2" 16 | cloudevents "github.com/cloudevents/sdk-go/v2" 17 | ) 18 | 19 | func main() { 20 | saramaConfig := sarama.NewConfig() 21 | saramaConfig.Version = sarama.V2_0_0_0 22 | 23 | receiver, err := kafka_sarama.NewConsumer([]string{"127.0.0.1:9092"}, saramaConfig, "test-group-id", "test-topic") 24 | if err != nil { 25 | log.Fatalf("failed to create protocol: %s", err.Error()) 26 | } 27 | 28 | defer receiver.Close(context.Background()) 29 | 30 | c, err := cloudevents.NewClient(receiver) 31 | if err != nil { 32 | log.Fatalf("failed to create client, %v", err) 33 | } 34 | 35 | log.Printf("will listen consuming topic test-topic\n") 36 | err = c.StartReceiver(context.Background(), receive) 37 | if err != nil { 38 | log.Fatalf("failed to start receiver: %s", err) 39 | } else { 40 | log.Printf("receiver stopped\n") 41 | } 42 | } 43 | 44 | func receive(ctx context.Context, event cloudevents.Event) { 45 | fmt.Printf("%s", event) 46 | } 47 | -------------------------------------------------------------------------------- /samples/kafka_confluent/README.md: -------------------------------------------------------------------------------- 1 | # Confluent kafka samples 2 | 3 | To run the samples, you need a running Kafka cluster. 4 | 5 | To run a sample Kafka cluster using docker: 6 | 7 | ``` 8 | docker run --rm --net=host confluentinc/confluent-local 9 | ``` -------------------------------------------------------------------------------- /samples/kafka_confluent/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/samples/kafka_confluent 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | replace github.com/cloudevents/sdk-go/v2 => ../../v2 8 | 9 | replace github.com/cloudevents/sdk-go/protocol/kafka_confluent/v2 => ./../../protocol/kafka_confluent/v2 10 | 11 | require ( 12 | github.com/cloudevents/sdk-go/protocol/kafka_confluent/v2 v2.16.0 13 | github.com/cloudevents/sdk-go/v2 v2.16.0 14 | github.com/confluentinc/confluent-kafka-go/v2 v2.10.0 15 | ) 16 | 17 | require ( 18 | github.com/google/uuid v1.6.0 // indirect 19 | github.com/json-iterator/go v1.1.12 // indirect 20 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 21 | github.com/modern-go/reflect2 v1.0.2 // indirect 22 | go.uber.org/multierr v1.11.0 // indirect 23 | go.uber.org/zap v1.27.0 // indirect 24 | ) 25 | -------------------------------------------------------------------------------- /samples/mqtt/README.md: -------------------------------------------------------------------------------- 1 | MQTT samples 2 | 3 | To run the samples, you need a running MQTT broker. 4 | 5 | To run a sample MQTT broker using docker: 6 | 7 | ```bash 8 | docker run -it --rm --name mosquitto -p 1883:1883 eclipse-mosquitto:2.0 mosquitto -c /mosquitto-no-auth.conf 9 | ``` -------------------------------------------------------------------------------- /samples/mqtt/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/samples/mqtt 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | require ( 8 | github.com/cloudevents/sdk-go/protocol/mqtt_paho/v2 v2.16.0 9 | github.com/cloudevents/sdk-go/v2 v2.16.0 10 | github.com/eclipse/paho.golang v0.22.0 11 | github.com/google/uuid v1.6.0 12 | ) 13 | 14 | require ( 15 | github.com/json-iterator/go v1.1.12 // indirect 16 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 17 | github.com/modern-go/reflect2 v1.0.2 // indirect 18 | go.uber.org/multierr v1.11.0 // indirect 19 | go.uber.org/zap v1.27.0 // indirect 20 | ) 21 | 22 | replace github.com/cloudevents/sdk-go/v2 => ../../v2 23 | 24 | replace github.com/cloudevents/sdk-go/protocol/mqtt_paho/v2 => ../../protocol/mqtt_paho/v2 25 | -------------------------------------------------------------------------------- /samples/mqtt/receiver/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package main 7 | 8 | import ( 9 | "context" 10 | "fmt" 11 | "log" 12 | "net" 13 | 14 | mqtt_paho "github.com/cloudevents/sdk-go/protocol/mqtt_paho/v2" 15 | cloudevents "github.com/cloudevents/sdk-go/v2" 16 | "github.com/eclipse/paho.golang/paho" 17 | ) 18 | 19 | func main() { 20 | ctx := context.Background() 21 | conn, err := net.Dial("tcp", "127.0.0.1:1883") 22 | if err != nil { 23 | log.Fatalf("failed to connect to mqtt broker: %s", err.Error()) 24 | } 25 | config := &paho.ClientConfig{ 26 | ClientID: "receiver-client-id", 27 | Conn: conn, 28 | } 29 | subscribeOpt := &paho.Subscribe{ 30 | Subscriptions: []paho.SubscribeOptions{ 31 | { 32 | Topic: "test-topic", 33 | QoS: 0, 34 | }, 35 | }, 36 | } 37 | p, err := mqtt_paho.New(ctx, config, mqtt_paho.WithSubscribe(subscribeOpt)) 38 | if err != nil { 39 | log.Fatalf("failed to create protocol: %s", err.Error()) 40 | } 41 | defer p.Close(ctx) 42 | 43 | c, err := cloudevents.NewClient(p) 44 | if err != nil { 45 | log.Fatalf("failed to create client, %v", err) 46 | } 47 | 48 | log.Printf("receiver start consuming messages from test-topic\n") 49 | err = c.StartReceiver(ctx, receive) 50 | if err != nil { 51 | log.Fatalf("failed to start receiver: %s", err) 52 | } else { 53 | log.Printf("receiver stopped\n") 54 | } 55 | } 56 | 57 | func receive(ctx context.Context, event cloudevents.Event) { 58 | fmt.Printf("%s", event) 59 | } 60 | -------------------------------------------------------------------------------- /samples/nats/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/samples/nats 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | require ( 8 | github.com/cloudevents/sdk-go/protocol/nats/v2 v2.16.0 9 | github.com/cloudevents/sdk-go/v2 v2.16.0 10 | github.com/google/uuid v1.6.0 11 | github.com/kelseyhightower/envconfig v1.4.0 12 | ) 13 | 14 | require ( 15 | github.com/json-iterator/go v1.1.12 // indirect 16 | github.com/klauspost/compress v1.18.0 // indirect 17 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 18 | github.com/modern-go/reflect2 v1.0.2 // indirect 19 | github.com/nats-io/nats.go v1.42.0 // indirect 20 | github.com/nats-io/nkeys v0.4.11 // indirect 21 | github.com/nats-io/nuid v1.0.1 // indirect 22 | go.uber.org/multierr v1.11.0 // indirect 23 | go.uber.org/zap v1.27.0 // indirect 24 | golang.org/x/crypto v0.38.0 // indirect 25 | golang.org/x/sys v0.33.0 // indirect 26 | ) 27 | 28 | replace github.com/cloudevents/sdk-go/v2 => ../../v2 29 | 30 | replace github.com/cloudevents/sdk-go/protocol/nats/v2 => ../../protocol/nats/v2 31 | -------------------------------------------------------------------------------- /samples/nats_jetstream/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/samples/nats_jetstream 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | require ( 8 | github.com/cloudevents/sdk-go/protocol/nats_jetstream/v2 v2.16.0 9 | github.com/cloudevents/sdk-go/v2 v2.16.0 10 | github.com/google/uuid v1.6.0 11 | github.com/kelseyhightower/envconfig v1.4.0 12 | ) 13 | 14 | require ( 15 | github.com/json-iterator/go v1.1.12 // indirect 16 | github.com/klauspost/compress v1.18.0 // indirect 17 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 18 | github.com/modern-go/reflect2 v1.0.2 // indirect 19 | github.com/nats-io/nats.go v1.42.0 // indirect 20 | github.com/nats-io/nkeys v0.4.11 // indirect 21 | github.com/nats-io/nuid v1.0.1 // indirect 22 | go.uber.org/multierr v1.11.0 // indirect 23 | go.uber.org/zap v1.27.0 // indirect 24 | golang.org/x/crypto v0.38.0 // indirect 25 | golang.org/x/sys v0.33.0 // indirect 26 | ) 27 | 28 | replace github.com/cloudevents/sdk-go/v2 => ../../v2 29 | 30 | replace github.com/cloudevents/sdk-go/protocol/nats_jetstream/v2 => ./../../protocol/nats_jetstream/v2 31 | -------------------------------------------------------------------------------- /samples/nats_jetstream/v3/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/samples/nats_jetstream/v3 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | require ( 8 | github.com/cloudevents/sdk-go/protocol/nats_jetstream/v3 v3.0.0 9 | github.com/cloudevents/sdk-go/v2 v2.16.0 10 | github.com/google/uuid v1.6.0 11 | github.com/kelseyhightower/envconfig v1.4.0 12 | github.com/nats-io/nats.go v1.42.0 13 | ) 14 | 15 | require ( 16 | github.com/json-iterator/go v1.1.12 // indirect 17 | github.com/klauspost/compress v1.18.0 // indirect 18 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 19 | github.com/modern-go/reflect2 v1.0.2 // indirect 20 | github.com/nats-io/nkeys v0.4.11 // indirect 21 | github.com/nats-io/nuid v1.0.1 // indirect 22 | go.uber.org/multierr v1.11.0 // indirect 23 | go.uber.org/zap v1.27.0 // indirect 24 | golang.org/x/crypto v0.38.0 // indirect 25 | golang.org/x/sys v0.33.0 // indirect 26 | ) 27 | 28 | replace github.com/cloudevents/sdk-go/v2 => ../../../v2 29 | 30 | replace github.com/cloudevents/sdk-go/protocol/nats_jetstream/v3 => ./../../../protocol/nats_jetstream/v3 31 | -------------------------------------------------------------------------------- /samples/stan/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/samples/stan 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | require ( 8 | github.com/cloudevents/sdk-go/protocol/stan/v2 v2.16.0 9 | github.com/cloudevents/sdk-go/v2 v2.16.0 10 | ) 11 | 12 | require ( 13 | github.com/gogo/protobuf v1.3.2 // indirect 14 | github.com/google/uuid v1.6.0 // indirect 15 | github.com/hashicorp/go-hclog v1.1.0 // indirect 16 | github.com/hashicorp/go-msgpack v1.1.5 // indirect 17 | github.com/hashicorp/go-msgpack/v2 v2.1.3 // indirect 18 | github.com/hashicorp/raft v1.3.9 // indirect 19 | github.com/json-iterator/go v1.1.12 // indirect 20 | github.com/klauspost/compress v1.18.0 // indirect 21 | github.com/minio/highwayhash v1.0.2 // indirect 22 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 23 | github.com/modern-go/reflect2 v1.0.2 // indirect 24 | github.com/nats-io/jwt/v2 v2.2.1-0.20220113022732-58e87895b296 // indirect 25 | github.com/nats-io/nats.go v1.42.0 // indirect 26 | github.com/nats-io/nkeys v0.4.11 // indirect 27 | github.com/nats-io/nuid v1.0.1 // indirect 28 | github.com/nats-io/stan.go v0.10.4 // indirect 29 | github.com/prometheus/procfs v0.7.3 // indirect 30 | go.etcd.io/bbolt v1.3.6 // indirect 31 | go.uber.org/multierr v1.11.0 // indirect 32 | go.uber.org/zap v1.27.0 // indirect 33 | golang.org/x/crypto v0.38.0 // indirect 34 | golang.org/x/sys v0.33.0 // indirect 35 | ) 36 | 37 | replace github.com/cloudevents/sdk-go/v2 => ../../v2 38 | 39 | replace github.com/cloudevents/sdk-go/protocol/stan/v2 => ../../protocol/stan/v2 40 | -------------------------------------------------------------------------------- /samples/stan/receiver/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package main 7 | 8 | import ( 9 | "context" 10 | "fmt" 11 | "log" 12 | 13 | cestan "github.com/cloudevents/sdk-go/protocol/stan/v2" 14 | cloudevents "github.com/cloudevents/sdk-go/v2" 15 | ) 16 | 17 | func main() { 18 | receiver, err := cestan.NewConsumer("test-cluster", "test-client", "test-subject", cestan.StanOptions()) 19 | if err != nil { 20 | log.Fatalf("failed to create protocol: %v", err) 21 | } 22 | 23 | defer receiver.Close(context.Background()) 24 | 25 | c, err := cloudevents.NewClient(receiver, cloudevents.WithTimeNow(), cloudevents.WithUUIDs()) 26 | if err != nil { 27 | log.Fatalf("failed to create client: %v", err) 28 | } 29 | 30 | log.Printf("will listen consuming topic test-topic\n") 31 | err = c.StartReceiver(context.TODO(), receive) 32 | if err != nil { 33 | log.Fatalf("failed to start receiver: %s", err) 34 | } else { 35 | log.Printf("receiver stopped\n") 36 | } 37 | 38 | } 39 | 40 | func receive(_ context.Context, event cloudevents.Event) { 41 | fmt.Printf("%s", event) 42 | } 43 | -------------------------------------------------------------------------------- /samples/stan/sender/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package main 7 | 8 | import ( 9 | "context" 10 | "log" 11 | 12 | cestan "github.com/cloudevents/sdk-go/protocol/stan/v2" 13 | cloudevents "github.com/cloudevents/sdk-go/v2" 14 | ) 15 | 16 | func main() { 17 | s, err := cestan.NewSender("test-cluster", "test-client", "test-subject", cestan.StanOptions()) 18 | if err != nil { 19 | log.Fatalf("failed to create protocol: %v", err) 20 | } 21 | 22 | defer s.Close(context.Background()) 23 | 24 | c, err := cloudevents.NewClient(s, cloudevents.WithTimeNow(), cloudevents.WithUUIDs()) 25 | if err != nil { 26 | log.Fatalf("failed to create client: %v", err) 27 | } 28 | 29 | for i := 0; i < 10; i++ { 30 | e := cloudevents.NewEvent() 31 | e.SetType("com.cloudevents.sample.sent") 32 | e.SetSource("https://github.com/cloudevents/sdk-go/v2/samples/stan/sender") 33 | _ = e.SetData(cloudevents.ApplicationJSON, map[string]interface{}{ 34 | "id": i, 35 | "message": "Hello, World!", 36 | }) 37 | 38 | if result := c.Send(context.Background(), e); cloudevents.IsUndelivered(result) { 39 | log.Printf("failed to send: %v", err) 40 | } else { 41 | log.Printf("sent: %d, accepted: %t", i, cloudevents.IsACK(result)) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /samples/ws/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/samples/ws 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | require ( 8 | github.com/cloudevents/sdk-go/protocol/ws/v2 v2.16.0 9 | github.com/cloudevents/sdk-go/v2 v2.16.0 10 | ) 11 | 12 | require ( 13 | github.com/google/uuid v1.6.0 // indirect 14 | github.com/json-iterator/go v1.1.12 // indirect 15 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 16 | github.com/modern-go/reflect2 v1.0.2 // indirect 17 | go.uber.org/multierr v1.11.0 // indirect 18 | go.uber.org/zap v1.27.0 // indirect 19 | nhooyr.io/websocket v1.8.17 // indirect 20 | ) 21 | 22 | replace github.com/cloudevents/sdk-go/v2 => ../../v2 23 | 24 | replace github.com/cloudevents/sdk-go/protocol/ws/v2 => ../../protocol/ws/v2 25 | -------------------------------------------------------------------------------- /sql/v2/Makefile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudevents/sdk-go/a50d97a11f88b611e9cda2727a0f3e617cbcf06e/sql/v2/Makefile -------------------------------------------------------------------------------- /sql/v2/expression.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package v2 7 | 8 | import cloudevents "github.com/cloudevents/sdk-go/v2" 9 | 10 | // Expression represents a parsed CloudEvents SQL Expression. 11 | type Expression interface { 12 | 13 | // Evaluate the expression using the provided input type. 14 | // The return value can be either int32, bool or string. 15 | // The evaluation fails as soon as an error arises. 16 | Evaluate(event cloudevents.Event) (interface{}, error) 17 | } 18 | -------------------------------------------------------------------------------- /sql/v2/expression/base_expressions.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package expression 7 | 8 | import cesql "github.com/cloudevents/sdk-go/sql/v2" 9 | 10 | type baseUnaryExpression struct { 11 | child cesql.Expression 12 | } 13 | 14 | type baseBinaryExpression struct { 15 | left cesql.Expression 16 | right cesql.Expression 17 | } 18 | -------------------------------------------------------------------------------- /sql/v2/expression/comparison_expressions.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package expression 7 | 8 | import ( 9 | cesql "github.com/cloudevents/sdk-go/sql/v2" 10 | "github.com/cloudevents/sdk-go/sql/v2/utils" 11 | cloudevents "github.com/cloudevents/sdk-go/v2" 12 | ) 13 | 14 | type equalExpression struct { 15 | baseBinaryExpression 16 | equal bool 17 | } 18 | 19 | func (s equalExpression) Evaluate(event cloudevents.Event) (interface{}, error) { 20 | leftVal, err := s.left.Evaluate(event) 21 | if err != nil { 22 | return false, err 23 | } 24 | 25 | rightVal, err := s.right.Evaluate(event) 26 | if err != nil { 27 | return false, err 28 | } 29 | 30 | leftVal, err = utils.Cast(leftVal, cesql.TypeFromVal(rightVal)) 31 | if err != nil { 32 | return false, err 33 | } 34 | 35 | return (leftVal == rightVal) == s.equal, nil 36 | } 37 | 38 | func NewEqualExpression(left cesql.Expression, right cesql.Expression) cesql.Expression { 39 | return equalExpression{ 40 | baseBinaryExpression: baseBinaryExpression{ 41 | left: left, 42 | right: right, 43 | }, 44 | equal: true, 45 | } 46 | } 47 | 48 | func NewNotEqualExpression(left cesql.Expression, right cesql.Expression) cesql.Expression { 49 | return equalExpression{ 50 | baseBinaryExpression: baseBinaryExpression{ 51 | left: left, 52 | right: right, 53 | }, 54 | equal: false, 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /sql/v2/expression/exists_expression.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package expression 7 | 8 | import ( 9 | cesql "github.com/cloudevents/sdk-go/sql/v2" 10 | "github.com/cloudevents/sdk-go/sql/v2/utils" 11 | cloudevents "github.com/cloudevents/sdk-go/v2" 12 | ) 13 | 14 | type existsExpression struct { 15 | identifier string 16 | } 17 | 18 | func (l existsExpression) Evaluate(event cloudevents.Event) (interface{}, error) { 19 | return utils.ContainsAttribute(event, l.identifier), nil 20 | } 21 | 22 | func NewExistsExpression(identifier string) cesql.Expression { 23 | return existsExpression{identifier: identifier} 24 | } 25 | -------------------------------------------------------------------------------- /sql/v2/expression/identifier_expression.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package expression 7 | 8 | import ( 9 | cesql "github.com/cloudevents/sdk-go/sql/v2" 10 | sqlerrors "github.com/cloudevents/sdk-go/sql/v2/errors" 11 | "github.com/cloudevents/sdk-go/sql/v2/utils" 12 | cloudevents "github.com/cloudevents/sdk-go/v2" 13 | ) 14 | 15 | type identifierExpression struct { 16 | identifier string 17 | } 18 | 19 | func (l identifierExpression) Evaluate(event cloudevents.Event) (interface{}, error) { 20 | value := utils.GetAttribute(event, l.identifier) 21 | if value == nil { 22 | return false, sqlerrors.NewMissingAttributeError(l.identifier) 23 | } 24 | 25 | return value, nil 26 | } 27 | 28 | func NewIdentifierExpression(identifier string) cesql.Expression { 29 | return identifierExpression{identifier: identifier} 30 | } 31 | -------------------------------------------------------------------------------- /sql/v2/expression/in_expression.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package expression 7 | 8 | import ( 9 | cesql "github.com/cloudevents/sdk-go/sql/v2" 10 | "github.com/cloudevents/sdk-go/sql/v2/utils" 11 | cloudevents "github.com/cloudevents/sdk-go/v2" 12 | ) 13 | 14 | type inExpression struct { 15 | leftExpression cesql.Expression 16 | setExpression []cesql.Expression 17 | } 18 | 19 | func (l inExpression) Evaluate(event cloudevents.Event) (interface{}, error) { 20 | leftValue, err := l.leftExpression.Evaluate(event) 21 | if err != nil { 22 | return false, err 23 | } 24 | 25 | for _, rightExpression := range l.setExpression { 26 | rightValue, err := rightExpression.Evaluate(event) 27 | if err != nil { 28 | return false, err 29 | } 30 | 31 | rightValue, err = utils.Cast(rightValue, cesql.TypeFromVal(leftValue)) 32 | if err != nil { 33 | return false, err 34 | } 35 | 36 | if leftValue == rightValue { 37 | return true, nil 38 | } 39 | } 40 | 41 | return false, nil 42 | } 43 | 44 | func NewInExpression(leftExpression cesql.Expression, setExpression []cesql.Expression) cesql.Expression { 45 | return inExpression{leftExpression, setExpression} 46 | } 47 | -------------------------------------------------------------------------------- /sql/v2/expression/literal_expression.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package expression 7 | 8 | import ( 9 | cesql "github.com/cloudevents/sdk-go/sql/v2" 10 | cloudevents "github.com/cloudevents/sdk-go/v2" 11 | ) 12 | 13 | type literalExpression struct { 14 | value interface{} 15 | } 16 | 17 | func (l literalExpression) Evaluate(event cloudevents.Event) (interface{}, error) { 18 | return l.value, nil 19 | } 20 | 21 | func NewLiteralExpression(value interface{}) cesql.Expression { 22 | return literalExpression{value: value} 23 | } 24 | -------------------------------------------------------------------------------- /sql/v2/expression/negate_expression.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package expression 7 | 8 | import ( 9 | cesql "github.com/cloudevents/sdk-go/sql/v2" 10 | "github.com/cloudevents/sdk-go/sql/v2/utils" 11 | cloudevents "github.com/cloudevents/sdk-go/v2" 12 | ) 13 | 14 | type negateExpression baseUnaryExpression 15 | 16 | func (l negateExpression) Evaluate(event cloudevents.Event) (interface{}, error) { 17 | val, err := l.child.Evaluate(event) 18 | if err != nil { 19 | return int32(0), err 20 | } 21 | 22 | val, err = utils.Cast(val, cesql.IntegerType) 23 | if err != nil { 24 | return int32(0), err 25 | } 26 | 27 | return -(val.(int32)), nil 28 | } 29 | 30 | func NewNegateExpression(child cesql.Expression) cesql.Expression { 31 | return negateExpression{child: child} 32 | } 33 | -------------------------------------------------------------------------------- /sql/v2/expression/not_expression.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package expression 7 | 8 | import ( 9 | cesql "github.com/cloudevents/sdk-go/sql/v2" 10 | "github.com/cloudevents/sdk-go/sql/v2/utils" 11 | cloudevents "github.com/cloudevents/sdk-go/v2" 12 | ) 13 | 14 | type notExpression baseUnaryExpression 15 | 16 | func (l notExpression) Evaluate(event cloudevents.Event) (interface{}, error) { 17 | val, err := l.child.Evaluate(event) 18 | if err != nil { 19 | return false, err 20 | } 21 | 22 | val, err = utils.Cast(val, cesql.BooleanType) 23 | if err != nil { 24 | return false, err 25 | } 26 | 27 | return !(val.(bool)), nil 28 | } 29 | 30 | func NewNotExpression(child cesql.Expression) cesql.Expression { 31 | return notExpression{child: child} 32 | } 33 | -------------------------------------------------------------------------------- /sql/v2/function.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package v2 7 | 8 | import cloudevents "github.com/cloudevents/sdk-go/v2" 9 | 10 | type Function interface { 11 | Name() string 12 | Arity() int 13 | IsVariadic() bool 14 | ArgType(index int) *Type 15 | ReturnType() Type 16 | 17 | Run(event cloudevents.Event, arguments []interface{}) (interface{}, error) 18 | } 19 | -------------------------------------------------------------------------------- /sql/v2/function/function.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package function 7 | 8 | import ( 9 | cesql "github.com/cloudevents/sdk-go/sql/v2" 10 | cloudevents "github.com/cloudevents/sdk-go/v2" 11 | ) 12 | 13 | type FuncType func(cloudevents.Event, []interface{}) (interface{}, error) 14 | 15 | type function struct { 16 | name string 17 | fixedArgs []cesql.Type 18 | variadicArgs *cesql.Type 19 | returnType cesql.Type 20 | fn FuncType 21 | } 22 | 23 | func (f function) Name() string { 24 | return f.name 25 | } 26 | 27 | func (f function) Arity() int { 28 | return len(f.fixedArgs) 29 | } 30 | 31 | func (f function) IsVariadic() bool { 32 | return f.variadicArgs != nil 33 | } 34 | 35 | func (f function) ArgType(index int) *cesql.Type { 36 | if index < len(f.fixedArgs) { 37 | return &f.fixedArgs[index] 38 | } 39 | return f.variadicArgs 40 | } 41 | 42 | func (f function) ReturnType() cesql.Type { 43 | return f.returnType 44 | } 45 | 46 | func (f function) Run(event cloudevents.Event, arguments []interface{}) (interface{}, error) { 47 | return f.fn(event, arguments) 48 | } 49 | 50 | func NewFunction(name string, 51 | fixedArgs []cesql.Type, 52 | variadicArgs *cesql.Type, 53 | returnType cesql.Type, 54 | fn FuncType) cesql.Function { 55 | return function{ 56 | name: name, 57 | fixedArgs: fixedArgs, 58 | variadicArgs: variadicArgs, 59 | returnType: returnType, 60 | fn: fn, 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /sql/v2/function/integer_functions.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package function 7 | 8 | import ( 9 | cesql "github.com/cloudevents/sdk-go/sql/v2" 10 | sqlerrors "github.com/cloudevents/sdk-go/sql/v2/errors" 11 | cloudevents "github.com/cloudevents/sdk-go/v2" 12 | "math" 13 | ) 14 | 15 | var AbsFunction function = function{ 16 | name: "ABS", 17 | fixedArgs: []cesql.Type{cesql.IntegerType}, 18 | variadicArgs: nil, 19 | returnType: cesql.IntegerType, 20 | fn: func(event cloudevents.Event, i []interface{}) (interface{}, error) { 21 | x := i[0].(int32) 22 | if x == math.MinInt32 { 23 | return int32(math.MaxInt32), sqlerrors.NewMathError("integer overflow while computing ABS") 24 | } 25 | if x < 0 { 26 | return -x, nil 27 | } 28 | return x, nil 29 | }, 30 | } 31 | -------------------------------------------------------------------------------- /sql/v2/gen/CESQLParser.tokens: -------------------------------------------------------------------------------- 1 | SPACE=1 2 | LR_BRACKET=2 3 | RR_BRACKET=3 4 | COMMA=4 5 | SINGLE_QUOTE_SYMB=5 6 | DOUBLE_QUOTE_SYMB=6 7 | AND=7 8 | OR=8 9 | XOR=9 10 | NOT=10 11 | STAR=11 12 | DIVIDE=12 13 | MODULE=13 14 | PLUS=14 15 | MINUS=15 16 | EQUAL=16 17 | NOT_EQUAL=17 18 | GREATER=18 19 | GREATER_OR_EQUAL=19 20 | LESS=20 21 | LESS_GREATER=21 22 | LESS_OR_EQUAL=22 23 | LIKE=23 24 | EXISTS=24 25 | IN=25 26 | TRUE=26 27 | FALSE=27 28 | DQUOTED_STRING_LITERAL=28 29 | SQUOTED_STRING_LITERAL=29 30 | INTEGER_LITERAL=30 31 | IDENTIFIER=31 32 | IDENTIFIER_WITH_NUMBER=32 33 | FUNCTION_IDENTIFIER_WITH_UNDERSCORE=33 34 | '('=2 35 | ')'=3 36 | ','=4 37 | '\''=5 38 | '"'=6 39 | 'AND'=7 40 | 'OR'=8 41 | 'XOR'=9 42 | 'NOT'=10 43 | '*'=11 44 | '/'=12 45 | '%'=13 46 | '+'=14 47 | '-'=15 48 | '='=16 49 | '!='=17 50 | '>'=18 51 | '>='=19 52 | '<'=20 53 | '<>'=21 54 | '<='=22 55 | 'LIKE'=23 56 | 'EXISTS'=24 57 | 'IN'=25 58 | 'TRUE'=26 59 | 'FALSE'=27 60 | -------------------------------------------------------------------------------- /sql/v2/gen/CESQLParserLexer.tokens: -------------------------------------------------------------------------------- 1 | SPACE=1 2 | LR_BRACKET=2 3 | RR_BRACKET=3 4 | COMMA=4 5 | SINGLE_QUOTE_SYMB=5 6 | DOUBLE_QUOTE_SYMB=6 7 | AND=7 8 | OR=8 9 | XOR=9 10 | NOT=10 11 | STAR=11 12 | DIVIDE=12 13 | MODULE=13 14 | PLUS=14 15 | MINUS=15 16 | EQUAL=16 17 | NOT_EQUAL=17 18 | GREATER=18 19 | GREATER_OR_EQUAL=19 20 | LESS=20 21 | LESS_GREATER=21 22 | LESS_OR_EQUAL=22 23 | LIKE=23 24 | EXISTS=24 25 | IN=25 26 | TRUE=26 27 | FALSE=27 28 | DQUOTED_STRING_LITERAL=28 29 | SQUOTED_STRING_LITERAL=29 30 | INTEGER_LITERAL=30 31 | IDENTIFIER=31 32 | IDENTIFIER_WITH_NUMBER=32 33 | FUNCTION_IDENTIFIER_WITH_UNDERSCORE=33 34 | '('=2 35 | ')'=3 36 | ','=4 37 | '\''=5 38 | '"'=6 39 | 'AND'=7 40 | 'OR'=8 41 | 'XOR'=9 42 | 'NOT'=10 43 | '*'=11 44 | '/'=12 45 | '%'=13 46 | '+'=14 47 | '-'=15 48 | '='=16 49 | '!='=17 50 | '>'=18 51 | '>='=19 52 | '<'=20 53 | '<>'=21 54 | '<='=22 55 | 'LIKE'=23 56 | 'EXISTS'=24 57 | 'IN'=25 58 | 'TRUE'=26 59 | 'FALSE'=27 60 | -------------------------------------------------------------------------------- /sql/v2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/sql/v2 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | require ( 8 | github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 9 | github.com/cloudevents/sdk-go/v2 v2.16.0 10 | github.com/stretchr/testify v1.10.0 11 | gopkg.in/yaml.v2 v2.4.0 12 | sigs.k8s.io/yaml v1.4.0 13 | ) 14 | 15 | require ( 16 | github.com/davecgh/go-spew v1.1.1 // indirect 17 | github.com/google/go-cmp v0.7.0 // indirect 18 | github.com/google/uuid v1.6.0 // indirect 19 | github.com/json-iterator/go v1.1.12 // indirect 20 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 21 | github.com/modern-go/reflect2 v1.0.2 // indirect 22 | github.com/pmezard/go-difflib v1.0.0 // indirect 23 | go.uber.org/multierr v1.11.0 // indirect 24 | go.uber.org/zap v1.27.0 // indirect 25 | gopkg.in/yaml.v3 v3.0.1 // indirect 26 | ) 27 | 28 | replace github.com/cloudevents/sdk-go/v2 => ../../v2 29 | -------------------------------------------------------------------------------- /sql/v2/parser/case_changing_stream.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package parser 7 | 8 | import ( 9 | "unicode" 10 | 11 | "github.com/antlr/antlr4/runtime/Go/antlr" 12 | ) 13 | 14 | // Took from https://github.com/antlr/antlr4/blob/master/doc/resources/case_changing_stream.go 15 | 16 | // CaseChangingStream wraps an existing CharStream, but upper cases, or 17 | // lower cases the input before it is tokenized. 18 | type CaseChangingStream struct { 19 | antlr.CharStream 20 | 21 | upper bool 22 | } 23 | 24 | // NewCaseChangingStream returns a new CaseChangingStream that forces 25 | // all tokens read from the underlying stream to be either upper case 26 | // or lower case based on the upper argument. 27 | func NewCaseChangingStream(in antlr.CharStream, upper bool) *CaseChangingStream { 28 | return &CaseChangingStream{in, upper} 29 | } 30 | 31 | // LA gets the value of the symbol at offset from the current position 32 | // from the underlying CharStream and converts it to either upper case 33 | // or lower case. 34 | func (is *CaseChangingStream) LA(offset int) int { 35 | in := is.CharStream.LA(offset) 36 | if in < 0 { 37 | // Such as antlr.TokenEOF which is -1 38 | return in 39 | } 40 | if is.upper { 41 | return int(unicode.ToUpper(rune(in))) 42 | } 43 | return int(unicode.ToLower(rune(in))) 44 | } 45 | -------------------------------------------------------------------------------- /sql/v2/runtime/test/tck/user_defined_functions.yaml: -------------------------------------------------------------------------------- 1 | name: User defined functions 2 | tests: 3 | - name: HASPREFIX (1) 4 | expression: "HASPREFIX('abcdef', 'ab')" 5 | result: true 6 | - name: HASPREFIX (2) 7 | expression: "HASPREFIX('abcdef', 'abcdef')" 8 | result: true 9 | - name: HASPREFIX (3) 10 | expression: "HASPREFIX('abcdef', '')" 11 | result: true 12 | - name: HASPREFIX (4) 13 | expression: "HASPREFIX('abcdef', 'gh')" 14 | result: false 15 | - name: HASPREFIX (5) 16 | expression: "HASPREFIX('abcdef', 'abcdefg')" 17 | result: false 18 | 19 | - name: KONKAT (1) 20 | expression: "KONKAT('a', 'b', 'c')" 21 | result: abc 22 | - name: KONKAT (2) 23 | expression: "KONKAT()" 24 | result: "" 25 | - name: KONKAT (3) 26 | expression: "KONKAT('a')" 27 | result: "a" -------------------------------------------------------------------------------- /sql/v2/test/tck/README.md: -------------------------------------------------------------------------------- 1 | # CloudEvents Expression Language TCK 2 | 3 | Each file of this TCK contains a set of test cases, testing one or more specific features of the language. 4 | 5 | The root file structure is composed by: 6 | 7 | * `name`: Name of the test suite contained in the file 8 | * `tests`: List of tests 9 | 10 | Each test definition includes: 11 | 12 | * `name`: Name of the test case 13 | * `expression`: Expression to test. 14 | * `result`: Expected result (OPTIONAL). Can be a boolean, an integer or a string. 15 | * `error`: Expected error (OPTIONAL). If absent, no error is expected. 16 | * `event`: Input event (OPTIONAL). If present, this is a valid event serialized in JSON format. If absent, when testing 17 | the expression, any valid event can be passed. 18 | * `eventOverrides`: Overrides to the input event (OPTIONAL). This might be used when `event` is missing, in order to 19 | define only some specific values, while the other (REQUIRED) attributes can be any value. 20 | 21 | The `error` values could be any of the following: 22 | 23 | * `parse`: Error while parsing the expression 24 | * `math`: Math error while evaluating a math operator 25 | * `cast`: Casting error 26 | * `missingFunction`: Addressed a missing function 27 | * `functionEvaluation`: Error while evaluating a function 28 | * `missingAttribute`: Error due to a missing attribute 29 | * `generic`: A generic error 30 | -------------------------------------------------------------------------------- /sql/v2/test/tck/case_sensitivity.yaml: -------------------------------------------------------------------------------- 1 | name: Case sensitivity 2 | tests: 3 | - name: TRUE 4 | expression: TRUE 5 | result: true 6 | - name: true 7 | expression: true 8 | result: true 9 | - name: tRuE 10 | expression: tRuE 11 | result: true 12 | 13 | - name: FALSE 14 | expression: FALSE 15 | result: false 16 | - name: false 17 | expression: false 18 | result: false 19 | - name: FaLsE 20 | expression: FaLsE 21 | result: false 22 | 23 | - name: String literals casing preserved 24 | expression: "'aBcD'" 25 | result: aBcD 26 | -------------------------------------------------------------------------------- /sql/v2/test/tck/context_attributes_access.yaml: -------------------------------------------------------------------------------- 1 | name: Context attributest test 2 | tests: 3 | - name: Access to required attribute 4 | expression: id 5 | eventOverrides: 6 | id: myId 7 | result: myId 8 | - name: Access to optional attribute 9 | expression: subject 10 | eventOverrides: 11 | subject: mySubject 12 | result: mySubject 13 | - name: Absent optional attribute 14 | expression: subject 15 | event: 16 | specversion: "1.0" 17 | id: myId 18 | source: localhost.localdomain 19 | type: myType 20 | result: false 21 | error: missingAttribute 22 | - name: Access to optional boolean extension 23 | expression: mybool 24 | eventOverrides: 25 | mybool: true 26 | result: true 27 | - name: Access to optional integer extension 28 | expression: myint 29 | eventOverrides: 30 | myint: 10 31 | result: 10 32 | - name: Access to optional string extension 33 | expression: myext 34 | eventOverrides: 35 | myext: "my extension" 36 | result: "my extension" 37 | - name: URL type cohercion to string 38 | expression: source 39 | event: 40 | specversion: "1.0" 41 | id: myId 42 | source: "http://localhost/source" 43 | type: myType 44 | result: "http://localhost/source" 45 | - name: Timestamp type cohercion to string 46 | expression: time 47 | event: 48 | specversion: "1.0" 49 | id: myId 50 | source: "http://localhost/source" 51 | type: myType 52 | time: 2018-04-26T14:48:09+02:00 53 | result: 2018-04-26T14:48:09+02:00 54 | -------------------------------------------------------------------------------- /sql/v2/test/tck/integer_builtin_functions.yaml: -------------------------------------------------------------------------------- 1 | name: Integer builtin functions 2 | tests: 3 | - name: ABS (1) 4 | expression: ABS(10) 5 | result: 10 6 | - name: ABS (2) 7 | expression: ABS(-10) 8 | result: 10 9 | - name: ABS (3) 10 | expression: ABS(0) 11 | result: 0 12 | - name: ABS overflow 13 | expression: ABS(-2147483648) 14 | result: 2147483647 15 | error: math 16 | 17 | -------------------------------------------------------------------------------- /sql/v2/test/tck/literals.yaml: -------------------------------------------------------------------------------- 1 | name: Literals 2 | tests: 3 | - name: TRUE literal 4 | expression: TRUE 5 | result: true 6 | - name: FALSE literal 7 | expression: FALSE 8 | result: false 9 | 10 | - name: 0 literal 11 | expression: 0 12 | result: 0 13 | - name: 1 literal 14 | expression: 1 15 | result: 1 16 | 17 | - name: String literal single quoted 18 | expression: "'abc'" 19 | result: abc 20 | - name: String literal double quoted 21 | expression: "\"abc\"" 22 | result: abc 23 | 24 | - name: String literal single quoted with case 25 | expression: "'aBc'" 26 | result: aBc 27 | - name: String literal double quoted with case 28 | expression: "\"AbC\"" 29 | result: AbC 30 | 31 | - name: Escaped string literal (1) 32 | expression: "'a\"b\\'c'" 33 | result: a"b'c 34 | - name: Escaped string literal (2) 35 | expression: "\"a'b\\\"c\"" 36 | result: a'b"c 37 | -------------------------------------------------------------------------------- /sql/v2/test/tck/negate_operator.yaml: -------------------------------------------------------------------------------- 1 | name: Negate operator 2 | tests: 3 | - name: Minus 10 4 | expression: -10 5 | result: -10 6 | - name: Minus minus 10 7 | expression: --10 8 | result: 10 9 | 10 | - name: Minus 10 with casting 11 | expression: -'10' 12 | result: -10 13 | - name: Minus minus 10 with casting 14 | expression: --'10' 15 | result: 10 16 | 17 | - name: Minus with boolean cast 18 | expression: -TRUE 19 | result: -1 20 | 21 | - name: Minus with missing attribute 22 | expression: -missing 23 | result: 0 24 | error: missingAttribute 25 | -------------------------------------------------------------------------------- /sql/v2/test/tck/not_operator.yaml: -------------------------------------------------------------------------------- 1 | name: Not operator 2 | tests: 3 | - name: Not true 4 | expression: NOT TRUE 5 | result: false 6 | - name: Not false 7 | expression: NOT FALSE 8 | result: true 9 | 10 | - name: Not true with casting 11 | expression: NOT 'TRUE' 12 | result: false 13 | - name: Not false 10 with casting 14 | expression: NOT 'FALSE' 15 | result: true 16 | 17 | - name: Not true with casting 18 | expression: NOT 10 19 | result: false 20 | 21 | - name: Not missing attribute 22 | expression: NOT missing 23 | result: false 24 | error: missingAttribute 25 | -------------------------------------------------------------------------------- /sql/v2/test/tck/parse_errors.yaml: -------------------------------------------------------------------------------- 1 | name: Parsing errors 2 | tests: 3 | - name: No closed parenthesis 4 | expression: ABC( 5 | error: parse 6 | -------------------------------------------------------------------------------- /sql/v2/test/tck/sub_expression.yaml: -------------------------------------------------------------------------------- 1 | name: Sub expressions 2 | tests: 3 | - name: Sub expression with literal 4 | expression: "(TRUE)" 5 | result: true 6 | 7 | - name: Math (1) 8 | expression: "4 * (2 + 3)" 9 | result: 20 10 | - name: Math (2) 11 | expression: "(2 + 3) * 4" 12 | result: 20 13 | -------------------------------------------------------------------------------- /sql/v2/types.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package v2 7 | 8 | type Type uint8 9 | 10 | const ( 11 | StringType Type = iota 12 | IntegerType 13 | BooleanType 14 | AnyType 15 | ) 16 | 17 | func TypePtr(t Type) *Type { 18 | return &t 19 | } 20 | 21 | func (t Type) IsSameType(val interface{}) bool { 22 | return TypeFromVal(val) == t 23 | } 24 | 25 | func (t Type) String() string { 26 | switch t { 27 | case IntegerType: 28 | return "Integer" 29 | case BooleanType: 30 | return "Boolean" 31 | case StringType: 32 | return "String" 33 | } 34 | return "Any" 35 | } 36 | 37 | func (t Type) ZeroValue() interface{} { 38 | switch t { 39 | case StringType: 40 | return "" 41 | case IntegerType: 42 | return 0 43 | case BooleanType: 44 | return false 45 | case AnyType: 46 | // by default, return false 47 | return false 48 | } 49 | return false 50 | } 51 | 52 | func TypeFromVal(val interface{}) Type { 53 | switch val.(type) { 54 | case string: 55 | return StringType 56 | case int32: 57 | return IntegerType 58 | case bool: 59 | return BooleanType 60 | } 61 | return AnyType 62 | } 63 | -------------------------------------------------------------------------------- /test/benchmark/e2e/benchmark_case.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package e2e 7 | 8 | type BenchmarkCase struct { 9 | PayloadSize int 10 | Parallelism int 11 | OutputSenders int 12 | } 13 | 14 | func GenerateAllBenchmarkCases( 15 | payloadMin int, 16 | payloadMax int, 17 | parallelismMin int, 18 | parallelismMax int, 19 | outputSendersMin int, 20 | outputSendersMax int, 21 | ) []BenchmarkCase { 22 | var cases []BenchmarkCase 23 | 24 | for payload := payloadMin; payload <= payloadMax; payload *= 2 { 25 | for parallelism := parallelismMin; parallelism <= parallelismMax; parallelism += 1 { 26 | for outputSenders := outputSendersMin; outputSenders <= outputSendersMax; outputSenders += 1 { 27 | cases = append(cases, BenchmarkCase{payload, parallelism, outputSenders}) 28 | } 29 | } 30 | } 31 | 32 | return cases 33 | } 34 | -------------------------------------------------------------------------------- /test/benchmark/e2e/benchmark_result.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package e2e 7 | 8 | import ( 9 | "encoding/csv" 10 | "strconv" 11 | "testing" 12 | ) 13 | 14 | type BenchmarkResult struct { 15 | BenchmarkCase 16 | testing.BenchmarkResult 17 | } 18 | 19 | func (br *BenchmarkResult) record() []string { 20 | return []string{ 21 | strconv.Itoa(br.Parallelism), 22 | strconv.Itoa(br.PayloadSize), 23 | strconv.Itoa(br.OutputSenders), 24 | strconv.FormatInt(br.NsPerOp(), 10), 25 | strconv.FormatInt(br.AllocedBytesPerOp(), 10), 26 | } 27 | } 28 | 29 | type BenchmarkResults []BenchmarkResult 30 | 31 | func (br BenchmarkResults) WriteToCsv(writer *csv.Writer) error { 32 | for _, i2 := range br { 33 | err := writer.Write(i2.record()) 34 | if err != nil { 35 | return err 36 | } 37 | } 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /test/benchmark/e2e/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | // Package e2e holds end-to-end eventing benchmarks. 7 | package e2e 8 | -------------------------------------------------------------------------------- /test/benchmark/e2e/http/3d_plot_allocs.gnuplot: -------------------------------------------------------------------------------- 1 | set datafile separator comma 2 | set datafile missing NaN 3 | set xlabel "Parallelism" 4 | set ylabel "Number of output senders" 5 | set zlabel "Memory Allocated/Ops" 6 | payload_size_kb=ARG1 7 | payload_size=payload_size_kb*1024 8 | 9 | print "Plotting with payload size ".payload_size."" 10 | 11 | splot "baseline-binary.csv" using 1:3:($2==payload_size?$5:1/0) title "Baseline Binary ".payload_size_kb."kb" with linespoint, \ 12 | "baseline-structured.csv" using 1:3:($2==payload_size?$5:1/0) title "Baseline Structured ".payload_size_kb."kb" with linespoint, \ 13 | "binding-structured-to-structured.csv" using 1:3:($2==payload_size?$5:1/0) title "Binding Structured to Structured ".payload_size_kb."kb" with linespoint, \ 14 | "binding-structured-to-binary.csv" using 1:3:($2==payload_size?$5:1/0) title "Binding Structured to Binary ".payload_size_kb."kb" with linespoint, \ 15 | "binding-binary-to-structured.csv" using 1:3:($2==payload_size?$5:1/0) title "Binding Binary to Structured ".payload_size_kb."kb" with linespoint, \ 16 | "binding-binary-to-binary.csv" using 1:3:($2==payload_size?$5:1/0) title "Binding Binary to Binary ".payload_size_kb."kb" with linespoint, \ 17 | "client-binary.csv" using 1:3:($2==payload_size?$5:1/0) title "Client Binary ".payload_size_kb."kb" with linespoint, \ 18 | "client-structured.csv" using 1:3:($2==payload_size?$5:1/0) title "Client Structured ".payload_size_kb."kb" with linespoint 19 | pause -1 20 | -------------------------------------------------------------------------------- /test/benchmark/e2e/http/3d_plot_ns.gnuplot: -------------------------------------------------------------------------------- 1 | set datafile separator comma 2 | set datafile missing NaN 3 | set xlabel "Parallelism" 4 | set ylabel "Number of output senders" 5 | set zlabel "Nanoseconds/Ops" 6 | payload_size_kb=ARG1 7 | payload_size=payload_size_kb*1024 8 | 9 | print "Plotting with payload size ".payload_size."" 10 | 11 | splot "baseline-binary.csv" using 1:3:($2==payload_size?$4:1/0) title "Baseline Binary ".payload_size_kb."kb" with linespoint, \ 12 | "baseline-structured.csv" using 1:3:($2==payload_size?$4:1/0) title "Baseline Structured ".payload_size_kb."kb" with linespoint, \ 13 | "binding-structured-to-structured.csv" using 1:3:($2==payload_size?$4:1/0) title "Binding Structured to Structured ".payload_size_kb."kb" with linespoint, \ 14 | "binding-structured-to-binary.csv" using 1:3:($2==payload_size?$4:1/0) title "Binding Structured to Binary ".payload_size_kb."kb" with linespoint, \ 15 | "binding-binary-to-structured.csv" using 1:3:($2==payload_size?$4:1/0) title "Binding Binary to Structured ".payload_size_kb."kb" with linespoint, \ 16 | "binding-binary-to-binary.csv" using 1:3:($2==payload_size?$4:1/0) title "Binding Binary to Binary ".payload_size_kb."kb" with linespoint, \ 17 | "client-binary.csv" using 1:3:($2==payload_size?$4:1/0) title "Client Binary ".payload_size_kb."kb" with linespoint, \ 18 | "client-structured.csv" using 1:3:($2==payload_size?$4:1/0) title "Client Structured ".payload_size_kb."kb" with linespoint 19 | pause -1 20 | -------------------------------------------------------------------------------- /test/benchmark/e2e/http/README.md: -------------------------------------------------------------------------------- 1 | # E2E Benchmark to compare HTTP binding & HTTP transport 2 | 3 | This benchmark aims to provide a comparison between package `github.com/cloudevents/sdk-go/pkg/bindings/http` and `github.com/cloudevents/sdk-go/pkg/cloudevents/transport/http` 4 | 5 | ## Metrics 6 | 7 | Test keys are: 8 | 9 | * Parallelism (Configuration of `GOMAXPROCS` value, from 1 to `runtime.NumCPU()`) 10 | * Payload size in kb 11 | 12 | ## Run and visualize results 13 | 14 | ### Build 15 | 16 | ```shell script 17 | go build main.go 18 | ``` 19 | 20 | ### Run all tests 21 | 22 | ```shell script 23 | ./main --bench=baseline > baseline.csv && ./main --bench=receiver-sender > receiver-sender.csv && ./main --bench=client > client.csv 24 | ``` 25 | 26 | ### Plot results 27 | 28 | An example plot script is provided to plot parallelism - nanoseconds/ops, given the payload size: 29 | 30 | ```shell script 31 | gnuplot -c plot_parallelism_ns.gnuplot 32 | ``` 33 | 34 | Example: 35 | 36 | ```shell script 37 | gnuplot -c plot_parallelism_ns.gnuplot 16 38 | ``` 39 | -------------------------------------------------------------------------------- /test/conformance/kafka_steps_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package conformance 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/IBM/sarama" 12 | "github.com/cloudevents/sdk-go/protocol/kafka_sarama/v2" 13 | "github.com/cloudevents/sdk-go/v2/binding" 14 | "github.com/cucumber/godog" 15 | ) 16 | 17 | var consumerMessage *sarama.ConsumerMessage 18 | 19 | func KafkaFeatureContext(ctx *godog.ScenarioContext) { 20 | ctx.BeforeScenario(func(*godog.Scenario) { 21 | consumerMessage = nil 22 | }) 23 | ctx.Step(`^Kafka Protocol Binding is supported$`, func() error { 24 | return nil 25 | }) 26 | 27 | ctx.Step(`^a Kafka message with payload:$`, func(payload *godog.DocString) error { 28 | consumerMessage = &sarama.ConsumerMessage{ 29 | Value: []byte(payload.Content), 30 | } 31 | 32 | return nil 33 | }) 34 | 35 | ctx.Step(`^Kafka headers:$`, func(headers *godog.Table) error { 36 | consumerMessage.Headers = make([]*sarama.RecordHeader, len(headers.Rows)) 37 | 38 | for i, row := range headers.Rows { 39 | var key = row.Cells[0].Value 40 | var value = row.Cells[1].Value 41 | consumerMessage.Headers[i] = &sarama.RecordHeader{Key: []byte(key), Value: []byte(value)} 42 | } 43 | 44 | return nil 45 | }) 46 | 47 | ctx.Step(`^parsed as Kafka message$`, func() error { 48 | message := kafka_sarama.NewMessageFromConsumerMessage(consumerMessage) 49 | 50 | event, err := binding.ToEvent(context.TODO(), message) 51 | 52 | if err != nil { 53 | return err 54 | } 55 | 56 | currentEvent = event 57 | return nil 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /test/conformance/run_test.go: -------------------------------------------------------------------------------- 1 | //go:build conformance 2 | // +build conformance 3 | 4 | /* 5 | Copyright 2021 The CloudEvents Authors 6 | SPDX-License-Identifier: Apache-2.0 7 | */ 8 | package conformance 9 | 10 | import ( 11 | "flag" 12 | "os" 13 | "testing" 14 | 15 | "github.com/cucumber/godog" 16 | "github.com/cucumber/godog/colors" 17 | ) 18 | 19 | var opts = godog.Options{ 20 | Output: colors.Colored(os.Stdout), 21 | Format: "pretty", 22 | } 23 | 24 | func TestMain(m *testing.M) { 25 | flag.Parse() 26 | if len(flag.Args()) > 0 { 27 | opts.Paths = flag.Args() 28 | } else { 29 | opts.Paths = []string{ 30 | "../../conformance/features/", 31 | } 32 | } 33 | 34 | status := godog.TestSuite{ 35 | Name: "CloudEvents", 36 | ScenarioInitializer: InitializeScenario, 37 | Options: &opts, 38 | }.Run() 39 | 40 | if st := m.Run(); st > status { 41 | status = st 42 | } 43 | os.Exit(status) 44 | } 45 | 46 | func InitializeScenario(ctx *godog.ScenarioContext) { 47 | CloudEventsFeatureContext(ctx) 48 | HTTPFeatureContext(ctx) 49 | KafkaFeatureContext(ctx) 50 | } 51 | -------------------------------------------------------------------------------- /test/integration/http/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | // Package http contains HTTP integration test helpers. 7 | package http 8 | -------------------------------------------------------------------------------- /test/observability/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/test/observability 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | require ( 8 | github.com/cloudevents/sdk-go/observability/opentelemetry/v2 v2.16.0 9 | github.com/cloudevents/sdk-go/v2 v2.16.0 10 | github.com/stretchr/testify v1.10.0 11 | go.opentelemetry.io/otel v1.36.0 12 | go.opentelemetry.io/otel/sdk v1.36.0 13 | go.opentelemetry.io/otel/trace v1.36.0 14 | ) 15 | 16 | require ( 17 | github.com/davecgh/go-spew v1.1.1 // indirect 18 | github.com/felixge/httpsnoop v1.0.4 // indirect 19 | github.com/go-logr/logr v1.4.3 // indirect 20 | github.com/go-logr/stdr v1.2.2 // indirect 21 | github.com/google/uuid v1.6.0 // indirect 22 | github.com/json-iterator/go v1.1.12 // indirect 23 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 24 | github.com/modern-go/reflect2 v1.0.2 // indirect 25 | github.com/pmezard/go-difflib v1.0.0 // indirect 26 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect 27 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect 28 | go.opentelemetry.io/otel/metric v1.36.0 // indirect 29 | go.uber.org/multierr v1.11.0 // indirect 30 | go.uber.org/zap v1.27.0 // indirect 31 | golang.org/x/sys v0.33.0 // indirect 32 | gopkg.in/yaml.v3 v3.0.1 // indirect 33 | ) 34 | 35 | replace github.com/cloudevents/sdk-go/observability/opentelemetry/v2 => ../../observability/opentelemetry/v2 36 | 37 | replace github.com/cloudevents/sdk-go/v2 => ../../v2 38 | -------------------------------------------------------------------------------- /test/observability/opentelemetry/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package opentelemetry validates the carrier and observability service instrumentation with the default SDK. 8 | 9 | This package is in a separate module from the instrumentation it tests to 10 | isolate the dependency of the default OTel SDK and not impose this as a transitive 11 | dependency for users. 12 | */ 13 | package opentelemetry 14 | -------------------------------------------------------------------------------- /test/sql/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/test/sql 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | replace github.com/cloudevents/sdk-go/v2 => ./../../v2 8 | 9 | replace github.com/cloudevents/sdk-go/sql/v2 => ./../../sql/v2 10 | 11 | require ( 12 | github.com/cloudevents/sdk-go/sql/v2 v2.16.0 13 | github.com/cloudevents/sdk-go/v2 v2.16.0 14 | ) 15 | 16 | require ( 17 | github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 // indirect 18 | github.com/google/uuid v1.6.0 // indirect 19 | github.com/json-iterator/go v1.1.12 // indirect 20 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 21 | github.com/modern-go/reflect2 v1.0.2 // indirect 22 | go.uber.org/multierr v1.11.0 // indirect 23 | go.uber.org/zap v1.27.0 // indirect 24 | ) 25 | -------------------------------------------------------------------------------- /test/sql/shortcircuit_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package sql 7 | 8 | import ( 9 | "testing" 10 | 11 | cesql "github.com/cloudevents/sdk-go/sql/v2/parser" 12 | cloudevents "github.com/cloudevents/sdk-go/v2" 13 | ) 14 | 15 | func TestShortCircuitAND(t *testing.T) { 16 | 17 | sql := "(EXISTS revisiontype AND revisiontype=Branch) OR (branch='master')" 18 | 19 | expression, err := cesql.Parse(string(sql)) 20 | evt := cloudevents.NewEvent("1.0") 21 | evt.SetID("evt-1") 22 | evt.SetType("what-ever") 23 | evt.SetSource("/event") 24 | evt.SetExtension("branch", "master") 25 | val, err := expression.Evaluate(evt) 26 | 27 | if err != nil { 28 | t.Errorf("err should be nil: %s", err.Error()) 29 | } else { 30 | if !val.(bool) { 31 | t.Errorf("should be true ,but :%s", val) 32 | } 33 | } 34 | } 35 | 36 | func TestShortCircuitOR(t *testing.T) { 37 | 38 | sql := "(branch='master' OR revisiontype=Branch)" 39 | 40 | expression, err := cesql.Parse(string(sql)) 41 | evt := cloudevents.NewEvent("1.0") 42 | evt.SetID("evt-1") 43 | evt.SetType("what-ever") 44 | evt.SetSource("/event") 45 | evt.SetExtension("branch", "master") 46 | val, err := expression.Evaluate(evt) 47 | 48 | if err != nil { 49 | t.Errorf("err should be nil: %s", err.Error()) 50 | } else { 51 | if !val.(bool) { 52 | t.Errorf("should be true ,but :%s", val) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tools/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/tools 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | require github.com/kelseyhightower/envconfig v1.4.0 8 | -------------------------------------------------------------------------------- /tools/go.sum: -------------------------------------------------------------------------------- 1 | github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= 2 | github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= 3 | -------------------------------------------------------------------------------- /tools/http/raw/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package main 7 | 8 | import ( 9 | "fmt" 10 | "log" 11 | "net/http" 12 | "net/http/httputil" 13 | 14 | "github.com/kelseyhightower/envconfig" 15 | ) 16 | 17 | type RawHTTP struct { 18 | Port int `envconfig:"PORT" default:"8080"` 19 | } 20 | 21 | func (raw *RawHTTP) ServeHTTP(w http.ResponseWriter, r *http.Request) { 22 | w.WriteHeader(http.StatusOK) 23 | if reqBytes, err := httputil.DumpRequest(r, true); err == nil { 24 | log.Printf("Raw HTTP Request:\n%+v", string(reqBytes)) 25 | _, _ = w.Write(reqBytes) 26 | } else { 27 | log.Printf("Failed to call DumpRequest: %s", err) 28 | } 29 | fmt.Println("------------------------------") 30 | } 31 | 32 | func main() { 33 | var env RawHTTP 34 | if err := envconfig.Process("", &env); err != nil { 35 | log.Fatalf("Failed to process env var: %s", err) 36 | } 37 | log.Printf("Starting listening on :%d\n", env.Port) 38 | log.Println(http.ListenAndServe(fmt.Sprintf(":%d", env.Port), &env)) 39 | } 40 | -------------------------------------------------------------------------------- /v2/binding/buffering/acks_before_finish_message.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package buffering 7 | 8 | import ( 9 | "sync/atomic" 10 | 11 | "github.com/cloudevents/sdk-go/v2/binding" 12 | "github.com/cloudevents/sdk-go/v2/binding/spec" 13 | ) 14 | 15 | type acksMessage struct { 16 | binding.Message 17 | requiredAcks int32 18 | } 19 | 20 | func (m *acksMessage) GetAttribute(k spec.Kind) (spec.Attribute, interface{}) { 21 | return m.Message.(binding.MessageMetadataReader).GetAttribute(k) 22 | } 23 | 24 | func (m *acksMessage) GetExtension(s string) interface{} { 25 | return m.Message.(binding.MessageMetadataReader).GetExtension(s) 26 | } 27 | 28 | func (m *acksMessage) GetWrappedMessage() binding.Message { 29 | return m.Message 30 | } 31 | 32 | func (m *acksMessage) Finish(err error) error { 33 | remainingAcks := atomic.AddInt32(&m.requiredAcks, -1) 34 | if remainingAcks == 0 { 35 | return m.Message.Finish(err) 36 | } 37 | return nil 38 | } 39 | 40 | var _ binding.MessageWrapper = (*acksMessage)(nil) 41 | 42 | // WithAcksBeforeFinish returns a wrapper for m that calls m.Finish() 43 | // only after the specified number of acks are received. 44 | // Use it when you need to dispatch a Message using several Sender instances 45 | func WithAcksBeforeFinish(m binding.Message, requiredAcks int) binding.Message { 46 | return &acksMessage{Message: m, requiredAcks: int32(requiredAcks)} 47 | } 48 | -------------------------------------------------------------------------------- /v2/binding/buffering/copy_message_benchmark_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package buffering 7 | 8 | import ( 9 | "context" 10 | "testing" 11 | 12 | . "github.com/cloudevents/sdk-go/v2/binding/test" 13 | . "github.com/cloudevents/sdk-go/v2/test" 14 | ) 15 | 16 | var err error 17 | 18 | func BenchmarkBufferMessageFromStructured(b *testing.B) { 19 | e := FullEvent() 20 | input := MustCreateMockStructuredMessage(b, e) 21 | ctx := context.Background() 22 | for i := 0; i < b.N; i++ { 23 | outputMessage, _ := BufferMessage(ctx, input) 24 | err = outputMessage.Finish(nil) 25 | } 26 | } 27 | 28 | func BenchmarkBufferMessageFromBinary(b *testing.B) { 29 | e := FullEvent() 30 | input := MustCreateMockBinaryMessage(e) 31 | ctx := context.Background() 32 | for i := 0; i < b.N; i++ { 33 | outputMessage, _ := BufferMessage(ctx, input) 34 | err = outputMessage.Finish(nil) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /v2/binding/buffering/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | // Package buffering provides APIs for buffered messages. 7 | package buffering 8 | -------------------------------------------------------------------------------- /v2/binding/finish_message.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package binding 7 | 8 | import "github.com/cloudevents/sdk-go/v2/binding/spec" 9 | 10 | type finishMessage struct { 11 | Message 12 | finish func(error) 13 | } 14 | 15 | func (m *finishMessage) GetAttribute(k spec.Kind) (spec.Attribute, interface{}) { 16 | return m.Message.(MessageMetadataReader).GetAttribute(k) 17 | } 18 | 19 | func (m *finishMessage) GetExtension(s string) interface{} { 20 | return m.Message.(MessageMetadataReader).GetExtension(s) 21 | } 22 | 23 | func (m *finishMessage) GetWrappedMessage() Message { 24 | return m.Message 25 | } 26 | 27 | func (m *finishMessage) Finish(err error) error { 28 | err2 := m.Message.Finish(err) // Finish original message first 29 | if m.finish != nil { 30 | m.finish(err) // Notify callback 31 | } 32 | return err2 33 | } 34 | 35 | var _ MessageWrapper = (*finishMessage)(nil) 36 | 37 | // WithFinish returns a wrapper for m that calls finish() and 38 | // m.Finish() in its Finish(). 39 | // Allows code to be notified when a message is Finished. 40 | func WithFinish(m Message, finish func(error)) Message { 41 | return &finishMessage{Message: m, finish: finish} 42 | } 43 | -------------------------------------------------------------------------------- /v2/binding/format/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package format formats structured events. 8 | 9 | The "application/cloudevents+json" format is built-in and always 10 | available. Other formats may be added. 11 | */ 12 | package format 13 | -------------------------------------------------------------------------------- /v2/binding/spec/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package spec provides spec-version metadata. 8 | 9 | For use by code that maps events using (prefixed) attribute name strings. 10 | Supports handling multiple spec versions uniformly. 11 | */ 12 | package spec 13 | -------------------------------------------------------------------------------- /v2/binding/spec/match_exact_version_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package spec_test 7 | 8 | import ( 9 | "net/textproto" 10 | "testing" 11 | 12 | "github.com/stretchr/testify/require" 13 | 14 | "github.com/cloudevents/sdk-go/v2/binding/spec" 15 | "github.com/cloudevents/sdk-go/v2/event" 16 | "github.com/cloudevents/sdk-go/v2/test" 17 | ) 18 | 19 | func TestMatchExactVersion(t *testing.T) { 20 | test.EachEvent(t, test.AllVersions([]event.Event{test.FullEvent()}), func(t *testing.T, e event.Event) { 21 | e = e.Clone() 22 | s := spec.WithPrefixMatchExact( 23 | func(s string) string { 24 | if s == "datacontenttype" { 25 | return "Content-Type" 26 | } else { 27 | return textproto.CanonicalMIMEHeaderKey("Ce-" + s) 28 | } 29 | }, 30 | "Ce-", 31 | ) 32 | sv := s.Version(e.SpecVersion()) 33 | require.NotNil(t, sv) 34 | 35 | require.Equal(t, e.ID(), sv.Attribute("Ce-Id").Get(e.Context)) 36 | require.Equal(t, "id", sv.Attribute("Ce-Id").Name()) 37 | 38 | require.Equal(t, e.DataContentType(), sv.Attribute("Content-Type").Get(e.Context)) 39 | require.Equal(t, "datacontenttype", sv.Attribute("Content-Type").Name()) 40 | }) 41 | } 42 | -------------------------------------------------------------------------------- /v2/binding/spec/spec_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package spec_test 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/cloudevents/sdk-go/v2/binding/spec" 12 | "github.com/cloudevents/sdk-go/v2/event" 13 | "github.com/cloudevents/sdk-go/v2/test" 14 | tassert "github.com/stretchr/testify/assert" 15 | "github.com/stretchr/testify/require" 16 | ) 17 | 18 | func TestVersions(t *testing.T) { 19 | assert := tassert.New(t) 20 | versions := spec.New() 21 | assert.Equal("specversion", versions.PrefixedSpecVersionName()) 22 | 23 | want := []string{"1.0", "0.3"} 24 | all := versions.Versions() 25 | assert.Equal(len(want), len(all)) 26 | for i, s := range want { 27 | assert.Equal(s, all[i].String()) 28 | assert.Equal(s, all[i].NewContext().GetSpecVersion()) 29 | converted := all[i].Convert(event.EventContextV1{}.AsV1()) 30 | assert.Equal(s, converted.GetSpecVersion(), "%v %v %v", i, s, converted) 31 | } 32 | assert.Equal(want[0], versions.Latest().NewContext().GetSpecVersion()) 33 | } 34 | 35 | func TestSetAttribute(t *testing.T) { 36 | test.EachEvent(t, test.AllVersions([]event.Event{test.MinEvent()}), func(t *testing.T, e event.Event) { 37 | e = e.Clone() 38 | s := spec.WithPrefix("ce_") 39 | sv := s.Version(e.SpecVersion()) 40 | 41 | id := "another-id" 42 | require.NoError(t, sv.SetAttribute(e.Context, "ce_id", id)) 43 | require.Equal(t, id, e.ID()) 44 | 45 | extName := "ce_someExt" 46 | extValue := "extValue" 47 | require.NoError(t, sv.SetAttribute(e.Context, extName, extValue)) 48 | require.Equal(t, extValue, e.Extensions()["someext"]) 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /v2/binding/structured_writer.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package binding 7 | 8 | import ( 9 | "context" 10 | "io" 11 | 12 | "github.com/cloudevents/sdk-go/v2/binding/format" 13 | ) 14 | 15 | // StructuredWriter is used to visit a structured Message and generate a new representation. 16 | // 17 | // Protocols that supports structured encoding should implement this interface to implement direct 18 | // structured to structured encoding and event to structured encoding. 19 | type StructuredWriter interface { 20 | // Event receives an io.Reader for the whole event. 21 | SetStructuredEvent(ctx context.Context, format format.Format, event io.Reader) error 22 | } 23 | -------------------------------------------------------------------------------- /v2/binding/test/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package test provides utilities to test binding implementations and transformers. 8 | */ 9 | package test 10 | -------------------------------------------------------------------------------- /v2/binding/test/mock_transformer.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package test 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/stretchr/testify/require" 12 | 13 | "github.com/cloudevents/sdk-go/v2/binding" 14 | ) 15 | 16 | type MockTransformer struct { 17 | Invoked int 18 | } 19 | 20 | func (m *MockTransformer) Transform(binding.MessageMetadataReader, binding.MessageMetadataWriter) error { 21 | m.Invoked++ 22 | return nil 23 | } 24 | 25 | var _ binding.Transformer = (*MockTransformer)(nil) 26 | 27 | func AssertTransformerInvokedOneTime(t *testing.T, m *MockTransformer) { 28 | require.Equal(t, 29 | 1, 30 | m.Invoked, 31 | "Transformer must be invoked one time, while it was invoked %d", 32 | m.Invoked, 33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /v2/binding/test/mock_unknown_message.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package test 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/cloudevents/sdk-go/v2/binding" 12 | ) 13 | 14 | type unknownMessage struct{} 15 | 16 | func (u unknownMessage) ReadEncoding() binding.Encoding { 17 | return binding.EncodingUnknown 18 | } 19 | 20 | func (u unknownMessage) ReadStructured(context.Context, binding.StructuredWriter) error { 21 | return binding.ErrNotStructured 22 | } 23 | 24 | func (u unknownMessage) ReadBinary(context.Context, binding.BinaryWriter) error { 25 | return binding.ErrNotBinary 26 | } 27 | 28 | func (u unknownMessage) Finish(error) error { 29 | return nil 30 | } 31 | 32 | var UnknownMessage binding.Message = unknownMessage{} 33 | -------------------------------------------------------------------------------- /v2/binding/transformer.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package binding 7 | 8 | // Transformer is an interface that implements a transformation 9 | // process while transferring the event from the Message 10 | // implementation to the provided encoder 11 | // 12 | // When a write function (binding.Write, binding.ToEvent, buffering.CopyMessage, etc.) 13 | // takes Transformer(s) as parameter, it eventually converts the message to a form 14 | // which correctly implements MessageMetadataReader, in order to guarantee that transformation 15 | // is applied 16 | type Transformer interface { 17 | Transform(MessageMetadataReader, MessageMetadataWriter) error 18 | } 19 | 20 | // TransformerFunc is a type alias to implement a Transformer through a function pointer 21 | type TransformerFunc func(MessageMetadataReader, MessageMetadataWriter) error 22 | 23 | func (t TransformerFunc) Transform(r MessageMetadataReader, w MessageMetadataWriter) error { 24 | return t(r, w) 25 | } 26 | 27 | var _ Transformer = (TransformerFunc)(nil) 28 | 29 | // Transformers is a utility alias to run several Transformer 30 | type Transformers []Transformer 31 | 32 | func (t Transformers) Transform(r MessageMetadataReader, w MessageMetadataWriter) error { 33 | for _, transformer := range t { 34 | err := transformer.Transform(r, w) 35 | if err != nil { 36 | return err 37 | } 38 | } 39 | return nil 40 | } 41 | 42 | var _ Transformer = (Transformers)(nil) 43 | -------------------------------------------------------------------------------- /v2/binding/transformer/add_metadata.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package transformer 7 | 8 | import ( 9 | "github.com/cloudevents/sdk-go/v2/binding" 10 | "github.com/cloudevents/sdk-go/v2/binding/spec" 11 | "github.com/cloudevents/sdk-go/v2/types" 12 | ) 13 | 14 | // AddAttribute adds a cloudevents attribute (if missing) during the encoding process 15 | func AddAttribute(attributeKind spec.Kind, value interface{}) binding.TransformerFunc { 16 | return SetAttribute(attributeKind, func(i2 interface{}) (i interface{}, err error) { 17 | if types.IsZero(i2) { 18 | return value, nil 19 | } 20 | return i2, nil 21 | }) 22 | } 23 | 24 | // AddExtension adds a cloudevents extension (if missing) during the encoding process 25 | func AddExtension(name string, value interface{}) binding.TransformerFunc { 26 | return SetExtension(name, func(i2 interface{}) (i interface{}, err error) { 27 | if types.IsZero(i2) { 28 | return value, nil 29 | } 30 | return i2, nil 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /v2/binding/transformer/default_metadata.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package transformer 7 | 8 | import ( 9 | "time" 10 | 11 | "github.com/cloudevents/sdk-go/v2/binding" 12 | "github.com/cloudevents/sdk-go/v2/binding/spec" 13 | "github.com/cloudevents/sdk-go/v2/types" 14 | ) 15 | 16 | var ( 17 | // Add the cloudevents time attribute, if missing, to time.Now() 18 | AddTimeNow binding.Transformer = addTimeNow{} 19 | ) 20 | 21 | type addTimeNow struct{} 22 | 23 | func (a addTimeNow) Transform(reader binding.MessageMetadataReader, writer binding.MessageMetadataWriter) error { 24 | attr, ti := reader.GetAttribute(spec.Time) 25 | if ti == nil { 26 | return writer.SetAttribute(attr, types.Timestamp{Time: time.Now()}) 27 | } 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /v2/binding/transformer/delete_metadata.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package transformer 7 | 8 | import ( 9 | "github.com/cloudevents/sdk-go/v2/binding" 10 | "github.com/cloudevents/sdk-go/v2/binding/spec" 11 | ) 12 | 13 | // DeleteAttribute deletes a cloudevents attribute during the encoding process 14 | func DeleteAttribute(attributeKind spec.Kind) binding.TransformerFunc { 15 | return SetAttribute(attributeKind, func(i2 interface{}) (i interface{}, err error) { 16 | return nil, nil 17 | }) 18 | } 19 | 20 | // DeleteExtension deletes a cloudevents extension during the encoding process 21 | func DeleteExtension(name string) binding.TransformerFunc { 22 | return SetExtension(name, func(i2 interface{}) (i interface{}, err error) { 23 | return nil, nil 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /v2/binding/transformer/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | // Package transformer provides methods for creating event message transformers. 7 | package transformer 8 | -------------------------------------------------------------------------------- /v2/binding/transformer/version.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package transformer 7 | 8 | import ( 9 | "github.com/cloudevents/sdk-go/v2/binding" 10 | "github.com/cloudevents/sdk-go/v2/binding/spec" 11 | ) 12 | 13 | // Version converts the event context version to the specified one. 14 | func Version(newVersion spec.Version) binding.TransformerFunc { 15 | return func(reader binding.MessageMetadataReader, writer binding.MessageMetadataWriter) error { 16 | _, sv := reader.GetAttribute(spec.SpecVersion) 17 | if newVersion.String() == sv { 18 | return nil 19 | } 20 | 21 | for _, newAttr := range newVersion.Attributes() { 22 | oldAttr, val := reader.GetAttribute(newAttr.Kind()) 23 | if oldAttr != nil && val != nil { 24 | // Erase old attr 25 | err := writer.SetAttribute(oldAttr, nil) 26 | if err != nil { 27 | return nil 28 | } 29 | if newAttr.Kind() == spec.SpecVersion { 30 | err = writer.SetAttribute(newAttr, newVersion.String()) 31 | if err != nil { 32 | return nil 33 | } 34 | } else { 35 | err = writer.SetAttribute(newAttr, val) 36 | if err != nil { 37 | return nil 38 | } 39 | } 40 | } 41 | } 42 | return nil 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /v2/binding/utils/structured_message.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package utils 7 | 8 | import ( 9 | "context" 10 | "io" 11 | 12 | "github.com/cloudevents/sdk-go/v2/binding" 13 | "github.com/cloudevents/sdk-go/v2/binding/format" 14 | ) 15 | 16 | type genericStructuredMessage struct { 17 | format format.Format 18 | reader io.Reader 19 | } 20 | 21 | // NewStructuredMessage wraps a format and an io.Reader returning an implementation of Message 22 | // This message *cannot* be read several times safely 23 | func NewStructuredMessage(format format.Format, reader io.Reader) *genericStructuredMessage { 24 | return &genericStructuredMessage{reader: reader, format: format} 25 | } 26 | 27 | var _ binding.Message = (*genericStructuredMessage)(nil) 28 | 29 | func (m *genericStructuredMessage) ReadEncoding() binding.Encoding { 30 | return binding.EncodingStructured 31 | } 32 | 33 | func (m *genericStructuredMessage) ReadStructured(ctx context.Context, encoder binding.StructuredWriter) error { 34 | return encoder.SetStructuredEvent(ctx, m.format, m.reader) 35 | } 36 | 37 | func (m *genericStructuredMessage) ReadBinary(ctx context.Context, encoder binding.BinaryWriter) error { 38 | return binding.ErrNotBinary 39 | } 40 | 41 | func (m *genericStructuredMessage) Finish(err error) error { 42 | if closer, ok := m.reader.(io.ReadCloser); ok { 43 | if err2 := closer.Close(); err2 != nil { 44 | return err2 45 | } 46 | } 47 | return err 48 | } 49 | -------------------------------------------------------------------------------- /v2/binding/utils/structured_message_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package utils_test 7 | 8 | import ( 9 | "bytes" 10 | "context" 11 | "io" 12 | "testing" 13 | 14 | "github.com/stretchr/testify/require" 15 | 16 | "github.com/cloudevents/sdk-go/v2/binding" 17 | "github.com/cloudevents/sdk-go/v2/binding/format" 18 | "github.com/cloudevents/sdk-go/v2/binding/utils" 19 | "github.com/cloudevents/sdk-go/v2/test" 20 | ) 21 | 22 | func TestNewStructuredMessage(t *testing.T) { 23 | testEvent := test.ConvertEventExtensionsToString(t, test.FullEvent()) 24 | jsonBytes := test.MustJSON(t, testEvent) 25 | 26 | message := utils.NewStructuredMessage(format.JSON, io.NopCloser(bytes.NewReader(jsonBytes))) 27 | 28 | require.Equal(t, binding.EncodingStructured, message.ReadEncoding()) 29 | 30 | event := test.MustToEvent(t, context.TODO(), message) 31 | test.AssertEventEquals(t, testEvent, event) 32 | 33 | require.NoError(t, message.Finish(nil)) 34 | } 35 | -------------------------------------------------------------------------------- /v2/binding/utils/write_structured_message.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package utils 7 | 8 | import ( 9 | "context" 10 | "io" 11 | 12 | "github.com/cloudevents/sdk-go/v2/binding" 13 | "github.com/cloudevents/sdk-go/v2/binding/format" 14 | ) 15 | 16 | // WriteStructured fills the provided io.Writer with the binding.Message m in structured mode. 17 | // Using context you can tweak the encoding processing (more details on binding.Write documentation). 18 | func WriteStructured(ctx context.Context, m binding.Message, writer io.Writer, transformers ...binding.Transformer) error { 19 | _, err := binding.Write( 20 | ctx, 21 | m, 22 | wsMessageWriter{writer}, 23 | nil, 24 | transformers..., 25 | ) 26 | return err 27 | } 28 | 29 | type wsMessageWriter struct { 30 | io.Writer 31 | } 32 | 33 | func (w wsMessageWriter) SetStructuredEvent(_ context.Context, _ format.Format, event io.Reader) error { 34 | _, err := io.Copy(w.Writer, event) 35 | if err != nil { 36 | // Try to close anyway 37 | _ = w.tryToClose() 38 | return err 39 | } 40 | 41 | return w.tryToClose() 42 | } 43 | 44 | func (w wsMessageWriter) tryToClose() error { 45 | if closer, ok := w.Writer.(io.WriteCloser); ok { 46 | return closer.Close() 47 | } 48 | return nil 49 | } 50 | 51 | var _ binding.StructuredWriter = wsMessageWriter{} // Test it conforms to the interface 52 | -------------------------------------------------------------------------------- /v2/client/client_http.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package client 7 | 8 | import ( 9 | "github.com/cloudevents/sdk-go/v2/protocol/http" 10 | ) 11 | 12 | // NewHTTP provides the good defaults for the common case using an HTTP 13 | // Protocol client. 14 | // The WithTimeNow, and WithUUIDs client options are also applied to the 15 | // client, all outbound events will have a time and id set if not already 16 | // present. 17 | func NewHTTP(opts ...http.Option) (Client, error) { 18 | p, err := http.New(opts...) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | c, err := New(p, WithTimeNow(), WithUUIDs()) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | return c, nil 29 | } 30 | 31 | // NewDefault has been replaced by NewHTTP 32 | // Deprecated. To get the same as NewDefault provided, please use NewHTTP with 33 | // the observability service passed as an option, or client.NewClientHTTP from 34 | // package github.com/cloudevents/sdk-go/observability/opencensus/v2/client 35 | var NewDefault = NewHTTP 36 | -------------------------------------------------------------------------------- /v2/client/client_observed.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package client 7 | 8 | // NewObserved produces a new client with the provided transport object and applied 9 | // client options. 10 | // Deprecated: This now has the same behaviour of New, and will be removed in future releases. 11 | // As New, you must provide the observability service to use. 12 | var NewObserved = New 13 | -------------------------------------------------------------------------------- /v2/client/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package client holds the recommended entry points for interacting with the CloudEvents Golang SDK. The client wraps 8 | a selected transport. The client adds validation and defaulting for sending events, and flexible receiver method 9 | registration. For full details, read the `client.Client` documentation. 10 | */ 11 | package client 12 | -------------------------------------------------------------------------------- /v2/client/http_receiver.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package client 7 | 8 | import ( 9 | "context" 10 | cecontext "github.com/cloudevents/sdk-go/v2/context" 11 | thttp "github.com/cloudevents/sdk-go/v2/protocol/http" 12 | "go.uber.org/zap" 13 | "net/http" 14 | ) 15 | 16 | func NewHTTPReceiveHandler(ctx context.Context, p *thttp.Protocol, fn interface{}) (*EventReceiver, error) { 17 | invoker, err := newReceiveInvoker(fn, noopObservabilityService{}, nil, nil, false) //TODO(slinkydeveloper) maybe not nil? 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | return &EventReceiver{ 23 | p: p, 24 | invoker: invoker, 25 | }, nil 26 | } 27 | 28 | type EventReceiver struct { 29 | p *thttp.Protocol 30 | invoker Invoker 31 | } 32 | 33 | func (r *EventReceiver) ServeHTTP(rw http.ResponseWriter, req *http.Request) { 34 | // Prepare to handle the message if there's one (context cancellation will ensure this closes) 35 | go func() { 36 | ctx := req.Context() 37 | msg, respFn, err := r.p.Respond(ctx) 38 | if err != nil { 39 | cecontext.LoggerFrom(context.TODO()).Debugw("failed to call Respond", zap.Error(err)) 40 | } else if err := r.invoker.Invoke(ctx, msg, respFn); err != nil { 41 | cecontext.LoggerFrom(context.TODO()).Debugw("failed to call Invoke", zap.Error(err)) 42 | } 43 | }() 44 | r.p.ServeHTTP(rw, req) 45 | } 46 | -------------------------------------------------------------------------------- /v2/context/context_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package context_test 7 | 8 | import ( 9 | "context" 10 | "net/url" 11 | "testing" 12 | 13 | cecontext "github.com/cloudevents/sdk-go/v2/context" 14 | "github.com/google/go-cmp/cmp" 15 | ) 16 | 17 | func TestTargetContext(t *testing.T) { 18 | exampleDotCom, _ := url.Parse("http://example.com") 19 | 20 | testCases := map[string]struct { 21 | target string 22 | ctx context.Context 23 | want *url.URL 24 | }{ 25 | "todo context, set url": { 26 | ctx: context.TODO(), 27 | target: "http://example.com", 28 | want: exampleDotCom, 29 | }, 30 | "bad url": { 31 | ctx: context.TODO(), 32 | target: "%", 33 | }, 34 | "already set target": { 35 | ctx: cecontext.WithTarget(context.TODO(), "http://example2.com"), 36 | target: "http://example.com", 37 | want: exampleDotCom, 38 | }, 39 | } 40 | for n, tc := range testCases { 41 | t.Run(n, func(t *testing.T) { 42 | 43 | ctx := cecontext.WithTarget(tc.ctx, tc.target) 44 | 45 | got := cecontext.TargetFrom(ctx) 46 | 47 | if diff := cmp.Diff(tc.want, got); diff != "" { 48 | t.Errorf("unexpected (-want, +got) = %v", diff) 49 | } 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /v2/context/delegating.go: -------------------------------------------------------------------------------- 1 | package context 2 | 3 | import "context" 4 | 5 | type valuesDelegating struct { 6 | context.Context 7 | parent context.Context 8 | } 9 | 10 | // ValuesDelegating wraps a child and parent context. It will perform Value() 11 | // lookups first on the child, and then fall back to the child. All other calls 12 | // go solely to the child context. 13 | func ValuesDelegating(child, parent context.Context) context.Context { 14 | return &valuesDelegating{ 15 | Context: child, 16 | parent: parent, 17 | } 18 | } 19 | 20 | func (c *valuesDelegating) Value(key interface{}) interface{} { 21 | if val := c.Context.Value(key); val != nil { 22 | return val 23 | } 24 | return c.parent.Value(key) 25 | } 26 | -------------------------------------------------------------------------------- /v2/context/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package context holds the last resort overrides and fyi objects that can be passed to clients and transports added to 8 | context.Context objects. 9 | */ 10 | package context 11 | -------------------------------------------------------------------------------- /v2/context/logger.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package context 7 | 8 | import ( 9 | "context" 10 | 11 | "go.uber.org/zap" 12 | ) 13 | 14 | // Opaque key type used to store logger 15 | type loggerKeyType struct{} 16 | 17 | var loggerKey = loggerKeyType{} 18 | 19 | // fallbackLogger is the logger is used when there is no logger attached to the context. 20 | var fallbackLogger *zap.SugaredLogger 21 | 22 | func init() { 23 | if logger, err := zap.NewProduction(); err != nil { 24 | // We failed to create a fallback logger. 25 | fallbackLogger = zap.NewNop().Sugar() 26 | } else { 27 | fallbackLogger = logger.Named("fallback").Sugar() 28 | } 29 | } 30 | 31 | // WithLogger returns a new context with the logger injected into the given context. 32 | func WithLogger(ctx context.Context, logger *zap.SugaredLogger) context.Context { 33 | if logger == nil { 34 | return context.WithValue(ctx, loggerKey, fallbackLogger) 35 | } 36 | return context.WithValue(ctx, loggerKey, logger) 37 | } 38 | 39 | // LoggerFrom returns the logger stored in context. 40 | func LoggerFrom(ctx context.Context) *zap.SugaredLogger { 41 | l := ctx.Value(loggerKey) 42 | if l != nil { 43 | if logger, ok := l.(*zap.SugaredLogger); ok { 44 | return logger 45 | } 46 | } 47 | return fallbackLogger 48 | } 49 | -------------------------------------------------------------------------------- /v2/context/logger_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package context 7 | 8 | import ( 9 | "context" 10 | "testing" 11 | 12 | "github.com/google/go-cmp/cmp" 13 | "github.com/google/go-cmp/cmp/cmpopts" 14 | "go.uber.org/zap" 15 | ) 16 | 17 | func TestLoggerContext(t *testing.T) { 18 | var namedLogger *zap.SugaredLogger 19 | if logger, err := zap.NewProduction(); err != nil { 20 | t.Fatal(err) 21 | } else { 22 | namedLogger = logger.Named("unittest").Sugar() 23 | } 24 | 25 | nopLogger := zap.NewNop().Sugar() 26 | 27 | testCases := map[string]struct { 28 | logger *zap.SugaredLogger 29 | ctx context.Context 30 | want *zap.SugaredLogger 31 | }{ 32 | "todo context, set logger": { 33 | ctx: context.TODO(), 34 | logger: namedLogger, 35 | want: namedLogger, 36 | }, 37 | "already set logger": { 38 | ctx: WithLogger(context.TODO(), nopLogger), 39 | logger: namedLogger, 40 | want: namedLogger, 41 | }, 42 | } 43 | for n, tc := range testCases { 44 | t.Run(n, func(t *testing.T) { 45 | 46 | ctx := WithLogger(tc.ctx, tc.logger) 47 | got := LoggerFrom(ctx) 48 | 49 | if diff := cmp.Diff(tc.want, got, cmpopts.IgnoreUnexported(zap.SugaredLogger{})); diff != "" { 50 | t.Errorf("unexpected (-want, +got) = %v", diff) 51 | } 52 | }) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /v2/event/content_type.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package event 7 | 8 | const ( 9 | TextPlain = "text/plain" 10 | TextJSON = "text/json" 11 | ApplicationJSON = "application/json" 12 | ApplicationXML = "application/xml" 13 | ApplicationCloudEventsJSON = "application/cloudevents+json" 14 | ApplicationCloudEventsBatchJSON = "application/cloudevents-batch+json" 15 | ) 16 | 17 | // StringOfApplicationJSON returns a string pointer to "application/json" 18 | func StringOfApplicationJSON() *string { 19 | a := ApplicationJSON 20 | return &a 21 | } 22 | 23 | // StringOfApplicationXML returns a string pointer to "application/xml" 24 | func StringOfApplicationXML() *string { 25 | a := ApplicationXML 26 | return &a 27 | } 28 | 29 | // StringOfTextPlain returns a string pointer to "text/plain" 30 | func StringOfTextPlain() *string { 31 | a := TextPlain 32 | return &a 33 | } 34 | 35 | // StringOfApplicationCloudEventsJSON returns a string pointer to 36 | // "application/cloudevents+json" 37 | func StringOfApplicationCloudEventsJSON() *string { 38 | a := ApplicationCloudEventsJSON 39 | return &a 40 | } 41 | 42 | // StringOfApplicationCloudEventsBatchJSON returns a string pointer to 43 | // "application/cloudevents-batch+json" 44 | func StringOfApplicationCloudEventsBatchJSON() *string { 45 | a := ApplicationCloudEventsBatchJSON 46 | return &a 47 | } 48 | -------------------------------------------------------------------------------- /v2/event/data_content_encoding.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package event 7 | 8 | const ( 9 | Base64 = "base64" 10 | ) 11 | 12 | // StringOfBase64 returns a string pointer to "Base64" 13 | func StringOfBase64() *string { 14 | a := Base64 15 | return &a 16 | } 17 | -------------------------------------------------------------------------------- /v2/event/data_content_encoding_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package event_test 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/cloudevents/sdk-go/v2/event" 12 | 13 | "github.com/google/go-cmp/cmp" 14 | ) 15 | 16 | func TestStringOfBase64(t *testing.T) { 17 | want := strptr("base64") 18 | got := event.StringOfBase64() 19 | 20 | if diff := cmp.Diff(want, got); diff != "" { 21 | t.Errorf("unexpected string (-want, +got) = %v", diff) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /v2/event/datacodec/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package datacodec holds the data codec registry and adds known encoders and decoders supporting media types such as 8 | `application/json` and `application/xml`. 9 | */ 10 | package datacodec 11 | -------------------------------------------------------------------------------- /v2/event/datacodec/json/data.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package json 7 | 8 | import ( 9 | "context" 10 | "encoding/json" 11 | "fmt" 12 | "reflect" 13 | ) 14 | 15 | // Decode takes `in` as []byte. 16 | // If Event sent the payload as base64, Decoder assumes that `in` is the 17 | // decoded base64 byte array. 18 | func Decode(ctx context.Context, in []byte, out interface{}) error { 19 | if in == nil { 20 | return nil 21 | } 22 | if out == nil { 23 | return fmt.Errorf("out is nil") 24 | } 25 | 26 | if err := json.Unmarshal(in, out); err != nil { 27 | return fmt.Errorf("[json] found bytes \"%s\", but failed to unmarshal: %s", string(in), err.Error()) 28 | } 29 | return nil 30 | } 31 | 32 | // Encode attempts to json.Marshal `in` into bytes. Encode will inspect `in` 33 | // and returns `in` unmodified if it is detected that `in` is already a []byte; 34 | // Or json.Marshal errors. 35 | func Encode(ctx context.Context, in interface{}) ([]byte, error) { 36 | if in == nil { 37 | return nil, nil 38 | } 39 | 40 | it := reflect.TypeOf(in) 41 | switch it.Kind() { 42 | case reflect.Slice: 43 | if it.Elem().Kind() == reflect.Uint8 { 44 | 45 | if b, ok := in.([]byte); ok && len(b) > 0 { 46 | // check to see if it is a pre-encoded byte string. 47 | if b[0] == byte('"') || b[0] == byte('{') || b[0] == byte('[') { 48 | return b, nil 49 | } 50 | } 51 | 52 | } 53 | } 54 | 55 | return json.Marshal(in) 56 | } 57 | -------------------------------------------------------------------------------- /v2/event/datacodec/json/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package json holds the encoder/decoder implementation for `application/json`. 8 | */ 9 | package json 10 | -------------------------------------------------------------------------------- /v2/event/datacodec/text/data.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package text 7 | 8 | import ( 9 | "context" 10 | "fmt" 11 | ) 12 | 13 | // Text codec converts []byte or string to string and vice-versa. 14 | 15 | func Decode(_ context.Context, in []byte, out interface{}) error { 16 | p, _ := out.(*string) 17 | if p == nil { 18 | return fmt.Errorf("text.Decode out: want *string, got %T", out) 19 | } 20 | *p = string(in) 21 | return nil 22 | } 23 | 24 | func Encode(_ context.Context, in interface{}) ([]byte, error) { 25 | s, ok := in.(string) 26 | if !ok { 27 | return nil, fmt.Errorf("text.Encode in: want string, got %T", in) 28 | } 29 | return []byte(s), nil 30 | } 31 | -------------------------------------------------------------------------------- /v2/event/datacodec/text/data_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package text_test 7 | 8 | import ( 9 | "context" 10 | "testing" 11 | 12 | "github.com/cloudevents/sdk-go/v2/event/datacodec/text" 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | var ctx = context.Background() 17 | 18 | func TestEncode(t *testing.T) { 19 | assert := assert.New(t) 20 | 21 | b, err := text.Encode(ctx, "") 22 | assert.NoError(err) 23 | assert.Empty(b) 24 | 25 | b, err = text.Encode(ctx, "hello😀") 26 | assert.NoError(err) 27 | assert.Equal("hello😀", string(b)) 28 | 29 | _, err = text.Encode(ctx, []byte("x")) 30 | assert.EqualError(err, "text.Encode in: want string, got []uint8") 31 | _, err = text.Encode(ctx, nil) 32 | assert.EqualError(err, "text.Encode in: want string, got ") 33 | } 34 | 35 | func TestDecode(t *testing.T) { 36 | assert := assert.New(t) 37 | var s string 38 | assert.NoError(text.Decode(ctx, []byte("bye"), &s)) 39 | assert.Equal("bye", s) 40 | assert.NoError(text.Decode(ctx, []byte{}, &s)) 41 | assert.Equal("", s) 42 | s = "xxx" 43 | assert.NoError(text.Decode(ctx, nil, &s)) 44 | assert.Equal("", s) 45 | } 46 | -------------------------------------------------------------------------------- /v2/event/datacodec/text/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package text holds the encoder/decoder implementation for `text/plain`. 8 | */ 9 | package text 10 | -------------------------------------------------------------------------------- /v2/event/datacodec/xml/data.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package xml 7 | 8 | import ( 9 | "context" 10 | "encoding/xml" 11 | "fmt" 12 | ) 13 | 14 | // Decode takes `in` as []byte. 15 | // If Event sent the payload as base64, Decoder assumes that `in` is the 16 | // decoded base64 byte array. 17 | func Decode(ctx context.Context, in []byte, out interface{}) error { 18 | if in == nil { 19 | return nil 20 | } 21 | 22 | if err := xml.Unmarshal(in, out); err != nil { 23 | return fmt.Errorf("[xml] found bytes, but failed to unmarshal: %s %s", err.Error(), string(in)) 24 | } 25 | return nil 26 | } 27 | 28 | // Encode attempts to xml.Marshal `in` into bytes. Encode will inspect `in` 29 | // and returns `in` unmodified if it is detected that `in` is already a []byte; 30 | // Or xml.Marshal errors. 31 | func Encode(ctx context.Context, in interface{}) ([]byte, error) { 32 | if b, ok := in.([]byte); ok { 33 | // check to see if it is a pre-encoded byte string. 34 | if len(b) > 0 && b[0] == byte('"') { 35 | return b, nil 36 | } 37 | } 38 | 39 | return xml.Marshal(in) 40 | } 41 | -------------------------------------------------------------------------------- /v2/event/datacodec/xml/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package xml holds the encoder/decoder implementation for `application/xml`. 8 | */ 9 | package xml 10 | -------------------------------------------------------------------------------- /v2/event/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package event provides primitives to work with CloudEvents specification: https://github.com/cloudevents/spec. 8 | */ 9 | package event 10 | -------------------------------------------------------------------------------- /v2/event/event_validation.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package event 7 | 8 | import ( 9 | "fmt" 10 | "strings" 11 | ) 12 | 13 | type ValidationError map[string]error 14 | 15 | func (e ValidationError) Error() string { 16 | b := strings.Builder{} 17 | for k, v := range e { 18 | b.WriteString(k) 19 | b.WriteString(": ") 20 | b.WriteString(v.Error()) 21 | b.WriteRune('\n') 22 | } 23 | return b.String() 24 | } 25 | 26 | // Validate performs a spec based validation on this event. 27 | // Validation is dependent on the spec version specified in the event context. 28 | func (e Event) Validate() error { 29 | if e.Context == nil { 30 | return ValidationError{"specversion": fmt.Errorf("missing Event.Context")} 31 | } 32 | 33 | errs := map[string]error{} 34 | if e.FieldErrors != nil { 35 | for k, v := range e.FieldErrors { 36 | errs[k] = v 37 | } 38 | } 39 | 40 | if fieldErrors := e.Context.Validate(); fieldErrors != nil { 41 | for k, v := range fieldErrors { 42 | errs[k] = v 43 | } 44 | } 45 | 46 | if len(errs) > 0 { 47 | return ValidationError(errs) 48 | } 49 | return nil 50 | } 51 | -------------------------------------------------------------------------------- /v2/event/extensions_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package event 7 | 8 | import ( 9 | "errors" 10 | "testing" 11 | ) 12 | 13 | func TestEvent_validateExtensionName(t *testing.T) { 14 | testCases := map[string]struct { 15 | key string 16 | want error 17 | }{ 18 | "empty key": { 19 | key: "", 20 | want: errors.New("bad key, CloudEvents attribute names MUST NOT be empty"), 21 | }, 22 | "invalid character": { 23 | key: "invalid_key", 24 | want: errors.New("bad key, CloudEvents attribute names MUST consist of lower-case letters ('a' to 'z'), upper-case letters ('A' to 'Z') or digits ('0' to '9') from the ASCII character set"), 25 | }, 26 | "valid key": { 27 | key: "validkey123", 28 | want: nil, 29 | }, 30 | } 31 | 32 | for name, tc := range testCases { 33 | t.Run(name, func(t *testing.T) { 34 | err := validateExtensionName(tc.key) 35 | if err != nil && err.Error() != tc.want.Error() || err == nil && tc.want != nil { 36 | t.Errorf("unexpected error, expected: %v, actual: %v", tc.want, err) 37 | } 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /v2/extensions/dataref_extension.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package extensions 7 | 8 | import ( 9 | "github.com/cloudevents/sdk-go/v2/event" 10 | "net/url" 11 | ) 12 | 13 | const DataRefExtensionKey = "dataref" 14 | 15 | // DataRefExtension represents the CloudEvents Dataref (claim check pattern) 16 | // extension for cloudevents contexts, 17 | // See https://github.com/cloudevents/spec/blob/main/cloudevents/extensions/dataref.md 18 | // for more info 19 | type DataRefExtension struct { 20 | DataRef string `json:"dataref"` 21 | } 22 | 23 | // AddDataRefExtension adds the dataref attribute to the cloudevents context 24 | func AddDataRefExtension(e *event.Event, dataRef string) error { 25 | if _, err := url.Parse(dataRef); err != nil { 26 | return err 27 | } 28 | e.SetExtension(DataRefExtensionKey, dataRef) 29 | return nil 30 | } 31 | 32 | // GetDataRefExtension returns any dataref attribute present in the 33 | // cloudevent event/context and a bool to indicate if it was found. 34 | // If not found, the DataRefExtension.DataRef value will be "" 35 | func GetDataRefExtension(e event.Event) (DataRefExtension, bool) { 36 | if dataRefValue, ok := e.Extensions()[DataRefExtensionKey]; ok { 37 | dataRefStr, _ := dataRefValue.(string) 38 | return DataRefExtension{DataRef: dataRefStr}, true 39 | } 40 | return DataRefExtension{}, false 41 | } 42 | -------------------------------------------------------------------------------- /v2/extensions/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | // Package extensions provides implementations of common event extensions. 7 | package extensions 8 | -------------------------------------------------------------------------------- /v2/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cloudevents/sdk-go/v2 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.8 6 | 7 | require ( 8 | github.com/google/go-cmp v0.7.0 9 | github.com/google/uuid v1.6.0 10 | github.com/json-iterator/go v1.1.12 11 | github.com/stretchr/testify v1.10.0 12 | github.com/valyala/bytebufferpool v1.0.0 13 | go.uber.org/zap v1.27.0 14 | golang.org/x/sync v0.14.0 15 | golang.org/x/time v0.11.0 16 | ) 17 | 18 | require ( 19 | github.com/davecgh/go-spew v1.1.1 // indirect 20 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 21 | github.com/modern-go/reflect2 v1.0.2 // indirect 22 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect 23 | github.com/pmezard/go-difflib v1.0.0 // indirect 24 | go.uber.org/multierr v1.11.0 // indirect 25 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect 26 | gopkg.in/yaml.v3 v3.0.1 // indirect 27 | ) 28 | -------------------------------------------------------------------------------- /v2/observability/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package observability holds metrics and tracing common keys. 8 | */ 9 | package observability 10 | -------------------------------------------------------------------------------- /v2/observability/keys.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package observability 7 | 8 | const ( 9 | // ClientSpanName is the key used to start spans from the client. 10 | ClientSpanName = "cloudevents.client" 11 | 12 | // metrics/tracing attributes 13 | SpecversionAttr = "cloudevents.specversion" 14 | IdAttr = "cloudevents.id" 15 | TypeAttr = "cloudevents.type" 16 | SourceAttr = "cloudevents.source" 17 | SubjectAttr = "cloudevents.subject" 18 | DatacontenttypeAttr = "cloudevents.datacontenttype" 19 | ) 20 | -------------------------------------------------------------------------------- /v2/protocol/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package protocol defines interfaces to decouple the client package 8 | from protocol implementations. 9 | 10 | Most event sender and receiver applications should not use this 11 | package, they should use the client package. This package is for 12 | infrastructure developers implementing new transports, or intermediary 13 | components like importers, channels or brokers. 14 | 15 | Available protocols: 16 | 17 | * HTTP (using net/http) 18 | * Kafka (using github.com/IBM/sarama) 19 | * AMQP (using pack.ag/amqp) 20 | * Go Channels 21 | * Nats 22 | * Nats Streaming (stan) 23 | * Google PubSub 24 | */ 25 | package protocol 26 | -------------------------------------------------------------------------------- /v2/protocol/error.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package protocol 7 | 8 | import "fmt" 9 | 10 | // ErrTransportMessageConversion is an error produced when the transport 11 | // message can not be converted. 12 | type ErrTransportMessageConversion struct { 13 | fatal bool 14 | handled bool 15 | transport string 16 | message string 17 | } 18 | 19 | // NewErrTransportMessageConversion makes a new ErrTransportMessageConversion. 20 | func NewErrTransportMessageConversion(transport, message string, handled, fatal bool) *ErrTransportMessageConversion { 21 | return &ErrTransportMessageConversion{ 22 | transport: transport, 23 | message: message, 24 | handled: handled, 25 | fatal: fatal, 26 | } 27 | } 28 | 29 | // IsFatal reports if this error should be considered fatal. 30 | func (e *ErrTransportMessageConversion) IsFatal() bool { 31 | return e.fatal 32 | } 33 | 34 | // Handled reports if this error should be considered accepted and no further action. 35 | func (e *ErrTransportMessageConversion) Handled() bool { 36 | return e.handled 37 | } 38 | 39 | // Error implements error.Error 40 | func (e *ErrTransportMessageConversion) Error() string { 41 | return fmt.Sprintf("transport %s failed to convert message: %s", e.transport, e.message) 42 | } 43 | -------------------------------------------------------------------------------- /v2/protocol/gochan/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package gochan implements the CloudEvent transport implementation using go chan. 8 | */ 9 | package gochan 10 | -------------------------------------------------------------------------------- /v2/protocol/gochan/protocol.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package gochan 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/cloudevents/sdk-go/v2/binding" 12 | "github.com/cloudevents/sdk-go/v2/protocol" 13 | ) 14 | 15 | const ( 16 | defaultChanDepth = 20 17 | ) 18 | 19 | // SendReceiver is a reference implementation for using the CloudEvents binding 20 | // integration. 21 | type SendReceiver struct { 22 | sender protocol.SendCloser 23 | receiver protocol.Receiver 24 | } 25 | 26 | func New() *SendReceiver { 27 | ch := make(chan binding.Message, defaultChanDepth) 28 | 29 | return &SendReceiver{ 30 | sender: Sender(ch), 31 | receiver: Receiver(ch), 32 | } 33 | } 34 | 35 | func (sr *SendReceiver) Send(ctx context.Context, in binding.Message, transformers ...binding.Transformer) (err error) { 36 | return sr.sender.Send(ctx, in, transformers...) 37 | } 38 | 39 | func (sr *SendReceiver) Receive(ctx context.Context) (binding.Message, error) { 40 | return sr.receiver.Receive(ctx) 41 | } 42 | 43 | func (sr *SendReceiver) Close(ctx context.Context) error { 44 | return sr.sender.Close(ctx) 45 | } 46 | -------------------------------------------------------------------------------- /v2/protocol/gochan/receiver.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package gochan 7 | 8 | import ( 9 | "context" 10 | "fmt" 11 | "github.com/cloudevents/sdk-go/v2/protocol" 12 | "io" 13 | 14 | "github.com/cloudevents/sdk-go/v2/binding" 15 | ) 16 | 17 | // Receiver implements Receiver by receiving Messages from a channel. 18 | type Receiver <-chan binding.Message 19 | 20 | func (r Receiver) Receive(ctx context.Context) (binding.Message, error) { 21 | if ctx == nil { 22 | return nil, fmt.Errorf("nil Context") 23 | } 24 | 25 | select { 26 | case <-ctx.Done(): 27 | return nil, io.EOF 28 | case m, ok := <-r: 29 | if !ok { 30 | return nil, io.EOF 31 | } 32 | return m, nil 33 | } 34 | } 35 | 36 | var _ protocol.Receiver = (*Receiver)(nil) 37 | -------------------------------------------------------------------------------- /v2/protocol/gochan/requester.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package gochan 7 | 8 | import ( 9 | "context" 10 | "errors" 11 | "fmt" 12 | 13 | "github.com/cloudevents/sdk-go/v2/binding" 14 | "github.com/cloudevents/sdk-go/v2/protocol" 15 | ) 16 | 17 | type Requester struct { 18 | Ch chan<- binding.Message 19 | Reply func(message binding.Message) (binding.Message, error) 20 | } 21 | 22 | func (s *Requester) Send(ctx context.Context, m binding.Message, transformers ...binding.Transformer) (err error) { 23 | if ctx == nil { 24 | return fmt.Errorf("nil Context") 25 | } else if m == nil { 26 | return fmt.Errorf("nil Message") 27 | } 28 | 29 | defer func() { 30 | err2 := m.Finish(err) 31 | if err == nil { 32 | err = err2 33 | } 34 | }() 35 | select { 36 | case <-ctx.Done(): 37 | return ctx.Err() 38 | case s.Ch <- m: 39 | return nil 40 | } 41 | } 42 | 43 | func (s *Requester) Request(ctx context.Context, m binding.Message, transformers ...binding.Transformer) (res binding.Message, err error) { 44 | defer func() { 45 | err2 := m.Finish(err) 46 | if err == nil { 47 | err = err2 48 | } 49 | }() 50 | select { 51 | case <-ctx.Done(): 52 | return nil, ctx.Err() 53 | case s.Ch <- m: 54 | return s.Reply(m) 55 | } 56 | } 57 | 58 | func (s *Requester) Close(ctx context.Context) (err error) { 59 | defer func() { 60 | if recover() != nil { 61 | err = errors.New("trying to close a closed Sender") 62 | } 63 | }() 64 | close(s.Ch) 65 | return nil 66 | } 67 | 68 | var _ protocol.RequesterCloser = (*Requester)(nil) 69 | -------------------------------------------------------------------------------- /v2/protocol/gochan/responder.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package gochan 7 | 8 | import ( 9 | "context" 10 | "fmt" 11 | "io" 12 | 13 | "github.com/cloudevents/sdk-go/v2/binding" 14 | "github.com/cloudevents/sdk-go/v2/protocol" 15 | ) 16 | 17 | type ChanResponderResponse struct { 18 | Message binding.Message 19 | Result protocol.Result 20 | } 21 | 22 | // Responder implements Responder by receiving Messages from a channel and outputting the result in an output channel. 23 | // All message received in the `Out` channel must be finished 24 | type Responder struct { 25 | In <-chan binding.Message 26 | Out chan<- ChanResponderResponse 27 | } 28 | 29 | func (r *Responder) Respond(ctx context.Context) (binding.Message, protocol.ResponseFn, error) { 30 | if ctx == nil { 31 | return nil, nil, fmt.Errorf("nil Context") 32 | } 33 | 34 | select { 35 | case <-ctx.Done(): 36 | return nil, nil, ctx.Err() 37 | case m, ok := <-r.In: 38 | if !ok { 39 | return nil, nil, io.EOF 40 | } 41 | return m, func(ctx context.Context, message binding.Message, result protocol.Result, transformers ...binding.Transformer) error { 42 | r.Out <- ChanResponderResponse{ 43 | Message: message, 44 | Result: result, 45 | } 46 | return nil 47 | }, nil 48 | } 49 | } 50 | 51 | var _ protocol.Responder = (*Responder)(nil) 52 | -------------------------------------------------------------------------------- /v2/protocol/gochan/sender.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package gochan 7 | 8 | import ( 9 | "context" 10 | "errors" 11 | "fmt" 12 | 13 | "github.com/cloudevents/sdk-go/v2/binding" 14 | "github.com/cloudevents/sdk-go/v2/protocol" 15 | ) 16 | 17 | // Sender implements Sender by sending Messages on a channel. 18 | type Sender chan<- binding.Message 19 | 20 | func (s Sender) Send(ctx context.Context, m binding.Message, transformers ...binding.Transformer) (err error) { 21 | if ctx == nil { 22 | return fmt.Errorf("nil Context") 23 | } else if m == nil { 24 | return fmt.Errorf("nil Message") 25 | } 26 | 27 | defer func() { 28 | err2 := m.Finish(err) 29 | if err == nil { 30 | err = err2 31 | } 32 | }() 33 | select { 34 | case <-ctx.Done(): 35 | return ctx.Err() 36 | case s <- m: 37 | return nil 38 | } 39 | } 40 | 41 | func (s Sender) Close(ctx context.Context) (err error) { 42 | defer func() { 43 | if recover() != nil { 44 | err = errors.New("trying to close a closed Sender") 45 | } 46 | }() 47 | close(s) 48 | return nil 49 | } 50 | 51 | var _ protocol.SendCloser = (Sender)(nil) 52 | -------------------------------------------------------------------------------- /v2/protocol/http/context.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package http 7 | 8 | import ( 9 | "context" 10 | 11 | nethttp "net/http" 12 | "net/url" 13 | ) 14 | 15 | type requestKey struct{} 16 | 17 | // RequestData holds the http.Request information subset that can be 18 | // used to retrieve HTTP information for an incoming CloudEvent. 19 | type RequestData struct { 20 | URL *url.URL 21 | Header nethttp.Header 22 | RemoteAddr string 23 | Host string 24 | } 25 | 26 | // WithRequestDataAtContext uses the http.Request to add RequestData 27 | // information to the Context. 28 | func WithRequestDataAtContext(ctx context.Context, r *nethttp.Request) context.Context { 29 | if r == nil { 30 | return ctx 31 | } 32 | 33 | return context.WithValue(ctx, requestKey{}, &RequestData{ 34 | URL: r.URL, 35 | Header: r.Header, 36 | RemoteAddr: r.RemoteAddr, 37 | Host: r.Host, 38 | }) 39 | } 40 | 41 | // RequestDataFromContext retrieves RequestData from the Context. 42 | // If not set nil is returned. 43 | func RequestDataFromContext(ctx context.Context) *RequestData { 44 | if req := ctx.Value(requestKey{}); req != nil { 45 | return req.(*RequestData) 46 | } 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /v2/protocol/http/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package http implements an HTTP binding using net/http module 8 | */ 9 | package http 10 | -------------------------------------------------------------------------------- /v2/protocol/http/headers.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package http 7 | 8 | import ( 9 | "context" 10 | "github.com/cloudevents/sdk-go/v2/binding" 11 | "net/http" 12 | "net/textproto" 13 | "strings" 14 | "unicode" 15 | 16 | "github.com/cloudevents/sdk-go/v2/binding/spec" 17 | ) 18 | 19 | var attributeHeadersMapping map[string]string 20 | 21 | type customHeaderKey int 22 | 23 | const ( 24 | headerKey customHeaderKey = iota 25 | ) 26 | 27 | func init() { 28 | attributeHeadersMapping = make(map[string]string) 29 | for _, v := range specs.Versions() { 30 | for _, a := range v.Attributes() { 31 | if a.Kind() == spec.DataContentType { 32 | attributeHeadersMapping[a.Name()] = ContentType 33 | } else { 34 | attributeHeadersMapping[a.Name()] = textproto.CanonicalMIMEHeaderKey(prefix + a.Name()) 35 | } 36 | } 37 | } 38 | } 39 | 40 | func extNameToHeaderName(name string) string { 41 | var b strings.Builder 42 | b.Grow(len(name) + len(prefix)) 43 | b.WriteString(prefix) 44 | b.WriteRune(unicode.ToUpper(rune(name[0]))) 45 | b.WriteString(name[1:]) 46 | return b.String() 47 | } 48 | 49 | func HeaderFrom(ctx context.Context) http.Header { 50 | return binding.GetOrDefaultFromCtx(ctx, headerKey, make(http.Header)).(http.Header) 51 | } 52 | 53 | func WithCustomHeader(ctx context.Context, header http.Header) context.Context { 54 | return context.WithValue(ctx, headerKey, header) 55 | } 56 | -------------------------------------------------------------------------------- /v2/protocol/http/headers_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package http 7 | 8 | import ( 9 | "context" 10 | "net/http" 11 | "reflect" 12 | "testing" 13 | ) 14 | 15 | func TestHeaderFrom(t *testing.T) { 16 | type args struct { 17 | ctx context.Context 18 | } 19 | tests := []struct { 20 | name string 21 | args args 22 | want http.Header 23 | }{ 24 | { 25 | name: "empty header", 26 | args: args{ 27 | ctx: context.TODO(), 28 | }, 29 | want: make(http.Header), 30 | }, 31 | { 32 | name: "header with value", 33 | args: args{ 34 | ctx: WithCustomHeader(context.TODO(), map[string][]string{"header": {"value"}}), 35 | }, 36 | want: map[string][]string{"header": {"value"}}, 37 | }, 38 | } 39 | for _, tt := range tests { 40 | t.Run(tt.name, func(t *testing.T) { 41 | if got := HeaderFrom(tt.args.ctx); !reflect.DeepEqual(got, tt.want) { 42 | t.Errorf("HeaderFrom() = %v, want %v", got, tt.want) 43 | } 44 | }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /v2/protocol/http/protocol_rate.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package http 7 | 8 | import ( 9 | "context" 10 | "net/http" 11 | ) 12 | 13 | type RateLimiter interface { 14 | // Allow attempts to take one token from the rate limiter for the specified 15 | // request. It returns ok when this operation was successful. In case ok is 16 | // false, reset will indicate the time in seconds when it is safe to perform 17 | // another attempt. An error is returned when this operation failed, e.g. due to 18 | // a backend error. 19 | Allow(ctx context.Context, r *http.Request) (ok bool, reset uint64, err error) 20 | // Close terminates rate limiter and cleans up any data structures or 21 | // connections that may remain open. After a store is stopped, Take() should 22 | // always return zero values. 23 | Close(ctx context.Context) error 24 | } 25 | 26 | type noOpLimiter struct{} 27 | 28 | func (n noOpLimiter) Allow(ctx context.Context, r *http.Request) (bool, uint64, error) { 29 | return true, 0, nil 30 | } 31 | 32 | func (n noOpLimiter) Close(ctx context.Context) error { 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /v2/protocol/http/result.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package http 7 | 8 | import ( 9 | "errors" 10 | "fmt" 11 | 12 | "github.com/cloudevents/sdk-go/v2/protocol" 13 | ) 14 | 15 | // NewResult returns a fully populated http Result that should be used as 16 | // a transport.Result. 17 | func NewResult(statusCode int, messageFmt string, args ...interface{}) protocol.Result { 18 | return &Result{ 19 | StatusCode: statusCode, 20 | Format: messageFmt, 21 | Args: args, 22 | } 23 | } 24 | 25 | // Result wraps the fields required to make adjustments for http Responses. 26 | type Result struct { 27 | StatusCode int 28 | Format string 29 | Args []interface{} 30 | } 31 | 32 | // make sure Result implements error. 33 | var _ error = (*Result)(nil) 34 | 35 | // Is returns if the target error is a Result type checking target. 36 | func (e *Result) Is(target error) bool { 37 | if o, ok := target.(*Result); ok { 38 | return e.StatusCode == o.StatusCode 39 | } 40 | 41 | // Special case for nil == ACK 42 | if o, ok := target.(*protocol.Receipt); ok { 43 | if e == nil && o.ACK { 44 | return true 45 | } 46 | } 47 | 48 | // Allow for wrapped errors. 49 | if e != nil { 50 | err := fmt.Errorf(e.Format, e.Args...) 51 | return errors.Is(err, target) 52 | } 53 | return false 54 | } 55 | 56 | // Error returns the string that is formed by using the format string with the 57 | // provided args. 58 | func (e *Result) Error() string { 59 | return fmt.Sprintf("%d: %v", e.StatusCode, fmt.Errorf(e.Format, e.Args...)) 60 | } 61 | -------------------------------------------------------------------------------- /v2/protocol/lifecycle.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package protocol 7 | 8 | import ( 9 | "context" 10 | ) 11 | 12 | // Opener is the common interface for things that need to be opened. 13 | type Opener interface { 14 | // OpenInbound is a blocking call and ctx is used to stop the Inbound message Receiver/Responder. 15 | // Closing the context won't close the Receiver/Responder, aka it won't invoke Close(ctx). 16 | OpenInbound(ctx context.Context) error 17 | } 18 | 19 | // Closer is the common interface for things that can be closed. 20 | // After invoking Close(ctx), you cannot reuse the object you closed. 21 | type Closer interface { 22 | Close(ctx context.Context) error 23 | } 24 | -------------------------------------------------------------------------------- /v2/protocol/outbound.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package protocol 7 | 8 | import ( 9 | "context" 10 | 11 | "github.com/cloudevents/sdk-go/v2/binding" 12 | ) 13 | 14 | // Sender sends messages. 15 | type Sender interface { 16 | // Send a message. 17 | // 18 | // Send returns when the "outbound" message has been sent. The Sender may 19 | // still be expecting acknowledgment or holding other state for the message. 20 | // 21 | // m.Finish() is called when sending is finished (both succeeded or failed): 22 | // expected acknowledgments (or errors) have been received, the Sender is 23 | // no longer holding any state for the message. 24 | // m.Finish() may be called during or after Send(). 25 | // 26 | // transformers are applied when the message is written on the wire. 27 | Send(ctx context.Context, m binding.Message, transformers ...binding.Transformer) error 28 | } 29 | 30 | // SendCloser is a Sender that can be closed. 31 | type SendCloser interface { 32 | Sender 33 | Closer 34 | } 35 | 36 | // Requester sends a message and receives a response 37 | // 38 | // Optional interface that may be implemented by protocols that support 39 | // request/response correlation. 40 | type Requester interface { 41 | // Request sends m like Sender.Send() but also arranges to receive a response. 42 | Request(ctx context.Context, m binding.Message, transformers ...binding.Transformer) (binding.Message, error) 43 | } 44 | 45 | // RequesterCloser is a Requester that can be closed. 46 | type RequesterCloser interface { 47 | Requester 48 | Closer 49 | } 50 | -------------------------------------------------------------------------------- /v2/protocol/test/benchmark.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package test 7 | 8 | import ( 9 | "context" 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | "golang.org/x/sync/errgroup" 14 | 15 | "github.com/cloudevents/sdk-go/v2/binding" 16 | "github.com/cloudevents/sdk-go/v2/protocol" 17 | "github.com/cloudevents/sdk-go/v2/test" 18 | ) 19 | 20 | // BenchmarkSendReceive implements a simple send/receive benchmark. 21 | // Requires a sender and receiver that are connected to each other. 22 | func BenchmarkSendReceive(b *testing.B, s protocol.Sender, r protocol.Receiver) { 23 | e := test.FullEvent() 24 | m := (*binding.EventMessage)(&e) 25 | ctx := context.Background() 26 | b.ResetTimer() // Don't count setup. 27 | for i := 0; i < b.N; i++ { 28 | n := 10 // Messages to send async. 29 | g := errgroup.Group{} 30 | g.Go(func() error { 31 | for j := 0; j < n; j++ { 32 | if err := s.Send(ctx, m); err != nil { 33 | return err 34 | } 35 | } 36 | return nil 37 | }) 38 | g.Go(func() error { 39 | for j := 0; j < n; j++ { 40 | m, err := r.Receive(ctx) 41 | if err != nil { 42 | return err 43 | } 44 | if err := m.Finish(nil); err != nil { 45 | return err 46 | } 47 | } 48 | return nil 49 | }) 50 | assert.NoError(b, g.Wait()) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /v2/protocol/test/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package test 7 | 8 | /* 9 | Package test provides utilities to test binding implementations and transformers. 10 | 11 | */ 12 | -------------------------------------------------------------------------------- /v2/staticcheck.conf: -------------------------------------------------------------------------------- 1 | checks = [ 2 | "all", "-ST1003", 3 | ] -------------------------------------------------------------------------------- /v2/test/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | /* 7 | Package test has utilities (asserts, mocks, ...) to use cloudevents in your tests 8 | */ 9 | package test 10 | -------------------------------------------------------------------------------- /v2/test/event_asserts.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package test 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/cloudevents/sdk-go/v2/event" 12 | ) 13 | 14 | // AssertEvent is a "matcher like" assertion method to test the properties of an event 15 | func AssertEvent(t testing.TB, have event.Event, matchers ...EventMatcher) { 16 | err := AllOf(matchers...)(have) 17 | if err != nil { 18 | t.Fatalf("Error while matching event: %s", err.Error()) 19 | } 20 | } 21 | 22 | // AssertEventContextEquals asserts that two event.Event contexts are equals 23 | func AssertEventContextEquals(t testing.TB, want event.EventContext, have event.EventContext) { 24 | if err := IsContextEqualTo(want)(event.Event{Context: have}); err != nil { 25 | t.Fatalf("Error while matching event context: %s", err.Error()) 26 | } 27 | } 28 | 29 | // AssertEventEquals asserts that two event.Event are equals 30 | func AssertEventEquals(t testing.TB, want event.Event, have event.Event) { 31 | if err := IsEqualTo(want)(have); err != nil { 32 | t.Fatalf("Error while matching event: %s", err.Error()) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /v2/types/allocate.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The CloudEvents Authors 3 | SPDX-License-Identifier: Apache-2.0 4 | */ 5 | 6 | package types 7 | 8 | import "reflect" 9 | 10 | // Allocate allocates a new instance of type t and returns: 11 | // asPtr is of type t if t is a pointer type and of type &t otherwise 12 | // asValue is a Value of type t pointing to the same data as asPtr 13 | func Allocate(obj interface{}) (asPtr interface{}, asValue reflect.Value) { 14 | if obj == nil { 15 | return nil, reflect.Value{} 16 | } 17 | 18 | switch t := reflect.TypeOf(obj); t.Kind() { 19 | case reflect.Ptr: 20 | reflectPtr := reflect.New(t.Elem()) 21 | asPtr = reflectPtr.Interface() 22 | asValue = reflectPtr 23 | case reflect.Map: 24 | reflectPtr := reflect.MakeMap(t) 25 | asPtr = reflectPtr.Interface() 26 | asValue = reflectPtr 27 | case reflect.String: 28 | reflectPtr := reflect.New(t) 29 | asPtr = "" 30 | asValue = reflectPtr.Elem() 31 | case reflect.Slice: 32 | reflectPtr := reflect.MakeSlice(t, 0, 0) 33 | asPtr = reflectPtr.Interface() 34 | asValue = reflectPtr 35 | default: 36 | reflectPtr := reflect.New(t) 37 | asPtr = reflectPtr.Interface() 38 | asValue = reflectPtr.Elem() 39 | } 40 | return 41 | } 42 | --------------------------------------------------------------------------------