diff --git a/.github/actions/cleanup-stale/action.yaml b/.github/actions/cleanup-stale/action.yaml index 5df2e9b24..a82536d73 100644 --- a/.github/actions/cleanup-stale/action.yaml +++ b/.github/actions/cleanup-stale/action.yaml @@ -44,7 +44,7 @@ runs: password: ${{ inputs.token }} - name: Delete untagged images with no dependency - uses: Chizkiyahu/delete-untagged-ghcr-action@v3 + uses: Chizkiyahu/delete-untagged-ghcr-action@v4 with: repository: ${{ github.repository }} repository_owner: ${{ github.repository_owner }} diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 844e49223..da7cc5758 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -12,8 +12,8 @@ jobs: matrix: include: # check support for oldest supported golang version - - name: go1.20 - go-version: "~1.20" + - name: go1.22 + go-version: "~1.22" runs-on: ubuntu-latest steps: - name: Checkout @@ -22,7 +22,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: ${{ matrix.go-version || '^1.20' }} + go-version: ${{ matrix.go-version || '^1.22' }} check-latest: true - run: | diff --git a/.github/workflows/checkFormat.yml b/.github/workflows/checkFormat.yml index d81d5976e..a6226037e 100644 --- a/.github/workflows/checkFormat.yml +++ b/.github/workflows/checkFormat.yml @@ -22,7 +22,7 @@ jobs: fetch-depth: 0 - uses: actions/setup-go@v5 with: - go-version: "^1.20" # The Go version to download (if necessary) and use. + go-version: "^1.22" # The Go version to download (if necessary) and use. check-latest: true - name: Check formatting diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 9c80297ec..6a7b33e10 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -24,13 +24,13 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: "^1.20" # The Go version to download (if necessary) and use. + go-version: "^1.22" # The Go version to download (if necessary) and use. check-latest: true cache: false - run: go version - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v5 with: version: latest args: --timeout=5m diff --git a/.github/workflows/measureMemory.yml b/.github/workflows/measureMemory.yml index 201e0604b..f011846ef 100644 --- a/.github/workflows/measureMemory.yml +++ b/.github/workflows/measureMemory.yml @@ -288,7 +288,7 @@ jobs: - name: Resolve database id: db run: | - if [ "${{ github.event_name != 'workflow_dispatch' || inputs.cql }}" == "true" ]; then + if [ "${{ github.event_name == 'workflow_dispatch' && inputs.cql }}" == "true" ]; then echo "name=cqldb" >> $GITHUB_OUTPUT else echo "name=mongodb" >> $GITHUB_OUTPUT diff --git a/.github/workflows/staticAnalysis.yml b/.github/workflows/staticAnalysis.yml index e32c75abf..8acc7d31f 100644 --- a/.github/workflows/staticAnalysis.yml +++ b/.github/workflows/staticAnalysis.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: "^1.20" # The Go version to download (if necessary) and use. + go-version: "^1.22" # The Go version to download (if necessary) and use. check-latest: true - run: go version diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 418962e3c..5c7139d79 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -98,6 +98,14 @@ jobs: with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + # login to ghcr.io so we can download device/bridge-device package + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Run a test run: | make ${{ matrix.cmd }} TEST_CHECK_RACE=${{ matrix.checkRace }} \ diff --git a/.golangci.yml b/.golangci.yml index 6373304e8..9a83c0b0a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,17 +1,8 @@ -run: - skip-dirs: - - dependency - - http-gateway/grpc-websocket-proxy - skip-dirs-use-default: false - linters-settings: - gocyclo: - min-complexity: 15 - govet: - # check-shadowing: true - check-shadowing: false exhaustive: default-signifies-exhaustive: true + gocyclo: + min-complexity: 15 gomodguard: blocked: modules: @@ -21,90 +12,116 @@ linters-settings: gosec: excludes: - G402 + govet: + enable: + - nilness + - shadow stylecheck: - go: "1.20" checks: ["all", "-ST1003"] linters: enable: + - asasalint # Check for pass []any as any in variadic func(...any) - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers - bidichk # Checks for dangerous unicode character sequences - # - bodyclose # checks whether HTTP response body is closed successfully - # - contextcheck # check the function whether use a non-inherited context - - decorder # check declaration order and count of types, constants, variables and functions - # - depguard # Go linter that checks if package imports are in a list of acceptable packages + - bodyclose # Checks whether HTTP response body is closed successfully + # - copyloopvar # Detects places where loop variables are copied + - decorder # Check declaration order and count of types, constants, variables and functions - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) - # - dupl # Tool for code clone detection - - durationcheck # check for two durations multiplied together + - dupl # Tool for code clone detection + - dupword # A linter that checks for duplicate words in the source code (usually miswritten) + - durationcheck # Check for two durations multiplied together - errcheck # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and optionally reports occasions, where the check for the returned error can be omitted. - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. - errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. - # - exhaustive # check exhaustiveness of enum switch statements + - execinquery # Execinquery is a linter about query string checker in Query function which reads your Go src files and warning it finds. - exportloopref # checks for pointers to enclosing loop variables - # - forbidigo # Forbids identifiers # - forcetypeassert # finds forced type assertions - gci # Gci control golang package import order and make it always deterministic. - # - gochecknoglobals # Checks that no globals are present in Go code - # - gochecknoinits # Checks that no init functions are present in Go code - # - gocognit # Computes and checks the cognitive complexity of functions + - gocheckcompilerdirectives # Checks that go compiler directive comments (//go:) are valid. + - gocognit # Computes and checks the cognitive complexity of functions - goconst # Finds repeated strings that could be replaced by a constant - gocritic # The most opinionated Go source code linter - # - gocyclo # Computes and checks the cyclomatic complexity of functions + - gocyclo # Computes and checks the cyclomatic complexity of functions # - godox # Tool for detection of FIXME, TODO and other comment keywords # - goerr113 # Golang linter to check the errors handling expressions - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification - gofumpt # Gofumpt checks whether code was gofumpt-ed. - goheader # Checks is file header matches to pattern - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports - - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. + # - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. - goprintffuncname # Checks that printf-like functions are named with `f` at the end - gosec # Inspects source code for security problems + - gosmopolitan # Report certain i18n/l10n anti-patterns in your Go codebase - gosimple # Linter for Go source code that specializes in simplifying a code - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string - - grouper # An analyzer to analyze expression groups. + - grouper # An analyzer to analyze expression groups - importas # Enforces consistent import aliases - ineffassign # Detects when assignments to existing variables are not used + # - intrange # Intrange is a linter to find places where for loops could make use of an integer range + - loggercheck # Checks key value pairs for common logger libraries (kitlog,klog,logr,zap). + - mirror # Reports wrong mirror patterns of bytes/strings usage - misspell # Finds commonly misspelled English words in comments - nakedret # Finds naked returns in functions greater than a specified function length + - nestif # Reports deeply nested if statements - nilerr # Finds the code that returns nil even if it checks that the error is not nil. # - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. # - noctx # noctx finds sending http request without context.Context - nolintlint # Reports ill-formed or insufficient nolint directives - # - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test + - nosprintfhostport # Checks for misuse of Sprintf to construct a host with port in a URL + - perfsprint # Checks that fmt.Sprintf can be replaced with a faster alternative. + - prealloc # Finds slice declarations that could potentially be preallocated - predeclared # find code that shadows one of Go's predeclared identifiers + - protogetter # Reports direct reads from proto message fields when getters should be used. - revive # golint replacement, finds style mistakes + - reassign # Checks that package variables are not reassigned + - sloglint # Ensure consistent code style when using log/slog + - spancheck # Checks for mistakes with OpenTelemetry/Census spans - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks - stylecheck # Stylecheck is a replacement for golint - # - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 - # - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes + - tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 + - testifylint # Checks usage of github.com/stretchr/testify. + - tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code - unconvert # Remove unnecessary type conversions - unparam # Reports unused function parameters - unused # Checks Go code for unused constants, variables, functions and types - # - wastedassign # wastedassign finds wasted assignment statements + - usestdlibvars # A linter that detect the possibility to use variables/constants from the Go standard library. + - wastedassign # wastedassign finds wasted assignment statements - whitespace # Tool for detection of leading and trailing whitespace disable: - containedctx # containedctx is a linter that detects struct contained context.Context field + - contextcheck # check the function whether use a non-inherited context - cyclop # checks function and package cyclomatic complexity + - depguard # Go linter that checks if package imports are in a list of acceptable packages + - exhaustive # Check exhaustiveness of enum switch statements - exhaustivestruct # Checks if all struct's fields are initialized + - exhaustruct # Checks if all structure fields are initialized. + - forbidigo # Forbids identifiers - funlen # Tool for detection of long functions + - gochecknoglobals # Checks that no globals are present in Go code + - gochecknoinits # Checks that no init functions are present in Go code - godot # Check if comments end in a period - gomnd # An analyzer to detect magic numbers. - ifshort # Checks that your code uses short syntax for if-statements whenever possible + - inamedparam # Reports interfaces with unnamed method parameters. + - interfacebloat # A linter that checks the number of methods inside an interface - ireturn # Accept Interfaces, Return Concrete Types - lll # Reports long lines - maintidx # maintidx measures the maintainability index of each function. - makezero # Finds slice declarations with non-zero initial length - maligned # Tool to detect Go structs that would take less memory if their fields were sorted - - nestif # Reports deeply nested if statements - nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity - - prealloc # Finds slice declarations that could potentially be preallocated + - nonamedreturns # Reports all named returns + - paralleltest # paralleltest detects missing usage of t.Parallel() method in your Go test - promlinter # Check Prometheus metrics naming via promlint - rowserrcheck # checks whether Err of rows is checked successfully - sqlclosecheck # Checks that sql.Rows and sql.Stmt are closed. + - tagalign # Check that struct tags are well aligned. - tagliatelle # Checks the struct tags. + - testableexamples # linter checks if examples are testable (have an expected output) - testpackage # linter that makes you use a separate _test package - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers - varnamelen # checks that the length of a variable's name matches its scope @@ -112,6 +129,12 @@ linters: - wsl # Whitespace Linter - Forces you to use empty lines! issues: + exclude-dirs: + - dependency + - http-gateway/grpc-websocket-proxy + exclude-files: + - ".*\\.pb\\.go$" + - ".*\\.pb\\.gw\\.go$" exclude-rules: - path: _test.go linters: @@ -123,10 +146,6 @@ issues: - gocognit - gocyclo - gosec - - path: tools/nats-server-config-reloader/pkg/natsreloader/natsreloader.go - text: "G404:" - linters: - - gosec - path: pkg/time/delay.go text: "G404:" linters: @@ -138,5 +157,20 @@ issues: text: "SA1019:" linters: - staticcheck + - path: grpc-gateway/client/client.go + linters: + - dupword + - path: coap-gateway/service/session.go + linters: + - dupl + - path: grpc-gateway/client/subscription.go + linters: + - dupl + - path: resource-aggregate/events/resourceLinks.*.go|resource-aggregate/client/sync.*.go|resource-aggregate/service/grpcApi.go|resource-aggregate/events/resource.*.go + linters: + - dupl # Fix found issues (if it's supported by the linter). # fix: true + +run: + go: "1.22" diff --git a/.vscode/settings.json b/.vscode/settings.json index 1eb846981..cfa605fd3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -27,14 +27,17 @@ "TEST_MEMORY_COAP_GATEWAY_EXPECTED_RSS_IN_MB": "1000", "TEST_MEMORY_COAP_GATEWAY_RESOURCE_DATA_SIZE": "200", "TEST_COAP_GATEWAY_LOG_LEVEL": "info", - "TEST_COAP_GATEWAY_LOG_DUMP_BODY": "false", + "TEST_COAP_GATEWAY_LOG_DUMP_BODY": "false", "TEST_RESOURCE_AGGREGATE_LOG_LEVEL": "info", - "TEST_RESOURCE_AGGREGATE_LOG_DUMP_BODY": "false", + "TEST_RESOURCE_AGGREGATE_LOG_DUMP_BODY": "false", "TEST_GRPC_GATEWAY_LOG_LEVEL": "info", - "TEST_GRPC_GATEWAY_LOG_DUMP_BODY": "false", + "TEST_GRPC_GATEWAY_LOG_DUMP_BODY": "false", "TEST_IDENTITY_STORE_LOG_LEVEL": "info", - "TEST_IDENTITY_STORE_LOG_DUMP_BODY": "false", + "TEST_IDENTITY_STORE_LOG_DUMP_BODY": "false", "TEST_DATABASE": "mongoDB", + "TEST_BRIDGE_DEVICE_CONFIG": "${workspaceFolder}/.tmp/bridge/config-test.yaml", + // "TEST_DEVICE_NAME": "bridged-device-0", + // "TEST_DEVICE_TYPE": "bridged", // "GODEBUG": "scavtrace=1", // "TEST_COAP_GATEWAY_UDP_ENABLED": "true", // "GOMAXPROCS": 1, diff --git a/Dockerfile.test b/Dockerfile.test index 9b4d1596d..620c3a696 100644 --- a/Dockerfile.test +++ b/Dockerfile.test @@ -1,11 +1,12 @@ FROM ubuntu:22.04 AS hub-test RUN apt-get update \ && DEBIAN_FRONTEND="noninteractive" apt-get install -y --no-install-recommends build-essential ca-certificates curl git make patch sudo \ + && apt-get clean \ && curl -sSL https://get.docker.com/ | sh # apt: ca-certificates git make sudo RUN git clone https://github.com/udhos/update-golang.git \ && cd update-golang \ - && sudo RELEASE=1.20.13 ./update-golang.sh \ + && sudo RELEASE=1.22.3 ./update-golang.sh \ && ln -s /usr/local/go/bin/go /usr/bin/go WORKDIR $GOPATH/src/github.com/plgd-dev/hub COPY go.mod go.sum ./ @@ -15,9 +16,12 @@ COPY . . WORKDIR $GOPATH/src/github.com/plgd-dev/hub/tools/cert-tool RUN go build -o /usr/bin/cert-tool -WORKDIR $GOPATH/src/github.com/plgd-dev/hub +WORKDIR /usr/local/go # apt: patch -RUN ( cd /usr/local/go && patch -p1 < $GOPATH/src/github.com/plgd-dev/hub/tools/docker/patches/shrink_tls_conn.patch ) +RUN ( patch -p1 < "$GOPATH/src/github.com/plgd-dev/hub/tools/docker/patches/shrink_tls_conn.patch" ) + + +WORKDIR $GOPATH/src/github.com/plgd-dev/hub # RUN go mod tidy diff --git a/Makefile b/Makefile index ca4e2d7e0..3e5cbf8fe 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ TEST_DATABASE ?= mongodb CERT_TOOL_SIGN_ALG ?= ECDSA-SHA256 # supported values: P256, P384, P521 CERT_TOOL_ELLIPTIC_CURVE ?= P256 -CERT_TOOL_IMAGE=ghcr.io/plgd-dev/hub/cert-tool:vnext +CERT_TOOL_IMAGE = ghcr.io/plgd-dev/hub/cert-tool:vnext SUBDIRS := bundle certificate-authority cloud2cloud-connector cloud2cloud-gateway coap-gateway grpc-gateway resource-aggregate resource-directory http-gateway identity-store test/oauth-server tools/cert-tool snapshot-service .PHONY: $(SUBDIRS) push proto/generate clean build test env mongo nats certificates hub-build http-gateway-www simulators @@ -225,6 +225,64 @@ simulators: simulators/clean $(call RUN-DOCKER-DEVICE,$(DEVICE_SIMULATOR_RES_OBSERVABLE_NAME),$(DEVICE_SIMULATOR_RES_OBSERVABLE_IMG)) .PHONY: simulators +BRIDGE_DEVICE_SRC_DIR = $(WORKING_DIRECTORY)/test/bridge-device +BRIDGE_DEVICE_IMAGE = ghcr.io/plgd-dev/device/bridge-device:vnext +BRIDGE_DEVICE_NAME = bridgedev +BRIDGE_DEVICE_ID ?= 8f596b43-29c0-4147-8b40-e99268ab30f7 +BRIDGE_DEVICE_RESOURCES_PER_DEVICE ?= 3 +BRIDGE_DEVICES_COUNT ?= 3 + +define SET-BRIDGE-DEVICE-CONFIG + yq -i '.apis.coap.id = "$(BRIDGE_DEVICE_ID)"' $(1) + yq -i '.apis.coap.externalAddresses=["127.0.0.1:15683","[::1]:15683"]' $(1) + yq -i '.cloud.enabled=true' $(1) + yq -i '.cloud.cloudID="$(CLOUD_SID)"' $(1) + yq -i '.cloud.tls.caPoolPath="$(2)/certs/root_ca.crt"' $(1) + yq -i '.cloud.tls.keyPath="$(2)/certs/coap.key"' $(1) + yq -i '.cloud.tls.certPath="$(2)/certs/coap.crt"' $(1) + yq -i '.numGeneratedBridgedDevices=$(BRIDGE_DEVICES_COUNT)' $(1) + yq -i '.numResourcesPerDevice=$(BRIDGE_DEVICE_RESOURCES_PER_DEVICE)' $(1) + yq -i '.thingDescription.enabled=true' $(1) + yq -i '.thingDescription.file="$(2)/bridge/bridge-device.jsonld"' $(1) +endef + +# config-docker.yaml -> copy of configuration with paths valid inside docker container +# config-test.yaml -> copy of configuration with paths valid on host machine +simulators/bridge/env: simulators/bridge/clean certificates + mkdir -p $(WORKING_DIRECTORY)/.tmp/bridge + cp $(BRIDGE_DEVICE_SRC_DIR)/bridge-device.jsonld $(WORKING_DIRECTORY)/.tmp/bridge/ + cp $(BRIDGE_DEVICE_SRC_DIR)/config.yaml $(WORKING_DIRECTORY)/.tmp/bridge/config-docker.yaml + $(call SET-BRIDGE-DEVICE-CONFIG,$(WORKING_DIRECTORY)/.tmp/bridge/config-docker.yaml,) + cp $(BRIDGE_DEVICE_SRC_DIR)/config.yaml $(WORKING_DIRECTORY)/.tmp/bridge/config-test.yaml + $(call SET-BRIDGE-DEVICE-CONFIG,$(WORKING_DIRECTORY)/.tmp/bridge/config-test.yaml,$(WORKING_DIRECTORY)/.tmp) + +.PHONY: simulators/bridge/env + +define RUN-BRIDGE-DOCKER-DEVICE + docker pull $(BRIDGE_DEVICE_IMAGE) ; \ + docker run \ + -d \ + --name=$(BRIDGE_DEVICE_NAME) \ + --network=host \ + -v $(WORKING_DIRECTORY)/.tmp/certs:/certs \ + -v $(WORKING_DIRECTORY)/.tmp/bridge:/bridge \ + $(BRIDGE_DEVICE_IMAGE) -config /bridge/config-docker.yaml +endef + +simulators/bridge: simulators/bridge/env + $(call RUN-BRIDGE-DOCKER-DEVICE) + +.PHONY: simulators/bridge + +simulators/bridge/clean: + rm -rf $(WORKING_DIRECTORY)/.tmp/bridge || : + $(call REMOVE-DOCKER-DEVICE,$(BRIDGE_DEVICE_NAME)) + +.PHONY: simulators/bridge/clean + +simulators: simulators/bridge +simulators/clean: simulators/bridge/clean + env/test/mem: clean certificates nats mongo privateKeys scylla .PHONY: env/test/mem @@ -235,6 +293,7 @@ define RUN-DOCKER docker run \ --rm \ --network=host \ + -v $(WORKING_DIRECTORY)/.tmp/bridge:/bridge \ -v $(WORKING_DIRECTORY)/.tmp/certs:/certs \ -v $(WORKING_DIRECTORY)/.tmp/coverage:/coverage \ -v $(WORKING_DIRECTORY)/.tmp/report:/report \ @@ -253,6 +312,7 @@ define RUN-DOCKER -e TEST_OAUTH_SERVER_ID_TOKEN_PRIVATE_KEY=/privKeys/idTokenKey.pem \ -e TEST_OAUTH_SERVER_ACCESS_TOKEN_PRIVATE_KEY=/privKeys/accessTokenKey.pem \ -e TEST_HTTP_GW_WWW_ROOT=/usr/local/www \ + -e TEST_BRIDGE_DEVICE_CONFIG=/bridge/config-docker.yaml \ hub-test \ $(1) ; endef diff --git a/bundle/Dockerfile b/bundle/Dockerfile index 2721fd6e6..71ee55fb4 100644 --- a/bundle/Dockerfile +++ b/bundle/Dockerfile @@ -1,11 +1,14 @@ # syntax=docker/dockerfile:1 -FROM golang:1.20.13-alpine AS build +FROM golang:1.22.3-alpine AS build RUN apk add --no-cache curl git build-base WORKDIR $GOPATH/src/github.com/plgd-dev/hub COPY go.mod go.sum ./ RUN go mod download COPY . . -RUN ( cd /usr/local/go && patch -p1 < $GOPATH/src/github.com/plgd-dev/hub/tools/docker/patches/shrink_tls_conn.patch ) +WORKDIR /usr/local/go +RUN ( patch -p1 < "$GOPATH/src/github.com/plgd-dev/hub/tools/docker/patches/shrink_tls_conn.patch" ) +WORKDIR $GOPATH/src/github.com/plgd-dev/hub + ARG root_directory=$GOPATH/src/github.com/plgd-dev/hub ARG COMMIT_DATE @@ -17,57 +20,90 @@ ARG RELEASE_URL #coap-gateway ARG service=coap-gateway WORKDIR $root_directory/$service -RUN go build -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" -o /go/bin/$service ./cmd/service +RUN go build \ + -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" \ + -o "/go/bin/$service" \ + ./cmd/service #grpc-gateway ARG service=grpc-gateway WORKDIR $root_directory/$service -RUN go build -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" -o /go/bin/$service ./cmd/service +RUN go build \ + -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" \ + -o "/go/bin/$service" \ + ./cmd/service #http-gateway ARG service=http-gateway WORKDIR $root_directory/$service -RUN go build -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" -o /go/bin/$service ./cmd/service +RUN go build \ + -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" \ + -o "/go/bin/$service" \ + ./cmd/service #resource-directory ARG service=resource-directory WORKDIR $root_directory/$service -RUN go build -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" -o /go/bin/$service ./cmd/service +RUN go build \ + -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" \ + -o "/go/bin/$service" \ + ./cmd/service #resource-aggregate ARG service=resource-aggregate WORKDIR $root_directory/$service -RUN go build -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" -o /go/bin/$service ./cmd/service +RUN go build \ + -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" \ + -o "/go/bin/$service" \ + ./cmd/service #identity-store ARG service=identity-store WORKDIR $root_directory/$service -RUN go build -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" -o /go/bin/$service ./cmd/service +RUN go build \ + -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" \ + -o "/go/bin/$service" \ + ./cmd/service #certificate-authority ARG service=certificate-authority WORKDIR $root_directory/$service -RUN go build -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" -o /go/bin/$service ./cmd/service +RUN go build \ + -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" \ + -o "/go/bin/$service" \ + ./cmd/service #oauth-server ARG service=oauth-server WORKDIR $root_directory/test/$service -RUN go build -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" -o /go/bin/$service ./cmd/service +RUN go build \ + -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" \ + -o "/go/bin/$service" \ + ./cmd/service #cloud2cloud-gateway ARG service=cloud2cloud-gateway WORKDIR $root_directory/$service -RUN go build -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" -o /go/bin/$service ./cmd/service +RUN go build \ + -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" \ + -o "/go/bin/$service" \ + ./cmd/service #cloud2cloud-connector ARG service=cloud2cloud-connector WORKDIR $root_directory/$service -RUN go build -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" -o /go/bin/$service ./cmd/service +RUN go build \ + -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" \ + -o "/go/bin/$service" \ + ./cmd/service #cert-tool ARG tool=cert-tool WORKDIR $root_directory/tools/$tool -RUN go build -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" -o /go/bin/$tool ./ +RUN go build \ + -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" \ + -o "/go/bin/$tool" \ + ./ #snapshot-service ARG service=snapshot-service @@ -95,18 +131,20 @@ RUN unzip ./nats.zip -d ./nats RUN cp ./nats/*/nats /go/bin/nats FROM ubuntu:22.04 AS service -RUN apt update # iproute2 -> ip utility in run.sh # netcat -> nc utility in run.sh # nginx -> nginx server in run.sh # openssl -> openssl utility in run.sh -RUN apt-get install -y --no-install-recommends ca-certificates gnupg iproute2 netcat nginx openssl wget curl sudo coreutils +RUN apt update \ + && apt-get install -y --no-install-recommends ca-certificates gnupg iproute2 netcat nginx openssl wget curl sudo coreutils \ + && apt-get clean # yq utility in run.sh RUN wget https://github.com/mikefarah/yq/releases/download/v4.6.3/yq_linux_$(dpkg --print-architecture) -O /usr/bin/yq && chmod +x /usr/bin/yq RUN wget -qO - https://pgp.mongodb.com/server-6.0.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/mongodb-6.0.gpg RUN echo "deb [ arch=$(dpkg --print-architecture) ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-6.0.list -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends mongodb-org mongodb-org-server +RUN apt update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends mongodb-org mongodb-org-server \ + && apt-get clean COPY --from=build /go/bin/coap-gateway /usr/local/bin/coap-gateway COPY --from=build /go/src/github.com/plgd-dev/hub/coap-gateway/config.yaml /configs/coap-gateway.yaml diff --git a/bundle/client/grpc/main.go b/bundle/client/grpc/main.go index ae9372d13..11659f814 100644 --- a/bundle/client/grpc/main.go +++ b/bundle/client/grpc/main.go @@ -63,7 +63,7 @@ func getServiceToken(authAddr string, tls *tls.Config) (string, error) { } token := body["access_token"] if token == "" { - return "", fmt.Errorf("token not found in body") + return "", errors.New("token not found in body") } return token, nil } @@ -235,7 +235,7 @@ func main() { *accesstoken = getAccessToken(*authAddr, &tlsCfg) } - conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(credentials.NewTLS(&tlsCfg))) + conn, err := grpc.NewClient(*addr, grpc.WithTransportCredentials(credentials.NewTLS(&tlsCfg))) if err != nil { log.Fatalf("Error dialing: %v", err) } diff --git a/bundle/client/ob/main.go b/bundle/client/ob/main.go index 024d91d45..9f88fdd43 100644 --- a/bundle/client/ob/main.go +++ b/bundle/client/ob/main.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "crypto/tls" + "errors" "flag" "fmt" "log" @@ -66,7 +67,7 @@ func getServiceToken(authAddr string) (string, error) { } token := body["access_token"] if token == "" { - return "", fmt.Errorf("token not found in body") + return "", errors.New("token not found in body") } return token, nil } @@ -177,7 +178,7 @@ func main() { tlsCfg := tls.Config{ InsecureSkipVerify: true, } - grpcConn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(credentials.NewTLS(&tlsCfg))) + grpcConn, err := grpc.NewClient(*addr, grpc.WithTransportCredentials(credentials.NewTLS(&tlsCfg))) if err != nil { log.Fatalf("cannot connect to grpc: %v", err) } diff --git a/certificate-authority/pb/signingRecords.go b/certificate-authority/pb/signingRecords.go index 6fa3654d2..b6eccf5cf 100644 --- a/certificate-authority/pb/signingRecords.go +++ b/certificate-authority/pb/signingRecords.go @@ -1,6 +1,7 @@ package pb import ( + "errors" "fmt" "sort" @@ -26,7 +27,7 @@ func (signingRecord *SigningRecord) Unmarshal(b []byte) error { func (signingRecord *SigningRecord) Validate() error { if signingRecord.GetId() == "" { - return fmt.Errorf("empty signing record ID") + return errors.New("empty signing record ID") } if _, err := uuid.Parse(signingRecord.GetId()); err != nil { return fmt.Errorf("invalid signing record ID(%v): %w", signingRecord.GetId(), err) @@ -37,19 +38,19 @@ func (signingRecord *SigningRecord) Validate() error { } } if signingRecord.GetCommonName() == "" { - return fmt.Errorf("empty signing record commonName") + return errors.New("empty signing record commonName") } if signingRecord.GetOwner() == "" { - return fmt.Errorf("empty signing record owner") + return errors.New("empty signing record owner") } if signingRecord.GetCredential() != nil && signingRecord.GetCredential().GetDate() == 0 { - return fmt.Errorf("empty signing credential date") + return errors.New("empty signing credential date") } if signingRecord.GetCredential() != nil && signingRecord.GetCredential().GetValidUntilDate() == 0 { - return fmt.Errorf("empty signing record credential expiration date") + return errors.New("empty signing record credential expiration date") } if signingRecord.GetCredential() != nil && signingRecord.GetCredential().GetCertificatePem() == "" { - return fmt.Errorf("empty signing record credential certificate") + return errors.New("empty signing record credential certificate") } return nil } diff --git a/certificate-authority/service/cleanDatabase_test.go b/certificate-authority/service/cleanDatabase_test.go index 529c983cd..29ba16dbb 100644 --- a/certificate-authority/service/cleanDatabase_test.go +++ b/certificate-authority/service/cleanDatabase_test.go @@ -3,6 +3,7 @@ package service_test import ( "context" "errors" + "fmt" "io" "testing" "time" @@ -26,6 +27,7 @@ func TestCertificateAuthorityServerCleanUpSigningRecords(t *testing.T) { cfg := test.MakeConfig(t) cfg.Clients.Storage.ExtendCronParserBySeconds = true cfg.Clients.Storage.CleanUpRecords = "*/1 * * * * *" + fmt.Printf("%v\n\n", test.MakeConfig(t)) shutDown := testService.SetUpServices(context.Background(), t, testService.SetUpServicesCertificateAuthority|testService.SetUpServicesOAuth, testService.WithCAConfig(cfg)) defer shutDown() @@ -76,23 +78,25 @@ func TestCertificateAuthorityServerCleanUpSigningRecords(t *testing.T) { require.NoError(t, err) var got pb.SigningRecords for { - r, err := client.Recv() - if errors.Is(err, io.EOF) { + r, errR := client.Recv() + if errors.Is(errR, io.EOF) { break } + require.NoError(t, errR) got = append(got, r) } - require.Equal(t, 1, len(got)) + require.Len(t, got, 1) time.Sleep(4 * time.Second) client, err = grpcClient.GetSigningRecords(ctx, &pb.GetSigningRecordsRequest{}) require.NoError(t, err) got = nil for { - r, err := client.Recv() - if errors.Is(err, io.EOF) { + r, errR := client.Recv() + if errors.Is(errR, io.EOF) { break } + require.NoError(t, errR) got = append(got, r) } - require.Equal(t, 0, len(got)) + require.Empty(t, got) } diff --git a/certificate-authority/service/config.go b/certificate-authority/service/config.go index a9583fe47..7a0852e2c 100644 --- a/certificate-authority/service/config.go +++ b/certificate-authority/service/config.go @@ -5,7 +5,7 @@ import ( "net" "time" - gocron "github.com/go-co-op/gocron" + "github.com/go-co-op/gocron/v2" "github.com/google/uuid" grpcService "github.com/plgd-dev/hub/v2/certificate-authority/service/grpc" storeConfig "github.com/plgd-dev/hub/v2/certificate-authority/store/config" @@ -89,20 +89,22 @@ func (c *StorageConfig) Validate() error { if c.CleanUpRecords == "" { return nil } - s := gocron.NewScheduler(time.Local) - if c.ExtendCronParserBySeconds { - s = s.CronWithSeconds(c.CleanUpRecords) - } else { - s = s.Cron(c.CleanUpRecords) + s, err := gocron.NewScheduler(gocron.WithLocation(time.Local)) //nolint:gosmopolitan + if err != nil { + return fmt.Errorf("cannot create cron job: %w", err) } - _, err := s.Do(func() { - // do nothing - }) + defer func() { + if errS := s.Shutdown(); errS != nil { + log.Errorf("failed to shutdown cron job: %w", errS) + } + }() + _, err = s.NewJob(gocron.CronJob(c.CleanUpRecords, c.ExtendCronParserBySeconds), + gocron.NewTask(func() { + // do nothing + })) if err != nil { return fmt.Errorf("cleanUpRecords('%v') - %w", c.CleanUpRecords, err) } - s.Clear() - s.Stop() return nil } diff --git a/certificate-authority/service/config_test.go b/certificate-authority/service/config_test.go new file mode 100644 index 000000000..58427612d --- /dev/null +++ b/certificate-authority/service/config_test.go @@ -0,0 +1,203 @@ +// ************************************************************************ +// Copyright (C) 2022 plgd.dev, s.r.o. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ************************************************************************ + +package service_test + +import ( + "testing" + + "github.com/plgd-dev/hub/v2/certificate-authority/service" + "github.com/plgd-dev/hub/v2/certificate-authority/test" + "github.com/stretchr/testify/require" +) + +func TestConfigValidate(t *testing.T) { + type args struct { + cfg service.Config + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "valid", + args: args{ + cfg: func() service.Config { + c := test.MakeConfig(t) + c.Clients.Storage.ExtendCronParserBySeconds = true + c.Clients.Storage.CleanUpRecords = "*/1 * * * * *" + return c + }(), + }, + }, + { + name: "invalid log config", + args: args{ + cfg: func() service.Config { + c := test.MakeConfig(t) + c.Log.Level = 42 + return c + }(), + }, + wantErr: true, + }, + { + name: "invalid grpc api config", + args: args{ + cfg: func() service.Config { + c := test.MakeConfig(t) + c.APIs.GRPC.Addr = "invalid" + return c + }(), + }, + wantErr: true, + }, + { + name: "invalid http api config", + args: args{ + cfg: func() service.Config { + c := test.MakeConfig(t) + c.APIs.HTTP.Addr = "invalid" + return c + }(), + }, + wantErr: true, + }, + { + name: "invalid signer config", + args: args{ + cfg: func() service.Config { + c := test.MakeConfig(t) + c.Signer.CAPool = 42 + return c + }(), + }, + wantErr: true, + }, + { + name: "invalid clients storage config", + args: args{ + cfg: func() service.Config { + c := test.MakeConfig(t) + c.Clients.Storage.CleanUpRecords = "invalid" + return c + }(), + }, + wantErr: true, + }, + { + name: "invalid clients telemetry config", + args: args{ + cfg: func() service.Config { + c := test.MakeConfig(t) + c.Clients.OpenTelemetryCollector.GRPC.Enabled = true + c.Clients.OpenTelemetryCollector.GRPC.Connection.Addr = "" + return c + }(), + }, + wantErr: true, + }, + { + name: "invalid hubID", + args: args{ + cfg: func() service.Config { + c := test.MakeConfig(t) + c.HubID = "invalid" + return c + }(), + }, + wantErr: true, + }, + { + name: "invalid signer", + args: args{ + cfg: func() service.Config { + c := test.MakeConfig(t) + c.Signer.CertFile = "invalid" + return c + }(), + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.args.cfg.Validate() + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + }) + } +} + +func TestStorageConfigValidate(t *testing.T) { + type args struct { + cfg service.StorageConfig + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "valid - disabled", + args: args{ + cfg: func() service.StorageConfig { + c := test.MakeStorageConfig() + c.CleanUpRecords = "" + return c + }(), + }, + }, + { + name: "invalid", + args: args{ + cfg: func() service.StorageConfig { + c := test.MakeStorageConfig() + c.CleanUpRecords = "invalid" + return c + }(), + }, + wantErr: true, + }, + { + name: "invalid db", + args: args{ + cfg: func() service.StorageConfig { + c := test.MakeStorageConfig() + c.Embedded.Use = "invalid" + return c + }(), + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.args.cfg.Validate() + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + }) + } +} diff --git a/certificate-authority/service/grpc/deleteSigningRecords_test.go b/certificate-authority/service/grpc/deleteSigningRecords_test.go index e1a6e9c88..573fdff84 100644 --- a/certificate-authority/service/grpc/deleteSigningRecords_test.go +++ b/certificate-authority/service/grpc/deleteSigningRecords_test.go @@ -55,7 +55,7 @@ func TestCertificateAuthorityServerDeleteSigningRecords(t *testing.T) { name: "valid", args: args{ req: &pb.DeleteSigningRecordsRequest{ - IdFilter: []string{r.Id}, + IdFilter: []string{r.GetId()}, }, }, want: 1, diff --git a/certificate-authority/service/grpc/getSigningRecords_test.go b/certificate-authority/service/grpc/getSigningRecords_test.go index 21a744c10..c7f1d859a 100644 --- a/certificate-authority/service/grpc/getSigningRecords_test.go +++ b/certificate-authority/service/grpc/getSigningRecords_test.go @@ -63,7 +63,7 @@ func TestCertificateAuthorityServerGetSigningRecords(t *testing.T) { name: "valid", args: args{ req: &pb.GetSigningRecordsRequest{ - IdFilter: []string{r.Id}, + IdFilter: []string{r.GetId()}, }, }, want: []*pb.SigningRecord{r}, diff --git a/certificate-authority/service/grpc/infoData.go b/certificate-authority/service/grpc/infoData.go index e3cd974d0..b8dc2b5d2 100644 --- a/certificate-authority/service/grpc/infoData.go +++ b/certificate-authority/service/grpc/infoData.go @@ -3,6 +3,7 @@ package grpc import ( "crypto/x509" "encoding/pem" + "errors" "fmt" "regexp" ) @@ -25,7 +26,7 @@ func (d infoData) String() string { func getInfoData(csr []byte) (infoData, error) { csrBlock, _ := pem.Decode(csr) if csrBlock == nil { - return infoData{}, fmt.Errorf("pem not found") + return infoData{}, errors.New("pem not found") } certificateRequest, err := x509.ParseCertificateRequest(csrBlock.Bytes) diff --git a/certificate-authority/service/grpc/signCertificate.go b/certificate-authority/service/grpc/signCertificate.go index 350187284..45252f07c 100644 --- a/certificate-authority/service/grpc/signCertificate.go +++ b/certificate-authority/service/grpc/signCertificate.go @@ -3,6 +3,7 @@ package grpc import ( "context" "crypto/x509" + "errors" "fmt" "regexp" "time" @@ -104,7 +105,7 @@ func (s *CertificateAuthorityServer) SignCertificate(ctx context.Context, req *p } signer := s.GetSigner() if signer == nil { - return nil, logger.LogAndReturnError(status.Errorf(codes.InvalidArgument, fmtError, fmt.Errorf("signer is empty"))) + return nil, logger.LogAndReturnError(status.Errorf(codes.InvalidArgument, fmtError, errors.New("signer is empty"))) } cert, signingRecord, err := signer.Sign(ctx, req.GetCertificateSigningRequest()) if err != nil { diff --git a/certificate-authority/service/grpc/signCertificate_test.go b/certificate-authority/service/grpc/signCertificate_test.go index 0f0d2a05a..38189e797 100644 --- a/certificate-authority/service/grpc/signCertificate_test.go +++ b/certificate-authority/service/grpc/signCertificate_test.go @@ -66,7 +66,7 @@ func testSigningByFunction(t *testing.T, signFn ClientSignFunc, csr ...[]byte) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.CERTIFICATE_AUTHORITY_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.CERTIFICATE_AUTHORITY_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -113,7 +113,7 @@ func TestCertificateAuthorityServerSignCSRWithDifferentPublicKeys(t *testing.T) defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.CERTIFICATE_AUTHORITY_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.CERTIFICATE_AUTHORITY_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/certificate-authority/service/grpc/signIdentityCertificate.go b/certificate-authority/service/grpc/signIdentityCertificate.go index ccde56cd8..be765018d 100644 --- a/certificate-authority/service/grpc/signIdentityCertificate.go +++ b/certificate-authority/service/grpc/signIdentityCertificate.go @@ -3,6 +3,7 @@ package grpc import ( "context" "crypto/x509/pkix" + "errors" "fmt" "github.com/plgd-dev/hub/v2/certificate-authority/pb" @@ -45,7 +46,7 @@ func (s *CertificateAuthorityServer) SignIdentityCertificate(ctx context.Context } signer := s.GetSigner() if signer == nil { - return nil, logger.LogAndReturnError(status.Errorf(codes.InvalidArgument, fmtError, fmt.Errorf("signer is empty"))) + return nil, logger.LogAndReturnError(status.Errorf(codes.InvalidArgument, fmtError, errors.New("signer is empty"))) } cert, signingRecord, err := signer.SignIdentityCSR(ctx, req.GetCertificateSigningRequest()) if err != nil { diff --git a/certificate-authority/service/grpc/signIdentityCertificate_test.go b/certificate-authority/service/grpc/signIdentityCertificate_test.go index 9c81133f8..a4a1b4260 100644 --- a/certificate-authority/service/grpc/signIdentityCertificate_test.go +++ b/certificate-authority/service/grpc/signIdentityCertificate_test.go @@ -51,7 +51,7 @@ func TestCertificateAuthorityServerSignDeviceIdentityCSRWithDifferentPublicKeys( defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.CERTIFICATE_AUTHORITY_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.CERTIFICATE_AUTHORITY_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -79,7 +79,7 @@ func TestCertificateAuthorityServerSignOwnerIdentityCSRWithDifferentPublicKeys(t defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.CERTIFICATE_AUTHORITY_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.CERTIFICATE_AUTHORITY_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/certificate-authority/service/grpc/signer.go b/certificate-authority/service/grpc/signer.go index d4f014afc..0b4866e0a 100644 --- a/certificate-authority/service/grpc/signer.go +++ b/certificate-authority/service/grpc/signer.go @@ -6,7 +6,6 @@ import ( "crypto/ecdsa" "crypto/x509" "errors" - "fmt" "time" "github.com/karrick/tparse/v2" @@ -26,7 +25,7 @@ type Signer struct { func checkCertificatePrivateKey(cert []*x509.Certificate, priv *ecdsa.PrivateKey) error { if len(cert) == 0 { - return fmt.Errorf("at least one certificate need to be set") + return errors.New("at least one certificate need to be set") } x509Cert := cert[0] switch pub := x509Cert.PublicKey.(type) { @@ -57,7 +56,7 @@ func NewSigner(ownerClaim string, hubID string, signerConfig SignerConfig) (*Sig if err != nil { return nil, err } - if err := checkCertificatePrivateKey(certificate, privateKey); err != nil { + if err = checkCertificatePrivateKey(certificate, privateKey); err != nil { return nil, err } if len(certificate) == 1 && pkgX509.IsRootCA(certificate[0]) { @@ -76,13 +75,13 @@ func NewSigner(ownerClaim string, hubID string, signerConfig SignerConfig) (*Sig certificateAuthorities := make([]*x509.Certificate, 0, len(signerConfig.caPoolArray)*4) for _, caFile := range signerConfig.caPoolArray { - data, err := caFile.Read() - if err != nil { - return nil, err + data, errR := caFile.Read() + if errR != nil { + return nil, errR } - certs, err := pkgX509.ParseX509(data) - if err != nil { - return nil, err + certs, errR := pkgX509.ParseX509(data) + if errR != nil { + return nil, errR } certificateAuthorities = append(certificateAuthorities, certs...) } @@ -108,21 +107,26 @@ func NewSigner(ownerClaim string, hubID string, signerConfig SignerConfig) (*Sig }, nil } +func (s *Signer) prepareSigningRecord(ctx context.Context, template *x509.Certificate) (*pb.SigningRecord, error) { + subject, err := overrideSubject(ctx, template.Subject, s.ownerClaim, s.hubID, "") + if err != nil { + return nil, err + } + template.Subject = subject + owner, err := ownerToUUID(ctx, s.ownerClaim) + if err != nil { + return nil, err + } + return toSigningRecord(owner, template) +} + func (s *Signer) Sign(ctx context.Context, csr []byte) ([]byte, *pb.SigningRecord, error) { notBefore := s.validFrom() notAfter := notBefore.Add(s.validFor) var signingRecord *pb.SigningRecord signer := certificateSigner.New(s.certificate, s.privateKey, certificateSigner.WithNotBefore(notBefore), certificateSigner.WithNotAfter(notAfter), certificateSigner.WithOverrideCertTemplate(func(template *x509.Certificate) error { - subject, err := overrideSubject(ctx, template.Subject, s.ownerClaim, s.hubID, "") - if err != nil { - return err - } - template.Subject = subject - owner, err := ownerToUUID(ctx, s.ownerClaim) - if err != nil { - return err - } - signingRecord, err = toSigningRecord(owner, template) + var err error + signingRecord, err = s.prepareSigningRecord(ctx, template) return err })) crt, err := signer.Sign(ctx, csr) @@ -134,16 +138,8 @@ func (s *Signer) SignIdentityCSR(ctx context.Context, csr []byte) ([]byte, *pb.S notAfter := notBefore.Add(s.validFor) var signingRecord *pb.SigningRecord signer := certificateSigner.NewIdentityCertificateSigner(s.certificate, s.privateKey, certificateSigner.WithNotBefore(notBefore), certificateSigner.WithNotAfter(notAfter), certificateSigner.WithOverrideCertTemplate(func(template *x509.Certificate) error { - subject, err := overrideSubject(ctx, template.Subject, s.ownerClaim, s.hubID, "uuid:") - if err != nil { - return err - } - template.Subject = subject - owner, err := ownerToUUID(ctx, s.ownerClaim) - if err != nil { - return err - } - signingRecord, err = toSigningRecord(owner, template) + var err error + signingRecord, err = s.prepareSigningRecord(ctx, template) return err })) cert, err := signer.Sign(ctx, csr) diff --git a/certificate-authority/service/service.go b/certificate-authority/service/service.go index 5312aa732..c4c240c5c 100644 --- a/certificate-authority/service/service.go +++ b/certificate-authority/service/service.go @@ -6,7 +6,7 @@ import ( "fmt" "time" - gocron "github.com/go-co-op/gocron" + "github.com/go-co-op/gocron/v2" grpcService "github.com/plgd-dev/hub/v2/certificate-authority/service/grpc" httpService "github.com/plgd-dev/hub/v2/certificate-authority/service/http" "github.com/plgd-dev/hub/v2/certificate-authority/store" @@ -52,33 +52,35 @@ func newStore(ctx context.Context, config StorageConfig, fileWatcher *fsnotify.W return nil, nil, err } fl.AddFunc(func() { - if err := db.Close(ctx); err != nil { - log.Errorf("failed to close mongodb store: %w", err) + if errC := db.Close(ctx); errC != nil { + log.Errorf("failed to close mongodb store: %w", errC) } }) if config.CleanUpRecords == "" { return db, fl.ToFunction(), nil } - s := gocron.NewScheduler(time.Local) - if config.ExtendCronParserBySeconds { - s = s.CronWithSeconds(config.CleanUpRecords) - } else { - s = s.Cron(config.CleanUpRecords) + s, err := gocron.NewScheduler(gocron.WithLocation(time.Local)) //nolint:gosmopolitan + if err != nil { + fl.Execute() + return nil, nil, fmt.Errorf("cannot create cron job: %w", err) } - _, err = s.Do(func() { + + _, err = s.NewJob(gocron.CronJob(config.CleanUpRecords, config.ExtendCronParserBySeconds), gocron.NewTask(func() { _, errDel := db.DeleteNonDeviceExpiredRecords(ctx, time.Now()) if errDel != nil && !errors.Is(errDel, store.ErrNotSupported) { log.Errorf("failed to delete expired signing records: %w", errDel) } - }) + })) if err != nil { fl.Execute() return nil, nil, fmt.Errorf("cannot create cron job: %w", err) } - fl.AddFunc(s.Clear) - fl.AddFunc(s.Stop) - s.StartAsync() - + fl.AddFunc(func() { + if errS := s.Shutdown(); errS != nil { + log.Errorf("failed to shutdown cron job: %w", errS) + } + }) + s.Start() return db, fl.ToFunction(), nil } diff --git a/certificate-authority/service/service_test.go b/certificate-authority/service/service_test.go deleted file mode 100644 index 47f7b6d81..000000000 --- a/certificate-authority/service/service_test.go +++ /dev/null @@ -1,38 +0,0 @@ -// ************************************************************************ -// Copyright (C) 2022 plgd.dev, s.r.o. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ************************************************************************ - -package service_test - -import ( - "context" - "fmt" - "testing" - - "github.com/plgd-dev/hub/v2/certificate-authority/test" - testService "github.com/plgd-dev/hub/v2/test/service" - "github.com/stretchr/testify/require" -) - -func TestServiceServe(t *testing.T) { - fmt.Printf("%v\n\n", test.MakeConfig(t)) - - shutDown := testService.SetUpServices(context.Background(), t, testService.SetUpServicesCertificateAuthority|testService.SetUpServicesOAuth) - defer shutDown() - - cfg := test.MakeConfig(t) - cfg.Clients.Storage.CleanUpRecords = "/2 * * * *" - require.Error(t, cfg.Validate()) -} diff --git a/certificate-authority/store/cqldb/signingRecords.go b/certificate-authority/store/cqldb/signingRecords.go index 5ae8e3629..73924579c 100644 --- a/certificate-authority/store/cqldb/signingRecords.go +++ b/certificate-authority/store/cqldb/signingRecords.go @@ -81,7 +81,7 @@ func (s *Store) CreateSigningRecord(ctx context.Context, signingRecord *store.Si return err } if !applied { - return fmt.Errorf("cannot insert signing record: already exists") + return errors.New("cannot insert signing record: already exists") } return nil } @@ -251,7 +251,7 @@ func (s *Store) deviceIDFilterToPrimaryKeys(ctx context.Context, owner string, d func (s *Store) ownerFilterToPrimaryKeys(ctx context.Context, owner string) (primaryKeysValues, error) { if owner == "" { - return nil, fmt.Errorf("invalid owner") + return nil, errors.New("invalid owner") } var b strings.Builder @@ -393,7 +393,7 @@ func (i *SigningRecordsIterator) close() error { func (i *SigningRecordsIterator) Next(_ context.Context, s *store.SigningRecord) bool { for i.next(s) { - if _, ok := i.provided[s.Id]; !ok { + if _, ok := i.provided[s.GetId()]; !ok { // filter duplicated records i.provided[s.GetId()] = struct{}{} return true diff --git a/certificate-authority/store/cqldb/signingRecords_test.go b/certificate-authority/store/cqldb/signingRecords_test.go index 918252230..f2421c41d 100644 --- a/certificate-authority/store/cqldb/signingRecords_test.go +++ b/certificate-authority/store/cqldb/signingRecords_test.go @@ -175,8 +175,8 @@ func TestStoreUpdateSigningRecord(t *testing.T) { } require.NoError(t, err) var h testSigningRecordHandler - err = s.LoadSigningRecords(ctx, tt.args.sub.Owner, &pb.GetSigningRecordsRequest{ - IdFilter: []string{tt.args.sub.Id}, + err = s.LoadSigningRecords(ctx, tt.args.sub.GetOwner(), &pb.GetSigningRecordsRequest{ + IdFilter: []string{tt.args.sub.GetId()}, }, h.Handle) require.NoError(t, err) require.Len(t, h.lcs, 1) @@ -355,7 +355,7 @@ func TestStoreDeleteExpiredRecords(t *testing.T) { var h1 testSigningRecordHandler err = s.LoadSigningRecords(ctx, "owner", nil, h1.Handle) require.NoError(t, err) - require.Len(t, h1.lcs, 0) + require.Empty(t, h1.lcs) } type testSigningRecordHandler struct { @@ -467,7 +467,7 @@ func TestStoreLoadSigningRecords(t *testing.T) { name: "id - another owner", args: args{ owner: "another owner", - query: &store.SigningRecordsQuery{IdFilter: []string{lcs[1].Id}}, + query: &store.SigningRecordsQuery{IdFilter: []string{lcs[1].GetId()}}, }, want: []*store.SigningRecord{lcs[1]}, }, @@ -475,7 +475,7 @@ func TestStoreLoadSigningRecords(t *testing.T) { name: "multiple queries", args: args{ owner: "owner", - query: &store.SigningRecordsQuery{IdFilter: []string{lcs[0].Id, lcs[2].Id}}, + query: &store.SigningRecordsQuery{IdFilter: []string{lcs[0].GetId(), lcs[2].GetId()}}, }, want: []*store.SigningRecord{lcs[0], lcs[2]}, }, @@ -498,7 +498,7 @@ func TestStoreLoadSigningRecords(t *testing.T) { args: args{ owner: "owner", query: &store.SigningRecordsQuery{ - IdFilter: []string{lcs[0].Id, lcs[2].Id}, + IdFilter: []string{lcs[0].GetId(), lcs[2].GetId()}, DeviceIdFilter: []string{lcs[0].GetDeviceId()}, }, }, @@ -520,10 +520,10 @@ func TestStoreLoadSigningRecords(t *testing.T) { var h testSigningRecordHandler err := s.LoadSigningRecords(ctx, "owner", tt.args.query, h.Handle) if tt.wantErr { - assert.Error(t, err) + require.Error(t, err) return } - assert.NoError(t, err) + require.NoError(t, err) require.Len(t, h.lcs, len(tt.want)) h.lcs.Sort() tt.want.Sort() @@ -570,7 +570,7 @@ func BenchmarkSigningRecords(b *testing.B) { go func(l *pb.SigningRecord) { defer wg.Done() err := s.UpdateSigningRecord(ctx, l) - require.NoError(b, err) + assert.NoError(b, err) }(l) } wg.Wait() diff --git a/certificate-authority/store/mongodb/signingRecords.go b/certificate-authority/store/mongodb/signingRecords.go index 124339e91..1217f9ca7 100644 --- a/certificate-authority/store/mongodb/signingRecords.go +++ b/certificate-authority/store/mongodb/signingRecords.go @@ -79,7 +79,12 @@ func toSigningRecordsQueryFilter(owner string, queries *store.SigningRecordsQuer } switch len(or) { case 0: - return bson.D{} + if owner == "" { + return bson.D{} + } + return bson.D{ + {Key: store.OwnerKey, Value: owner}, + } case 1: return or[0] } @@ -91,7 +96,7 @@ func (s *Store) DeleteSigningRecords(ctx context.Context, owner string, query *s IdFilter: query.GetIdFilter(), DeviceIdFilter: query.GetDeviceIdFilter(), } - res, err := s.Collection(signingRecordsCol).DeleteOne(ctx, toSigningRecordsQueryFilter(owner, &q)) + res, err := s.Collection(signingRecordsCol).DeleteMany(ctx, toSigningRecordsQueryFilter(owner, &q)) if err != nil { return -1, multierror.Append(ErrCannotRemoveSigningRecord, err) } diff --git a/certificate-authority/store/mongodb/signingRecords_test.go b/certificate-authority/store/mongodb/signingRecords_test.go index ff8e81549..312f1a29b 100644 --- a/certificate-authority/store/mongodb/signingRecords_test.go +++ b/certificate-authority/store/mongodb/signingRecords_test.go @@ -93,9 +93,9 @@ func TestStoreUpdateSigningRecord(t *testing.T) { t.Run(tt.name, func(t *testing.T) { err := s.UpdateSigningRecord(ctx, tt.args.sub) if tt.wantErr { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } err = s.FlushBulkWriter() require.NoError(t, err) @@ -103,11 +103,28 @@ func TestStoreUpdateSigningRecord(t *testing.T) { } } -func TestStoreDeleteSigningRecord(t *testing.T) { - const id1 = "9d017fad-2961-4fcc-94a9-1e1291a88ffc" - deviceID1 := hubTest.GenerateDeviceIDbyIdx(1) - const id2 = "9d017fad-2961-4fcc-94a9-1e1291a88ffd" - deviceID2 := hubTest.GenerateDeviceIDbyIdx(2) +func TestStoreDeleteSigningRecords(t *testing.T) { + records := []struct { + id string + deviceID string + }{ + { + id: "9d017fad-2961-4fcc-94a9-1e1291a88ffc", + deviceID: hubTest.GenerateDeviceIDbyIdx(1), + }, + { + id: "9d017fad-2961-4fcc-94a9-1e1291a88ffd", + deviceID: hubTest.GenerateDeviceIDbyIdx(2), + }, + { + id: "9d017fad-2961-4fcc-94a9-1e1291a88ffe", + deviceID: hubTest.GenerateDeviceIDbyIdx(3), + }, + { + id: "9d017fad-2961-4fcc-94a9-1e1291a88fff", + deviceID: hubTest.GenerateDeviceIDbyIdx(4), + }, + } const owner = "owner" type args struct { owner string @@ -134,7 +151,7 @@ func TestStoreDeleteSigningRecord(t *testing.T) { args: args{ owner: "owner1", query: &store.DeleteSigningRecordsQuery{ - IdFilter: []string{id1}, + IdFilter: []string{records[0].id}, }, }, want: 0, @@ -144,7 +161,7 @@ func TestStoreDeleteSigningRecord(t *testing.T) { args: args{ owner: owner, query: &store.DeleteSigningRecordsQuery{ - DeviceIdFilter: []string{deviceID1}, + DeviceIdFilter: []string{records[0].deviceID}, }, }, want: 1, @@ -154,11 +171,21 @@ func TestStoreDeleteSigningRecord(t *testing.T) { args: args{ owner: owner, query: &store.DeleteSigningRecordsQuery{ - IdFilter: []string{id2}, + IdFilter: []string{records[1].id}, }, }, want: 1, }, + { + name: "multiple ids", + args: args{ + owner: owner, + query: &store.DeleteSigningRecordsQuery{ + IdFilter: []string{records[2].id, records[3].id}, + }, + }, + want: 2, + }, { name: "valid - empty", args: args{ @@ -173,43 +200,31 @@ func TestStoreDeleteSigningRecord(t *testing.T) { defer cleanUpStore() ctx := context.Background() - err := s.CreateSigningRecord(ctx, &store.SigningRecord{ - Id: id1, - Owner: owner, - CommonName: "commonName", - PublicKey: "publicKey", - DeviceId: deviceID1, - CreationDate: constDate().UnixNano(), - Credential: &pb.CredentialStatus{ - CertificatePem: "certificate", - Date: constDate().UnixNano(), - ValidUntilDate: constDate().UnixNano(), - }, - }) - require.NoError(t, err) - err = s.CreateSigningRecord(ctx, &store.SigningRecord{ - Id: id2, - Owner: owner, - CommonName: "commonName", - PublicKey: "publicKey", - DeviceId: deviceID2, - CreationDate: constDate().UnixNano(), - Credential: &pb.CredentialStatus{ - CertificatePem: "certificate", - Date: constDate().UnixNano(), - ValidUntilDate: constDate().UnixNano(), - }, - }) - require.NoError(t, err) + for _, r := range records { + err := s.CreateSigningRecord(ctx, &store.SigningRecord{ + Id: r.id, + Owner: owner, + CommonName: "commonName", + PublicKey: "publicKey", + DeviceId: r.deviceID, + CreationDate: constDate().UnixNano(), + Credential: &pb.CredentialStatus{ + CertificatePem: "certificate", + Date: constDate().UnixNano(), + ValidUntilDate: constDate().UnixNano(), + }, + }) + require.NoError(t, err) + } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { n, err := s.DeleteSigningRecords(ctx, tt.args.owner, tt.args.query) if tt.wantErr { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) - assert.Equal(t, tt.want, n) + require.NoError(t, err) + require.Equal(t, tt.want, n) } }) } @@ -268,8 +283,8 @@ func TestStoreDeleteExpiredRecords(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := s.DeleteNonDeviceExpiredRecords(ctx, tt.args.now) - assert.NoError(t, err) - assert.Equal(t, tt.want, got) + require.NoError(t, err) + require.Equal(t, tt.want, got) }) } } @@ -293,10 +308,13 @@ func TestStoreLoadSigningRecords(t *testing.T) { const id = "9d017fad-2961-4fcc-94a9-1e1291a88ffc" const id1 = "9d017fad-2961-4fcc-94a9-1e1291a88ffd" const id2 = "9d017fad-2961-4fcc-94a9-1e1291a88ffe" + const owner = "owner" + const differentOwner = "owner2" + const differentOwnerRecordId = "9d017fad-2961-4fcc-94a9-1e1291a88fff" upds := pb.SigningRecords{ { Id: id, - Owner: "owner", + Owner: owner, CommonName: "commonName", PublicKey: "publicKey", DeviceId: hubTest.GenerateDeviceIDbyIdx(0), @@ -309,7 +327,7 @@ func TestStoreLoadSigningRecords(t *testing.T) { }, { Id: id1, - Owner: "owner", + Owner: owner, CommonName: "commonName1", CreationDate: constDate().UnixNano(), PublicKey: "publicKey", @@ -322,7 +340,7 @@ func TestStoreLoadSigningRecords(t *testing.T) { }, { Id: id2, - Owner: "owner", + Owner: owner, CommonName: "commonName2", CreationDate: constDate().UnixNano(), PublicKey: "publicKey", @@ -333,6 +351,19 @@ func TestStoreLoadSigningRecords(t *testing.T) { ValidUntilDate: constDate().UnixNano(), }, }, + { + Id: differentOwnerRecordId, + Owner: differentOwner, + CommonName: "commonName2", + CreationDate: constDate().UnixNano(), + PublicKey: "publicKey", + DeviceId: hubTest.GenerateDeviceIDbyIdx(3), + Credential: &pb.CredentialStatus{ + CertificatePem: "certificate", + Date: constDate().UnixNano(), + ValidUntilDate: constDate().UnixNano(), + }, + }, } lcs := upds @@ -356,7 +387,7 @@ func TestStoreLoadSigningRecords(t *testing.T) { { name: "id", args: args{ - owner: "owner", + owner: owner, query: &store.SigningRecordsQuery{IdFilter: []string{lcs[1].GetId()}}, }, want: []*store.SigningRecord{lcs[1]}, @@ -364,7 +395,7 @@ func TestStoreLoadSigningRecords(t *testing.T) { { name: "commonName", args: args{ - owner: "owner", + owner: owner, query: &store.SigningRecordsQuery{CommonNameFilter: []string{lcs[1].GetCommonName()}}, }, want: []*store.SigningRecord{lcs[1]}, @@ -372,26 +403,40 @@ func TestStoreLoadSigningRecords(t *testing.T) { { name: "DeviceID", args: args{ - owner: "owner", + owner: owner, query: &store.SigningRecordsQuery{DeviceIdFilter: []string{lcs[1].GetDeviceId()}}, }, want: []*store.SigningRecord{lcs[1]}, }, { - name: "id - another owner", + name: "multiple queries", args: args{ - owner: "another owner", - query: &store.SigningRecordsQuery{IdFilter: []string{lcs[1].Id}}, + owner: owner, + query: &store.SigningRecordsQuery{IdFilter: []string{lcs[0].GetId(), lcs[2].GetId()}}, }, - want: []*store.SigningRecord{lcs[1]}, + want: []*store.SigningRecord{lcs[0], lcs[2]}, }, { - name: "multiple queries", + name: "different owner", args: args{ - owner: "owner", - query: &store.SigningRecordsQuery{IdFilter: []string{lcs[0].Id, lcs[2].Id}}, + owner: differentOwner, + }, + want: []*store.SigningRecord{lcs[3]}, + }, + { + name: "different owner - id", + args: args{ + owner: differentOwner, + query: &store.SigningRecordsQuery{IdFilter: []string{differentOwnerRecordId}}, + }, + want: []*store.SigningRecord{lcs[3]}, + }, + { + name: "different owner but id belongs to owner", + args: args{ + owner: differentOwner, + query: &store.SigningRecordsQuery{IdFilter: []string{lcs[1].GetId()}}, }, - want: []*store.SigningRecord{lcs[0], lcs[2]}, }, { name: "all records", @@ -403,7 +448,7 @@ func TestStoreLoadSigningRecords(t *testing.T) { { name: "not found", args: args{ - owner: "owner", + owner: owner, query: &store.SigningRecordsQuery{IdFilter: []string{"not found"}}, }, }, @@ -421,7 +466,7 @@ func TestStoreLoadSigningRecords(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var h testSigningRecordHandler - err := s.LoadSigningRecords(ctx, "owner", tt.args.query, h.Handle) + err := s.LoadSigningRecords(ctx, tt.args.owner, tt.args.query, h.Handle) require.NoError(t, err) require.Len(t, h.lcs, len(tt.want)) h.lcs.Sort() @@ -468,12 +513,12 @@ func BenchmarkSigningRecords(b *testing.B) { go func(l *pb.SigningRecord) { defer wg.Done() err := s.UpdateSigningRecord(ctx, l) - require.NoError(b, err) + assert.NoError(b, err) }(l) } wg.Wait() err := s.FlushBulkWriter() - require.NoError(b, err) + assert.NoError(b, err) }() } } diff --git a/certificate-authority/store/mongodb/store.go b/certificate-authority/store/mongodb/store.go index f1c1bb5b5..90b0115cf 100644 --- a/certificate-authority/store/mongodb/store.go +++ b/certificate-authority/store/mongodb/store.go @@ -49,9 +49,7 @@ func New(ctx context.Context, cfg *Config, fileWatcher *fsnotify.Watcher, logger certManager.Close() return nil, err } - s.SetOnClear(func(c context.Context) error { - return s.clearDatabases(ctx) - }) + s.SetOnClear(s.clearDatabases) s.AddCloseFunc(certManager.Close) return &s, nil } diff --git a/charts/plgd-hub/Chart.lock b/charts/plgd-hub/Chart.lock index 63f6c64c9..aac7c15ab 100644 --- a/charts/plgd-hub/Chart.lock +++ b/charts/plgd-hub/Chart.lock @@ -1,12 +1,12 @@ dependencies: - name: nats repository: https://nats-io.github.io/k8s/helm/charts/ - version: 0.19.14 + version: 1.1.9 - name: mongodb repository: https://charts.bitnami.com/bitnami - version: 13.4.3 + version: 13.18.3 - name: scylla repository: https://scylla-operator-charts.storage.googleapis.com/stable version: v1.10.0 -digest: sha256:19d6a1d0ed96e796922eeeb3d1e4920f1aea3774af332a5f8704b190a5e428fe -generated: "2023-11-03T15:24:42.261277287Z" +digest: sha256:3ed14035e169597cbd91e8521414c8bbae13879d27bda6726ce45b1e74572050 +generated: "2024-03-05T10:43:45.524423289Z" diff --git a/charts/plgd-hub/Chart.yaml b/charts/plgd-hub/Chart.yaml index 9dab65931..91c970029 100644 --- a/charts/plgd-hub/Chart.yaml +++ b/charts/plgd-hub/Chart.yaml @@ -11,11 +11,11 @@ appVersion: vnext dependencies: - name: "nats" - version: "0.19.14" + version: "1.1.9" repository: "https://nats-io.github.io/k8s/helm/charts/" condition: nats.enabled - name: "mongodb" - version: "13.4.3" + version: "13.18.3" repository: "https://charts.bitnami.com/bitnami" condition: mongodb.enabled - name: "scylla" diff --git a/charts/plgd-hub/README.md b/charts/plgd-hub/README.md index 9f2c56415..6ffe125fd 100644 --- a/charts/plgd-hub/README.md +++ b/charts/plgd-hub/README.md @@ -45,8 +45,8 @@ global: | Repository | Name | Version | |------------|------|---------| -| https://charts.bitnami.com/bitnami | mongodb | 13.4.3 | -| https://nats-io.github.io/k8s/helm/charts/ | nats | 0.19.14 | +| https://charts.bitnami.com/bitnami | mongodb | 13.18.3 | +| https://nats-io.github.io/k8s/helm/charts/ | nats | 1.1.8 | | https://scylla-operator-charts.storage.googleapis.com/stable | scylla | 1.10.0 | ## Values @@ -70,11 +70,15 @@ global: | certificateauthority.clients.storage.cqlDB.keyspace.replication.class | string | `"SimpleStrategy"` | | | certificateauthority.clients.storage.cqlDB.keyspace.replication.replication_factor | int | `1` | | | certificateauthority.clients.storage.cqlDB.numConnections | int | `16` | | +| certificateauthority.clients.storage.cqlDB.port | int | `9142` | | +| certificateauthority.clients.storage.cqlDB.reconnectionPolicy.constant.interval | string | `"3s"` | | +| certificateauthority.clients.storage.cqlDB.reconnectionPolicy.constant.maxRetries | int | `3` | | | certificateauthority.clients.storage.cqlDB.table | string | `"signedCertificateRecords"` | | | certificateauthority.clients.storage.cqlDB.tls.caPool | string | `nil` | | | certificateauthority.clients.storage.cqlDB.tls.certFile | string | `nil` | | | certificateauthority.clients.storage.cqlDB.tls.keyFile | string | `nil` | | | certificateauthority.clients.storage.cqlDB.tls.useSystemCAPool | bool | `false` | | +| certificateauthority.clients.storage.cqlDB.useHostnameResolution | bool | `true` | Resolve IP address to hostname before validate certificate. If false, the TLS validator will use ip/hostname advertised by the Cassandra node. | | certificateauthority.clients.storage.mongoDB.bulkWrite.documentLimit | int | `1000` | The maximum number of documents to cache before an immediate write. | | certificateauthority.clients.storage.mongoDB.bulkWrite.throttleTime | string | `"500ms"` | The amount of time to wait until a record is written to mongodb. Any records collected during the throttle time will also be written. A throttle time of zero writes immediately. If recordLimit is reached, all records are written immediately | | certificateauthority.clients.storage.mongoDB.bulkWrite.timeout | string | `"1m0s"` | A time limit for write bulk to mongodb. A Timeout of zero means no timeout. | @@ -295,7 +299,7 @@ global: | extraAuthorizationCAPool.mountPath | string | `"/certs/extra"` | Mount path for custom auth ca pool | | extraAuthorizationCAPool.name | string | `"authorization-ca-pool"` | Name of secret for storing custom auth ca pool | | extraDeploy | string | `nil` | Extra deploy. Resolved as template | -| global | object | `{"audience":"","authority":null,"authorizationCAPool":null,"defaultCommandTimeToLive":"10s","deviceIdClaim":null,"domain":null,"enableWildCartCert":true,"hubId":null,"oauth":{"device":[],"web":{"clientID":null,"scopes":["openid"]}},"openTelemetryExporter":{"address":null,"enabled":false,"keepAlive":{"permitWithoutStream":true,"time":"10s","timeout":"20s"},"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false}},"ownerClaim":"sub"}` | Global config variables | +| global | object | `{"audience":"","authority":null,"authorizationCAPool":null,"defaultCommandTimeToLive":"10s","deviceIdClaim":null,"domain":null,"enableWildCartCert":true,"hubId":null,"oauth":{"device":[],"web":{"clientID":null,"scopes":["openid"]}},"openTelemetryExporter":{"address":null,"enabled":false,"keepAlive":{"permitWithoutStream":true,"time":"10s","timeout":"20s"},"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false}},"ownerClaim":"sub","useDatabase":"mongoDB"}` | Global config variables | | global.audience | string | `""` | OAuth audience | | global.authority | string | `nil` | OAuth authority | | global.authorizationCAPool | string | `nil` | Custom CA certificate for authorization endpoint in PEM format | @@ -311,6 +315,7 @@ global: | global.openTelemetryExporter.keepAlive | object | `{"permitWithoutStream":true,"time":"10s","timeout":"20s"}` | Expoter keep alive configuration | | global.openTelemetryExporter.tls | object | `{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false}` | Expoter TLS configuration | | global.ownerClaim | string | `"sub"` | OAuth owner Claim | +| global.useDatabase | string | `"mongoDB"` | Use database. Supported values: "mongoDB", "cqlDB" | | grpcgateway.affinity | object | `{}` | Affinity definition | | grpcgateway.apis | object | `{"grpc":{"address":null,"authorization":{"audience":"","authority":"","http":{"idleConnTimeout":"30s","maxConnsPerHost":32,"maxIdleConns":16,"maxIdleConnsPerHost":16,"timeout":"10s","tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":true}}},"enforcementPolicy":{"minTime":"5s","permitWithoutStream":true},"keepAlive":{"maxConnectionAge":"0s","maxConnectionAgeGrace":"0s","maxConnectionIdle":"0s","time":"2h","timeout":"20s"},"ownerCacheExpiration":"1m","recvMsgSize":4194304,"sendMsgSize":4194304,"subscriptionBufferSize":1000,"tls":{"caPool":null,"certFile":null,"clientCertificateRequired":false,"keyFile":null}}}` | For complete grpc-gateway service configuration see [plgd/grpc-gateway](https://github.com/plgd-dev/hub/tree/main/grpc-gateway) | | grpcgateway.clients | object | `{"certificateAuthority":{"grpc":{"address":"","keepAlive":{"permitWithoutStream":true,"time":"10s","timeout":"20s"},"recvMsgSize":4194304,"sendMsgSize":4194304,"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false}}},"eventBus":{"goPoolSize":16,"nats":{"pendingLimits":{"bytesLimit":"67108864","msgLimit":524288},"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false},"url":null}},"identityStore":{"grpc":{"address":"","keepAlive":{"permitWithoutStream":true,"time":"10s","timeout":"20s"},"recvMsgSize":4194304,"sendMsgSize":4194304,"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false}}},"resourceAggregate":{"grpc":{"address":"","keepAlive":{"permitWithoutStream":true,"time":"10s","timeout":"20s"},"recvMsgSize":4194304,"sendMsgSize":4194304,"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false}}},"resourceDirectory":{"grpc":{"address":"","keepAlive":{"permitWithoutStream":true,"time":"10s","timeout":"20s"},"recvMsgSize":4194304,"sendMsgSize":4194304,"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false}}}}` | For complete grpc-gateway service configuration see [plgd/grpc-gateway](https://github.com/plgd-dev/hub/tree/main/grpc-gateway) | @@ -430,11 +435,12 @@ global: | httpgateway.service.targetPort | string | `"http"` | Target port | | httpgateway.service.type | string | `"ClusterIP"` | | | httpgateway.tolerations | object | `{}` | Toleration definition | -| httpgateway.ui | object | `{"directory":"/usr/local/var/www","enabled":true,"theme":"","webConfiguration":{"deviceOAuthClient":{"audience":null,"clientID":null,"providerName":null,"scopes":[]},"httpGatewayAddress":"","webOAuthClient":{"audience":"","clientID":"","scopes":[]}}}` | For complete http-gateway service configuration see [plgd/http-gateway](https://github.com/plgd-dev/hub/tree/main/http-gateway) | +| httpgateway.ui | object | `{"directory":"/usr/local/var/www","enabled":true,"theme":"","webConfiguration":{"deviceOAuthClient":{"audience":null,"clientID":null,"providerName":null,"scopes":[]},"deviceProvisioningService":"","httpGatewayAddress":"","visibility":{"mainSidebar":{"apiTokens":false,"certificates":true,"chatRoom":true,"configuration":true,"dashboard":false,"deviceFirmwareUpdate":false,"deviceLogs":false,"deviceProvisioning":true,"devices":true,"docs":true,"integrations":false,"pendingCommands":true,"remoteClients":true,"schemaHub":false}},"webOAuthClient":{"audience":"","clientID":"","scopes":[]}}}` | For complete http-gateway service configuration see [plgd/http-gateway](https://github.com/plgd-dev/hub/tree/main/http-gateway) | | httpgateway.uiDomain | string | `nil` | Domain for UI Default: {{ global.domain }} | | identitystore.affinity | object | `{}` | Affinity definition | | identitystore.apis | object | `{"grpc":{"address":null,"authorization":{"audience":null,"authority":null,"http":{"idleConnTimeout":"30s","maxConnsPerHost":32,"maxIdleConns":16,"maxIdleConnsPerHost":16,"timeout":"10s","tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":true}},"ownerClaim":null},"enforcementPolicy":{"minTime":"5s","permitWithoutStream":true},"keepAlive":{"maxConnectionAge":"0s","maxConnectionAgeGrace":"0s","maxConnectionIdle":"0s","time":"2h","timeout":"20s"},"recvMsgSize":4194304,"sendMsgSize":4194304,"tls":{"caPool":null,"certFile":null,"clientCertificateRequired":true,"keyFile":null}}}` | For complete identity service configuration see [plgd/identity](https://github.com/plgd-dev/hub/tree/main/identity) | -| identitystore.clients | object | `{"eventBus":{"nats":{"flusherTimeout":"30s","jetstream":false,"tls":{"useSystemCAPool":false},"url":""}},"storage":{"cqlDB":{"connectTimeout":"10s","hosts":[],"keyspace":{"create":true,"name":"plgdhub","replication":{"class":"SimpleStrategy","replication_factor":1}},"numConnections":16,"table":"deviceOwners","tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false}},"mongoDB":{"database":"ownersDevices","maxConnIdleTime":"4m0s","maxPoolSize":16,"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false},"uri":null},"use":"mongoDB"}}` | For complete identity service configuration see [plgd/authorization](https://github.com/plgd-dev/hub/tree/main/identity) | +| identitystore.clients | object | `{"eventBus":{"nats":{"flusherTimeout":"30s","jetstream":false,"tls":{"useSystemCAPool":false},"url":""}},"storage":{"cqlDB":{"connectTimeout":"10s","hosts":[],"keyspace":{"create":true,"name":"plgdhub","replication":{"class":"SimpleStrategy","replication_factor":1}},"numConnections":16,"port":9142,"reconnectionPolicy":{"constant":{"interval":"3s","maxRetries":3}},"table":"deviceOwners","tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false},"useHostnameResolution":true},"mongoDB":{"database":"ownersDevices","maxConnIdleTime":"4m0s","maxPoolSize":16,"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false},"uri":null},"use":"mongoDB"}}` | For complete identity service configuration see [plgd/authorization](https://github.com/plgd-dev/hub/tree/main/identity) | +| identitystore.clients.storage.cqlDB.useHostnameResolution | bool | `true` | Resolve IP address to hostname before validate certificate. If false, the TLS validator will use ip/hostname advertised by the Cassandra node. | | identitystore.config | object | `{"fileName":"service.yaml","mountPath":"/config","volume":"config"}` | yaml configuration | | identitystore.config.fileName | string | `"service.yaml"` | File name | | identitystore.config.mountPath | string | `"/config"` | Service configuration mount path | @@ -553,8 +559,8 @@ global: | mockoauthserver.service.targetPort | string | `"http"` | Target port | | mockoauthserver.service.type | string | `"ClusterIP"` | | | mockoauthserver.tolerations | object | `{}` | Toleration definition | -| mongodb | object | `{"arbiter":{"enabled":false},"architecture":"replicaset","auth":{"enabled":false},"customLivenessProbe":{"exec":{"command":["/bin/bash","-c","/certs/livenessProbe.sh"]},"failureThreshold":6,"initialDelaySeconds":30,"periodSeconds":20,"successThreshold":1,"timeoutSeconds":10},"customReadinessProbe":{"exec":{"command":["bash","-ec","TLS_OPTIONS='--tls --tlsCertificateKeyFile=/certs/cert.pem --tlsCAFile=/certs/ca.pem'\n/certs/mongo $TLS_OPTIONS --eval 'db.hello().isWritablePrimary || db.hello().secondary' | grep -q 'true'\n"]},"failureThreshold":6,"initialDelaySeconds":10,"periodSeconds":20,"successThreshold":1,"timeoutSeconds":10},"enabled":true,"extraEnvVars":[{"name":"MONGODB_EXTRA_FLAGS","value":"--tlsMode=requireTLS --tlsCertificateKeyFile=/certs/cert.pem --tlsCAFile=/certs/ca.pem"},{"name":"MONGODB_CLIENT_EXTRA_FLAGS","value":"--tls --tlsCertificateKeyFile=/certs/cert.pem --tlsCAFile=/certs/ca.pem"}],"extraVolumeMounts":[{"mountPath":"/certs","name":"mongodb-crt"},{"mountPath":"/certs-original","name":"mongodb-cm-crt"}],"extraVolumes":[{"emptyDir":{},"name":"mongodb-crt"},{"name":"mongodb-cm-crt","secret":{"secretName":"mongodb-cm-crt"}}],"fullnameOverride":"mongodb","image":{"debug":true,"net":{"port":27017}},"initContainers":[{"command":["sh","-c","/bin/bash <<'EOF'\n#!/bin/bash\ncp /opt/bitnami/mongodb/bin/mongo /certs/mongo\necho '\nINIT=0\nwhile [[ $# -gt 0 ]]; do\n case $1 in\n --init)\n INIT=1\n shift\n ;;\n esac\ndone\n\nCERT_CRT=/certs-original/tls.crt\nCERT_SHA256=/certs/cert.sha256.$(sha256sum -z ${CERT_CRT} | cut -d \" \" -f 1)\nCA=/certs-original/ca.crt\nCA_SHA256=/certs/ca.sha256.$(sha256sum -z ${CA} | cut -d \" \" -f 1)\nROTATE_CERTIFICATES=0\nif [ ! -f ${CERT_SHA256} ]; then\n rm -f /certs/cert.sha256.*\n cat ${CERT_CRT} > /certs/cert.pem\n cat /certs-original/tls.key >> /certs/cert.pem\n touch ${CERT_SHA256}\n ROTATE_CERTIFICATES=1\nfi\n\nif [ ! -f ${CA_SHA256} ]; then\n rm -f /certs/ca.sha256.*\n cp ${CA} /certs/ca.pem\n touch ${CA_SHA256}\n ROTATE_CERTIFICATES=1\nfi\n\nif [ \"${INIT}\" == \"1\" ]; then\n exit 0\nfi\n\nif [ \"${ROTATE_CERTIFICATES}\" == \"1\" ]; then\n echo \"Rotating certificates\"\n /certs/mongo --tls --tlsCertificateKeyFile=/certs/cert.pem --tlsCAFile=/certs/ca.pem --eval db.adminCommand\\(\"{rotateCertificates: 1, message: \\\"Rotating certificates\\\"}\"\\)\nelse\n echo \"Ping database\"\n /certs/mongo --tls --tlsCertificateKeyFile=/certs/cert.pem --tlsCAFile=/certs/ca.pem --eval db.adminCommand\\(\\\"ping\\\"\\)\nfi\n' > /certs/livenessProbe.sh\nchmod u+x /certs/livenessProbe.sh\n/certs/livenessProbe.sh --init\nEOF\n"],"image":"docker.io/bitnami/mongodb:5.0.10-debian-11-r3","imagePullPolicy":"IfNotPresent","name":"mongo-binary","volumeMounts":[{"mountPath":"/certs","name":"mongodb-crt"},{"mountPath":"/certs-original","name":"mongodb-cm-crt"}]}],"livenessProbe":{"enabled":false},"persistence":{"enabled":true},"readinessProbe":{"enabled":false},"replicaCount":3,"replicaSetName":"rs0","tls":{"enabled":false}}` | External mongodb-replica dependency setup | -| nats | object | `{"cluster":{"enabled":false,"noAdvertise":false},"enabled":true,"leafnodes":{"enabled":false,"noAdvertise":false},"nats":{"tls":{"ca":"ca.crt","cert":"tls.crt","key":"tls.key","secret":{"name":"nats-service-crt"},"verify":true}},"natsbox":{"enabled":false},"reloader":{"image":"ghcr.io/plgd-dev/hub/nats-server-config-reloader:vnext","pullPolicy":"Always"}}` | External nats dependency setup | +| mongodb | object | `{"arbiter":{"enabled":false},"architecture":"replicaset","auth":{"enabled":false},"customLivenessProbe":{"exec":{"command":["/bin/bash","-c","/certs/livenessProbe.sh"]},"failureThreshold":6,"initialDelaySeconds":30,"periodSeconds":20,"successThreshold":1,"timeoutSeconds":10},"customReadinessProbe":{"exec":{"command":["bash","-ec","TLS_OPTIONS='--tls --tlsCertificateKeyFile=/certs/cert.pem --tlsCAFile=/certs/ca.pem'\n/certs/mongo $TLS_OPTIONS --eval 'db.hello().isWritablePrimary || db.hello().secondary' | grep -q 'true'\n"]},"failureThreshold":6,"initialDelaySeconds":10,"periodSeconds":20,"successThreshold":1,"timeoutSeconds":10},"enabled":true,"extraEnvVars":[{"name":"MONGODB_EXTRA_FLAGS","value":"--tlsMode=requireTLS --tlsCertificateKeyFile=/certs/cert.pem --tlsCAFile=/certs/ca.pem"},{"name":"MONGODB_CLIENT_EXTRA_FLAGS","value":"--tls --tlsCertificateKeyFile=/certs/cert.pem --tlsCAFile=/certs/ca.pem"}],"extraVolumeMounts":[{"mountPath":"/certs","name":"mongodb-crt"},{"mountPath":"/certs-original","name":"mongodb-cm-crt"}],"extraVolumes":[{"emptyDir":{},"name":"mongodb-crt"},{"name":"mongodb-cm-crt","secret":{"secretName":"mongodb-cm-crt"}}],"fullnameOverride":"mongodb","image":{"debug":true},"initContainers":[{"command":["sh","-c","/bin/bash <<'EOF'\n#!/bin/bash\ncp /opt/bitnami/mongodb/bin/mongo /certs/mongo\necho '\nINIT=0\nwhile [[ $# -gt 0 ]]; do\n case $1 in\n --init)\n INIT=1\n shift\n ;;\n esac\ndone\n\nCERT_CRT=/certs-original/tls.crt\nCERT_SHA256=/certs/cert.sha256.$(sha256sum -z ${CERT_CRT} | cut -d \" \" -f 1)\nCA=/certs-original/ca.crt\nCA_SHA256=/certs/ca.sha256.$(sha256sum -z ${CA} | cut -d \" \" -f 1)\nROTATE_CERTIFICATES=0\nif [ ! -f ${CERT_SHA256} ]; then\n rm -f /certs/cert.sha256.*\n cat ${CERT_CRT} > /certs/cert.pem\n cat /certs-original/tls.key >> /certs/cert.pem\n touch ${CERT_SHA256}\n ROTATE_CERTIFICATES=1\nfi\n\nif [ ! -f ${CA_SHA256} ]; then\n rm -f /certs/ca.sha256.*\n cp ${CA} /certs/ca.pem\n touch ${CA_SHA256}\n ROTATE_CERTIFICATES=1\nfi\n\nif [ \"${INIT}\" == \"1\" ]; then\n exit 0\nfi\n\nif [ \"${ROTATE_CERTIFICATES}\" == \"1\" ]; then\n echo \"Rotating certificates\"\n /certs/mongo --tls --tlsCertificateKeyFile=/certs/cert.pem --tlsCAFile=/certs/ca.pem --eval db.adminCommand\\(\"{rotateCertificates: 1, message: \\\"Rotating certificates\\\"}\"\\)\nelse\n echo \"Ping database\"\n /certs/mongo --tls --tlsCertificateKeyFile=/certs/cert.pem --tlsCAFile=/certs/ca.pem --eval db.adminCommand\\(\\\"ping\\\"\\)\nfi\n' > /certs/livenessProbe.sh\nchmod u+x /certs/livenessProbe.sh\n/certs/livenessProbe.sh --init\nEOF\n"],"image":"docker.io/bitnami/mongodb:5.0.10-debian-11-r3","imagePullPolicy":"IfNotPresent","name":"mongo-binary","volumeMounts":[{"mountPath":"/certs","name":"mongodb-crt"},{"mountPath":"/certs-original","name":"mongodb-cm-crt"}]}],"livenessProbe":{"enabled":false},"persistence":{"enabled":true},"readinessProbe":{"enabled":false},"replicaCount":3,"replicaSetName":"rs0","tls":{"enabled":false}}` | External mongodb-replica dependency setup | +| nats | object | `{"config":{"nats":{"tls":{"enabled":true,"merge":{"verify":true},"secretName":"nats-service-crt"}}},"enabled":true,"monitor":{"enabled":false},"natsBox":{"enabled":false},"tlsCA":{"enabled":true,"secretName":"nats-service-crt"}}` | External nats dependency setup | | resourceaggregate.affinity | object | `{}` | Affinity definition | | resourceaggregate.apis.grpc.address | string | `nil` | | | resourceaggregate.apis.grpc.authorization.audience | string | `nil` | | @@ -583,7 +589,8 @@ global: | resourceaggregate.apis.grpc.tls.certFile | string | `nil` | | | resourceaggregate.apis.grpc.tls.clientCertificateRequired | bool | `true` | | | resourceaggregate.apis.grpc.tls.keyFile | string | `nil` | | -| resourceaggregate.clients | object | `{"eventBus":{"nats":{"flusherTimeout":"30s","jetstream":false,"pendingLimits":{"bytesLimit":"67108864","msgLimit":524288},"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false},"url":null}},"eventStore":{"cqlDB":{"connectTimeout":"10s","hosts":[],"keyspace":{"create":true,"name":"plgdhub","replication":{"class":"SimpleStrategy","replication_factor":1}},"numConnections":16,"table":"events","tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false}},"defaultCommandTimeToLive":null,"mongoDB":{"batchSize":128,"database":"eventStore","maxConnIdleTime":"4m0s","maxPoolSize":16,"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false},"uri":null},"occMaxRetry":8,"use":"mongoDB"},"identityStore":{"grpc":{"address":null,"keepAlive":{"permitWithoutStream":true,"time":"10s","timeout":"20s"},"recvMsgSize":4194304,"sendMsgSize":4194304,"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false}}}}` | For complete resource-aggregate service configuration see [plgd/resource-aggregate](https://github.com/plgd-dev/hub/tree/main/resource-aggregate) | +| resourceaggregate.clients | object | `{"eventBus":{"nats":{"flusherTimeout":"30s","jetstream":false,"pendingLimits":{"bytesLimit":"67108864","msgLimit":524288},"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false},"url":null}},"eventStore":{"cqlDB":{"connectTimeout":"10s","hosts":[],"keyspace":{"create":true,"name":"plgdhub","replication":{"class":"SimpleStrategy","replication_factor":1}},"numConnections":16,"port":9142,"reconnectionPolicy":{"constant":{"interval":"3s","maxRetries":3}},"table":"events","tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false},"useHostnameResolution":true},"defaultCommandTimeToLive":null,"mongoDB":{"batchSize":128,"database":"eventStore","maxConnIdleTime":"4m0s","maxPoolSize":16,"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false},"uri":null},"occMaxRetry":8,"use":"mongoDB"},"identityStore":{"grpc":{"address":null,"keepAlive":{"permitWithoutStream":true,"time":"10s","timeout":"20s"},"recvMsgSize":4194304,"sendMsgSize":4194304,"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false}}}}` | For complete resource-aggregate service configuration see [plgd/resource-aggregate](https://github.com/plgd-dev/hub/tree/main/resource-aggregate) | +| resourceaggregate.clients.eventStore.cqlDB.useHostnameResolution | bool | `true` | Resolve IP address to hostname before validate certificate. If false, the TLS validator will use ip/hostname advertised by the Cassandra node. | | resourceaggregate.config | object | `{"fileName":"service.yaml","mountPath":"/config","volume":"config"}` | Service configuration | | resourceaggregate.config.fileName | string | `"service.yaml"` | Service configuration file name | | resourceaggregate.config.mountPath | string | `"/config"` | Configuration mount path | @@ -636,7 +643,8 @@ global: | resourceaggregate.tolerations | object | `{}` | Toleration definition | | resourcedirectory.affinity | object | `{}` | Affinity definition | | resourcedirectory.apis | object | `{"grpc":{"address":null,"authorization":{"audience":null,"authority":null,"http":{"idleConnTimeout":"30s","maxConnsPerHost":32,"maxIdleConns":16,"maxIdleConnsPerHost":16,"timeout":"10s","tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":true}},"ownerClaim":null},"enforcementPolicy":{"minTime":"5s","permitWithoutStream":true},"keepAlive":{"maxConnectionAge":"0s","maxConnectionAgeGrace":"0s","maxConnectionIdle":"0s","time":"2h","timeout":"20s"},"ownerCacheExpiration":"1m","recvMsgSize":4194304,"sendMsgSize":4194304,"tls":{"caPool":null,"certFile":null,"clientCertificateRequired":true,"keyFile":null}}}` | For complete resource-directory service configuration see [plgd/resource-directory](https://github.com/plgd-dev/hub/tree/main/resource-directory) | -| resourcedirectory.clients | object | `{"eventBus":{"goPoolSize":16,"nats":{"pendingLimits":{"bytesLimit":"67108864","msgLimit":"524288"},"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false},"url":""}},"eventStore":{"cacheExpiration":"20m","cqlDB":{"connectTimeout":"10s","hosts":[],"keyspace":{"create":true,"name":"plgdhub","replication":{"class":"SimpleStrategy","replication_factor":1}},"numConnections":16,"table":"events","tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false}},"mongoDB":{"batchSize":128,"database":"eventStore","maxConnIdleTime":"4m0s","maxPoolSize":16,"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false},"uri":""},"use":"mongoDB"},"identityStore":{"cacheExpiration":"1m","grpc":{"address":"","keepAlive":{"permitWithoutStream":true,"time":"10s","timeout":"20s"},"recvMsgSize":4194304,"sendMsgSize":4194304,"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false}},"oauth":{"audience":"","clientID":null,"clientSecret":null,"http":{"idleConnTimeout":"30s","maxConnsPerHost":32,"maxIdleConns":16,"maxIdleConnsPerHost":16,"timeout":"10s","tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false}},"scopes":[],"tokenURL":"","verifyServiceTokenFrequency":"10s"},"ownerClaim":null,"pullFrequency":"15s"}}` | For complete resource-directory service configuration see [plgd/resource-directory](https://github.com/plgd-dev/hub/tree/main/resource-directory) | +| resourcedirectory.clients | object | `{"eventBus":{"goPoolSize":16,"nats":{"pendingLimits":{"bytesLimit":"67108864","msgLimit":"524288"},"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false},"url":""}},"eventStore":{"cacheExpiration":"20m","cqlDB":{"connectTimeout":"10s","hosts":[],"keyspace":{"create":true,"name":"plgdhub","replication":{"class":"SimpleStrategy","replication_factor":1}},"numConnections":16,"port":9142,"reconnectionPolicy":{"constant":{"interval":"3s","maxRetries":3}},"table":"events","tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false},"useHostnameResolution":true},"mongoDB":{"batchSize":128,"database":"eventStore","maxConnIdleTime":"4m0s","maxPoolSize":16,"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false},"uri":""},"use":"mongoDB"},"identityStore":{"cacheExpiration":"1m","grpc":{"address":"","keepAlive":{"permitWithoutStream":true,"time":"10s","timeout":"20s"},"recvMsgSize":4194304,"sendMsgSize":4194304,"tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false}},"oauth":{"audience":"","clientID":null,"clientSecret":null,"http":{"idleConnTimeout":"30s","maxConnsPerHost":32,"maxIdleConns":16,"maxIdleConnsPerHost":16,"timeout":"10s","tls":{"caPool":null,"certFile":null,"keyFile":null,"useSystemCAPool":false}},"scopes":[],"tokenURL":"","verifyServiceTokenFrequency":"10s"},"ownerClaim":null,"pullFrequency":"15s"}}` | For complete resource-directory service configuration see [plgd/resource-directory](https://github.com/plgd-dev/hub/tree/main/resource-directory) | +| resourcedirectory.clients.eventStore.cqlDB.useHostnameResolution | bool | `true` | Resolve IP address to hostname before validate certificate. If false, the TLS validator will use ip/hostname advertised by the Cassandra node. | | resourcedirectory.config | object | `{"fileName":"service.yaml","mountPath":"/config","volume":"config"}` | Service configuration | | resourcedirectory.config.fileName | string | `"service.yaml"` | Service configuration file | | resourcedirectory.config.mountPath | string | `"/config"` | Configuration mount path | @@ -687,9 +695,23 @@ global: | resourcedirectory.service.targetPort | string | `"grpc"` | Target port | | resourcedirectory.service.type | string | `"ClusterIP"` | resource-directory service type | | resourcedirectory.tolerations | object | `{}` | Toleration definition | -| scylla.datacenter | string | `"dc1"` | | -| scylla.enabled | bool | `false` | | +| scylla.datacenter | string | `"dc-1"` | | +| scylla.enabled | bool | `false` | Enable scylla service. Required scylla operator: https://github.com/scylladb/scylla-operator/blob/master/docs/source/generic.md#deploy-scylla-operator | +| scylla.racks[0].members | int | `3` | | +| scylla.racks[0].name | string | `"dc-1a"` | | +| scylla.racks[0].resources.limits.cpu | int | `1` | | +| scylla.racks[0].resources.limits.memory | string | `"4Gi"` | | +| scylla.racks[0].resources.requests.cpu | int | `1` | | +| scylla.racks[0].resources.requests.memory | string | `"4Gi"` | | +| scylla.racks[0].scyllaConfig | string | `"scylla-cfg"` | | +| scylla.racks[0].storage.capacity | string | `"10Gi"` | | +| scylla.racks[0].volumeMounts[0].mountPath | string | `"/certs"` | | +| scylla.racks[0].volumeMounts[0].name | string | `"scylla-certs-volume"` | | +| scylla.racks[0].volumes[0].name | string | `"scylla-certs-volume"` | | +| scylla.racks[0].volumes[0].secret.secretName | string | `"scylla-dc-1a-crt"` | | +| scylla.scyllaImage.tag | string | `"5.2.9"` | | +| scylla.sysctls[0] | string | `"fs.aio-max-nr=2097152"` | | ---------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.11.3](https://github.com/norwoodj/helm-docs/releases/v1.11.3) +Autogenerated from chart metadata using [helm-docs v1.12.0](https://github.com/norwoodj/helm-docs/releases/v1.12.0) diff --git a/charts/plgd-hub/templates/certs/internal/nats-crt.yaml b/charts/plgd-hub/templates/certs/internal/nats-crt.yaml index d425bcb18..a800f32de 100644 --- a/charts/plgd-hub/templates/certs/internal/nats-crt.yaml +++ b/charts/plgd-hub/templates/certs/internal/nats-crt.yaml @@ -1,14 +1,14 @@ -{{- if and .Values.nats.enabled .Values.certmanager.enabled }} +{{- if and .Values.nats.enabled .Values.certmanager.enabled .Values.nats.config.nats.tls.enabled }} {{- $natsServiceName := printf "%s-nats" ( include "nats.fullname" . ) }} apiVersion: cert-manager.io/v1 kind: Certificate metadata: - name: {{ .Values.nats.nats.tls.secret.name }} + name: {{ .Values.nats.config.nats.tls.secretName }} namespace: {{ .Release.Namespace }} labels: {{- include "plgd-hub.labels" . | nindent 4 }} spec: - secretName: {{ .Values.nats.nats.tls.secret.name }} + secretName: {{ .Values.nats.config.nats.tls.secretName }} privateKey: algorithm: {{ .Values.certmanager.internal.cert.key.algorithm | default .Values.certmanager.default.cert.key.algorithm }} size: {{ .Values.certmanager.internal.cert.key.size | default .Values.certmanager.default.cert.key.size }} diff --git a/charts/plgd-hub/values.yaml b/charts/plgd-hub/values.yaml index 4d9a252f0..811daff00 100644 --- a/charts/plgd-hub/values.yaml +++ b/charts/plgd-hub/values.yaml @@ -56,25 +56,20 @@ extraDeploy: # -- External nats dependency setup nats: enabled: true - nats: - tls: - secret: - name: nats-service-crt - ca: ca.crt - cert: tls.crt - key: tls.key - verify: true - cluster: - enabled: false - noAdvertise: false - leafnodes: + tlsCA: + enabled: true + secretName: nats-service-crt + config: + nats: + tls: + enabled: true + secretName: nats-service-crt + merge: + verify: true + natsBox: enabled: false - noAdvertise: false - natsbox: + monitor: enabled: false - reloader: - image: ghcr.io/plgd-dev/hub/nats-server-config-reloader:vnext - pullPolicy: Always # -- External mongodb-replica dependency setup @@ -92,8 +87,6 @@ mongodb: enabled: false image: debug: true - net: - port: 27017 tls: enabled: false extraEnvVars: diff --git a/cloud2cloud-connector/events/event.go b/cloud2cloud-connector/events/event.go index 0d5379041..e8cccab97 100644 --- a/cloud2cloud-connector/events/event.go +++ b/cloud2cloud-connector/events/event.go @@ -4,6 +4,7 @@ import ( "crypto/hmac" "crypto/sha256" "encoding/hex" + "errors" "fmt" "net/http" "strconv" @@ -51,7 +52,7 @@ func invalidKey(key string) string { } func invalidKeyError(key string) error { - return fmt.Errorf(invalidKey(key)) + return errors.New(invalidKey(key)) } func invalidKeyValueError(key string, value interface{}, err error) error { @@ -120,7 +121,7 @@ func ParseEventHeader(r *http.Request) (h EventHeader, _ error) { if r.Method == "POST" && v != "" { acceptEncoding = strings.Split(v, ",") if len(acceptEncoding) != 1 { - return h, invalidKeyValueError(AcceptEncodingKey, acceptEncoding, fmt.Errorf("more than 1")) + return h, invalidKeyValueError(AcceptEncodingKey, acceptEncoding, errors.New("more than 1")) } } diff --git a/cloud2cloud-connector/service/addLinkedAccount.go b/cloud2cloud-connector/service/addLinkedAccount.go index 83b0ae089..a79e0ffdb 100644 --- a/cloud2cloud-connector/service/addLinkedAccount.go +++ b/cloud2cloud-connector/service/addLinkedAccount.go @@ -4,6 +4,7 @@ import ( "context" "crypto/rand" "encoding/base64" + "errors" "fmt" "net/http" "time" @@ -28,7 +29,7 @@ func (h *LinkedCloudHandler) Handle(ctx context.Context, iter store.LinkedCloudI h.linkedCloud = s return iter.Err() } - return fmt.Errorf("not found") + return errors.New("not found") } func generateRandomString(n int) (string, error) { @@ -42,14 +43,14 @@ func generateRandomString(n int) (string, error) { func (rh *RequestHandler) handleOAuth(w http.ResponseWriter, r *http.Request, linkedAccount store.LinkedAccount, linkedCloud store.LinkedCloud) (int, error) { t, err := generateRandomString(32) if err != nil { - return http.StatusInternalServerError, fmt.Errorf("cannot generate token") + return http.StatusInternalServerError, errors.New("cannot generate token") } _, loaded := rh.provisionCache.LoadOrStore(t, cache.NewElement(ProvisionCacheData{ linkedAccount: linkedAccount, linkedCloud: linkedCloud, }, time.Now().Add(CacheExpiration), nil)) if loaded { - return http.StatusInternalServerError, fmt.Errorf("cannot store key - collision") + return http.StatusInternalServerError, errors.New("cannot store key - collision") } if !linkedAccount.Data.HasOrigin() { @@ -86,7 +87,7 @@ func (rh *RequestHandler) addLinkedAccount(w http.ResponseWriter, r *http.Reques UserID: userID, } if linkedAccount.LinkedCloudID == "" { - return http.StatusBadRequest, fmt.Errorf("invalid cloud_id") + return http.StatusBadRequest, errors.New("invalid cloud_id") } return rh.handleOAuth(w, r, linkedAccount, linkedCloud) } diff --git a/cloud2cloud-connector/service/deviceSubscriptionHandlers.go b/cloud2cloud-connector/service/deviceSubscriptionHandlers.go index 2f9b0276d..25a700ea7 100644 --- a/cloud2cloud-connector/service/deviceSubscriptionHandlers.go +++ b/cloud2cloud-connector/service/deviceSubscriptionHandlers.go @@ -2,6 +2,7 @@ package service import ( "context" + "errors" "fmt" "sync/atomic" "time" @@ -41,15 +42,15 @@ func (h deviceSubscriptionHandlers) RetrieveResource(ctx context.Context, event } func (h deviceSubscriptionHandlers) DeleteResource(context.Context, *raEvents.ResourceDeletePending) error { - return fmt.Errorf(NOT_SUPPORTED_ERR) + return errors.New(NOT_SUPPORTED_ERR) } func (h deviceSubscriptionHandlers) CreateResource(context.Context, *raEvents.ResourceCreatePending) error { - return fmt.Errorf(NOT_SUPPORTED_ERR) + return errors.New(NOT_SUPPORTED_ERR) } func (h deviceSubscriptionHandlers) UpdateDeviceMetadata(context.Context, *raEvents.DeviceMetadataUpdatePending) error { - return fmt.Errorf(NOT_SUPPORTED_ERR) + return errors.New(NOT_SUPPORTED_ERR) } func (h deviceSubscriptionHandlers) OnDeviceSubscriberReconnectError(err error) { @@ -115,7 +116,7 @@ func (c *DevicesSubscription) Add(ctx context.Context, deviceID string, linkedAc return retrieveResource(ctx, c.tracerProvider, c.raClient, val, linkedAccount, linkedCloud) }, onError: func(err error) { - log.Errorf("device %v subscription(ResourceUpdatePending, ResourceRetrievePending) was closed", deviceID) + log.Errorf("device %v subscription(ResourceUpdatePending, ResourceRetrievePending) was closed: %w", deviceID, err) c.data.Delete(getKey(linkedAccount.UserID, deviceID)) }, }) diff --git a/cloud2cloud-connector/service/devicesSubscription.go b/cloud2cloud-connector/service/devicesSubscription.go index 439f2a8e4..f68b47a6c 100644 --- a/cloud2cloud-connector/service/devicesSubscription.go +++ b/cloud2cloud-connector/service/devicesSubscription.go @@ -135,7 +135,7 @@ func (s *SubscriptionManager) handleDevicesUnregistered(ctx context.Context, sub if err != nil { errors = multierror.Append(errors, fmt.Errorf("cannot remove device %v from user: %w", device.ID, err)) } - if err == nil && len(resp.DeviceIds) != 1 { + if err == nil && len(resp.GetDeviceIds()) != 1 { errors = multierror.Append(errors, fmt.Errorf("cannot remove device %v from user", device.ID)) } err = s.devicesSubscription.Delete(userID, device.ID) diff --git a/cloud2cloud-connector/service/getDevices_test.go b/cloud2cloud-connector/service/getDevices_test.go index 1f57dcfcc..db3ee190b 100644 --- a/cloud2cloud-connector/service/getDevices_test.go +++ b/cloud2cloud-connector/service/getDevices_test.go @@ -70,7 +70,7 @@ func testRequestHandlerGetDevices(t *testing.T, events store.Events) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(c2cConnectorTest.GRPC_GATEWAY_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(c2cConnectorTest.GRPC_GATEWAY_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -94,11 +94,12 @@ func testRequestHandlerGetDevices(t *testing.T, events store.Events) { break } require.NoError(t, err) - assert.NotEmpty(t, dev.ProtocolIndependentId) + assert.NotEmpty(t, dev.GetProtocolIndependentId()) dev.ProtocolIndependentId = "" if dev.GetMetadata().GetConnection() != nil { dev.GetMetadata().GetConnection().Id = "" dev.GetMetadata().GetConnection().ConnectedAt = 0 + dev.GetMetadata().GetConnection().LocalEndpoints = nil } if dev.GetMetadata().GetTwinSynchronization() != nil { dev.GetMetadata().GetTwinSynchronization().CommandMetadata = nil diff --git a/cloud2cloud-connector/service/oauthCallback.go b/cloud2cloud-connector/service/oauthCallback.go index d4522cd84..890a365bb 100644 --- a/cloud2cloud-connector/service/oauthCallback.go +++ b/cloud2cloud-connector/service/oauthCallback.go @@ -2,6 +2,7 @@ package service import ( "context" + "errors" "fmt" "net/http" @@ -43,7 +44,7 @@ func (rh *RequestHandler) oAuthCallback(w http.ResponseWriter, r *http.Request) cacheData := rh.provisionCache.Load(state) if cacheData == nil { - return http.StatusBadRequest, fmt.Errorf("invalid/expired OAuth state") + return http.StatusBadRequest, errors.New("invalid/expired OAuth state") } rh.provisionCache.Delete(state) data := cacheData.Data() diff --git a/cloud2cloud-connector/service/pull.go b/cloud2cloud-connector/service/pull.go index 3bc4589de..ca97e02e2 100644 --- a/cloud2cloud-connector/service/pull.go +++ b/cloud2cloud-connector/service/pull.go @@ -70,7 +70,7 @@ func getOwnerDevices(ctx context.Context, isClient pbIS.IdentityStoreClient) (ma continue } - ownerDevices[device.DeviceId] = true + ownerDevices[device.GetDeviceId()] = true } return ownerDevices, nil } @@ -193,7 +193,7 @@ func (p *pullDevicesHandler) deleteDevice(ctx context.Context, userID, deviceID if err != nil { errors = multierror.Append(errors, fmt.Errorf("cannot delete device %v: %w", deviceID, err)) } - if err == nil && len(resp.DeviceIds) != 1 { + if err == nil && len(resp.GetDeviceIds()) != 1 { errors = multierror.Append(errors, fmt.Errorf("cannot remove device %v", deviceID)) } return errors.ErrorOrNil() diff --git a/cloud2cloud-connector/service/requestHandler.go b/cloud2cloud-connector/service/requestHandler.go index f8bfa780d..265cfe217 100644 --- a/cloud2cloud-connector/service/requestHandler.go +++ b/cloud2cloud-connector/service/requestHandler.go @@ -82,7 +82,7 @@ func NewHTTP(requestHandler *RequestHandler, authInterceptor kitNetHttp.Intercep r := router.NewRouter() r.StrictSlash(true) r.Use(kitNetHttp.CreateLoggingMiddleware(kitNetHttp.WithLogger(logger))) - r.Use(kitNetHttp.CreateAuthMiddleware(authInterceptor, func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) { + r.Use(kitNetHttp.CreateAuthMiddleware(authInterceptor, func(_ context.Context, w http.ResponseWriter, r *http.Request, err error) { logAndWriteErrorResponse(fmt.Errorf("cannot process request on %v: %w", r.RequestURI, err), http.StatusUnauthorized, w) })) diff --git a/cloud2cloud-connector/service/retrieveResourceFromDevice_test.go b/cloud2cloud-connector/service/retrieveResourceFromDevice_test.go index 4f74d2905..80e41a20e 100644 --- a/cloud2cloud-connector/service/retrieveResourceFromDevice_test.go +++ b/cloud2cloud-connector/service/retrieveResourceFromDevice_test.go @@ -48,7 +48,7 @@ func testRequestHandlerGetResourceFromDevice(t *testing.T, events store.Events) }, }, wantContentType: message.AppOcfCbor.String(), - want: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceLightInstanceHref("1"), "", map[string]interface{}{ + want: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", map[string]interface{}{ "name": "Light", "power": uint64(0), "state": false, @@ -62,7 +62,7 @@ func testRequestHandlerGetResourceFromDevice(t *testing.T, events store.Events) }, }, wantContentType: message.AppOcfCbor.String(), - want: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceSwitchesHref, "", []map[interface{}]interface{}{ + want: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceSwitchesHref, test.TestResourceSwitchesResourceTypes, "", []map[interface{}]interface{}{ { "href": test.TestResourceSwitchesInstanceHref(switchID), "if": []interface{}{interfaces.OC_IF_A, interfaces.OC_IF_BASELINE}, @@ -80,7 +80,7 @@ func testRequestHandlerGetResourceFromDevice(t *testing.T, events store.Events) }, }, wantContentType: message.AppOcfCbor.String(), - want: pbTest.MakeResourceRetrieved(t, deviceID, device.ResourceURI, "", map[string]interface{}{ + want: pbTest.MakeResourceRetrieved(t, deviceID, device.ResourceURI, test.TestResourceDeviceResourceTypes, "", map[string]interface{}{ "di": deviceID, "dmv": "ocf.res.1.3.0", "icv": "ocf.2.0.5", @@ -107,7 +107,7 @@ func testRequestHandlerGetResourceFromDevice(t *testing.T, events store.Events) defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(c2cConnectorTest.GRPC_GATEWAY_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(c2cConnectorTest.GRPC_GATEWAY_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/cloud2cloud-connector/service/service.go b/cloud2cloud-connector/service/service.go index 2a2a53b94..5f4b786cc 100644 --- a/cloud2cloud-connector/service/service.go +++ b/cloud2cloud-connector/service/service.go @@ -125,8 +125,8 @@ func newStore(ctx context.Context, config pkgMongo.Config, fileWatcher *fsnotify return nil, nil, fmt.Errorf("cannot create mongodb subscription store: %w", err) } fl.AddFunc(func() { - if err := db.Close(ctx); err != nil { - log.Errorf("failed to close subscription store: %w", err) + if errC := db.Close(ctx); errC != nil { + log.Errorf("failed to close subscription store: %w", errC) } }) diff --git a/cloud2cloud-connector/service/store.go b/cloud2cloud-connector/service/store.go index 481469fc5..a7cf41bdc 100644 --- a/cloud2cloud-connector/service/store.go +++ b/cloud2cloud-connector/service/store.go @@ -2,7 +2,7 @@ package service import ( "context" - "fmt" + "errors" "github.com/plgd-dev/hub/v2/cloud2cloud-connector/store" "github.com/plgd-dev/hub/v2/pkg/log" @@ -128,7 +128,7 @@ func (s *Store) PullOutSubscription(subscripionID string) (SubscriptionData, boo func (s *Store) PullOutCloud(ctx context.Context, cloudID string) (*CloudData, error) { cloud, ok := s.cache.PullOutCloud(cloudID) if !ok { - return cloud, fmt.Errorf("not found") + return cloud, errors.New("not found") } return cloud, s.db.RemoveLinkedCloud(ctx, cloudID) } @@ -136,7 +136,7 @@ func (s *Store) PullOutCloud(ctx context.Context, cloudID string) (*CloudData, e func (s *Store) PullOutLinkedAccount(ctx context.Context, cloudID, linkedAccountID string) (*LinkedAccountData, error) { cloud, ok := s.cache.PullOutLinkedAccount(cloudID, linkedAccountID) if !ok { - return cloud, fmt.Errorf("not found") + return cloud, errors.New("not found") } return cloud, s.db.RemoveLinkedAccount(ctx, linkedAccountID) } diff --git a/cloud2cloud-connector/service/subscriptions.go b/cloud2cloud-connector/service/subscriptions.go index 7ae4ebaeb..d95a30792 100644 --- a/cloud2cloud-connector/service/subscriptions.go +++ b/cloud2cloud-connector/service/subscriptions.go @@ -107,13 +107,13 @@ func subscribe(ctx context.Context, tracerProvider trace.TracerProvider, href, c go func() { defer func() { - if err := w.Close(); err != nil { - log.Errorf("failed to close write pipe: %v", err) + if errC := w.Close(); errC != nil { + log.Errorf("failed to close write pipe: %w", errC) } }() - err := json.WriteTo(w, reqBody) - if err != nil { - log.Errorf("cannot encode %+v to json: %w", reqBody, err) + errW := json.WriteTo(w, reqBody) + if errW != nil { + log.Errorf("cannot encode %+v to json: %w", reqBody, errW) } }() httpResp, err := client.Do(req) @@ -212,7 +212,7 @@ func (s *SubscriptionManager) handleEvent(ctx context.Context, header events.Eve ctx = kitNetGrpc.CtxWithToken(ctx, subData.linkedAccount.Data.Origin().AccessToken.String()) if header.EventType == events.EventType_SubscriptionCanceled { - err := s.handleCancelEvent(header) + err = s.handleCancelEvent(header) if err != nil { return http.StatusGone, fmt.Errorf("cannot cancel subscription: %w", err) } diff --git a/cloud2cloud-connector/service/taskProcessor.go b/cloud2cloud-connector/service/taskProcessor.go index f38d018a0..497f284bb 100644 --- a/cloud2cloud-connector/service/taskProcessor.go +++ b/cloud2cloud-connector/service/taskProcessor.go @@ -150,9 +150,9 @@ func (h *TaskProcessor) Run(ctx context.Context, subscriptionManager *Subscripti for process { select { case e := <-h.tasksChan: - ctx, cancel := context.WithTimeout(ctx, h.timeout) + runctx, cancel := context.WithTimeout(ctx, h.timeout) defer cancel() - err := h.runTask(ctx, e, subscriptionManager) + err := h.runTask(runctx, e, subscriptionManager) if err != nil { log.Errorf("cannot process task %+v: %w", e, err) } diff --git a/cloud2cloud-connector/service/updateResource.go b/cloud2cloud-connector/service/updateResource.go index f00edaf23..fb53a7e78 100644 --- a/cloud2cloud-connector/service/updateResource.go +++ b/cloud2cloud-connector/service/updateResource.go @@ -40,12 +40,12 @@ func updateDeviceResource(ctx context.Context, tracerProvider trace.TracerProvid go func() { defer func() { if errC := w.Close(); errC != nil { - log.Errorf("failed to close write pipe: %v", errC) + log.Errorf("failed to close write pipe: %w", errC) } }() - _, err := w.Write(content) - if err != nil { - log.Errorf("cannot update content of device %v resource %v: %w", deviceID, href, err) + _, errW := w.Write(content) + if errW != nil { + log.Errorf("cannot update content of device %v resource %v: %w", deviceID, href, errW) } }() httpResp, err := client.Do(req) diff --git a/cloud2cloud-connector/service/updateResource_test.go b/cloud2cloud-connector/service/updateResource_test.go index 89fc58e6c..652cb58f6 100644 --- a/cloud2cloud-connector/service/updateResource_test.go +++ b/cloud2cloud-connector/service/updateResource_test.go @@ -54,8 +54,9 @@ func testRequestHandlerUpdateResource(t *testing.T, events store.Events) { Content: &commands.Content{ CoapContentFormat: -1, }, - Status: commands.Status_OK, - AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + Status: commands.Status_OK, + AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + ResourceTypes: test.TestResourceLightInstanceResourceTypes, }, }, { @@ -77,8 +78,9 @@ func testRequestHandlerUpdateResource(t *testing.T, events store.Events) { Content: &commands.Content{ CoapContentFormat: -1, }, - Status: commands.Status_OK, - AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + Status: commands.Status_OK, + AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + ResourceTypes: test.TestResourceLightInstanceResourceTypes, }, }, { @@ -100,8 +102,9 @@ func testRequestHandlerUpdateResource(t *testing.T, events store.Events) { Content: &commands.Content{ CoapContentFormat: -1, }, - Status: commands.Status_OK, - AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + Status: commands.Status_OK, + AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + ResourceTypes: test.TestResourceLightInstanceResourceTypes, }, }, { @@ -137,7 +140,7 @@ func testRequestHandlerUpdateResource(t *testing.T, events store.Events) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(c2cConnectorTest.GRPC_GATEWAY_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(c2cConnectorTest.GRPC_GATEWAY_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/cloud2cloud-connector/store/mongodb/linkedAccounts.go b/cloud2cloud-connector/store/mongodb/linkedAccounts.go index 77eb611fd..55414965c 100644 --- a/cloud2cloud-connector/store/mongodb/linkedAccounts.go +++ b/cloud2cloud-connector/store/mongodb/linkedAccounts.go @@ -14,21 +14,21 @@ const resLinkedAccountCName = "linkedAccounts" func validateLinkedAccount(sub store.LinkedAccount) error { if sub.ID == "" { - return fmt.Errorf("invalid ID") + return errors.New("invalid ID") } if sub.UserID == "" { - return fmt.Errorf("invalid UserID") + return errors.New("invalid UserID") } if sub.LinkedCloudID == "" { - return fmt.Errorf("invalid LinkedCloudID") + return errors.New("invalid LinkedCloudID") } origin := sub.Data.Origin() if origin.AccessToken == "" && origin.RefreshToken == "" { - return fmt.Errorf("invalid Data.OriginCloud.AccessToken and Data.OriginCloud.RefreshToken") + return errors.New("invalid Data.OriginCloud.AccessToken and Data.OriginCloud.RefreshToken") } target := sub.Data.Target() if target.AccessToken == "" && target.RefreshToken == "" { - return fmt.Errorf("invalid Data.TargetCloud.AccessToken and Data.TargetCloud.RefreshToken") + return errors.New("invalid Data.TargetCloud.AccessToken and Data.TargetCloud.RefreshToken") } return nil } @@ -59,21 +59,21 @@ func (s *Store) UpdateLinkedAccount(ctx context.Context, sub store.LinkedAccount return fmt.Errorf("cannot update linked account: %w", err) } if res.MatchedCount == 0 { - return fmt.Errorf("cannot update linked account: not found") + return errors.New("cannot update linked account: not found") } return nil } func (s *Store) RemoveLinkedAccount(ctx context.Context, linkedAccountID string) error { if linkedAccountID == "" { - return fmt.Errorf("cannot remove linked account: invalid linkedAccountID") + return errors.New("cannot remove linked account: invalid linkedAccountID") } res, err := s.Collection(resLinkedAccountCName).DeleteOne(ctx, bson.M{"_id": linkedAccountID}) if err != nil { return fmt.Errorf("cannot remove linked account: %w", err) } if res.DeletedCount == 0 { - return fmt.Errorf("cannot remove linked account: not found") + return errors.New("cannot remove linked account: not found") } return nil } diff --git a/cloud2cloud-connector/store/mongodb/linkedAccounts_test.go b/cloud2cloud-connector/store/mongodb/linkedAccounts_test.go index bcfd24f20..0bb3ed8e6 100644 --- a/cloud2cloud-connector/store/mongodb/linkedAccounts_test.go +++ b/cloud2cloud-connector/store/mongodb/linkedAccounts_test.go @@ -7,7 +7,6 @@ import ( "github.com/plgd-dev/hub/v2/cloud2cloud-connector/store" "github.com/plgd-dev/hub/v2/cloud2cloud-connector/test" "github.com/plgd-dev/hub/v2/pkg/security/oauth2" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -45,10 +44,10 @@ func TestStoreInsertLinkedAccount(t *testing.T) { t.Run(tt.name, func(t *testing.T) { err := s.InsertLinkedAccount(ctx, tt.args.sub) if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) + require.Error(t, err) + return } + require.NoError(t, err) }) } } @@ -107,10 +106,10 @@ func TestStoreUpdateLinkedAccount(t *testing.T) { t.Run(tt.name, func(t *testing.T) { err := s.UpdateLinkedAccount(ctx, tt.args.sub) if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) + require.Error(t, err) + return } + require.NoError(t, err) }) } } @@ -159,10 +158,10 @@ func TestStoreRemoveLinkedAccount(t *testing.T) { t.Run(tt.name, func(t *testing.T) { err := s.RemoveLinkedAccount(ctx, tt.args.linkedAccountID) if tt.wantErr { - assert.Error(t, err) + require.Error(t, err) return } - assert.NoError(t, err) + require.NoError(t, err) }) } } @@ -241,11 +240,11 @@ func TestStoreLoadLinkedAccounts(t *testing.T) { var h testLinkedAccountHandler err := s.LoadLinkedAccounts(ctx, tt.args.query, &h) if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.Equal(t, tt.want, h.accs) + require.Error(t, err) + return } + require.NoError(t, err) + require.Equal(t, tt.want, h.accs) }) } } diff --git a/cloud2cloud-connector/store/mongodb/linkedClouds.go b/cloud2cloud-connector/store/mongodb/linkedClouds.go index 48af42c9f..857249014 100644 --- a/cloud2cloud-connector/store/mongodb/linkedClouds.go +++ b/cloud2cloud-connector/store/mongodb/linkedClouds.go @@ -14,22 +14,22 @@ const resLinkedCloudCName = "LinkedCloud" func validateLinkedCloud(sub store.LinkedCloud) error { if sub.ID == "" { - return fmt.Errorf("cannot save linked cloud: invalid Id") + return errors.New("cannot save linked cloud: invalid Id") } if sub.Endpoint.URL == "" { - return fmt.Errorf("cannot save linked cloud: invalid URL") + return errors.New("cannot save linked cloud: invalid URL") } if sub.OAuth.ClientID == "" { - return fmt.Errorf("cannot save linked cloud: invalid ClientId") + return errors.New("cannot save linked cloud: invalid ClientId") } if sub.OAuth.ClientSecret == "" { - return fmt.Errorf("cannot save linked cloud: invalid ClientSecret") + return errors.New("cannot save linked cloud: invalid ClientSecret") } if sub.OAuth.AuthURL == "" { - return fmt.Errorf("cannot save linked cloud: invalid AuthUrl") + return errors.New("cannot save linked cloud: invalid AuthUrl") } if sub.OAuth.TokenURL == "" { - return fmt.Errorf("cannot save linked cloud: invalid TokenUrl") + return errors.New("cannot save linked cloud: invalid TokenUrl") } return nil } @@ -46,7 +46,7 @@ func (s *Store) UpdateLinkedCloud(ctx context.Context, sub store.LinkedCloud) er return fmt.Errorf("cannot save linked cloud: %w", err) } if res.MatchedCount == 0 { - return fmt.Errorf("cannot update linked cloud: not found") + return errors.New("cannot update linked cloud: not found") } return nil } @@ -67,7 +67,7 @@ func (s *Store) InsertLinkedCloud(ctx context.Context, sub store.LinkedCloud) er func (s *Store) RemoveLinkedCloud(ctx context.Context, linkedCloudID string) error { if linkedCloudID == "" { - return fmt.Errorf("cannot remove linked cloud: invalid LinkedCloudId") + return errors.New("cannot remove linked cloud: invalid LinkedCloudId") } res, err := s.Collection(resLinkedCloudCName).DeleteOne(ctx, bson.M{"_id": linkedCloudID}) @@ -75,7 +75,7 @@ func (s *Store) RemoveLinkedCloud(ctx context.Context, linkedCloudID string) err return fmt.Errorf("cannot remove linked cloud: %w", err) } if res.DeletedCount == 0 { - return fmt.Errorf("cannot remove linked cloud: not found") + return errors.New("cannot remove linked cloud: not found") } return nil } diff --git a/cloud2cloud-connector/store/mongodb/linkedClouds_test.go b/cloud2cloud-connector/store/mongodb/linkedClouds_test.go index a983ae00d..b1b68867a 100644 --- a/cloud2cloud-connector/store/mongodb/linkedClouds_test.go +++ b/cloud2cloud-connector/store/mongodb/linkedClouds_test.go @@ -7,7 +7,6 @@ import ( "github.com/plgd-dev/hub/v2/cloud2cloud-connector/store" "github.com/plgd-dev/hub/v2/cloud2cloud-connector/test" "github.com/plgd-dev/hub/v2/pkg/security/oauth2/oauth" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -85,10 +84,10 @@ func TestStoreUpdateLinkedCloud(t *testing.T) { t.Run(tt.name, func(t *testing.T) { err := s.UpdateLinkedCloud(ctx, tt.args.sub) if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) + require.Error(t, err) + return } + require.NoError(t, err) }) } } @@ -142,10 +141,10 @@ func TestStoreRemoveLinkedCloud(t *testing.T) { t.Run(tt.name, func(t *testing.T) { err := s.RemoveLinkedCloud(ctx, tt.args.LinkedCloudID) if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) + require.Error(t, err) + return } + require.NoError(t, err) }) } } @@ -242,11 +241,11 @@ func TestStoreLoadLinkedClouds(t *testing.T) { var h testLinkedCloudHandler err := s.LoadLinkedClouds(ctx, tt.args.query, &h) if tt.wantErr { - assert.Error(t, err) + require.Error(t, err) return } - assert.NoError(t, err) - assert.Equal(t, tt.want, h.lcs) + require.NoError(t, err) + require.Equal(t, tt.want, h.lcs) }) } } diff --git a/cloud2cloud-connector/store/mongodb/store.go b/cloud2cloud-connector/store/mongodb/store.go index eccf4c4b5..205661dd7 100644 --- a/cloud2cloud-connector/store/mongodb/store.go +++ b/cloud2cloud-connector/store/mongodb/store.go @@ -19,9 +19,7 @@ func NewStore(ctx context.Context, cfg pkgMongo.Config, tls *tls.Config, tracerP return nil, err } s := Store{m} - s.SetOnClear(func(c context.Context) error { - return s.clearDatabases(ctx) - }) + s.SetOnClear(s.clearDatabases) return &s, nil } diff --git a/cloud2cloud-connector/test/test.go b/cloud2cloud-connector/test/test.go index 94b90604a..398879c7b 100644 --- a/cloud2cloud-connector/test/test.go +++ b/cloud2cloud-connector/test/test.go @@ -51,7 +51,7 @@ func SetUpClouds(ctx context.Context, t *testing.T, deviceID string, supportedEv cloud2 := SetUpCloudWithConnector(t) ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - cloud1Conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + cloud1Conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/cloud2cloud-gateway/service/httpApi.go b/cloud2cloud-gateway/service/httpApi.go index 968f254c0..b8df6758f 100644 --- a/cloud2cloud-gateway/service/httpApi.go +++ b/cloud2cloud-gateway/service/httpApi.go @@ -172,7 +172,7 @@ func NewHTTP(requestHandler *RequestHandler, authInterceptor kitNetHttp.Intercep r := router.NewRouter() r.StrictSlash(true) r.Use(kitNetHttp.CreateLoggingMiddleware(kitNetHttp.WithLogger(logger))) - r.Use(kitNetHttp.CreateAuthMiddleware(authInterceptor, func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) { + r.Use(kitNetHttp.CreateAuthMiddleware(authInterceptor, func(_ context.Context, w http.ResponseWriter, r *http.Request, err error) { logAndWriteErrorResponse(fmt.Errorf("cannot process request on %v: %w", r.RequestURI, err), http.StatusUnauthorized, w) })) diff --git a/cloud2cloud-gateway/service/retrieveAllDevicesSubscription_test.go b/cloud2cloud-gateway/service/retrieveAllDevicesSubscription_test.go index 455e38a8f..d894edc55 100644 --- a/cloud2cloud-gateway/service/retrieveAllDevicesSubscription_test.go +++ b/cloud2cloud-gateway/service/retrieveAllDevicesSubscription_test.go @@ -32,7 +32,7 @@ func TestRequestHandlerRetrieveDevicesSubscription(t *testing.T) { tearDown := service.SetUp(ctx, t) defer tearDown() - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/cloud2cloud-gateway/service/retrieveDevice.go b/cloud2cloud-gateway/service/retrieveDevice.go index fd972a7e7..36fde2079 100644 --- a/cloud2cloud-gateway/service/retrieveDevice.go +++ b/cloud2cloud-gateway/service/retrieveDevice.go @@ -65,7 +65,7 @@ func (rh *RequestHandler) GetResourceLinks(ctx context.Context, deviceIdFilter [ } } if len(resourceLinks) == 0 { - return nil, fmt.Errorf("cannot get resource links: not found") + return nil, errors.New("cannot get resource links: not found") } return resourceLinks, nil } @@ -91,11 +91,11 @@ func unmarshalContent(c *commands.Content, m interface{}) error { case message.TextPlain.String(): switch v := m.(type) { case *string: - *v = string(c.Data) + *v = string(c.GetData()) case *[]byte: - *v = c.Data + *v = c.GetData() case *interface{}: - *v = string(c.Data) + *v = string(c.GetData()) default: return fmt.Errorf("cannot unmarshal resource content: invalid type (%T)", m) } @@ -216,7 +216,7 @@ func (rh *RequestHandler) RetrieveDeviceWithContentQuery(ctx context.Context, w case ContentQueryAllValue: return rh.RetrieveDeviceWithRepresentations(ctx, w, routeVars[deviceIDKey], encoder) } - return http.StatusBadRequest, fmt.Errorf("invalid content query parameter") + return http.StatusBadRequest, errors.New("invalid content query parameter") } func (rh *RequestHandler) RetrieveDevice(w http.ResponseWriter, r *http.Request) { diff --git a/cloud2cloud-gateway/service/retrieveDeviceSubscription_test.go b/cloud2cloud-gateway/service/retrieveDeviceSubscription_test.go index faa15c1d6..023a3200e 100644 --- a/cloud2cloud-gateway/service/retrieveDeviceSubscription_test.go +++ b/cloud2cloud-gateway/service/retrieveDeviceSubscription_test.go @@ -35,7 +35,7 @@ func TestRequestHandlerRetrieveDeviceSubscription(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/cloud2cloud-gateway/service/retrieveDevice_test.go b/cloud2cloud-gateway/service/retrieveDevice_test.go index 81cea0cbb..b8abebabb 100644 --- a/cloud2cloud-gateway/service/retrieveDevice_test.go +++ b/cloud2cloud-gateway/service/retrieveDevice_test.go @@ -61,12 +61,14 @@ type DevicesAllRepresentation struct { func getDevicesAllRepresentation(t *testing.T, deviceID, deviceName, switchID string) DevicesAllRepresentation { links := test.GetAllBackendResourceRepresentations(t, deviceID, deviceName) for i := range links { + links[i].ResourceTypes = nil if strings.HasSuffix(links[i].Href, test.TestResourceSwitchesHref) { l := test.DefaultSwitchResourceLink(deviceID, switchID) l.DeviceID = "" links[i].Representation = schema.ResourceLinks{l} continue } + // according OCF spec, resource link should not contain resource types field with content } links = append(links, test.ResourceLinkRepresentation{ Href: "/" + commands.NewResourceID(deviceID, test.TestResourceSwitchesInstanceHref(switchID)).ToString(), @@ -92,7 +94,7 @@ func TestRequestHandlerRetrieveDevice(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -268,6 +270,9 @@ func TestRequestHandlerRetrieveDevice(t *testing.T) { d.Device.ProtocolIndependentID = "" d.Device.ManufacturerName = nil d.Links = d.Links.Sort() + for i := range d.Links { + d.Links[i].ResourceTypes = nil + } got = d } else if _, ok := tt.want.(DevicesBaseRepresentation); ok { d := DevicesBaseRepresentation{} diff --git a/cloud2cloud-gateway/service/retrieveResource.go b/cloud2cloud-gateway/service/retrieveResource.go index a9f99d3b9..1dd61b911 100644 --- a/cloud2cloud-gateway/service/retrieveResource.go +++ b/cloud2cloud-gateway/service/retrieveResource.go @@ -2,6 +2,7 @@ package service import ( "context" + "errors" "fmt" "net/http" "strings" @@ -48,7 +49,7 @@ func (rh *RequestHandler) RetrieveResourceWithContentQuery(ctx context.Context, } return code, err } - return http.StatusBadRequest, fmt.Errorf("invalid content query parameter") + return http.StatusBadRequest, errors.New("invalid content query parameter") } func (rh *RequestHandler) RetrieveResource(w http.ResponseWriter, r *http.Request) { diff --git a/cloud2cloud-gateway/service/retrieveResourceSubscription.go b/cloud2cloud-gateway/service/retrieveResourceSubscription.go index 738f408bd..b61d7766f 100644 --- a/cloud2cloud-gateway/service/retrieveResourceSubscription.go +++ b/cloud2cloud-gateway/service/retrieveResourceSubscription.go @@ -1,6 +1,7 @@ package service import ( + "errors" "fmt" "net/http" @@ -15,7 +16,7 @@ func (rh *RequestHandler) retrieveSubscription(w http.ResponseWriter, r *http.Re sub, ok := rh.subMgr.Load(subscriptionID) if !ok { - return http.StatusNotFound, fmt.Errorf("not found") + return http.StatusNotFound, errors.New("not found") } if href != "" && sub.Href != href { diff --git a/cloud2cloud-gateway/service/retrieveResourceSubscription_test.go b/cloud2cloud-gateway/service/retrieveResourceSubscription_test.go index 051879cf0..ee53eb16d 100644 --- a/cloud2cloud-gateway/service/retrieveResourceSubscription_test.go +++ b/cloud2cloud-gateway/service/retrieveResourceSubscription_test.go @@ -35,7 +35,7 @@ func TestRequestHandlerRetrieveResourceSubscription(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/cloud2cloud-gateway/service/retrieveResource_test.go b/cloud2cloud-gateway/service/retrieveResource_test.go index ba3868ed4..84ca55784 100644 --- a/cloud2cloud-gateway/service/retrieveResource_test.go +++ b/cloud2cloud-gateway/service/retrieveResource_test.go @@ -34,7 +34,7 @@ func TestRequestHandlerRetrieveResource(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/cloud2cloud-gateway/service/subscribeToDevice_test.go b/cloud2cloud-gateway/service/subscribeToDevice_test.go index 45e4bfd6e..bc920e07b 100644 --- a/cloud2cloud-gateway/service/subscribeToDevice_test.go +++ b/cloud2cloud-gateway/service/subscribeToDevice_test.go @@ -46,7 +46,7 @@ func TestRequestHandlerSubscribeToDevicePublishedOnly(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -66,7 +66,7 @@ func TestRequestHandlerSubscribeToDevicePublishedOnly(t *testing.T) { subID := subscriber.Subscribe(ctx, t, token, deviceID, "", c2cEvents.EventTypes{c2cEvents.EventType_ResourcesPublished}) ev := <-dataChan - publishedResources := test.ResourceLinksToResources(deviceID, test.TestDevsimResources) + publishedResources := test.ResourceLinksToResources(deviceID, test.GetAllBackendResourceLinks()) assert.Equal(t, c2cEvents.EventType_ResourcesPublished, ev.GetHeader().EventType) links := ev.GetData().(schema.ResourceLinks) resources := testSubscribeToDeviceDecodeResources(links) @@ -91,7 +91,7 @@ func TestRequestHandlerSubscribeToDevice(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -118,7 +118,7 @@ func TestRequestHandlerSubscribeToDevice(t *testing.T) { require.Len(t, events, 2) for _, ev := range events { if ev.GetHeader().EventType == c2cEvents.EventType_ResourcesPublished { - publishedResources := test.ResourceLinksToResources(deviceID, test.TestDevsimResources) + publishedResources := test.ResourceLinksToResources(deviceID, test.GetAllBackendResourceLinks()) links := ev.GetData().(schema.ResourceLinks) resources := testSubscribeToDeviceDecodeResources(links) test.CheckProtobufs(t, publishedResources, resources, test.RequireToCheckFunc(require.Equal)) @@ -157,7 +157,7 @@ func TestRequestHandlerSubscribeToDevice(t *testing.T) { resources = testSubscribeToDeviceDecodeResources(links) var unpublishedSwitches []*commands.Resource for _, res := range resources { - if res.Href == test.TestResourceSwitchesInstanceHref(switchID2) { + if res.GetHref() == test.TestResourceSwitchesInstanceHref(switchID2) { unpublishedSwitches = append(unpublishedSwitches, &commands.Resource{ DeviceId: deviceID, Href: test.TestResourceSwitchesInstanceHref(switchID2), diff --git a/cloud2cloud-gateway/service/subscribeToDevices_test.go b/cloud2cloud-gateway/service/subscribeToDevices_test.go index 4b6fed496..c8cef2096 100644 --- a/cloud2cloud-gateway/service/subscribeToDevices_test.go +++ b/cloud2cloud-gateway/service/subscribeToDevices_test.go @@ -54,7 +54,7 @@ func TestRequestHandlerSubscribeToDevices(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -77,21 +77,21 @@ func TestRequestHandlerSubscribeToDevices(t *testing.T) { r := router.NewRouter() r.StrictSlash(true) r.HandleFunc(eventsURI, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - h, err := events.ParseEventHeader(r) - assert.NoError(t, err) + h, err2 := events.ParseEventHeader(r) + assert.NoError(t, err2) //nolint:testifylint defer func() { _ = r.Body.Close() }() assert.Equal(t, wantEventType, h.EventType) - buf, err := io.ReadAll(r.Body) - assert.NoError(t, err) + buf, err2 := io.ReadAll(r.Body) + assert.NoError(t, err2) //nolint:testifylint var v interface{} - err = json.Decode(buf, &v) - assert.NoError(t, err) - assert.Equal(t, v, wantEventContent) + err2 = json.Decode(buf, &v) + assert.NoError(t, err2) //nolint:testifylint + assert.Equal(t, wantEventContent, v) w.WriteHeader(http.StatusOK) - err = eventsServer.Close() - assert.NoError(t, err) + err2 = eventsServer.Close() + assert.NoError(t, err2) })).Methods("POST") _ = http.Serve(eventsServer, r) }() @@ -109,7 +109,7 @@ func TestRequestHandlerSubscribeToDevices(t *testing.T) { require.NoError(t, err) req := testHttp.NewHTTPRequest(http.MethodPost, uri, bytes.NewBuffer(data)).AuthToken(token).Accept(accept).Build(ctx, t) resp := testHttp.DoHTTPRequest(t, req) - assert.Equal(t, wantCode, resp.StatusCode) + require.Equal(t, wantCode, resp.StatusCode) defer func() { _ = resp.Body.Close() }() @@ -147,7 +147,7 @@ func TestRequestHandlerSubscribeToDevicesOffline(t *testing.T) { gwShutdown := coapgwTest.New(t, coapgwCfg) ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -170,21 +170,21 @@ func TestRequestHandlerSubscribeToDevicesOffline(t *testing.T) { r := router.NewRouter() r.StrictSlash(true) r.HandleFunc(eventsURI, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - h, err := events.ParseEventHeader(r) - assert.NoError(t, err) + h, err2 := events.ParseEventHeader(r) + assert.NoError(t, err2) //nolint:testifylint defer func() { _ = r.Body.Close() }() assert.Equal(t, wantEventType, h.EventType) - buf, err := io.ReadAll(r.Body) - assert.NoError(t, err) + buf, err2 := io.ReadAll(r.Body) + assert.NoError(t, err2) //nolint:testifylint var v interface{} - err = json.Decode(buf, &v) - assert.NoError(t, err) - assert.Equal(t, v, wantEventContent) + err2 = json.Decode(buf, &v) + assert.NoError(t, err2) //nolint:testifylint + assert.Equal(t, wantEventContent, v) w.WriteHeader(http.StatusOK) - err = eventsServer.Close() - assert.NoError(t, err) + err2 = eventsServer.Close() + assert.NoError(t, err2) })).Methods("POST") _ = http.Serve(eventsServer, r) }() @@ -202,7 +202,7 @@ func TestRequestHandlerSubscribeToDevicesOffline(t *testing.T) { require.NoError(t, err) req := testHttp.NewHTTPRequest(http.MethodPost, uri, bytes.NewBuffer(data)).AuthToken(oauthTest.GetDefaultAccessToken(t)).Accept(accept).Build(ctx, t) resp := testHttp.DoHTTPRequest(t, req) - assert.Equal(t, wantCode, resp.StatusCode) + require.Equal(t, wantCode, resp.StatusCode) defer func() { _ = resp.Body.Close() }() diff --git a/cloud2cloud-gateway/service/subscribeToResource_test.go b/cloud2cloud-gateway/service/subscribeToResource_test.go index ba1f60bef..b4cf32957 100644 --- a/cloud2cloud-gateway/service/subscribeToResource_test.go +++ b/cloud2cloud-gateway/service/subscribeToResource_test.go @@ -31,7 +31,7 @@ func TestRequestHandlerSubscribeToResource(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -78,7 +78,7 @@ func TestRequestHandlerSubscribeToResourceTokenTimeout(t *testing.T) { defer tearDown() c2cgwShutdown := c2cTest.SetUp(t) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/cloud2cloud-gateway/service/subscriptionManager.go b/cloud2cloud-gateway/service/subscriptionManager.go index eac5d2795..0cd36bac1 100644 --- a/cloud2cloud-gateway/service/subscriptionManager.go +++ b/cloud2cloud-gateway/service/subscriptionManager.go @@ -2,6 +2,7 @@ package service import ( "context" + "errors" "fmt" "sync" "time" @@ -73,12 +74,12 @@ func (s *SubscriptionManager) storeToSubs(sub store.Subscription) { func (s *SubscriptionManager) Connect(id string) error { subRaw, ok := s.subscriptions.Load(id) if !ok { - return fmt.Errorf("not found") + return errors.New("not found") } sub := subRaw.(*SubscriptionData) if sub.sub != nil { if !ok { - return fmt.Errorf("already connected") + return errors.New("already connected") } } ctx := s.ctx @@ -108,7 +109,7 @@ func (s *SubscriptionManager) Load(id string) (store.Subscription, bool) { } func cancelSubscription(ctx context.Context, emitEvent emitEventFunc, sub store.Subscription) error { - _, err := emitEvent(ctx, events.EventType_SubscriptionCanceled, sub, func(ctx context.Context) (uint64, error) { + _, err := emitEvent(ctx, events.EventType_SubscriptionCanceled, sub, func(_ context.Context) (uint64, error) { return sub.SequenceNumber, nil }, nil) return err @@ -117,7 +118,7 @@ func cancelSubscription(ctx context.Context, emitEvent emitEventFunc, sub store. func (s *SubscriptionManager) PullOut(ctx context.Context, id, href string) (store.Subscription, error) { subDataRaw, ok := s.subscriptions.PullOut(id) if !ok { - return store.Subscription{}, fmt.Errorf("not found") + return store.Subscription{}, errors.New("not found") } subData := subDataRaw.(*SubscriptionData) if href != "" && subData.data.Href != href { diff --git a/cloud2cloud-gateway/service/subscriptions.go b/cloud2cloud-gateway/service/subscriptions.go index 0d1605a11..80db85b77 100644 --- a/cloud2cloud-gateway/service/subscriptions.go +++ b/cloud2cloud-gateway/service/subscriptions.go @@ -221,7 +221,7 @@ func (s *SubscriptionData) createDevicesSubscription(ctx context.Context, emitEv } for _, e := range eventTypes { - _, err := emitEvent(ctx, e, s.Data(), s.IncrementSequenceNumber, makeDevicesRepresentation([]string{})) + _, err = emitEvent(ctx, e, s.Data(), s.IncrementSequenceNumber, makeDevicesRepresentation([]string{})) if err != nil { return nil, createSubDevicesError(err) } @@ -339,7 +339,7 @@ func (s *SubscriptionData) createDeviceSubscription(ctx context.Context, emitEve func (s *SubscriptionData) Connect(ctx context.Context, emitEvent emitEventFunc, deleteSub func(ctx context.Context, subID, href string) (store.Subscription, error)) error { if s.Subscription() != nil { - return fmt.Errorf("is already connected") + return errors.New("is already connected") } h := closeEventHandler{ ctx: ctx, @@ -361,20 +361,19 @@ func (s *SubscriptionData) Connect(ctx context.Context, emitEvent emitEventFunc, } sub, err := createSubscriptionFunc(ctx, emitEvent, &h) - if err != nil { - if status.Convert(err).Code() == codes.Unauthenticated { - subToCancel, errSub := deleteSub(ctx, s.data.ID, "") - if errSub == nil { - if err2 := cancelSubscription(ctx, emitEvent, subToCancel); err2 != nil { - log.Errorf("cannot cancel subscription %v: %w", subToCancel.ID, err2) - } + if err == nil { + s.Store(sub) + return nil + } + if status.Convert(err).Code() == codes.Unauthenticated { + subToCancel, errSub := deleteSub(ctx, s.data.ID, "") + if errSub == nil { + if err2 := cancelSubscription(ctx, emitEvent, subToCancel); err2 != nil { + log.Errorf("cannot cancel subscription %v: %w", subToCancel.ID, err2) } } - return err } - - s.Store(sub) - return nil + return err } func (s *SubscriptionData) IncrementSequenceNumber(ctx context.Context) (uint64, error) { diff --git a/cloud2cloud-gateway/service/updateResource.go b/cloud2cloud-gateway/service/updateResource.go index 9e4703487..60cb21109 100644 --- a/cloud2cloud-gateway/service/updateResource.go +++ b/cloud2cloud-gateway/service/updateResource.go @@ -47,7 +47,7 @@ func statusToHttpStatus(status commands.Status) int { func sendResponse(w http.ResponseWriter, processed *raEvents.ResourceUpdated) (int, error) { statusCode := statusToHttpStatus(processed.GetStatus()) - if processed.Content != nil { + if processed.GetContent() != nil { var content interface{} err := unmarshalContent(processed.GetContent(), &content) if err != nil { diff --git a/cloud2cloud-gateway/store/mongodb/store.go b/cloud2cloud-gateway/store/mongodb/store.go index 6628c2429..f7b80bf01 100644 --- a/cloud2cloud-gateway/store/mongodb/store.go +++ b/cloud2cloud-gateway/store/mongodb/store.go @@ -3,6 +3,7 @@ package mongodb import ( "context" "crypto/tls" + "errors" "fmt" pkgMongo "github.com/plgd-dev/hub/v2/pkg/mongodb" @@ -22,15 +23,15 @@ func NewStore(ctx context.Context, cfg pkgMongo.Config, tls *tls.Config, tracerP if err != nil { return nil, err } - s.SetOnClear(func(c context.Context) error { - return s.DropCollection(ctx, subscriptionsCName) + s.SetOnClear(func(clearCtx context.Context) error { + return s.DropCollection(clearCtx, subscriptionsCName) }) return &Store{s}, nil } func incrementSubscriptionSequenceNumber(ctx context.Context, col *mongo.Collection, subscriptionID string) (uint64, error) { if subscriptionID == "" { - return 0, fmt.Errorf("cannot increment sequence number: invalid subscriptionID") + return 0, errors.New("cannot increment sequence number: invalid subscriptionID") } var res bson.M diff --git a/cloud2cloud-gateway/store/mongodb/subscription.go b/cloud2cloud-gateway/store/mongodb/subscription.go index 2d8513960..8649063ca 100644 --- a/cloud2cloud-gateway/store/mongodb/subscription.go +++ b/cloud2cloud-gateway/store/mongodb/subscription.go @@ -75,49 +75,49 @@ func makeDBSub(sub store.Subscription) DBSub { func validateDevicesSubscription(sub store.Subscription) error { if sub.DeviceID != "" { - return fmt.Errorf("invalid DeviceID for devices subscription type") + return errors.New("invalid DeviceID for devices subscription type") } if sub.Href != "" { - return fmt.Errorf("invalid Href for devices subscription type") + return errors.New("invalid Href for devices subscription type") } return nil } func validateDeviceSubscription(sub store.Subscription) error { if sub.DeviceID == "" { - return fmt.Errorf("invalid DeviceID for device subscription type") + return errors.New("invalid DeviceID for device subscription type") } if sub.Href != "" { - return fmt.Errorf("invalid Href for device subscription type") + return errors.New("invalid Href for device subscription type") } return nil } func validateResourceSubscription(sub store.Subscription) error { if sub.DeviceID == "" { - return fmt.Errorf("invalid DeviceID for resource subscription type") + return errors.New("invalid DeviceID for resource subscription type") } if sub.Href == "" { - return fmt.Errorf("invalid Href for resource subscription type") + return errors.New("invalid Href for resource subscription type") } return nil } func validateSubscription(sub store.Subscription) error { if sub.ID == "" { - return fmt.Errorf("invalid ID") + return errors.New("invalid ID") } if len(sub.EventTypes) == 0 { - return fmt.Errorf("invalid EventTypes") + return errors.New("invalid EventTypes") } if sub.URL == "" { - return fmt.Errorf("invalid URL") + return errors.New("invalid URL") } if sub.SigningSecret == "" { - return fmt.Errorf("invalid SigningSecret") + return errors.New("invalid SigningSecret") } if sub.AccessToken == "" { - return fmt.Errorf("invalid AccessToken") + return errors.New("invalid AccessToken") } switch sub.Type { @@ -156,7 +156,7 @@ func (s *Store) IncrementSubscriptionSequenceNumber(ctx context.Context, subscri func (s *Store) SetInitialized(ctx context.Context, subscriptionID string) error { col := s.Collection(subscriptionsCName) if subscriptionID == "" { - return fmt.Errorf("cannot set initialized: invalid subscriptionId") + return errors.New("cannot set initialized: invalid subscriptionId") } opts := &options.UpdateOptions{} @@ -193,7 +193,7 @@ func (s *Store) LoadSubscriptions(ctx context.Context, query store.SubscriptionQ case query.Type == "" && query.DeviceID == "" && query.Href == "": iter, err = col.Find(ctx, bson.M{}) case query.Type == "": - return fmt.Errorf("invalid Type") + return errors.New("invalid Type") case query.DeviceID != "" && query.Href != "": q := bson.M{ typeKey: query.Type, diff --git a/cloud2cloud-gateway/test/events.go b/cloud2cloud-gateway/test/events.go index c59398a91..aad72ac83 100644 --- a/cloud2cloud-gateway/test/events.go +++ b/cloud2cloud-gateway/test/events.go @@ -57,25 +57,29 @@ func WaitForEvents(ch EventChan, timeout time.Duration) []Event { return events } -func DecodeEvent(t *testing.T, etype events.EventType, data []byte) interface{} { +func decodeEvent(etype events.EventType, data []byte) (interface{}, error) { switch etype { case events.EventType_ResourcesPublished: fallthrough case events.EventType_ResourcesUnpublished: var links schema.ResourceLinks err := json.Decode(data, &links) - assert.NoError(t, err) - return links + if err != nil { + return nil, err + } + return links, nil case events.EventType_ResourceChanged: var colContent []map[interface{}]interface{} err := json.Decode(data, &colContent) if err == nil { - return colContent + return colContent, nil } var content map[interface{}]interface{} err = json.Decode(data, &content) - assert.NoError(t, err) - return content + if err != nil { + return nil, err + } + return content, nil case events.EventType_DevicesRegistered: fallthrough case events.EventType_DevicesUnregistered: @@ -85,11 +89,13 @@ func DecodeEvent(t *testing.T, etype events.EventType, data []byte) interface{} case events.EventType_DevicesOffline: var devices []map[string]string err := json.Decode(data, &devices) - assert.NoError(t, err) - return devices + if err != nil { + return nil, err + } + return devices, nil } - return nil + return nil, nil } func NewEventsServer(t *testing.T, uri string) *EventsServer { @@ -134,14 +140,15 @@ func (s *EventsServer) Run(t *testing.T) EventChan { r.StrictSlash(true) r.HandleFunc(s.uri, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { h, err := events.ParseEventHeader(r) - assert.NoError(t, err) + assert.NoError(t, err) //nolint:testifylint defer func() { _ = r.Body.Close() }() buf, err := io.ReadAll(r.Body) - assert.NoError(t, err) + assert.NoError(t, err) //nolint:testifylint - data := DecodeEvent(t, h.EventType, buf) + data, err := decodeEvent(h.EventType, buf) + assert.NoError(t, err) //nolint:testifylint dataChan <- Event{ header: h, data: data, diff --git a/coap-gateway/coapconv/coapconv.go b/coap-gateway/coapconv/coapconv.go index a00296795..7ff762d5c 100644 --- a/coap-gateway/coapconv/coapconv.go +++ b/coap-gateway/coapconv/coapconv.go @@ -9,6 +9,8 @@ import ( "strings" "github.com/google/uuid" + "github.com/plgd-dev/device/v2/pkg/codec/cbor" + "github.com/plgd-dev/device/v2/pkg/codec/json" "github.com/plgd-dev/device/v2/schema/interfaces" "github.com/plgd-dev/device/v2/schema/resources" "github.com/plgd-dev/go-coap/v3/message" @@ -18,7 +20,6 @@ import ( "github.com/plgd-dev/hub/v2/coap-gateway/uri" "github.com/plgd-dev/hub/v2/resource-aggregate/commands" "github.com/plgd-dev/hub/v2/resource-aggregate/events" - "github.com/plgd-dev/kit/v2/codec/cbor" ) func StatusToCoapCode(status commands.Status, operation Operation) codes.Code { @@ -110,7 +111,7 @@ func newCoapResourceCreateOrUpdateRequest(ctx context.Context, messagePool *pool return nil, fmt.Errorf("invalid content type for request: %w", err) } if content == nil { - return nil, fmt.Errorf("invalid content for request") + return nil, errors.New("invalid content for request") } token, err := message.GetToken() if err != nil { @@ -310,14 +311,20 @@ func NewConfirmResourceDeleteRequest(resourceID *commands.ResourceId, correlatio } } -func NewNotifyResourceChangedRequest(resourceID *commands.ResourceId, connectionID string, req *pool.Message) *commands.NotifyResourceChangedRequest { +func NewNotifyResourceChangedRequest(resourceID *commands.ResourceId, resourceTypes []string, connectionID string, req *pool.Message) *commands.NotifyResourceChangedRequest { content := NewContent(req.Options(), req.Body()) metadata := NewCommandMetadata(req.Sequence(), connectionID) + rtFromBody := tryToGetResourceTypesFromContent(content.GetCoapContentFormat(), content.GetData()) + if len(rtFromBody) > 0 { + resourceTypes = rtFromBody + } + return &commands.NotifyResourceChangedRequest{ ResourceId: resourceID, Content: content, CommandMetadata: metadata, + ResourceTypes: resourceTypes, Status: CoapCodeToStatus(req.Code(), Update), Etag: getETagFromMessage(req), } @@ -344,6 +351,30 @@ func filterOutEmptyResource(resource resources.BatchRepresentation) (isEmpty boo return isEmpty, false } +type ct struct { + ResourceTypes []string `json:"rt"` +} + +func tryToGetResourceTypesFromContent(contentFormat int32, content []byte) []string { + if len(content) == 0 { + return nil + } + decode := func([]byte, interface{}) error { + return errors.New("unsupported") + } + switch contentFormat { + case int32(message.AppOcfCbor), int32(message.AppCBOR): + decode = cbor.Decode + case int32(message.AppJSON): + decode = json.Decode + } + var c ct + if err := decode(content, &c); err == nil { + return c.ResourceTypes + } + return nil +} + func NewNotifyResourceChangedRequestsFromBatchResourceDiscovery(deviceID, connectionID string, req *pool.Message) ([]*commands.NotifyResourceChangedRequest, error) { data, contentFormat := GetContentData(req.Options(), req.Body()) metadata := NewCommandMetadata(req.Sequence(), connectionID) @@ -378,6 +409,10 @@ func NewNotifyResourceChangedRequestsFromBatchResourceDiscovery(deviceID, connec data = nil code = commands.Status_NOT_FOUND } + resourceTypes := r.ResourceTypes + if len(resourceTypes) == 0 { + resourceTypes = tryToGetResourceTypesFromContent(contentFormat, r.Content) + } resourceChangedReq := &commands.NotifyResourceChangedRequest{ ResourceId: commands.NewResourceID(deviceID, r.Href()), Content: &commands.Content{ @@ -388,6 +423,7 @@ func NewNotifyResourceChangedRequestsFromBatchResourceDiscovery(deviceID, connec CommandMetadata: metadata, Status: code, Etag: r.ETag, + ResourceTypes: resourceTypes, } if len(etag) > 0 && bytes.Equal(etag, r.ETag) { latestETagResource = resourceChangedReq @@ -416,8 +452,8 @@ func NewUpdateResourceRequest(resourceID *commands.ResourceId, req *mux.Message, return &commands.UpdateResourceRequest{ ResourceId: resourceID, Content: &commands.Content{ - Data: content.Data, - ContentType: content.ContentType, + Data: content.GetData(), + ContentType: content.GetContentType(), }, ResourceInterface: resourceInterface, CommandMetadata: metadata, diff --git a/coap-gateway/service/auth.go b/coap-gateway/service/auth.go index 407beea6e..72003e5b5 100644 --- a/coap-gateway/service/auth.go +++ b/coap-gateway/service/auth.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "crypto/x509" + "errors" "fmt" "time" @@ -20,14 +21,14 @@ import ( type Interceptor = func(ctx context.Context, code codes.Code, path string) (context.Context, error) func newAuthInterceptor() Interceptor { - return func(ctx context.Context, code codes.Code, path string) (context.Context, error) { + return func(ctx context.Context, _ codes.Code, path string) (context.Context, error) { switch path { case uri.RefreshToken, uri.SignUp, uri.SignIn, plgdtime.ResourceURI: return ctx, nil } e := ctx.Value(&authCtxKey) if e == nil { - return ctx, fmt.Errorf("invalid authorization context") + return ctx, errors.New("invalid authorization context") } authCtx := e.(*authorizationContext) err := authCtx.IsValid() @@ -60,7 +61,7 @@ func (s *Service) verifyDeviceID(tlsDeviceID string, claim pkgJwt.Claims) (strin return jwtDeviceID, nil } if tlsDeviceID == "" { - return "", fmt.Errorf("certificate of device doesn't contain device id") + return "", errors.New("certificate of device doesn't contain device id") } if s.config.APIs.COAP.Authorization.DeviceIDClaim != "" && jwtDeviceID != tlsDeviceID { return "", fmt.Errorf("access token issued to the device ('%v') used by the different device ('%v')", jwtDeviceID, tlsDeviceID) @@ -81,7 +82,7 @@ func (s *Service) VerifyAndResolveDeviceID(tlsDeviceID, paramDeviceID string, cl func verifyChain(chain []*x509.Certificate, capool *x509.CertPool, identityPropertiesRequired bool) error { if len(chain) == 0 { - return fmt.Errorf("certificate chain is empty") + return errors.New("certificate chain is empty") } certificate := chain[0] intermediateCAPool := x509.NewCertPool() @@ -109,10 +110,10 @@ func verifyChain(chain []*x509.Certificate, capool *x509.CertPool, identityPrope } } if !ekuHasClient { - return fmt.Errorf("the extended key usage field in the device certificate does not contain client authentication") + return errors.New("the extended key usage field in the device certificate does not contain client authentication") } if !ekuHasServer { - return fmt.Errorf("the extended key usage field in the device certificate does not contain server authentication") + return errors.New("the extended key usage field in the device certificate does not contain server authentication") } if !identityPropertiesRequired { return nil @@ -130,18 +131,18 @@ func MakeGetConfigForClient(tlsCfg *tls.Config, identityPropertiesRequired bool) MinVersion: tlsCfg.MinVersion, ClientAuth: tlsCfg.ClientAuth, ClientCAs: tlsCfg.ClientCAs, - VerifyPeerCertificate: func(rawCerts [][]byte, chains [][]*x509.Certificate) error { - var errors *multierror.Error + VerifyPeerCertificate: func(_ [][]byte, chains [][]*x509.Certificate) error { + var errs *multierror.Error for _, chain := range chains { err := verifyChain(chain, tlsCfg.ClientCAs, identityPropertiesRequired) if err == nil { return nil } - errors = multierror.Append(errors, err) + errs = multierror.Append(errs, err) } - err := fmt.Errorf("empty chains") - if errors.ErrorOrNil() != nil { - err = errors + err := errors.New("empty chains") + if errs.ErrorOrNil() != nil { + err = errs } return pkgX509.NewError(chains, err) }, diff --git a/coap-gateway/service/clientCreateHandler.go b/coap-gateway/service/clientCreateHandler.go index b61c21d28..1cd264066 100644 --- a/coap-gateway/service/clientCreateHandler.go +++ b/coap-gateway/service/clientCreateHandler.go @@ -37,14 +37,14 @@ func clientCreateHandler(req *mux.Message, client *session) (*pool.Message, erro code = coapconv.GrpcErr2CoapCode(err, coapconv.Create) return nil, statusErrorf(code, errFmtCreateResource, fmt.Sprintf(" /%v%v", deviceID, href), err) } - if content == nil || len(content.Data) == 0 { + if len(content.GetData()) == 0 { return client.createResponse(code, req.Token(), coapMessage.TextPlain, nil), nil } - mediaType, err := coapconv.MakeMediaType(-1, content.ContentType) + mediaType, err := coapconv.MakeMediaType(-1, content.GetContentType()) if err != nil { return nil, statusErrorf(coapCodes.BadRequest, "cannot encode response for create resource %v: %w", fmt.Sprintf(" /%v%v", deviceID, href), err) } - return client.createResponse(code, req.Token(), mediaType, content.Data), nil + return client.createResponse(code, req.Token(), mediaType, content.GetData()), nil } func clientCreateDeviceHandler(req *mux.Message, client *session, deviceID, href string) (*commands.Content, error) { diff --git a/coap-gateway/service/clientDeleteHandler.go b/coap-gateway/service/clientDeleteHandler.go index 1f346c765..cc5ed7488 100644 --- a/coap-gateway/service/clientDeleteHandler.go +++ b/coap-gateway/service/clientDeleteHandler.go @@ -38,14 +38,14 @@ func clientDeleteHandler(req *mux.Message, client *session) (*pool.Message, erro return nil, statusErrorf(code, errFmtDeleteResource, fmt.Sprintf(" /%v%v", deviceID, href), err) } - if content == nil || len(content.Data) == 0 { + if len(content.GetData()) == 0 { return client.createResponse(code, req.Token(), coapMessage.TextPlain, nil), nil } - mediaType, err := coapconv.MakeMediaType(-1, content.ContentType) + mediaType, err := coapconv.MakeMediaType(-1, content.GetContentType()) if err != nil { return nil, statusErrorf(code, errFmtDeleteResource, fmt.Sprintf(" /%v%v", deviceID, href), err) } - return client.createResponse(code, req.Token(), mediaType, content.Data), nil + return client.createResponse(code, req.Token(), mediaType, content.GetData()), nil } func clientDeleteResourceHandler(req *mux.Message, client *session, deviceID, href string) (*commands.Content, error) { diff --git a/coap-gateway/service/clientObserveHandler.go b/coap-gateway/service/clientObserveHandler.go index 2221886d5..00f8d4813 100644 --- a/coap-gateway/service/clientObserveHandler.go +++ b/coap-gateway/service/clientObserveHandler.go @@ -178,17 +178,14 @@ func (s *resourceSubscription) Init(ctx context.Context) error { var d *events.ResourceChanged for { - resource, err := client.Recv() - if errors.Is(err, io.EOF) { + resource, errR := client.Recv() + if errors.Is(errR, io.EOF) { break } - if err != nil { - return err + if errR != nil { + return errR } - d = resource.Data - } - if err != nil { - return err + d = resource.GetData() } authCtx, err := s.client.GetAuthorizationContext() if err != nil { @@ -254,7 +251,7 @@ func startResourceObservation(req *mux.Message, client *session, authCtx *author return nil, statusErrorf(coapconv.GrpcErr2CoapCode(err, coapconv.Retrieve), "%w", getStartObserveResourceErr(deviceID, href, err)) } if !ok { - return nil, statusErrorf(coapCodes.Unauthorized, "%w", getStartObserveResourceErr(deviceID, href, fmt.Errorf("unauthorized access"))) + return nil, statusErrorf(coapCodes.Unauthorized, "%w", getStartObserveResourceErr(deviceID, href, errors.New("unauthorized access"))) } token := req.Token().String() sub := newResourceSubscription(req, client, authCtx, deviceID, href) @@ -294,7 +291,7 @@ func stopResourceObservation(req *mux.Message, client *session, deviceID, href s return nil, statusErrorf(coapCodes.BadRequest, "%w", getStopObserveResourceErr(deviceID, href, err)) } if !canceled { - return nil, statusErrorf(coapCodes.BadRequest, "%w", getStopObserveResourceErr(deviceID, href, fmt.Errorf("subscription not found"))) + return nil, statusErrorf(coapCodes.BadRequest, "%w", getStopObserveResourceErr(deviceID, href, errors.New("subscription not found"))) } return CreateResourceContentToObserver(client, nil, 1, req.Token()) } @@ -306,7 +303,7 @@ func clientResetObservationHandler(req *mux.Message, client *session) (*pool.Mes return nil, statusErrorf(coapCodes.BadRequest, "%w", fmt.Errorf("cannot reset resource observation: %w", err)) } if !canceled { - return nil, statusErrorf(coapCodes.BadRequest, "%w", fmt.Errorf("cannot reset resource observation: not found")) + return nil, statusErrorf(coapCodes.BadRequest, "%w", errors.New("cannot reset resource observation: not found")) } // reset does not send response return nil, nil diff --git a/coap-gateway/service/clientObserveHandler_test.go b/coap-gateway/service/clientObserveHandler_test.go index aa8fa146d..1c67158fe 100644 --- a/coap-gateway/service/clientObserveHandler_test.go +++ b/coap-gateway/service/clientObserveHandler_test.go @@ -145,7 +145,7 @@ func TestClientObserveHandlerCloseObservation(t *testing.T) { require.NoError(t, err) ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/coap-gateway/service/clientObserver.go b/coap-gateway/service/clientObserver.go index 07df6b2d9..61a141d9a 100644 --- a/coap-gateway/service/clientObserver.go +++ b/coap-gateway/service/clientObserver.go @@ -2,6 +2,7 @@ package service import ( "context" + "errors" "fmt" "github.com/plgd-dev/hub/v2/coap-gateway/service/observation" @@ -30,7 +31,7 @@ func (c *session) getDeviceObserver(ctx context.Context) (*observation.DeviceObs } deviceObserver, ok := v.(*observation.DeviceObserver) if !ok { - return nil, false, getError(fmt.Errorf("invalid future value")) + return nil, false, getError(errors.New("invalid future value")) } return deviceObserver, true, nil } @@ -59,8 +60,7 @@ func (c *session) replaceDeviceObserverWithDeviceTwin(ctx context.Context, twinE observationType = obs.GetObservationType() } if deviceID == "" { - err := fmt.Errorf("cannot replace device observer: invalid device id") - return false, err + return false, errors.New("cannot replace device observer: invalid device id") } twinEnabled = twinEnabled || twinForceSynchronization if !twinForceSynchronization && prevTwinEnabled == twinEnabled { @@ -68,8 +68,8 @@ func (c *session) replaceDeviceObserverWithDeviceTwin(ctx context.Context, twinE } deviceObserverFuture, setDeviceObserver := future.New() oldDeviceObserver := c.replaceDeviceObserver(deviceObserverFuture) - if err := cleanDeviceObserver(ctx, oldDeviceObserver); err != nil { - c.Errorf("failed to close replaced device observer: %w", err) + if errD := cleanDeviceObserver(ctx, oldDeviceObserver); errD != nil { + c.Errorf("failed to close replaced device observer: %w", errD) } deviceObserver, err := observation.NewDeviceObserver(c.Context(), deviceID, c, c, c, @@ -98,7 +98,7 @@ func toDeviceObserver(ctx context.Context, devObsFut *future.Future) (*observati } deviceObserver, ok := v.(*observation.DeviceObserver) if !ok { - return nil, fmt.Errorf("invalid future value") + return nil, errors.New("invalid future value") } return deviceObserver, nil } diff --git a/coap-gateway/service/clientRetrieveHandler.go b/coap-gateway/service/clientRetrieveHandler.go index 99b72f21a..c00f265e0 100644 --- a/coap-gateway/service/clientRetrieveHandler.go +++ b/coap-gateway/service/clientRetrieveHandler.go @@ -57,14 +57,14 @@ func clientRetrieveHandler(req *mux.Message, client *session) (*pool.Message, er } } - if content == nil || len(content.Data) == 0 { + if len(content.GetData()) == 0 { return client.createResponse(code, req.Token(), coapMessage.TextPlain, nil), nil } - mediaType, err := coapconv.MakeMediaType(-1, content.ContentType) + mediaType, err := coapconv.MakeMediaType(-1, content.GetContentType()) if err != nil { return nil, statusErrorf(code, errFmtRetrieveResource, fmt.Sprintf(" /%v%v", deviceID, href), err) } - return client.createResponse(code, req.Token(), mediaType, content.Data), nil + return client.createResponse(code, req.Token(), mediaType, content.GetData()), nil } func clientRetrieveFromResourceTwinHandler(ctx context.Context, client *session, deviceID, href string, etag []byte) (*commands.Content, coapCodes.Code, error) { @@ -91,14 +91,14 @@ func clientRetrieveFromResourceTwinHandler(ctx context.Context, client *session, if err != nil { return nil, coapconv.GrpcErr2CoapCode(err, coapconv.Retrieve), err } - if resourceValue.GetData().GetResourceId().GetDeviceId() == deviceID && resourceValue.GetData().GetResourceId().GetHref() == href && resourceValue.GetData().Content != nil { + if resourceValue.GetData().GetResourceId().GetDeviceId() == deviceID && resourceValue.GetData().GetResourceId().GetHref() == href && resourceValue.GetData().GetContent() != nil { if etag != nil && bytes.Equal(etag, resourceValue.GetData().GetEtag()) { return nil, coapCodes.Valid, nil } - return resourceValue.GetData().Content, coapCodes.Content, nil + return resourceValue.GetData().GetContent(), coapCodes.Content, nil } } - return nil, coapCodes.NotFound, fmt.Errorf("not found") + return nil, coapCodes.NotFound, errors.New("not found") } func clientRetrieveFromDeviceHandler(req *mux.Message, client *session, deviceID, href string) (*commands.Content, coapCodes.Code, error) { diff --git a/coap-gateway/service/clientRetrieveHandler_test.go b/coap-gateway/service/clientRetrieveHandler_test.go index 1484fed73..150d85fae 100644 --- a/coap-gateway/service/clientRetrieveHandler_test.go +++ b/coap-gateway/service/clientRetrieveHandler_test.go @@ -106,7 +106,7 @@ func TestClientRetrieveHandler(t *testing.T) { req.SetOptionString(message.URIQuery, tt.args.query) } resp, err := co.Do(req) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, tt.wantsCode.String(), resp.Code().String()) }) } diff --git a/coap-gateway/service/clientUpdateHandler.go b/coap-gateway/service/clientUpdateHandler.go index 4d76f09db..c3b6564ba 100644 --- a/coap-gateway/service/clientUpdateHandler.go +++ b/coap-gateway/service/clientUpdateHandler.go @@ -47,14 +47,14 @@ func clientUpdateHandler(req *mux.Message, client *session) (*pool.Message, erro code = coapconv.GrpcErr2CoapCode(err, coapconv.Update) return nil, statusErrorf(code, errFmtUpdateResource, fmt.Sprintf(" /%v%v", deviceID, href), err) } - if content == nil || len(content.Data) == 0 { + if len(content.GetData()) == 0 { return client.createResponse(code, req.Token(), coapMessage.TextPlain, nil), nil } - mediaType, err := coapconv.MakeMediaType(-1, content.ContentType) + mediaType, err := coapconv.MakeMediaType(-1, content.GetContentType()) if err != nil { return nil, statusErrorf(code, "cannot encode response for update resource /%v%v: %w", deviceID, href, err) } - return client.createResponse(code, req.Token(), mediaType, content.Data), nil + return client.createResponse(code, req.Token(), mediaType, content.GetData()), nil } func clientUpdateDeviceHandler(req *mux.Message, client *session, deviceID, href string) (*commands.Content, error) { diff --git a/coap-gateway/service/config.go b/coap-gateway/service/config.go index e49aa1e61..2f488fa65 100644 --- a/coap-gateway/service/config.go +++ b/coap-gateway/service/config.go @@ -1,6 +1,7 @@ package service import ( + "errors" "fmt" "time" @@ -174,7 +175,7 @@ func (c *COAPConfigMarshalerUnmarshaler) Validate() error { return err } if !c.InjectedCOAPConfig.TLSConfig.IdentityPropertiesRequired && c.Authorization.DeviceIDClaim != "" { - return fmt.Errorf("tls.identityPropertiesRequired('%v') - %w", c.InjectedCOAPConfig.TLSConfig.IdentityPropertiesRequired, fmt.Errorf("combination with authorization.deviceIDClaim is not supported")) + return fmt.Errorf("tls.identityPropertiesRequired('%v') - %w", c.InjectedCOAPConfig.TLSConfig.IdentityPropertiesRequired, errors.New("combination with authorization.deviceIDClaim is not supported")) } return nil } diff --git a/coap-gateway/service/devicesStatusUpdater.go b/coap-gateway/service/devicesStatusUpdater.go index 6077ab19f..e5f1875ce 100644 --- a/coap-gateway/service/devicesStatusUpdater.go +++ b/coap-gateway/service/devicesStatusUpdater.go @@ -40,10 +40,11 @@ func (u *devicesStatusUpdater) updateOnlineStatus(ctx context.Context, client *s DeviceId: authCtx.GetDeviceID(), Update: &commands.UpdateDeviceMetadataRequest_Connection{ Connection: &commands.Connection{ - Status: commands.Connection_ONLINE, - ConnectedAt: pkgTime.UnixNano(connectedAt), - Protocol: client.GetApplicationProtocol(), - ServiceId: u.serviceInstanceID.String(), + Status: commands.Connection_ONLINE, + ConnectedAt: pkgTime.UnixNano(connectedAt), + Protocol: client.GetApplicationProtocol(), + ServiceId: u.serviceInstanceID.String(), + LocalEndpoints: client.getLocalEndpoints(), }, }, CommandMetadata: &commands.CommandMetadata{ diff --git a/coap-gateway/service/devicesStatusUpdater_test.go b/coap-gateway/service/devicesStatusUpdater_test.go index 4948dcc90..02eba2127 100644 --- a/coap-gateway/service/devicesStatusUpdater_test.go +++ b/coap-gateway/service/devicesStatusUpdater_test.go @@ -1,5 +1,5 @@ -//go:build test -// +build test +//go:build test || device_integration +// +build test device_integration package service_test @@ -18,21 +18,22 @@ import ( "github.com/plgd-dev/hub/v2/resource-aggregate/commands" test "github.com/plgd-dev/hub/v2/test" "github.com/plgd-dev/hub/v2/test/config" + "github.com/plgd-dev/hub/v2/test/device" oauthService "github.com/plgd-dev/hub/v2/test/oauth-server/service" oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test" - "github.com/plgd-dev/hub/v2/test/service" + testService "github.com/plgd-dev/hub/v2/test/service" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/grpc/credentials" ) -func onboardDeviceAndGetDevice(ctx context.Context, t *testing.T, deviceID string, oauthCfg oauthService.Config, coapCfg coapService.Config) (*pb.Device, time.Time /*startOnboard*/, time.Duration /*delta*/) { - tearDown := service.SetUp(ctx, t, service.WithOAuthConfig(oauthCfg), service.WithCOAPGWConfig(coapCfg)) - defer tearDown() +func onboardDeviceAndGetDevice(ctx context.Context, t *testing.T, device device.Device, oauthCfg oauthService.Config, coapCfg coapService.Config, wait time.Duration) (*pb.Device, time.Time /*startOnboard*/, time.Duration /*delta*/) { + oauthShutdown := oauthTest.New(t, oauthCfg) + servicesTeardown := testService.SetUpServices(context.Background(), t, testService.SetUpServicesCertificateAuthority|testService.SetUpServicesId|testService.SetUpServicesResourceAggregate|testService.SetUpServicesResourceDirectory|testService.SetUpServicesCoapGateway|testService.SetUpServicesGrpcGateway, testService.WithCOAPGWConfig(coapCfg)) ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -42,12 +43,17 @@ func onboardDeviceAndGetDevice(ctx context.Context, t *testing.T, deviceID strin c := pb.NewGrpcGatewayClient(conn) startOnboard := time.Now() - _, shutdownDevSim := test.OnboardDevSim(ctx, t, c, deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) - defer shutdownDevSim() + shutdownDevSim := test.OnboardDevice(ctx, t, c, device, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, device.GetDefaultResources()) deltaOnboard := time.Since(startOnboard) / 2 + // stop oauth server to don't allow refresh token during sleep + oauthShutdown() + // for update resource-directory cache - time.Sleep(time.Second) + time.Sleep(wait) + oauthShutdown = oauthTest.New(t, oauthCfg) + ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) + defer oauthShutdown() client, err := c.GetDevices(ctx, &pb.GetDevicesRequest{}) require.NoError(t, err) @@ -58,16 +64,18 @@ func onboardDeviceAndGetDevice(ctx context.Context, t *testing.T, deviceID strin break } require.NoError(t, err) - assert.NotEmpty(t, dev.ProtocolIndependentId) + assert.NotEmpty(t, dev.GetProtocolIndependentId()) dev.ProtocolIndependentId = "" devices = append(devices, dev) } require.Len(t, devices, 1) + shutdownDevSim() + servicesTeardown() return devices[0], startOnboard, deltaOnboard } -func TestDevicesStatusUpdaterDisabledAndDeviceAccessTokenHasNoExpiration(t *testing.T) { - deviceID := test.MustFindDeviceByName(test.TestDeviceName) +func TestDevicesStatusAccessTokenHasNoExpiration(t *testing.T) { + d := test.MustFindTestDevice() ctx, cancel := context.WithTimeout(context.Background(), config.TEST_TIMEOUT) defer cancel() @@ -75,13 +83,13 @@ func TestDevicesStatusUpdaterDisabledAndDeviceAccessTokenHasNoExpiration(t *test oauthCfg.OAuthSigner.Clients.Find(config.OAUTH_MANAGER_CLIENT_ID).AccessTokenLifetime = 0 coapCfg := coapgwTest.MakeConfig(t) - device, _, _ := onboardDeviceAndGetDevice(ctx, t, deviceID, oauthCfg, coapCfg) + device, _, _ := onboardDeviceAndGetDevice(ctx, t, d, oauthCfg, coapCfg, time.Second) assert.Equal(t, commands.Connection_ONLINE, device.Metadata.Connection.Status) } -func TestDevicesStatusUpdaterDisabledAndDeviceAccessTokenHasExpiration(t *testing.T) { - deviceID := test.MustFindDeviceByName(test.TestDeviceName) +func TestDevicesStatusAccessTokenHasExpiration(t *testing.T) { + d := test.MustFindTestDevice() ctx, cancel := context.WithTimeout(context.Background(), config.TEST_TIMEOUT) defer cancel() @@ -89,8 +97,25 @@ func TestDevicesStatusUpdaterDisabledAndDeviceAccessTokenHasExpiration(t *testin accessTokenLifetime := time.Second * 10 oauthCfg.OAuthSigner.Clients.Find(config.OAUTH_MANAGER_CLIENT_ID).AccessTokenLifetime = accessTokenLifetime coapCfg := coapgwTest.MakeConfig(t) + coapCfg.APIs.COAP.OwnerCacheExpiration = time.Second - device, _, _ := onboardDeviceAndGetDevice(ctx, t, deviceID, oauthCfg, coapCfg) + device, _, _ := onboardDeviceAndGetDevice(ctx, t, d, oauthCfg, coapCfg, time.Second) assert.Equal(t, commands.Connection_ONLINE, device.Metadata.Connection.Status) } + +func TestDevicesStatusAccessTokenHasExpirationAndTokenWillExpire(t *testing.T) { + d := test.MustFindTestDevice() + ctx, cancel := context.WithTimeout(context.Background(), config.TEST_TIMEOUT) + defer cancel() + + oauthCfg := oauthTest.MakeConfig(t) + accessTokenLifetime := time.Second * 10 + oauthCfg.OAuthSigner.Clients.Find(config.OAUTH_MANAGER_CLIENT_ID).AccessTokenLifetime = accessTokenLifetime + coapCfg := coapgwTest.MakeConfig(t) + coapCfg.APIs.COAP.OwnerCacheExpiration = time.Second + + device, _, _ := onboardDeviceAndGetDevice(ctx, t, d, oauthCfg, coapCfg, accessTokenLifetime) + + assert.Equal(t, commands.Connection_OFFLINE, device.Metadata.Connection.Status) +} diff --git a/coap-gateway/service/exchangeCache.go b/coap-gateway/service/exchangeCache.go index d0d758e39..b798d44b8 100644 --- a/coap-gateway/service/exchangeCache.go +++ b/coap-gateway/service/exchangeCache.go @@ -2,7 +2,7 @@ package service import ( "context" - "fmt" + "errors" "sync" "github.com/plgd-dev/hub/v2/pkg/security/oauth2" @@ -40,7 +40,7 @@ func (e *ExchangeCache) getFutureToken(authorizationCode string) (*future.Future // Execute Exchange or returned cached value. func (e *ExchangeCache) Execute(ctx context.Context, provider *oauth2.PlgdProvider, authorizationCode string) (*oauth2.Token, error) { if authorizationCode == "" { - return nil, fmt.Errorf("invalid authorization code") + return nil, errors.New("invalid authorization code") } f, set := e.getFutureToken(authorizationCode) diff --git a/coap-gateway/service/log.go b/coap-gateway/service/log.go index 15e74d9b4..9b55c1a14 100644 --- a/coap-gateway/service/log.go +++ b/coap-gateway/service/log.go @@ -15,7 +15,7 @@ import ( const logNotificationKey = "notification" -var toNil = func(args ...interface{}) { +var toNil = func(...interface{}) { // Do nothing because we don't want to log anything } diff --git a/coap-gateway/service/mem_test.go b/coap-gateway/service/mem_test.go index 200c84728..94a502d2b 100644 --- a/coap-gateway/service/mem_test.go +++ b/coap-gateway/service/mem_test.go @@ -6,6 +6,7 @@ package service_test import ( "context" "crypto/tls" + "errors" "fmt" "os" "os/exec" @@ -221,7 +222,7 @@ func testDevices(t *testing.T, numDevices, numResources, expRSSInMB int, resourc err = cmdService.Start() require.NoError(t, err) } else { - require.NoError(t, fmt.Errorf("service process is dead - canceling test")) + require.NoError(t, errors.New("service process is dead - canceling test")) } } t.Logf("waiting for service to start: %v, serviceRSS %v\n", time.Since(now), bToMb(serviceRSS)) @@ -250,12 +251,12 @@ func testDevices(t *testing.T, numDevices, numResources, expRSSInMB int, resourc }() coapShutdown := func() { - err := s.Close() - require.NoError(t, err) + errC := s.Close() + require.NoError(t, errC) wg.Wait() } defer coapShutdown() - grpcConn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + grpcConn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/coap-gateway/service/message/response_test.go b/coap-gateway/service/message/response_test.go index c78868e94..7c8ab93d1 100644 --- a/coap-gateway/service/message/response_test.go +++ b/coap-gateway/service/message/response_test.go @@ -2,6 +2,7 @@ package message import ( "context" + "errors" "fmt" "net/http" "testing" @@ -72,7 +73,7 @@ func TestIsTempError(t *testing.T) { }, { name: "any error as temporary", - args: args{err: fmt.Errorf("any error")}, + args: args{err: errors.New("any error")}, want: true, }, { diff --git a/coap-gateway/service/observation/deviceObserver.go b/coap-gateway/service/observation/deviceObserver.go index 8eed989cd..baaf63218 100644 --- a/coap-gateway/service/observation/deviceObserver.go +++ b/coap-gateway/service/observation/deviceObserver.go @@ -15,6 +15,7 @@ import ( "github.com/plgd-dev/hub/v2/coap-gateway/resource" "github.com/plgd-dev/hub/v2/grpc-gateway/pb" "github.com/plgd-dev/hub/v2/pkg/log" + "github.com/plgd-dev/hub/v2/pkg/net/coap" pkgStrings "github.com/plgd-dev/hub/v2/pkg/strings" "github.com/plgd-dev/hub/v2/resource-aggregate/commands" pbRD "github.com/plgd-dev/hub/v2/resource-directory/pb" @@ -165,7 +166,7 @@ func WithMaxETagsCountInRequest(v uint32) MaxETagsCountInRequestOpt { } func prepareSetupDeviceObserver(ctx context.Context, deviceID string, coapConn ClientConn, rdClient GrpcGatewayClient, raClient ResourceAggregateClient, cfg DeviceObserverConfig) (DeviceObserverConfig, []*commands.Resource, error) { - links, sequence, err := GetResourceLinks(ctx, coapConn, resources.ResourceURI) + links, sequence, err := coap.GetResourceLinksWithLinkInterface(ctx, coapConn, resources.ResourceURI) switch { case err == nil: if cfg.ObservationType == ObservationType_Detect { @@ -254,8 +255,8 @@ func NewDeviceObserver(ctx context.Context, deviceID string, coapConn ClientConn if cfg.ObservationType == ObservationType_PerDevice { etags := getETags(ctx, deviceID, rdClient, cfg) - resourcesObserver, err := createDiscoveryResourceObserver(ctx, deviceID, coapConn, callbacks, etags, cfg.Logger) - if err == nil { + resourcesObserver, errC := createDiscoveryResourceObserver(ctx, deviceID, coapConn, callbacks, etags, cfg.Logger) + if errC == nil { return &DeviceObserver{ deviceID: deviceID, observationType: ObservationType_PerDevice, @@ -266,15 +267,15 @@ func NewDeviceObserver(ctx context.Context, deviceID string, coapConn ClientConn raClient: raClient, }, nil } - cfg.Logger.Debugf("NewDeviceObserver: failed to create /oic/res observation for device(%v): %v", deviceID, err) + cfg.Logger.Debugf("NewDeviceObserver: failed to create /oic/res observation for device(%v): %v", deviceID, errC) } if cfg.RequireBatchObserveEnabled { return nil, createError(fmt.Errorf("device(%v) doesn't support batch observe, which is required by configuration", deviceID)) } - resourcesObserver, err := createPublishedResourcesObserver(ctx, deviceID, coapConn, callbacks, published, cfg.Logger) - if err != nil { - return nil, createError(err) + resourcesObserver, errC := createPublishedResourcesObserver(ctx, deviceID, coapConn, callbacks, published, cfg.Logger) + if errC != nil { + return nil, createError(errC) } return &DeviceObserver{ deviceID: deviceID, @@ -288,12 +289,12 @@ func NewDeviceObserver(ctx context.Context, deviceID string, coapConn ClientConn } func emptyDeviceIDError() error { - return fmt.Errorf("empty deviceID") + return errors.New("empty deviceID") } func IsDiscoveryResourceObservable(links schema.ResourceLinks) (bool, error) { if len(links) == 0 { - return false, fmt.Errorf("no links") + return false, errors.New("no links") } resourceHref := resources.ResourceURI observeInterface := interfaces.OC_IF_B @@ -327,7 +328,7 @@ func loadTwinEnabled(ctx context.Context, rdClient GrpcGatewayClient, deviceID s return fmt.Errorf("cannot get device(%v) metadata: %w", deviceID, err) } if deviceID == "" { - return false, metadataError(fmt.Errorf("invalid deviceID")) + return false, metadataError(errors.New("invalid deviceID")) } deviceMetadataClient, err := rdClient.GetDevicesMetadata(ctx, &pb.GetDevicesMetadataRequest{ DeviceIdFilter: []string{deviceID}, @@ -412,7 +413,7 @@ func (d *DeviceObserver) GetResources() ([]*commands.ResourceId, error) { return nil, nil } if d.resourcesObserver == nil { - return nil, getResourcesError(fmt.Errorf("resources observer is nil")) + return nil, getResourcesError(errors.New("resources observer is nil")) } return d.resourcesObserver.getResources(), nil } diff --git a/coap-gateway/service/observation/deviceObserver_test.go b/coap-gateway/service/observation/deviceObserver_test.go index 31f4d9342..04e3e489a 100644 --- a/coap-gateway/service/observation/deviceObserver_test.go +++ b/coap-gateway/service/observation/deviceObserver_test.go @@ -34,6 +34,7 @@ import ( coapgwTestService "github.com/plgd-dev/hub/v2/test/coap-gateway/service" coapgwTest "github.com/plgd-dev/hub/v2/test/coap-gateway/test" "github.com/plgd-dev/hub/v2/test/config" + "github.com/plgd-dev/hub/v2/test/device/ocf" oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test" pbTest "github.com/plgd-dev/hub/v2/test/pb" "github.com/plgd-dev/hub/v2/test/service" @@ -121,13 +122,13 @@ func (h *observerHandler) SignIn(req coapgwService.CoapSignInReq) (coapgwService err = h.service.Submit(func() { if prevDeviceObserver != nil { - v, err := prevDeviceObserver.Get(h.ctx) - require.NoError(h.t, err) + v, errD := prevDeviceObserver.Get(h.ctx) + require.NoError(h.t, errD) obs := v.(*observation.DeviceObserver) obs.Clean(h.ctx) } - deviceObserver, err := h.deviceObserverFactory.makeDeviceObserver(h.ctx, h.coapConn, h.OnObserveResource, h.OnGetResourceContent, h.UpdateTwinSynchronizationStatus, observation.WithRequireBatchObserveEnabled(h.requireBatchObserveEnabled)) - require.NoError(h.t, err) + deviceObserver, errD := h.deviceObserverFactory.makeDeviceObserver(h.ctx, h.coapConn, h.OnObserveResource, h.OnGetResourceContent, h.UpdateTwinSynchronizationStatus, observation.WithRequireBatchObserveEnabled(h.requireBatchObserveEnabled)) + require.NoError(h.t, errD) setDeviceObserver(deviceObserver, nil) }) require.NoError(h.t, err) @@ -160,8 +161,8 @@ func (h *observerHandler) PublishResources(req coapgwTestService.PublishRequest) return nil } -func (h *observerHandler) OnObserveResource(ctx context.Context, deviceID, resourceHref string, _ bool, notification *pool.Message) error { - err := h.DefaultObserverHandler.OnObserveResource(ctx, deviceID, resourceHref, notification) +func (h *observerHandler) OnObserveResource(ctx context.Context, deviceID, resourceHref string, resourceTypes []string, _ bool, notification *pool.Message) error { + err := h.DefaultObserverHandler.OnObserveResource(ctx, deviceID, resourceHref, resourceTypes, notification) require.NoError(h.t, err) if !h.done.Load() { h.observedResourceChan <- commands.NewResourceID(deviceID, resourceHref) @@ -169,8 +170,8 @@ func (h *observerHandler) OnObserveResource(ctx context.Context, deviceID, resou return nil } -func (h *observerHandler) OnGetResourceContent(ctx context.Context, deviceID, resourceHref string, notification *pool.Message) error { - err := h.DefaultObserverHandler.OnGetResourceContent(ctx, deviceID, resourceHref, notification) +func (h *observerHandler) OnGetResourceContent(ctx context.Context, deviceID, resourceHref string, resourceTypes []string, notification *pool.Message) error { + err := h.DefaultObserverHandler.OnGetResourceContent(ctx, deviceID, resourceHref, resourceTypes, notification) require.NoError(h.t, err) if !h.done.Load() { h.retrievedResourceChan <- commands.NewResourceID(deviceID, resourceHref) @@ -198,19 +199,19 @@ func TestDeviceObserverRegisterForPublishedResources(t *testing.T) { require.Equal(t, observation.ObservationType_PerResource, obs.GetObservationType()) res, err := obs.GetResources() require.NoError(t, err) - pbTest.CmpResourceIds(t, test.ResourceLinksToResourceIds(deviceID, test.TestDevsimResources), res) + pbTest.CmpResourceIds(t, test.ResourceLinksToResourceIds(deviceID, test.GetAllBackendResourceLinks()), res) } expectedObserved := strings.MakeSet() for _, resID := range test.ResourceLinksToResourceIds(deviceID, test.FilterResourceLink(func(rl schema.ResourceLink) bool { return rl.Policy.BitMask.Has(schema.Observable) - }, test.TestDevsimResources)) { + }, test.GetAllBackendResourceLinks())) { expectedObserved.Add(resID.ToString()) } expectedRetrieved := strings.MakeSet() for _, resID := range test.ResourceLinksToResourceIds(deviceID, test.FilterResourceLink(func(rl schema.ResourceLink) bool { return !rl.Policy.BitMask.Has(schema.Observable) - }, test.TestDevsimResources)) { + }, test.GetAllBackendResourceLinks())) { expectedRetrieved.Add(resID.ToString()) } runTestDeviceObserverRegister(ctx, t, deviceID, expectedObserved, expectedRetrieved, validateData, nil, nil, false) @@ -230,19 +231,19 @@ func TestDeviceObserverRegisterForPublishedResourcesWithAlreadyPublishedResource require.Equal(t, observation.ObservationType_PerResource, obs.GetObservationType()) res, err := obs.GetResources() require.NoError(t, err) - pbTest.CmpResourceIds(t, test.ResourceLinksToResourceIds(deviceID, test.TestDevsimResources), res) + pbTest.CmpResourceIds(t, test.ResourceLinksToResourceIds(deviceID, test.GetAllBackendResourceLinks()), res) } expectedObserved := strings.MakeSet() for _, resID := range test.ResourceLinksToResourceIds(deviceID, test.FilterResourceLink(func(rl schema.ResourceLink) bool { return rl.Policy.BitMask.Has(schema.Observable) - }, test.TestDevsimResources)) { + }, test.GetAllBackendResourceLinks())) { expectedObserved.Add(resID.ToString()) } expectedRetrieved := strings.MakeSet() for _, resID := range test.ResourceLinksToResourceIds(deviceID, test.FilterResourceLink(func(rl schema.ResourceLink) bool { return !rl.Policy.BitMask.Has(schema.Observable) - }, test.TestDevsimResources)) { + }, test.GetAllBackendResourceLinks())) { expectedRetrieved.Add(resID.ToString()) } runTestDeviceObserverRegister(ctx, t, deviceID, expectedObserved, expectedRetrieved, validateData, testPreregisterVirtualDevice, testValidateResourceLinks, false) @@ -270,7 +271,7 @@ func TestDeviceObserverRegisterForDiscoveryResource(t *testing.T) { } func testPreregisterVirtualDevice(ctx context.Context, t *testing.T, deviceID string, grpcClient pb.GrpcGatewayClient, raClient raPb.ResourceAggregateClient) { - isConn, err := grpc.Dial(config.IDENTITY_STORE_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + isConn, err := grpc.NewClient(config.IDENTITY_STORE_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -298,14 +299,14 @@ func testPreregisterVirtualDevice(ctx context.Context, t *testing.T, deviceID st ev, err := client.Recv() require.NoError(t, err) require.NotEmpty(t, ev.GetOperationProcessed()) - require.Equal(t, ev.GetOperationProcessed().GetErrorStatus().GetCode(), pb.Event_OperationProcessed_ErrorStatus_OK) - virtualdevice.CreateDevice(ctx, t, "name-"+deviceID, deviceID, numResources, test.StringToApplicationProtocol(config.ACTIVE_COAP_SCHEME), isClient, raClient) - resources := virtualdevice.CreateDeviceResourceLinks(deviceID, numResources) + require.Equal(t, pb.Event_OperationProcessed_ErrorStatus_OK, ev.GetOperationProcessed().GetErrorStatus().GetCode()) + virtualdevice.CreateDevice(ctx, t, "name-"+deviceID, deviceID, numResources, false, test.StringToApplicationProtocol(config.ACTIVE_COAP_SCHEME), isClient, raClient) + resources := virtualdevice.CreateDeviceResourceLinks(deviceID, numResources, false) links := make([]schema.ResourceLink, 0, len(resources)) for _, r := range resources { links = append(links, r.ToSchema()) } - test.WaitForDevice(t, client, deviceID, ev.GetSubscriptionId(), ev.GetCorrelationId(), links) + test.WaitForDevice(t, client, ocf.NewDevice(deviceID, test.TestDeviceName), ev.GetSubscriptionId(), ev.GetCorrelationId(), links) } func testValidateResourceLinks(ctx context.Context, t *testing.T, deviceID string, grpcClient pb.GrpcGatewayClient, _ raPb.ResourceAggregateClient) { @@ -451,7 +452,7 @@ func runTestDeviceObserverRegister(ctx context.Context, t *testing.T, deviceID s coapShutdown := coapgwTest.SetUp(t, makeHandler, validateHandler) defer coapShutdown() - grpcConn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + grpcConn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/coap-gateway/service/observation/observation.go b/coap-gateway/service/observation/observation.go deleted file mode 100644 index 6d8caf9b1..000000000 --- a/coap-gateway/service/observation/observation.go +++ /dev/null @@ -1,42 +0,0 @@ -package observation - -import ( - "context" - "fmt" - - "github.com/plgd-dev/device/v2/schema" - "github.com/plgd-dev/device/v2/schema/interfaces" - coapMessage "github.com/plgd-dev/go-coap/v3/message" - "github.com/plgd-dev/go-coap/v3/message/codes" - "github.com/plgd-dev/hub/v2/coap-gateway/uri" - "github.com/plgd-dev/kit/v2/codec/cbor" -) - -// Query resource links from the given resource with the interface oic.if.ll. -func GetResourceLinks(ctx context.Context, coapConn ClientConn, href string) (schema.ResourceLinks, uint64, error) { - msg, err := coapConn.Get(ctx, href, coapMessage.Option{ - ID: coapMessage.URIQuery, - Value: []byte(uri.InterfaceQueryKeyPrefix + interfaces.OC_IF_LL), - }) - if err != nil { - return schema.ResourceLinks{}, 0, err - } - defer coapConn.ReleaseMessage(msg) - - if msg.Code() != codes.Content { - return schema.ResourceLinks{}, 0, fmt.Errorf("invalid response code %v", msg.Code()) - } - - data := msg.Body() - if data == nil { - return schema.ResourceLinks{}, 0, fmt.Errorf("empty response") - } - - var links schema.ResourceLinks - err = cbor.ReadFrom(msg.Body(), &links) - if err != nil { - return schema.ResourceLinks{}, 0, err - } - - return links, msg.Sequence(), nil -} diff --git a/coap-gateway/service/observation/observation_test.go b/coap-gateway/service/observation/observation_test.go index 0e9b4c4a5..f8f88c16d 100644 --- a/coap-gateway/service/observation/observation_test.go +++ b/coap-gateway/service/observation/observation_test.go @@ -11,6 +11,7 @@ import ( coapgwService "github.com/plgd-dev/hub/v2/coap-gateway/service" "github.com/plgd-dev/hub/v2/coap-gateway/service/observation" "github.com/plgd-dev/hub/v2/grpc-gateway/pb" + "github.com/plgd-dev/hub/v2/pkg/net/coap" kitNetGrpc "github.com/plgd-dev/hub/v2/pkg/net/grpc" "github.com/plgd-dev/hub/v2/pkg/sync/task/future" "github.com/plgd-dev/hub/v2/test" @@ -51,7 +52,7 @@ func TestIsResourceObservableWithInterface(t *testing.T) { ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) handlerFuture, setHandler := future.New() - makeHandler := func(service *coapgwTestService.Service, opts ...coapgwTestService.Option) coapgwTestService.ServiceHandler { + makeHandler := func(_ *coapgwTestService.Service, opts ...coapgwTestService.Option) coapgwTestService.ServiceHandler { cfg := coapgwTestService.ServiceHandlerConfig{} for _, o := range opts { o.Apply(&cfg) @@ -68,7 +69,7 @@ func TestIsResourceObservableWithInterface(t *testing.T) { coapShutdown := coapgwTest.SetUp(t, makeHandler, nil) defer coapShutdown() - grpcConn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + grpcConn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -116,7 +117,7 @@ func TestIsResourceObservableWithInterface(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - links, _, err := observation.GetResourceLinks(ctx, handler.coapConn, tt.args.href) + links, _, err := coap.GetResourceLinksWithLinkInterface(ctx, handler.coapConn, tt.args.href) if tt.wantErr { require.Error(t, err) return diff --git a/coap-gateway/service/observation/observedResource.go b/coap-gateway/service/observation/observedResource.go index 560b72aab..2f269a5f1 100644 --- a/coap-gateway/service/observation/observedResource.go +++ b/coap-gateway/service/observation/observedResource.go @@ -20,21 +20,23 @@ type Observation = interface { // Thread-safe wrapper with additional data for *tcp.Observation. type observedResource struct { - href string - resInterface string - synced atomic.Bool - isObservable bool - private struct { // guarded by mutex + href string + resInterface string + synced atomic.Bool + isObservable bool + resourceTypes []string + private struct { // guarded by mutex mutex sync.Mutex observation Observation } } -func newObservedResource(href, resInterface string, isObservable bool) *observedResource { +func newObservedResource(href, resInterface string, isObservable bool, resourceTypes []string) *observedResource { return &observedResource{ - href: href, - resInterface: resInterface, - isObservable: isObservable, + href: href, + resInterface: resInterface, + isObservable: isObservable, + resourceTypes: resourceTypes, } } diff --git a/coap-gateway/service/observation/resourcesObserver.go b/coap-gateway/service/observation/resourcesObserver.go index ffe438afe..e8897ac1c 100644 --- a/coap-gateway/service/observation/resourcesObserver.go +++ b/coap-gateway/service/observation/resourcesObserver.go @@ -2,6 +2,7 @@ package observation import ( "context" + "errors" "fmt" "sync" "time" @@ -15,8 +16,8 @@ import ( ) type ( - OnObserveResource = func(ctx context.Context, deviceID, resourceHref string, batch bool, notification *pool.Message) error - OnGetResourceContent = func(ctx context.Context, deviceID, resourceHref string, notification *pool.Message) error + OnObserveResource = func(ctx context.Context, deviceID, resourceHref string, resourceTypes []string, batch bool, notification *pool.Message) error + OnGetResourceContent = func(ctx context.Context, deviceID, resourceHref string, resourceTypes []string, notification *pool.Message) error UpdateTwinSynchronization = func(ctx context.Context, deviceID string, status commands.TwinSynchronization_State, t time.Time) error ) @@ -68,13 +69,13 @@ func newResourcesObserver(deviceID string, coapConn ClientConn, callbacks Resour fatalCannotCreate(emptyDeviceIDError()) } if coapConn == nil { - fatalCannotCreate(fmt.Errorf("invalid coap-gateway connection")) + fatalCannotCreate(errors.New("invalid coap-gateway connection")) } if callbacks.OnObserveResource == nil { - fatalCannotCreate(fmt.Errorf("invalid onObserveResource callback")) + fatalCannotCreate(errors.New("invalid onObserveResource callback")) } if callbacks.OnGetResourceContent == nil { - fatalCannotCreate(fmt.Errorf("invalid onGetResourceContent callback")) + fatalCannotCreate(errors.New("invalid onGetResourceContent callback")) } return &resourcesObserver{ deviceID: deviceID, @@ -124,15 +125,13 @@ func (o *resourcesObserver) setSynchronizedAtResource(href string) bool { o.private.lock.Lock() defer o.private.lock.Unlock() i := o.private.resources.search(href) - synced := false if i < len(o.private.resources) && o.private.resources[i].Equals(href) { - synced = o.private.resources[i].synced.CompareAndSwap(false, true) + if !o.private.resources[i].synced.CompareAndSwap(false, true) { + return false + } } else { return false } - if !synced { - return false - } if o.isSynchronizedLocked() { return true } @@ -154,7 +153,7 @@ func (o *resourcesObserver) addResourceLocked(res *commands.Resource, obsInterfa if o.private.resources.contains(href) { return nil, nil } - obsRes := newObservedResource(href, obsInterface, res.IsObservable()) + obsRes := newObservedResource(href, obsInterface, res.IsObservable(), res.GetResourceTypes()) o.private.resources = o.private.resources.insert(obsRes) return obsRes, nil } @@ -180,7 +179,7 @@ func (o *resourcesObserver) handleResource(ctx context.Context, obsRes *observed if len(etags) > 0 { etag = etags[0] } - return o.getResourceContent(ctx, obsRes.Href(), etag) + return o.getResourceContent(ctx, obsRes.Href(), obsRes.resourceTypes, etag) } // Register to COAP-GW resource observation for given resource @@ -204,7 +203,7 @@ func (o *resourcesObserver) observeResource(ctx context.Context, obsRes *observe } } - if err2 := o.callbacks.OnObserveResource(ctx, o.deviceID, obsRes.Href(), batchObservation, msg); err2 != nil { + if err2 := o.callbacks.OnObserveResource(ctx, o.deviceID, obsRes.Href(), obsRes.resourceTypes, batchObservation, msg); err2 != nil { _ = o.logger.LogAndReturnError(cannotObserveResourceError(o.deviceID, obsRes.Href(), err2)) return } @@ -213,13 +212,13 @@ func (o *resourcesObserver) observeResource(ctx context.Context, obsRes *observe return nil, cannotObserveResourceError(o.deviceID, obsRes.Href(), err) } if obs.Canceled() { - return nil, cannotObserveResourceError(o.deviceID, obsRes.Href(), fmt.Errorf("resource not observable")) + return nil, cannotObserveResourceError(o.deviceID, obsRes.Href(), errors.New("resource not observable")) } return obs, nil } // Request resource content form COAP-GW -func (o *resourcesObserver) getResourceContent(ctx context.Context, href string, etag []byte) error { +func (o *resourcesObserver) getResourceContent(ctx context.Context, href string, resourceTypes []string, etag []byte) error { cannotGetResourceError := func(deviceID, href string, err error) error { return fmt.Errorf("cannot get resource /%v%v content: %w", deviceID, href, err) } @@ -243,7 +242,7 @@ func (o *resourcesObserver) getResourceContent(ctx context.Context, href string, o.coapConn.ReleaseMessage(resp) } }() - if err := o.callbacks.OnGetResourceContent(ctx, o.deviceID, href, resp); err != nil { + if err := o.callbacks.OnGetResourceContent(ctx, o.deviceID, href, resourceTypes, resp); err != nil { return cannotGetResourceError(o.deviceID, href, err) } return nil @@ -373,8 +372,8 @@ func (o *resourcesObserver) CleanObservedResources(ctx context.Context) { } func (o *resourcesObserver) cleanObservedResourcesLocked(ctx context.Context) { - observedResources := o.popObservedResourcesLocked() - for _, obs := range observedResources { + ors := o.popObservedResourcesLocked() + for _, obs := range ors { if v := obs.PopObservation(); v != nil { if err := v.Cancel(ctx); err != nil { o.logger.Errorf("cannot cancel resource('/%v%v') observation: %w", o.deviceID, obs.Href(), err) diff --git a/coap-gateway/service/refreshCache.go b/coap-gateway/service/refreshCache.go index 338554213..39531da0e 100644 --- a/coap-gateway/service/refreshCache.go +++ b/coap-gateway/service/refreshCache.go @@ -2,7 +2,7 @@ package service import ( "context" - "fmt" + "errors" "sync" "github.com/plgd-dev/hub/v2/pkg/log" @@ -66,7 +66,7 @@ func refresh(ctx context.Context, providers map[string]*oauth2.PlgdProvider, que return nil, err } - return nil, fmt.Errorf("invalid token") + return nil, errors.New("invalid token") } func (r *RefreshCache) getFutureToken(refreshToken string) (*future.Future, future.SetFunc) { @@ -83,7 +83,7 @@ func (r *RefreshCache) getFutureToken(refreshToken string) (*future.Future, futu func (r *RefreshCache) Execute(ctx context.Context, providers map[string]*oauth2.PlgdProvider, queue *queue.Queue, refreshToken string, logger log.Logger) (*oauth2.Token, error) { if refreshToken == "" { - return nil, fmt.Errorf("invalid refreshToken") + return nil, errors.New("invalid refreshToken") } f, set := r.getFutureToken(refreshToken) diff --git a/coap-gateway/service/refreshToken.go b/coap-gateway/service/refreshToken.go index a818c795b..df1347b21 100644 --- a/coap-gateway/service/refreshToken.go +++ b/coap-gateway/service/refreshToken.go @@ -117,7 +117,7 @@ func getRefreshTokenDataFromClaims(ctx context.Context, client *session, accessT owner = req.UserID } if owner == "" { - return "", "", fmt.Errorf("cannot determine owner") + return "", "", errors.New("cannot determine owner") } return deviceID, owner, nil } @@ -149,7 +149,7 @@ func refreshTokenPostHandler(req *mux.Message, client *session) (*pool.Message, } if token.RefreshToken == "" { - return nil, statusErrorf(coapCodes.Unauthorized, "%w", fmt.Errorf(fmtErr, refreshToken.DeviceID, fmt.Errorf("refresh didn't return a refresh token"))) + return nil, statusErrorf(coapCodes.Unauthorized, "%w", fmt.Errorf(fmtErr, refreshToken.DeviceID, errors.New("refresh didn't return a refresh token"))) } deviceID, owner, err := getRefreshTokenDataFromClaims(req.Context(), client, token.AccessToken.String(), refreshToken) @@ -163,12 +163,12 @@ func refreshTokenPostHandler(req *mux.Message, client *session) (*pool.Message, return nil, statusErrorf(coapCodes.Unauthorized, "%w", fmt.Errorf(fmtErr, deviceID, fmt.Errorf("cannot check owning: %w", err))) } if !ok { - return nil, statusErrorf(coapCodes.Unauthorized, "%w", fmt.Errorf(fmtErr, deviceID, fmt.Errorf("device is not registered"))) + return nil, statusErrorf(coapCodes.Unauthorized, "%w", fmt.Errorf(fmtErr, deviceID, errors.New("device is not registered"))) } expire, ok := ValidUntil(token.Expiry) if !ok { - return nil, statusErrorf(coapCodes.Unauthorized, "%w", fmt.Errorf(fmtErr, deviceID, fmt.Errorf("expired access token"))) + return nil, statusErrorf(coapCodes.Unauthorized, "%w", fmt.Errorf(fmtErr, deviceID, errors.New("expired access token"))) } validUntil := pkgTime.Unix(0, expire) diff --git a/coap-gateway/service/resourceDirectory.go b/coap-gateway/service/resourceDirectory.go index 3d4b26971..61c6a8dcb 100644 --- a/coap-gateway/service/resourceDirectory.go +++ b/coap-gateway/service/resourceDirectory.go @@ -146,7 +146,7 @@ func observeResources(ctx context.Context, client *session, w wkRd, sequenceNumb return } if !ok { - x.client.Errorf("%w", x.observeError(x.w.DeviceID, fmt.Errorf("cannot get device observer"))) + x.client.Errorf("%w", x.observeError(x.w.DeviceID, errors.New("cannot get device observer"))) return } if errObs := obs.AddPublishedResources(x.ctx, x.publishedResources); errObs != nil { @@ -170,8 +170,8 @@ func resourceDirectoryPublishHandler(req *mux.Message, client *session) (*pool.M return nil, statusErrorf(coapCodes.BadRequest, "%w", err) } - if errCode, err := observeResources(req.Context(), client, w, req.Sequence()); err != nil { - return nil, statusErrorf(errCode, "%w", err) + if errCode, errO := observeResources(req.Context(), client, w, req.Sequence()); errO != nil { + return nil, statusErrorf(errCode, "%w", errO) } accept := coapconv.GetAccept(req.Options()) @@ -193,11 +193,13 @@ func parseUnpublishQueryString(queries []string) (deviceID string, instanceIDs [ if err != nil { return "", nil, fmt.Errorf("cannot parse unpublish query: %w", err) } - if di := values.Get("di"); di != "" { + for _, di := range values["di"] { + if deviceID != "" { + return "", nil, fmt.Errorf("unable to parse unpublish query: duplicate in parameter di(%v), previously di(%v)", di, deviceID) + } deviceID = di } - - if ins := values.Get("ins"); ins != "" { + for _, ins := range values["ins"] { i, err := strconv.Atoi(ins) if err != nil { return "", nil, fmt.Errorf("cannot convert %v to number", ins) @@ -207,7 +209,7 @@ func parseUnpublishQueryString(queries []string) (deviceID string, instanceIDs [ } if deviceID == "" { - return "", nil, fmt.Errorf("deviceID not found") + return "", nil, errors.New("deviceID not found") } return diff --git a/coap-gateway/service/resourceDirectory_internal_test.go b/coap-gateway/service/resourceDirectory_internal_test.go new file mode 100644 index 000000000..5cf9ef032 --- /dev/null +++ b/coap-gateway/service/resourceDirectory_internal_test.go @@ -0,0 +1,71 @@ +//go:build test +// +build test + +package service + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParseUnpublishQueryString(t *testing.T) { + tests := []struct { + name string + queries []string + expectedID string + expectedIns []int64 + wantErr bool + }{ + { + name: "ValidQueries", + queries: []string{"di=device1", "ins=1", "ins=2"}, + expectedID: "device1", + expectedIns: []int64{1, 2}, + }, + { + name: "MultipleInsInOneQuery", + queries: []string{"di=device1&ins=1&ins=2"}, + expectedID: "device1", + expectedIns: []int64{1, 2}, + }, + { + name: "InvalidIns", + queries: []string{"di=device1", "ins=abc"}, + wantErr: true, + }, + { + name: "One of the queries is invalid", + queries: []string{"di=device1", "ins=1", "invalid_query"}, + expectedID: "device1", + expectedIns: []int64{1}, + }, + { + name: "MultipleDeviceIDs", + queries: []string{"di=device1&di=device2"}, + wantErr: true, + }, + { + name: "DeviceIDandInsAreNotSet", + queries: []string{"invalidQuery=123"}, + wantErr: true, + }, + { + name: "EmptyQueries", + queries: []string{}, + wantErr: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + deviceID, instanceIDs, err := parseUnpublishQueryString(test.queries) + if test.wantErr { + require.Error(t, err) + return + } + require.Equal(t, test.expectedID, deviceID) + require.Equal(t, test.expectedIns, instanceIDs) + }) + } +} diff --git a/coap-gateway/service/service.go b/coap-gateway/service/service.go index 4ce87f54e..e01492659 100644 --- a/coap-gateway/service/service.go +++ b/coap-gateway/service/service.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "crypto/x509" + "errors" "fmt" "net" "os" @@ -291,7 +292,7 @@ func New(ctx context.Context, config Config, fileWatcher *fsnotify.Watcher, logg if firstProvider == nil { nats.Close() - return nil, fmt.Errorf("device providers are empty") + return nil, errors.New("device providers are empty") } keyCache := jwt.NewKeyCache(firstProvider.OpenID.JWKSURL, firstProvider.HTTP()) @@ -357,7 +358,7 @@ func onUnprocessedRequestError(code coapCodes.Code) error { if code == coapCodes.Content { errMsg = "notification from client was dropped" } - return fmt.Errorf(errMsg) + return errors.New(errMsg) } func wantToCloseClientOnError(req *mux.Message) bool { @@ -391,7 +392,7 @@ func (s *Service) processCommandTask(req *mux.Message, client *session, span tra }() } default: - err := onUnprocessedRequestError(req.Code()) + err = onUnprocessedRequestError(req.Code()) client.logRequestResponse(req, nil, err) span.RecordError(err) span.SetStatus(otelCodes.Error, err.Error()) @@ -449,7 +450,7 @@ func executeCommand(s mux.ResponseWriter, req *mux.Message, server *Service, fnc if !ok { client = newSession(server, s.Conn(), "", time.Time{}) if req.Code() == coapCodes.Empty { - client.logRequestResponse(req, nil, fmt.Errorf("cannot handle command: client not found")) + client.logRequestResponse(req, nil, errors.New("cannot handle command: client not found")) client.Close() return } @@ -495,25 +496,27 @@ func getTLSInfo(conn net.Conn, logger log.Logger) (deviceID string, validUntil t logger.Debugf("cannot get deviceID from certificate: certificate is not set") return "", time.Time{} } - if tlsCon, ok := conn.(*dtls.Conn); ok { - peerCertificates := tlsCon.ConnectionState().PeerCertificates - if len(peerCertificates) > 0 { - cert, err := x509.ParseCertificate(peerCertificates[0]) - if err != nil { - logger.Warnf("cannot get deviceID from certificate: %w", err) - return "", time.Time{} - } - deviceID, err := coap.GetDeviceIDFromIdentityCertificate(cert) - if err == nil { - return deviceID, cert.NotAfter - } - logger.Warnf("cannot get deviceID from certificate %v: %w", cert.Subject.CommonName, err) - return "", cert.NotAfter - } - logger.Debugf("cannot get deviceID from certificate: certificate is not set") + + tlsCon, ok := conn.(*dtls.Conn) + if !ok { + logger.Debugf("cannot get deviceID from certificate: unsupported connection type") return "", time.Time{} } - logger.Debugf("cannot get deviceID from certificate: unsupported connection type") + peerCertificates := tlsCon.ConnectionState().PeerCertificates + if len(peerCertificates) > 0 { + cert, err := x509.ParseCertificate(peerCertificates[0]) + if err != nil { + logger.Warnf("cannot get deviceID from certificate: %w", err) + return "", time.Time{} + } + deviceID, err := coap.GetDeviceIDFromIdentityCertificate(cert) + if err == nil { + return deviceID, cert.NotAfter + } + logger.Warnf("cannot get deviceID from certificate %v: %w", cert.Subject.CommonName, err) + return "", cert.NotAfter + } + logger.Debugf("cannot get deviceID from certificate: certificate is not set") return "", time.Time{} } diff --git a/coap-gateway/service/service_test.go b/coap-gateway/service/service_test.go index 4adc86f91..c705725fa 100644 --- a/coap-gateway/service/service_test.go +++ b/coap-gateway/service/service_test.go @@ -97,7 +97,7 @@ func TestShutdownServiceWithDeviceIssue627(t *testing.T) { coapShutdown := coapgwTest.SetUp(t) defer coapShutdown() - grpcConn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + grpcConn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/coap-gateway/service/session.go b/coap-gateway/service/session.go index 8943cc59c..b4f7f7803 100644 --- a/coap-gateway/service/session.go +++ b/coap-gateway/service/session.go @@ -25,6 +25,7 @@ import ( "github.com/plgd-dev/hub/v2/grpc-gateway/pb" idEvents "github.com/plgd-dev/hub/v2/identity-store/events" "github.com/plgd-dev/hub/v2/pkg/log" + "github.com/plgd-dev/hub/v2/pkg/net/coap" coapService "github.com/plgd-dev/hub/v2/pkg/net/coap/service" kitNetGrpc "github.com/plgd-dev/hub/v2/pkg/net/grpc" "github.com/plgd-dev/hub/v2/pkg/opentelemetry/otelcoap" @@ -81,13 +82,13 @@ func (a *authorizationContext) GetJWTClaims() pkgJwt.Claims { func (a *authorizationContext) IsValid() error { if a == nil { - return fmt.Errorf("invalid authorization context") + return errors.New("invalid authorization context") } if a.AccessToken == "" { - return fmt.Errorf("invalid access token") + return errors.New("invalid access token") } if !a.Expire.IsZero() && time.Now().UnixNano() > a.Expire.UnixNano() { - return fmt.Errorf("token is expired") + return errors.New("token is expired") } return nil } @@ -316,7 +317,7 @@ func (c *session) cancelResourceSubscription(token string) (bool, error) { // // The received notification is released by this function at the correct moment and must not be released // by the caller. -func (c *session) onGetResourceContent(ctx context.Context, deviceID, href string, notification *pool.Message) error { +func (c *session) onGetResourceContent(ctx context.Context, deviceID, href string, resourceTypes []string, notification *pool.Message) error { cannotGetResourceContentError := func(deviceID, href string, err error) error { return fmt.Errorf("cannot get resource /%v%v content: %w", deviceID, href, err) } @@ -326,6 +327,7 @@ func (c *session) onGetResourceContent(ctx context.Context, deviceID, href strin notification *pool.Message deviceID string href string + resourceTypes []string c *session cannotGetResourceContentError func(deviceID, href string, err error) error }{ @@ -333,6 +335,7 @@ func (c *session) onGetResourceContent(ctx context.Context, deviceID, href strin notification: notification, deviceID: deviceID, href: href, + resourceTypes: resourceTypes, c: c, cannotGetResourceContentError: cannotGetResourceContentError, } @@ -341,7 +344,7 @@ func (c *session) onGetResourceContent(ctx context.Context, deviceID, href strin if x.notification.Code() == codes.NotFound { x.c.unpublishResourceLinks(x.c.getUserAuthorizedContext(x.ctx), []string{x.href}, nil) } - err2 := x.c.notifyContentChanged(x.deviceID, x.href, false, x.notification) + err2 := x.c.notifyContentChanged(x.deviceID, x.href, x.resourceTypes, false, x.notification) if err2 != nil { // hub is out of sync with the device, for recovery, the device is disconnected from the hub x.c.Close() @@ -369,7 +372,7 @@ func (c *session) onGetResourceContent(ctx context.Context, deviceID, href strin // // The received notification is released by this function at the correct moment and must not be released // by the caller. -func (c *session) onObserveResource(ctx context.Context, deviceID, href string, batch bool, notification *pool.Message) error { +func (c *session) onObserveResource(ctx context.Context, deviceID, href string, resourceTypes []string, batch bool, notification *pool.Message) error { cannotObserResourceError := func(err error) error { return fmt.Errorf("cannot handle resource observation: %w", err) } @@ -379,6 +382,7 @@ func (c *session) onObserveResource(ctx context.Context, deviceID, href string, notification *pool.Message deviceID string href string + resourceTypes []string c *session cannotObserResourceError func(err error) error batch bool @@ -387,6 +391,7 @@ func (c *session) onObserveResource(ctx context.Context, deviceID, href string, notification: notification, deviceID: deviceID, href: href, + resourceTypes: resourceTypes, c: c, cannotObserResourceError: cannotObserResourceError, batch: batch, @@ -396,7 +401,7 @@ func (c *session) onObserveResource(ctx context.Context, deviceID, href string, if x.notification.Code() == codes.NotFound { x.c.unpublishResourceLinks(x.c.getUserAuthorizedContext(x.notification.Context()), []string{x.href}, nil) } - err2 := x.c.notifyContentChanged(x.deviceID, x.href, x.batch, x.notification) + err2 := x.c.notifyContentChanged(x.deviceID, x.href, x.resourceTypes, x.batch, x.notification) if err2 != nil { // hub is out of sync with the device, for recovery, the device is disconnected from the hub x.c.Close() @@ -516,7 +521,17 @@ func (c *session) batchNotifyContentChanged(ctx context.Context, deviceID string return err } -func (c *session) notifyContentChanged(deviceID, href string, batch bool, notification *pool.Message) error { +func (c *session) getLocalEndpoints() []string { + localEndpoints, err := coap.GetEndpointsFromDeviceResource(c.Context(), c) + if err != nil { + c.getLogger().Warnf("cannot get local endpoints: %v", err) + return nil + } + c.getLogger().With(log.LocalEndpointsKey, localEndpoints).Debugf("local endpoints retrieval successful.") + return localEndpoints +} + +func (c *session) notifyContentChanged(deviceID, href string, resourceTypes []string, batch bool, notification *pool.Message) error { if !c.blockSignOff.TryAcquire(1) { c.getLogger().Debugf("cannot notify resource /%v%v content changed: signOff processing", deviceID, href) return nil @@ -550,7 +565,7 @@ func (c *session) notifyContentChanged(deviceID, href string, batch bool, notifi } return nil } - _, err = c.server.raClient.NotifyResourceChanged(ctx, coapconv.NewNotifyResourceChangedRequest(commands.NewResourceID(deviceID, href), c.RemoteAddr().String(), notification)) + _, err = c.server.raClient.NotifyResourceChanged(ctx, coapconv.NewNotifyResourceChangedRequest(commands.NewResourceID(deviceID, href), resourceTypes, c.RemoteAddr().String(), notification)) if err != nil { return notifyError(deviceID, href, err) } @@ -816,21 +831,21 @@ func (c *session) unpublishResourceLinks(ctx context.Context, hrefs []string, in return nil } - if len(resp.UnpublishedHrefs) == 0 { + if len(resp.GetUnpublishedHrefs()) == 0 { return nil } observer, ok, err := c.getDeviceObserver(ctx) if err != nil { logUnpublishError(err) - return resp.UnpublishedHrefs + return resp.GetUnpublishedHrefs() } if !ok { - logUnpublishError(fmt.Errorf("device observer not found")) - return resp.UnpublishedHrefs + logUnpublishError(errors.New("device observer not found")) + return resp.GetUnpublishedHrefs() } - observer.RemovePublishedResources(ctx, resp.UnpublishedHrefs) - return resp.UnpublishedHrefs + observer.RemovePublishedResources(ctx, resp.GetUnpublishedHrefs()) + return resp.GetUnpublishedHrefs() } func (c *session) sendErrorConfirmResourceCreate(ctx context.Context, resourceID *commands.ResourceId, correlationID string, code codes.Code, errToSend error) { diff --git a/coap-gateway/service/signIn.go b/coap-gateway/service/signIn.go index d50380bb4..fcf6ff884 100644 --- a/coap-gateway/service/signIn.go +++ b/coap-gateway/service/signIn.go @@ -2,6 +2,7 @@ package service import ( "context" + "errors" "fmt" "time" @@ -33,13 +34,13 @@ type CoapSignInResp struct { // Check that all required request fields are set func (request CoapSignInReq) checkOAuthRequest() error { if request.DeviceID == "" { - return fmt.Errorf("invalid device id") + return errors.New("invalid device id") } if request.UserID == "" { - return fmt.Errorf("invalid user id") + return errors.New("invalid user id") } if request.AccessToken == "" { - return fmt.Errorf("invalid access token") + return errors.New("invalid access token") } return nil } @@ -169,10 +170,10 @@ func subscribeToDeviceEvents(client *session, owner, deviceID string) error { if evt == nil { return } - if evt.Owner != owner { + if evt.GetOwner() != owner { return } - if !strings.Contains(evt.DeviceIds, deviceID) { + if !strings.Contains(evt.GetDeviceIds(), deviceID) { return } client.Close() @@ -217,7 +218,7 @@ func getSignInDataFromClaims(ctx context.Context, client *session, signIn CoapSi return "", time.Time{}, err } - if err := jwtClaims.ValidateOwnerClaim(client.server.config.APIs.COAP.Authorization.OwnerClaim, signIn.UserID); err != nil { + if err = jwtClaims.ValidateOwnerClaim(client.server.config.APIs.COAP.Authorization.OwnerClaim, signIn.UserID); err != nil { return "", time.Time{}, err } @@ -229,7 +230,11 @@ func getSignInDataFromClaims(ctx context.Context, client *session, signIn CoapSi expTime, _ := jwtClaims.GetExpirationTime() validUntil := time.Time{} if expTime != nil { - validUntil = expTime.Time + if time.Until(expTime.Time) < 2*client.server.config.APIs.COAP.OwnerCacheExpiration { + return "", time.Time{}, fmt.Errorf("access token will expire (%v) in less time than the interval for checking expiration (%v)", expTime.Time, 2*client.server.config.APIs.COAP.OwnerCacheExpiration) + } + // set expiration time before token expiration to allow sign off device. + validUntil = expTime.Time.Add(-2 * client.server.config.APIs.COAP.OwnerCacheExpiration) } return deviceID, validUntil, nil diff --git a/coap-gateway/service/signIn_test.go b/coap-gateway/service/signIn_test.go index bdc02be8d..c93de8701 100644 --- a/coap-gateway/service/signIn_test.go +++ b/coap-gateway/service/signIn_test.go @@ -7,7 +7,6 @@ import ( "context" "crypto/tls" "errors" - "fmt" "strings" "testing" "time" @@ -75,7 +74,7 @@ func TestSignInDeviceSubscriptionHandler(t *testing.T) { defer shutdown() ctx := kitNetGrpc.CtxWithToken(context.Background(), oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -159,7 +158,7 @@ func TestDontCreateObservationAfterRefreshTokenAndSignIn(t *testing.T) { observedPath[path] = struct{}{} h(w, r) } else { - require.NoError(t, fmt.Errorf("cannot observe the same resource twice")) + require.NoError(t, errors.New("cannot observe the same resource twice")) } }) if co == nil { @@ -320,6 +319,6 @@ func TestCertificateExpiration(t *testing.T) { // connection was closed by certificate expiration return case <-time.After(2 * duration): - require.NoError(t, fmt.Errorf("timeout")) + require.NoError(t, errors.New("timeout")) } } diff --git a/coap-gateway/service/signOff.go b/coap-gateway/service/signOff.go index b4f6409e8..7145b8512 100644 --- a/coap-gateway/service/signOff.go +++ b/coap-gateway/service/signOff.go @@ -2,6 +2,7 @@ package service import ( "context" + "errors" "fmt" "math" "net/url" @@ -74,13 +75,13 @@ func (s signOffData) updateSignOffDataFromAuthContext(client *session) signOffDa func (s signOffData) validateSignOffData() error { if s.deviceID == "" { - return fmt.Errorf("invalid device id") + return errors.New("invalid device id") } if s.userID == "" { - return fmt.Errorf("invalid user id") + return errors.New("invalid user id") } if s.accessToken == "" { - return fmt.Errorf("invalid access token") + return errors.New("invalid access token") } return nil } @@ -91,7 +92,7 @@ func getSignOffDataFromClaims(ctx context.Context, client *session, sod signOffD return "", err } - if err := jwtClaims.ValidateOwnerClaim(client.server.config.APIs.COAP.Authorization.OwnerClaim, sod.userID); err != nil { + if err = jwtClaims.ValidateOwnerClaim(client.server.config.APIs.COAP.Authorization.OwnerClaim, sod.userID); err != nil { return "", err } @@ -125,7 +126,7 @@ func signOffHandler(req *mux.Message, client *session) (*pool.Message, error) { err = client.blockSignOff.Acquire(ctx, math.MaxInt64) if err != nil { - return nil, statusErrorf(coapCodes.ServiceUnavailable, errFmtSignOff, fmt.Errorf("cannot acquire sign off lock: some commands are in progress")) + return nil, statusErrorf(coapCodes.ServiceUnavailable, errFmtSignOff, errors.New("cannot acquire sign off lock: some commands are in progress")) } defer client.blockSignOff.Release(math.MaxInt64) diff --git a/coap-gateway/service/signUp.go b/coap-gateway/service/signUp.go index d2a2c205f..a6c49f8f7 100644 --- a/coap-gateway/service/signUp.go +++ b/coap-gateway/service/signUp.go @@ -2,6 +2,7 @@ package service import ( "context" + "errors" "fmt" "github.com/plgd-dev/go-coap/v3/message" @@ -34,10 +35,10 @@ type CoapSignUpResponse struct { // Check that all required request fields are set func (request CoapSignUpRequest) checkOAuthRequest() error { if request.DeviceID == "" { - return fmt.Errorf("invalid device id") + return errors.New("invalid device id") } if request.AuthorizationCode == "" { - return fmt.Errorf("invalid authorization code") + return errors.New("invalid authorization code") } return nil } @@ -81,7 +82,7 @@ func getSignUpToken(ctx context.Context, client *session, signUp CoapSignUpReque return nil, statusErrorf(coapCodes.ServiceUnavailable, errFmtSignUP, err) } if token.RefreshToken == "" { - return nil, statusErrorf(coapCodes.Unauthorized, errFmtSignUP, fmt.Errorf("exchange didn't return a refresh token")) + return nil, statusErrorf(coapCodes.Unauthorized, errFmtSignUP, errors.New("exchange didn't return a refresh token")) } return token, nil } @@ -97,7 +98,7 @@ func getSignUpDataFromClaims(ctx context.Context, client *session, accessToken s return "", "", err } if owner == "" { - return "", "", fmt.Errorf("cannot determine owner") + return "", "", errors.New("cannot determine owner") } deviceID, err := client.server.VerifyAndResolveDeviceID(client.tlsDeviceID, signUp.DeviceID, claim) @@ -129,7 +130,7 @@ func signUpPostHandler(req *mux.Message, client *session) (*pool.Message, error) validUntil, ok := ValidUntil(token.Expiry) if !ok { - return nil, statusErrorf(coapCodes.Unauthorized, errFmtSignUP, fmt.Errorf("expired access token")) + return nil, statusErrorf(coapCodes.Unauthorized, errFmtSignUP, errors.New("expired access token")) } deviceID, owner, err := getSignUpDataFromClaims(req.Context(), client, token.AccessToken.String(), signUp) @@ -139,7 +140,7 @@ func signUpPostHandler(req *mux.Message, client *session) (*pool.Message, error) setDeviceIDToTracerSpan(req.Context(), deviceID) ctx := kitNetGrpc.CtxWithToken(req.Context(), token.AccessToken.String()) - if _, err := client.server.isClient.AddDevice(ctx, &pb.AddDeviceRequest{ + if _, err = client.server.isClient.AddDevice(ctx, &pb.AddDeviceRequest{ DeviceId: deviceID, }); err != nil { return nil, statusErrorf(coapconv.GrpcErr2CoapCode(err, coapconv.Update), errFmtSignUP, err) diff --git a/coap-gateway/service/utils_test.go b/coap-gateway/service/utils_test.go index 8aab65d32..eb7aad785 100644 --- a/coap-gateway/service/utils_test.go +++ b/coap-gateway/service/utils_test.go @@ -13,7 +13,6 @@ import ( "crypto/x509" "encoding/pem" "errors" - "fmt" "io" "os" "reflect" @@ -435,7 +434,7 @@ func testCoapDialWithHandler(t *testing.T, deviceID string, withTLS, identityCer InsecureSkipVerify: true, VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { if len(rawCerts) == 0 { - return fmt.Errorf("empty certificates chain") + return errors.New("empty certificates chain") } intermediateCAPool := x509.NewCertPool() certs := make([]*x509.Certificate, 0, len(rawCerts)) diff --git a/coap-gateway/uri/uri.go b/coap-gateway/uri/uri.go index 34e82e1f0..b198e124d 100644 --- a/coap-gateway/uri/uri.go +++ b/coap-gateway/uri/uri.go @@ -15,6 +15,8 @@ const ( ResourceDiscovery = Base + "/res" ResourceRoute = ApiV1 + "/devices" - InterfaceQueryKey = "if" - InterfaceQueryKeyPrefix = InterfaceQueryKey + "=" + InterfaceQueryKey = "if" + InterfaceQueryKeyPrefix = InterfaceQueryKey + "=" + ResourceTypeQueryKey = "rt" + ResourceTypeQueryKeyPrefix = ResourceTypeQueryKey + "=" ) diff --git a/dependency/googleapis b/dependency/googleapis index 29fdc0036..700240618 160000 --- a/dependency/googleapis +++ b/dependency/googleapis @@ -1 +1 @@ -Subproject commit 29fdc0036ce62452d188c5a246aa4664ef9cc4a3 +Subproject commit 7002406181eb300da880701035a25157a5099abb diff --git a/go.mod b/go.mod index 61995cfff..4fcdcdcbb 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/plgd-dev/hub/v2 -go 1.21 +go 1.22 toolchain go1.22.0 @@ -9,112 +9,100 @@ require ( github.com/felixge/httpsnoop v1.0.4 github.com/fsnotify/fsnotify v1.7.0 github.com/fullstorydev/grpchan v1.1.1 - github.com/fxamacker/cbor/v2 v2.5.0 + github.com/fxamacker/cbor/v2 v2.6.0 github.com/go-co-op/gocron v1.37.0 + github.com/go-co-op/gocron/v2 v2.3.0 github.com/gocql/gocql v1.6.0 - github.com/golang-jwt/jwt/v5 v5.2.0 + github.com/golang-jwt/jwt/v5 v5.2.1 github.com/golang/snappy v0.0.4 github.com/google/go-querystring v1.1.0 - github.com/google/uuid v1.5.0 + github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 github.com/gorilla/websocket v1.5.1 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 - github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 github.com/hashicorp/go-multierror v1.1.1 github.com/jessevdk/go-flags v1.5.0 github.com/json-iterator/go v1.1.12 github.com/jtacoma/uritemplates v1.0.0 github.com/karrick/tparse/v2 v2.8.2 - github.com/lestrrat-go/jwx/v2 v2.0.19 - github.com/nats-io/nats.go v1.31.0 - github.com/panjf2000/ants/v2 v2.9.0 - github.com/pion/dtls/v2 v2.2.8-0.20240102042511-9ffd96c827fe + github.com/lestrrat-go/jwx/v2 v2.0.21 + github.com/nats-io/nats.go v1.34.1 + github.com/panjf2000/ants/v2 v2.9.1 + github.com/pion/dtls/v2 v2.2.8-0.20240501061905-2c36d63320a0 github.com/pion/logging v0.2.2 - github.com/plgd-dev/device/v2 v2.2.5-0.20240112105119-e165727b7d41 - github.com/plgd-dev/go-coap/v3 v3.3.1 + github.com/plgd-dev/device/v2 v2.5.1-0.20240513064831-b553d1a87e1c + github.com/plgd-dev/go-coap/v3 v3.3.4 github.com/plgd-dev/kit/v2 v2.0.0-20211006190727-057b33161b90 github.com/pseudomuto/protoc-gen-doc v1.5.1 github.com/sirupsen/logrus v1.9.3 - github.com/stretchr/testify v1.8.4 - github.com/tidwall/gjson v1.17.0 + github.com/stretchr/testify v1.9.0 + github.com/tidwall/gjson v1.17.1 github.com/tidwall/sjson v1.2.5 github.com/ugorji/go/codec v1.2.12 github.com/vincent-petithory/dataurl v1.0.0 - go.mongodb.org/mongo-driver v1.13.1 - go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.46.1 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 - go.opentelemetry.io/otel v1.21.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 - go.opentelemetry.io/otel/metric v1.21.0 - go.opentelemetry.io/otel/sdk v1.21.0 - go.opentelemetry.io/otel/trace v1.21.0 + github.com/web-of-things-open-source/thingdescription-go v0.0.0-20240510130416-741fef736e1e + go.mongodb.org/mongo-driver v1.15.0 + go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.49.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 + go.opentelemetry.io/otel v1.24.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 + go.opentelemetry.io/otel/metric v1.24.0 + go.opentelemetry.io/otel/sdk v1.24.0 + go.opentelemetry.io/otel/trace v1.24.0 go.uber.org/atomic v1.11.0 - go.uber.org/zap v1.26.0 - golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e - golang.org/x/net v0.21.0 - golang.org/x/oauth2 v0.16.0 - golang.org/x/sync v0.6.0 - google.golang.org/api v0.156.0 - google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 - google.golang.org/grpc v1.60.1 + go.uber.org/zap v1.27.0 + golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f + golang.org/x/net v0.24.0 + golang.org/x/oauth2 v0.19.0 + golang.org/x/sync v0.7.0 + google.golang.org/genproto/googleapis/api v0.0.0-20240429193739-8cf5692501f6 + google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6 + google.golang.org/grpc v1.63.2 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 - google.golang.org/protobuf v1.32.0 + google.golang.org/protobuf v1.34.0 gopkg.in/yaml.v3 v3.0.1 ) require ( - cloud.google.com/go/compute v1.23.3 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/compute v1.25.1 // indirect github.com/Masterminds/semver v1.4.2 // indirect github.com/Masterminds/sprig v2.15.0+incompatible // indirect - github.com/a8m/envsubst v1.4.2 // indirect - github.com/alecthomas/participle/v2 v2.1.1 // indirect github.com/aokoli/goutils v1.0.1 // indirect - github.com/bufbuild/protocompile v0.7.1 // indirect - github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/bufbuild/protocompile v0.13.0 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect - github.com/dimchansky/utfbom v1.1.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dsnet/golib/memfile v1.0.0 // indirect - github.com/elliotchance/orderedmap v1.5.1 // indirect - github.com/envoyproxy/protoc-gen-validate v1.0.2 // indirect - github.com/fatih/color v1.16.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect + github.com/fredbi/uri v1.1.0 // indirect + github.com/go-json-experiment/json v0.0.0-20240418180308-af2d5061e6c2 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/goccy/go-yaml v1.11.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/s2a-go v0.1.7 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect - github.com/googleapis/gax-go/v2 v2.12.0 // indirect - github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 // indirect github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/huandu/xstrings v1.0.0 // indirect github.com/imdario/mergo v0.3.4 // indirect - github.com/jhump/protoreflect v1.15.4 // indirect - github.com/jinzhu/copier v0.4.0 // indirect - github.com/klauspost/compress v1.17.4 // indirect + github.com/jhump/protoreflect v1.16.0 // indirect + github.com/jonboulle/clockwork v0.4.0 // indirect + github.com/klauspost/compress v1.17.8 // indirect github.com/lestrrat-go/blackmagic v1.0.2 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect - github.com/lestrrat-go/httprc v1.0.4 // indirect + github.com/lestrrat-go/httprc v1.0.5 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/option v1.0.1 // indirect - github.com/magiconair/properties v1.8.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mikefarah/yq/v4 v4.41.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/montanaflynn/stats v0.7.1 // indirect github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007 // indirect github.com/nats-io/nkeys v0.4.7 // indirect github.com/nats-io/nuid v1.0.1 // indirect - github.com/pelletier/go-toml/v2 v2.1.1 // indirect - github.com/pion/transport/v3 v3.0.1 // indirect + github.com/pion/transport/v3 v3.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pseudomuto/protokit v0.2.0 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect @@ -126,21 +114,28 @@ require ( github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect - github.com/yuin/gopher-lua v1.1.1 // indirect - go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect - go.opentelemetry.io/proto/otlp v1.0.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect + go.opentelemetry.io/proto/otlp v1.2.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.19.0 // indirect - golang.org/x/sys v0.17.0 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/sys v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 // indirect ) -// note: github.com/pion/dtls/v2/pkg/net package is not yet available in release branches -exclude github.com/pion/dtls/v2 v2.2.9 +replace ( + // note: github.com/pion/dtls/v2/pkg/net package is not yet available in release branches, + // so we force to the use of the pinned master branch + github.com/pion/dtls/v2 => github.com/pion/dtls/v2 v2.2.8-0.20240501061905-2c36d63320a0 + // later versions require go 1.22 + github.com/youmark/pkcs8 => github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a + // later versions require go 1.21 + go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo => go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.49.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc => go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp => go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 + go.opentelemetry.io/otel => go.opentelemetry.io/otel v1.24.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc => go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 + go.opentelemetry.io/otel/metric => go.opentelemetry.io/otel/metric v1.24.0 + go.opentelemetry.io/otel/sdk => go.opentelemetry.io/otel/sdk v1.24.0 + go.opentelemetry.io/otel/trace => go.opentelemetry.io/otel/trace v1.24.0 +) diff --git a/go.sum b/go.sum index 3c80433ef..3a30a0030 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= -cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= +cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= +cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU= +cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -9,10 +10,6 @@ github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITg github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/sprig v2.15.0+incompatible h1:0gSxPGWS9PAr7U2NsQ2YQg6juRDINkUyuvbb4b2Xm8w= github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/a8m/envsubst v1.4.2 h1:4yWIHXOLEJHQEFd4UjrWDrYeYlV7ncFWJOCBRLOZHQg= -github.com/a8m/envsubst v1.4.2/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY= -github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8= -github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= github.com/aokoli/goutils v1.0.1 h1:7fpzNGoJ3VA8qcrm++XEE1QUe0mIwNeLa02Nwq7RDkg= github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -20,55 +17,56 @@ github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYE github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/bufbuild/protocompile v0.7.1 h1:Kd8fb6EshOHXNNRtYAmLAwy/PotlyFoN0iMbuwGNh0M= -github.com/bufbuild/protocompile v0.7.1/go.mod h1:+Etjg4guZoAqzVk2czwEQP12yaxLJ8DxuqCJ9qHdH94= +github.com/bufbuild/protocompile v0.13.0 h1:6cwUB0Y2tSvmNxsbunwzmIto3xOlJOV7ALALuVOs92M= +github.com/bufbuild/protocompile v0.13.0/go.mod h1:dr++fGGeMPWHv7jPeT06ZKukm45NJscd7rUxQVzEKRk= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= -github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/dsnet/golib/memfile v0.0.0-20190531212259-571cdbcff553/go.mod h1:tXGNW9q3RwvWt1VV2qrRKlSSz0npnh12yftCSCy2T64= github.com/dsnet/golib/memfile v0.0.0-20200723050859-c110804dfa93/go.mod h1:tXGNW9q3RwvWt1VV2qrRKlSSz0npnh12yftCSCy2T64= github.com/dsnet/golib/memfile v1.0.0 h1:J9pUspY2bDCbF9o+YGwcf3uG6MdyITfh/Fk3/CaEiFs= github.com/dsnet/golib/memfile v1.0.0/go.mod h1:tXGNW9q3RwvWt1VV2qrRKlSSz0npnh12yftCSCy2T64= -github.com/elliotchance/orderedmap v1.5.1 h1:G1X4PYlljzimbdQ3RXmtIZiQ9d6aRQ3sH1nzjq5mECE= -github.com/elliotchance/orderedmap v1.5.1/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= -github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= +github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/favadi/protoc-go-inject-tag v1.4.0 h1:K3KXxbgRw5WT4f43LbglARGz/8jVsDOS7uMjG4oNvXY= github.com/favadi/protoc-go-inject-tag v1.4.0/go.mod h1:AZ+PK+QDKUOLlBRG0rYiKkUX5Hw7+7GTFzlU99GFSbQ= +github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= +github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fredbi/uri v1.1.0 h1:OqLpTXtyRg9ABReqvDGdJPqZUxs8cyBDOMXBbskCaB8= +github.com/fredbi/uri v1.1.0/go.mod h1:aYTUoAXBOq7BLfVJ8GnKmfcuURosB1xyHDIfWeC/iW4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fullstorydev/grpchan v1.1.1 h1:heQqIJlAv5Cnks9a70GRL2EJke6QQoUB25VGR6TZQas= github.com/fullstorydev/grpchan v1.1.1/go.mod h1:f4HpiV8V6htfY/K44GWV1ESQzHBTq7DinhzqQ95lpgc= github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE= -github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA= +github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-acme/lego v2.7.2+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0= github.com/go-co-op/gocron v1.37.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY= +github.com/go-co-op/gocron/v2 v2.3.0 h1:UmXdUuql3h/+JN3rFDhZdDvgeCjR+r/zrSsQNZje8uo= +github.com/go-co-op/gocron/v2 v2.3.0/go.mod h1:ckPQw96ZuZLRUGu88vVpd9a6d9HakI14KWahFZtGvNw= +github.com/go-json-experiment/json v0.0.0-20240418180308-af2d5061e6c2 h1:lhCu2IkNoFfDdcjHos2ZtLdAsyxLZbkpijNzhvvM6BY= +github.com/go-json-experiment/json v0.0.0-20240418180308-af2d5061e6c2/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -83,8 +81,6 @@ github.com/go-ocf/kit v0.0.0-20200728130040-4aebdb6982bc/go.mod h1:TIsoMT/iB7t9P github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I= -github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= github.com/gocql/gocql v1.6.0 h1:IdFdOTbnpbd0pDhl4REKQDM+Q0SzKXQ1Yh+YZZ8T/qU= github.com/gocql/gocql v1.6.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -92,12 +88,9 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= -github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -109,12 +102,9 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -124,23 +114,19 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= -github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= -github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= @@ -149,10 +135,10 @@ github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZH github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1 h1:HcUWd006luQPljE73d5sk+/VgYPGUReEVz2y1/qylwY= -github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1/go.mod h1:w9Y7gY31krpLmrVU5ZPG9H7l9fZuRu5/3R3S3FMtVQ4= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -171,10 +157,10 @@ github.com/jhump/gopoet v0.0.0-20190322174617-17282ff210b3/go.mod h1:me9yfT6IJSl github.com/jhump/gopoet v0.1.0/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7UvLHRQ= github.com/jhump/protoreflect v1.11.0/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E= -github.com/jhump/protoreflect v1.15.4 h1:mrwJhfQGGljwvR/jPEocli8KA6G9afbQpH8NY2wORcI= -github.com/jhump/protoreflect v1.15.4/go.mod h1:2B+zwrnMY3TTIqEK01OG/d3pyUycQBfDf+bx8fE2DNg= -github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= -github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/jhump/protoreflect v1.16.0 h1:54fZg+49widqXYQ0b+usAFHbMkBGR4PpXrsHc8+TBDg= +github.com/jhump/protoreflect v1.16.0/go.mod h1:oYPd7nPvcBw/5wlDfm/AVmU9zH9BgqGCI469pGxfj/8= +github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= +github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtacoma/uritemplates v1.0.0 h1:xwx5sBF7pPAb0Uj8lDC1Q/aBPpOFyQza7OC705ZlLCo= @@ -187,15 +173,15 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= +github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -204,71 +190,55 @@ github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= -github.com/lestrrat-go/httprc v1.0.4 h1:bAZymwoZQb+Oq8MEbyipag7iSq6YIga8Wj6GOiJGdI8= -github.com/lestrrat-go/httprc v1.0.4/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= +github.com/lestrrat-go/httprc v1.0.5 h1:bsTfiH8xaKOJPrg1R+E3iE/AWZr/x0Phj9PBTG/OLUk= +github.com/lestrrat-go/httprc v1.0.5/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= github.com/lestrrat-go/iter v0.0.0-20200422075355-fc1769541911/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= github.com/lestrrat-go/jwx v1.0.2/go.mod h1:TPF17WiSFegZo+c20fdpw49QD+/7n4/IsGvEmCSWwT0= -github.com/lestrrat-go/jwx/v2 v2.0.19 h1:ekv1qEZE6BVct89QA+pRF6+4pCpfVrOnEJnTnT4RXoY= -github.com/lestrrat-go/jwx/v2 v2.0.19/go.mod h1:l3im3coce1lL2cDeAjqmaR+Awx+X8Ih+2k8BuHNJ4CU= +github.com/lestrrat-go/jwx/v2 v2.0.21 h1:jAPKupy4uHgrHFEdjVjNkUgoBKtVDgrQPB/h55FHrR0= +github.com/lestrrat-go/jwx/v2 v2.0.21/go.mod h1:09mLW8zto6bWL9GbwnqAli+ArLf+5M33QLQPDggkUWM= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/lestrrat-go/pdebug v0.0.0-20200204225717-4d6bd78da58d/go.mod h1:B06CSso/AWxiPejj+fheUINGeBKeeEZNt8w+EoU7+L8= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/mikefarah/yq/v4 v4.41.1 h1:HIyr+VCctIpiMl/mhplQJkzvsFeEVv7AurigXeX4cuI= -github.com/mikefarah/yq/v4 v4.41.1/go.mod h1:Qpwgwtmrn3XBeeACw0z8brRNBijX8Lw2RIiJFmS0+uE= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007 h1:28i1IjGcx8AofiB4N3q5Yls55VEaitzuEPkFJEVgGkA= github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= -github.com/nats-io/nats.go v1.31.0 h1:/WFBHEc/dOKBF6qf1TZhrdEfTmOZ5JzdJ+Y3m6Y/p7E= -github.com/nats-io/nats.go v1.31.0/go.mod h1:di3Bm5MLsoB4Bx61CBTsxuarI36WbhAwOm8QrW39+i8= +github.com/nats-io/nats.go v1.34.1 h1:syWey5xaNHZgicYBemv0nohUPPmaLteiBEUT6Q5+F/4= +github.com/nats-io/nats.go v1.34.1/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/panjf2000/ants/v2 v2.4.3/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A= -github.com/panjf2000/ants/v2 v2.9.0 h1:SztCLkVxBRigbg+vt0S5QvF5vxAbxbKt09/YfAJ0tEo= -github.com/panjf2000/ants/v2 v2.9.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I= +github.com/panjf2000/ants/v2 v2.9.1 h1:Q5vh5xohbsZXGcD6hhszzGqB7jSSc2/CRr3QKIga8Kw= +github.com/panjf2000/ants/v2 v2.9.1/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= -github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/pion/dtls/v2 v2.0.1-0.20200503085337-8e86b3a7d585/go.mod h1:/GahSOC8ZY/+17zkaGJIG4OUkSGAcZu/N/g3roBOCkM= -github.com/pion/dtls/v2 v2.0.10-0.20210502094952-3dc563b9aede/go.mod h1:86wv5dgx2J/z871nUR+5fTTY9tISLUlo+C5Gm86r1Hs= -github.com/pion/dtls/v2 v2.2.8-0.20240102042511-9ffd96c827fe h1:irKb6v72GiA86l+Tp0UZjSZqiOj8LwFxBDvzkhmoO/w= -github.com/pion/dtls/v2 v2.2.8-0.20240102042511-9ffd96c827fe/go.mod h1:eWsePf7yhHNQGu6UKU6xs2XMUYpHX2uqqvyZhNB6ooA= +github.com/pion/dtls/v2 v2.2.8-0.20240501061905-2c36d63320a0 h1:050ahk2K4HqwxPi2YM6Yc4lIttwNSY2+n9xPVsS3zoQ= +github.com/pion/dtls/v2 v2.2.8-0.20240501061905-2c36d63320a0/go.mod h1:tjBBbkwKGSQQZl36HQa2va5HqR9rWhujhlJMrgE2b/o= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= -github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE= -github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= -github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZIWJ6q9A= -github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM= -github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= -github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= +github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4= +github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/plgd-dev/device/v2 v2.2.5-0.20240112105119-e165727b7d41 h1:1oTDtgAjVkeOX63MzvdiB/kx2RS1UYOtQMs6ywNejvA= -github.com/plgd-dev/device/v2 v2.2.5-0.20240112105119-e165727b7d41/go.mod h1:US5p507VILktZa6Ep77gTEqwY8F+IbVUmtbt5aTtdlI= +github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= +github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= +github.com/plgd-dev/device/v2 v2.5.1-0.20240513064831-b553d1a87e1c h1:kNF2KvyCzA8IMERdHUrL/LMdsuZM/tXGjLVzEX2lcg4= +github.com/plgd-dev/device/v2 v2.5.1-0.20240513064831-b553d1a87e1c/go.mod h1:2mFPs55x2Li76zkrHdRNY3yOqVWSh59hiUw+6FYXA0k= github.com/plgd-dev/go-coap/v2 v2.0.4-0.20200819112225-8eb712b901bc/go.mod h1:+tCi9Q78H/orWRtpVWyBgrr4vKFo2zYtbbxUllerBp4= github.com/plgd-dev/go-coap/v2 v2.4.1-0.20210517130748-95c37ac8e1fa/go.mod h1:rA7fc7ar+B/qa+Q0hRqv7yj/EMtIlmo1l7vkQGSrHPU= -github.com/plgd-dev/go-coap/v3 v3.3.1 h1:oxVnn5MAOEWImoGgoG5S0grTXQXqhNu5gQqW7zMPu40= -github.com/plgd-dev/go-coap/v3 v3.3.1/go.mod h1:r3KbxxpBgOPmC0fh6y0yUrDvJh3LdCrhD2zdRr0L1j4= +github.com/plgd-dev/go-coap/v3 v3.3.4 h1:clDLFOXXmXfhZqB0eSk6WJs2iYfjC2J22Ixwu5MHiO0= +github.com/plgd-dev/go-coap/v3 v3.3.4/go.mod h1:vxBvAgXxL+Au/58XYTM+8ftqO/ycFC9/Dh+uI72xYjA= github.com/plgd-dev/kit v0.0.0-20200819113605-d5fcf3e94f63/go.mod h1:Yl9zisyXfPdtP9hTWlJqjJYXmgU/jtSDKttz9/CeD90= github.com/plgd-dev/kit/v2 v2.0.0-20211006190727-057b33161b90 h1:TC1HJ/UbyflJFPvaOdGmNZ5TeFGex1/dyr9urNGLy7M= github.com/plgd-dev/kit/v2 v2.0.0-20211006190727-057b33161b90/go.mod h1:Z7oKFLSGQjdi8eInxwFCs0tSApuEM1o0qNck+sJYp4M= @@ -285,6 +255,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -294,6 +265,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -302,13 +274,13 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= -github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= +github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= @@ -325,6 +297,8 @@ github.com/valyala/fasthttp v1.12.0/go.mod h1:229t1eWu9UXTPmoUkbpN/fctKPBY4IJoFX github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI= github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U= +github.com/web-of-things-open-source/thingdescription-go v0.0.0-20240510130416-741fef736e1e h1:blQyU8WqqyRcBmaAPLiU5cTg9BSQu04CJZ/ffEzgI1s= +github.com/web-of-things-open-source/thingdescription-go v0.0.0-20240510130416-741fef736e1e/go.mod h1:L/jWuWf+v7rmuFykpUP/runRXTnnA0QdGGgou8vzPrw= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= @@ -333,38 +307,33 @@ github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= -github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= -go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk= -go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.46.1 h1:C6OqX3inTcc1vUX2BL7Au7cQO20/0fCI02XdInR8m5Y= -go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.46.1/go.mod h1:M9ZtzJcGI4ejexSjUP69JmhbzAe93mu2xUBH3QBUtLM= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= -go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= -go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0/go.mod h1:zgBdWWAu7oEEMC06MMKc5NLbA/1YDXV1sMpSqEeLQLg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0= -go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= -go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= -go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= -go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc= +go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= +go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.49.0 h1:qF3LdpkD3Kbaw0Smsh+SVcJI/mtYGz9ZdCmu0YF2Lo4= +go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.49.0/go.mod h1:eqNF9g7W06ubrU7jk6M6UW9OTrcSPZvVY10cw9DUJ7c= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= +go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM= +go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= +go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= +go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= +go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= +go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= +go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= +go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= +go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -373,6 +342,7 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -382,28 +352,23 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e h1:723BNChdd0c2Wk6WOE320qGBiPtYx0F0Bbm1kriShfE= -golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= +golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -428,25 +393,19 @@ golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210502030024-e5908800b52b/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= +golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -456,8 +415,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -474,22 +433,19 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -498,7 +454,6 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -523,34 +478,25 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/api v0.156.0 h1:yloYcGbBtVYjLKQe4enCunxvwn3s2w/XPrrhVf6MsvQ= -google.golang.org/api v0.156.0/go.mod h1:bUSmn4KFO0Q+69zo9CNIDp4Psi6BqM0np0CbzKRSiSY= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg= -google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0= -google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1 h1:OPXtXn7fNMaXwO3JvOmF1QyTc00jsSFFz1vXXBOdCDo= -google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 h1:gphdwh0npgs8elJ4T6J+DQJHPVF7RsuJHCfwztUb4J4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= +google.golang.org/genproto/googleapis/api v0.0.0-20240429193739-8cf5692501f6 h1:DTJM0R8LECCgFeUwApvcEJHz85HLagW8uRENYxHh1ww= +google.golang.org/genproto/googleapis/api v0.0.0-20240429193739-8cf5692501f6/go.mod h1:10yRODfgim2/T8csjQsMPgZOMvtytXKTDRzH6HRGzRw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6 h1:DujSIu+2tC9Ht0aPNA7jgj23Iq8Ewi5sgkQ++wdvonE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240429193739-8cf5692501f6/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= -google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -564,8 +510,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4= +google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -574,8 +520,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 h1:6D+BvnJ/j6e222UW8s2qTSe3wGBtvo0MbVQG/c5k8RE= -gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473/go.mod h1:N1eN2tsCx0Ydtgjl4cqmbRCsY4/+z4cYDeqwZTk6zog= gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/grpc-gateway/client/client.go b/grpc-gateway/client/client.go index d4959d88f..c049e8255 100644 --- a/grpc-gateway/client/client.go +++ b/grpc-gateway/client/client.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "crypto/x509" + "errors" "fmt" "sync" "time" @@ -44,7 +45,7 @@ func New(client pb.GrpcGatewayClient) *Client { // NewFromConfig constructs a new client client. For every call there is expected jwt token for grpc stored in context. func NewFromConfig(cfg *Config, tlsCfg *tls.Config) (*Client, error) { if cfg == nil || cfg.GatewayAddress == "" { - return nil, fmt.Errorf("missing client client config") + return nil, errors.New("missing client config") } keepAlive := keepalive.ClientParameters{ @@ -52,7 +53,7 @@ func NewFromConfig(cfg *Config, tlsCfg *tls.Config) (*Client, error) { PermitWithoutStream: true, } - conn, err := grpc.Dial(cfg.GatewayAddress, grpc.WithKeepaliveParams(keepAlive), grpc.WithTransportCredentials(credentials.NewTLS(tlsCfg))) + conn, err := grpc.NewClient(cfg.GatewayAddress, grpc.WithKeepaliveParams(keepAlive), grpc.WithTransportCredentials(credentials.NewTLS(tlsCfg))) if err != nil { return nil, fmt.Errorf("cannot create certificate authority client: %w", err) } @@ -194,19 +195,20 @@ func (c *Client) GetResourceLinksIterator(ctx context.Context, deviceIDs []strin // GetResourcesIterator gets resources contents from resource twin (cache of backend). JWT token must be stored in context for grpc call. // By resourceIDs you can specify resources by deviceID and Href which will be retrieved from the backend, nil means all resources. // Or by deviceIDs or resourceTypes you can filter output when you get all resources. -// Eg: +// +// Example: // // get all resources // it := client.GetResourcesIterator(ctx, nil, nil) // // get all oic.wk.d resources -// iter := client.GetResourcesIterator(ctx, nil, nil, "oic.wk.d") +// it := client.GetResourcesIterator(ctx, nil, nil, "oic.wk.d") // // get oic.wk.d resources of 2 devices -// iter := client.GetResourcesIterator(ctx, nil, string["60f6869d-343a-4989-7462-81ef215d31af", "07ef9eb6-1ce9-4ce4-73a6-9ee0a1d534d2"], "oic.wk.d") +// it := client.GetResourcesIterator(ctx, nil, string["60f6869d-343a-4989-7462-81ef215d31af", "07ef9eb6-1ce9-4ce4-73a6-9ee0a1d534d2"], "oic.wk.d") // // get a certain resource /oic/p of the device"60f6869d-343a-4989-7462-81ef215d31af" -// iter := client.GetResourcesIterator(ctx, commands.NewResourceID("60f6869d-343a-4989-7462-81ef215d31af", /oic/p), nil) +// it := client.GetResourcesIterator(ctx, commands.NewResourceID("60f6869d-343a-4989-7462-81ef215d31af", /oic/p), nil) // // Next queries the next resource value. // Returns false when failed or having no more items. diff --git a/grpc-gateway/client/client_test.go b/grpc-gateway/client/client_test.go index b487e5c38..94555f8c9 100644 --- a/grpc-gateway/client/client_test.go +++ b/grpc-gateway/client/client_test.go @@ -111,9 +111,6 @@ func (h *gatewayHandler) GetResources(_ *pb.GetResourcesRequest, srv pb.GrpcGate if err != nil { return err } - if err != nil { - return err - } return nil } diff --git a/grpc-gateway/client/createResource_test.go b/grpc-gateway/client/createResource_test.go index 28c4b0c97..aad482a4c 100644 --- a/grpc-gateway/client/createResource_test.go +++ b/grpc-gateway/client/createResource_test.go @@ -70,17 +70,17 @@ func TestClient_CreateResource(t *testing.T) { c := NewTestClient(t) defer func() { err := c.Close() - assert.NoError(t, err) + require.NoError(t, err) }() _, shutdownDevSim := test.OnboardDevSim(ctx, t, c.GrpcGatewayClient(), deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) defer shutdownDevSim() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctx, cancel := context.WithTimeout(ctx, time.Second) + runctx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() var got interface{} - err := c.CreateResource(ctx, tt.args.deviceID, tt.args.href, tt.args.data, &got) + err := c.CreateResource(runctx, tt.args.deviceID, tt.args.href, tt.args.data, &got) if tt.wantErr { require.Error(t, err) var grpcStatus interface { diff --git a/grpc-gateway/client/deleteResource_test.go b/grpc-gateway/client/deleteResource_test.go index 501bb2d65..9dbc20918 100644 --- a/grpc-gateway/client/deleteResource_test.go +++ b/grpc-gateway/client/deleteResource_test.go @@ -15,7 +15,6 @@ import ( "github.com/plgd-dev/hub/v2/test/config" oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test" "github.com/plgd-dev/hub/v2/test/service" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -87,7 +86,7 @@ func TestClientDeleteResource(t *testing.T) { c := NewTestClient(t) defer func() { err := c.Close() - assert.NoError(t, err) + require.NoError(t, err) }() _, shutdownDevsim := test.OnboardDevSim(ctx, t, c.GrpcGatewayClient(), deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) defer shutdownDevsim() @@ -95,10 +94,10 @@ func TestClientDeleteResource(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctx, cancel := context.WithTimeout(ctx, time.Second) + runctx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() var got interface{} - err := c.DeleteResource(ctx, tt.args.deviceID, tt.args.href, &got) + err := c.DeleteResource(runctx, tt.args.deviceID, tt.args.href, &got) if tt.wantErr { require.Error(t, err) return @@ -176,7 +175,7 @@ func TestClientBatchDeleteResource(t *testing.T) { c := NewTestClient(t) defer func() { err := c.Close() - assert.NoError(t, err) + require.NoError(t, err) }() _, shutdown := test.OnboardDevSim(ctx, t, c.GrpcGatewayClient(), deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) defer shutdown() @@ -184,10 +183,10 @@ func TestClientBatchDeleteResource(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctx, cancel := context.WithTimeout(ctx, time.Second) + runctx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() var got test.CollectionLinkRepresentations - err := c.DeleteResource(ctx, tt.args.deviceID, tt.args.href, &got, client.WithInterface(interfaces.OC_IF_B)) + err := c.DeleteResource(runctx, tt.args.deviceID, tt.args.href, &got, client.WithInterface(interfaces.OC_IF_B)) if tt.wantErr { require.Error(t, err) require.Equal(t, tt.wantErrCode, status.Convert(err).Code()) diff --git a/grpc-gateway/client/deviceSubscriber.go b/grpc-gateway/client/deviceSubscriber.go index e4daefa5e..23e76688a 100644 --- a/grpc-gateway/client/deviceSubscriber.go +++ b/grpc-gateway/client/deviceSubscriber.go @@ -322,7 +322,7 @@ func (s *DeviceSubscriber) createSpanEvent(ctx context.Context, name string) (co opentelemetry.InstrumentationName, trace.WithInstrumentationVersion(opentelemetry.SemVersion()), ) - return tracer.Start(ctx, name, trace.WithSpanKind(trace.SpanKindConsumer)) + return tracer.Start(ctx, name, trace.WithSpanKind(trace.SpanKindConsumer)) //nolint:spancheck } func (s *DeviceSubscriber) processPendingCommand(ctx context.Context, h *DeviceSubscriptionHandlers, ev *pbGRPC.PendingCommand) error { diff --git a/grpc-gateway/client/deviceSubscriptions.go b/grpc-gateway/client/deviceSubscriptions.go index 6ce78045e..e1579ae94 100644 --- a/grpc-gateway/client/deviceSubscriptions.go +++ b/grpc-gateway/client/deviceSubscriptions.go @@ -114,7 +114,7 @@ func (s *DeviceSubscriptions) doOp(ctx context.Context, req *pb.SubscribeToEvent return nil, fmt.Errorf("unexpected event %+v", ev) } if op.GetErrorStatus().GetCode() != pb.Event_OperationProcessed_ErrorStatus_OK { - return nil, fmt.Errorf(op.GetErrorStatus().GetMessage()) + return nil, errors.New(op.GetErrorStatus().GetMessage()) } return ev, nil } @@ -146,70 +146,70 @@ type deviceSub struct { func (s *deviceSub) HandleResourcePublished(ctx context.Context, val *events.ResourceLinksPublished) error { if s.ResourcePublishedHandler == nil { - return fmt.Errorf("ResourcePublishedHandler in not supported") + return errors.New("ResourcePublishedHandler in not supported") } return s.ResourcePublishedHandler.HandleResourcePublished(ctx, val) } func (s *deviceSub) HandleResourceUnpublished(ctx context.Context, val *events.ResourceLinksUnpublished) error { if s.ResourceUnpublishedHandler == nil { - return fmt.Errorf("ResourceUnpublishedHandler in not supported") + return errors.New("ResourceUnpublishedHandler in not supported") } return s.ResourceUnpublishedHandler.HandleResourceUnpublished(ctx, val) } func (s *deviceSub) HandleResourceUpdatePending(ctx context.Context, val *events.ResourceUpdatePending) error { if s.ResourceUpdatePendingHandler == nil { - return fmt.Errorf("ResourceUpdatePendingHandler in not supported") + return errors.New("ResourceUpdatePendingHandler in not supported") } return s.ResourceUpdatePendingHandler.HandleResourceUpdatePending(ctx, val) } func (s *deviceSub) HandleResourceUpdated(ctx context.Context, val *events.ResourceUpdated) error { if s.ResourceUpdatedHandler == nil { - return fmt.Errorf("ResourceUpdatedHandler in not supported") + return errors.New("ResourceUpdatedHandler in not supported") } return s.ResourceUpdatedHandler.HandleResourceUpdated(ctx, val) } func (s *deviceSub) HandleResourceRetrievePending(ctx context.Context, val *events.ResourceRetrievePending) error { if s.ResourceRetrievePendingHandler == nil { - return fmt.Errorf("ResourceRetrievePendingHandler in not supported") + return errors.New("ResourceRetrievePendingHandler in not supported") } return s.ResourceRetrievePendingHandler.HandleResourceRetrievePending(ctx, val) } func (s *deviceSub) HandleResourceRetrieved(ctx context.Context, val *events.ResourceRetrieved) error { if s.ResourceRetrievedHandler == nil { - return fmt.Errorf("ResourceRetrievedHandler in not supported") + return errors.New("ResourceRetrievedHandler in not supported") } return s.ResourceRetrievedHandler.HandleResourceRetrieved(ctx, val) } func (s *deviceSub) HandleResourceDeletePending(ctx context.Context, val *events.ResourceDeletePending) error { if s.ResourceDeletePendingHandler == nil { - return fmt.Errorf("ResourceDeletePendingHandler in not supported") + return errors.New("ResourceDeletePendingHandler in not supported") } return s.ResourceDeletePendingHandler.HandleResourceDeletePending(ctx, val) } func (s *deviceSub) HandleResourceDeleted(ctx context.Context, val *events.ResourceDeleted) error { if s.ResourceDeletedHandler == nil { - return fmt.Errorf("ResourceDeletedHandler in not supported") + return errors.New("ResourceDeletedHandler in not supported") } return s.ResourceDeletedHandler.HandleResourceDeleted(ctx, val) } func (s *deviceSub) HandleResourceCreatePending(ctx context.Context, val *events.ResourceCreatePending) error { if s.ResourceCreatePendingHandler == nil { - return fmt.Errorf("ResourceCreatePendingHandler in not supported") + return errors.New("ResourceCreatePendingHandler in not supported") } return s.ResourceCreatePendingHandler.HandleResourceCreatePending(ctx, val) } func (s *deviceSub) HandleResourceCreated(ctx context.Context, val *events.ResourceCreated) error { if s.ResourceCreatedHandler == nil { - return fmt.Errorf("ResourceCreatedHandler in not supported") + return errors.New("ResourceCreatedHandler in not supported") } return s.ResourceCreatedHandler.HandleResourceCreated(ctx, val) } @@ -229,7 +229,7 @@ func (s *Subcription) Cancel(ctx context.Context) error { func getSubscribeTypeAndHandler(closeErrorHandler SubscriptionHandler, handle interface{}) ([]pb.SubscribeToEvents_CreateSubscription_Event, *deviceSub, error) { if closeErrorHandler == nil { - return nil, nil, fmt.Errorf("invalid closeErrorHandler") + return nil, nil, errors.New("invalid closeErrorHandler") } var resourcePublishedHandler ResourcePublishedHandler var resourceUnpublishedHandler ResourceUnpublishedHandler @@ -285,7 +285,7 @@ func getSubscribeTypeAndHandler(closeErrorHandler SubscriptionHandler, handle in } if len(filterEvents) == 0 { - return nil, nil, fmt.Errorf("invalid handler - supported handlers: ResourcePublishedHandler, ResourceUnpublishedHandler, ResourceUpdatePendingHandler, ResourceUpdatedHandler, ResourceRetrievePendingHandler, ResourceRetrievedHandler, ResourceDeletePendingHandler, ResourceDeletedHandler, ResourceCreatePendingHandler, ResourceCreatedHandler") + return nil, nil, errors.New("invalid handler - supported handlers: ResourcePublishedHandler, ResourceUnpublishedHandler, ResourceUpdatePendingHandler, ResourceUpdatedHandler, ResourceRetrievePendingHandler, ResourceRetrievedHandler, ResourceDeletePendingHandler, ResourceDeletedHandler, ResourceCreatePendingHandler, ResourceCreatedHandler") } return filterEvents, &deviceSub{ @@ -306,7 +306,7 @@ func getSubscribeTypeAndHandler(closeErrorHandler SubscriptionHandler, handle in func (s *DeviceSubscriptions) Subscribe(ctx context.Context, deviceID string, closeErrorHandler SubscriptionHandler, handle interface{}) (*Subcription, error) { token, err := uuid.NewRandom() if err != nil { - return nil, fmt.Errorf("cannot generate token for subscribe") + return nil, errors.New("cannot generate token for subscribe") } filterEvents, eh, err := getSubscribeTypeAndHandler(closeErrorHandler, handle) @@ -336,7 +336,7 @@ func (s *DeviceSubscriptions) Subscribe(ctx context.Context, deviceID string, cl } cancelToken, err := uuid.NewRandom() if err != nil { - return fmt.Errorf("cannot generate token for cancellation") + return errors.New("cannot generate token for cancellation") } _, err = s.doOp(ctx, &pb.SubscribeToEvents{ Action: &pb.SubscribeToEvents_CancelSubscription_{ @@ -437,7 +437,7 @@ func (s *DeviceSubscriptions) handleSubscriptionCanceled(e *pb.Event) (processed ha.OnClose() return true } - ha.Error(fmt.Errorf(reason)) + ha.Error(errors.New(reason)) return true } diff --git a/grpc-gateway/client/deviceSubscriptions_test.go b/grpc-gateway/client/deviceSubscriptions_test.go index def1d9155..15d19b7a3 100644 --- a/grpc-gateway/client/deviceSubscriptions_test.go +++ b/grpc-gateway/client/deviceSubscriptions_test.go @@ -23,7 +23,6 @@ import ( pbTest "github.com/plgd-dev/hub/v2/test/pb" "github.com/plgd-dev/hub/v2/test/service" "github.com/plgd-dev/kit/v2/codec/cbor" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/trace/noop" ) @@ -85,8 +84,8 @@ func TestObserveDeviceResourcesRetrieve(t *testing.T) { c := NewTestClient(t) defer func() { - err := c.Close() - assert.NoError(t, err) + errC := c.Close() + require.NoError(t, errC) }() deviceID, shutdownDevSim := test.OnboardDevSim(ctx, t, c.GrpcGatewayClient(), deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) defer shutdownDevSim() @@ -95,8 +94,8 @@ func TestObserveDeviceResourcesRetrieve(t *testing.T) { sub, err := c.NewDeviceSubscription(ctx, deviceID, h) require.NoError(t, err) defer func() { - wait, err := sub.Cancel() - require.NoError(t, err) + wait, errC := sub.Cancel() + require.NoError(t, errC) wait() }() @@ -115,14 +114,14 @@ func TestObserveDeviceResourcesRetrieve(t *testing.T) { SubscriptionId: sub.ID(), CorrelationId: retrieveCorrelationID, Type: &pb.Event_ResourceRetrievePending{ - ResourceRetrievePending: pbTest.MakeResourceRetrievePending(deviceID, platform.ResourceURI, retrieveCorrelationID), + ResourceRetrievePending: pbTest.MakeResourceRetrievePending(deviceID, platform.ResourceURI, []string{platform.ResourceType}, retrieveCorrelationID), }, }, { SubscriptionId: sub.ID(), CorrelationId: retrieveCorrelationID, Type: &pb.Event_ResourceRetrieved{ - ResourceRetrieved: pbTest.MakeResourceRetrieved(t, deviceID, platform.ResourceURI, retrieveCorrelationID, + ResourceRetrieved: pbTest.MakeResourceRetrieved(t, deviceID, platform.ResourceURI, []string{platform.ResourceType}, retrieveCorrelationID, map[string]interface{}{ "mnmn": "ocfcloud.com", "x.org.iotivity.version": test.GetIotivityLiteVersion(t, deviceID), @@ -180,8 +179,8 @@ func TestObserveDeviceResourcesUpdate(t *testing.T) { c := NewTestClient(t) defer func() { - err := c.Close() - assert.NoError(t, err) + errC := c.Close() + require.NoError(t, errC) }() deviceID, shutdownDevSim := test.OnboardDevSim(ctx, t, c.GrpcGatewayClient(), deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) defer shutdownDevSim() @@ -193,8 +192,8 @@ func TestObserveDeviceResourcesUpdate(t *testing.T) { sub, err := c.NewDeviceSubscription(ctx, deviceID, h) require.NoError(t, err) defer func() { - wait, err := sub.Cancel() - require.NoError(t, err) + wait, errC := sub.Cancel() + require.NoError(t, errC) wait() }() @@ -208,8 +207,8 @@ func TestObserveDeviceResourcesUpdate(t *testing.T) { v := map[string]interface{}{ "value": true, } - d, err := cbor.Encode(v) - require.NoError(t, err) + d, errEnc := cbor.Encode(v) + require.NoError(t, errEnc) return d }(), }, @@ -225,7 +224,7 @@ func TestObserveDeviceResourcesUpdate(t *testing.T) { SubscriptionId: sub.ID(), CorrelationId: updCorrelationID, Type: &pb.Event_ResourceUpdatePending{ - ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceSwitchesInstanceHref(switchID), updCorrelationID, + ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceSwitchesInstanceHref(switchID), test.TestResourceSwitchesInstanceResourceTypes, updCorrelationID, map[string]interface{}{ "value": true, }), @@ -253,7 +252,8 @@ func TestObserveDeviceResourcesUpdate(t *testing.T) { return d }(), }, - AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, updCorrelationID, oauthService.DeviceUserID), + AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, updCorrelationID, oauthService.DeviceUserID), + ResourceTypes: test.TestResourceSwitchesInstanceResourceTypes, }, }, }, @@ -308,8 +308,8 @@ func TestObserveDeviceResourcesCreateAndDelete(t *testing.T) { c := NewTestClient(t) defer func() { - err := c.Close() - assert.NoError(t, err) + errC := c.Close() + require.NoError(t, errC) }() deviceID, shutdownDevSim := test.OnboardDevSim(ctx, t, c.GrpcGatewayClient(), deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) defer shutdownDevSim() @@ -318,8 +318,8 @@ func TestObserveDeviceResourcesCreateAndDelete(t *testing.T) { sub, err := c.NewDeviceSubscription(ctx, deviceID, h) require.NoError(t, err) defer func() { - wait, err := sub.Cancel() - require.NoError(t, err) + wait, errC := sub.Cancel() + require.NoError(t, errC) wait() }() @@ -345,7 +345,7 @@ func TestObserveDeviceResourcesCreateAndDelete(t *testing.T) { SubscriptionId: sub.ID(), CorrelationId: createCorrelationID, Type: &pb.Event_ResourceCreatePending{ - ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, test.TestResourceSwitchesHref, createCorrelationID, + ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, test.TestResourceSwitchesHref, test.TestResourceSwitchesResourceTypes, createCorrelationID, test.MakeSwitchResourceDefaultData()), }, }, @@ -353,7 +353,7 @@ func TestObserveDeviceResourcesCreateAndDelete(t *testing.T) { SubscriptionId: sub.ID(), CorrelationId: createCorrelationID, Type: &pb.Event_ResourceCreated{ - ResourceCreated: pbTest.MakeResourceCreated(t, deviceID, test.TestResourceSwitchesHref, createCorrelationID, + ResourceCreated: pbTest.MakeResourceCreated(t, deviceID, test.TestResourceSwitchesHref, test.TestResourceSwitchesResourceTypes, createCorrelationID, pbTest.MakeCreateSwitchResourceResponseData(switchID)), }, }, @@ -383,7 +383,7 @@ func TestObserveDeviceResourcesCreateAndDelete(t *testing.T) { SubscriptionId: sub.ID(), CorrelationId: delCorrelationID, Type: &pb.Event_ResourceDeletePending{ - ResourceDeletePending: pbTest.MakeResourceDeletePending(deviceID, test.TestResourceSwitchesInstanceHref(switchID), + ResourceDeletePending: pbTest.MakeResourceDeletePending(deviceID, test.TestResourceSwitchesInstanceHref(switchID), test.TestResourceSwitchesInstanceResourceTypes, delCorrelationID), }, }, @@ -391,7 +391,7 @@ func TestObserveDeviceResourcesCreateAndDelete(t *testing.T) { SubscriptionId: sub.ID(), CorrelationId: delCorrelationID, Type: &pb.Event_ResourceDeleted{ - ResourceDeleted: pbTest.MakeResourceDeleted(deviceID, test.TestResourceSwitchesInstanceHref(switchID), + ResourceDeleted: pbTest.MakeResourceDeleted(deviceID, test.TestResourceSwitchesInstanceHref(switchID), test.TestResourceSwitchesInstanceResourceTypes, delCorrelationID), }, }, diff --git a/grpc-gateway/client/devicesSubscription.go b/grpc-gateway/client/devicesSubscription.go index 5106cab38..2a02cfdec 100644 --- a/grpc-gateway/client/devicesSubscription.go +++ b/grpc-gateway/client/devicesSubscription.go @@ -69,7 +69,7 @@ func NewDevicesSubscription(ctx context.Context, closeErrorHandler SubscriptionH } if deviceMetadataUpdatedHandler == nil && deviceRegisteredHandler == nil && deviceUnregisteredHandler == nil { - return nil, fmt.Errorf("invalid handler - it's supports: DeviceMetadataUpdatedHandler, DeviceRegisteredHandler, DeviceUnregisteredHandler") + return nil, errors.New("invalid handler - it's supports: DeviceMetadataUpdatedHandler, DeviceRegisteredHandler, DeviceUnregisteredHandler") } client, err := New(gwClient).SubscribeToEventsWithCurrentState(ctx, time.Minute) if err != nil { @@ -95,7 +95,7 @@ func NewDevicesSubscription(ctx context.Context, closeErrorHandler SubscriptionH return nil, fmt.Errorf("unexpected event %+v", ev) } if op.GetErrorStatus().GetCode() != pb.Event_OperationProcessed_ErrorStatus_OK { - return nil, fmt.Errorf(op.GetErrorStatus().GetMessage()) + return nil, errors.New(op.GetErrorStatus().GetMessage()) } var wg sync.WaitGroup @@ -169,7 +169,7 @@ func (s *DevicesSubscription) handleCancel(cancel *pb.Event_SubscriptionCanceled s.closeErrorHandler.OnClose() return } - s.closeErrorHandler.Error(fmt.Errorf(reason)) + s.closeErrorHandler.Error(errors.New(reason)) } func (s *DevicesSubscription) runRecv() { diff --git a/grpc-gateway/client/generalMessageCodec.go b/grpc-gateway/client/generalMessageCodec.go index 3d32eda06..d83db6c64 100644 --- a/grpc-gateway/client/generalMessageCodec.go +++ b/grpc-gateway/client/generalMessageCodec.go @@ -1,6 +1,7 @@ package client import ( + "errors" "fmt" "io" @@ -55,7 +56,7 @@ func (GeneralMessageCodec) Decode(m *pool.Message, v interface{}) error { } if m.Body() == nil { - return fmt.Errorf("unexpected empty body") + return errors.New("unexpected empty body") } if err := decoder(m.Body(), v); err != nil { diff --git a/grpc-gateway/client/getDevice_test.go b/grpc-gateway/client/getDevice_test.go index d47cbc4b1..525d5f517 100644 --- a/grpc-gateway/client/getDevice_test.go +++ b/grpc-gateway/client/getDevice_test.go @@ -16,7 +16,6 @@ import ( "github.com/plgd-dev/hub/v2/test/config" oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test" "github.com/plgd-dev/hub/v2/test/service" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -96,7 +95,7 @@ func TestClient_GetDevice(t *testing.T) { c := NewTestClient(t) defer func() { err := c.Close() - assert.NoError(t, err) + require.NoError(t, err) }() _, shutdownDevSim := test.OnboardDevSim(ctx, t, c.GrpcGatewayClient(), deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) @@ -104,9 +103,9 @@ func TestClient_GetDevice(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctx, cancel := context.WithTimeout(ctx, time.Second) + runctx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - got, err := c.GetDevice(ctx, tt.args.deviceID) + got, err := c.GetDevice(runctx, tt.args.deviceID) if tt.wantErr { require.Error(t, err) return @@ -117,6 +116,7 @@ func TestClient_GetDevice(t *testing.T) { got.Device.ProtocolIndependentId = "" got.Device.Metadata.Connection.Id = "" got.Device.Metadata.Connection.ConnectedAt = 0 + got.Device.Metadata.Connection.LocalEndpoints = nil got.Device.Metadata.Connection.ServiceId = "" got.Device.Metadata.TwinSynchronization.SyncingAt = 0 got.Device.Metadata.TwinSynchronization.InSyncAt = 0 diff --git a/grpc-gateway/client/getDevices_test.go b/grpc-gateway/client/getDevices_test.go index 289cd9c65..e92774ab1 100644 --- a/grpc-gateway/client/getDevices_test.go +++ b/grpc-gateway/client/getDevices_test.go @@ -11,7 +11,6 @@ import ( "github.com/plgd-dev/hub/v2/test/config" oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test" "github.com/plgd-dev/hub/v2/test/service" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -21,6 +20,7 @@ func sortDevices(s map[string]*client.DeviceDetails) map[string]*client.DeviceDe x.Device.ProtocolIndependentId = "" x.Device.Metadata.Connection.Id = "" x.Device.Metadata.Connection.ConnectedAt = 0 + x.Device.Metadata.Connection.LocalEndpoints = nil x.Device.Metadata.Connection.ServiceId = "" x.Device.Metadata.TwinSynchronization.SyncingAt = 0 x.Device.Metadata.TwinSynchronization.InSyncAt = 0 @@ -72,7 +72,7 @@ func TestClient_GetDevices(t *testing.T) { c := NewTestClient(t) defer func() { err := c.Close() - assert.NoError(t, err) + require.NoError(t, err) }() _, shutdownDevSim := test.OnboardDevSim(ctx, t, c.GrpcGatewayClient(), deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) @@ -80,9 +80,9 @@ func TestClient_GetDevices(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctx, cancel := context.WithTimeout(ctx, time.Second) + runctx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - got, err := c.GetDevices(ctx, tt.args.opts...) + got, err := c.GetDevices(runctx, tt.args.opts...) if tt.wantErr { require.Error(t, err) return diff --git a/grpc-gateway/client/getResource_test.go b/grpc-gateway/client/getResource_test.go index b72f10e88..6131789f8 100644 --- a/grpc-gateway/client/getResource_test.go +++ b/grpc-gateway/client/getResource_test.go @@ -13,7 +13,6 @@ import ( "github.com/plgd-dev/hub/v2/test/config" oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test" "github.com/plgd-dev/hub/v2/test/service" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -104,7 +103,7 @@ func TestClientGetResource(t *testing.T) { c := NewTestClient(t) defer func() { err := c.Close() - assert.NoError(t, err) + require.NoError(t, err) }() _, shutdownDevSim := test.OnboardDevSim(ctx, t, c.GrpcGatewayClient(), deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) @@ -112,10 +111,10 @@ func TestClientGetResource(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctx, cancel := context.WithTimeout(ctx, time.Second) + runctx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() var got interface{} - err := c.GetResource(ctx, tt.args.deviceID, tt.args.href, &got, tt.args.opts...) + err := c.GetResource(runctx, tt.args.deviceID, tt.args.href, &got, tt.args.opts...) if tt.wantErr { require.Error(t, err) return diff --git a/grpc-gateway/client/maintenance_test.go b/grpc-gateway/client/maintenance_test.go index 6404029a3..7256a3b0e 100644 --- a/grpc-gateway/client/maintenance_test.go +++ b/grpc-gateway/client/maintenance_test.go @@ -10,7 +10,6 @@ import ( "github.com/plgd-dev/hub/v2/test/config" oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test" "github.com/plgd-dev/hub/v2/test/service" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -54,7 +53,7 @@ func TestClientFactoryReset(t *testing.T) { c := NewTestClient(t) defer func() { err := c.Close() - assert.NoError(t, err) + require.NoError(t, err) }() _, _ = test.OnboardDevSim(ctx, t, c.GrpcGatewayClient(), deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) @@ -108,7 +107,7 @@ func TestClientReboot(t *testing.T) { c := NewTestClient(t) defer func() { err := c.Close() - assert.NoError(t, err) + require.NoError(t, err) }() _, shutdownDevSim := test.OnboardDevSim(ctx, t, c.GrpcGatewayClient(), deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) diff --git a/grpc-gateway/client/observeDeviceResources.go b/grpc-gateway/client/observeDeviceResources.go index 8bca2b728..8ea852174 100644 --- a/grpc-gateway/client/observeDeviceResources.go +++ b/grpc-gateway/client/observeDeviceResources.go @@ -46,7 +46,7 @@ func (c *Client) ObserveDeviceResources(ctx context.Context, deviceID string, ha sub, err := c.NewDeviceSubscription(ctx, deviceID, &deviceResourcesObservation{ h: handler, removeSubscription: func() { - if _, err := c.stopObservingDeviceResources(ID.String()); err != nil { + if _, err = c.stopObservingDeviceResources(ID.String()); err != nil { handler.Error(fmt.Errorf("failed to stop device('%v') resources observation: %w", ID.String(), err)) } }, diff --git a/grpc-gateway/client/observeDeviceResources_test.go b/grpc-gateway/client/observeDeviceResources_test.go index bdf19df89..9a05ff6a8 100644 --- a/grpc-gateway/client/observeDeviceResources_test.go +++ b/grpc-gateway/client/observeDeviceResources_test.go @@ -13,7 +13,6 @@ import ( oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test" pbTest "github.com/plgd-dev/hub/v2/test/pb" "github.com/plgd-dev/hub/v2/test/service" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -28,7 +27,7 @@ func TestObserveDeviceResourcesPublish(t *testing.T) { c := NewTestClient(t) defer func() { err := c.Close() - assert.NoError(t, err) + require.NoError(t, err) }() deviceID, shutdownDevSim := test.OnboardDevSim(ctx, t, c.GrpcGatewayClient(), deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) defer shutdownDevSim() diff --git a/grpc-gateway/client/observeDevices_test.go b/grpc-gateway/client/observeDevices_test.go index df9943bb0..6f32642a2 100644 --- a/grpc-gateway/client/observeDevices_test.go +++ b/grpc-gateway/client/observeDevices_test.go @@ -2,6 +2,7 @@ package client_test import ( "context" + "errors" "fmt" "testing" @@ -11,7 +12,6 @@ import ( "github.com/plgd-dev/hub/v2/test/config" oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test" "github.com/plgd-dev/hub/v2/test/service" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -26,7 +26,7 @@ func TestObserveDevices(t *testing.T) { c := NewTestClient(t) defer func() { err := c.Close() - assert.NoError(t, err) + require.NoError(t, err) }() deviceID, shutdownDevSim := test.OnboardDevSim(ctx, t, c.GrpcGatewayClient(), deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) @@ -43,7 +43,7 @@ func TestObserveDevices(t *testing.T) { select { case res = <-h.res: case <-ctx.Done(): - require.NoError(t, fmt.Errorf("timeout")) + require.NoError(t, errors.New("timeout")) } require.Equal(t, client.DevicesObservationEvent{ DeviceIDs: []string{deviceID}, @@ -53,7 +53,7 @@ func TestObserveDevices(t *testing.T) { select { case res = <-h.res: case <-ctx.Done(): - require.NoError(t, fmt.Errorf("timeout")) + require.NoError(t, errors.New("timeout")) } require.Equal(t, client.DevicesObservationEvent{ DeviceIDs: []string{deviceID}, @@ -64,7 +64,7 @@ func TestObserveDevices(t *testing.T) { select { case res = <-h.res: case <-ctx.Done(): - require.NoError(t, fmt.Errorf("timeout")) + require.NoError(t, errors.New("timeout")) } require.True(t, res.Event == client.DevicesObservationEvent_OFFLINE || res.Event == client.DevicesObservationEvent_UNREGISTERED) require.Equal(t, client.DevicesObservationEvent{ diff --git a/grpc-gateway/client/observeResource_test.go b/grpc-gateway/client/observeResource_test.go index a95e56221..cdeaca197 100644 --- a/grpc-gateway/client/observeResource_test.go +++ b/grpc-gateway/client/observeResource_test.go @@ -27,8 +27,8 @@ func TestObservingResource(t *testing.T) { c := NewTestClient(t) defer func() { - err := c.Close() - assert.NoError(t, err) + errC := c.Close() + require.NoError(t, errC) }() deviceID, shutdownDevSim := test.OnboardDevSim(ctx, t, c.GrpcGatewayClient(), deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) defer shutdownDevSim() @@ -37,8 +37,8 @@ func TestObservingResource(t *testing.T) { id, err := c.ObserveResource(ctx, deviceID, configuration.ResourceURI, h) require.NoError(t, err) defer func() { - err := c.StopObservingResource(id) - require.NoError(t, err) + errS := c.StopObservingResource(id) + require.NoError(t, errS) }() name := "observe simulator" @@ -56,7 +56,7 @@ func TestObservingResource(t *testing.T) { require.Equal(t, name, d.Name) err = c.UpdateResource(ctx, deviceID, configuration.ResourceURI, map[string]interface{}{"n": test.TestDeviceName}, nil) - assert.NoError(t, err) + require.NoError(t, err) } func makeTestObservationHandler() *testObservationHandler { diff --git a/grpc-gateway/client/resourceSubscription.go b/grpc-gateway/client/resourceSubscription.go index 85e11bd56..abe5f9cc5 100644 --- a/grpc-gateway/client/resourceSubscription.go +++ b/grpc-gateway/client/resourceSubscription.go @@ -54,7 +54,7 @@ func NewResourceSubscription(ctx context.Context, resourceID *commands.ResourceI } if resourceContentChangedHandler == nil { - return nil, fmt.Errorf("invalid handler - it's supports: ResourceContentChangedHandler") + return nil, errors.New("invalid handler - it's supports: ResourceContentChangedHandler") } client, err := New(gwClient).SubscribeToEventsWithCurrentState(ctx, time.Minute) if err != nil { @@ -85,7 +85,7 @@ func NewResourceSubscription(ctx context.Context, resourceID *commands.ResourceI return nil, fmt.Errorf("unexpected event %+v", ev) } if op.GetErrorStatus().GetCode() != pb.Event_OperationProcessed_ErrorStatus_OK { - return nil, fmt.Errorf(op.GetErrorStatus().GetMessage()) + return nil, errors.New(op.GetErrorStatus().GetMessage()) } var wg sync.WaitGroup @@ -141,7 +141,7 @@ func (s *ResourceSubscription) handleCancel(cancel *pb.Event_SubscriptionCancele s.closeErrorHandler.OnClose() return } - s.closeErrorHandler.Error(fmt.Errorf(reason)) + s.closeErrorHandler.Error(errors.New(reason)) } func (s *ResourceSubscription) runRecv() { diff --git a/grpc-gateway/client/updateResource_test.go b/grpc-gateway/client/updateResource_test.go index b9161dd7a..f77e1e8f9 100644 --- a/grpc-gateway/client/updateResource_test.go +++ b/grpc-gateway/client/updateResource_test.go @@ -13,7 +13,6 @@ import ( "github.com/plgd-dev/hub/v2/test/config" oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test" "github.com/plgd-dev/hub/v2/test/service" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -97,18 +96,18 @@ func TestClientUpdateResource(t *testing.T) { c := NewTestClient(t) defer func() { - err := c.Close() - assert.NoError(t, err) + errC := c.Close() + require.NoError(t, errC) }() _, shutdownDevSim := test.OnboardDevSim(ctx, t, c.GrpcGatewayClient(), deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) defer shutdownDevSim() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctx, cancel := context.WithTimeout(ctx, time.Second) + runctx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() var got interface{} - err := c.UpdateResource(ctx, tt.args.deviceID, tt.args.href, tt.args.data, &got, tt.args.opts...) + err := c.UpdateResource(runctx, tt.args.deviceID, tt.args.href, tt.args.data, &got, tt.args.opts...) if tt.wantErr { require.Error(t, err) return @@ -135,7 +134,7 @@ func TestUpdateConfigurationName(t *testing.T) { c := NewTestClient(t) defer func() { err := c.Close() - assert.NoError(t, err) + require.NoError(t, err) }() deviceID, shutdownDevSim := test.OnboardDevSim(ctx, t, c.GrpcGatewayClient(), deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) defer shutdownDevSim() @@ -145,7 +144,7 @@ func TestUpdateConfigurationName(t *testing.T) { for _, link := range test.GetAllBackendResourceLinks() { var got interface{} err := c.GetResource(ctx, devID, link.Href, &got) - assert.NoError(t, err) + require.NoError(t, err) resourceData[link.Href] = got } return resourceData diff --git a/grpc-gateway/pb/README.md b/grpc-gateway/pb/README.md index 0cb9b99e9..50ea16d47 100644 --- a/grpc-gateway/pb/README.md +++ b/grpc-gateway/pb/README.md @@ -1491,6 +1491,7 @@ https://github.com/openconnectivityfoundation/core/blob/master/schemas/oic.links | audit_context | [AuditContext](#resourceaggregate-pb-AuditContext) | | | | event_metadata | [EventMetadata](#resourceaggregate-pb-EventMetadata) | | | | etag | [bytes](#bytes) | | etag of the resource used by twin synchronization | +| resource_types | [string](#string) | repeated | | | open_telemetry_carrier | [ResourceChanged.OpenTelemetryCarrierEntry](#resourceaggregate-pb-ResourceChanged-OpenTelemetryCarrierEntry) | repeated | Open telemetry data propagated to asynchronous events | @@ -1527,6 +1528,7 @@ https://github.com/openconnectivityfoundation/core/blob/master/schemas/oic.links | audit_context | [AuditContext](#resourceaggregate-pb-AuditContext) | | | | event_metadata | [EventMetadata](#resourceaggregate-pb-EventMetadata) | | | | valid_until | [int64](#int64) | | unix timestamp in nanoseconds (https://golang.org/pkg/time/#Time.UnixNano) when pending event is considered as expired. 0 means forever. | +| resource_types | [string](#string) | repeated | | | open_telemetry_carrier | [ResourceCreatePending.OpenTelemetryCarrierEntry](#resourceaggregate-pb-ResourceCreatePending-OpenTelemetryCarrierEntry) | repeated | Open telemetry data propagated to asynchronous events | @@ -1563,6 +1565,7 @@ https://github.com/openconnectivityfoundation/core/blob/master/schemas/oic.links | content | [Content](#resourceaggregate-pb-Content) | | | | audit_context | [AuditContext](#resourceaggregate-pb-AuditContext) | | | | event_metadata | [EventMetadata](#resourceaggregate-pb-EventMetadata) | | | +| resource_types | [string](#string) | repeated | | | open_telemetry_carrier | [ResourceCreated.OpenTelemetryCarrierEntry](#resourceaggregate-pb-ResourceCreated-OpenTelemetryCarrierEntry) | repeated | Open telemetry data propagated to asynchronous events | @@ -1599,6 +1602,7 @@ https://github.com/openconnectivityfoundation/core/blob/master/schemas/oic.links | event_metadata | [EventMetadata](#resourceaggregate-pb-EventMetadata) | | | | valid_until | [int64](#int64) | | unix timestamp in nanoseconds (https://golang.org/pkg/time/#Time.UnixNano) when pending event is considered as expired. 0 means forever. | | resource_interface | [string](#string) | | | +| resource_types | [string](#string) | repeated | | | open_telemetry_carrier | [ResourceDeletePending.OpenTelemetryCarrierEntry](#resourceaggregate-pb-ResourceDeletePending-OpenTelemetryCarrierEntry) | repeated | Open telemetry data propagated to asynchronous events | @@ -1635,6 +1639,7 @@ https://github.com/openconnectivityfoundation/core/blob/master/schemas/oic.links | content | [Content](#resourceaggregate-pb-Content) | | | | audit_context | [AuditContext](#resourceaggregate-pb-AuditContext) | | | | event_metadata | [EventMetadata](#resourceaggregate-pb-EventMetadata) | | | +| resource_types | [string](#string) | repeated | | | open_telemetry_carrier | [ResourceDeleted.OpenTelemetryCarrierEntry](#resourceaggregate-pb-ResourceDeleted-OpenTelemetryCarrierEntry) | repeated | Open telemetry data propagated to asynchronous events | @@ -1776,6 +1781,7 @@ https://github.com/openconnectivityfoundation/cloud-services/blob/master/swagger | event_metadata | [EventMetadata](#resourceaggregate-pb-EventMetadata) | | | | valid_until | [int64](#int64) | | unix timestamp in nanoseconds (https://golang.org/pkg/time/#Time.UnixNano) when pending event is considered as expired. 0 means forever. | | etag | [bytes](#bytes) | repeated | | +| resource_types | [string](#string) | repeated | | | open_telemetry_carrier | [ResourceRetrievePending.OpenTelemetryCarrierEntry](#resourceaggregate-pb-ResourceRetrievePending-OpenTelemetryCarrierEntry) | repeated | Open telemetry data propagated to asynchronous events | @@ -1813,6 +1819,7 @@ https://github.com/openconnectivityfoundation/cloud-services/blob/master/swagger | audit_context | [AuditContext](#resourceaggregate-pb-AuditContext) | | | | event_metadata | [EventMetadata](#resourceaggregate-pb-EventMetadata) | | | | etag | [bytes](#bytes) | | | +| resource_types | [string](#string) | repeated | | | open_telemetry_carrier | [ResourceRetrieved.OpenTelemetryCarrierEntry](#resourceaggregate-pb-ResourceRetrieved-OpenTelemetryCarrierEntry) | repeated | Open telemetry data propagated to asynchronous events | @@ -1852,6 +1859,7 @@ https://github.com/openconnectivityfoundation/cloud-services/blob/master/swagger | resource_delete_pendings | [ResourceDeletePending](#resourceaggregate-pb-ResourceDeletePending) | repeated | expired events will be removed by creating a new snapshot. | | audit_context | [AuditContext](#resourceaggregate-pb-AuditContext) | | | | event_metadata | [EventMetadata](#resourceaggregate-pb-EventMetadata) | | | +| resource_types | [string](#string) | repeated | | @@ -1872,6 +1880,7 @@ https://github.com/openconnectivityfoundation/cloud-services/blob/master/swagger | audit_context | [AuditContext](#resourceaggregate-pb-AuditContext) | | | | event_metadata | [EventMetadata](#resourceaggregate-pb-EventMetadata) | | | | valid_until | [int64](#int64) | | unix timestamp in nanoseconds (https://golang.org/pkg/time/#Time.UnixNano) when pending event is considered as expired. 0 means forever. | +| resource_types | [string](#string) | repeated | | | open_telemetry_carrier | [ResourceUpdatePending.OpenTelemetryCarrierEntry](#resourceaggregate-pb-ResourceUpdatePending-OpenTelemetryCarrierEntry) | repeated | Open telemetry data propagated to asynchronous events | @@ -1908,6 +1917,7 @@ https://github.com/openconnectivityfoundation/cloud-services/blob/master/swagger | content | [Content](#resourceaggregate-pb-Content) | | | | audit_context | [AuditContext](#resourceaggregate-pb-AuditContext) | | | | event_metadata | [EventMetadata](#resourceaggregate-pb-EventMetadata) | | | +| resource_types | [string](#string) | repeated | | | open_telemetry_carrier | [ResourceUpdated.OpenTelemetryCarrierEntry](#resourceaggregate-pb-ResourceUpdated-OpenTelemetryCarrierEntry) | repeated | Open telemetry data propagated to asynchronous events | diff --git a/grpc-gateway/pb/doc.html b/grpc-gateway/pb/doc.html index 8d4c2586b..94218a2ae 100644 --- a/grpc-gateway/pb/doc.html +++ b/grpc-gateway/pb/doc.html @@ -4178,6 +4178,13 @@

ResourceChanged

etag of the resource used by twin synchronization

+ + resource_types + string + repeated +

+ + open_telemetry_carrier ResourceChanged.OpenTelemetryCarrierEntry @@ -4268,6 +4275,13 @@

ResourceCreatePending

unix timestamp in nanoseconds (https://golang.org/pkg/time/#Time.UnixNano) when pending event is considered as expired. 0 means forever.

+ + resource_types + string + repeated +

+ + open_telemetry_carrier ResourceCreatePending.OpenTelemetryCarrierEntry @@ -4358,6 +4372,13 @@

ResourceCreated

+ + resource_types + string + repeated +

+ + open_telemetry_carrier ResourceCreated.OpenTelemetryCarrierEntry @@ -4448,6 +4469,13 @@

ResourceDeletePending

+ + resource_types + string + repeated +

+ + open_telemetry_carrier ResourceDeletePending.OpenTelemetryCarrierEntry @@ -4538,6 +4566,13 @@

ResourceDeleted

+ + resource_types + string + repeated +

+ + open_telemetry_carrier ResourceDeleted.OpenTelemetryCarrierEntry @@ -4877,6 +4912,13 @@

ResourceRetrievePending

+ + resource_types + string + repeated +

+ + open_telemetry_carrier ResourceRetrievePending.OpenTelemetryCarrierEntry @@ -4974,6 +5016,13 @@

ResourceRetrieved

+ + resource_types + string + repeated +

+ + open_telemetry_carrier ResourceRetrieved.OpenTelemetryCarrierEntry @@ -5085,6 +5134,13 @@

ResourceStateSnapshotTa

+ + resource_types + string + repeated +

+ + @@ -5144,6 +5200,13 @@

ResourceUpdatePending

unix timestamp in nanoseconds (https://golang.org/pkg/time/#Time.UnixNano) when pending event is considered as expired. 0 means forever.

+ + resource_types + string + repeated +

+ + open_telemetry_carrier ResourceUpdatePending.OpenTelemetryCarrierEntry @@ -5234,6 +5297,13 @@

ResourceUpdated

+ + resource_types + string + repeated +

+ + open_telemetry_carrier ResourceUpdated.OpenTelemetryCarrierEntry diff --git a/grpc-gateway/pb/getResources.go b/grpc-gateway/pb/getResources.go index 7596ab70f..78869173c 100644 --- a/grpc-gateway/pb/getResources.go +++ b/grpc-gateway/pb/getResources.go @@ -2,7 +2,7 @@ package pb import ( "encoding/base64" - "fmt" + "errors" "net/url" "strings" @@ -16,7 +16,7 @@ func parseQuery(m url.Values, query string) (err error) { var key string key, query, _ = strings.Cut(query, "&") if strings.Contains(key, ";") { - err = fmt.Errorf("invalid semicolon separator in query") + err = errors.New("invalid semicolon separator in query") continue } if key == "" { diff --git a/grpc-gateway/pb/hubConfiguration.go b/grpc-gateway/pb/hubConfiguration.go index b2ecf7036..b53fbf4ab 100644 --- a/grpc-gateway/pb/hubConfiguration.go +++ b/grpc-gateway/pb/hubConfiguration.go @@ -1,18 +1,22 @@ package pb +func copyScopes(scopes []string) []string { + if len(scopes) == 0 { + return nil + } + sc := make([]string, len(scopes)) + copy(sc, scopes) + return sc +} + func (r *WebOAuthClient) Clone() *WebOAuthClient { if r == nil { return nil } - var scopes []string - if len(r.Scopes) > 0 { - scopes = make([]string, len(r.Scopes)) - copy(scopes, r.Scopes) - } return &WebOAuthClient{ - ClientId: r.ClientId, - Audience: r.Audience, - Scopes: scopes, + ClientId: r.GetClientId(), + Audience: r.GetAudience(), + Scopes: copyScopes(r.GetScopes()), } } @@ -20,15 +24,10 @@ func (r *DeviceOAuthClient) Clone() *DeviceOAuthClient { if r == nil { return nil } - var scopes []string - if len(r.Scopes) > 0 { - scopes = make([]string, len(r.Scopes)) - copy(scopes, r.Scopes) - } return &DeviceOAuthClient{ - ProviderName: r.ProviderName, - ClientId: r.ClientId, - Audience: r.Audience, - Scopes: scopes, + ProviderName: r.GetProviderName(), + ClientId: r.GetClientId(), + Audience: r.GetAudience(), + Scopes: copyScopes(r.GetScopes()), } } diff --git a/grpc-gateway/pb/hubConfiguration_test.go b/grpc-gateway/pb/hubConfiguration_test.go index 0f430f4a9..0287be823 100644 --- a/grpc-gateway/pb/hubConfiguration_test.go +++ b/grpc-gateway/pb/hubConfiguration_test.go @@ -14,7 +14,7 @@ func TestDeviceOAuthClient_Clone(t *testing.T) { } b := a.Clone() require.Equal(t, &a, b) - require.NotEqual(t, fmt.Sprintf("%p", &a.Scopes), fmt.Sprintf("%p", b.Scopes)) + require.NotEqual(t, fmt.Sprintf("%p", &a.Scopes), fmt.Sprintf("%p", b.GetScopes())) } func TestWebOAuthClient_Clone(t *testing.T) { @@ -24,5 +24,5 @@ func TestWebOAuthClient_Clone(t *testing.T) { } b := a.Clone() require.Equal(t, &a, b) - require.NotEqual(t, fmt.Sprintf("%p", &a.Scopes), fmt.Sprintf("%p", b.Scopes)) + require.NotEqual(t, fmt.Sprintf("%p", &a.Scopes), fmt.Sprintf("%p", b.GetScopes())) } diff --git a/grpc-gateway/pb/service.swagger.json b/grpc-gateway/pb/service.swagger.json index f42b11cca..a67e85522 100644 --- a/grpc-gateway/pb/service.swagger.json +++ b/grpc-gateway/pb/service.swagger.json @@ -1393,6 +1393,13 @@ "serviceId": { "type": "string", "description": "The service.ID, which identify the device being served, must be set when the status is ONLINE. However, during an OFFLINE event, they will be sed to empty values." + }, + "localEndpoints": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The last local endpoints of the device, and it is set when the status is ONLINE." } } }, @@ -1765,6 +1772,12 @@ "format": "byte", "title": "etag of the resource used by twin synchronization" }, + "resourceTypes": { + "type": "array", + "items": { + "type": "string" + } + }, "openTelemetryCarrier": { "type": "object", "additionalProperties": { @@ -1794,6 +1807,12 @@ "format": "int64", "description": "unix timestamp in nanoseconds (https://golang.org/pkg/time/#Time.UnixNano) when pending event is considered as expired. 0 means forever." }, + "resourceTypes": { + "type": "array", + "items": { + "type": "string" + } + }, "openTelemetryCarrier": { "type": "object", "additionalProperties": { @@ -1821,6 +1840,12 @@ "eventMetadata": { "$ref": "#/definitions/resourceaggregatepbEventMetadata" }, + "resourceTypes": { + "type": "array", + "items": { + "type": "string" + } + }, "openTelemetryCarrier": { "type": "object", "additionalProperties": { @@ -1850,6 +1875,12 @@ "resourceInterface": { "type": "string" }, + "resourceTypes": { + "type": "array", + "items": { + "type": "string" + } + }, "openTelemetryCarrier": { "type": "object", "additionalProperties": { @@ -1877,6 +1908,12 @@ "eventMetadata": { "$ref": "#/definitions/resourceaggregatepbEventMetadata" }, + "resourceTypes": { + "type": "array", + "items": { + "type": "string" + } + }, "openTelemetryCarrier": { "type": "object", "additionalProperties": { @@ -2018,6 +2055,12 @@ "format": "byte" } }, + "resourceTypes": { + "type": "array", + "items": { + "type": "string" + } + }, "openTelemetryCarrier": { "type": "object", "additionalProperties": { @@ -2049,6 +2092,12 @@ "type": "string", "format": "byte" }, + "resourceTypes": { + "type": "array", + "items": { + "type": "string" + } + }, "openTelemetryCarrier": { "type": "object", "additionalProperties": { @@ -2104,6 +2153,12 @@ }, "eventMetadata": { "$ref": "#/definitions/resourceaggregatepbEventMetadata" + }, + "resourceTypes": { + "type": "array", + "items": { + "type": "string" + } } } }, @@ -2130,6 +2185,12 @@ "format": "int64", "description": "unix timestamp in nanoseconds (https://golang.org/pkg/time/#Time.UnixNano) when pending event is considered as expired. 0 means forever." }, + "resourceTypes": { + "type": "array", + "items": { + "type": "string" + } + }, "openTelemetryCarrier": { "type": "object", "additionalProperties": { @@ -2157,6 +2218,12 @@ "eventMetadata": { "$ref": "#/definitions/resourceaggregatepbEventMetadata" }, + "resourceTypes": { + "type": "array", + "items": { + "type": "string" + } + }, "openTelemetryCarrier": { "type": "object", "additionalProperties": { diff --git a/grpc-gateway/service/cancelPendingCommands_test.go b/grpc-gateway/service/cancelPendingCommands_test.go index e1be47df2..8b98a010c 100644 --- a/grpc-gateway/service/cancelPendingCommands_test.go +++ b/grpc-gateway/service/cancelPendingCommands_test.go @@ -19,7 +19,7 @@ func TestRequestHandlerCancelPendingCommands(t *testing.T) { client, resourcePendings, _, shutdown := pbTest.InitPendingEvents(ctx, t) defer shutdown() - require.Equal(t, len(resourcePendings), 4) + require.Len(t, resourcePendings, 4) type args struct { req *pb.CancelPendingCommandsRequest diff --git a/grpc-gateway/service/cancelPendingMetadataUpdates_test.go b/grpc-gateway/service/cancelPendingMetadataUpdates_test.go index 26f776eac..c7b8446d8 100644 --- a/grpc-gateway/service/cancelPendingMetadataUpdates_test.go +++ b/grpc-gateway/service/cancelPendingMetadataUpdates_test.go @@ -19,7 +19,7 @@ func TestRequestHandlerCancelPendingMetadataUpdates(t *testing.T) { client, _, devicePendings, shutdown := pbTest.InitPendingEvents(ctx, t) defer shutdown() - require.Equal(t, len(devicePendings), 2) + require.Len(t, devicePendings, 2) type args struct { req *pb.CancelPendingMetadataUpdatesRequest diff --git a/grpc-gateway/service/createAndDeleteResource_test.go b/grpc-gateway/service/createAndDeleteResource_test.go index 3214465a0..38866992a 100644 --- a/grpc-gateway/service/createAndDeleteResource_test.go +++ b/grpc-gateway/service/createAndDeleteResource_test.go @@ -58,7 +58,7 @@ func createSwitchResource(ctx context.Context, t *testing.T, c pb.GrpcGatewayCli }) require.NoError(t, err) switchData := pbTest.MakeCreateSwitchResourceResponseData(switchID) - want := pbTest.MakeResourceCreated(t, deviceID, test.TestResourceSwitchesHref, "", switchData) + want := pbTest.MakeResourceCreated(t, deviceID, test.TestResourceSwitchesHref, test.TestResourceSwitchesResourceTypes, "", switchData) pbTest.CmpResourceCreated(t, want, got.GetData()) } @@ -67,7 +67,7 @@ func deleteSwitchResource(ctx context.Context, t *testing.T, c pb.GrpcGatewayCli ResourceId: commands.NewResourceID(deviceID, test.TestResourceSwitchesInstanceHref(switchID)), }) require.NoError(t, err) - want := pbTest.MakeResourceDeleted(deviceID, test.TestResourceSwitchesInstanceHref(switchID), "") + want := pbTest.MakeResourceDeleted(deviceID, test.TestResourceSwitchesInstanceHref(switchID), test.TestResourceSwitchesInstanceResourceTypes, "") pbTest.CmpResourceDeleted(t, want, got.GetData()) } @@ -76,7 +76,7 @@ func createSwitchResourceExpectedEvents(t *testing.T, deviceID, subID, correlati SubscriptionId: subID, CorrelationId: correlationID, Type: &pb.Event_ResourceCreatePending{ - ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, test.TestResourceSwitchesHref, "", + ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, test.TestResourceSwitchesHref, test.TestResourceSwitchesResourceTypes, "", test.MakeSwitchResourceDefaultData()), }, } @@ -85,7 +85,7 @@ func createSwitchResourceExpectedEvents(t *testing.T, deviceID, subID, correlati SubscriptionId: subID, CorrelationId: correlationID, Type: &pb.Event_ResourceChanged{ - ResourceChanged: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceSwitchesHref, "", + ResourceChanged: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceSwitchesHref, test.TestResourceSwitchesResourceTypes, "", []map[string]interface{}{ { "href": test.TestResourceSwitchesInstanceHref(switchID), @@ -105,7 +105,7 @@ func createSwitchResourceExpectedEvents(t *testing.T, deviceID, subID, correlati SubscriptionId: subID, CorrelationId: correlationID, Type: &pb.Event_ResourceCreated{ - ResourceCreated: pbTest.MakeResourceCreated(t, deviceID, test.TestResourceSwitchesHref, "", + ResourceCreated: pbTest.MakeResourceCreated(t, deviceID, test.TestResourceSwitchesHref, test.TestResourceSwitchesResourceTypes, "", test.MakeSwitchResourceData(map[string]interface{}{ "href": test.TestResourceSwitchesInstanceHref(switchID), "rep": map[string]interface{}{ @@ -144,7 +144,7 @@ func createSwitchResourceExpectedEvents(t *testing.T, deviceID, subID, correlati SubscriptionId: subID, CorrelationId: correlationID, Type: &pb.Event_ResourceChanged{ - ResourceChanged: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceSwitchesInstanceHref(switchID), "", + ResourceChanged: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceSwitchesInstanceHref(switchID), test.TestResourceSwitchesInstanceResourceTypes, "", map[string]interface{}{ "value": false, }), @@ -209,8 +209,9 @@ func deleteSwitchResourceExpectedEvents(t *testing.T, deviceID, subID, correlati CorrelationId: correlationID, Type: &pb.Event_ResourceDeletePending{ ResourceDeletePending: &events.ResourceDeletePending{ - ResourceId: commands.NewResourceID(deviceID, test.TestResourceSwitchesInstanceHref(switchID)), - AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + ResourceId: commands.NewResourceID(deviceID, test.TestResourceSwitchesInstanceHref(switchID)), + AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + ResourceTypes: test.TestResourceSwitchesInstanceResourceTypes, }, }, } @@ -219,7 +220,7 @@ func deleteSwitchResourceExpectedEvents(t *testing.T, deviceID, subID, correlati SubscriptionId: subID, CorrelationId: correlationID, Type: &pb.Event_ResourceDeleted{ - ResourceDeleted: pbTest.MakeResourceDeleted(deviceID, test.TestResourceSwitchesInstanceHref(switchID), ""), + ResourceDeleted: pbTest.MakeResourceDeleted(deviceID, test.TestResourceSwitchesInstanceHref(switchID), test.TestResourceSwitchesInstanceResourceTypes, ""), }, } @@ -239,11 +240,11 @@ func deleteSwitchResourceExpectedEvents(t *testing.T, deviceID, subID, correlati SubscriptionId: subID, CorrelationId: correlationID, Type: &pb.Event_ResourceChanged{ - ResourceChanged: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceSwitchesHref, "", []interface{}{}), + ResourceChanged: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceSwitchesHref, test.TestResourceSwitchesResourceTypes, "", []interface{}{}), }, } - res := pbTest.MakeResourceChanged(t, deviceID, test.TestResourceSwitchesInstanceHref(switchID), "", nil) + res := pbTest.MakeResourceChanged(t, deviceID, test.TestResourceSwitchesInstanceHref(switchID), test.TestResourceSwitchesInstanceResourceTypes, "", nil) res.Status = commands.Status_NOT_FOUND res.Content.CoapContentFormat = -1 res.Content.ContentType = "" @@ -297,7 +298,7 @@ func TestCreateAndDeleteResource(t *testing.T) { ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/grpc-gateway/service/createResource_test.go b/grpc-gateway/service/createResource_test.go index 8ca708517..28fe924cf 100644 --- a/grpc-gateway/service/createResource_test.go +++ b/grpc-gateway/service/createResource_test.go @@ -151,7 +151,7 @@ func TestRequestHandlerCreateResource(t *testing.T) { ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -173,7 +173,7 @@ func TestRequestHandlerCreateResource(t *testing.T) { return } require.NoError(t, err) - resp := pbTest.MakeResourceCreated(t, deviceID, tt.args.href, "", tt.wantData) + resp := pbTest.MakeResourceCreated(t, deviceID, tt.args.href, test.TestResourceSwitchesResourceTypes, "", tt.wantData) pbTest.CmpResourceCreated(t, resp, got.GetData()) }) } diff --git a/grpc-gateway/service/deleteDevices.go b/grpc-gateway/service/deleteDevices.go index 134b500a6..692ab8be2 100644 --- a/grpc-gateway/service/deleteDevices.go +++ b/grpc-gateway/service/deleteDevices.go @@ -28,7 +28,7 @@ func partitionDeletedDevices(expected, actual []string) ([]string, []string) { func (r *RequestHandler) DeleteDevices(ctx context.Context, req *pb.DeleteDevicesRequest) (*pb.DeleteDevicesResponse, error) { // get unique non-empty ids - deviceIDs, _ := strings.Split(strings.Unique(req.DeviceIdFilter), func(s string) bool { + deviceIDs, _ := strings.Split(strings.Unique(req.GetDeviceIdFilter()), func(s string) bool { return s != "" }) diff --git a/grpc-gateway/service/deleteDevices_test.go b/grpc-gateway/service/deleteDevices_test.go index 40b742fbf..210bdd884 100644 --- a/grpc-gateway/service/deleteDevices_test.go +++ b/grpc-gateway/service/deleteDevices_test.go @@ -62,7 +62,7 @@ func TestRequestHandlerDeleteDevices(t *testing.T) { ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -78,7 +78,7 @@ func TestRequestHandlerDeleteDevices(t *testing.T) { t.Run(tt.name, func(t *testing.T) { resp, err := c.DeleteDevices(ctx, tt.args.req) require.NoError(t, err) - require.Equal(t, tt.want.DeviceIds, resp.DeviceIds) + require.Equal(t, tt.want.GetDeviceIds(), resp.GetDeviceIds()) }) } } @@ -87,7 +87,7 @@ func waitForOperationProcessedEvent(t *testing.T, subClient pb.GrpcGateway_Subsc ev, err := subClient.Recv() require.NoError(t, err) expectedEvent := &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), CorrelationId: corID, Type: &pb.Event_OperationProcessed_{ OperationProcessed: &pb.Event_OperationProcessed{ @@ -105,7 +105,7 @@ func waitForStopEvent(t *testing.T, subClient pb.GrpcGateway_SubscribeToEventsCl require.NoError(t, err) expectedEvent := &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), CorrelationId: corID, Type: &pb.Event_DeviceUnregistered_{ DeviceUnregistered: &pb.Event_DeviceUnregistered{ @@ -129,7 +129,7 @@ func TestRequestHandlerReconnectAfterDeleteDevice(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -161,7 +161,7 @@ func TestRequestHandlerReconnectAfterDeleteDevice(t *testing.T) { DeviceIdFilter: []string{deviceID}, }) require.NoError(t, err) - require.Equal(t, []string{deviceID}, resp.DeviceIds) + require.Equal(t, []string{deviceID}, resp.GetDeviceIds()) waitForStopEvent(t, subClient, deviceID, correlationID) err = subClient.CloseSend() require.NoError(t, err) diff --git a/grpc-gateway/service/deleteResource_test.go b/grpc-gateway/service/deleteResource_test.go index 0b66e8559..d73b0a64d 100644 --- a/grpc-gateway/service/deleteResource_test.go +++ b/grpc-gateway/service/deleteResource_test.go @@ -105,7 +105,7 @@ func TestRequestHandlerDeleteResource(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -131,7 +131,7 @@ func TestRequestHandlerDeleteResource(t *testing.T) { } require.NoError(t, err) - want := pbTest.MakeResourceDeleted(deviceID, tt.args.href, "") + want := pbTest.MakeResourceDeleted(deviceID, tt.args.href, test.TestResourceSwitchesInstanceResourceTypes, "") pbTest.CmpResourceDeleted(t, want, got.GetData()) }) } @@ -147,7 +147,7 @@ func TestRequestHandlerDeleteResourceAfterUnpublish(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -187,8 +187,8 @@ func TestRequestHandlerDeleteResourceAfterUnpublish(t *testing.T) { }, }) require.NoError(t, err) - require.Len(t, respUnpublish.UnpublishedHrefs, 1) - require.Equal(t, respUnpublish.UnpublishedHrefs[0], test.TestResourceSwitchesInstanceHref(switchID2)) + require.Len(t, respUnpublish.GetUnpublishedHrefs(), 1) + require.Equal(t, respUnpublish.GetUnpublishedHrefs()[0], test.TestResourceSwitchesInstanceHref(switchID2)) // for update resource-directory cache time.Sleep(time.Second) @@ -264,7 +264,7 @@ func TestRequestHandlerBatchDeleteResource(t *testing.T) { href: test.TestResourceSwitchesHref, }, want: func() *events.ResourceDeleted { - rdel := pbTest.MakeResourceDeleted(deviceID, test.TestResourceSwitchesHref, "") + rdel := pbTest.MakeResourceDeleted(deviceID, test.TestResourceSwitchesHref, test.TestResourceSwitchesResourceTypes, "") links := test.CollectionLinkRepresentations{} for _, switchID := range switchIDs { links = append(links, test.CollectionLinkRepresentation{ @@ -289,7 +289,7 @@ func TestRequestHandlerBatchDeleteResource(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/grpc-gateway/service/getDevicesMetadata_test.go b/grpc-gateway/service/getDevicesMetadata_test.go index aaf941553..9ee9c7612 100644 --- a/grpc-gateway/service/getDevicesMetadata_test.go +++ b/grpc-gateway/service/getDevicesMetadata_test.go @@ -126,7 +126,7 @@ func TestRequestHandlerGetDevicesMetadata(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/grpc-gateway/service/getDevices_test.go b/grpc-gateway/service/getDevices_test.go index c074499a1..26412bdbb 100644 --- a/grpc-gateway/service/getDevices_test.go +++ b/grpc-gateway/service/getDevices_test.go @@ -70,7 +70,7 @@ func TestRequestHandlerGetDevices(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -96,7 +96,7 @@ func TestRequestHandlerGetDevices(t *testing.T) { break } require.NoError(t, err) - assert.NotEmpty(t, dev.ProtocolIndependentId) + assert.NotEmpty(t, dev.GetProtocolIndependentId()) assert.NotEmpty(t, dev.GetData().GetContent().GetData()) assert.NotEmpty(t, dev.GetMetadata().GetConnection().GetServiceId()) devices = append(devices, dev) diff --git a/grpc-gateway/service/getEvents_test.go b/grpc-gateway/service/getEvents_test.go index bdbb443a1..e468a32c1 100644 --- a/grpc-gateway/service/getEvents_test.go +++ b/grpc-gateway/service/getEvents_test.go @@ -49,7 +49,7 @@ func TestRequestHandlerGetEvents(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -63,7 +63,7 @@ func TestRequestHandlerGetEvents(t *testing.T) { defer shutdownDevSim() events := getAllEvents(ctx, t, c) - require.True(t, len(events) > 0) + require.NotEmpty(t, events) type args struct { req *pb.GetEventsRequest diff --git a/grpc-gateway/service/getHubConfiguration_test.go b/grpc-gateway/service/getHubConfiguration_test.go index e374ff4fc..2de53c938 100644 --- a/grpc-gateway/service/getHubConfiguration_test.go +++ b/grpc-gateway/service/getHubConfiguration_test.go @@ -36,7 +36,7 @@ func TestRequestHandlerGetHubConfiguration(t *testing.T) { tearDown := service.SetUp(ctx, t) defer tearDown() - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -51,7 +51,7 @@ func TestRequestHandlerGetHubConfiguration(t *testing.T) { got, err := c.GetHubConfiguration(ctxWithoutToken, &pb.HubConfigurationRequest{}) require.NoError(t, err) require.NotEmpty(t, got.GetBuildInfo()) - got.BuildInfo.ReleaseUrl = expected.BuildInfo.ReleaseUrl + got.BuildInfo.ReleaseUrl = expected.GetBuildInfo().GetReleaseUrl() pbTest.CmpHubConfigurationResponse(t, tt.want, got) }) } diff --git a/grpc-gateway/service/getResourceFromDevice_test.go b/grpc-gateway/service/getResourceFromDevice_test.go index 27afd55df..048728654 100644 --- a/grpc-gateway/service/getResourceFromDevice_test.go +++ b/grpc-gateway/service/getResourceFromDevice_test.go @@ -24,6 +24,7 @@ import ( "github.com/plgd-dev/hub/v2/test/config" oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test" pbTest "github.com/plgd-dev/hub/v2/test/pb" + "github.com/plgd-dev/hub/v2/test/sdk" "github.com/plgd-dev/hub/v2/test/service" "github.com/stretchr/testify/require" "google.golang.org/grpc" @@ -71,7 +72,7 @@ func TestRequestHandlerGetResourceFromDevice(t *testing.T) { TimeToLive: int64(time.Hour), }, }, - want: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceLightInstanceHref("1"), "", + want: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", map[string]interface{}{ "name": "Light", "power": uint64(0), @@ -87,7 +88,7 @@ func TestRequestHandlerGetResourceFromDevice(t *testing.T) { TimeToLive: int64(time.Hour), }, }, - want: pbTest.MakeResourceRetrieved(t, deviceID, device.ResourceURI, "", + want: pbTest.MakeResourceRetrieved(t, deviceID, device.ResourceURI, test.TestResourceDeviceResourceTypes, "", map[string]interface{}{ "n": deviceName, "di": deviceID, @@ -106,7 +107,7 @@ func TestRequestHandlerGetResourceFromDevice(t *testing.T) { TimeToLive: int64(time.Hour), }, }, - want: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceSwitchesHref, "", + want: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceSwitchesHref, test.TestResourceSwitchesResourceTypes, "", []map[string]interface{}{ { "href": test.TestResourceSwitchesInstanceHref(switchID), @@ -129,7 +130,7 @@ func TestRequestHandlerGetResourceFromDevice(t *testing.T) { TimeToLive: int64(time.Hour), }, }, - want: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceSwitchesInstanceHref(switchID), "", + want: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceSwitchesInstanceHref(switchID), test.TestResourceSwitchesInstanceResourceTypes, "", map[string]interface{}{ "value": false, }, @@ -145,7 +146,7 @@ func TestRequestHandlerGetResourceFromDevice(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -174,12 +175,12 @@ func TestRequestHandlerGetResourceFromDevice(t *testing.T) { } func validateETags(ctx context.Context, t *testing.T, c pb.GrpcGatewayClient, deviceID, href string) { - sdkClient, err := test.NewSDKClient() + sdkClient, err := sdk.NewClient() require.NoError(t, err) defer func() { - err := sdkClient.Close(context.Background()) - require.NoError(t, err) + errC := sdkClient.Close(context.Background()) + require.NoError(t, errC) }() // get resource from device via SDK @@ -200,7 +201,7 @@ func validateETags(ctx context.Context, t *testing.T, c pb.GrpcGatewayClient, de Etag: [][]byte{cfg2.GetData().GetEtag()}, }) require.NoError(t, err) - require.Equal(t, checkTag.GetData().GetStatus(), commands.Status_NOT_MODIFIED) + require.Equal(t, commands.Status_NOT_MODIFIED, checkTag.GetData().GetStatus()) require.Empty(t, checkTag.GetData().GetContent().GetData()) require.Empty(t, checkTag.GetData().GetContent().GetContentType()) require.Equal(t, int32(-1), checkTag.GetData().GetContent().GetCoapContentFormat()) @@ -253,7 +254,7 @@ func TestRequestHandlerCheckResourceETag(t *testing.T) { ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/grpc-gateway/service/getResourceLinks_test.go b/grpc-gateway/service/getResourceLinks_test.go index 5d8107b0f..b79b3e50b 100644 --- a/grpc-gateway/service/getResourceLinks_test.go +++ b/grpc-gateway/service/getResourceLinks_test.go @@ -36,7 +36,7 @@ func TestRequestHandlerGetResourceLinks(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/grpc-gateway/service/getResources_test.go b/grpc-gateway/service/getResources_test.go index a92ed0017..e16e45e11 100644 --- a/grpc-gateway/service/getResources_test.go +++ b/grpc-gateway/service/getResources_test.go @@ -34,7 +34,7 @@ func TestRequestHandlerGetResources(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -118,7 +118,7 @@ func TestRequestHandlerGetResources(t *testing.T) { want: []*pb.Resource{ { Types: []string{types.CORE_LIGHT}, - Data: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceLightInstanceHref("1"), "", + Data: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", map[string]interface{}{ "state": false, "power": uint64(0), @@ -153,7 +153,8 @@ func TestRequestHandlerGetResources(t *testing.T) { Content: &commands.Content{ CoapContentFormat: -1, }, - AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + ResourceTypes: test.TestResourceLightInstanceResourceTypes, }, }, }, @@ -168,7 +169,7 @@ func TestRequestHandlerGetResources(t *testing.T) { want: []*pb.Resource{ { Types: []string{types.BINARY_SWITCH}, - Data: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceSwitchesInstanceHref(switchID), "", + Data: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceSwitchesInstanceHref(switchID), test.TestResourceSwitchesInstanceResourceTypes, "", map[string]interface{}{ "value": false, }), diff --git a/grpc-gateway/service/subscribeToEvents_test.go b/grpc-gateway/service/subscribeToEvents_test.go index 8af9dc33b..60af3bc2b 100644 --- a/grpc-gateway/service/subscribeToEvents_test.go +++ b/grpc-gateway/service/subscribeToEvents_test.go @@ -51,7 +51,7 @@ func TestRequestHandlerSubscribeToEvents(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -201,7 +201,7 @@ func TestRequestHandlerSubscribeToEvents(t *testing.T) { require.NoError(t, err) defer func() { errC := client.CloseSend() - assert.NoError(t, errC) + require.NoError(t, errC) }() var wg sync.WaitGroup wg.Add(1) @@ -209,14 +209,14 @@ func TestRequestHandlerSubscribeToEvents(t *testing.T) { defer wg.Done() var events []*pb.Event for range tt.want { - ev, err := client.Recv() - if errors.Is(err, io.EOF) { + ev, errR := client.Recv() + if errors.Is(errR, io.EOF) { break } - require.NoError(t, err) + assert.NoError(t, errR) events = append(events, ev) } - pbTest.CmpEvents(t, tt.want, events) + pbTest.AssertCmpEvents(t, tt.want, events) }() err = client.Send(tt.args.sub) require.NoError(t, err) @@ -234,7 +234,7 @@ func TestRequestHandlerSubscribeForCreateEvents(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -265,7 +265,7 @@ func TestRequestHandlerSubscribeForCreateEvents(t *testing.T) { ev, err := client.Recv() require.NoError(t, err) expectedEvent := &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), Type: pbTest.OperationProcessedOK(), CorrelationId: "testToken", } @@ -278,10 +278,10 @@ func TestRequestHandlerSubscribeForCreateEvents(t *testing.T) { ev, err = client.Recv() require.NoError(t, err) pbTest.CmpEvent(t, &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), CorrelationId: "testToken", Type: &pb.Event_ResourceCreatePending{ - ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, test.TestResourceSwitchesHref, "", + ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, test.TestResourceSwitchesHref, test.TestResourceSwitchesResourceTypes, "", switchData), }, }, ev, "") @@ -299,10 +299,10 @@ func TestRequestHandlerSubscribeForCreateEvents(t *testing.T) { ev, err = client.Recv() require.NoError(t, err) pbTest.CmpEvent(t, &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), CorrelationId: "testToken", Type: &pb.Event_ResourceCreated{ - ResourceCreated: pbTest.MakeResourceCreated(t, deviceID, test.TestResourceSwitchesHref, "", switchData), + ResourceCreated: pbTest.MakeResourceCreated(t, deviceID, test.TestResourceSwitchesHref, test.TestResourceSwitchesResourceTypes, "", switchData), }, }, ev, "") } @@ -316,7 +316,7 @@ func TestRequestHandlerSubscribeForHrefEvents(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -346,7 +346,7 @@ func TestRequestHandlerSubscribeForHrefEvents(t *testing.T) { ev, err := client.Recv() require.NoError(t, err) expectedEvent := &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), Type: pbTest.OperationProcessedOK(), CorrelationId: "testToken", } @@ -364,10 +364,10 @@ func TestRequestHandlerSubscribeForHrefEvents(t *testing.T) { } if ev.GetResourceCreatePending() != nil { pbTest.CmpEvent(t, &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), CorrelationId: "testToken", Type: &pb.Event_ResourceCreatePending{ - ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, test.TestResourceSwitchesHref, "", + ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, test.TestResourceSwitchesHref, test.TestResourceSwitchesResourceTypes, "", switchData), }, }, ev, "") @@ -395,10 +395,10 @@ func TestRequestHandlerSubscribeForHrefEvents(t *testing.T) { } if ev.GetResourceCreated() != nil { pbTest.CmpEvent(t, &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), CorrelationId: "testToken", Type: &pb.Event_ResourceCreated{ - ResourceCreated: pbTest.MakeResourceCreated(t, deviceID, test.TestResourceSwitchesHref, "", switchData), + ResourceCreated: pbTest.MakeResourceCreated(t, deviceID, test.TestResourceSwitchesHref, test.TestResourceSwitchesResourceTypes, "", switchData), }, }, ev, "") break @@ -438,7 +438,7 @@ func TestRequestHandlerSubscribeForPendingCommands(t *testing.T) { want: []*pb.PendingCommand{ { Command: &pb.PendingCommand_ResourceUpdatePending{ - ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceLightInstanceHref("1"), "", + ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", map[string]interface{}{ "power": 1, }), @@ -479,13 +479,14 @@ func TestRequestHandlerSubscribeForPendingCommands(t *testing.T) { DeviceId: deviceID, Href: platform.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: []string{platform.ResourceType}, }, }, }, { Command: &pb.PendingCommand_ResourceCreatePending{ - ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, "", + ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, test.TestResourceDeviceResourceTypes, "", map[string]interface{}{ "power": 1, }), @@ -498,13 +499,14 @@ func TestRequestHandlerSubscribeForPendingCommands(t *testing.T) { DeviceId: deviceID, Href: device.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceDeviceResourceTypes, }, }, }, { Command: &pb.PendingCommand_ResourceUpdatePending{ - ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceLightInstanceHref("1"), "", + ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", map[string]interface{}{ "power": 1, }), @@ -529,7 +531,8 @@ func TestRequestHandlerSubscribeForPendingCommands(t *testing.T) { DeviceId: deviceID, Href: platform.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: []string{platform.ResourceType}, }, }, }, @@ -547,7 +550,7 @@ func TestRequestHandlerSubscribeForPendingCommands(t *testing.T) { want: []*pb.PendingCommand{ { Command: &pb.PendingCommand_ResourceCreatePending{ - ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, "", + ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, test.TestResourceDeviceResourceTypes, "", map[string]interface{}{ "power": 1, }), @@ -573,7 +576,8 @@ func TestRequestHandlerSubscribeForPendingCommands(t *testing.T) { DeviceId: deviceID, Href: device.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceDeviceResourceTypes, }, }, }, @@ -591,7 +595,7 @@ func TestRequestHandlerSubscribeForPendingCommands(t *testing.T) { want: []*pb.PendingCommand{ { Command: &pb.PendingCommand_ResourceUpdatePending{ - ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceLightInstanceHref("1"), "", + ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", map[string]interface{}{ "power": 1, }), @@ -645,7 +649,7 @@ func TestRequestHandlerSubscribeForPendingCommands(t *testing.T) { ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -663,9 +667,9 @@ func TestRequestHandlerSubscribeForPendingCommands(t *testing.T) { secureGWShutdown() createFn := func(timeToLive time.Duration) { - ctx, cancel := context.WithTimeout(ctx, time.Second) + createCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.CreateResource(ctx, &pb.CreateResourceRequest{ + _, errC := c.CreateResource(createCtx, &pb.CreateResourceRequest{ ResourceId: commands.NewResourceID(deviceID, device.ResourceURI), Content: &pb.Content{ ContentType: message.AppOcfCbor.String(), @@ -675,27 +679,27 @@ func TestRequestHandlerSubscribeForPendingCommands(t *testing.T) { }, TimeToLive: int64(timeToLive), }) - require.Error(t, err) + require.Error(t, errC) } createFn(time.Millisecond * 500) // for test expired event createFn(0) retrieveFn := func(timeToLive time.Duration) { - ctx, cancel := context.WithTimeout(ctx, time.Second) + retrieveCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.GetResourceFromDevice(ctx, &pb.GetResourceFromDeviceRequest{ + _, errG := c.GetResourceFromDevice(retrieveCtx, &pb.GetResourceFromDeviceRequest{ ResourceId: commands.NewResourceID(deviceID, platform.ResourceURI), TimeToLive: int64(timeToLive), }) - require.Error(t, err) + require.Error(t, errG) } retrieveFn(time.Millisecond * 500) // for test expired event retrieveFn(0) updateFn := func(timeToLive time.Duration) { - ctx, cancel := context.WithTimeout(ctx, time.Second) + updateCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.UpdateResource(ctx, &pb.UpdateResourceRequest{ + _, errU := c.UpdateResource(updateCtx, &pb.UpdateResourceRequest{ ResourceId: commands.NewResourceID(deviceID, test.TestResourceLightInstanceHref("1")), Content: &pb.Content{ ContentType: message.AppOcfCbor.String(), @@ -705,32 +709,32 @@ func TestRequestHandlerSubscribeForPendingCommands(t *testing.T) { }, TimeToLive: int64(timeToLive), }) - require.Error(t, err) + require.Error(t, errU) } updateFn(time.Millisecond * 500) // for test expired event updateFn(0) deleteFn := func(timeToLive time.Duration) { - ctx, cancel := context.WithTimeout(ctx, time.Second) + deleteCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.DeleteResource(ctx, &pb.DeleteResourceRequest{ + _, errD := c.DeleteResource(deleteCtx, &pb.DeleteResourceRequest{ ResourceId: commands.NewResourceID(deviceID, device.ResourceURI), TimeToLive: int64(timeToLive), }) - require.Error(t, err) + require.Error(t, errD) } deleteFn(time.Millisecond * 500) // for test expired event deleteFn(0) updateDeviceMetadataFn := func(timeToLive time.Duration) { - ctx, cancel := context.WithTimeout(ctx, time.Second) + updateCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.UpdateDeviceMetadata(ctx, &pb.UpdateDeviceMetadataRequest{ + _, errU := c.UpdateDeviceMetadata(updateCtx, &pb.UpdateDeviceMetadataRequest{ DeviceId: deviceID, TwinEnabled: false, TimeToLive: int64(timeToLive), }) - require.Error(t, err) + require.Error(t, errU) } updateDeviceMetadataFn(time.Millisecond * 500) // for test expired event updateDeviceMetadataFn(0) // for test expired event @@ -751,12 +755,12 @@ func TestRequestHandlerSubscribeForPendingCommands(t *testing.T) { ev, err := client.Recv() require.NoError(t, err) expectedEvent := &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), Type: pbTest.OperationProcessedOK(), CorrelationId: correlationID, } test.CheckProtobufs(t, expectedEvent, ev, test.RequireToCheckFunc(require.Equal)) - subscriptionID := ev.SubscriptionId + subscriptionID := ev.GetSubscriptionId() fmt.Printf("sub %v\n", subscriptionID) values := make([]*pb.PendingCommand, 0, 1) @@ -860,17 +864,17 @@ func TestRequestHandlerIssue270(t *testing.T) { ev, err := client.Recv() require.NoError(t, err) expectedEvent := &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), Type: pbTest.OperationProcessedOK(), CorrelationId: "testToken", } - fmt.Printf("SUBSCRIPTION ID: %v\n", ev.SubscriptionId) + fmt.Printf("SUBSCRIPTION ID: %v\n", ev.GetSubscriptionId()) test.CheckProtobufs(t, expectedEvent, ev, test.RequireToCheckFunc(require.Equal)) ev, err = client.Recv() require.NoError(t, err) expectedEvent = &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), Type: &pb.Event_DeviceRegistered_{ DeviceRegistered: &pb.Event_DeviceRegistered{ DeviceIds: []string{}, @@ -887,7 +891,7 @@ func TestRequestHandlerIssue270(t *testing.T) { ev, err = client.Recv() require.NoError(t, err) expectedEvent = &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), Type: &pb.Event_DeviceRegistered_{ DeviceRegistered: &pb.Event_DeviceRegistered{ DeviceIds: []string{deviceID}, @@ -903,7 +907,7 @@ func TestRequestHandlerIssue270(t *testing.T) { ev, err = client.Recv() require.NoError(t, err) pbTest.CmpEvent(t, &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), Type: &pb.Event_DeviceMetadataUpdated{ DeviceMetadataUpdated: &events.DeviceMetadataUpdated{ DeviceId: deviceID, @@ -931,7 +935,7 @@ func TestRequestHandlerIssue270(t *testing.T) { if ev.GetDeviceUnregistered() != nil { expectedEvent = &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), Type: &pb.Event_DeviceUnregistered_{ DeviceUnregistered: &pb.Event_DeviceUnregistered{ DeviceIds: []string{deviceID}, @@ -962,7 +966,7 @@ func waitForDevice(t *testing.T, client pb.GrpcGateway_SubscribeToEventsClient, // this alternate to multiple values ev.GetDeviceMetadataUpdated().TwinSynchronization = nil pbTest.CmpEvent(t, &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), Type: &pb.Event_DeviceMetadataUpdated{ DeviceMetadataUpdated: &events.DeviceMetadataUpdated{ DeviceId: deviceID, @@ -1037,7 +1041,7 @@ func TestCoAPGatewayServiceHeartbeat(t *testing.T) { ev, err := client.Recv() require.NoError(t, err) expectedEvent := &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), Type: pbTest.OperationProcessedOK(), CorrelationId: "testToken", } @@ -1067,7 +1071,7 @@ func TestCoAPGatewayServiceHeartbeat(t *testing.T) { ev, err = client.Recv() require.NoError(t, err) pbTest.CmpEvent(t, &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), Type: &pb.Event_DeviceMetadataUpdated{ DeviceMetadataUpdated: &events.DeviceMetadataUpdated{ DeviceId: deviceID, @@ -1108,7 +1112,7 @@ func TestCoAPGatewayServiceHeartbeat(t *testing.T) { ev, err = client.Recv() require.NoError(t, err) pbTest.CmpEvent(t, &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), Type: &pb.Event_DeviceMetadataUpdated{ DeviceMetadataUpdated: &events.DeviceMetadataUpdated{ DeviceId: deviceID, diff --git a/grpc-gateway/service/updateDeviceMetadata_test.go b/grpc-gateway/service/updateDeviceMetadata_test.go index b9f3a26a3..74a5cea9d 100644 --- a/grpc-gateway/service/updateDeviceMetadata_test.go +++ b/grpc-gateway/service/updateDeviceMetadata_test.go @@ -3,7 +3,7 @@ package service_test import ( "context" "crypto/tls" - "fmt" + "errors" "testing" "time" @@ -25,7 +25,6 @@ import ( "github.com/plgd-dev/hub/v2/test/config" oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test" "github.com/plgd-dev/hub/v2/test/service" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -107,7 +106,7 @@ func (f *deviceMetadataUpdatedFilter) WaitForEvent(t time.Duration, correlationI return &ev, nil } case <-time.After(time.Until(deadline)): - return nil, fmt.Errorf("timeout") + return nil, errors.New("timeout") } } } @@ -122,7 +121,7 @@ func TestRequestHandlerUpdateDeviceMetadataTwinEnabled(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -157,10 +156,10 @@ func TestRequestHandlerUpdateDeviceMetadataTwinEnabled(t *testing.T) { obsDeviceMetadataUpdated, err := s.Subscribe(ctx, uuid.New().String(), utils.GetDeviceMetadataEventSubject("*", deviceID, (&events.DeviceMetadataUpdated{}).EventType()), deviceMetadataUpdatedFilter) require.NoError(t, err) defer func() { - err := obs.Close() - assert.NoError(t, err) - err = obsDeviceMetadataUpdated.Close() - assert.NoError(t, err) + errC := obs.Close() + require.NoError(t, errC) + errC = obsDeviceMetadataUpdated.Close() + require.NoError(t, errC) }() _, err = c.UpdateDeviceMetadata(ctx, &pb.UpdateDeviceMetadataRequest{ @@ -176,12 +175,12 @@ func TestRequestHandlerUpdateDeviceMetadataTwinEnabled(t *testing.T) { }) require.NoError(t, err) require.False(t, ev.GetData().GetTwinEnabled()) - require.Equal(t, ev.GetData().GetTwinSynchronization().GetState(), commands.TwinSynchronization_DISABLED) + require.Equal(t, commands.TwinSynchronization_DISABLED, ev.GetData().GetTwinSynchronization().GetState()) deviceMetadataUpdated, err := deviceMetadataUpdatedFilter.WaitForEvent(time.Second, ev.GetData().GetAuditContext().GetCorrelationId()) require.NoError(t, err) require.False(t, deviceMetadataUpdated.GetTwinEnabled()) - require.Equal(t, deviceMetadataUpdated.GetTwinSynchronization().GetState(), commands.TwinSynchronization_DISABLED) + require.Equal(t, commands.TwinSynchronization_DISABLED, deviceMetadataUpdated.GetTwinSynchronization().GetState()) _, err = c.UpdateResource(ctx, &pb.UpdateResourceRequest{ ResourceInterface: interfaces.OC_IF_BASELINE, @@ -215,12 +214,12 @@ func TestRequestHandlerUpdateDeviceMetadataTwinEnabled(t *testing.T) { }) require.NoError(t, err) require.True(t, ev.GetData().GetTwinEnabled()) - require.NotEqual(t, ev.GetData().GetTwinSynchronization().GetState(), commands.TwinSynchronization_DISABLED) + require.NotEqual(t, commands.TwinSynchronization_DISABLED, ev.GetData().GetTwinSynchronization().GetState()) deviceMetadataUpdated, err = deviceMetadataUpdatedFilter.WaitForEvent(time.Second, ev.GetData().GetAuditContext().GetCorrelationId()) require.NoError(t, err) require.True(t, deviceMetadataUpdated.GetTwinEnabled()) - require.NotEqual(t, deviceMetadataUpdated.GetTwinSynchronization().GetState(), commands.TwinSynchronization_DISABLED) + require.NotEqual(t, commands.TwinSynchronization_DISABLED, deviceMetadataUpdated.GetTwinSynchronization().GetState()) for { deviceMetadataUpdated, err = deviceMetadataUpdatedFilter.WaitForEvent(time.Second, "") @@ -266,7 +265,7 @@ func waitForResourceChanged(filter *contentChangedFilter, ignoreHrefs ...string) return ev } for _, ignoreHref := range ignoreHrefs { - if ignoreHref == evChanged.GetResourceId().Href { + if ignoreHref == evChanged.GetResourceId().GetHref() { return waitForResourceChanged(filter, ignoreHrefs...) } } @@ -284,7 +283,7 @@ func TestRequestHandlerUpdateDeviceMetadataTwinForceSynchronization(t *testing.T defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -319,10 +318,10 @@ func TestRequestHandlerUpdateDeviceMetadataTwinForceSynchronization(t *testing.T obsDeviceMetadataUpdated, err := s.Subscribe(ctx, uuid.New().String(), utils.GetDeviceMetadataEventSubject("*", deviceID, (&events.DeviceMetadataUpdated{}).EventType()), deviceMetadataUpdatedFilter) require.NoError(t, err) defer func() { - err := obs.Close() - assert.NoError(t, err) - err = obsDeviceMetadataUpdated.Close() - assert.NoError(t, err) + errC := obs.Close() + require.NoError(t, errC) + errC = obsDeviceMetadataUpdated.Close() + require.NoError(t, errC) }() ev, err := c.UpdateDeviceMetadata(ctx, &pb.UpdateDeviceMetadataRequest{ @@ -336,7 +335,7 @@ func TestRequestHandlerUpdateDeviceMetadataTwinForceSynchronization(t *testing.T deviceMetadataUpdated, err := deviceMetadataUpdatedFilter.WaitForEvent(time.Second, ev.GetData().GetAuditContext().GetCorrelationId()) require.NoError(t, err) require.False(t, deviceMetadataUpdated.GetTwinEnabled()) - require.Equal(t, deviceMetadataUpdated.GetTwinSynchronization().GetState(), commands.TwinSynchronization_DISABLED) + require.Equal(t, commands.TwinSynchronization_DISABLED, deviceMetadataUpdated.GetTwinSynchronization().GetState()) require.Equal(t, int64(0), ev.GetData().GetTwinSynchronization().GetForceSynchronizationAt()) evResourceChanged := waitForResourceChanged(v, plgdtime.ResourceURI) @@ -355,7 +354,7 @@ func TestRequestHandlerUpdateDeviceMetadataTwinForceSynchronization(t *testing.T deviceMetadataUpdated, err = deviceMetadataUpdatedFilter.WaitForEvent(time.Second, ev.GetData().GetAuditContext().GetCorrelationId()) require.NoError(t, err) require.True(t, deviceMetadataUpdated.GetTwinEnabled()) - require.NotEqual(t, deviceMetadataUpdated.GetTwinSynchronization().GetState(), commands.TwinSynchronization_DISABLED) + require.NotEqual(t, commands.TwinSynchronization_DISABLED, deviceMetadataUpdated.GetTwinSynchronization().GetState()) require.Greater(t, ev.GetData().GetTwinSynchronization().GetForceSynchronizationAt(), checkTwin) checkTwin = ev.GetData().GetTwinSynchronization().GetForceSynchronizationAt() @@ -412,7 +411,7 @@ func TestRequestHandlerUpdateDeviceMetadataTwinForceSynchronization(t *testing.T deviceMetadataUpdated, err = deviceMetadataUpdatedFilter.WaitForEvent(time.Second, ev.GetData().GetAuditContext().GetCorrelationId()) require.NoError(t, err) require.True(t, deviceMetadataUpdated.GetTwinEnabled()) - require.NotEqual(t, deviceMetadataUpdated.GetTwinSynchronization().GetState(), commands.TwinSynchronization_DISABLED) + require.NotEqual(t, commands.TwinSynchronization_DISABLED, deviceMetadataUpdated.GetTwinSynchronization().GetState()) require.Greater(t, ev.GetData().GetTwinSynchronization().GetForceSynchronizationAt(), checkTwin) checkTwin = ev.GetData().GetTwinSynchronization().GetForceSynchronizationAt() diff --git a/grpc-gateway/service/updateResource_test.go b/grpc-gateway/service/updateResource_test.go index e8e4cf1dc..10e1f05f5 100644 --- a/grpc-gateway/service/updateResource_test.go +++ b/grpc-gateway/service/updateResource_test.go @@ -111,7 +111,7 @@ func TestUpdateResource(t *testing.T) { }, }, }, - want: pbTest.MakeResourceUpdated(t, deviceID, test.TestResourceLightInstanceHref("1"), "", nil), + want: pbTest.MakeResourceUpdated(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", nil), }, { name: "valid with interface", @@ -127,7 +127,7 @@ func TestUpdateResource(t *testing.T) { }, }, }, - want: pbTest.MakeResourceUpdated(t, deviceID, test.TestResourceLightInstanceHref("1"), "", nil), + want: pbTest.MakeResourceUpdated(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", nil), }, { name: "revert update", @@ -143,7 +143,7 @@ func TestUpdateResource(t *testing.T) { }, }, }, - want: pbTest.MakeResourceUpdated(t, deviceID, test.TestResourceLightInstanceHref("1"), "", nil), + want: pbTest.MakeResourceUpdated(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", nil), }, { name: "update /switches/1", @@ -170,8 +170,9 @@ func TestUpdateResource(t *testing.T) { "value": true, }), }, - Status: commands.Status_OK, - AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + Status: commands.Status_OK, + AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + ResourceTypes: test.TestResourceSwitchesInstanceResourceTypes, }, }, } @@ -183,7 +184,7 @@ func TestUpdateResource(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -227,7 +228,7 @@ func TestRequestHandlerGetAfterUpdateResource(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -244,7 +245,7 @@ func TestRequestHandlerGetAfterUpdateResource(t *testing.T) { client, err := c.GetResources(ctx, &pb.GetResourcesRequest{ DeviceIdFilter: []string{devID}, }) - assert.NoError(t, err) + require.NoError(t, err) for { value, err := client.Recv() if errors.Is(err, io.EOF) { @@ -290,7 +291,7 @@ func TestRequestHandlerRunMultipleUpdateResource(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -307,13 +308,13 @@ func TestRequestHandlerRunMultipleUpdateResource(t *testing.T) { func() { t.Logf("TestRequestHandlerMultipleUpdateResource:run %v\n", i) lightHref := test.TestResourceLightInstanceHref("1") - ctx, cancel := context.WithTimeout(ctx, time.Second*3) + subCtx, cancel := context.WithTimeout(ctx, time.Second*3) defer cancel() - subClient, err := c.SubscribeToEvents(ctx) + subClient, err := c.SubscribeToEvents(subCtx) require.NoError(t, err) defer func() { - err = subClient.CloseSend() - require.NoError(t, err) + errC := subClient.CloseSend() + require.NoError(t, errC) }() err = subClient.Send(&pb.SubscribeToEvents{Action: &pb.SubscribeToEvents_CreateSubscription_{ @@ -329,9 +330,9 @@ func TestRequestHandlerRunMultipleUpdateResource(t *testing.T) { require.NoError(t, err) ev, err := subClient.Recv() require.NoError(t, err) - require.Equal(t, ev.GetOperationProcessed().GetErrorStatus().GetCode(), pb.Event_OperationProcessed_ErrorStatus_OK) + require.Equal(t, pb.Event_OperationProcessed_ErrorStatus_OK, ev.GetOperationProcessed().GetErrorStatus().GetCode()) for j := 1; j >= 0; j-- { - _, err = c.UpdateResource(ctx, &pb.UpdateResourceRequest{ + _, err = c.UpdateResource(subCtx, &pb.UpdateResourceRequest{ ResourceId: commands.NewResourceID(deviceID, lightHref), Content: &pb.Content{ ContentType: message.AppOcfCbor.String(), @@ -352,7 +353,7 @@ func TestRequestHandlerRunMultipleUpdateResource(t *testing.T) { for j := 1; j >= 0; j-- { ev, err = subClient.Recv() require.NoError(t, err) - pbTest.CmpResourceChanged(t, pbTest.MakeResourceChanged(t, deviceID, lightHref, "", makeLightData(j)), ev.GetResourceChanged(), "") + pbTest.CmpResourceChanged(t, pbTest.MakeResourceChanged(t, deviceID, lightHref, test.TestResourceLightInstanceResourceTypes, "", makeLightData(j)), ev.GetResourceChanged(), "") } }() } @@ -373,7 +374,7 @@ func TestRequestHandlerRunMultipleParallelUpdateResource(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -393,10 +394,10 @@ func TestRequestHandlerRunMultipleParallelUpdateResource(t *testing.T) { go func() { defer wg.Done() lightHref := test.TestResourceLightInstanceHref("1") - ctx, cancel := context.WithTimeout(ctx, timeout) + updateCtx, cancel := context.WithTimeout(ctx, timeout) defer cancel() for j := 1; j >= 0; j-- { - _, err := c.UpdateResource(ctx, &pb.UpdateResourceRequest{ + _, err := c.UpdateResource(updateCtx, &pb.UpdateResourceRequest{ ResourceId: commands.NewResourceID(deviceID, lightHref), Content: &pb.Content{ ContentType: message.AppOcfCbor.String(), @@ -405,7 +406,7 @@ func TestRequestHandlerRunMultipleParallelUpdateResource(t *testing.T) { }), }, }) - require.NoError(t, err) + assert.NoError(t, err) } }() } diff --git a/grpc-gateway/subscription/subscription.go b/grpc-gateway/subscription/subscription.go index b86be8524..a830f3f22 100644 --- a/grpc-gateway/subscription/subscription.go +++ b/grpc-gateway/subscription/subscription.go @@ -1,6 +1,7 @@ package subscription import ( + "errors" "fmt" "github.com/google/uuid" @@ -129,13 +130,8 @@ func (s *Sub) Init(owner string, subCache *SubscriptionsCache) error { return nil } -func (s *Sub) isFilteredEvent(e *pb.Event, eventType FilterBitmask) (bool, error) { - if e == nil { - return false, fmt.Errorf("invalid event") - } - if !IsFilteredBit(s.filter, eventType) { - return false, nil - } +//nolint:gocyclo +func (s *Sub) isFilteredEventByType(e *pb.Event) (bool, error) { switch ev := e.GetType().(type) { case *pb.Event_DeviceRegistered_: return true, nil @@ -173,6 +169,16 @@ func (s *Sub) isFilteredEvent(e *pb.Event, eventType FilterBitmask) (bool, error return false, fmt.Errorf("unknown event type('%T')", e.GetType()) } +func (s *Sub) isFilteredEvent(e *pb.Event, eventType FilterBitmask) (bool, error) { + if e == nil { + return false, errors.New("invalid event") + } + if !IsFilteredBit(s.filter, eventType) { + return false, nil + } + return s.isFilteredEventByType(e) +} + func (s *Sub) ProcessEvent(e *pb.Event, eventType FilterBitmask) error { ok, err := s.isFilteredEvent(e, eventType) if err != nil { diff --git a/grpc-gateway/subscription/subscription_test.go b/grpc-gateway/subscription/subscription_test.go index 371360bbc..79762f6b0 100644 --- a/grpc-gateway/subscription/subscription_test.go +++ b/grpc-gateway/subscription/subscription_test.go @@ -43,7 +43,7 @@ func waitForEvent(ctx context.Context, t *testing.T, recvChan <-chan *pb.Event) func check(t *testing.T, ev *pb.Event, expectedEvent *pb.Event) { if expectedEvent.GetResourcePublished() != nil { - expectedEvent.SubscriptionId = ev.SubscriptionId + expectedEvent.SubscriptionId = ev.GetSubscriptionId() } pbTest.CmpEvent(t, expectedEvent, ev, "") } @@ -70,7 +70,7 @@ func checkAndValidateUpdate(ctx context.Context, t *testing.T, rac raservice.Res }) require.NoError(t, err) - resourceUpdatePending := pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceLightInstanceHref("1"), updCorrelationID, + resourceUpdatePending := pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, updCorrelationID, map[string]interface{}{ "power": value, }, @@ -90,7 +90,7 @@ func checkAndValidateUpdate(ctx context.Context, t *testing.T, rac raservice.Res check(t, ev, &pb.Event{ SubscriptionId: s.Id(), Type: &pb.Event_ResourceUpdated{ - ResourceUpdated: pbTest.MakeResourceUpdated(t, deviceID, test.TestResourceLightInstanceHref("1"), updCorrelationID, nil), + ResourceUpdated: pbTest.MakeResourceUpdated(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, updCorrelationID, nil), }, CorrelationId: correlationID, }) @@ -98,7 +98,7 @@ func checkAndValidateUpdate(ctx context.Context, t *testing.T, rac raservice.Res check(t, ev, &pb.Event{ SubscriptionId: s.Id(), Type: &pb.Event_ResourceChanged{ - ResourceChanged: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceLightInstanceHref("1"), "", + ResourceChanged: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", test.LightResourceRepresentation{ Name: "Light", Power: value, @@ -126,8 +126,9 @@ func checkAndValidateRetrieve(ctx context.Context, t *testing.T, rac raservice.R SubscriptionId: s.Id(), Type: &pb.Event_ResourceRetrievePending{ ResourceRetrievePending: &events.ResourceRetrievePending{ - ResourceId: commands.NewResourceID(deviceID, test.TestResourceLightInstanceHref("1")), - AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, retrieveCorrelationID, oauthService.DeviceUserID), + ResourceId: commands.NewResourceID(deviceID, test.TestResourceLightInstanceHref("1")), + AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, retrieveCorrelationID, oauthService.DeviceUserID), + ResourceTypes: test.TestResourceLightInstanceResourceTypes, }, }, CorrelationId: correlationID, @@ -135,7 +136,7 @@ func checkAndValidateRetrieve(ctx context.Context, t *testing.T, rac raservice.R check(t, waitForEvent(ctx, t, recvChan), &pb.Event{ SubscriptionId: s.Id(), Type: &pb.Event_ResourceRetrieved{ - ResourceRetrieved: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceLightInstanceHref("1"), + ResourceRetrieved: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, retrieveCorrelationID, test.LightResourceRepresentation{ Name: "Light", @@ -154,7 +155,7 @@ func getResourceChangedEvents(t *testing.T, deviceID, correlationID, subscriptio events[rid.GetHref()] = &pb.Event{ SubscriptionId: subscriptionID, Type: &pb.Event_ResourceChanged{ - ResourceChanged: pbTest.MakeResourceChanged(t, deviceID, rid.GetHref(), "", res.Representation), + ResourceChanged: pbTest.MakeResourceChanged(t, deviceID, rid.GetHref(), res.ResourceTypes, "", res.Representation), }, CorrelationId: correlationID, } @@ -217,8 +218,8 @@ func TestRequestHandlerSubscribeToEvents(t *testing.T) { err = s.Init(owner, subCache) require.NoError(t, err) defer func() { - err := s.Close() - require.NoError(t, err) + errC := s.Close() + require.NoError(t, errC) }() deviceID, shutdownDevSim := test.OnboardDevSim(ctx, t, rdc, deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, nil) diff --git a/grpc-gateway/subscription/subscriptionsCache.go b/grpc-gateway/subscription/subscriptionsCache.go index d0fb8c673..997fbd6f3 100644 --- a/grpc-gateway/subscription/subscriptionsCache.go +++ b/grpc-gateway/subscription/subscriptionsCache.go @@ -391,13 +391,13 @@ func (c *SubscriptionsCache) Subscribe(subject string, onEvent SendEventWithType closeFunc = c.makeCloseFunc(subject, handlerID) } if s.subscription == nil { - err := s.subscribeLocked(subject, c.conn.Subscribe, func(msg *nats.Msg) { - if err := s.handleEvent(msg); err != nil { - c.errFunc(err) + errS := s.subscribeLocked(subject, c.conn.Subscribe, func(msg *nats.Msg) { + if errH := s.handleEvent(msg); errH != nil { + c.errFunc(errH) } }) - if err != nil { - return err + if errS != nil { + return errS } } return nil diff --git a/http-gateway/Dockerfile b/http-gateway/Dockerfile index ede92cf49..ea2ea05a4 100644 --- a/http-gateway/Dockerfile +++ b/http-gateway/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1 -FROM golang:1.20.13-alpine AS build +FROM golang:1.22.3-alpine AS build ARG VERSION ARG COMMIT_DATE ARG SHORT_COMMIT @@ -11,10 +11,16 @@ COPY go.mod go.sum ./ RUN go mod download COPY . . RUN go mod vendor -RUN ( cd /usr/local/go && patch -p1 < $GOPATH/src/github.com/plgd-dev/hub/tools/docker/patches/shrink_tls_conn.patch ) -RUN ( cd ./vendor/golang.org/x/oauth2 && patch -p1 < $GOPATH/src/github.com/plgd-dev/hub/tools/docker/patches/golang_org_x_oauth2_propagate_error.patch ) +WORKDIR /usr/local/go +RUN ( patch -p1 < "$GOPATH/src/github.com/plgd-dev/hub/tools/docker/patches/shrink_tls_conn.patch" ) +WORKDIR $GOPATH/src/github.com/plgd-dev/hub/vendor/golang.org/x/oauth2 +RUN ( patch -p1 < "$GOPATH/src/github.com/plgd-dev/hub/tools/docker/patches/golang_org_x_oauth2_propagate_error.patch" ) WORKDIR $GOPATH/src/github.com/plgd-dev/hub/http-gateway -RUN CGO_ENABLED=0 go build -mod=vendor -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" -o /go/bin/http-gateway ./cmd/service +RUN go build \ + -mod=vendor \ + -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" \ + -o /go/bin/http-gateway \ + ./cmd/service FROM alpine:3.19 AS security-provider RUN apk add -U --no-cache ca-certificates @@ -27,4 +33,4 @@ COPY --from=security-provider /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY http-gateway/web/build /usr/local/var/www COPY --from=build /go/bin/http-gateway /usr/local/bin/http-gateway USER nonroot -ENTRYPOINT [ "/usr/local/bin/http-gateway" ] \ No newline at end of file +ENTRYPOINT [ "/usr/local/bin/http-gateway" ] diff --git a/http-gateway/Dockerfile.www b/http-gateway/Dockerfile.www index 9c97348fa..8cfa637bb 100644 --- a/http-gateway/Dockerfile.www +++ b/http-gateway/Dockerfile.www @@ -1,11 +1,11 @@ # syntax=docker/dockerfile:1 FROM node:18 AS build-web COPY http-gateway/web /web -RUN cd /web && \ - npm config set fetch-retries 3 && \ +WORKDIR /web +RUN npm config set fetch-retries 3 && \ npm config set fetch-retry-mintimeout 600000 && \ npm config set fetch-retry-maxtimeout 1200000 && \ npm config set fetch-timeout 1800000 && \ npm install --ignore-scripts && \ npm run :generate:theme && \ - npm run build \ No newline at end of file + npm run build diff --git a/http-gateway/serverMux/jsonMarshaler_test.go b/http-gateway/serverMux/jsonMarshaler_test.go index e4263e59e..567dfc578 100644 --- a/http-gateway/serverMux/jsonMarshaler_test.go +++ b/http-gateway/serverMux/jsonMarshaler_test.go @@ -27,7 +27,7 @@ func TestJsonMarshalerMarshal(t *testing.T) { { name: "valid", args: args{ - v: pb.MakeResourceChanged(t, "deviceID", "/href", "correlationID", map[interface{}]interface{}{ + v: pb.MakeResourceChanged(t, "deviceID", "/href", []string{"type"}, "correlationID", map[interface{}]interface{}{ "key": "value", }), }, @@ -44,7 +44,8 @@ func TestJsonMarshalerMarshal(t *testing.T) { "deviceId": "deviceID", "href": "/href", }, - "status": "OK", + "status": "OK", + "resourceTypes": []interface{}{"type"}, }, }, } diff --git a/http-gateway/serverMux/router.go b/http-gateway/serverMux/router.go index bd716a3e0..9c0d85a9c 100644 --- a/http-gateway/serverMux/router.go +++ b/http-gateway/serverMux/router.go @@ -14,7 +14,7 @@ import ( func NewRouter(queryCaseInsensitive map[string]string, authInterceptor kitHttp.Interceptor, opts ...kitHttp.LogOpt) *router.Router { r := router.NewRouter() r.Use(kitHttp.CreateLoggingMiddleware(opts...)) - r.Use(kitHttp.CreateAuthMiddleware(authInterceptor, func(ctx context.Context, w http.ResponseWriter, r *http.Request, err error) { + r.Use(kitHttp.CreateAuthMiddleware(authInterceptor, func(_ context.Context, w http.ResponseWriter, r *http.Request, err error) { WriteError(w, grpc.ForwardErrorf(codes.Unauthenticated, "cannot access to %v: %w", r.RequestURI, err)) })) r.Use(kitHttp.CreateMakeQueryCaseInsensitiveMiddleware(queryCaseInsensitive, opts...)) diff --git a/http-gateway/service/cancelPendingCommands_test.go b/http-gateway/service/cancelPendingCommands_test.go index 887e20bb9..362e955f3 100644 --- a/http-gateway/service/cancelPendingCommands_test.go +++ b/http-gateway/service/cancelPendingCommands_test.go @@ -32,7 +32,7 @@ func TestRequestHandlerCancelPendingCommands(t *testing.T) { _, resourcePendings, _, shutdown := pbTest.InitPendingEvents(ctx, t) defer shutdown() - require.Equal(t, len(resourcePendings), 4) + require.Len(t, resourcePendings, 4) type args struct { deviceID string @@ -109,7 +109,7 @@ func TestRequestHandlerCancelResourceCommand(t *testing.T) { _, resourcePendings, _, shutdown := pbTest.InitPendingEvents(ctx, t) defer shutdown() - require.Equal(t, len(resourcePendings), 4) + require.Len(t, resourcePendings, 4) type args struct { deviceID string diff --git a/http-gateway/service/cancelPendingMetadataUpdate_test.go b/http-gateway/service/cancelPendingMetadataUpdate_test.go index 7caf37295..945ede80c 100644 --- a/http-gateway/service/cancelPendingMetadataUpdate_test.go +++ b/http-gateway/service/cancelPendingMetadataUpdate_test.go @@ -22,7 +22,7 @@ func TestRequestHandlerCancelDeviceMetadataUpdate(t *testing.T) { _, _, devicePendings, shutdown := pbTest.InitPendingEvents(ctx, t) defer shutdown() - require.Equal(t, len(devicePendings), 2) + require.Len(t, devicePendings, 2) type args struct { deviceID string diff --git a/http-gateway/service/cancelPendingMetadataUpdates_test.go b/http-gateway/service/cancelPendingMetadataUpdates_test.go index 4e997c275..f21bf3f13 100644 --- a/http-gateway/service/cancelPendingMetadataUpdates_test.go +++ b/http-gateway/service/cancelPendingMetadataUpdates_test.go @@ -22,7 +22,7 @@ func TestRequestHandlerCancelPendingMetadataUpdates(t *testing.T) { _, _, devicePendings, shutdown := pbTest.InitPendingEvents(ctx, t) defer shutdown() - require.Equal(t, len(devicePendings), 2) + require.Len(t, devicePendings, 2) type args struct { deviceID string diff --git a/http-gateway/service/createResource_test.go b/http-gateway/service/createResource_test.go index 404371fe3..55008bf06 100644 --- a/http-gateway/service/createResource_test.go +++ b/http-gateway/service/createResource_test.go @@ -187,7 +187,7 @@ func TestRequestHandler_CreateResource(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -219,7 +219,7 @@ func TestRequestHandler_CreateResource(t *testing.T) { return } require.NoError(t, err) - want := pbTest.MakeResourceCreated(t, deviceID, tt.args.href, "", tt.wantData) + want := pbTest.MakeResourceCreated(t, deviceID, tt.args.href, test.TestResourceSwitchesResourceTypes, "", tt.wantData) pbTest.CmpResourceCreated(t, want, got.GetData()) }) } diff --git a/http-gateway/service/deleteDevice.go b/http-gateway/service/deleteDevice.go index 48b71c444..a43eb8076 100644 --- a/http-gateway/service/deleteDevice.go +++ b/http-gateway/service/deleteDevice.go @@ -2,36 +2,19 @@ package service import ( "net/http" - "net/http/httptest" - "github.com/google/go-querystring/query" - "github.com/gorilla/mux" "github.com/plgd-dev/hub/v2/http-gateway/serverMux" - "github.com/plgd-dev/hub/v2/http-gateway/uri" kitNetGrpc "github.com/plgd-dev/hub/v2/pkg/net/grpc" "google.golang.org/grpc/codes" ) func (requestHandler *RequestHandler) deleteDevice(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - deviceID := vars[uri.DeviceIDKey] - type Options struct { - DeviceIDFilter []string `url:"deviceIdFilter"` - } - opt := Options{ - DeviceIDFilter: []string{deviceID}, - } - v, err := query.Values(opt) + deviceID, rec, err := requestHandler.serveDevicesRequest(r) if err != nil { serverMux.WriteError(w, kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "cannot delete device('%v'): %v", deviceID, err)) return } - r.URL.Path = uri.Devices - r.URL.RawQuery = v.Encode() - rec := httptest.NewRecorder() - requestHandler.mux.ServeHTTP(rec, r) - - toSimpleResponse(w, rec, func(w http.ResponseWriter, err error) { + toSimpleResponse(w, rec, false, func(w http.ResponseWriter, err error) { serverMux.WriteError(w, kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "cannot delete device('%v'): %v", deviceID, err)) }, streamResponseKey) } diff --git a/http-gateway/service/deleteDevices_test.go b/http-gateway/service/deleteDevices_test.go index 3a5ab5309..2ebb1bb9d 100644 --- a/http-gateway/service/deleteDevices_test.go +++ b/http-gateway/service/deleteDevices_test.go @@ -34,7 +34,7 @@ func TestRequestHandlerDeleteDevices(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -93,7 +93,7 @@ func TestRequestHandlerDeleteDevices(t *testing.T) { var got pb.DeleteDevicesResponse err = httpgwTest.Unmarshal(resp.StatusCode, resp.Body, &got) require.NoError(t, err) - require.Equal(t, tt.want.DeviceIds, got.DeviceIds) + require.Equal(t, tt.want.GetDeviceIds(), got.GetDeviceIds()) }) } } diff --git a/http-gateway/service/deleteResource_test.go b/http-gateway/service/deleteResource_test.go index 1b832a95b..95742cae3 100644 --- a/http-gateway/service/deleteResource_test.go +++ b/http-gateway/service/deleteResource_test.go @@ -119,7 +119,7 @@ func TestRequestHandlerDeleteResource(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -149,7 +149,7 @@ func TestRequestHandlerDeleteResource(t *testing.T) { return } require.NoError(t, err) - want := pbTest.MakeResourceDeleted(deviceID, tt.args.href, "") + want := pbTest.MakeResourceDeleted(deviceID, tt.args.href, test.TestResourceSwitchesInstanceResourceTypes, "") pbTest.CmpResourceDeleted(t, want, got.GetData()) }) } @@ -198,7 +198,7 @@ func TestRequestHandlerBatchDeleteResource(t *testing.T) { href: test.TestResourceSwitchesHref, }, want: func() *events.ResourceDeleted { - rdel := pbTest.MakeResourceDeleted(deviceID, test.TestResourceSwitchesHref, "") + rdel := pbTest.MakeResourceDeleted(deviceID, test.TestResourceSwitchesHref, test.TestResourceSwitchesResourceTypes, "") links := test.CollectionLinkRepresentations{} for _, switchID := range switchIDs { links = append(links, test.CollectionLinkRepresentation{ @@ -229,7 +229,7 @@ func TestRequestHandlerBatchDeleteResource(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/http-gateway/service/getDevice.go b/http-gateway/service/getDevice.go index 0cf9e3985..8a3eac3c7 100644 --- a/http-gateway/service/getDevice.go +++ b/http-gateway/service/getDevice.go @@ -25,7 +25,8 @@ func isNotModifiedResponse(result interface{}) bool { } data, ok := m["data"] if ok { - tmp, ok := data.(map[string]interface{}) + var tmp map[string]interface{} + tmp, ok = data.(map[string]interface{}) if !ok { return false } @@ -64,7 +65,10 @@ func writeSimpleResponse(w http.ResponseWriter, rec *httptest.ResponseRecorder, } } -func toSimpleResponse(w http.ResponseWriter, rec *httptest.ResponseRecorder, writeError func(w http.ResponseWriter, err error), responseKeys ...string) { +func getResponse(rec *httptest.ResponseRecorder, allowEmpty bool, responseKeys ...string) (interface{}, error) { + if len(rec.Body.Bytes()) == 0 && allowEmpty { + return nil, nil + } iter := json.NewDecoder(bytes.NewReader(rec.Body.Bytes())) datas := make([]interface{}, 0, 1) for { @@ -74,21 +78,17 @@ func toSimpleResponse(w http.ResponseWriter, rec *httptest.ResponseRecorder, wri break } if err != nil { - writeError(w, err) - return + return nil, err } datas = append(datas, v) } if len(datas) == 0 { - writeError(w, kitNetGrpc.ForwardErrorf(codes.NotFound, "not found")) - return + return nil, kitNetGrpc.ForwardErrorf(codes.NotFound, "not found") } if len(datas) != 1 { - writeError(w, kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "invalid number of responses")) - return + return nil, kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "invalid number of responses") } - var result interface{} - result = datas[0] + result := datas[0] for _, key := range responseKeys { m, ok := result.(map[string]interface{}) if !ok { @@ -101,10 +101,19 @@ func toSimpleResponse(w http.ResponseWriter, rec *httptest.ResponseRecorder, wri break } } + return result, nil +} + +func toSimpleResponse(w http.ResponseWriter, rec *httptest.ResponseRecorder, allowEmpty bool, writeError func(w http.ResponseWriter, err error), responseKeys ...string) { + result, err := getResponse(rec, allowEmpty, responseKeys...) + if err != nil { + writeError(w, err) + return + } writeSimpleResponse(w, rec, result, writeError) } -func (requestHandler *RequestHandler) getDevice(w http.ResponseWriter, r *http.Request) { +func (requestHandler *RequestHandler) serveDevicesRequest(r *http.Request) (string, *httptest.ResponseRecorder, error) { vars := mux.Vars(r) deviceID := vars[uri.DeviceIDKey] type Options struct { @@ -115,15 +124,22 @@ func (requestHandler *RequestHandler) getDevice(w http.ResponseWriter, r *http.R } v, err := query.Values(opt) if err != nil { - serverMux.WriteError(w, kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "cannot get device('%v'): %v", deviceID, err)) - return + return deviceID, nil, err } r.URL.Path = uri.Devices r.URL.RawQuery = v.Encode() rec := httptest.NewRecorder() requestHandler.mux.ServeHTTP(rec, r) + return deviceID, rec, nil +} - toSimpleResponse(w, rec, func(w http.ResponseWriter, err error) { +func (requestHandler *RequestHandler) getDevice(w http.ResponseWriter, r *http.Request) { + deviceID, rec, err := requestHandler.serveDevicesRequest(r) + if err != nil { + serverMux.WriteError(w, kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "cannot get device('%v'): %v", deviceID, err)) + return + } + toSimpleResponse(w, rec, false, func(w http.ResponseWriter, err error) { serverMux.WriteError(w, kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "cannot get device('%v'): %v", deviceID, err)) }, streamResponseKey) } diff --git a/http-gateway/service/getDevicePendingCommands_test.go b/http-gateway/service/getDevicePendingCommands_test.go index 1f6d5281e..af60afb90 100644 --- a/http-gateway/service/getDevicePendingCommands_test.go +++ b/http-gateway/service/getDevicePendingCommands_test.go @@ -63,13 +63,14 @@ func TestRequestHandlerGetDevicePendingCommands(t *testing.T) { DeviceId: deviceID, Href: platform.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: []string{platform.ResourceType}, }, }, }, { Command: &pb.PendingCommand_ResourceCreatePending{ - ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, "", + ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, test.TestResourceDeviceResourceTypes, "", map[string]interface{}{ "power": 1, }), @@ -82,13 +83,14 @@ func TestRequestHandlerGetDevicePendingCommands(t *testing.T) { DeviceId: deviceID, Href: device.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceDeviceResourceTypes, }, }, }, { Command: &pb.PendingCommand_ResourceUpdatePending{ - ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceLightInstanceHref("1"), "", + ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", map[string]interface{}{ "power": 1, }), @@ -111,7 +113,8 @@ func TestRequestHandlerGetDevicePendingCommands(t *testing.T) { DeviceId: deviceID, Href: platform.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: []string{platform.ResourceType}, }, }, }, @@ -127,7 +130,7 @@ func TestRequestHandlerGetDevicePendingCommands(t *testing.T) { want: []*pb.PendingCommand{ { Command: &pb.PendingCommand_ResourceCreatePending{ - ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, "", + ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, test.TestResourceDeviceResourceTypes, "", map[string]interface{}{ "power": 1, }, @@ -151,7 +154,8 @@ func TestRequestHandlerGetDevicePendingCommands(t *testing.T) { DeviceId: deviceID, Href: device.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceDeviceResourceTypes, }, }, }, @@ -167,7 +171,7 @@ func TestRequestHandlerGetDevicePendingCommands(t *testing.T) { want: []*pb.PendingCommand{ { Command: &pb.PendingCommand_ResourceUpdatePending{ - ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceLightInstanceHref("1"), "", + ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", map[string]interface{}{ "power": 1, }), @@ -185,7 +189,7 @@ func TestRequestHandlerGetDevicePendingCommands(t *testing.T) { want: []*pb.PendingCommand{ { Command: &pb.PendingCommand_ResourceCreatePending{ - ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, "", + ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, test.TestResourceDeviceResourceTypes, "", map[string]interface{}{ "power": 1, }), @@ -198,7 +202,8 @@ func TestRequestHandlerGetDevicePendingCommands(t *testing.T) { DeviceId: deviceID, Href: device.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceDeviceResourceTypes, }, }, }, @@ -231,7 +236,7 @@ func TestRequestHandlerGetDevicePendingCommands(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -246,9 +251,9 @@ func TestRequestHandlerGetDevicePendingCommands(t *testing.T) { secureGWShutdown() createFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + createCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.CreateResource(ctx, &pb.CreateResourceRequest{ + _, errC := c.CreateResource(createCtx, &pb.CreateResourceRequest{ ResourceId: commands.NewResourceID(deviceID, device.ResourceURI), Content: &pb.Content{ ContentType: message.AppOcfCbor.String(), @@ -257,22 +262,22 @@ func TestRequestHandlerGetDevicePendingCommands(t *testing.T) { }), }, }) - require.Error(t, err) + require.Error(t, errC) } createFn() retrieveFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + retrieveCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.GetResourceFromDevice(ctx, &pb.GetResourceFromDeviceRequest{ + _, errG := c.GetResourceFromDevice(retrieveCtx, &pb.GetResourceFromDeviceRequest{ ResourceId: commands.NewResourceID(deviceID, platform.ResourceURI), }) - require.Error(t, err) + require.Error(t, errG) } retrieveFn() updateFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + updateCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.UpdateResource(ctx, &pb.UpdateResourceRequest{ + _, errU := c.UpdateResource(updateCtx, &pb.UpdateResourceRequest{ ResourceId: commands.NewResourceID(deviceID, test.TestResourceLightInstanceHref("1")), Content: &pb.Content{ ContentType: message.AppOcfCbor.String(), @@ -281,26 +286,26 @@ func TestRequestHandlerGetDevicePendingCommands(t *testing.T) { }), }, }) - require.Error(t, err) + require.Error(t, errU) } updateFn() deleteFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + deleteCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.DeleteResource(ctx, &pb.DeleteResourceRequest{ + _, errD := c.DeleteResource(deleteCtx, &pb.DeleteResourceRequest{ ResourceId: commands.NewResourceID(deviceID, device.ResourceURI), }) - require.Error(t, err) + require.Error(t, errD) } deleteFn() updateDeviceMetadataFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + updateCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.UpdateDeviceMetadata(ctx, &pb.UpdateDeviceMetadataRequest{ + _, errU := c.UpdateDeviceMetadata(updateCtx, &pb.UpdateDeviceMetadataRequest{ DeviceId: deviceID, TwinEnabled: false, }) - require.Error(t, err) + require.Error(t, errU) } updateDeviceMetadataFn() diff --git a/http-gateway/service/getDeviceResourceLinks.go b/http-gateway/service/getDeviceResourceLinks.go index bc9c865b8..7eb6b3d90 100644 --- a/http-gateway/service/getDeviceResourceLinks.go +++ b/http-gateway/service/getDeviceResourceLinks.go @@ -40,7 +40,7 @@ func (requestHandler *RequestHandler) getDeviceResourceLinks(w http.ResponseWrit rec := httptest.NewRecorder() requestHandler.mux.ServeHTTP(rec, r) - toSimpleResponse(w, rec, func(w http.ResponseWriter, err error) { + toSimpleResponse(w, rec, false, func(w http.ResponseWriter, err error) { serverMux.WriteError(w, kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "cannot get device('%v') resource links: %v", deviceID, err)) }, streamResponseKey) } diff --git a/http-gateway/service/getDeviceResourceLinks_test.go b/http-gateway/service/getDeviceResourceLinks_test.go index bd5549d93..889ddcd9b 100644 --- a/http-gateway/service/getDeviceResourceLinks_test.go +++ b/http-gateway/service/getDeviceResourceLinks_test.go @@ -41,7 +41,7 @@ func TestRequestHandlerGetDeviceResourceLinks(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/http-gateway/service/getDeviceResources_test.go b/http-gateway/service/getDeviceResources_test.go index 7cecbea5e..284c2d396 100644 --- a/http-gateway/service/getDeviceResources_test.go +++ b/http-gateway/service/getDeviceResources_test.go @@ -34,7 +34,7 @@ func getResourceChanged(t *testing.T, deviceID string, href string) *events.Reso for _, l := range test.GetAllBackendResourceRepresentations(t, deviceID, test.TestDeviceName) { rid := commands.ResourceIdFromString(l.Href) if rid.GetHref() == href { - return pbTest.MakeResourceChanged(t, deviceID, rid.GetHref(), "", l.Representation) + return pbTest.MakeResourceChanged(t, deviceID, rid.GetHref(), l.ResourceTypes, "", l.Representation) } } return nil @@ -65,7 +65,7 @@ func getResources(t *testing.T, deviceID, deviceName, switchID string) []*pb.Res if rid.GetHref() == test.TestResourceSwitchesHref { resources = append(resources, &pb.Resource{ Types: getResourceType(rid.GetHref()), - Data: pbTest.MakeResourceChanged(t, deviceID, rid.GetHref(), "", []interface{}{ + Data: pbTest.MakeResourceChanged(t, deviceID, rid.GetHref(), res.ResourceTypes, "", []interface{}{ map[string]interface{}{ "href": test.TestResourceSwitchesInstanceHref(switchID), "if": []string{interfaces.OC_IF_A, interfaces.OC_IF_BASELINE}, @@ -80,13 +80,13 @@ func getResources(t *testing.T, deviceID, deviceName, switchID string) []*pb.Res } else { resources = append(resources, &pb.Resource{ Types: getResourceType(rid.GetHref()), - Data: pbTest.MakeResourceChanged(t, deviceID, rid.GetHref(), "", res.Representation), + Data: pbTest.MakeResourceChanged(t, deviceID, rid.GetHref(), res.ResourceTypes, "", res.Representation), }) } } resources = append(resources, &pb.Resource{ Types: []string{types.BINARY_SWITCH}, - Data: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceSwitchesInstanceHref(switchID), "", test.SwitchResourceRepresentation{}), + Data: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceSwitchesInstanceHref(switchID), test.TestResourceSwitchesInstanceResourceTypes, "", test.SwitchResourceRepresentation{}), }) return resources } @@ -154,7 +154,7 @@ func TestRequestHandlerGetDeviceResources(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) defer func() { diff --git a/http-gateway/service/getDevice_test.go b/http-gateway/service/getDevice_test.go index 3e3c09c98..e3390aeb5 100644 --- a/http-gateway/service/getDevice_test.go +++ b/http-gateway/service/getDevice_test.go @@ -68,7 +68,7 @@ func TestRequestHandlerGetDevice(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -101,7 +101,7 @@ func TestRequestHandlerGetDevice(t *testing.T) { return } require.NoError(t, err) - assert.NotEmpty(t, dev.ProtocolIndependentId) + assert.NotEmpty(t, dev.GetProtocolIndependentId()) devices = append(devices, &dev) } pbTest.CmpDeviceValues(t, tt.want, devices) diff --git a/http-gateway/service/getDevicesMetadata_test.go b/http-gateway/service/getDevicesMetadata_test.go index 8e542ec94..4ffc9668d 100644 --- a/http-gateway/service/getDevicesMetadata_test.go +++ b/http-gateway/service/getDevicesMetadata_test.go @@ -121,7 +121,7 @@ func TestRequestHandlerGetDevicesMetadata(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/http-gateway/service/getDevices_test.go b/http-gateway/service/getDevices_test.go index db6db4416..b7604c9ed 100644 --- a/http-gateway/service/getDevices_test.go +++ b/http-gateway/service/getDevices_test.go @@ -121,7 +121,7 @@ func TestRequestHandlerGetDevices(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) defer func() { @@ -134,7 +134,7 @@ func TestRequestHandlerGetDevices(t *testing.T) { defer shutdownDevSim() toStringSlice := func(s []pb.GetDevicesRequest_Status) []string { - var sf []string + sf := make([]string, 0, len(s)) for _, v := range s { sf = append(sf, strconv.FormatInt(int64(v), 10)) } @@ -158,7 +158,7 @@ func TestRequestHandlerGetDevices(t *testing.T) { break } require.NoError(t, err) - assert.NotEmpty(t, dev.ProtocolIndependentId) + assert.NotEmpty(t, dev.GetProtocolIndependentId()) devices = append(devices, &dev) } pbTest.CmpDeviceValues(t, tt.want, devices) diff --git a/http-gateway/service/getEvents_test.go b/http-gateway/service/getEvents_test.go index ae72ae375..9cec4e49d 100644 --- a/http-gateway/service/getEvents_test.go +++ b/http-gateway/service/getEvents_test.go @@ -56,7 +56,7 @@ func TestRequestHandlerGetEvents(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/http-gateway/service/getHubConfiguration.go b/http-gateway/service/getHubConfiguration.go index f63659c77..4bfb2e90f 100644 --- a/http-gateway/service/getHubConfiguration.go +++ b/http-gateway/service/getHubConfiguration.go @@ -78,7 +78,7 @@ func (requestHandler *RequestHandler) getHubConfiguration(w http.ResponseWriter, m := serverMux.NewJsonpbMarshaler() w.Header().Set(contentTypeHeaderKey, uri.ApplicationProtoJsonContentType) w.WriteHeader(http.StatusOK) - if err := m.NewEncoder(w).Encode(resp); err != nil { + if err = m.NewEncoder(w).Encode(resp); err != nil { log.Errorf("failed to write response: %v", err) } return diff --git a/http-gateway/service/getPendingCommands_test.go b/http-gateway/service/getPendingCommands_test.go index 9813962c4..3682ba020 100644 --- a/http-gateway/service/getPendingCommands_test.go +++ b/http-gateway/service/getPendingCommands_test.go @@ -63,7 +63,7 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { want: []*pb.PendingCommand{ { Command: &pb.PendingCommand_ResourceUpdatePending{ - ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceLightInstanceHref("1"), "", + ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", map[string]interface{}{ "power": 1, }), @@ -96,13 +96,14 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { DeviceId: deviceID, Href: platform.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: []string{platform.ResourceType}, }, }, }, { Command: &pb.PendingCommand_ResourceCreatePending{ - ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, "", + ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, test.TestResourceDeviceResourceTypes, "", map[string]interface{}{ "power": 1, }), @@ -115,13 +116,14 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { DeviceId: deviceID, Href: device.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceDeviceResourceTypes, }, }, }, { Command: &pb.PendingCommand_ResourceUpdatePending{ - ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceLightInstanceHref("1"), "", + ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", map[string]interface{}{ "power": 1, }), @@ -143,7 +145,8 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { DeviceId: deviceID, Href: platform.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: []string{platform.ResourceType}, }, }, }, @@ -158,7 +161,7 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { want: []*pb.PendingCommand{ { Command: &pb.PendingCommand_ResourceCreatePending{ - ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, "", + ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, test.TestResourceDeviceResourceTypes, "", map[string]interface{}{ "power": 1, }), @@ -180,7 +183,8 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { DeviceId: deviceID, Href: device.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceDeviceResourceTypes, }, }, }, @@ -195,7 +199,7 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { want: []*pb.PendingCommand{ { Command: &pb.PendingCommand_ResourceUpdatePending{ - ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceLightInstanceHref("1"), "", + ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", map[string]interface{}{ "power": 1, }), @@ -212,7 +216,7 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { want: []*pb.PendingCommand{ { Command: &pb.PendingCommand_ResourceCreatePending{ - ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, "", + ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, test.TestResourceDeviceResourceTypes, "", map[string]interface{}{ "power": 1, }), @@ -225,7 +229,8 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { DeviceId: deviceID, Href: device.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceDeviceResourceTypes, }, }, }, @@ -278,7 +283,7 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -293,9 +298,9 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { secureGWShutdown() createFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + createCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.CreateResource(ctx, &pb.CreateResourceRequest{ + _, errC := c.CreateResource(createCtx, &pb.CreateResourceRequest{ ResourceId: commands.NewResourceID(deviceID, device.ResourceURI), Content: &pb.Content{ ContentType: message.AppOcfCbor.String(), @@ -304,22 +309,22 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { }), }, }) - require.Error(t, err) + require.Error(t, errC) } createFn() retrieveFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + retrieveCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.GetResourceFromDevice(ctx, &pb.GetResourceFromDeviceRequest{ + _, errG := c.GetResourceFromDevice(retrieveCtx, &pb.GetResourceFromDeviceRequest{ ResourceId: commands.NewResourceID(deviceID, platform.ResourceURI), }) - require.Error(t, err) + require.Error(t, errG) } retrieveFn() updateFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + updateCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.UpdateResource(ctx, &pb.UpdateResourceRequest{ + _, errU := c.UpdateResource(updateCtx, &pb.UpdateResourceRequest{ ResourceId: commands.NewResourceID(deviceID, test.TestResourceLightInstanceHref("1")), Content: &pb.Content{ ContentType: message.AppOcfCbor.String(), @@ -328,26 +333,26 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { }), }, }) - require.Error(t, err) + require.Error(t, errU) } updateFn() deleteFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + deleteCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.DeleteResource(ctx, &pb.DeleteResourceRequest{ + _, errD := c.DeleteResource(deleteCtx, &pb.DeleteResourceRequest{ ResourceId: commands.NewResourceID(deviceID, device.ResourceURI), }) - require.Error(t, err) + require.Error(t, errD) } deleteFn() updateDeviceMetadataFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + updateCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.UpdateDeviceMetadata(ctx, &pb.UpdateDeviceMetadataRequest{ + _, errU := c.UpdateDeviceMetadata(updateCtx, &pb.UpdateDeviceMetadataRequest{ DeviceId: deviceID, TwinEnabled: false, }) - require.Error(t, err) + require.Error(t, errU) } updateDeviceMetadataFn() diff --git a/http-gateway/service/getPendingMetadataUpdates_test.go b/http-gateway/service/getPendingMetadataUpdates_test.go index 263a3d1ea..622861bb7 100644 --- a/http-gateway/service/getPendingMetadataUpdates_test.go +++ b/http-gateway/service/getPendingMetadataUpdates_test.go @@ -93,7 +93,7 @@ func TestRequestHandlerGetPendingMetadataUpdates(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -108,9 +108,9 @@ func TestRequestHandlerGetPendingMetadataUpdates(t *testing.T) { secureGWShutdown() createFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + createCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.CreateResource(ctx, &pb.CreateResourceRequest{ + _, errC := c.CreateResource(createCtx, &pb.CreateResourceRequest{ ResourceId: commands.NewResourceID(deviceID, device.ResourceURI), Content: &pb.Content{ ContentType: message.AppOcfCbor.String(), @@ -119,22 +119,22 @@ func TestRequestHandlerGetPendingMetadataUpdates(t *testing.T) { }), }, }) - require.Error(t, err) + require.Error(t, errC) } createFn() retrieveFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + retrieveCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.GetResourceFromDevice(ctx, &pb.GetResourceFromDeviceRequest{ + _, errG := c.GetResourceFromDevice(retrieveCtx, &pb.GetResourceFromDeviceRequest{ ResourceId: commands.NewResourceID(deviceID, platform.ResourceURI), }) - require.Error(t, err) + require.Error(t, errG) } retrieveFn() updateFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + updateCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.UpdateResource(ctx, &pb.UpdateResourceRequest{ + _, errU := c.UpdateResource(updateCtx, &pb.UpdateResourceRequest{ ResourceId: commands.NewResourceID(deviceID, test.TestResourceLightInstanceHref("1")), Content: &pb.Content{ ContentType: message.AppOcfCbor.String(), @@ -143,26 +143,26 @@ func TestRequestHandlerGetPendingMetadataUpdates(t *testing.T) { }), }, }) - require.Error(t, err) + require.Error(t, errU) } updateFn() deleteFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + deleteCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.DeleteResource(ctx, &pb.DeleteResourceRequest{ + _, errD := c.DeleteResource(deleteCtx, &pb.DeleteResourceRequest{ ResourceId: commands.NewResourceID(deviceID, device.ResourceURI), }) - require.Error(t, err) + require.Error(t, errD) } deleteFn() updateDeviceMetadataFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + updateCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.UpdateDeviceMetadata(ctx, &pb.UpdateDeviceMetadataRequest{ + _, errU := c.UpdateDeviceMetadata(updateCtx, &pb.UpdateDeviceMetadataRequest{ DeviceId: deviceID, TwinEnabled: false, }) - require.Error(t, err) + require.Error(t, errU) } updateDeviceMetadataFn() diff --git a/http-gateway/service/getResource.go b/http-gateway/service/getResource.go index 1f0272cd6..9f8db8a07 100644 --- a/http-gateway/service/getResource.go +++ b/http-gateway/service/getResource.go @@ -2,18 +2,22 @@ package service import ( "encoding/base64" + "fmt" "net/http" "net/http/httptest" + "strconv" "strings" "github.com/google/go-querystring/query" "github.com/gorilla/mux" + "github.com/plgd-dev/device/v2/pkg/codec/json" "github.com/plgd-dev/hub/v2/grpc-gateway/pb" "github.com/plgd-dev/hub/v2/http-gateway/serverMux" "github.com/plgd-dev/hub/v2/http-gateway/uri" kitNetGrpc "github.com/plgd-dev/hub/v2/pkg/net/grpc" "github.com/plgd-dev/hub/v2/resource-aggregate/commands" "google.golang.org/grpc/codes" + "google.golang.org/protobuf/encoding/protojson" ) const errFmtFromTwin = "cannot get resource('%v') from twin: %w" @@ -47,7 +51,7 @@ func getETags(r *http.Request) [][]byte { return etags } -func (requestHandler *RequestHandler) getResourceFromTwin(w http.ResponseWriter, r *http.Request, resourceID *pb.ResourceIdFilter) { +func (requestHandler *RequestHandler) getResourceFromTwin(r *http.Request, resourceID *pb.ResourceIdFilter) (*httptest.ResponseRecorder, error) { type Options struct { ResourceIDFilter []string `url:"httpResourceIdFilter"` } @@ -57,33 +61,35 @@ func (requestHandler *RequestHandler) getResourceFromTwin(w http.ResponseWriter, v, err := query.Values(opt) if err != nil { - serverMux.WriteError(w, kitNetGrpc.ForwardErrorf(codes.InvalidArgument, errFmtFromTwin, resourceID, err)) - return + return nil, err } r.URL.Path = uri.Resources r.URL.RawQuery = v.Encode() rec := httptest.NewRecorder() requestHandler.mux.ServeHTTP(rec, r) + return rec, nil +} - toSimpleResponse(w, rec, func(w http.ResponseWriter, err error) { - serverMux.WriteError(w, kitNetGrpc.ForwardErrorf(codes.InvalidArgument, errFmtFromTwin, resourceID, err)) - }, streamResponseKey) +func parseBoolQuery(str string) bool { + val, err := strconv.ParseBool(str) + if err != nil { + return false + } + return val } -func (requestHandler *RequestHandler) getResource(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - deviceID := vars[uri.DeviceIDKey] - resourceHref := vars[uri.ResourceHrefKey] - twin := r.URL.Query().Get(uri.TwinQueryKey) - resourceInterface := r.URL.Query().Get(uri.ResourceInterfaceQueryKey) +func (requestHandler *RequestHandler) serveResourceRequest(r *http.Request, deviceID, resourceHref, twin, resourceInterface string) (*httptest.ResponseRecorder, bool, error) { resourceID := pb.ResourceIdFilter{ ResourceId: commands.NewResourceID(deviceID, resourceHref), Etag: getETags(r), } - if (twin == "" || strings.ToLower(twin) == "true") && resourceInterface == "" { - requestHandler.getResourceFromTwin(w, r, &resourceID) - return + if (twin == "" || parseBoolQuery(twin)) && resourceInterface == "" { + rec, err := requestHandler.getResourceFromTwin(r, &resourceID) + if err != nil { + return nil, false, kitNetGrpc.ForwardErrorf(codes.InvalidArgument, errFmtFromTwin, &resourceID, err) + } + return rec, true, nil } query := r.URL.Query() @@ -97,7 +103,91 @@ func (requestHandler *RequestHandler) getResource(w http.ResponseWriter, r *http rec := httptest.NewRecorder() requestHandler.mux.ServeHTTP(rec, r) - toSimpleResponse(w, rec, func(w http.ResponseWriter, err error) { - serverMux.WriteError(w, kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "cannot get resource('%v') from the device: %v", resourceID.ToString(), err)) - }) + return rec, false, nil +} + +func jsonGetValueOnPath(v interface{}, path ...string) (interface{}, error) { + for idx, p := range path { + if v == nil { + return nil, fmt.Errorf("doesn't contains %v", strings.Join(path[:idx+1], ".")) + } + m, ok := v.(map[interface{}]interface{}) + if !ok { + return nil, fmt.Errorf("%v is not a map but %T", strings.Join(path[:idx+1], "."), v) + } + v, ok = m[p] + if !ok { + return nil, fmt.Errorf("doesn't contains %v", strings.Join(path[:idx+1], ".")) + } + } + return v, nil +} + +func isContentEmpty(data []byte) bool { + if len(data) == 0 { + return true + } + var ct commands.Content + err := protojson.Unmarshal(data, &ct) + if err != nil { + return false + } + return len(ct.GetData()) == 0 && ct.GetCoapContentFormat() == -1 +} + +func (requestHandler *RequestHandler) filterOnlyContent(rec *httptest.ResponseRecorder, contentPath ...string) (resetContent bool) { + if rec.Code == http.StatusNotModified { + rec.Body.Reset() + return false + } + if rec.Code != http.StatusOK { + return false + } + var v map[interface{}]interface{} + err := json.Decode(rec.Body.Bytes(), &v) + if err != nil { + requestHandler.logger.Debugf("filter only content: cannot decode response : %v", err) + return false + } + content, err := jsonGetValueOnPath(v, contentPath...) + if err != nil { + requestHandler.logger.With("body", v).Debugf("filter only content: %v", err) + return false + } + body, err := json.Encode(content) + if err != nil { + requestHandler.logger.With("body", v).Debugf("filter only content: cannot encode 'content' object: %v", err) + return false + } + rec.Body.Reset() + if !isContentEmpty(body) { + _, _ = rec.Body.Write(body) + return false + } + return true +} + +func (requestHandler *RequestHandler) getResource(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + deviceID := vars[uri.DeviceIDKey] + resourceHref := vars[uri.ResourceHrefKey] + twin := r.URL.Query().Get(uri.TwinQueryKey) + onlyContent := r.URL.Query().Get(uri.OnlyContentQueryKey) + resourceInterface := r.URL.Query().Get(uri.ResourceInterfaceQueryKey) + rec, fromTwin, err := requestHandler.serveResourceRequest(r, deviceID, resourceHref, twin, resourceInterface) + if err != nil { + serverMux.WriteError(w, err) + return + } + allowEmptyContent := false + if parseBoolQuery(onlyContent) { + filterPath := []string{"result", "data", "content"} + if !fromTwin { + filterPath = []string{"data", "content"} + } + allowEmptyContent = requestHandler.filterOnlyContent(rec, filterPath...) + } + toSimpleResponse(w, rec, allowEmptyContent, func(w http.ResponseWriter, err error) { + serverMux.WriteError(w, kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "cannot get resource('%v/%v') from the device: %v", deviceID, resourceHref, err)) + }, streamResponseKey) } diff --git a/http-gateway/service/getResourceLinks_test.go b/http-gateway/service/getResourceLinks_test.go index 49d301598..053984318 100644 --- a/http-gateway/service/getResourceLinks_test.go +++ b/http-gateway/service/getResourceLinks_test.go @@ -40,7 +40,7 @@ func TestRequestHandlerGetResourceLinks(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) defer func() { diff --git a/http-gateway/service/getResourcePendingCommands_test.go b/http-gateway/service/getResourcePendingCommands_test.go index b06eb5d70..885a82939 100644 --- a/http-gateway/service/getResourcePendingCommands_test.go +++ b/http-gateway/service/getResourcePendingCommands_test.go @@ -59,7 +59,7 @@ func TestRequestHandlerGetResourcePendingCommands(t *testing.T) { want: []*pb.PendingCommand{ { Command: &pb.PendingCommand_ResourceCreatePending{ - ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, "", + ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, test.TestResourceDeviceResourceTypes, "", map[string]interface{}{ "power": 1, }), @@ -72,7 +72,8 @@ func TestRequestHandlerGetResourcePendingCommands(t *testing.T) { DeviceId: deviceID, Href: device.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceDeviceResourceTypes, }, }, }, @@ -89,7 +90,7 @@ func TestRequestHandlerGetResourcePendingCommands(t *testing.T) { want: []*pb.PendingCommand{ { Command: &pb.PendingCommand_ResourceCreatePending{ - ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, "", + ResourceCreatePending: pbTest.MakeResourceCreatePending(t, deviceID, device.ResourceURI, test.TestResourceDeviceResourceTypes, "", map[string]interface{}{ "power": 1, }), @@ -113,7 +114,8 @@ func TestRequestHandlerGetResourcePendingCommands(t *testing.T) { DeviceId: deviceID, Href: device.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceDeviceResourceTypes, }, }, }, @@ -146,7 +148,7 @@ func TestRequestHandlerGetResourcePendingCommands(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -161,9 +163,9 @@ func TestRequestHandlerGetResourcePendingCommands(t *testing.T) { secureGWShutdown() createFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + createCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.CreateResource(ctx, &pb.CreateResourceRequest{ + _, errC := c.CreateResource(createCtx, &pb.CreateResourceRequest{ ResourceId: commands.NewResourceID(deviceID, device.ResourceURI), Content: &pb.Content{ ContentType: message.AppOcfCbor.String(), @@ -172,22 +174,22 @@ func TestRequestHandlerGetResourcePendingCommands(t *testing.T) { }), }, }) - require.Error(t, err) + require.Error(t, errC) } createFn() retrieveFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + retrieveCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.GetResourceFromDevice(ctx, &pb.GetResourceFromDeviceRequest{ + _, errG := c.GetResourceFromDevice(retrieveCtx, &pb.GetResourceFromDeviceRequest{ ResourceId: commands.NewResourceID(deviceID, platform.ResourceURI), }) - require.Error(t, err) + require.Error(t, errG) } retrieveFn() updateFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + updateCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.UpdateResource(ctx, &pb.UpdateResourceRequest{ + _, errU := c.UpdateResource(updateCtx, &pb.UpdateResourceRequest{ ResourceId: commands.NewResourceID(deviceID, test.TestResourceLightInstanceHref("1")), Content: &pb.Content{ ContentType: message.AppOcfCbor.String(), @@ -196,26 +198,26 @@ func TestRequestHandlerGetResourcePendingCommands(t *testing.T) { }), }, }) - require.Error(t, err) + require.Error(t, errU) } updateFn() deleteFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + deleteCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.DeleteResource(ctx, &pb.DeleteResourceRequest{ + _, errD := c.DeleteResource(deleteCtx, &pb.DeleteResourceRequest{ ResourceId: commands.NewResourceID(deviceID, device.ResourceURI), }) - require.Error(t, err) + require.Error(t, errD) } deleteFn() updateDeviceMetadataFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + deleteCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.UpdateDeviceMetadata(ctx, &pb.UpdateDeviceMetadataRequest{ + _, errU := c.UpdateDeviceMetadata(deleteCtx, &pb.UpdateDeviceMetadataRequest{ DeviceId: deviceID, TwinEnabled: false, }) - require.Error(t, err) + require.Error(t, errU) } updateDeviceMetadataFn() diff --git a/http-gateway/service/getResource_test.go b/http-gateway/service/getResource_test.go index 7512db5f1..cc2fa9006 100644 --- a/http-gateway/service/getResource_test.go +++ b/http-gateway/service/getResource_test.go @@ -9,6 +9,7 @@ import ( "net/http" "testing" + "github.com/plgd-dev/device/v2/pkg/codec/json" "github.com/plgd-dev/device/v2/schema/interfaces" "github.com/plgd-dev/device/v2/test/resource/types" "github.com/plgd-dev/hub/v2/grpc-gateway/pb" @@ -47,7 +48,7 @@ func TestRequestHandlerGetResource(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -91,9 +92,10 @@ func TestRequestHandlerGetResource(t *testing.T) { DeviceId: deviceID, Href: test.TestResourceLightInstanceHref("1"), }, - Status: commands.Status_OK, - Content: &commands.Content{}, // content is encoded as json - AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + Status: commands.Status_OK, + Content: &commands.Content{}, // content is encoded as json + AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + ResourceTypes: test.TestResourceLightInstanceResourceTypes, }, wantCode: http.StatusOK, }, @@ -104,7 +106,7 @@ func TestRequestHandlerGetResource(t *testing.T) { deviceID: deviceID, resourceHref: test.TestResourceLightInstanceHref("1"), }, - want: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceLightInstanceHref("1"), "", + want: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", map[string]interface{}{ "state": false, "power": uint64(0), @@ -152,7 +154,7 @@ func TestRequestHandlerGetResource(t *testing.T) { resourceHref: test.TestResourceLightInstanceHref("1"), resourceInterface: interfaces.OC_IF_BASELINE, }, - want: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceLightInstanceHref("1"), "", + want: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", map[string]interface{}{ "state": false, "power": uint64(0), @@ -171,7 +173,7 @@ func TestRequestHandlerGetResource(t *testing.T) { resourceHref: test.TestResourceLightInstanceHref("1"), twin: newBool(false), }, - want: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceLightInstanceHref("1"), "", + want: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", map[string]interface{}{ "state": false, "power": uint64(0), @@ -206,7 +208,7 @@ func TestRequestHandlerGetResource(t *testing.T) { values = append(values, value.GetData()) } if tt.wantCode != http.StatusOK { - require.Len(t, values, 0) + require.Empty(t, values) return } require.Len(t, values, 1) @@ -214,3 +216,94 @@ func TestRequestHandlerGetResource(t *testing.T) { }) } } + +func TestRequestHandlerGetResourceWithOnlyContent(t *testing.T) { + deviceID := test.MustFindDeviceByName(test.TestDeviceName) + + ctx, cancel := context.WithTimeout(context.Background(), config.TEST_TIMEOUT) + defer cancel() + + tearDown := service.SetUp(ctx, t) + defer tearDown() + + shutdownHttp := httpgwTest.SetUp(t) + defer shutdownHttp() + + token := oauthTest.GetDefaultAccessToken(t) + ctx = kitNetGrpc.CtxWithToken(ctx, token) + + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: test.GetRootCertificatePool(t), + }))) + require.NoError(t, err) + defer func() { + _ = conn.Close() + }() + c := pb.NewGrpcGatewayClient(conn) + + _, shutdownDevSim := test.OnboardDevSim(ctx, t, c, deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) + defer shutdownDevSim() + + type args struct { + deviceID string + resourceHref string + twin *bool + } + tests := []struct { + name string + args args + want interface{} + wantCode int + }{ + { + name: "json: get resource from twin", + args: args{ + deviceID: deviceID, + resourceHref: test.TestResourceLightInstanceHref("1"), + }, + want: map[interface{}]interface{}{"name": "Light", "power": uint64(0x0), "state": false}, + wantCode: http.StatusOK, + }, + { + name: "json: get resource from device", + args: args{ + deviceID: deviceID, + resourceHref: test.TestResourceLightInstanceHref("1"), + twin: newBool(false), + }, + want: map[interface{}]interface{}{"name": "Light", "power": uint64(0x0), "state": false}, + wantCode: http.StatusOK, + }, + { + name: "json: not exists", + args: args{ + deviceID: deviceID, + resourceHref: "/not-exists", + }, + wantCode: http.StatusNotFound, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rb := httpgwTest.NewRequest(http.MethodGet, uri.AliasDeviceResource, nil).AuthToken(token) + rb.DeviceId(tt.args.deviceID).ResourceHref(tt.args.resourceHref) + rb.OnlyContent(true) + if tt.args.twin != nil { + rb.Twin(*tt.args.twin) + } + resp := httpgwTest.HTTPDo(t, rb.Build()) + defer func() { + _ = resp.Body.Close() + }() + require.Equal(t, tt.wantCode, resp.StatusCode) + if tt.wantCode != http.StatusOK { + return + } + var got interface{} + err := json.ReadFrom(resp.Body, &got) + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +} diff --git a/http-gateway/service/getResources_test.go b/http-gateway/service/getResources_test.go index 0d1544a0b..e2206e5c3 100644 --- a/http-gateway/service/getResources_test.go +++ b/http-gateway/service/getResources_test.go @@ -47,7 +47,7 @@ func TestRequestHandlerGetResources(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -128,7 +128,7 @@ func TestRequestHandlerGetResources(t *testing.T) { want: []*pb.Resource{ { Types: []string{types.CORE_LIGHT}, - Data: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceLightInstanceHref("1"), "", + Data: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", map[string]interface{}{ "state": false, "power": uint64(0), @@ -147,7 +147,7 @@ func TestRequestHandlerGetResources(t *testing.T) { want: []*pb.Resource{ { Types: []string{types.BINARY_SWITCH}, - Data: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceSwitchesInstanceHref(switchID), "", + Data: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceSwitchesInstanceHref(switchID), test.TestResourceSwitchesInstanceResourceTypes, "", map[string]interface{}{ "value": false, }, @@ -178,7 +178,8 @@ func TestRequestHandlerGetResources(t *testing.T) { Content: &commands.Content{ CoapContentFormat: -1, }, - AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + ResourceTypes: test.TestResourceLightInstanceResourceTypes, }, }, }, @@ -206,7 +207,8 @@ func TestRequestHandlerGetResources(t *testing.T) { Content: &commands.Content{ CoapContentFormat: -1, }, - AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + ResourceTypes: test.TestResourceLightInstanceResourceTypes, }, }, }, diff --git a/http-gateway/service/getThings.go b/http-gateway/service/getThings.go new file mode 100644 index 000000000..56b09aee6 --- /dev/null +++ b/http-gateway/service/getThings.go @@ -0,0 +1,392 @@ +package service + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + + "github.com/google/uuid" + "github.com/gorilla/mux" + jsoniter "github.com/json-iterator/go" + bridgeDeviceTD "github.com/plgd-dev/device/v2/bridge/device/thingDescription" + "github.com/plgd-dev/device/v2/bridge/resources" + bridgeResourcesTD "github.com/plgd-dev/device/v2/bridge/resources/thingDescription" + schemaCloud "github.com/plgd-dev/device/v2/schema/cloud" + schemaCredential "github.com/plgd-dev/device/v2/schema/credential" + schemaDevice "github.com/plgd-dev/device/v2/schema/device" + schemaMaintenance "github.com/plgd-dev/device/v2/schema/maintenance" + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/hub/v2/grpc-gateway/pb" + "github.com/plgd-dev/hub/v2/http-gateway/serverMux" + "github.com/plgd-dev/hub/v2/http-gateway/uri" + "github.com/plgd-dev/hub/v2/pkg/log" + kitNetGrpc "github.com/plgd-dev/hub/v2/pkg/net/grpc" + "github.com/plgd-dev/hub/v2/pkg/security/openid" + "github.com/plgd-dev/hub/v2/resource-aggregate/events" + wotTD "github.com/web-of-things-open-source/thingdescription-go/thingDescription" + "google.golang.org/grpc/codes" +) + +type ThingLink struct { + Href string `json:"href"` + Rel string `json:"rel"` +} + +type GetThingsResponse struct { + Base string `json:"base"` + Security *wotTD.TypeDeclaration `json:"security"` + ID string `json:"id"` + SecurityDefinitions map[string]wotTD.SecurityScheme `json:"securityDefinitions"` + Links []ThingLink `json:"links"` +} + +const ( + ThingLinkRelationItem = "item" + ThingLinkRelationCollection = "collection" +) + +func (requestHandler *RequestHandler) getResourceLinks(ctx context.Context, deviceFilter []string, typeFilter []string) ([]*events.ResourceLinksPublished, error) { + client, err := requestHandler.client.GrpcGatewayClient().GetResourceLinks(ctx, &pb.GetResourceLinksRequest{ + DeviceIdFilter: deviceFilter, + TypeFilter: typeFilter, + }) + if err != nil { + return nil, fmt.Errorf("cannot get resource links: %w", err) + } + links := make([]*events.ResourceLinksPublished, 0, 16) + for { + link, err := client.Recv() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return nil, fmt.Errorf("cannot receive resource link: %w", err) + } + links = append(links, link) + } + return links, nil +} + +func (requestHandler *RequestHandler) getThings(w http.ResponseWriter, r *http.Request) { + resLinks, err := requestHandler.getResourceLinks(r.Context(), nil, []string{bridgeResourcesTD.ResourceType}) + if err != nil { + serverMux.WriteError(w, err) + return + } + hubCfg, err := requestHandler.client.GrpcGatewayClient().GetHubConfiguration(r.Context(), &pb.HubConfigurationRequest{}) + if err != nil { + serverMux.WriteError(w, err) + return + } + + links := make([]ThingLink, 0, len(resLinks)) + for _, l := range resLinks { + links = append(links, ThingLink{ + Href: "/" + l.GetDeviceId(), + Rel: ThingLinkRelationItem, + }) + } + var td wotTD.ThingDescription + ThingSetSecurity(&td, requestHandler.openIDConfig) + + things := GetThingsResponse{ + Base: requestHandler.config.UI.WebConfiguration.HTTPGatewayAddress + uri.Things, + Links: links, + Security: td.Security, + SecurityDefinitions: td.SecurityDefinitions, + ID: "urn:uuid:" + hubCfg.GetId(), + } + if err := jsonResponseWriter(w, things); err != nil { + log.Errorf("failed to write response: %v", err) + } +} + +func CreateHTTPForms(hrefUri *url.URL, opsBits resources.SupportedOperation, contentType message.MediaType) []wotTD.FormElementProperty { + supportedByOps := map[resources.SupportedOperation]wotTD.StickyDescription{ + resources.SupportedOperationRead: wotTD.Readproperty, + resources.SupportedOperationWrite: wotTD.Writeproperty, + } + + ops := make([]string, 0, len(supportedByOps)) + for opBit, op := range supportedByOps { + if opsBits.HasOperation(opBit) { + ops = append(ops, string(op)) + } + } + if len(ops) == 0 { + return nil + } + q := hrefUri.Query() + if len(q) > 0 && q.Has("di") { + q.Del("di") + } + q.Add(uri.OnlyContentQueryKey, "1") + hrefUri.RawQuery = q.Encode() + return []wotTD.FormElementProperty{ + { + ContentType: bridgeDeviceTD.StringToPtr(contentType.String()), + Href: *hrefUri, + Op: &wotTD.FormElementPropertyOp{ + StringArray: ops, + }, + }, + } +} + +func patchProperty(pe wotTD.PropertyElement, deviceID, href string, contentType message.MediaType) (wotTD.PropertyElement, error) { + deviceUUID, err := uuid.Parse(deviceID) + if err != nil { + return wotTD.PropertyElement{}, fmt.Errorf("cannot parse deviceID: %w", err) + } + propertyBaseURL := "/" + uri.DevicesPathKey + "/" + deviceID + "/" + uri.ResourcesPathKey + patchFnMap := map[string]func(wotTD.PropertyElement) (wotTD.PropertyElement, error){ + schemaDevice.ResourceURI: func(pe wotTD.PropertyElement) (wotTD.PropertyElement, error) { + return bridgeResourcesTD.PatchDeviceResourcePropertyElement(pe, deviceUUID, propertyBaseURL, contentType, "", CreateHTTPForms) + }, + schemaMaintenance.ResourceURI: func(pe wotTD.PropertyElement) (wotTD.PropertyElement, error) { + return bridgeResourcesTD.PatchMaintenanceResourcePropertyElement(pe, deviceUUID, propertyBaseURL, contentType, CreateHTTPForms) + }, + schemaCloud.ResourceURI: func(pe wotTD.PropertyElement) (wotTD.PropertyElement, error) { + return bridgeResourcesTD.PatchCloudResourcePropertyElement(pe, deviceUUID, propertyBaseURL, contentType, CreateHTTPForms) + }, + schemaCredential.ResourceURI: func(pe wotTD.PropertyElement) (wotTD.PropertyElement, error) { + return bridgeResourcesTD.PatchCredentialResourcePropertyElement(pe, deviceUUID, propertyBaseURL, contentType, CreateHTTPForms) + }, + } + patchFn, ok := patchFnMap[href] + if ok { + pe, err = patchFn(pe) + if err != nil { + return wotTD.PropertyElement{}, err + } + return pe, nil + } + + propOps := bridgeDeviceTD.GetPropertyElementOperations(pe) + pe, err = bridgeDeviceTD.PatchPropertyElement(pe, nil, deviceUUID, propertyBaseURL+href, + propOps.ToSupportedOperations(), contentType, CreateHTTPForms) + if err != nil { + return wotTD.PropertyElement{}, err + } + return pe, nil +} + +var validRefs = map[string]struct{}{ + ThingLinkRelationItem: {}, + ThingLinkRelationCollection: {}, +} + +func isDeviceLink(le wotTD.IconLinkElement) (string, bool) { + if le.Href == "" { + return "", false + } + if le.Href[0] != '/' { + return "", false + } + if le.Rel == nil { + return "", false + } + + if _, ok := validRefs[*le.Rel]; !ok { + return "", false + } + linkedDeviceID := le.Href + if linkedDeviceID[0] == '/' { + linkedDeviceID = linkedDeviceID[1:] + } + uuidDeviceID, err := uuid.Parse(linkedDeviceID) + if err != nil { + return "", false + } + if uuidDeviceID == uuid.Nil { + return "", false + } + return linkedDeviceID, true +} + +func getLinkedDevices(links []wotTD.IconLinkElement) []string { + devices := make([]string, 0, len(links)) + for _, l := range links { + if deviceID, ok := isDeviceLink(l); ok { + devices = append(devices, deviceID) + } + } + return devices +} + +func ThingPatchLink(le wotTD.IconLinkElement, validateDevice map[string]struct{}) (wotTD.IconLinkElement, bool) { + if le.Href == "" { + return wotTD.IconLinkElement{}, false + } + device, ok := isDeviceLink(le) + if !ok { + return le, true + } + if len(validateDevice) == 0 { + return wotTD.IconLinkElement{}, false + } + if _, ok := validateDevice[device]; !ok { + return wotTD.IconLinkElement{}, false + } + le.Href = "/" + uri.ThingsPathKey + le.Href + return le, true +} + +func makeDevicePropertiesValidator(deviceID string, links []*events.ResourceLinksPublished) (map[string]struct{}, bool) { + for _, l := range links { + if l.GetDeviceId() == deviceID { + validateProperties := map[string]struct{}{} + for _, r := range l.GetResources() { + validateProperties[r.GetHref()] = struct{}{} + } + return validateProperties, true + } + } + return nil, false +} + +func makeDeviceLinkValidator(links []*events.ResourceLinksPublished) map[string]struct{} { + validator := make(map[string]struct{}) + for _, l := range links { + validator[l.GetDeviceId()] = struct{}{} + } + return validator +} + +func (requestHandler *RequestHandler) thingSetBase(td *wotTD.ThingDescription) error { + baseURL := requestHandler.config.UI.WebConfiguration.HTTPGatewayAddress + uri.API + base, err := url.Parse(baseURL) + if err != nil { + return fmt.Errorf("cannot parse base url: %w", err) + } + td.Base = *base + return nil +} + +func (requestHandler *RequestHandler) thingSetProperties(ctx context.Context, deviceID string, td *wotTD.ThingDescription) error { + deviceLinks, err := requestHandler.getResourceLinks(ctx, []string{deviceID}, nil) + if err != nil { + return fmt.Errorf("cannot get resource links: %w", err) + } + validateProperties, ok := makeDevicePropertiesValidator(deviceID, deviceLinks) + if !ok { + return fmt.Errorf("cannot get resource links for device %v", deviceID) + } + for href, prop := range td.Properties { + _, ok := validateProperties[href] + if !ok { + _, ok = validateProperties["/"+href] + } + if !ok { + delete(td.Properties, href) + continue + } + patchedProp, err := patchProperty(prop, deviceID, href, message.AppJSON) + if err != nil { + return fmt.Errorf("cannot patch device resource property element: %w", err) + } + td.Properties[href] = patchedProp + } + return nil +} + +func (requestHandler *RequestHandler) thingSetLinks(ctx context.Context, td *wotTD.ThingDescription) { + linkedDevices := getLinkedDevices(td.Links) + var validLinkedDevices map[string]struct{} + if len(linkedDevices) > 0 { + links, err := requestHandler.getResourceLinks(ctx, linkedDevices, []string{bridgeResourcesTD.ResourceType}) + if err == nil { + validLinkedDevices = makeDeviceLinkValidator(links) + } + } + patchedLinks := make([]wotTD.IconLinkElement, 0, len(td.Links)) + for _, link := range td.Links { + patchedLink, ok := ThingPatchLink(link, validLinkedDevices) + if !ok { + continue + } + patchedLinks = append(patchedLinks, patchedLink) + } + if len(patchedLinks) == 0 { + td.Links = nil + } else { + td.Links = patchedLinks + } +} + +func ThingSetSecurity(td *wotTD.ThingDescription, openIDConfig openid.Config) { + td.Security = &wotTD.TypeDeclaration{ + String: bridgeDeviceTD.StringToPtr("oauth2_sc"), + } + td.SecurityDefinitions = map[string]wotTD.SecurityScheme{ + "oauth2_sc": { + Scheme: "oauth2", + Flow: bridgeDeviceTD.StringToPtr("code"), + Authorization: &openIDConfig.AuthURL, + Token: &openIDConfig.TokenURL, + }, + } +} + +func (requestHandler *RequestHandler) thingDescriptionResponse(ctx context.Context, w http.ResponseWriter, rec *httptest.ResponseRecorder, writeError func(w http.ResponseWriter, err error), deviceID string) { + content := jsoniter.Get(rec.Body.Bytes(), streamResponseKey, "data", "content") + if content.ValueType() != jsoniter.ObjectValue { + writeError(w, errors.New("cannot decode thingDescription content")) + return + } + var td wotTD.ThingDescription + err := td.UnmarshalJSON([]byte(content.ToString())) + if err != nil { + writeError(w, fmt.Errorf("cannot decode thingDescription content: %w", err)) + return + } + + // .security + ThingSetSecurity(&td, requestHandler.openIDConfig) + + // .base + if err = requestHandler.thingSetBase(&td); err != nil { + writeError(w, fmt.Errorf("cannot set base url: %w", err)) + return + } + + // .properties.forms + if err = requestHandler.thingSetProperties(ctx, deviceID, &td); err != nil { + writeError(w, fmt.Errorf("cannot set properties: %w", err)) + } + + // .links + requestHandler.thingSetLinks(ctx, &td) + + // marshal thingDescription + data, err := td.MarshalJSON() + if err != nil { + writeError(w, fmt.Errorf("cannot encode thingDescription: %w", err)) + return + } + // copy everything from response recorder + // to actual response writer + for k, v := range rec.Header() { + w.Header()[k] = v + } + w.WriteHeader(rec.Code) + _, err = w.Write(data) + if err != nil { + writeError(w, kitNetGrpc.ForwardErrorf(codes.Internal, "cannot encode response: %v", err)) + } +} + +func (requestHandler *RequestHandler) getThing(w http.ResponseWriter, r *http.Request) { + vars := mux.Vars(r) + deviceID := vars[uri.DeviceIDKey] + rec, _, err := requestHandler.serveResourceRequest(r, deviceID, bridgeResourcesTD.ResourceURI, "", "") + if err != nil { + serverMux.WriteError(w, err) + return + } + requestHandler.thingDescriptionResponse(r.Context(), w, rec, serverMux.WriteError, deviceID) +} diff --git a/http-gateway/service/getThings_test.go b/http-gateway/service/getThings_test.go new file mode 100644 index 000000000..176c9142d --- /dev/null +++ b/http-gateway/service/getThings_test.go @@ -0,0 +1,452 @@ +package service_test + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strconv" + "testing" + + "github.com/google/uuid" + bridgeTD "github.com/plgd-dev/device/v2/bridge/device/thingDescription" + bridgeResourcesTD "github.com/plgd-dev/device/v2/bridge/resources/thingDescription" + "github.com/plgd-dev/device/v2/client/core" + bridgeDevice "github.com/plgd-dev/device/v2/cmd/bridge-device/device" + "github.com/plgd-dev/device/v2/pkg/codec/json" + deviceCoap "github.com/plgd-dev/device/v2/pkg/net/coap" + schemaDevice "github.com/plgd-dev/device/v2/schema/device" + schemaMaintenance "github.com/plgd-dev/device/v2/schema/maintenance" + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/hub/v2/grpc-gateway/pb" + httpgwService "github.com/plgd-dev/hub/v2/http-gateway/service" + httpgwTest "github.com/plgd-dev/hub/v2/http-gateway/test" + httpgwUri "github.com/plgd-dev/hub/v2/http-gateway/uri" + isPb "github.com/plgd-dev/hub/v2/identity-store/pb" + isTest "github.com/plgd-dev/hub/v2/identity-store/test" + kitNetGrpc "github.com/plgd-dev/hub/v2/pkg/net/grpc" + "github.com/plgd-dev/hub/v2/pkg/security/openid" + "github.com/plgd-dev/hub/v2/resource-aggregate/commands" + raPb "github.com/plgd-dev/hub/v2/resource-aggregate/service" + raTest "github.com/plgd-dev/hub/v2/resource-aggregate/test" + "github.com/plgd-dev/hub/v2/test" + "github.com/plgd-dev/hub/v2/test/config" + "github.com/plgd-dev/hub/v2/test/device/bridge" + oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test" + "github.com/plgd-dev/hub/v2/test/service" + vd "github.com/plgd-dev/hub/v2/test/virtual-device" + "github.com/stretchr/testify/require" + wotTD "github.com/web-of-things-open-source/thingdescription-go/thingDescription" + "golang.org/x/sync/semaphore" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +type virtualDevice struct { + name string + deviceID string + tdEnabled bool +} + +func createDevices(ctx context.Context, t *testing.T, numDevices int, protocol commands.Connection_Protocol) []virtualDevice { + ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) + + isConn, err := grpc.NewClient(config.IDENTITY_STORE_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: test.GetRootCertificatePool(t), + }))) + require.NoError(t, err) + defer func() { + _ = isConn.Close() + }() + isClient := isPb.NewIdentityStoreClient(isConn) + + raConn, err := grpc.NewClient(config.RESOURCE_AGGREGATE_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: test.GetRootCertificatePool(t), + }))) + require.NoError(t, err) + defer func() { + _ = raConn.Close() + }() + raClient := raPb.NewResourceAggregateClient(raConn) + + devices := make([]virtualDevice, 0, numDevices) + for i := 0; i < numDevices; i++ { + tdEnabled := (i%2 == 0) + devices = append(devices, virtualDevice{ + name: fmt.Sprintf("dev-%v", i), + deviceID: uuid.NewString(), + tdEnabled: tdEnabled, + }) + } + + numGoRoutines := int64(8) + sem := semaphore.NewWeighted(numGoRoutines) + for i := range devices { + err = sem.Acquire(ctx, 1) + require.NoError(t, err) + go func(dev virtualDevice) { + vd.CreateDevice(ctx, t, dev.name, dev.deviceID, 0, dev.tdEnabled, protocol, isClient, raClient) + sem.Release(1) + }(devices[i]) + } + err = sem.Acquire(ctx, numGoRoutines) + require.NoError(t, err) + return devices +} + +func TestRequestHandlerGetThings(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), config.TEST_TIMEOUT) + defer cancel() + + const services = service.SetUpServicesOAuth | service.SetUpServicesId | service.SetUpServicesResourceDirectory | + service.SetUpServicesGrpcGateway | service.SetUpServicesResourceAggregate + isConfig := isTest.MakeConfig(t) + isConfig.APIs.GRPC.TLS.ClientCertificateRequired = false + raConfig := raTest.MakeConfig(t) + raConfig.APIs.GRPC.TLS.ClientCertificateRequired = false + tearDown := service.SetUpServices(ctx, t, services, service.WithISConfig(isConfig), service.WithRAConfig(raConfig)) + defer tearDown() + + httpgwCfg := httpgwTest.MakeConfig(t, true) + shutdownHttp := httpgwTest.New(t, httpgwCfg) + defer shutdownHttp() + + token := oauthTest.GetDefaultAccessToken(t) + ctx = kitNetGrpc.CtxWithToken(ctx, token) + + numDevices := 10 + vds := createDevices(ctx, t, numDevices, test.StringToApplicationProtocol(config.ACTIVE_COAP_SCHEME)) + + rb := httpgwTest.NewRequest(http.MethodGet, httpgwUri.Things, nil).AuthToken(token) + resp := httpgwTest.HTTPDo(t, rb.Build()) + defer func() { + _ = resp.Body.Close() + }() + + var v httpgwService.GetThingsResponse + err := httpgwTest.UnmarshalJson(resp.StatusCode, resp.Body, &v) + require.NoError(t, err) + require.Equal(t, httpgwCfg.UI.WebConfiguration.HTTPGatewayAddress+httpgwUri.Things, v.Base) + vdsWithTD := []virtualDevice{} + for _, vd := range vds { + if vd.tdEnabled { + vdsWithTD = append(vdsWithTD, vd) + } + } + require.Len(t, v.Links, len(vdsWithTD)) + for _, dev := range vdsWithTD { + require.Contains(t, v.Links, httpgwService.ThingLink{ + Href: "/" + dev.deviceID, + Rel: httpgwService.ThingLinkRelationItem, + }) + } +} + +func TestBridgeDeviceGetThings(t *testing.T) { + bridgeDeviceCfg, err := test.GetBridgeDeviceConfig() + require.NoError(t, err) + ctx, cancel := context.WithTimeout(context.Background(), config.TEST_TIMEOUT) + defer cancel() + tearDown := service.SetUp(ctx, t) + defer tearDown() + token := oauthTest.GetDefaultAccessToken(t) + ctx = kitNetGrpc.CtxWithToken(ctx, token) + + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: test.GetRootCertificatePool(t), + }))) + require.NoError(t, err) + defer func() { + _ = conn.Close() + }() + c := pb.NewGrpcGatewayClient(conn) + + httpgwCfg := httpgwTest.MakeConfig(t, true) + shutdownHttp := httpgwTest.New(t, httpgwCfg) + defer shutdownHttp() + + var devIDs []string + for i := 0; i < bridgeDeviceCfg.NumGeneratedBridgedDevices; i++ { + bdName := test.TestBridgeDeviceInstanceName(strconv.Itoa(i)) + bdID := test.MustFindDeviceByName(bdName, func(d *core.Device) deviceCoap.OptionFunc { + return deviceCoap.WithQuery("di=" + d.DeviceID()) + }) + devIDs = append(devIDs, bdID) + bd := bridge.NewDevice(bdID, bdName, bridgeDeviceCfg.NumResourcesPerDevice, true) + shutdownBd := test.OnboardDevice(ctx, t, c, bd, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, bd.GetDefaultResources()) + defer shutdownBd() + } + + rb := httpgwTest.NewRequest(http.MethodGet, httpgwUri.Things, nil).AuthToken(token) + resp := httpgwTest.HTTPDo(t, rb.Build()) + defer func() { + _ = resp.Body.Close() + }() + + var v httpgwService.GetThingsResponse + err = httpgwTest.UnmarshalJson(resp.StatusCode, resp.Body, &v) + require.NoError(t, err) + require.Equal(t, httpgwCfg.UI.WebConfiguration.HTTPGatewayAddress+httpgwUri.Things, v.Base) + require.Len(t, v.Links, bridgeDeviceCfg.NumGeneratedBridgedDevices) + for _, devID := range devIDs { + require.Contains(t, v.Links, httpgwService.ThingLink{ + Href: "/" + devID, + Rel: httpgwService.ThingLinkRelationItem, + }) + } +} + +func getPatchedTD(t *testing.T, deviceCfg bridgeDevice.Config, deviceID string, links []wotTD.IconLinkElement, validateDevices map[string]struct{}, title, host string) *wotTD.ThingDescription { + td, err := bridgeDevice.GetThingDescription(deviceCfg.ThingDescription.File, deviceCfg.NumResourcesPerDevice) + require.NoError(t, err) + + baseURL := host + httpgwUri.API + base, err := url.Parse(baseURL) + require.NoError(t, err) + td.Base = *base + td.Title = title + id, err := bridgeTD.GetThingDescriptionID(deviceID) + require.NoError(t, err) + td.ID = id + + deviceUUID, err := uuid.Parse(deviceID) + require.NoError(t, err) + propertyBaseURL := "/" + httpgwUri.DevicesPathKey + "/" + deviceID + "/" + httpgwUri.ResourcesPathKey + dev, ok := bridgeResourcesTD.GetOCFResourcePropertyElement(schemaDevice.ResourceURI) + require.True(t, ok) + dev, err = bridgeResourcesTD.PatchDeviceResourcePropertyElement(dev, deviceUUID, propertyBaseURL, message.AppJSON, bridgeDevice.DeviceResourceType, httpgwService.CreateHTTPForms) + require.NoError(t, err) + schemaMap := bridgeDevice.GetDataSchemaForAdditionalProperties() + for name, schema := range schemaMap { + dev.Properties.DataSchemaMap[name] = schema + } + td.Properties[schemaDevice.ResourceURI] = dev + + httpgwService.ThingSetSecurity(&td, openid.Config{ + TokenURL: "https://localhost:20009/oauth/token", + AuthURL: "https://localhost:20009/authorize", + }) + + mnt, ok := bridgeResourcesTD.GetOCFResourcePropertyElement(schemaMaintenance.ResourceURI) + require.True(t, ok) + mnt, err = bridgeResourcesTD.PatchMaintenanceResourcePropertyElement(mnt, deviceUUID, propertyBaseURL, message.AppJSON, httpgwService.CreateHTTPForms) + require.NoError(t, err) + td.Properties[schemaMaintenance.ResourceURI] = mnt + + for i := 0; i < deviceCfg.NumResourcesPerDevice; i++ { + href := bridgeDevice.GetTestResourceHref(i) + prop := bridgeDevice.GetPropertyDescriptionForTestResource() + prop, err := bridgeDevice.PatchTestResourcePropertyElement(prop, deviceUUID, propertyBaseURL+href, message.AppJSON, httpgwService.CreateHTTPForms) + require.NoError(t, err) + td.Properties[href] = prop + } + + expectedLinks := make([]wotTD.IconLinkElement, 0, len(links)) + for _, link := range links { + patchedLink, ok := httpgwService.ThingPatchLink(link, validateDevices) + if !ok { + continue + } + expectedLinks = append(expectedLinks, patchedLink) + } + td.Links = expectedLinks + + return &td +} + +func bridgeDeviceInstanceName(idx int) string { + return test.TestBridgeDeviceInstanceName(strconv.Itoa(idx)) +} + +func onboardBridgeDevice(ctx context.Context, t *testing.T, idx int, c pb.GrpcGatewayClient, bridgeDeviceCfg bridgeDevice.Config) (string, func()) { + bdName := bridgeDeviceInstanceName(idx) + bdID := test.MustFindDeviceByName(bdName, func(d *core.Device) deviceCoap.OptionFunc { + return deviceCoap.WithQuery("di=" + d.DeviceID()) + }) + bd := bridge.NewDevice(bdID, bdName, bridgeDeviceCfg.NumResourcesPerDevice, true) + shutdownBd := test.OnboardDevice(ctx, t, c, bd, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, bd.GetDefaultResources()) + return bdID, shutdownBd +} + +func TestBridgeDeviceGetThing(t *testing.T) { + bridgeDeviceCfg, err := test.GetBridgeDeviceConfig() + require.NoError(t, err) + + ctx, cancel := context.WithTimeout(context.Background(), config.TEST_TIMEOUT) + defer cancel() + raCfg := raTest.MakeConfig(t) + raCfg.APIs.GRPC.TLS.ClientCertificateRequired = false + tearDown := service.SetUp(ctx, t, service.WithRAConfig(raCfg)) + defer tearDown() + token := oauthTest.GetDefaultAccessToken(t) + ctx = kitNetGrpc.CtxWithToken(ctx, token) + + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: test.GetRootCertificatePool(t), + }))) + require.NoError(t, err) + defer func() { + _ = conn.Close() + }() + c := pb.NewGrpcGatewayClient(conn) + + httpgwCfg := httpgwTest.MakeConfig(t, true) + shutdownHttp := httpgwTest.New(t, httpgwCfg) + defer shutdownHttp() + + deviceIDs := make([]string, 0, 2) + validLinkedDevices := make(map[string]struct{}, 2) + for i := 0; i < 2; i++ { + bdID, shutdownBd := onboardBridgeDevice(ctx, t, i, c, bridgeDeviceCfg) + defer shutdownBd() + deviceIDs = append(deviceIDs, bdID) + validLinkedDevices[bdID] = struct{}{} + } + + // update TD links in resource twin + wotRes, err := c.GetResources(ctx, &pb.GetResourcesRequest{ + ResourceIdFilter: []*pb.ResourceIdFilter{ + { + ResourceId: &commands.ResourceId{ + DeviceId: deviceIDs[0], + Href: bridgeResourcesTD.ResourceURI, + }, + }, + }, + }) + require.NoError(t, err) + resources := make([]*pb.Resource, 0, 1) + for { + res, errR := wotRes.Recv() + if errors.Is(errR, io.EOF) { + break + } + require.NoError(t, errR) + resources = append(resources, res) + } + require.Len(t, resources, 1) + + var updateLinksTD wotTD.ThingDescription + err = json.Decode(resources[0].GetData().GetContent().GetData(), &updateLinksTD) + require.NoError(t, err) + links := []wotTD.IconLinkElement{ + { + Rel: bridgeTD.StringToPtr("icon"), + Href: "https://example.com/icon.png", + }, + { + Rel: bridgeTD.StringToPtr(httpgwService.ThingLinkRelationItem), + Href: "/" + deviceIDs[1], + }, + { + Rel: bridgeTD.StringToPtr(httpgwService.ThingLinkRelationCollection), + Href: "/" + deviceIDs[1], + }, + { + Rel: bridgeTD.StringToPtr(httpgwService.ThingLinkRelationItem), + Href: "/" + uuid.NewString(), + }, + } + updateLinksTD.Links = links + data, err := json.Encode(updateLinksTD) + require.NoError(t, err) + + sub, err := c.SubscribeToEvents(ctx) + require.NoError(t, err) + + err = sub.Send(&pb.SubscribeToEvents{ + Action: &pb.SubscribeToEvents_CreateSubscription_{CreateSubscription: &pb.SubscribeToEvents_CreateSubscription{ + EventFilter: []pb.SubscribeToEvents_CreateSubscription_Event{pb.SubscribeToEvents_CreateSubscription_RESOURCE_CHANGED}, + ResourceIdFilter: []*pb.ResourceIdFilter{ + { + ResourceId: resources[0].GetData().GetResourceId(), + }, + }, + }}, + }) + require.NoError(t, err) + s, err := sub.Recv() + require.NoError(t, err) + require.Equal(t, pb.Event_OperationProcessed_ErrorStatus_OK, s.GetOperationProcessed().GetErrorStatus().GetCode()) + + // overwrite TD links in resource twin + raConn, err := grpc.NewClient(config.RESOURCE_AGGREGATE_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: test.GetRootCertificatePool(t), + }))) + require.NoError(t, err) + defer func() { + _ = raConn.Close() + }() + raC := raPb.NewResourceAggregateClient(raConn) + _, err = raC.NotifyResourceChanged(ctx, &commands.NotifyResourceChangedRequest{ + ResourceId: resources[0].GetData().GetResourceId(), + Status: commands.Status_OK, + Content: &commands.Content{ + Data: data, + CoapContentFormat: int32(message.AppJSON), + ContentType: message.AppJSON.String(), + }, + CommandMetadata: &commands.CommandMetadata{ + ConnectionId: "test", + Sequence: 1, + }, + }) + require.NoError(t, err) + + ev, err := sub.Recv() + require.NoError(t, err) + require.Equal(t, commands.Status_OK, ev.GetResourceChanged().GetStatus()) + + err = sub.CloseSend() + require.NoError(t, err) + + type args struct { + accept string + deviceID string + } + tests := []struct { + name string + args args + want *wotTD.ThingDescription + wantCode int + }{ + { + name: "json: get from resource twin", + args: args{ + deviceID: deviceIDs[0], + }, + want: getPatchedTD(t, bridgeDeviceCfg, deviceIDs[0], links, validLinkedDevices, bridgeDeviceInstanceName(0), httpgwCfg.UI.WebConfiguration.HTTPGatewayAddress), + wantCode: http.StatusOK, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rb := httpgwTest.NewRequest(http.MethodGet, httpgwUri.AliasDeviceThing, nil).AuthToken(token).Accept(tt.args.accept).DeviceId(tt.args.deviceID) + resp := httpgwTest.HTTPDo(t, rb.Build()) + defer func() { + _ = resp.Body.Close() + }() + require.Equal(t, tt.wantCode, resp.StatusCode) + values := make([]*wotTD.ThingDescription, 0, 1) + for { + var td wotTD.ThingDescription + err := json.ReadFrom(resp.Body, &td) + if errors.Is(err, io.EOF) { + break + } + require.NoError(t, err) + values = append(values, &td) + } + if tt.wantCode != http.StatusOK { + require.Empty(t, values) + return + } + require.Len(t, values, 1) + test.CmpThingDescription(t, tt.want, values[0]) + }) + } +} diff --git a/http-gateway/service/getWebConfiguration_test.go b/http-gateway/service/getWebConfiguration_test.go index c65d35e9f..6f3f5d1f0 100644 --- a/http-gateway/service/getWebConfiguration_test.go +++ b/http-gateway/service/getWebConfiguration_test.go @@ -2,7 +2,6 @@ package service_test import ( "context" - "encoding/json" "io" "net/http" "os" @@ -18,19 +17,6 @@ import ( "github.com/stretchr/testify/require" ) -func unmarshalWebConfiguration(code int, input io.Reader, v *httpgwService.WebConfiguration) error { - var data json.RawMessage - err := json.NewDecoder(input).Decode(&data) - if err != nil { - return err - } - if code != http.StatusOK { - return httpgwTest.UnmarshalError(data) - } - err = json.Unmarshal(data, v) - return err -} - func TestRegexpAPI(t *testing.T) { tests := []struct { val string @@ -104,7 +90,7 @@ func TestRequestHandlerGetWebConfiguration(t *testing.T) { assert.Equal(t, tt.wantHTTPCode, resp.StatusCode) var got httpgwService.WebConfiguration - err := unmarshalWebConfiguration(resp.StatusCode, resp.Body, &got) + err := httpgwTest.UnmarshalJson(resp.StatusCode, resp.Body, &got) if tt.wantErr { require.Error(t, err) return @@ -159,7 +145,7 @@ func TestRequestHandlerGetWebDirectory(t *testing.T) { want, err := os.ReadFile(tt.wantFile) require.NoError(t, err) - require.Equal(t, got, want) + require.Equal(t, want, got) }) } } diff --git a/http-gateway/service/requestHandler.go b/http-gateway/service/requestHandler.go index e74cc7364..66c15d7c5 100644 --- a/http-gateway/service/requestHandler.go +++ b/http-gateway/service/requestHandler.go @@ -16,13 +16,17 @@ import ( "github.com/plgd-dev/hub/v2/http-gateway/uri" "github.com/plgd-dev/hub/v2/pkg/log" kitHttp "github.com/plgd-dev/hub/v2/pkg/net/http" + "github.com/plgd-dev/hub/v2/pkg/security/openid" + pkgStrings "github.com/plgd-dev/hub/v2/pkg/strings" ) // RequestHandler for handling incoming request type RequestHandler struct { - client *client.Client - config *Config - mux *runtime.ServeMux + client *client.Client + config *Config + mux *runtime.ServeMux + openIDConfig openid.Config + logger log.Logger } func matchPrefixAndSplitURIPath(requestURI, prefix string) []string { @@ -38,22 +42,30 @@ func matchPrefixAndSplitURIPath(requestURI, prefix string) []string { return strings.Split(p, "/") } +func unescapeString(s string) string { + newS, err := pkgStrings.Unescape(s, pkgStrings.UnescapingModeAllCharacters, false) + if err != nil { + return s + } + return newS +} + func resourcePendingCommandsMatcher(r *http.Request, rm *mux.RouteMatch) bool { paths := matchPrefixAndSplitURIPath(r.RequestURI, uri.Devices) if len(paths) > 3 && paths[1] == uri.ResourcesPathKey && strings.Contains(paths[len(paths)-1], uri.PendingCommandsPathKey) { if rm.Vars == nil { rm.Vars = make(map[string]string) } - rm.Vars[uri.DeviceIDKey] = paths[0] - rm.Vars[uri.ResourceHrefKey] = strings.Split("/"+strings.Join(paths[2:len(paths)-1], "/"), "?")[0] + rm.Vars[uri.DeviceIDKey] = unescapeString(paths[0]) + rm.Vars[uri.ResourceHrefKey] = unescapeString(strings.Split("/"+strings.Join(paths[2:len(paths)-1], "/"), "?")[0]) return true } if len(paths) > 4 && paths[1] == uri.ResourcesPathKey && strings.Contains(paths[len(paths)-2], uri.PendingCommandsPathKey) { if rm.Vars == nil { rm.Vars = make(map[string]string) } - rm.Vars[uri.DeviceIDKey] = paths[0] - rm.Vars[uri.ResourceHrefKey] = "/" + strings.Join(paths[2:len(paths)-2], "/") + rm.Vars[uri.DeviceIDKey] = unescapeString(paths[0]) + rm.Vars[uri.ResourceHrefKey] = unescapeString("/" + strings.Join(paths[2:len(paths)-2], "/")) rm.Vars[uri.CorrelationIDKey] = strings.Split(paths[len(paths)-1], "?")[0] return true } @@ -68,8 +80,8 @@ func resourceMatcher(r *http.Request, rm *mux.RouteMatch) bool { if rm.Vars == nil { rm.Vars = make(map[string]string) } - rm.Vars[uri.DeviceIDKey] = paths[0] - rm.Vars[uri.ResourceHrefKey] = strings.Split("/"+strings.Join(paths[2:], "/"), "?")[0] + rm.Vars[uri.DeviceIDKey] = unescapeString(paths[0]) + rm.Vars[uri.ResourceHrefKey] = unescapeString(strings.Split("/"+strings.Join(paths[2:], "/"), "?")[0]) return true } return false @@ -81,8 +93,8 @@ func resourceLinksMatcher(r *http.Request, rm *mux.RouteMatch) bool { if rm.Vars == nil { rm.Vars = make(map[string]string) } - rm.Vars[uri.DeviceIDKey] = paths[0] - rm.Vars[uri.ResourceHrefKey] = strings.Split("/"+strings.Join(paths[2:], "/"), "?")[0] + rm.Vars[uri.DeviceIDKey] = unescapeString(paths[0]) + rm.Vars[uri.ResourceHrefKey] = unescapeString(strings.Split("/"+strings.Join(paths[2:], "/"), "?")[0]) return true } return false @@ -98,8 +110,21 @@ func resourceEventsMatcher(r *http.Request, rm *mux.RouteMatch) bool { if rm.Vars == nil { rm.Vars = make(map[string]string) } - rm.Vars[uri.DeviceIDKey] = paths[0] - rm.Vars[uri.ResourceHrefKey] = "/" + strings.Join(paths[2:len(paths)-1], "/") + rm.Vars[uri.DeviceIDKey] = unescapeString(paths[0]) + rm.Vars[uri.ResourceHrefKey] = unescapeString("/" + strings.Join(paths[2:len(paths)-1], "/")) + return true + } + return false +} + +func thingMatcher(r *http.Request, rm *mux.RouteMatch) bool { + // /api/v1/things/{deviceId} + paths := matchPrefixAndSplitURIPath(r.RequestURI, uri.Things) + if len(paths) == 1 { + if rm.Vars == nil { + rm.Vars = make(map[string]string) + } + rm.Vars[uri.DeviceIDKey] = unescapeString(paths[0]) return true } return false @@ -144,10 +169,12 @@ func (requestHandler *RequestHandler) setupUIHandler(r *mux.Router) { } // NewHTTP returns HTTP handler -func NewRequestHandler(config *Config, r *mux.Router, client *client.Client) (*RequestHandler, error) { +func NewRequestHandler(config *Config, r *mux.Router, client *client.Client, openIDConfig openid.Config, logger log.Logger) (*RequestHandler, error) { requestHandler := &RequestHandler{ - client: client, - config: config, + client: client, + config: config, + openIDConfig: openIDConfig, + logger: logger, mux: serverMux.New( runtime.WithMarshalerOption(ApplicationSubscribeToEventsMIMEWildcard, newSubscribeToEventsMarshaler(serverMux.NewJsonMarshaler())), runtime.WithMarshalerOption(ApplicationSubscribeToEventsProtoJsonContentType, serverMux.NewJsonpbMarshaler()), @@ -164,6 +191,7 @@ func NewRequestHandler(config *Config, r *mux.Router, client *client.Client) (*R r.HandleFunc(uri.AliasDeviceEvents, requestHandler.getEvents).Methods(http.MethodGet) r.HandleFunc(uri.Configuration, requestHandler.getHubConfiguration).Methods(http.MethodGet) r.HandleFunc(uri.HubConfiguration, requestHandler.getHubConfiguration).Methods(http.MethodGet) + r.HandleFunc(uri.Things, requestHandler.getThings).Methods(http.MethodGet) r.PathPrefix(uri.Devices).Methods(http.MethodPost).MatcherFunc(resourceLinksMatcher).HandlerFunc(requestHandler.createResource) r.PathPrefix(uri.Devices).Methods(http.MethodGet).MatcherFunc(resourcePendingCommandsMatcher).HandlerFunc(requestHandler.getResourcePendingCommands) @@ -172,6 +200,8 @@ func NewRequestHandler(config *Config, r *mux.Router, client *client.Client) (*R r.PathPrefix(uri.Devices).Methods(http.MethodPut).MatcherFunc(resourceMatcher).HandlerFunc(requestHandler.updateResource) r.PathPrefix(uri.Devices).Methods(http.MethodGet).MatcherFunc(resourceEventsMatcher).HandlerFunc(requestHandler.getEvents) + r.PathPrefix(uri.Things).Methods(http.MethodGet).MatcherFunc(thingMatcher).HandlerFunc(requestHandler.getThing) + // register grpc-proxy handler if err := pb.RegisterGrpcGatewayHandlerClient(context.Background(), requestHandler.mux, requestHandler.client.GrpcGatewayClient()); err != nil { return nil, fmt.Errorf("failed to register grpc-gateway handler: %w", err) diff --git a/http-gateway/service/service.go b/http-gateway/service/service.go index 47d9344b8..8f4161b2b 100644 --- a/http-gateway/service/service.go +++ b/http-gateway/service/service.go @@ -79,14 +79,14 @@ func New(ctx context.Context, config Config, fileWatcher *fsnotify.Watcher, logg return nil, fmt.Errorf("cannot connect to grpc-gateway: %w", err) } s.AddCloseFunc(func() { - err := grpcConn.Close() - if err != nil { - logger.Errorf("error occurs during close connection to grpc-gateway: %v", err) + errC := grpcConn.Close() + if errC != nil { + logger.Errorf("error occurs during close connection to grpc-gateway: %v", errC) } }) grpcClient := pb.NewGrpcGatewayClient(grpcConn.GRPC()) client := client.New(grpcClient) - _, err = NewRequestHandler(&config, s.GetRouter(), client) + _, err = NewRequestHandler(&config, s.GetRouter(), client, validator.OpenIDConfiguration(), logger) if err != nil { var errors *multierror.Error errors = multierror.Append(errors, fmt.Errorf("cannot create request handler: %w", err)) diff --git a/http-gateway/service/subscribeToEvents.go b/http-gateway/service/subscribeToEvents.go index 3f4edb6f7..9cfe1588b 100644 --- a/http-gateway/service/subscribeToEvents.go +++ b/http-gateway/service/subscribeToEvents.go @@ -28,7 +28,7 @@ func modifyResourceIdFilter(data []byte) []byte { resourceIdFilter := gjson.Get(string(data), "createSubscription.resourceIdFilter") newData := string(data) // append resourceIdFilter to httpResourceIdFilter - resourceIdFilter.ForEach(func(key, value gjson.Result) bool { + resourceIdFilter.ForEach(func(_, value gjson.Result) bool { newData, _ = sjson.Set(newData, "createSubscription.httpResourceIdFilter.-1", value.Str) return true }) diff --git a/http-gateway/service/subscribeToEvents_test.go b/http-gateway/service/subscribeToEvents_test.go index 7d9600db2..d284a2438 100644 --- a/http-gateway/service/subscribeToEvents_test.go +++ b/http-gateway/service/subscribeToEvents_test.go @@ -79,7 +79,7 @@ func (u *updateChecker) checkUpdateLightResource(ctx context.Context, t *testing expectedEvent := &pb.Event{ SubscriptionId: u.subUpdatedID, Type: &pb.Event_ResourceUpdatePending{ - ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, u.deviceID, test.TestResourceLightInstanceHref("1"), updCorrelationID, + ResourceUpdatePending: pbTest.MakeResourceUpdatePending(t, u.deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, updCorrelationID, map[string]interface{}{ "power": power, }), @@ -91,7 +91,7 @@ func (u *updateChecker) checkUpdateLightResource(ctx context.Context, t *testing expectedEvent := &pb.Event{ SubscriptionId: u.subUpdatedID, Type: &pb.Event_ResourceUpdated{ - ResourceUpdated: pbTest.MakeResourceUpdated(t, u.deviceID, test.TestResourceLightInstanceHref("1"), updCorrelationID, nil), + ResourceUpdated: pbTest.MakeResourceUpdated(t, u.deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, updCorrelationID, nil), }, CorrelationId: "updatePending + resourceUpdated", } @@ -100,7 +100,7 @@ func (u *updateChecker) checkUpdateLightResource(ctx context.Context, t *testing expectedEvent := &pb.Event{ SubscriptionId: u.baseSubID, Type: &pb.Event_ResourceChanged{ - ResourceChanged: pbTest.MakeResourceChanged(t, u.deviceID, test.TestResourceLightInstanceHref("1"), + ResourceChanged: pbTest.MakeResourceChanged(t, u.deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, ev.GetResourceChanged().GetAuditContext().GetCorrelationId(), map[string]interface{}{ "state": false, @@ -134,7 +134,7 @@ func testRequestHandlerSubscribeToEvents(t *testing.T, deviceID string, resource shutdownHttp := httpgwTest.SetUp(t) defer shutdownHttp() - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -160,30 +160,30 @@ func testRequestHandlerSubscribeToEvents(t *testing.T, deviceID string, resource send := func(req *pb.SubscribeToEvents) error { marshaler := runtime.JSONPb{} - data, err := marshaler.Marshal(req) - require.NoError(t, err) + data, errM := marshaler.Marshal(req) + require.NoError(t, errM) return wsConn.WriteMessage(websocket.TextMessage, data) } sendBackwardResourceIDFilter := func(req *pb.SubscribeToEvents, resourceIDFilter []string) error { marshaler := runtime.JSONPb{} - data, err := marshaler.Marshal(req) - require.NoError(t, err) + data, errM := marshaler.Marshal(req) + require.NoError(t, errM) - newData, err := sjson.Delete(string(data), "createSubscription.resourceIdFilter") - require.NoError(t, err) - newData, err = sjson.Set(newData, "createSubscription.resourceIdFilter", resourceIDFilter) - require.NoError(t, err) + newData, errM := sjson.Delete(string(data), "createSubscription.resourceIdFilter") + require.NoError(t, errM) + newData, errM = sjson.Set(newData, "createSubscription.resourceIdFilter", resourceIDFilter) + require.NoError(t, errM) return wsConn.WriteMessage(websocket.TextMessage, []byte(newData)) } recv := func() (*pb.Event, error) { - _, reader, err := wsConn.NextReader() - if err != nil { - return nil, err + _, reader, errM := wsConn.NextReader() + if errM != nil { + return nil, errM } var event pb.Event - err = httpgwTest.Unmarshal(http.StatusOK, reader, &event) - return &event, err + errM = httpgwTest.Unmarshal(http.StatusOK, reader, &event) + return &event, errM } createResourceSub := &pb.SubscribeToEvents{ CorrelationId: "testToken", @@ -212,7 +212,7 @@ func testRequestHandlerSubscribeToEvents(t *testing.T, deviceID string, resource ev, err := recv() require.NoError(t, err) expectedEvent := &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), Type: &pb.Event_OperationProcessed_{ OperationProcessed: &pb.Event_OperationProcessed{ ErrorStatus: &pb.Event_OperationProcessed_ErrorStatus{ @@ -223,7 +223,7 @@ func testRequestHandlerSubscribeToEvents(t *testing.T, deviceID string, resource CorrelationId: "testToken", } pbTest.CmpEvent(t, expectedEvent, ev, "") - baseSubID := ev.SubscriptionId + baseSubID := ev.GetSubscriptionId() deviceID, shutdownDevSim := test.OnboardDevSim(ctx, t, c, deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, nil) @@ -269,7 +269,7 @@ func testRequestHandlerSubscribeToEvents(t *testing.T, deviceID string, resource ev, err = recv() require.NoError(t, err) expectedEvent = &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), Type: &pb.Event_OperationProcessed_{ OperationProcessed: &pb.Event_OperationProcessed{ ErrorStatus: &pb.Event_OperationProcessed_ErrorStatus{ @@ -285,7 +285,7 @@ func testRequestHandlerSubscribeToEvents(t *testing.T, deviceID string, resource c: c, deviceID: deviceID, baseSubID: baseSubID, - subUpdatedID: ev.SubscriptionId, + subUpdatedID: ev.GetSubscriptionId(), recv: recv, } updChecker.checkUpdateLightResource(ctx, t, 99) @@ -308,7 +308,7 @@ func testRequestHandlerSubscribeToEvents(t *testing.T, deviceID string, resource ev, err = recv() require.NoError(t, err) expectedEvent = &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), Type: &pb.Event_OperationProcessed_{ OperationProcessed: &pb.Event_OperationProcessed{ ErrorStatus: &pb.Event_OperationProcessed_ErrorStatus{ @@ -319,7 +319,7 @@ func testRequestHandlerSubscribeToEvents(t *testing.T, deviceID string, resource CorrelationId: "receivePending + resourceReceived", } pbTest.CmpEvent(t, expectedEvent, ev, "") - subReceivedID := ev.SubscriptionId + subReceivedID := ev.GetSubscriptionId() _, err = c.GetResourceFromDevice(ctx, &pb.GetResourceFromDeviceRequest{ ResourceId: commands.NewResourceID(deviceID, test.TestResourceLightInstanceHref("1")), @@ -331,8 +331,9 @@ func testRequestHandlerSubscribeToEvents(t *testing.T, deviceID string, resource SubscriptionId: subReceivedID, Type: &pb.Event_ResourceRetrievePending{ ResourceRetrievePending: &events.ResourceRetrievePending{ - ResourceId: commands.NewResourceID(deviceID, test.TestResourceLightInstanceHref("1")), - AuditContext: ev.GetResourceRetrievePending().GetAuditContext(), + ResourceId: commands.NewResourceID(deviceID, test.TestResourceLightInstanceHref("1")), + AuditContext: ev.GetResourceRetrievePending().GetAuditContext(), + ResourceTypes: test.TestResourceLightInstanceResourceTypes, }, }, CorrelationId: "receivePending + resourceReceived", @@ -345,7 +346,7 @@ func testRequestHandlerSubscribeToEvents(t *testing.T, deviceID string, resource expectedEvent = &pb.Event{ SubscriptionId: subReceivedID, Type: &pb.Event_ResourceRetrieved{ - ResourceRetrieved: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceLightInstanceHref("1"), recvCorrelationID, + ResourceRetrieved: pbTest.MakeResourceRetrieved(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, recvCorrelationID, map[string]interface{}{ "name": "Light", "power": 0x0, @@ -367,7 +368,7 @@ func testRequestHandlerSubscribeToEvents(t *testing.T, deviceID string, resource if ev.GetDeviceUnregistered() != nil { expectedEvent = &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), Type: &pb.Event_DeviceUnregistered_{ DeviceUnregistered: &pb.Event_DeviceUnregistered{ DeviceIds: []string{deviceID}, diff --git a/http-gateway/service/updateDeviceMetadata_test.go b/http-gateway/service/updateDeviceMetadata_test.go index 9566dad49..12437af39 100644 --- a/http-gateway/service/updateDeviceMetadata_test.go +++ b/http-gateway/service/updateDeviceMetadata_test.go @@ -127,7 +127,7 @@ func TestRequestHandlerUpdateDeviceMetadata(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -163,8 +163,8 @@ func TestRequestHandlerUpdateDeviceMetadata(t *testing.T) { }() updateDeviceTwinSynchronization := func(in *pb.UpdateDeviceMetadataRequest) error { - data, err := protojson.Marshal(in) - require.NoError(t, err) + data, errM := protojson.Marshal(in) + require.NoError(t, errM) rb := httpgwTest.NewRequest(http.MethodPut, uri.DeviceMetadata, bytes.NewReader(data)).AuthToken(token).DeviceId(deviceID) resp := httpgwTest.HTTPDo(t, rb.Build()) @@ -173,8 +173,8 @@ func TestRequestHandlerUpdateDeviceMetadata(t *testing.T) { }(resp) var got pb.UpdateDeviceMetadataResponse - err = httpgwTest.Unmarshal(resp.StatusCode, resp.Body, &got) - return err + errM = httpgwTest.Unmarshal(resp.StatusCode, resp.Body, &got) + return errM } err = updateDeviceTwinSynchronization(&pb.UpdateDeviceMetadataRequest{ diff --git a/http-gateway/service/updateResource.go b/http-gateway/service/updateResource.go index 332930a18..e47c06cbf 100644 --- a/http-gateway/service/updateResource.go +++ b/http-gateway/service/updateResource.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "net/http" + "net/http/httptest" "github.com/gorilla/mux" "github.com/plgd-dev/go-coap/v3/message" @@ -53,5 +54,14 @@ func (requestHandler *RequestHandler) updateResource(w http.ResponseWriter, r *h } r.Body = newBody - requestHandler.mux.ServeHTTP(w, r) + rec := httptest.NewRecorder() + onlyContent := r.URL.Query().Get(uri.OnlyContentQueryKey) + requestHandler.mux.ServeHTTP(rec, r) + allowEmptyContent := false + if parseBoolQuery(onlyContent) { + allowEmptyContent = requestHandler.filterOnlyContent(rec, "data", "content") + } + toSimpleResponse(w, rec, allowEmptyContent, func(w http.ResponseWriter, err error) { + serverMux.WriteError(w, kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "cannot update resource('/%v%v') from the device: %v", deviceID, href, err)) + }, streamResponseKey) } diff --git a/http-gateway/service/updateResource_test.go b/http-gateway/service/updateResource_test.go index c22997670..19609f3e9 100644 --- a/http-gateway/service/updateResource_test.go +++ b/http-gateway/service/updateResource_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "github.com/plgd-dev/device/v2/pkg/codec/json" "github.com/plgd-dev/device/v2/schema/device" "github.com/plgd-dev/device/v2/schema/interfaces" "github.com/plgd-dev/go-coap/v3/message" @@ -38,6 +39,7 @@ func TestRequestHandlerUpdateResourcesValues(t *testing.T) { deviceID string resourceHref string resourceInterface string + onlyContent bool resourceData interface{} ttl time.Duration } @@ -54,6 +56,7 @@ func TestRequestHandlerUpdateResourcesValues(t *testing.T) { accept: uri.ApplicationProtoJsonContentType, deviceID: deviceID, resourceHref: "/unknown", + onlyContent: true, }, wantErr: true, wantHTTPCode: http.StatusNotFound, @@ -100,7 +103,7 @@ func TestRequestHandlerUpdateResourcesValues(t *testing.T) { "power": 1, }, }, - want: pbTest.MakeResourceUpdated(t, deviceID, test.TestResourceLightInstanceHref("1"), "", nil), + want: pbTest.MakeResourceUpdated(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", nil), wantHTTPCode: http.StatusOK, }, { @@ -114,7 +117,7 @@ func TestRequestHandlerUpdateResourcesValues(t *testing.T) { "power": 102, }, }, - want: pbTest.MakeResourceUpdated(t, deviceID, test.TestResourceLightInstanceHref("1"), "", nil), + want: pbTest.MakeResourceUpdated(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", nil), wantHTTPCode: http.StatusOK, }, { @@ -128,7 +131,7 @@ func TestRequestHandlerUpdateResourcesValues(t *testing.T) { }, resourceInterface: interfaces.OC_IF_BASELINE, }, - want: pbTest.MakeResourceUpdated(t, deviceID, test.TestResourceLightInstanceHref("1"), "", nil), + want: pbTest.MakeResourceUpdated(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", nil), wantHTTPCode: http.StatusOK, }, { @@ -142,7 +145,7 @@ func TestRequestHandlerUpdateResourcesValues(t *testing.T) { }, resourceInterface: interfaces.OC_IF_BASELINE, }, - want: pbTest.MakeResourceUpdated(t, deviceID, test.TestResourceLightInstanceHref("1"), "", nil), + want: pbTest.MakeResourceUpdated(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", nil), wantHTTPCode: http.StatusOK, }, { @@ -168,7 +171,8 @@ func TestRequestHandlerUpdateResourcesValues(t *testing.T) { "value": true, }), }, - AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + ResourceTypes: test.TestResourceSwitchesInstanceResourceTypes, }, wantHTTPCode: http.StatusOK, }, @@ -186,7 +190,7 @@ func TestRequestHandlerUpdateResourcesValues(t *testing.T) { token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -210,7 +214,7 @@ func TestRequestHandlerUpdateResourcesValues(t *testing.T) { require.NoError(t, err) rb := httpgwTest.NewRequest(http.MethodPut, uri.AliasDeviceResource, bytes.NewReader(data)).AuthToken(token).Accept(tt.args.accept) rb.DeviceId(tt.args.deviceID).ResourceHref(tt.args.resourceHref).ResourceInterface(tt.args.resourceInterface).ContentType(tt.args.contentType) - rb.AddTimeToLive(tt.args.ttl) + rb.AddTimeToLive(tt.args.ttl).OnlyContent(tt.args.onlyContent) resp := httpgwTest.HTTPDo(t, rb.Build()) defer func() { _ = resp.Body.Close() @@ -228,3 +232,104 @@ func TestRequestHandlerUpdateResourcesValues(t *testing.T) { }) } } + +func TestRequestHandlerUpdateResourcesValuesWithOnlyContent(t *testing.T) { + deviceID := test.MustFindDeviceByName(test.TestDeviceName) + switchID := "1" + type args struct { + accept string + contentType string + deviceID string + resourceHref string + resourceData interface{} + } + tests := []struct { + name string + args args + want interface{} + wantErr bool + wantHTTPCode int + }{ + { + name: "valid - accept " + uri.ApplicationProtoJsonContentType, + args: args{ + accept: uri.ApplicationProtoJsonContentType, + contentType: message.AppJSON.String(), + deviceID: deviceID, + resourceHref: test.TestResourceLightInstanceHref("1"), + resourceData: map[string]interface{}{ + "power": 102, + }, + }, + want: nil, + wantHTTPCode: http.StatusOK, + }, + { + name: "revert-update - accept empty", + args: args{ + contentType: message.AppJSON.String(), + deviceID: deviceID, + resourceHref: test.TestResourceLightInstanceHref("1"), + resourceData: map[string]interface{}{ + "power": 0, + }, + }, + want: nil, + wantHTTPCode: http.StatusOK, + }, + } + + ctx, cancel := context.WithTimeout(context.Background(), config.TEST_TIMEOUT) + defer cancel() + + tearDown := service.SetUp(ctx, t) + defer tearDown() + + shutdownHttp := httpgwTest.SetUp(t) + defer shutdownHttp() + + token := oauthTest.GetDefaultAccessToken(t) + ctx = kitNetGrpc.CtxWithToken(ctx, token) + + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: test.GetRootCertificatePool(t), + }))) + require.NoError(t, err) + defer func() { + _ = conn.Close() + }() + c := pb.NewGrpcGatewayClient(conn) + + _, shutdownDevSim := test.OnboardDevSim(ctx, t, c, deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, test.GetAllBackendResourceLinks()) + defer shutdownDevSim() + + test.AddDeviceSwitchResources(ctx, t, deviceID, c, switchID) + time.Sleep(200 * time.Millisecond) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + data, err := httpgwTest.GetContentData(&pb.Content{ + Data: test.EncodeToCbor(t, tt.args.resourceData), + ContentType: message.AppOcfCbor.String(), + }, tt.args.contentType) + require.NoError(t, err) + rb := httpgwTest.NewRequest(http.MethodPut, uri.AliasDeviceResource, bytes.NewReader(data)).AuthToken(token).Accept(tt.args.accept) + rb.DeviceId(tt.args.deviceID).ResourceHref(tt.args.resourceHref).ContentType(tt.args.contentType) + rb.OnlyContent(true) + resp := httpgwTest.HTTPDo(t, rb.Build()) + defer func() { + _ = resp.Body.Close() + }() + assert.Equal(t, tt.wantHTTPCode, resp.StatusCode) + + var got interface{} + err = json.ReadFrom(resp.Body, &got) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/http-gateway/swagger.wot.yaml b/http-gateway/swagger.wot.yaml new file mode 100644 index 000000000..ca7386796 --- /dev/null +++ b/http-gateway/swagger.wot.yaml @@ -0,0 +1,970 @@ +openapi: 3.1.0 +info: + title: plgd HTTP Gateway + version: 2.7.19 +servers: + - url: "https://try.plgd.cloud" +paths: + "/api/v1/things": + get: + tags: + - "Web of Things" + summary: "Retrieve Hub Thing Description" + description: | + Retrieve the description of the PLGD hub, including links to all associated things. + responses: + 200: + description: "Thing description of the plgd hub." + content: + application/json: + schema: + $ref: "#/components/schemas/thing" +components: + schemas: + anyUri: + type: string + description: + type: string + descriptions: + type: object + additionalProperties: + type: string + title: + type: string + titles: + type: object + additionalProperties: + type: string + security: + oneOf: + - type: array + items: + type: string + minItems: 1 + - type: string + scopes: + oneOf: + - type: array + items: + type: string + - type: string + subprotocol: + type: string + examples: + - longpoll + - websub + - sse + thing-context-td-uri-v1: + type: string + const: https://www.w3.org/2019/wot/td/v1 + thing-context-td-uri-v1.1: + type: string + const: https://www.w3.org/2022/wot/td/v1.1 + thing-context-td-uri-temp: + type: string + const: http://www.w3.org/ns/td + thing-context: + anyOf: + - $comment: New context URI with other vocabularies after it but not the old one + type: array + items: + - $ref: '#/components/schemas/thing-context-td-uri-v1.1' + additionalItems: + anyOf: + - $ref: '#/components/schemas/anyUri' + - type: object + not: + $ref: '#/components/schemas/thing-context-td-uri-v1' + - $comment: Only the new context URI + $ref: '#/components/schemas/thing-context-td-uri-v1.1' + - $comment: >- + Old context URI, followed by the new one and possibly other + vocabularies. minItems and contains are required since prefixItems + does not say all items should be provided + type: array + prefixItems: + - $ref: '#/components/schemas/thing-context-td-uri-v1' + - $ref: '#/components/schemas/thing-context-td-uri-v1.1' + minItems: 2 + contains: + $ref: '#/components/schemas/thing-context-td-uri-v1.1' + additionalItems: + anyOf: + - $ref: '#/components/schemas/anyUri' + - type: object + - $comment: >- + Old context URI, followed by possibly other vocabularies. minItems and + contains are required since prefixItems does not say all items should + be provided + type: array + prefixItems: + - $ref: '#/components/schemas/thing-context-td-uri-v1' + minItems: 1 + contains: + $ref: '#/components/schemas/thing-context-td-uri-v1' + additionalItems: + anyOf: + - $ref: '#/components/schemas/anyUri' + - type: object + - $comment: Only the old context URI + $ref: '#/components/schemas/thing-context-td-uri-v1' + bcp47_string: + type: string + pattern: >- + ^(((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+)|((en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)))$ + type_declaration: + oneOf: + - type: string + not: + const: tm:ThingModel + - type: array + items: + type: string + not: + const: tm:ThingModel + dataSchema-type: + type: string + enum: + - boolean + - integer + - number + - string + - object + - array + - 'null' + dataSchema: + type: object + properties: + '@type': + $ref: '#/components/schemas/type_declaration' + description: + $ref: '#/components/schemas/description' + title: + $ref: '#/components/schemas/title' + descriptions: + $ref: '#/components/schemas/descriptions' + titles: + $ref: '#/components/schemas/titles' + writeOnly: + type: boolean + readOnly: + type: boolean + oneOf: + type: array + items: + $ref: '#/components/schemas/dataSchema' + unit: + type: string + enum: + type: array + minItems: 1 + uniqueItems: true + format: + type: string + const: {} + default: {} + contentEncoding: + type: string + contentMediaType: + type: string + type: + $ref: '#/components/schemas/dataSchema-type' + items: + oneOf: + - $ref: '#/components/schemas/dataSchema' + - type: array + items: + $ref: '#/components/schemas/dataSchema' + maxItems: + type: integer + minimum: 0 + minItems: + type: integer + minimum: 0 + minimum: + type: number + maximum: + type: number + exclusiveMinimum: + type: number + exclusiveMaximum: + type: number + minLength: + type: integer + minimum: 0 + maxLength: + type: integer + minimum: 0 + multipleOf: + $ref: '#/components/schemas/multipleOfDefinition' + properties: + additionalProperties: + $ref: '#/components/schemas/dataSchema' + required: + type: array + items: + type: string + additionalResponsesDefinition: + type: array + items: + type: object + properties: + contentType: + type: string + schema: + type: string + success: + type: boolean + multipleOfDefinition: + type: + - integer + - number + exclusiveMinimum: 0 + expectedResponse: + type: object + properties: + contentType: + type: string + required: + - contentType + form_element_base: + type: object + properties: + op: + oneOf: + - type: string + - type: array + items: + type: string + href: + $ref: '#/components/schemas/anyUri' + contentType: + type: string + contentCoding: + type: string + subprotocol: + $ref: '#/components/schemas/subprotocol' + security: + $ref: '#/components/schemas/security' + scopes: + $ref: '#/components/schemas/scopes' + response: + $ref: '#/components/schemas/expectedResponse' + additionalResponses: + $ref: '#/components/schemas/additionalResponsesDefinition' + required: + - href + additionalProperties: true + form_element_property: + allOf: + - $ref: '#/components/schemas/form_element_base' + type: object + properties: + op: + oneOf: + - type: string + enum: + - readproperty + - writeproperty + - observeproperty + - unobserveproperty + - type: array + items: + type: string + enum: + - readproperty + - writeproperty + - observeproperty + - unobserveproperty + minItems: 1 + additionalProperties: true + form_element_action: + allOf: + - $ref: '#/components/schemas/form_element_base' + type: object + properties: + op: + oneOf: + - type: string + enum: + - invokeaction + - queryaction + - cancelaction + - type: array + items: + type: string + enum: + - invokeaction + - queryaction + - cancelaction + minItems: 1 + additionalProperties: true + form_element_event: + allOf: + - $ref: '#/components/schemas/form_element_base' + type: object + properties: + op: + oneOf: + - type: string + enum: + - subscribeevent + - unsubscribeevent + - type: array + items: + type: string + enum: + - subscribeevent + - unsubscribeevent + minItems: 1 + additionalProperties: true + form_element_root: + allOf: + - $ref: '#/components/schemas/form_element_base' + type: object + properties: + op: + oneOf: + - type: string + enum: + - readallproperties + - writeallproperties + - readmultipleproperties + - writemultipleproperties + - observeallproperties + - unobserveallproperties + - queryallactions + - subscribeallevents + - unsubscribeallevents + - type: array + items: + type: string + enum: + - readallproperties + - writeallproperties + - readmultipleproperties + - writemultipleproperties + - observeallproperties + - unobserveallproperties + - queryallactions + - subscribeallevents + - unsubscribeallevents + minItems: 1 + additionalProperties: true + required: + - op + form: + $comment: >- + This is NOT for validation purposes but for automatic generation of TS + types. For more info, please see: + https://github.com/w3c/wot-thing-description/pull/1319#issuecomment-994950057 + oneOf: + - $ref: '#/components/schemas/form_element_property' + - $ref: '#/components/schemas/form_element_action' + - $ref: '#/components/schemas/form_element_event' + - $ref: '#/components/schemas/form_element_root' + property_element: + type: object + properties: + '@type': + $ref: '#/components/schemas/type_declaration' + description: + $ref: '#/components/schemas/description' + descriptions: + $ref: '#/components/schemas/descriptions' + title: + $ref: '#/components/schemas/title' + titles: + $ref: '#/components/schemas/titles' + forms: + type: array + minItems: 1 + items: + $ref: '#/components/schemas/form_element_property' + uriVariables: + type: object + additionalProperties: + $ref: '#/components/schemas/dataSchema' + observable: + type: boolean + writeOnly: + type: boolean + readOnly: + type: boolean + oneOf: + type: array + items: + $ref: '#/components/schemas/dataSchema' + unit: + type: string + enum: + type: array + minItems: 1 + uniqueItems: true + format: + type: string + const: {} + default: {} + type: + $ref: '#/components/schemas/dataSchema-type' + items: + oneOf: + - $ref: '#/components/schemas/dataSchema' + - type: array + items: + $ref: '#/components/schemas/dataSchema' + maxItems: + type: integer + minimum: 0 + minItems: + type: integer + minimum: 0 + minimum: + type: number + maximum: + type: number + exclusiveMinimum: + type: number + exclusiveMaximum: + type: number + minLength: + type: integer + minimum: 0 + maxLength: + type: integer + minimum: 0 + multipleOf: + $ref: '#/components/schemas/multipleOfDefinition' + properties: + additionalProperties: + $ref: '#/components/schemas/dataSchema' + required: + type: array + items: + type: string + required: + - forms + additionalProperties: true + action_element: + type: object + properties: + '@type': + $ref: '#/components/schemas/type_declaration' + description: + $ref: '#/components/schemas/description' + descriptions: + $ref: '#/components/schemas/descriptions' + title: + $ref: '#/components/schemas/title' + titles: + $ref: '#/components/schemas/titles' + forms: + type: array + minItems: 1 + items: + $ref: '#/components/schemas/form_element_action' + uriVariables: + type: object + additionalProperties: + $ref: '#/components/schemas/dataSchema' + input: + $ref: '#/components/schemas/dataSchema' + output: + $ref: '#/components/schemas/dataSchema' + safe: + type: boolean + idempotent: + type: boolean + synchronous: + type: boolean + required: + - forms + additionalProperties: true + event_element: + type: object + properties: + '@type': + $ref: '#/components/schemas/type_declaration' + description: + $ref: '#/components/schemas/description' + descriptions: + $ref: '#/components/schemas/descriptions' + title: + $ref: '#/components/schemas/title' + titles: + $ref: '#/components/schemas/titles' + forms: + type: array + minItems: 1 + items: + $ref: '#/components/schemas/form_element_event' + uriVariables: + type: object + additionalProperties: + $ref: '#/components/schemas/dataSchema' + subscription: + $ref: '#/components/schemas/dataSchema' + data: + $ref: '#/components/schemas/dataSchema' + dataResponse: + $ref: '#/components/schemas/dataSchema' + cancellation: + $ref: '#/components/schemas/dataSchema' + required: + - forms + additionalProperties: true + base_link_element: + type: object + properties: + href: + $ref: '#/components/schemas/anyUri' + type: + type: string + rel: + type: string + anchor: + $ref: '#/components/schemas/anyUri' + hreflang: + anyOf: + - $ref: '#/components/schemas/bcp47_string' + - type: array + items: + $ref: '#/components/schemas/bcp47_string' + required: + - href + additionalProperties: true + link_element: + allOf: + - $ref: '#/components/schemas/base_link_element' + - not: + description: A basic link element should not contain sizes + type: object + properties: + sizes: {} + required: + - sizes + - not: + description: A basic link element should not contain icon or tm:extends + properties: + rel: + enum: + - icon + - tm:extends + required: + - rel + icon_link_element: + allOf: + - $ref: '#/components/schemas/base_link_element' + - properties: + rel: + const: icon + sizes: + type: string + pattern: '[0-9]*x[0-9]+' + required: + - rel + additionalSecurityScheme: + description: >- + Applies to additional SecuritySchemes not defined in the WoT TD + specification. + $comment: >- + Additional SecuritySchemes should always be defined via a context + extension, using a prefixed value for the scheme. This prefix (e.g. 'ace', + see the example below) must contain at least one character in order to + reference a valid JSON-LD context extension. + examples: + - scheme: ace:ACESecurityScheme + ace:as: coaps://as.example.com/token + ace:audience: coaps://rs.example.com + ace:scopes: + - limited + - special + ace:cnonce: true + type: object + properties: + '@type': + $ref: '#/components/schemas/type_declaration' + description: + $ref: '#/components/schemas/description' + descriptions: + $ref: '#/components/schemas/descriptions' + proxy: + $ref: '#/components/schemas/anyUri' + scheme: + type: string + pattern: .+:.* + required: + - scheme + additionalProperties: true + noSecurityScheme: + type: object + properties: + '@type': + $ref: '#/components/schemas/type_declaration' + description: + $ref: '#/components/schemas/description' + descriptions: + $ref: '#/components/schemas/descriptions' + proxy: + $ref: '#/components/schemas/anyUri' + scheme: + type: string + enum: + - nosec + required: + - scheme + additionalProperties: true + autoSecurityScheme: + type: object + properties: + '@type': + $ref: '#/components/schemas/type_declaration' + description: + $ref: '#/components/schemas/description' + descriptions: + $ref: '#/components/schemas/descriptions' + proxy: + $ref: '#/components/schemas/anyUri' + scheme: + type: string + enum: + - auto + not: + required: + - name + required: + - scheme + additionalProperties: true + comboSecurityScheme: + oneOf: + - type: object + properties: + '@type': + $ref: '#/components/schemas/type_declaration' + description: + $ref: '#/components/schemas/description' + descriptions: + $ref: '#/components/schemas/descriptions' + proxy: + $ref: '#/components/schemas/anyUri' + scheme: + type: string + enum: + - combo + oneOf: + type: array + minItems: 2 + items: + type: string + required: + - scheme + - oneOf + additionalProperties: true + - type: object + properties: + '@type': + $ref: '#/components/schemas/type_declaration' + description: + $ref: '#/components/schemas/description' + descriptions: + $ref: '#/components/schemas/descriptions' + proxy: + $ref: '#/components/schemas/anyUri' + scheme: + type: string + enum: + - combo + allOf: + type: array + minItems: 2 + items: + type: string + required: + - scheme + - allOf + additionalProperties: true + basicSecurityScheme: + type: object + properties: + '@type': + $ref: '#/components/schemas/type_declaration' + description: + $ref: '#/components/schemas/description' + descriptions: + $ref: '#/components/schemas/descriptions' + proxy: + $ref: '#/components/schemas/anyUri' + scheme: + type: string + enum: + - basic + in: + type: string + enum: + - header + - query + - body + - cookie + - auto + name: + type: string + required: + - scheme + additionalProperties: true + digestSecurityScheme: + type: object + properties: + '@type': + $ref: '#/components/schemas/type_declaration' + description: + $ref: '#/components/schemas/description' + descriptions: + $ref: '#/components/schemas/descriptions' + proxy: + $ref: '#/components/schemas/anyUri' + scheme: + type: string + enum: + - digest + qop: + type: string + enum: + - auth + - auth-int + in: + type: string + enum: + - header + - query + - body + - cookie + - auto + name: + type: string + required: + - scheme + additionalProperties: true + apiKeySecurityScheme: + type: object + properties: + '@type': + $ref: '#/components/schemas/type_declaration' + description: + $ref: '#/components/schemas/description' + descriptions: + $ref: '#/components/schemas/descriptions' + proxy: + $ref: '#/components/schemas/anyUri' + scheme: + type: string + enum: + - apikey + in: + type: string + enum: + - header + - query + - body + - cookie + - uri + - auto + name: + type: string + required: + - scheme + additionalProperties: true + bearerSecurityScheme: + type: object + properties: + '@type': + $ref: '#/components/schemas/type_declaration' + description: + $ref: '#/components/schemas/description' + descriptions: + $ref: '#/components/schemas/descriptions' + proxy: + $ref: '#/components/schemas/anyUri' + scheme: + type: string + enum: + - bearer + authorization: + $ref: '#/components/schemas/anyUri' + alg: + type: string + format: + type: string + in: + type: string + enum: + - header + - query + - body + - cookie + - auto + name: + type: string + required: + - scheme + additionalProperties: true + pskSecurityScheme: + type: object + properties: + '@type': + $ref: '#/components/schemas/type_declaration' + description: + $ref: '#/components/schemas/description' + descriptions: + $ref: '#/components/schemas/descriptions' + proxy: + $ref: '#/components/schemas/anyUri' + scheme: + type: string + enum: + - psk + identity: + type: string + required: + - scheme + additionalProperties: true + oAuth2SecurityScheme: + type: object + properties: + '@type': + $ref: '#/components/schemas/type_declaration' + description: + $ref: '#/components/schemas/description' + descriptions: + $ref: '#/components/schemas/descriptions' + proxy: + $ref: '#/components/schemas/anyUri' + scheme: + type: string + enum: + - oauth2 + authorization: + $ref: '#/components/schemas/anyUri' + token: + $ref: '#/components/schemas/anyUri' + refresh: + $ref: '#/components/schemas/anyUri' + scopes: + oneOf: + - type: array + items: + type: string + - type: string + flow: + anyOf: + - type: string + - type: string + enum: + - code + - client + required: + - scheme + additionalProperties: true + securityScheme: + oneOf: + - $ref: '#/components/schemas/noSecurityScheme' + - $ref: '#/components/schemas/autoSecurityScheme' + - $ref: '#/components/schemas/comboSecurityScheme' + - $ref: '#/components/schemas/basicSecurityScheme' + - $ref: '#/components/schemas/digestSecurityScheme' + - $ref: '#/components/schemas/apiKeySecurityScheme' + - $ref: '#/components/schemas/bearerSecurityScheme' + - $ref: '#/components/schemas/pskSecurityScheme' + - $ref: '#/components/schemas/oAuth2SecurityScheme' + - $ref: '#/components/schemas/additionalSecurityScheme' + thing: + type: object + properties: + id: + type: string + format: uri + title: + $ref: '#/components/schemas/title' + titles: + $ref: '#/components/schemas/titles' + properties: + type: object + additionalProperties: + $ref: '#/components/schemas/property_element' + actions: + type: object + additionalProperties: + $ref: '#/components/schemas/action_element' + events: + type: object + additionalProperties: + $ref: '#/components/schemas/event_element' + description: + $ref: '#/components/schemas/description' + descriptions: + $ref: '#/components/schemas/descriptions' + version: + type: object + properties: + instance: + type: string + required: + - instance + links: + type: array + items: + oneOf: + - $ref: '#/components/schemas/link_element' + - $ref: '#/components/schemas/icon_link_element' + forms: + type: array + minItems: 1 + items: + $ref: '#/components/schemas/form_element_root' + base: + $ref: '#/components/schemas/anyUri' + securityDefinitions: + type: object + minProperties: 1 + additionalProperties: + $ref: '#/components/schemas/securityScheme' + schemaDefinitions: + type: object + minProperties: 1 + additionalProperties: + $ref: '#/components/schemas/dataSchema' + support: + $ref: '#/components/schemas/anyUri' + created: + type: string + format: date-time + modified: + type: string + format: date-time + profile: + oneOf: + - $ref: '#/components/schemas/anyUri' + - type: array + minItems: 1 + items: + $ref: '#/components/schemas/anyUri' + security: + oneOf: + - type: string + - type: array + minItems: 1 + items: + type: string + uriVariables: + type: object + additionalProperties: + $ref: '#/components/schemas/dataSchema' + '@type': + $ref: '#/components/schemas/type_declaration' + '@context': + $ref: '#/components/schemas/thing-context' + required: + - title + - security + - securityDefinitions + - '@context' + additionalProperties: true diff --git a/http-gateway/swagger.yaml b/http-gateway/swagger.yaml index fd7cdc5a2..72329ff07 100644 --- a/http-gateway/swagger.yaml +++ b/http-gateway/swagger.yaml @@ -19,6 +19,40 @@ paths: application/json: schema: $ref: "#/components/schemas/HubConfiguration" + "/api/v1/things": + get: + tags: + - "Web of Things" + summary: "Retrieve Hub Thing Description" + description: | + Retrieve the description of the PLGD hub, including links to all associated things. + responses: + 200: + description: "Thing description of the plgd hub." + content: + application/json: + schema: + $ref: "https://raw.githubusercontent.com/plgd-dev/hub/main/http-gateway/swagger.wot.yaml#/components/schemas/thing" + 401: + $ref: "#/components/responses/unauthorized" + "/api/v1/things/{deviceId}": + get: + tags: + - "Web of Things" + summary: "Retrieve Device Thing Description" + description: | + Retrieve the thing description for a specific device. The device must be listed in the links of /api/v1/things. + parameters: + - $ref: "#/components/parameters/deviceId" + responses: + 200: + description: "Thing description of the device." + content: + application/json: + schema: + $ref: "https://raw.githubusercontent.com/plgd-dev/hub/main/http-gateway/swagger.wot.yaml#/components/schemas/thing" + 401: + $ref: "#/components/responses/unauthorized" "/api/v1/devices": get: tags: @@ -389,6 +423,7 @@ paths: - $ref: "#/components/parameters/shadow" - $ref: "#/components/parameters/timeToLive" - $ref: "#/components/parameters/etag" + - $ref: "#/components/parameters/onlyContent" responses: 200: description: "Resource content." @@ -419,6 +454,7 @@ paths: parameters: - $ref: "#/components/parameters/interface" - $ref: "#/components/parameters/timeToLive" + - $ref: "#/components/parameters/onlyContent" requestBody: description: "Updated content of the resource." content: @@ -961,6 +997,14 @@ components: type: string format: int64 description: "Unix timestamp in nanoseconds." + serviceId: + type: string + description: "The service.ID, which identify the device being served, must be set when the status is ONLINE. However, during an OFFLINE event, they will be sed to empty values." + localEndpoints: + type: array + description: "The last local endpoints of the device, and it is set when the status is ONLINE." + items: + type: string twinSynchronization: $ref: "#/components/schemas/TwinSynchronization" twinEnabled: @@ -1636,14 +1680,14 @@ components: title: "Visibility" deviceProvisioningService: type: string - title": "Address to device provisioning service HTTP API in format https://host:port" + title: "Address to device provisioning service HTTP API in format https://host:port" UIVisibility: type: object description: | true - show, false - hide properties: mainSidebar: - $ref: "#/definitions/UIVisibilityMainSidebar" + $ref: "#/components/schemas/UIVisibilityMainSidebar" title: "Main sidebar visibility" parameters: deviceId: @@ -1754,6 +1798,13 @@ components: schema: type: string format: base64 + onlyContent: + name: onlyContent + in: query + description: "Return only content of the resource in the response." + schema: + type: boolean + default: false responses: ok: description: Content is stored in body. diff --git a/http-gateway/test/http.go b/http-gateway/test/http.go index 8164c3055..ad47b5b3c 100644 --- a/http-gateway/test/http.go +++ b/http-gateway/test/http.go @@ -55,7 +55,7 @@ func (c *RequestBuilder) DeviceId(deviceID string) *RequestBuilder { } func (c *RequestBuilder) Twin(v bool) *RequestBuilder { - c.AddQuery(uri.TwinQueryKey, fmt.Sprintf("%v", v)) + c.AddQuery(uri.TwinQueryKey, strconv.FormatBool(v)) return c } @@ -63,7 +63,7 @@ func (c *RequestBuilder) Timestamp(v time.Time) *RequestBuilder { if v.IsZero() { return c } - c.AddQuery(uri.TimestampFilterQueryKey, fmt.Sprintf("%v", v.UnixNano())) + c.AddQuery(uri.TimestampFilterQueryKey, strconv.FormatInt(v.UnixNano(), 10)) return c } @@ -102,7 +102,7 @@ func (c *RequestBuilder) ResourceHref(resourceHref string) *RequestBuilder { } func (c *RequestBuilder) AuthToken(token string) *RequestBuilder { - c.header["Authorization"] = fmt.Sprintf("bearer %s", token) + c.header["Authorization"] = "bearer " + token return c } @@ -114,6 +114,11 @@ func (c *RequestBuilder) Accept(accept string) *RequestBuilder { return c } +func (c *RequestBuilder) OnlyContent(v bool) *RequestBuilder { + c.AddQuery(uri.OnlyContentQueryKey, strconv.FormatBool(v)) + return c +} + func (c *RequestBuilder) ContentType(contentType string) *RequestBuilder { if contentType == "" { return c diff --git a/http-gateway/test/pendingCommands.go b/http-gateway/test/pendingCommands.go index 81b1a6c10..0fe922f86 100644 --- a/http-gateway/test/pendingCommands.go +++ b/http-gateway/test/pendingCommands.go @@ -7,7 +7,7 @@ import ( ) func ToCommandsFilter(s []pb.GetPendingCommandsRequest_Command) []string { - var sf []string + sf := make([]string, 0, len(s)) for _, v := range s { sf = append(sf, strconv.FormatInt(int64(v), 10)) } diff --git a/http-gateway/test/test.go b/http-gateway/test/test.go index 2e7b7dd79..8c63f9714 100644 --- a/http-gateway/test/test.go +++ b/http-gateway/test/test.go @@ -2,6 +2,9 @@ package test import ( "context" + "encoding/json" + "io" + "net/http" "os" "sync" "time" @@ -11,7 +14,7 @@ import ( "github.com/plgd-dev/hub/v2/http-gateway/uri" "github.com/plgd-dev/hub/v2/pkg/fsnotify" "github.com/plgd-dev/hub/v2/pkg/log" - "github.com/plgd-dev/hub/v2/pkg/net/http" + pkgHttp "github.com/plgd-dev/hub/v2/pkg/net/http" "github.com/plgd-dev/hub/v2/test/config" testHttp "github.com/plgd-dev/hub/v2/test/http" "github.com/plgd-dev/kit/v2/codec/cbor" @@ -53,7 +56,7 @@ func MakeConfig(t require.TestingT, enableUI bool) service.Config { cfg.APIs.HTTP.Server = config.MakeHttpServerConfig() cfg.Clients.GrpcGateway.Connection = config.MakeGrpcClientConfig(config.GRPC_GW_HOST) - cfg.Clients.OpenTelemetryCollector = http.OpenTelemetryCollectorConfig{ + cfg.Clients.OpenTelemetryCollector = pkgHttp.OpenTelemetryCollectorConfig{ Config: config.MakeOpenTelemetryCollectorClient(), } @@ -111,3 +114,16 @@ func GetContentData(content *pb.Content, desiredContentType string) ([]byte, err } return []byte(v), err } + +func UnmarshalJson(code int, input io.Reader, v any) error { + var data json.RawMessage + err := json.NewDecoder(input).Decode(&data) + if err != nil { + return err + } + if code != http.StatusOK { + return UnmarshalError(data) + } + err = json.Unmarshal(data, v) + return err +} diff --git a/http-gateway/test/unmarshalProto.go b/http-gateway/test/unmarshalProto.go index 44e7a49a9..0d4318661 100644 --- a/http-gateway/test/unmarshalProto.go +++ b/http-gateway/test/unmarshalProto.go @@ -51,7 +51,7 @@ func Unmarshal(code int, input io.Reader, v protoreflect.ProtoMessage) error { u := protojson.UnmarshalOptions{ DiscardUnknown: true, } - err := u.Unmarshal(data, v) + err = u.Unmarshal(data, v) if err != nil { return err } diff --git a/http-gateway/uri/uri.go b/http-gateway/uri/uri.go index b20a5e6e8..54e53ab6f 100644 --- a/http-gateway/uri/uri.go +++ b/http-gateway/uri/uri.go @@ -21,6 +21,7 @@ const ( TimestampFilterQueryKey = "timestampFilter" CorrelationIdFilterQueryKey = "correlationIdFilter" ETagQueryKey = "etag" + OnlyContentQueryKey = "onlyContent" AliasInterfaceQueryKey = "interface" AliasCommandFilterQueryKey = "command" @@ -34,11 +35,13 @@ const ( AcceptHeaderKey = "Accept" ETagHeaderKey = "ETag" + DevicesPathKey = "devices" ResourcesPathKey = "resources" ResourceLinksPathKey = "resource-links" PendingCommandsPathKey = "pending-commands" PendingMetadataUpdatesPathKey = "pending-metadata-updates" EventsPathKey = "events" + ThingsPathKey = "things" ApplicationProtoJsonContentType = "application/protojson" @@ -53,6 +56,11 @@ const ( // web configuration for ui WebConfiguration = "/web_configuration.json" + // list devices with thing descriptions + Things = API + "/" + ThingsPathKey + // (HTTP ALIAS) GET /api/v1/things/{deviceId} + AliasDeviceThing = Things + "/{" + DeviceIDKey + "}" + // (GRPC + HTTP) GET /api/v1/devices -> rpc GetDevices // (GRPC + HTTP) DELETE /api/v1/devices -> rpc DeleteDevices Devices = API + "/devices" @@ -133,4 +141,5 @@ var QueryCaseInsensitive = map[string]string{ strings.ToLower(TimestampFilterQueryKey): TimestampFilterQueryKey, strings.ToLower(TimeToLiveQueryKey): TimeToLiveQueryKey, strings.ToLower(CorrelationIdFilterQueryKey): CorrelationIdFilterQueryKey, + strings.ToLower(OnlyContentQueryKey): OnlyContentQueryKey, } diff --git a/http-gateway/web/src/containers/Devices/Detail/DevicesDetailsPage/DevicesDetailsPage.tsx b/http-gateway/web/src/containers/Devices/Detail/DevicesDetailsPage/DevicesDetailsPage.tsx index 222cc871c..4bcc0a468 100644 --- a/http-gateway/web/src/containers/Devices/Detail/DevicesDetailsPage/DevicesDetailsPage.tsx +++ b/http-gateway/web/src/containers/Devices/Detail/DevicesDetailsPage/DevicesDetailsPage.tsx @@ -236,6 +236,7 @@ const DevicesDetailsPage: FC = (props) => { softwareUpdateData={softwareUpdateData?.result?.data?.content} twinSyncLoading={twinSyncLoading} types={data?.types} + endpoints={data?.metadata?.connection?.localEndpoints} /> ), }, diff --git a/http-gateway/web/src/containers/Devices/Detail/DevicesDetailsPage/Tabs/Tab1.tsx b/http-gateway/web/src/containers/Devices/Detail/DevicesDetailsPage/Tabs/Tab1.tsx index 508d0db23..69c45b69b 100644 --- a/http-gateway/web/src/containers/Devices/Detail/DevicesDetailsPage/Tabs/Tab1.tsx +++ b/http-gateway/web/src/containers/Devices/Detail/DevicesDetailsPage/Tabs/Tab1.tsx @@ -21,8 +21,19 @@ import DateFormat from '@/containers/PendingCommands/DateFormat' import testId from '@/testId' const Tab1: FC = (props) => { - const { isTwinEnabled, setTwinSynchronization, twinSyncLoading, deviceId, types, deviceName, model, pendingCommandsData, firmware, softwareUpdateData } = - props + const { + isTwinEnabled, + setTwinSynchronization, + twinSyncLoading, + deviceId, + types, + deviceName, + model, + pendingCommandsData, + firmware, + softwareUpdateData, + endpoints, + } = props const { formatMessage: _ } = useIntl() const resourceRegistrationObservationWSKey = getResourceRegistrationNotificationKey(deviceId) @@ -156,6 +167,22 @@ const Tab1: FC = (props) => { ), }, { attribute: _(t.status), value: pendingCommandsData ? `${pendingCommandsData.length} pending commands` : '-' }, + { + attribute: _(t.endpoints), + value: endpoints ? ( + + {endpoints?.map?.((endpoint: string) => {endpoint})} + + ) : ( +
-
+ ), + }, ]} /> diff --git a/http-gateway/web/src/containers/Devices/Detail/DevicesDetailsPage/Tabs/Tab1.types.ts b/http-gateway/web/src/containers/Devices/Detail/DevicesDetailsPage/Tabs/Tab1.types.ts index a82229bcb..fd020f3ae 100644 --- a/http-gateway/web/src/containers/Devices/Detail/DevicesDetailsPage/Tabs/Tab1.types.ts +++ b/http-gateway/web/src/containers/Devices/Detail/DevicesDetailsPage/Tabs/Tab1.types.ts @@ -20,4 +20,5 @@ export type Props = { purl: string // url with query parameters to update server signed: string // "vendor" } + endpoints?: string[] } diff --git a/http-gateway/web/src/containers/Devices/Devices.i18n.ts b/http-gateway/web/src/containers/Devices/Devices.i18n.ts index f4481918c..b4f4ea3be 100644 --- a/http-gateway/web/src/containers/Devices/Devices.i18n.ts +++ b/http-gateway/web/src/containers/Devices/Devices.i18n.ts @@ -565,4 +565,8 @@ export const messages = defineMessages({ id: 'devices.dps', defaultMessage: 'DPS', }, + endpoints: { + id: 'devices.endpoints', + defaultMessage: 'Endpoints', + }, }) diff --git a/http-gateway/web/src/containers/Devices/Devices.types.ts b/http-gateway/web/src/containers/Devices/Devices.types.ts index aabc69bf6..2621b8bad 100644 --- a/http-gateway/web/src/containers/Devices/Devices.types.ts +++ b/http-gateway/web/src/containers/Devices/Devices.types.ts @@ -26,6 +26,7 @@ export type DeviceDataType = { } connection?: { status?: string + localEndpoints?: string[] } twinEnabled?: boolean } diff --git a/identity-store/client/ownerCache.go b/identity-store/client/ownerCache.go index 7b440afbe..559cb6358 100644 --- a/identity-store/client/ownerCache.go +++ b/identity-store/client/ownerCache.go @@ -186,7 +186,7 @@ func (c *OwnerCache) getOwnerDevices(ctx context.Context, isClient pbIS.Identity if err != nil { return nil, status.Errorf(status.Convert(err).Code(), "cannot receive owners devices: %v", err) } - ownerDevices = append(ownerDevices, device.DeviceId) + ownerDevices = append(ownerDevices, device.GetDeviceId()) } return ownerDevices, nil } @@ -222,13 +222,13 @@ func (c *OwnerCache) Subscribe(owner string, onEvent func(e *events.Event)) (fun closeFunc = c.makeCloseFunc(owner, handlerID) } if s.subscription == nil { - err := s.subscribeLocked(owner, c.conn.Subscribe, func(msg *nats.Msg) { + errS := s.subscribeLocked(owner, c.conn.Subscribe, func(msg *nats.Msg) { if errH := s.Handle(msg); errH != nil { c.errFunc(errH) } }) - if err != nil { - return err + if errS != nil { + return errS } } return nil @@ -252,7 +252,6 @@ func (c *OwnerCache) Update(ctx context.Context) (added []string, removed []stri added, removed, err = s.syncDevicesLocked(ctx, owner, c) return err }) - if err != nil { return nil, nil, err } diff --git a/identity-store/client/ownerCache_test.go b/identity-store/client/ownerCache_test.go index 63e2e37f5..233ea169a 100644 --- a/identity-store/client/ownerCache_test.go +++ b/identity-store/client/ownerCache_test.go @@ -59,7 +59,7 @@ func TestOwnerCacheSubscribe(t *testing.T) { CertFile: cfg.APIs.GRPC.TLS.CertFile, KeyFile: cfg.APIs.GRPC.TLS.KeyFile, }, - }, fileWatcher, log.Get(), noop.NewTracerProvider(), grpc.WithPerRPCCredentials(kitNetGrpc.NewOAuthAccess(func(ctx context.Context) (*oauth2.Token, error) { + }, fileWatcher, log.Get(), noop.NewTracerProvider(), grpc.WithPerRPCCredentials(kitNetGrpc.NewOAuthAccess(func(context.Context) (*oauth2.Token, error) { return &oauth2.Token{ AccessToken: token, TokenType: "Bearer", @@ -127,7 +127,7 @@ func TestOwnerCacheSubscribe(t *testing.T) { time.Sleep(time.Millisecond * 100) for _, d := range devices[:2] { - _, err := c.AddDevice(ctx, &pb.AddDeviceRequest{DeviceId: d}) + _, err = c.AddDevice(ctx, &pb.AddDeviceRequest{DeviceId: d}) require.NoError(t, err) } time.Sleep(time.Millisecond * 100) @@ -171,7 +171,7 @@ func TestOwnerCacheSubscribe(t *testing.T) { DeviceIds: devices[:1], }) require.NoError(t, err) - assert.Equal(t, devices[:1], deleted.DeviceIds) + assert.Equal(t, devices[:1], deleted.GetDeviceIds()) // check update - after expiration time.Sleep(time.Millisecond * 100) // wait for synchronize the cache, so update doesn't change the cache diff --git a/identity-store/persistence/cqldb/persist.go b/identity-store/persistence/cqldb/persist.go index abc2f97b3..c4875c8e2 100644 --- a/identity-store/persistence/cqldb/persist.go +++ b/identity-store/persistence/cqldb/persist.go @@ -137,7 +137,7 @@ func (i *iterator) Close() { func (p *PersistenceTx) Persist(d *persistence.AuthorizedDevice) error { if d == nil { - return fmt.Errorf("cannot persist nil device") + return errors.New("cannot persist nil device") } if p.err != nil { return p.err diff --git a/identity-store/persistence/cqldb/persist_test.go b/identity-store/persistence/cqldb/persist_test.go index 713d10712..4aefa1071 100644 --- a/identity-store/persistence/cqldb/persist_test.go +++ b/identity-store/persistence/cqldb/persist_test.go @@ -11,7 +11,6 @@ import ( "github.com/plgd-dev/hub/v2/pkg/log" "github.com/plgd-dev/hub/v2/test" "github.com/plgd-dev/hub/v2/test/config" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/trace/noop" ) @@ -28,7 +27,7 @@ func newTestPersistence(t *testing.T) *cqldb.Store { Embedded: cfg, Table: "testDeviceOwnership", }, fileWatcher, log.Get(), noop.NewTracerProvider()) - assert.NoError(t, err) + require.NoError(t, err) p.AddCloseFunc(func() { errC := fileWatcher.Close() @@ -98,12 +97,12 @@ func TestPersistenceTxRetrieve(t *testing.T) { t.Run(tt.name, func(t *testing.T) { got, ok, err := tx.Retrieve(tt.deviceID, tt.owner) if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.Equal(t, tt.want, got) - assert.Equal(t, tt.wantOk, ok) + require.Error(t, err) + return } + require.NoError(t, err) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantOk, ok) }) } } @@ -158,12 +157,12 @@ func TestPersistenceTxRetrieveByDevice(t *testing.T) { t.Run(tt.name, func(t *testing.T) { got, ok, err := tx.RetrieveByDevice(tt.deviceID) if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.Equal(t, tt.want, got) - assert.Equal(t, tt.wantOk, ok) + require.Error(t, err) + return } + require.NoError(t, err) + require.Equal(t, tt.want, got) + require.Equal(t, tt.wantOk, ok) }) } } @@ -221,8 +220,8 @@ func TestPersistenceTxRetrieveByOwner(t *testing.T) { iter := tx.RetrieveByOwner(tt.owner) defer iter.Close() func() { - err := iter.Err() - require.NoError(t, err) + errC := iter.Err() + require.NoError(t, errC) }() var retrievedDevices []*persistence.AuthorizedDevice @@ -235,7 +234,7 @@ func TestPersistenceTxRetrieveByOwner(t *testing.T) { retrievedDevices = append(retrievedDevices, &device) } - assert.Equal(t, tt.devices, retrievedDevices) + require.Equal(t, tt.devices, retrievedDevices) }) } } @@ -322,10 +321,10 @@ func TestPersistenceTxPersist(t *testing.T) { } err = tx.Persist(tt.device) if tt.wantErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) + require.Error(t, err) + return } + require.NoError(t, err) }) } } diff --git a/identity-store/persistence/cqldb/store.go b/identity-store/persistence/cqldb/store.go index e4e75e3af..2c73504d9 100644 --- a/identity-store/persistence/cqldb/store.go +++ b/identity-store/persistence/cqldb/store.go @@ -15,7 +15,7 @@ import ( // Document const ( - // cqldbdb has all keys in in lowercase + // cqldbdb has all keys in lowercase ownerKey = "ownerkey" deviceIDKey = "deviceid" ) diff --git a/identity-store/persistence/mongodb/persist.go b/identity-store/persistence/mongodb/persist.go index b968151e0..893562f6c 100644 --- a/identity-store/persistence/mongodb/persist.go +++ b/identity-store/persistence/mongodb/persist.go @@ -211,7 +211,7 @@ func (p *PersistenceTx) Delete(deviceID, userID string) error { return err } if res.DeletedCount == 0 { - return fmt.Errorf("not found") + return errors.New("not found") } if err := p.tx.CommitTransaction(p.ctx); err != nil { return fmt.Errorf("cannot commit transaction: %w", err) diff --git a/identity-store/service/addDevice.go b/identity-store/service/addDevice.go index 46da8cc35..b4e9d72f6 100644 --- a/identity-store/service/addDevice.go +++ b/identity-store/service/addDevice.go @@ -90,13 +90,14 @@ func (s *Service) AddDevice(ctx context.Context, request *pb.AddDeviceRequest) ( return nil, log.LogAndReturnError(status.Errorf(codes.InvalidArgument, "cannot add device: %v", err)) } - if request.DeviceId == "" { + deviceID := request.GetDeviceId() + if deviceID == "" { return nil, log.LogAndReturnError(status.Errorf(codes.InvalidArgument, "cannot add device: invalid DeviceId")) } - dev, ok, err := tx.RetrieveByDevice(request.DeviceId) + dev, ok, err := tx.RetrieveByDevice(deviceID) if err != nil { - return nil, log.LogAndReturnError(status.Errorf(codes.Internal, "cannot add device %v: %v", request.DeviceId, err.Error())) + return nil, log.LogAndReturnError(status.Errorf(codes.Internal, "cannot add device %v: %v", deviceID, err.Error())) } if ok { if dev.Owner == owner { @@ -106,7 +107,7 @@ func (s *Service) AddDevice(ctx context.Context, request *pb.AddDeviceRequest) ( } d := persistence.AuthorizedDevice{ - DeviceID: request.DeviceId, + DeviceID: deviceID, Owner: owner, } @@ -114,7 +115,7 @@ func (s *Service) AddDevice(ctx context.Context, request *pb.AddDeviceRequest) ( return nil, log.LogAndReturnError(status.Errorf(codes.Internal, "cannot add device up: %v", err.Error())) } - s.publishDevicesRegistered(ctx, owner, userID, s.hubID, []string{request.DeviceId}) + s.publishDevicesRegistered(ctx, owner, userID, s.hubID, []string{deviceID}) return &pb.AddDeviceResponse{}, nil } diff --git a/identity-store/service/deleteDevices.go b/identity-store/service/deleteDevices.go index ef9a6193d..1347b7324 100644 --- a/identity-store/service/deleteDevices.go +++ b/identity-store/service/deleteDevices.go @@ -64,14 +64,14 @@ func (s *Service) publishDevicesUnregistered(ctx context.Context, owner, userID, func getDeviceIDs(request *pb.DeleteDevicesRequest, tx persistence.PersistenceTx, owner string) ([]string, error) { var deviceIds []string - if len(request.DeviceIds) == 0 { + if len(request.GetDeviceIds()) == 0 { var err error if deviceIds, err = getOwnerDevices(tx, owner); err != nil { return nil, status.Errorf(codes.InvalidArgument, "cannot delete devices: %v", err) } return deviceIds, nil } - deviceIds = getUniqueDeviceIds(request.DeviceIds) + deviceIds = getUniqueDeviceIds(request.GetDeviceIds()) if len(deviceIds) == 0 { return nil, status.Errorf(codes.InvalidArgument, "cannot delete devices: invalid DeviceIds") } @@ -112,7 +112,7 @@ func (s *Service) DeleteDevices(ctx context.Context, request *pb.DeleteDevicesRe return &pb.DeleteDevicesResponse{}, nil } - var deletedDeviceIDs []string + deletedDeviceIDs := make([]string, 0, len(deviceIDs)) for _, deviceID := range deviceIDs { ok, err := deleteDevice(tx, deviceID, owner) if err != nil { @@ -126,7 +126,9 @@ func (s *Service) DeleteDevices(ctx context.Context, request *pb.DeleteDevicesRe s.publishDevicesUnregistered(ctx, owner, userID, s.hubID, deletedDeviceIDs) - return &pb.DeleteDevicesResponse{ - DeviceIds: deletedDeviceIDs, - }, nil + resp := &pb.DeleteDevicesResponse{} + if len(deletedDeviceIDs) > 0 { + resp.DeviceIds = deletedDeviceIDs + } + return resp, nil } diff --git a/identity-store/service/deleteDevices_test.go b/identity-store/service/deleteDevices_test.go index 6ddddda66..1ee423c75 100644 --- a/identity-store/service/deleteDevices_test.go +++ b/identity-store/service/deleteDevices_test.go @@ -145,8 +145,8 @@ func TestServiceDeleteDevices(t *testing.T) { return } require.NoError(t, err) - sort.Strings(tt.want.DeviceIds) - sort.Strings(got.DeviceIds) + sort.Strings(tt.want.GetDeviceIds()) + sort.Strings(got.GetDeviceIds()) require.Equal(t, tt.want, got) }) } diff --git a/identity-store/service/getDevices_test.go b/identity-store/service/getDevices_test.go index 928fd73c5..1d1544012 100644 --- a/identity-store/service/getDevices_test.go +++ b/identity-store/service/getDevices_test.go @@ -9,7 +9,6 @@ import ( kitNetGrpc "github.com/plgd-dev/hub/v2/pkg/net/grpc" "github.com/plgd-dev/hub/v2/test" "github.com/plgd-dev/hub/v2/test/config" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc" ) @@ -27,13 +26,13 @@ func TestUserDevicesList(t *testing.T) { }() persistDevice(t, s.service.persistence, newTestDevice()) err := s.service.GetDevices(newGetDevicesRequest(), srv) - assert.NoError(t, err) + require.NoError(t, err) r := map[string]*pb.Device{ testDeviceID: { DeviceId: testDeviceID, }, } - assert.Equal(t, r, srv.resourceValues) + require.Equal(t, r, srv.resourceValues) } func TestListingMoreDevices(t *testing.T) { @@ -53,8 +52,7 @@ func TestListingMoreDevices(t *testing.T) { persistDevice(t, s.service.persistence, d) err := s.service.GetDevices(newGetDevicesRequest(), srv) - assert := assert.New(t) - assert.NoError(err) + require.NoError(t, err) r := map[string]*pb.Device{ testDeviceID: { DeviceId: testDeviceID, @@ -63,7 +61,7 @@ func TestListingMoreDevices(t *testing.T) { DeviceId: d.DeviceID, }, } - assert.Equal(r, srv.resourceValues) + require.Equal(t, r, srv.resourceValues) } func newGetDevicesRequest() *pb.GetDevicesRequest { @@ -86,7 +84,7 @@ func (d *mockGeDevicesServer) Send(r *pb.Device) error { if d.resourceValues == nil { d.resourceValues = make(map[string]*pb.Device) } - d.resourceValues[r.DeviceId] = r + d.resourceValues[r.GetDeviceId()] = r return nil } diff --git a/identity-store/service/service_test.go b/identity-store/service/service_test.go index 78d96afff..fdd330898 100644 --- a/identity-store/service/service_test.go +++ b/identity-store/service/service_test.go @@ -15,7 +15,6 @@ import ( natsTest "github.com/plgd-dev/hub/v2/resource-aggregate/cqrs/eventbus/nats/test" "github.com/plgd-dev/hub/v2/test" "github.com/plgd-dev/hub/v2/test/config" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/trace/noop" ) @@ -113,5 +112,5 @@ func persistDevice(t *testing.T, p Persistence, d *persistence.AuthorizedDevice) tx := p.NewTransaction(context.Background()) defer tx.Close() err := tx.Persist(d) - assert.Nil(t, err) + require.NoError(t, err) } diff --git a/pkg/config/database/config.go b/pkg/config/database/config.go index c0eb846bb..f1ecaa699 100644 --- a/pkg/config/database/config.go +++ b/pkg/config/database/config.go @@ -1,6 +1,7 @@ package database import ( + "errors" "fmt" "reflect" "strings" @@ -31,7 +32,7 @@ func (c *Config[MongoConfig, CQLDBConfig]) Validate() error { switch c.Use.ToLower() { case MongoDB.ToLower(): if reflect.ValueOf(c.MongoDB).Kind() == reflect.Ptr && reflect.ValueOf(c.MongoDB).IsNil() { - return fmt.Errorf("mongoDB - is empty") + return errors.New("mongoDB - is empty") } if err := c.MongoDB.Validate(); err != nil { return fmt.Errorf("mongoDB.%w", err) @@ -39,7 +40,7 @@ func (c *Config[MongoConfig, CQLDBConfig]) Validate() error { c.Use = "mongoDB" case CqlDB.ToLower(): if reflect.ValueOf(c.CqlDB).Kind() == reflect.Ptr && reflect.ValueOf(c.CqlDB).IsNil() { - return fmt.Errorf("cqlDB - is empty") + return errors.New("cqlDB - is empty") } if err := c.CqlDB.Validate(); err != nil { return fmt.Errorf("cqlDB.%w", err) diff --git a/pkg/cqldb/cqldb.go b/pkg/cqldb/cqldb.go index 49af196da..b47ab61be 100644 --- a/pkg/cqldb/cqldb.go +++ b/pkg/cqldb/cqldb.go @@ -130,9 +130,9 @@ func New(ctx context.Context, cfg Config, tls *tls.Config, logger log.Logger, _ logger: logger, } - s.onClear = func(c context.Context) error { + s.onClear = func(clearCtx context.Context) error { // default clear function drops the whole database - return s.DropKeyspace(ctx) + return s.DropKeyspace(clearCtx) } return s, nil } diff --git a/pkg/fsnotify/fsnotify_test.go b/pkg/fsnotify/fsnotify_test.go index e2503805b..f5a9f703a 100644 --- a/pkg/fsnotify/fsnotify_test.go +++ b/pkg/fsnotify/fsnotify_test.go @@ -25,7 +25,7 @@ func TestWatcher(t *testing.T) { err = w.Add("/tmp") require.NoError(t, err) - onEventHandler := func(event Event) {} + onEventHandler := func(Event) {} w.AddOnEventHandler(&onEventHandler) w.RemoveOnEventHandler(&onEventHandler) diff --git a/pkg/log/log.go b/pkg/log/log.go index 40d0182cc..c4aaee370 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -293,7 +293,7 @@ var getLogFuncMap = map[zapcore.Level]func(l *WrapSuggarLogger) func(args ...int FatalLevel: func(l *WrapSuggarLogger) func(args ...interface{}) { return l.Fatal }, } -var emptyLogFunc = func(args ...interface{}) { +var emptyLogFunc = func(...interface{}) { // do nothing } diff --git a/pkg/log/logKeys.go b/pkg/log/logKeys.go index aa6335560..0ffe57c60 100644 --- a/pkg/log/logKeys.go +++ b/pkg/log/logKeys.go @@ -29,6 +29,7 @@ var ( MessageKey = "message" SubjectsKey = "subjects" CertManagerKey = "certManager" + LocalEndpointsKey = "localEndpoints" ) func DurationToMilliseconds(duration time.Duration) float32 { diff --git a/pkg/log/log_test.go b/pkg/log/log_test.go index ced4caf0d..0a453ef96 100644 --- a/pkg/log/log_test.go +++ b/pkg/log/log_test.go @@ -3,12 +3,12 @@ package log import ( "crypto/x509" "crypto/x509/pkix" - "fmt" + "errors" "testing" "time" pkgX509 "github.com/plgd-dev/hub/v2/pkg/security/x509" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestNew(t *testing.T) { @@ -17,34 +17,34 @@ func TestNew(t *testing.T) { config := MakeDefaultConfig() Setup(config) - assert.NotPanics(t, func() { Debug(testStr) }) - assert.NotPanics(t, func() { Info(testStr) }) - assert.NotPanics(t, func() { + require.NotPanics(t, func() { Debug(testStr) }) + require.NotPanics(t, func() { Info(testStr) }) + require.NotPanics(t, func() { Info(testStr, pkgX509.NewError([][]*x509.Certificate{{&x509.Certificate{ Subject: pkix.Name{ CommonName: "certName", }, - }}}, fmt.Errorf(" x509"))) + }}}, errors.New(" x509"))) }) - assert.NotPanics(t, func() { Warn(testStr) }) - assert.NotPanics(t, func() { Error(testStr) }) + require.NotPanics(t, func() { Warn(testStr) }) + require.NotPanics(t, func() { Error(testStr) }) - assert.NotPanics(t, func() { Debugf(testStr) }) - assert.NotPanics(t, func() { Infof(testStr) }) - assert.NotPanics(t, func() { Warnf(testStr) }) - assert.NotPanics(t, func() { Errorf(testStr) }) + require.NotPanics(t, func() { Debugf(testStr) }) + require.NotPanics(t, func() { Infof(testStr) }) + require.NotPanics(t, func() { Warnf(testStr) }) + require.NotPanics(t, func() { Errorf(testStr) }) timesStr := []string{"rfc3339nano", "rfc3339", "iso8601", "millis", "nanos", ""} for _, str := range timesStr { var v TimeEncoderWrapper - assert.NoError(t, v.UnmarshalText([]byte(str))) + require.NoError(t, v.UnmarshalText([]byte(str))) text, err := v.MarshalText() - assert.NoError(t, err) - assert.Equal(t, str, string(text)) + require.NoError(t, err) + require.Equal(t, str, string(text)) } - assert.Equal(t, float32(1000), DurationToMilliseconds(time.Second)) - assert.Error(t, LogAndReturnError(fmt.Errorf(testStr))) + require.InEpsilon(t, float32(1000), DurationToMilliseconds(time.Second), 0.1) + require.Error(t, LogAndReturnError(errors.New(testStr))) cfg := MakeDefaultConfig() - assert.NoError(t, cfg.Validate()) + require.NoError(t, cfg.Validate()) } diff --git a/pkg/net/coap/getResourceLinks.go b/pkg/net/coap/getResourceLinks.go new file mode 100644 index 000000000..770a8bb63 --- /dev/null +++ b/pkg/net/coap/getResourceLinks.go @@ -0,0 +1,85 @@ +package coap + +import ( + "context" + "errors" + "fmt" + "net" + + "github.com/plgd-dev/device/v2/schema" + "github.com/plgd-dev/device/v2/schema/device" + "github.com/plgd-dev/device/v2/schema/interfaces" + "github.com/plgd-dev/device/v2/schema/resources" + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/message/pool" + "github.com/plgd-dev/hub/v2/coap-gateway/uri" + "github.com/plgd-dev/kit/v2/codec/cbor" +) + +type ClientConn = interface { + Get(ctx context.Context, path string, opts ...message.Option) (*pool.Message, error) + ReleaseMessage(m *pool.Message) + RemoteAddr() net.Addr +} + +// GetResourceLinks queries the resource links from the given resource. +func GetResourceLinks(ctx context.Context, coapConn ClientConn, href string, opts ...message.Option) (schema.ResourceLinks, uint64, error) { + msg, err := coapConn.Get(ctx, href, opts...) + if err != nil { + return schema.ResourceLinks{}, 0, err + } + defer coapConn.ReleaseMessage(msg) + + if msg.Code() != codes.Content { + return schema.ResourceLinks{}, 0, fmt.Errorf("invalid response code %v", msg.Code()) + } + + data := msg.Body() + if data == nil { + return schema.ResourceLinks{}, 0, errors.New("empty response") + } + + var links schema.ResourceLinks + err = cbor.ReadFrom(msg.Body(), &links) + if err != nil { + return schema.ResourceLinks{}, 0, err + } + + return links, msg.Sequence(), nil +} + +// GetResourceLinksWithLinkInterface query resource links from the given resource with the interface oic.if.ll. +func GetResourceLinksWithLinkInterface(ctx context.Context, coapConn ClientConn, href string) (schema.ResourceLinks, uint64, error) { + return GetResourceLinks(ctx, coapConn, href, message.Option{ + ID: message.URIQuery, + Value: []byte(uri.InterfaceQueryKeyPrefix + interfaces.OC_IF_LL), + }) +} + +// GetEndpointsFromResourceType retrieves the endpoints associated with a specific resource type. +func GetEndpointsFromResourceType(ctx context.Context, coapConn ClientConn, resourceType string) ([]string, error) { + links, _, err := GetResourceLinks(ctx, coapConn, resources.ResourceURI, message.Option{ + ID: message.URIQuery, + Value: []byte(uri.InterfaceQueryKeyPrefix + interfaces.OC_IF_LL), + }, message.Option{ + ID: message.URIQuery, + Value: []byte(uri.ResourceTypeQueryKeyPrefix + resourceType), + }) + if err != nil { + return nil, err + } + if len(links) == 0 { + return nil, errors.New("no local endpoints found") + } + endpoints := make([]string, 0, 8) + for _, ep := range links[0].Endpoints { + endpoints = append(endpoints, ep.URI) + } + return endpoints, nil +} + +// GetEndpointsFromDeviceResource retrieves the endpoints from the device resource. +func GetEndpointsFromDeviceResource(ctx context.Context, coapConn ClientConn) ([]string, error) { + return GetEndpointsFromResourceType(ctx, coapConn, device.ResourceType) +} diff --git a/pkg/net/coap/getResourceLinks_test.go b/pkg/net/coap/getResourceLinks_test.go new file mode 100644 index 000000000..9b951b49a --- /dev/null +++ b/pkg/net/coap/getResourceLinks_test.go @@ -0,0 +1,244 @@ +package coap_test + +import ( + "bytes" + "context" + "net" + "strings" + "testing" + + "github.com/plgd-dev/device/v2/schema" + "github.com/plgd-dev/device/v2/schema/device" + "github.com/plgd-dev/device/v2/schema/resources" + "github.com/plgd-dev/go-coap/v3/message" + "github.com/plgd-dev/go-coap/v3/message/codes" + "github.com/plgd-dev/go-coap/v3/message/pool" + "github.com/plgd-dev/hub/v2/coap-gateway/uri" + "github.com/plgd-dev/hub/v2/pkg/net/coap" + "github.com/plgd-dev/hub/v2/test" + "github.com/stretchr/testify/require" +) + +type testCoapConn struct { + devID string + links schema.ResourceLinks + t *testing.T +} + +func (t *testCoapConn) Get(ctx context.Context, path string, opts ...message.Option) (*pool.Message, error) { + var rtFilter []string + for _, opt := range opts { + if opt.ID == message.URIQuery { + vals := strings.SplitN(string(opt.Value), "=", 2) + if len(vals) != 2 { + continue + } + if vals[0] == uri.ResourceTypeQueryKey { + rtFilter = append(rtFilter, vals[1]) + } + } + } + resp := pool.NewMessage(ctx) + switch path { + case device.ResourceURI: + resp.SetCode(codes.Content) + resp.SetContentFormat(message.AppOcfCbor) + resp.SetBody(bytes.NewReader(test.EncodeToCbor(t.t, device.Device{ + ID: t.devID, + }))) + return resp, nil + case resources.ResourceURI: + links := t.links + if len(rtFilter) > 0 { + links = links.GetResourceLinks(rtFilter...) + } + if len(links) == 0 { + resp.SetCode(codes.BadRequest) + return resp, nil + } + resp.SetCode(codes.Content) + resp.SetContentFormat(message.AppOcfCbor) + resp.SetBody(bytes.NewReader(test.EncodeToCbor(t.t, links))) + return resp, nil + } + resp.SetCode(codes.NotFound) + return resp, nil +} + +func (t *testCoapConn) ReleaseMessage(*pool.Message) { +} + +func (t *testCoapConn) RemoteAddr() net.Addr { + return &net.TCPAddr{} +} + +func TestGetEndpointsFromDeviceResource(t *testing.T) { + type args struct { + ctx context.Context + coapConn coap.ClientConn + } + tests := []struct { + name string + args args + want []string + wantErr bool + }{ + { + name: "valid", + args: args{ + ctx: context.Background(), + coapConn: &testCoapConn{ + t: t, + devID: "dev1", + links: schema.ResourceLinks{ + { + DeviceID: "00000000-0000-0000-0000-000000000001", + Href: device.ResourceURI, + ResourceTypes: []string{device.ResourceType}, + Endpoints: []schema.Endpoint{ + { + URI: "coap://localhost:5683", + }, + { + URI: "coaps://localhost:5684", + }, + }, + }, + }, + }, + }, + want: []string{"coap://localhost:5683", "coaps://localhost:5684"}, + }, + { + name: "rt not found", + args: args{ + ctx: context.Background(), + coapConn: &testCoapConn{ + t: t, + devID: "dev1", + links: schema.ResourceLinks{ + { + DeviceID: "00000000-0000-0000-0000-000000000001", + Href: resources.ResourceURI, + ResourceTypes: []string{resources.ResourceType}, + Endpoints: []schema.Endpoint{ + { + URI: "coap://localhost:5683", + }, + { + URI: "coaps://localhost:5684", + }, + }, + }, + }, + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := coap.GetEndpointsFromDeviceResource(tt.args.ctx, tt.args.coapConn) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +} + +func TestGetResourceLinksWithLinkInterface(t *testing.T) { + type args struct { + ctx context.Context + coapConn coap.ClientConn + href string + } + tests := []struct { + name string + args args + want schema.ResourceLinks + wantErr bool + }{ + { + name: "valid", + args: args{ + ctx: context.Background(), + href: resources.ResourceURI, + coapConn: &testCoapConn{ + t: t, + devID: "dev1", + links: schema.ResourceLinks{ + { + DeviceID: "00000000-0000-0000-0000-000000000001", + Href: device.ResourceURI, + ResourceTypes: []string{device.ResourceType}, + Endpoints: []schema.Endpoint{ + { + URI: "coap://localhost:5683", + }, + { + URI: "coaps://localhost:5684", + }, + }, + }, + }, + }, + }, + want: schema.ResourceLinks{ + { + DeviceID: "00000000-0000-0000-0000-000000000001", + Href: device.ResourceURI, + ResourceTypes: []string{device.ResourceType}, + Endpoints: []schema.Endpoint{ + { + URI: "coap://localhost:5683", + }, + { + URI: "coaps://localhost:5684", + }, + }, + }, + }, + }, + { + name: "rt not found", + args: args{ + ctx: context.Background(), + href: device.ResourceURI, + coapConn: &testCoapConn{ + t: t, + devID: "dev1", + links: schema.ResourceLinks{ + { + DeviceID: "00000000-0000-0000-0000-000000000001", + Href: resources.ResourceURI, + ResourceTypes: []string{resources.ResourceType}, + Endpoints: []schema.Endpoint{ + { + URI: "coap://localhost:5683", + }, + { + URI: "coaps://localhost:5684", + }, + }, + }, + }, + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, _, err := coap.GetResourceLinksWithLinkInterface(tt.args.ctx, tt.args.coapConn, tt.args.href) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +} diff --git a/pkg/net/coap/service/config.go b/pkg/net/coap/service/config.go index 59eeee3ac..eeae3dfd4 100644 --- a/pkg/net/coap/service/config.go +++ b/pkg/net/coap/service/config.go @@ -1,6 +1,7 @@ package service import ( + "errors" "fmt" "strings" "time" @@ -48,7 +49,7 @@ func (c *Config) validateKeepAliveAndInactivityMonitor() error { } } if c.KeepAlive == nil && c.InactivityMonitor == nil { - return fmt.Errorf("keepAlive or inactivityMonitor must be set") + return errors.New("keepAlive or inactivityMonitor must be set") } return nil } diff --git a/pkg/net/coap/service/service_test.go b/pkg/net/coap/service/service_test.go index 5cfdafbb2..93d560629 100644 --- a/pkg/net/coap/service/service_test.go +++ b/pkg/net/coap/service/service_test.go @@ -16,6 +16,7 @@ import ( "github.com/plgd-dev/hub/v2/pkg/log" "github.com/plgd-dev/hub/v2/pkg/service" "github.com/plgd-dev/hub/v2/test/config" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/atomic" ) @@ -60,8 +61,8 @@ func TestNew(t *testing.T) { }, options: []func(*Options){ WithMessagePool(pool.New(uint32(1024), 1024)), - WithOnNewConnection(func(conn mux.Conn) {}), - WithOnInactivityConnection(func(conn mux.Conn) {}), + WithOnNewConnection(func(mux.Conn) {}), + WithOnInactivityConnection(func(mux.Conn) {}), WithOverrideTLS(func(cfg *tls.Config) *tls.Config { return cfg }), }, }, @@ -87,8 +88,8 @@ func TestNew(t *testing.T) { }, options: []func(*Options){ WithMessagePool(pool.New(uint32(1024), 1024)), - WithOnNewConnection(func(conn mux.Conn) {}), - WithOnInactivityConnection(func(conn mux.Conn) {}), + WithOnNewConnection(func(mux.Conn) {}), + WithOnInactivityConnection(func(mux.Conn) {}), WithOverrideTLS(func(cfg *tls.Config) *tls.Config { return cfg }), }, }, @@ -107,8 +108,8 @@ func TestNew(t *testing.T) { } require.NoError(t, err) go func() { - err := got.Serve() - require.NoError(t, err) + errS := got.Serve() + assert.NoError(t, errS) }() err = got.Close() require.NoError(t, err) @@ -152,8 +153,8 @@ func TestOnClientInactivityTCP(t *testing.T) { })) require.NoError(t, err) go func() { - err := got.Serve() - require.NoError(t, err) + errS := got.Serve() + assert.NoError(t, errS) }() time.Sleep(time.Second * 3) @@ -217,8 +218,8 @@ func TestOnClientInactivityUDP(t *testing.T) { })) require.NoError(t, err) go func() { - err := got.Serve() - require.NoError(t, err) + errS := got.Serve() + assert.NoError(t, errS) }() time.Sleep(time.Second * 3) @@ -278,8 +279,8 @@ func TestOnClientInactivityCustomTCP(t *testing.T) { closeChan := make(chan struct{}, 2) got, err := New(ctx, cfg, router, fileWatcher, logger, WithOnInactivityConnection(func(conn mux.Conn) { numInactiveClients.Inc() - err := conn.Close() - require.NoError(t, err) + errC := conn.Close() + require.NoError(t, errC) }), WithOnNewConnection(func(conn mux.Conn) { conn.AddOnClose(func() { closeChan <- struct{}{} @@ -287,8 +288,8 @@ func TestOnClientInactivityCustomTCP(t *testing.T) { })) require.NoError(t, err) go func() { - err := got.Serve() - require.NoError(t, err) + errS := got.Serve() + assert.NoError(t, errS) }() time.Sleep(time.Second * 3) @@ -349,8 +350,8 @@ func TestOnClientInactivityCustomUDP(t *testing.T) { closeChan := make(chan struct{}, 2) got, err := New(ctx, cfg, router, fileWatcher, logger, WithOnInactivityConnection(func(conn mux.Conn) { numInactiveClients.Inc() - err := conn.Close() - require.NoError(t, err) + errC := conn.Close() + require.NoError(t, errC) }), WithOnNewConnection(func(conn mux.Conn) { conn.AddOnClose(func() { closeChan <- struct{}{} @@ -358,8 +359,8 @@ func TestOnClientInactivityCustomUDP(t *testing.T) { })) require.NoError(t, err) go func() { - err := got.Serve() - require.NoError(t, err) + errS := got.Serve() + assert.NoError(t, errS) }() time.Sleep(time.Second * 3) diff --git a/pkg/net/grpc/error_test.go b/pkg/net/grpc/error_test.go index 50cbfc77c..8366fd3c1 100644 --- a/pkg/net/grpc/error_test.go +++ b/pkg/net/grpc/error_test.go @@ -97,7 +97,7 @@ func TestForwardErrorf(t *testing.T) { s, ok := status.FromError(err) require.True(t, ok) assert.Equal(t, tt.wantCode, s.Code()) - test.CheckProtobufs(t, tt.wantDetails, s.Details(), test.AssertToCheckFunc(assert.Equal)) + test.CheckProtobufs(t, tt.wantDetails, s.Details(), test.RequireToCheckFunc(require.Equal)) assert.Equal(t, tt.wantMessage, s.Message()) }) } diff --git a/pkg/net/grpc/server/makeDefaultOptions.go b/pkg/net/grpc/server/makeDefaultOptions.go index 667b94934..868ac95d2 100644 --- a/pkg/net/grpc/server/makeDefaultOptions.go +++ b/pkg/net/grpc/server/makeDefaultOptions.go @@ -251,7 +251,7 @@ func MakeDefaultOptions(auth pkgGrpc.AuthInterceptors, logger log.Logger, tracer }, } unaryInterceptors := []grpc.UnaryServerInterceptor{ - func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { + func(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { setGrpcRequest(ctx, req) return handler(ctx, req) }, @@ -298,7 +298,7 @@ func WithWhiteListedMethods(method ...string) Option { } func NewAuth(validator pkgGrpc.Validator, opts ...Option) pkgGrpc.AuthInterceptors { - interceptor := pkgGrpc.ValidateJWTWithValidator(validator, func(ctx context.Context, method string) jwt.ClaimsValidator { + interceptor := pkgGrpc.ValidateJWTWithValidator(validator, func(context.Context, string) jwt.ClaimsValidator { return pkgJwt.NewScopeClaims() }) var cfg config diff --git a/pkg/net/grpc/stub_test.go b/pkg/net/grpc/stub_test.go index 039d4f60c..ea2575ff2 100644 --- a/pkg/net/grpc/stub_test.go +++ b/pkg/net/grpc/stub_test.go @@ -17,7 +17,7 @@ func StubGrpcServer(opts ...grpc.ServerOption) *server.Server { } func StubGrpcClient(addr string) StubServiceClient { - conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) + conn, err := grpc.NewClient(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { panic(err) } diff --git a/pkg/net/http/convertError_test.go b/pkg/net/http/convertError_test.go index b1194c801..10c6a9fea 100644 --- a/pkg/net/http/convertError_test.go +++ b/pkg/net/http/convertError_test.go @@ -2,6 +2,7 @@ package http import ( "context" + "errors" "fmt" "net/http" "testing" @@ -42,11 +43,11 @@ func TestErrToStatus(t *testing.T) { { name: "coap", args: args{ - err: coapStatus.Error(forbidden, fmt.Errorf("coap error")), + err: coapStatus.Error(forbidden, errors.New("coap error")), }, want: http.StatusForbidden, }, - {name: "grpc", args: args{err: fmt.Errorf("unknown error")}, want: http.StatusInternalServerError}, + {name: "grpc", args: args{err: errors.New("unknown error")}, want: http.StatusInternalServerError}, {name: "sdkError", args: args{err: SdkError{errorCode: codes.PermissionDenied}}, want: http.StatusForbidden}, } for _, tt := range tests { diff --git a/pkg/net/http/interceptor.go b/pkg/net/http/interceptor.go index 37125b0b5..41b27d629 100644 --- a/pkg/net/http/interceptor.go +++ b/pkg/net/http/interceptor.go @@ -46,7 +46,7 @@ func (c DeniedClaims) Validate() error { } func MakeClaimsFunc(methods map[string][]AuthArgs) ClaimsFunc { - return func(ctx context.Context, method, uri string) jwt.ClaimsValidator { + return func(_ context.Context, method, uri string) jwt.ClaimsValidator { args, ok := methods[method] if !ok { return &DeniedClaims{Err: fmt.Errorf("inaccessible method: %v", method)} diff --git a/pkg/net/http/loggingMiddleware.go b/pkg/net/http/loggingMiddleware.go index a6a5761ef..19c592514 100644 --- a/pkg/net/http/loggingMiddleware.go +++ b/pkg/net/http/loggingMiddleware.go @@ -2,6 +2,7 @@ package http import ( "bufio" + "errors" "fmt" "net" "net/http" @@ -58,7 +59,7 @@ func (w *statusWriter) Write(b []byte) (int, error) { func (w *statusWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { writer, ok := w.ResponseWriter.(http.Hijacker) if !ok { - return nil, nil, fmt.Errorf("not supported by the underlying writer") + return nil, nil, errors.New("not supported by the underlying writer") } return writer.Hijack() } @@ -70,7 +71,7 @@ func (w *statusWriter) Flush() { } } -var toNil = func(args ...interface{}) { +var toNil = func(...interface{}) { // Do nothing because we don't want to log anything } diff --git a/pkg/net/http/service/config.go b/pkg/net/http/service/config.go index 76af609f4..cdde59691 100644 --- a/pkg/net/http/service/config.go +++ b/pkg/net/http/service/config.go @@ -1,6 +1,7 @@ package http import ( + "errors" "fmt" "github.com/plgd-dev/hub/v2/pkg/config" @@ -32,19 +33,19 @@ func (c *Config) Validate() error { return fmt.Errorf("HTTPConnection.%w", err) } if c.TraceProvider == nil { - return fmt.Errorf("traceProvider is required") + return errors.New("traceProvider is required") } if c.AuthRules == nil { - return fmt.Errorf("authRules is required") + return errors.New("authRules is required") } if c.FileWatcher == nil { - return fmt.Errorf("fileWatcher is required") + return errors.New("fileWatcher is required") } if c.Logger == nil { - return fmt.Errorf("logger is required") + return errors.New("logger is required") } if c.ServiceName == "" { - return fmt.Errorf("serviceName is required") + return errors.New("serviceName is required") } return nil diff --git a/pkg/opentelemetry/otelcoap/opentelemetry.go b/pkg/opentelemetry/otelcoap/opentelemetry.go index 6057bf73f..cbe67c104 100644 --- a/pkg/opentelemetry/otelcoap/opentelemetry.go +++ b/pkg/opentelemetry/otelcoap/opentelemetry.go @@ -109,5 +109,5 @@ func Start(ctx context.Context, path, method string, opts ...Option) (context.Co spanOpts = append(spanOpts, cfg.SpanStartOptions...) } - return tracer.Start(ctx, DefaultTransportFormatter(path), spanOpts...) + return tracer.Start(ctx, DefaultTransportFormatter(path), spanOpts...) //nolint:spancheck } diff --git a/pkg/security/certManager/client/certManager.go b/pkg/security/certManager/client/certManager.go index 8e550ee30..2ca175623 100644 --- a/pkg/security/certManager/client/certManager.go +++ b/pkg/security/certManager/client/certManager.go @@ -2,6 +2,7 @@ package client import ( "crypto/tls" + "errors" "fmt" "github.com/plgd-dev/hub/v2/pkg/config/property/urischeme" @@ -42,7 +43,7 @@ func (c *Config) Validate() error { func (c *Config) CAPoolArray() ([]urischeme.URIScheme, error) { if !c.validated { - return nil, fmt.Errorf("call Validate() first") + return nil, errors.New("call Validate() first") } return c.caPoolArray, nil } diff --git a/pkg/security/certManager/general/certManager.go b/pkg/security/certManager/general/certManager.go index b249ffab4..637e63358 100644 --- a/pkg/security/certManager/general/certManager.go +++ b/pkg/security/certManager/general/certManager.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "crypto/tls" "crypto/x509" + "errors" "fmt" "strings" "sync" @@ -191,7 +192,7 @@ func (a *CertManager) getTLSKeyPair() (*tls.Certificate, error) { } } if a.private.tlsKeyPair == nil { - return nil, fmt.Errorf("certificate is not loaded") + return nil, errors.New("certificate is not loaded") } return a.private.tlsKeyPair, nil diff --git a/pkg/security/certManager/general/certManager_test.go b/pkg/security/certManager/general/certManager_test.go index 433501cbd..3101e880b 100644 --- a/pkg/security/certManager/general/certManager_test.go +++ b/pkg/security/certManager/general/certManager_test.go @@ -178,18 +178,18 @@ func TestCertManagerWithExpiredCA(t *testing.T) { defer mng.Close() pool := mng.GetCertificateAuthorities() require.NotNil(t, pool) - require.Equal(t, 1, len(pool.Subjects())) //nolint:staticcheck + require.Len(t, pool.Subjects(), 1) //nolint:staticcheck time.Sleep(time.Second * 2) pool = mng.GetCertificateAuthorities() require.NotNil(t, pool) - require.Equal(t, 0, len(pool.Subjects())) //nolint:staticcheck + require.Empty(t, pool.Subjects()) //nolint:staticcheck caPem, _ = getCA(t, time.Now(), time.Second*100) err = os.WriteFile(caFile.Name(), caPem, os.FileMode(os.O_RDWR)) require.NoError(t, err) time.Sleep(time.Second * 1) pool = mng.GetCertificateAuthorities() require.NotNil(t, pool) - require.Equal(t, 1, len(pool.Subjects())) //nolint:staticcheck + require.Len(t, pool.Subjects(), 1) //nolint:staticcheck } // Check when cert expires diff --git a/pkg/security/certManager/server/certManager.go b/pkg/security/certManager/server/certManager.go index dd3a3fbb6..953d35cbb 100644 --- a/pkg/security/certManager/server/certManager.go +++ b/pkg/security/certManager/server/certManager.go @@ -2,6 +2,7 @@ package server import ( "crypto/tls" + "errors" "fmt" "github.com/plgd-dev/hub/v2/pkg/config/property/urischeme" @@ -43,7 +44,7 @@ func (c *Config) Validate() error { func (c *Config) CAPoolArray() ([]urischeme.URIScheme, error) { if !c.validated { - return nil, fmt.Errorf("call Validate() first") + return nil, errors.New("call Validate() first") } return c.caPoolArray, nil } diff --git a/pkg/security/certificateSigner/certificateSigner.go b/pkg/security/certificateSigner/certificateSigner.go index 2620e5e83..707588448 100644 --- a/pkg/security/certificateSigner/certificateSigner.go +++ b/pkg/security/certificateSigner/certificateSigner.go @@ -6,7 +6,7 @@ import ( "crypto/rand" "crypto/x509" "encoding/pem" - "fmt" + "errors" "math/big" "time" @@ -58,11 +58,11 @@ func New(caCert []*x509.Certificate, caKey crypto.PrivateKey, opts ...Opt) *Cert func (s *CertificateSigner) Sign(_ context.Context, csr []byte) ([]byte, error) { if len(s.caCert) == 0 { - return nil, fmt.Errorf("cannot sign with empty signer CA certificates") + return nil, errors.New("cannot sign with empty signer CA certificates") } csrBlock, _ := pem.Decode(csr) if csrBlock == nil { - return nil, fmt.Errorf("pem not found") + return nil, errors.New("pem not found") } certificateRequest, err := x509.ParseCertificateRequest(csrBlock.Bytes) @@ -106,7 +106,7 @@ func (s *CertificateSigner) Sign(_ context.Context, csr []byte) ([]byte, error) ExtraExtensions: certificateRequest.Extensions, } if s.cfg.OverrideCertTemplate != nil { - if err := s.cfg.OverrideCertTemplate(&template); err != nil { + if err = s.cfg.OverrideCertTemplate(&template); err != nil { return nil, err } } diff --git a/pkg/security/jwt/claims_test.go b/pkg/security/jwt/claims_test.go index db045c83a..17398991a 100644 --- a/pkg/security/jwt/claims_test.go +++ b/pkg/security/jwt/claims_test.go @@ -132,7 +132,7 @@ func TestAudienceOfOne(t *testing.T) { c[pkgJwt.ClaimAudience] = "test" aud, err := c.GetAudience() require.NoError(t, err) - require.Equal(t, []string(aud), []string{c[pkgJwt.ClaimAudience].(string)}) + require.Equal(t, []string{c[pkgJwt.ClaimAudience].(string)}, []string(aud)) } func TestAudienceOfTwo(t *testing.T) { @@ -467,7 +467,7 @@ func TestParseToken(t *testing.T) { func checkClaims(t *testing.T, tokenClaims jwt.Claims, expError error) { token := config.CreateJwtToken(t, tokenClaims) - _, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) { + _, err := jwt.Parse(token, func(*jwt.Token) (interface{}, error) { return jwt.VerificationKeySet{ Keys: []jwt.VerificationKey{ []uint8(config.JWTSecret), @@ -482,7 +482,6 @@ func checkClaims(t *testing.T, tokenClaims jwt.Claims, expError error) { } func TestValidate(t *testing.T) { - now := time.Now() expiredTime := now.Add(-time.Hour) futureTime := now.Add(time.Hour) diff --git a/pkg/security/jwt/jwk.go b/pkg/security/jwt/jwk.go index d7a87eb16..054c6a390 100644 --- a/pkg/security/jwt/jwk.go +++ b/pkg/security/jwt/jwk.go @@ -2,6 +2,7 @@ package jwt import ( "context" + "errors" "fmt" "net/http" "sync" @@ -53,21 +54,21 @@ func (c *KeyCache) GetKey(token *jwt.Token) (interface{}, error) { func (c *KeyCache) LookupKey(token *jwt.Token) (jwk.Key, error) { id, ok := token.Header["kid"].(string) if !ok { - return nil, fmt.Errorf("missing key id in token") + return nil, errors.New("missing key id in token") } c.m.Lock() defer c.m.Unlock() if c.keys == nil { - return nil, fmt.Errorf("empty JWK cache") + return nil, errors.New("empty JWK cache") } if key, ok := c.keys.LookupKeyID(id); ok { if key.Algorithm().String() == token.Method.Alg() { return key, nil } } - return nil, fmt.Errorf("could not find JWK") + return nil, errors.New("could not find JWK") } func (c *KeyCache) FetchKeys() error { diff --git a/pkg/security/jwt/scopeClaims_test.go b/pkg/security/jwt/scopeClaims_test.go index 2912cc08d..f41adef4d 100644 --- a/pkg/security/jwt/scopeClaims_test.go +++ b/pkg/security/jwt/scopeClaims_test.go @@ -61,7 +61,7 @@ func TestScopeClaimsInvalidScope(t *testing.T) { func checkScopedClaims(t *testing.T, tokenClaims jwt.Claims, expError error) { token := config.CreateJwtToken(t, tokenClaims) - _, err := jwt.ParseWithClaims(token, &pkgJwt.ScopeClaims{}, func(t *jwt.Token) (interface{}, error) { + _, err := jwt.ParseWithClaims(token, &pkgJwt.ScopeClaims{}, func(*jwt.Token) (interface{}, error) { return jwt.VerificationKeySet{ Keys: []jwt.VerificationKey{ []uint8(config.JWTSecret), diff --git a/pkg/security/jwt/validator_test.go b/pkg/security/jwt/validator_test.go index 59ca74c91..baef741e4 100644 --- a/pkg/security/jwt/validator_test.go +++ b/pkg/security/jwt/validator_test.go @@ -67,36 +67,36 @@ func TestClaims(t *testing.T) { require.ErrorIs(t, err, jwt.ErrTokenExpired) clientID, err := c.GetClientID() - assert.NoError(t, err) - assert.Equal(t, "test.client.id", clientID) + require.NoError(t, err) + require.Equal(t, "test.client.id", clientID) email, err := c.GetEmail() - assert.NoError(t, err) - assert.Equal(t, "user@example.com", email) + require.NoError(t, err) + require.Equal(t, "user@example.com", email) scope, err := c.GetScope() - assert.Contains(t, scope, "test.scope") - assert.NoError(t, err) + require.Contains(t, scope, "test.scope") + require.NoError(t, err) audience, err := c.GetAudience() - assert.NoError(t, err) - assert.Contains(t, audience, "http://identity-server:3001/resources") - assert.Contains(t, audience, "test.resource") + require.NoError(t, err) + require.Contains(t, audience, "http://identity-server:3001/resources") + require.Contains(t, audience, "test.resource") exp, err := c.GetExpirationTime() - assert.NoError(t, err) - assert.Equal(t, 2019, exp.Year()) + require.NoError(t, err) + require.Equal(t, 2019, exp.Year()) id, err := c.GetID() - assert.NoError(t, err) - assert.Empty(t, id) + require.NoError(t, err) + require.Empty(t, id) iat, err := c.GetIssuedAt() - assert.NoError(t, err) - assert.Nil(t, iat) + require.NoError(t, err) + require.Nil(t, iat) iss, err := c.GetIssuer() - assert.NoError(t, err) - assert.Equal(t, iss, "http://identity-server:3001") + require.NoError(t, err) + require.Equal(t, "http://identity-server:3001", iss) nbf, err := c.GetNotBefore() - assert.NoError(t, err) - assert.Equal(t, 2019, nbf.Year()) + require.NoError(t, err) + require.Equal(t, 2019, nbf.Year()) sub, err := c.GetSubject() - assert.NoError(t, err) - assert.Equal(t, "1b87effa-34e2-4a44-82c6-6e0ab80209ff", sub) + require.NoError(t, err) + require.Equal(t, "1b87effa-34e2-4a44-82c6-6e0ab80209ff", sub) } func TestParser(t *testing.T) { diff --git a/pkg/security/oauth2/plgd_clientCrendetials.go b/pkg/security/oauth2/plgd_clientCrendetials.go index 2df542aa2..5a18a7df3 100644 --- a/pkg/security/oauth2/plgd_clientCrendetials.go +++ b/pkg/security/oauth2/plgd_clientCrendetials.go @@ -42,9 +42,9 @@ func (p *ClientCredentialsPlgdProvider) Exchange(ctx context.Context, authorizat m := pkgJwt.Claims(claims) c := p.Config.ToDefaultClientCredentials() if p.deviceIDClaim != "" { - deviceID, err := m.GetDeviceID(p.deviceIDClaim) - if err != nil { - return nil, fmt.Errorf("cannot get deviceIDClaim: %w", err) + deviceID, errG := m.GetDeviceID(p.deviceIDClaim) + if errG != nil { + return nil, fmt.Errorf("cannot get deviceIDClaim: %w", errG) } if deviceID == "" { return nil, fmt.Errorf("deviceIDClaim('%v') is not set in token", p.deviceIDClaim) diff --git a/pkg/security/x509/load.go b/pkg/security/x509/load.go index bd5bf03bd..c76dc905c 100644 --- a/pkg/security/x509/load.go +++ b/pkg/security/x509/load.go @@ -5,7 +5,6 @@ import ( "crypto/x509" "encoding/pem" "errors" - "fmt" "os" ) @@ -25,7 +24,7 @@ func ParseX509(pemBlock []byte) ([]*x509.Certificate, error) { for { certDERBlock, tmp := pem.Decode(data) if certDERBlock == nil { - return nil, fmt.Errorf("cannot decode pem block") + return nil, errors.New("cannot decode pem block") } certs, err := x509.ParseCertificates(certDERBlock.Bytes) if err != nil { @@ -44,7 +43,7 @@ func ParseX509(pemBlock []byte) ([]*x509.Certificate, error) { func ParsePrivateKey(pemBlock []byte) (*ecdsa.PrivateKey, error) { certDERBlock, _ := pem.Decode(pemBlock) if certDERBlock == nil { - return nil, fmt.Errorf("cannot decode pem block") + return nil, errors.New("cannot decode pem block") } if key, err := x509.ParsePKCS8PrivateKey(certDERBlock.Bytes); err == nil { diff --git a/pkg/security/x509/verify.go b/pkg/security/x509/verify.go index c1aaf0f29..218866e8d 100644 --- a/pkg/security/x509/verify.go +++ b/pkg/security/x509/verify.go @@ -3,7 +3,7 @@ package x509 import ( "bytes" "crypto/x509" - "fmt" + "errors" ) func IsRootCA(cert *x509.Certificate) bool { @@ -29,10 +29,10 @@ func setCAPools(roots *x509.CertPool, intermediates *x509.CertPool, certs []*x50 // Verify verifies certificate against certificate authorities. func Verify(certificates []*x509.Certificate, certificateAuthorities []*x509.Certificate, useSystemRoots bool, opts x509.VerifyOptions) ([][]*x509.Certificate, error) { if len(certificates) == 0 { - return nil, fmt.Errorf("at least one certificate need to be set") + return nil, errors.New("at least one certificate need to be set") } if len(certificateAuthorities) == 0 { - return nil, fmt.Errorf("at least one certificate authority need to be set") + return nil, errors.New("at least one certificate authority need to be set") } intermediateCA := x509.NewCertPool() rootCA := x509.NewCertPool() diff --git a/pkg/service/service.go b/pkg/service/service.go index cb9f5c17c..3d58cbb64 100644 --- a/pkg/service/service.go +++ b/pkg/service/service.go @@ -1,7 +1,7 @@ package service import ( - "fmt" + "errors" "os" "os/signal" "sync" @@ -40,7 +40,7 @@ func (s *Service) Add(services ...APIService) { func (s *Service) Serve() error { if !s.serving.CompareAndSwap(false, true) { - return fmt.Errorf("already serving") + return errors.New("already serving") } defer close(s.done) var wg sync.WaitGroup diff --git a/pkg/strings/slice_test.go b/pkg/strings/slice_test.go index cc5ce265f..6fee31e9f 100644 --- a/pkg/strings/slice_test.go +++ b/pkg/strings/slice_test.go @@ -65,7 +65,7 @@ func TestIntersection(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := Intersection(tt.args.s1, tt.args.s2) - require.Equal(t, got, tt.want) + require.Equal(t, tt.want, got) }) } } @@ -121,8 +121,8 @@ func TestSplit(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotTrue, gotFalse := Split(tt.args.s, tt.args.f) - require.Equal(t, gotTrue, tt.wantTrue) - require.Equal(t, gotFalse, tt.wantFalse) + require.Equal(t, tt.wantTrue, gotTrue) + require.Equal(t, tt.wantFalse, gotFalse) }) } } @@ -168,7 +168,7 @@ func TestUnique(t *testing.T) { got := Unique(tt.args.s) sort.Strings(got) sort.Strings(tt.want) - require.Equal(t, got, tt.want) + require.Equal(t, tt.want, got) }) } } @@ -211,7 +211,7 @@ func TestContains(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := Contains(tt.args.slice, tt.args.s) - require.Equal(t, got, tt.want) + require.Equal(t, tt.want, got) }) } } diff --git a/pkg/strings/sortedSlice_test.go b/pkg/strings/sortedSlice_test.go index b7e192593..823a9f19b 100644 --- a/pkg/strings/sortedSlice_test.go +++ b/pkg/strings/sortedSlice_test.go @@ -335,7 +335,7 @@ func TestSortedSlice_Intersection(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.slice.Intersection(tt.args.second) - require.Equal(t, got, tt.want) + require.Equal(t, tt.want, got) }) } } @@ -392,7 +392,7 @@ func TestSortedSlice_Equal(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.slice.Equal(tt.args.second) - require.Equal(t, got, tt.want) + require.Equal(t, tt.want, got) }) } } @@ -473,7 +473,7 @@ func TestSortedSlice_IsSuperslice(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.slice.IsSuperslice(tt.args.s) - require.Equal(t, got, tt.want) + require.Equal(t, tt.want, got) }) } } diff --git a/pkg/strings/unescape.go b/pkg/strings/unescape.go new file mode 100644 index 000000000..5e879347d --- /dev/null +++ b/pkg/strings/unescape.go @@ -0,0 +1,147 @@ +package strings + +import ( + "strconv" + "strings" +) + +type MalformedSequenceError string + +func (e MalformedSequenceError) Error() string { + return "malformed path escape " + strconv.Quote(string(e)) +} + +// UnescapingMode defines the behavior of ServeMux when unescaping path parameters. +type UnescapingMode int + +const ( + // UnescapingModeAllExceptReserved unescapes all path parameters except RFC 6570 + // reserved characters. + UnescapingModeAllExceptReserved UnescapingMode = 1 + + // UnescapingModeAllExceptSlash unescapes URL path parameters except path + // separators, which will be left as "%2F". + UnescapingModeAllExceptSlash UnescapingMode = 2 + + // UnescapingModeAllCharacters unescapes all URL path parameters. + UnescapingModeAllCharacters UnescapingMode = 3 +) + +/* + * The following code is adopted and modified from Go's standard library + * and carries the attached license. + * + * Copyright 2009 The Go Authors. All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +// isHex returns whether or not the given byte is a valid hex character +func isHex(c byte) bool { + switch { + case '0' <= c && c <= '9': + return true + case 'a' <= c && c <= 'f': + return true + case 'A' <= c && c <= 'F': + return true + } + return false +} + +func isRFC6570Reserved(c byte) bool { + switch c { + case '!', '#', '$', '&', '\'', '(', ')', '*', + '+', ',', '/', ':', ';', '=', '?', '@', '[', ']': + return true + default: + return false + } +} + +// unHex converts a hex point to the bit representation +func unHex(c byte) byte { + switch { + case '0' <= c && c <= '9': + return c - '0' + case 'a' <= c && c <= 'f': + return c - 'a' + 10 + case 'A' <= c && c <= 'F': + return c - 'A' + 10 + } + return 0 +} + +// shouldUnescapeWithMode returns true if the character is escapable with the +// given mode +func shouldUnescapeWithMode(c byte, mode UnescapingMode) bool { + switch mode { + case UnescapingModeAllExceptReserved: + if isRFC6570Reserved(c) { + return false + } + case UnescapingModeAllExceptSlash: + if c == '/' { + return false + } + case UnescapingModeAllCharacters: + return true + } + return true +} + +// checkWellFormed checks if the given string is well-formed +func checkWellFormed(s string) (bool, error) { + n := 0 + for i := 0; i < len(s); { + if s[i] != '%' { + i++ + continue + } + n++ + if i+2 >= len(s) || !isHex(s[i+1]) || !isHex(s[i+2]) { + s = s[i:] + if len(s) > 3 { + s = s[:3] + } + return false, MalformedSequenceError(s) + } + i += 3 + } + return n != 0, nil +} + +// Unescape unescapes a path string using the provided mode +func Unescape(s string, mode UnescapingMode, multisegment bool) (string, error) { + if !multisegment { + mode = UnescapingModeAllCharacters + } + + // Count %, check that they're well-formed. + needEscape, err := checkWellFormed(s) + if err != nil { + return "", err + } + if !needEscape { + return s, nil + } + + var t strings.Builder + t.Grow(len(s)) + for i := 0; i < len(s); i++ { + switch s[i] { + case '%': + c := unHex(s[i+1])<<4 | unHex(s[i+2]) + if shouldUnescapeWithMode(c, mode) { + t.WriteByte(c) + i += 2 + continue + } + fallthrough + default: + t.WriteByte(s[i]) + } + } + + return t.String(), nil +} diff --git a/pkg/strings/unescape_internal_test.go b/pkg/strings/unescape_internal_test.go new file mode 100644 index 000000000..b3b4c093d --- /dev/null +++ b/pkg/strings/unescape_internal_test.go @@ -0,0 +1,84 @@ +package strings_test + +import ( + "testing" + + "github.com/plgd-dev/hub/v2/pkg/strings" + "github.com/stretchr/testify/require" +) + +func TestUnescape(t *testing.T) { + tbl := []struct { + name string + input string + mode strings.UnescapingMode + multisegment bool + expected string + expectedErr error + }{ + { + name: "No escaping required", + input: "hello world", + mode: strings.UnescapingModeAllCharacters, + multisegment: true, + expected: "hello world", + expectedErr: nil, + }, + { + name: "Single character escaping", + input: "/%20", + mode: strings.UnescapingModeAllCharacters, + multisegment: true, + expected: "/ ", + expectedErr: nil, + }, + { + name: "Multiple character escaping", + input: "hello%20world", + mode: strings.UnescapingModeAllCharacters, + multisegment: true, + expected: "hello world", + expectedErr: nil, + }, + { + name: "Invalid escape sequence", + input: "%2", + mode: strings.UnescapingModeAllCharacters, + multisegment: true, + expected: "", + expectedErr: strings.MalformedSequenceError("%2"), + }, + { + name: "Escaping except slash with multisegment=false", + input: "/%2F%23%20", + mode: strings.UnescapingModeAllExceptSlash, + multisegment: true, + expected: "/%2F# ", + expectedErr: nil, + }, + { + name: "Escaping except reserved with multisegment=true", + input: "/%2F%23%20", + mode: strings.UnescapingModeAllExceptReserved, + multisegment: true, + expected: "/%2F%23 ", + expectedErr: nil, + }, + { + name: "Escaping all characters with multisegment=true", + input: "/%2F%23%20", + mode: strings.UnescapingModeAllCharacters, + multisegment: true, + expected: "//# ", + expectedErr: nil, + }, + } + + for _, test := range tbl { + t.Run(test.name, func(t *testing.T) { + result, err := strings.Unescape(test.input, test.mode, test.multisegment) + require.Equal(t, test.expected, result) + require.Equal(t, test.expectedErr, err) + }) + } +} diff --git a/pkg/sync/task/future/future_test.go b/pkg/sync/task/future/future_test.go index adce8e2e1..452a9edd9 100644 --- a/pkg/sync/task/future/future_test.go +++ b/pkg/sync/task/future/future_test.go @@ -2,12 +2,13 @@ package future import ( "context" - "fmt" + "errors" "sync" "testing" "time" "github.com/plgd-dev/hub/v2/test/config" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -29,7 +30,7 @@ func TestFutureReadyAfterError(t *testing.T) { fut, set := New() require.False(t, fut.Ready()) - set(nil, fmt.Errorf("test error")) + set(nil, errors.New("test error")) require.True(t, fut.Ready()) val, err := fut.Get(context.Background()) @@ -60,8 +61,8 @@ func TestFutureGetMultithreaded(t *testing.T) { go func() { defer wg.Done() value, err := fut.Get(ctx) - require.NoError(t, err) - require.Equal(t, val, value.(string)) + assert.NoError(t, err) + assert.Equal(t, val, value.(string)) }() } diff --git a/pkg/sync/task/queue/queue.go b/pkg/sync/task/queue/queue.go index 29a9bdfcf..da64612e9 100644 --- a/pkg/sync/task/queue/queue.go +++ b/pkg/sync/task/queue/queue.go @@ -2,7 +2,7 @@ package queue import ( "container/list" - "fmt" + "errors" "sync" "github.com/panjf2000/ants/v2" @@ -21,7 +21,7 @@ type Queue struct { // New creates task queue which is processed by goroutines. func New(cfg Config) (*Queue, error) { if cfg.Size <= 0 { - return nil, fmt.Errorf("invalid value of Size") + return nil, errors.New("invalid value of Size") } p, err := ants.NewPool(cfg.GoPoolSize, ants.WithPreAlloc(true), ants.WithExpiryDuration(cfg.MaxIdleTime), ants.WithNonblocking(true)) if err != nil { @@ -38,7 +38,7 @@ func (q *Queue) appendQueue(tasks []func()) error { q.mutex.Lock() defer q.mutex.Unlock() if q.queue.Len()+len(tasks) > q.limit { - return fmt.Errorf("reached limit of max processed jobs") + return errors.New("reached limit of max processed jobs") } for _, t := range tasks { q.queue.PushBack(t) diff --git a/pkg/yaml/yaml_test.go b/pkg/yaml/yaml_test.go index 68a0833a0..f2eb5b74f 100644 --- a/pkg/yaml/yaml_test.go +++ b/pkg/yaml/yaml_test.go @@ -131,7 +131,7 @@ func TestMergeYamlNodes(t *testing.T) { // merge the nodes got, err := MergeYamlNodes(tt.args.node1, tt.args.node2) if tt.wantErr { - assert.Error(t, err) + require.Error(t, err) return } require.NoError(t, err) diff --git a/resource-aggregate/commands/commands.pb.go b/resource-aggregate/commands/commands.pb.go index 9bd65d903..4635eacdc 100644 --- a/resource-aggregate/commands/commands.pb.go +++ b/resource-aggregate/commands/commands.pb.go @@ -503,6 +503,7 @@ type NotifyResourceChangedRequest struct { CommandMetadata *CommandMetadata `protobuf:"bytes,3,opt,name=command_metadata,json=commandMetadata,proto3" json:"command_metadata,omitempty"` Status Status `protobuf:"varint,4,opt,name=status,proto3,enum=resourceaggregate.pb.Status" json:"status,omitempty"` Etag []byte `protobuf:"bytes,5,opt,name=etag,proto3" json:"etag,omitempty"` + ResourceTypes []string `protobuf:"bytes,6,rep,name=resource_types,json=resourceTypes,proto3" json:"resource_types,omitempty"` } func (x *NotifyResourceChangedRequest) Reset() { @@ -572,6 +573,13 @@ func (x *NotifyResourceChangedRequest) GetEtag() []byte { return nil } +func (x *NotifyResourceChangedRequest) GetResourceTypes() []string { + if x != nil { + return x.ResourceTypes + } + return nil +} + type NotifyResourceChangedResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1783,11 +1791,12 @@ type Connection struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Status Connection_Status `protobuf:"varint,1,opt,name=status,proto3,enum=resourceaggregate.pb.Connection_Status" json:"status,omitempty"` - Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` // when status is ONLINE, this field contains the connection id. To update state offline, this field must be same as the one in the previous message. - ConnectedAt int64 `protobuf:"varint,4,opt,name=connected_at,json=connectedAt,proto3" json:"connected_at,omitempty"` // timestamp when the device was connected - Protocol Connection_Protocol `protobuf:"varint,5,opt,name=protocol,proto3,enum=resourceaggregate.pb.Connection_Protocol" json:"protocol,omitempty"` // application protocol. It need to be set when the status is ONLINE. - ServiceId string `protobuf:"bytes,6,opt,name=service_id,json=serviceId,proto3" json:"service_id,omitempty"` // The service.ID, which identify the device being served, must be set when the status is ONLINE. However, during an OFFLINE event, they will be sed to empty values. + Status Connection_Status `protobuf:"varint,1,opt,name=status,proto3,enum=resourceaggregate.pb.Connection_Status" json:"status,omitempty"` + Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` // when status is ONLINE, this field contains the connection id. To update state offline, this field must be same as the one in the previous message. + ConnectedAt int64 `protobuf:"varint,4,opt,name=connected_at,json=connectedAt,proto3" json:"connected_at,omitempty"` // timestamp when the device was connected + Protocol Connection_Protocol `protobuf:"varint,5,opt,name=protocol,proto3,enum=resourceaggregate.pb.Connection_Protocol" json:"protocol,omitempty"` // application protocol. It need to be set when the status is ONLINE. + ServiceId string `protobuf:"bytes,6,opt,name=service_id,json=serviceId,proto3" json:"service_id,omitempty"` // The service.ID, which identify the device being served, must be set when the status is ONLINE. However, during an OFFLINE event, they will be sed to empty values. + LocalEndpoints []string `protobuf:"bytes,7,rep,name=local_endpoints,json=localEndpoints,proto3" json:"local_endpoints,omitempty"` // The last local endpoints of the device, and it is set when the status is ONLINE. } func (x *Connection) Reset() { @@ -1857,6 +1866,13 @@ func (x *Connection) GetServiceId() string { return "" } +func (x *Connection) GetLocalEndpoints() []string { + if x != nil { + return x.LocalEndpoints + } + return nil +} + type TwinSynchronization struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2890,7 +2906,7 @@ var file_resource_aggregate_pb_commands_proto_rawDesc = []byte{ 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, - 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0xb6, 0x02, 0x0a, + 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0xdd, 0x02, 0x0a, 0x1c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, @@ -2910,266 +2926,271 @@ var file_resource_aggregate_pb_commands_proto_rawDesc = []byte{ 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x04, 0x65, 0x74, 0x61, 0x67, 0x22, 0x68, 0x0a, 0x1d, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, + 0x04, 0x65, 0x74, 0x61, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x22, 0x68, 0x0a, 0x1d, + 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, + 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, + 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, + 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x6d, 0x0a, 0x21, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4e, + 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x48, 0x0a, 0x05, 0x62, + 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, + 0x62, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x05, + 0x62, 0x61, 0x74, 0x63, 0x68, 0x22, 0x6d, 0x0a, 0x22, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0d, 0x61, + 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x22, 0xdd, 0x02, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x41, + 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, + 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, + 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + 0x12, 0x20, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x6c, 0x69, 0x76, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x6f, 0x4c, 0x69, + 0x76, 0x65, 0x12, 0x50, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x22, 0x82, 0x01, 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, + 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, + 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, + 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0xc9, 0x02, 0x0a, 0x1c, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, + 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, + 0x0e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, + 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x12, 0x50, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, + 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x68, 0x0a, 0x1d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, - 0x6d, 0x0a, 0x21, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x48, 0x0a, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, - 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x4e, 0x6f, 0x74, 0x69, 0x66, - 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x05, 0x62, 0x61, 0x74, 0x63, 0x68, 0x22, 0x6d, - 0x0a, 0x22, 0x42, 0x61, 0x74, 0x63, 0x68, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, + 0xba, 0x02, 0x0a, 0x17, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x41, 0x0a, 0x0b, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, + 0x0a, 0x0e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x66, 0x61, 0x63, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x6f, 0x5f, + 0x6c, 0x69, 0x76, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, + 0x54, 0x6f, 0x4c, 0x69, 0x76, 0x65, 0x12, 0x50, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, + 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x04, 0x65, 0x74, 0x61, 0x67, 0x22, 0x84, 0x01, 0x0a, + 0x18, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x5f, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, + 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x22, 0xdf, 0x02, 0x0a, 0x1e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, - 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, - 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0xdd, 0x02, - 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, - 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, - 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x64, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, - 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x74, 0x69, 0x6d, - 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x6c, 0x69, 0x76, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x6f, 0x4c, 0x69, 0x76, 0x65, 0x12, 0x50, 0x0a, 0x10, 0x63, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, - 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0f, 0x63, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x82, 0x01, - 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x5f, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, - 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, - 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, - 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x22, 0xc9, 0x02, 0x0a, 0x1c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x34, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, - 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x50, 0x0a, 0x10, - 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0f, 0x63, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x68, - 0x0a, 0x1d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, - 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, - 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0xba, 0x02, 0x0a, 0x17, 0x52, 0x65, 0x74, - 0x72, 0x69, 0x65, 0x76, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, - 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x72, 0x72, 0x65, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x2d, - 0x0a, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x66, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x20, 0x0a, - 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x6c, 0x69, 0x76, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x6f, 0x4c, 0x69, 0x76, 0x65, 0x12, + 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x72, + 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, + 0x12, 0x34, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1c, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x50, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0c, 0x52, - 0x04, 0x65, 0x74, 0x61, 0x67, 0x22, 0x84, 0x01, 0x0a, 0x18, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, - 0x76, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x75, 0x6e, 0x74, 0x69, - 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, - 0x74, 0x69, 0x6c, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, - 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, - 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0xdf, 0x02, 0x0a, - 0x1e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72, - 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, - 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, - 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x50, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, - 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x74, - 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x65, 0x74, 0x61, 0x67, 0x22, 0x6a, - 0x0a, 0x1f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x61, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x04, 0x65, 0x74, 0x61, 0x67, 0x22, 0x6a, 0x0a, 0x1f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, + 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x22, 0xa4, 0x02, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x41, 0x0a, 0x0b, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, + 0x0a, 0x0e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x6f, + 0x5f, 0x6c, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x69, 0x6d, + 0x65, 0x54, 0x6f, 0x4c, 0x69, 0x76, 0x65, 0x12, 0x50, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x6e, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, + 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x82, 0x01, 0x0a, 0x16, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x75, 0x6e, 0x74, + 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, + 0x6e, 0x74, 0x69, 0x6c, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, + 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, + 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0xc9, 0x02, + 0x0a, 0x1c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x41, + 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, + 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, + 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, - 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, - 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0xa4, 0x02, 0x0a, 0x15, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, - 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x72, 0x72, 0x65, - 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x20, - 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x6c, 0x69, 0x76, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x6f, 0x4c, 0x69, 0x76, 0x65, - 0x12, 0x50, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, - 0x62, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, - 0x65, 0x22, 0x82, 0x01, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x12, 0x47, 0x0a, - 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, - 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, - 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0xc9, 0x02, 0x0a, 0x1c, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x72, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, - 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, - 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, - 0x64, 0x12, 0x34, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x1c, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, - 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x37, + 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x07, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x50, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x6e, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, + 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x68, 0x0a, 0x1d, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, + 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x22, 0xae, 0x02, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x41, 0x0a, + 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, + 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x12, 0x50, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, - 0x62, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x22, 0x68, 0x0a, 0x1d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, - 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, - 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0xae, 0x02, 0x0a, - 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, + 0x12, 0x20, 0x0a, 0x0c, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x6c, 0x69, 0x76, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x6f, 0x4c, 0x69, + 0x76, 0x65, 0x12, 0x50, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x22, 0x82, 0x01, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, + 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, + 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, + 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0xc9, 0x02, 0x0a, 0x1c, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, + 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, + 0x0e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, - 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x72, - 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, - 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, - 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x74, 0x69, 0x6d, - 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x6c, 0x69, 0x76, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x6f, 0x4c, 0x69, 0x76, 0x65, 0x12, 0x50, 0x0a, 0x10, 0x63, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, - 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0f, 0x63, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x82, 0x01, - 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x5f, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, - 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, - 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, - 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x22, 0xc9, 0x02, 0x0a, 0x1c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, - 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x34, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, + 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x6e, 0x74, 0x12, 0x50, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, - 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x50, 0x0a, 0x10, - 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0f, 0x63, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x68, - 0x0a, 0x1d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, - 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, - 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0xf7, 0x02, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x45, 0x0a, 0x08, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, + 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0x68, 0x0a, 0x1d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, - 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, - 0x6f, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, - 0x64, 0x22, 0x21, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x4f, + 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, + 0xa0, 0x03, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, + 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, + 0x41, 0x74, 0x12, 0x45, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x52, + 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x6c, 0x6f, 0x63, 0x61, + 0x6c, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x73, 0x22, 0x21, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x4f, 0x46, 0x46, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4f, 0x4e, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x01, 0x22, 0x52, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x08, 0x0a, diff --git a/resource-aggregate/commands/utils.go b/resource-aggregate/commands/utils.go index 33ed82e69..b0b973438 100644 --- a/resource-aggregate/commands/utils.go +++ b/resource-aggregate/commands/utils.go @@ -18,20 +18,20 @@ const ( // ToUUID converts resource href and device id to unique resource ID func (r *ResourceId) ToUUID() uuid.UUID { - if len(r.Href) == 0 { + if len(r.GetHref()) == 0 { return uuid.Nil } - return uuid.NewSHA1(uuid.NameSpaceURL, []byte(r.DeviceId+r.Href)) + return uuid.NewSHA1(uuid.NameSpaceURL, []byte(r.GetDeviceId()+r.GetHref())) } // ToUUID converts resource href and device id to unique resource ID func (r *Resource) ToUUID() uuid.UUID { - return uuid.NewSHA1(uuid.NameSpaceURL, []byte(r.DeviceId+r.Href)) + return uuid.NewSHA1(uuid.NameSpaceURL, []byte(r.GetDeviceId()+r.GetHref())) } // GetResourceID converts resource href and device id to resource id struct func (r *Resource) GetResourceID() *ResourceId { - return &ResourceId{DeviceId: r.DeviceId, Href: r.Href} + return &ResourceId{DeviceId: r.GetDeviceId(), Href: r.GetHref()} } func MakeLinksResourceUUID(deviceID string) uuid.UUID { @@ -138,24 +138,25 @@ func (r *ResourceId) Equal(r1 *ResourceId) bool { if r == nil || r1 == nil { return false } - return r.DeviceId == r1.DeviceId && r.Href == r1.Href + return r.GetDeviceId() == r1.GetDeviceId() && r.GetHref() == r1.GetHref() } func (r *ResourceId) ToString() string { if r == nil { return "" } - if r.DeviceId == "" { + deviceID := r.GetDeviceId() + if deviceID == "" { return "" } - if r.Href == "" { + href := r.GetHref() + if href == "" { return "" } - href := r.Href if href[0] != '/' { href = "/" + href } - return r.DeviceId + href + return deviceID + href } func ResourceIdFromString(v string) *ResourceId { diff --git a/resource-aggregate/cqrs/aggregate/aggregate.go b/resource-aggregate/cqrs/aggregate/aggregate.go index 3e73e7699..bec81ca0f 100644 --- a/resource-aggregate/cqrs/aggregate/aggregate.go +++ b/resource-aggregate/cqrs/aggregate/aggregate.go @@ -29,7 +29,7 @@ func NewDefaultRetryFunc(limit int) RetryFunc { counter := new(int) return func() (time.Time, error) { if *counter >= limit { - return time.Time{}, fmt.Errorf("retry reach limit") + return time.Time{}, errors.New("retry reach limit") } *counter++ return time.Now().Add(time.Millisecond * 10), nil @@ -37,20 +37,26 @@ func NewDefaultRetryFunc(limit int) RetryFunc { } // FactoryModelFunc creates model for aggregate -type FactoryModelFunc = func(ctx context.Context) (AggregateModel, error) +type FactoryModelFunc = func(ctx context.Context, groupID, aggregateID string) (AggregateModel, error) // Aggregate holds data for Handle command type Aggregate struct { - groupID string - aggregateID string - store eventstore.EventStore - retryFunc RetryFunc - factoryModel FactoryModelFunc - LogDebugfFunc eventstore.LogDebugfFunc + groupID string + aggregateID string + store eventstore.EventStore + retryFunc RetryFunc + factoryModel FactoryModelFunc + LogDebugfFunc eventstore.LogDebugfFunc + additionalModels []AdditionalModel +} + +type AdditionalModel struct { + GroupID string + AggregateID string } // NewAggregate creates aggregate. it load and store events created from commands -func NewAggregate(groupID, aggregateID string, retryFunc RetryFunc, store eventstore.EventStore, factoryModel FactoryModelFunc, logDebugfFunc eventstore.LogDebugfFunc) (*Aggregate, error) { +func NewAggregate(groupID, aggregateID string, retryFunc RetryFunc, store eventstore.EventStore, factoryModel FactoryModelFunc, logDebugfFunc eventstore.LogDebugfFunc, additionalModels ...AdditionalModel) (*Aggregate, error) { if groupID == "" { return nil, errors.New("cannot create aggregate: invalid groupID") } @@ -65,12 +71,13 @@ func NewAggregate(groupID, aggregateID string, retryFunc RetryFunc, store events } return &Aggregate{ - groupID: groupID, - aggregateID: aggregateID, - store: store, - factoryModel: factoryModel, - retryFunc: retryFunc, - LogDebugfFunc: logDebugfFunc, + groupID: groupID, + aggregateID: aggregateID, + store: store, + factoryModel: factoryModel, + retryFunc: retryFunc, + LogDebugfFunc: logDebugfFunc, + additionalModels: additionalModels, }, nil } @@ -130,28 +137,55 @@ func HandleRetry(ctx context.Context, retryFunc RetryFunc) error { select { case <-time.After(time.Until(when)): case <-ctx.Done(): - return fmt.Errorf("retry canceled") + return errors.New("retry canceled") } return nil } -func NewAggregateModel(ctx context.Context, groupID, aggregateID string, store eventstore.EventStore, logDebugfFunc eventstore.LogDebugfFunc, model AggregateModel) (*AggregateModelWrapper, error) { +func NewAggregateModel(ctx context.Context, groupID, aggregateID string, store eventstore.EventStore, logDebugfFunc eventstore.LogDebugfFunc, factoryModel FactoryModelFunc, additionalModels ...AdditionalModel) (*AggregateModelWrapper, error) { + models := make(map[string]AggregateModel, 1+len(additionalModels)) + model, err := factoryModel(ctx, groupID, aggregateID) + if err != nil { + return nil, fmt.Errorf("cannot create aggregate model: %w", err) + } + models[aggregateID] = model amodel := &AggregateModelWrapper{model: model} - ep := eventstore.NewProjection(store, func(ctx context.Context, groupID, aggregateID string) (eventstore.Model, error) { return amodel, nil }, logDebugfFunc) - err := ep.Project(ctx, []eventstore.SnapshotQuery{ - { - GroupID: groupID, - AggregateID: aggregateID, - }, + for _, r := range additionalModels { + model, err = factoryModel(ctx, r.GroupID, r.AggregateID) + if err != nil { + return nil, fmt.Errorf("cannot create aggregate model: %w", err) + } + models[r.AggregateID] = model + } + ep := eventstore.NewProjection(store, func(_ context.Context, _, projectionAggregateID string) (eventstore.Model, error) { + if projectionAggregateID == aggregateID { + return amodel, nil + } + if model, ok := models[projectionAggregateID]; ok { + return model, nil + } + return nil, fmt.Errorf("cannot create aggregate model for %v %v : not found", groupID, aggregateID) + }, logDebugfFunc) + q := make([]eventstore.SnapshotQuery, 0, 1+len(additionalModels)) + q = append(q, eventstore.SnapshotQuery{ + GroupID: groupID, + AggregateID: aggregateID, }) + for _, r := range additionalModels { + q = append(q, eventstore.SnapshotQuery{ + GroupID: r.GroupID, + AggregateID: r.AggregateID, + }) + } + err = ep.Project(ctx, q) if err != nil { return nil, fmt.Errorf("cannot load aggregate model: %w", err) } return amodel, nil } -func (a *Aggregate) FactoryModel(ctx context.Context) (AggregateModel, error) { - return a.factoryModel(ctx) +func (a *Aggregate) FactoryModel(ctx context.Context, groupID, aggregateID string) (AggregateModel, error) { + return a.factoryModel(ctx, groupID, aggregateID) } func (a *Aggregate) HandleCommandWithAggregateModelWrapper(ctx context.Context, cmd Command, amodel *AggregateModelWrapper) (events []eventstore.Event, concurrencyExcpetion bool, err error) { @@ -169,7 +203,7 @@ func (a *Aggregate) HandleCommandWithAggregateModelWrapper(ctx context.Context, } snapshot, ok := amodel.TakeSnapshot(newVersion + uint64(len(newEvents)-1)) if !ok { - return nil, false, fmt.Errorf("cannot take snapshot") + return nil, false, errors.New("cannot take snapshot") } // save all events except last one, because last one will be replaced by snapshot saveEvents := make([]eventstore.Event, 0, len(newEvents)) @@ -206,12 +240,7 @@ func (a *Aggregate) HandleCommand(ctx context.Context, cmd Command) ([]eventstor } firstIteration = false - model, err := a.factoryModel(ctx) - if err != nil { - return nil, errHandleCommand(err) - } - - amodel, err := NewAggregateModel(ctx, a.groupID, a.aggregateID, a.store, a.LogDebugfFunc, model) + amodel, err := NewAggregateModel(ctx, a.groupID, a.aggregateID, a.store, a.LogDebugfFunc, a.factoryModel, a.additionalModels...) if err != nil { return nil, errHandleCommand(err) } diff --git a/resource-aggregate/cqrs/aggregate/aggregateParallel_test.go b/resource-aggregate/cqrs/aggregate/aggregateParallel_test.go index d1388b868..0189f7496 100644 --- a/resource-aggregate/cqrs/aggregate/aggregateParallel_test.go +++ b/resource-aggregate/cqrs/aggregate/aggregateParallel_test.go @@ -19,6 +19,7 @@ import ( "github.com/plgd-dev/hub/v2/resource-aggregate/cqrs/eventstore/mongodb" "github.com/plgd-dev/hub/v2/resource-aggregate/events" "github.com/plgd-dev/hub/v2/test/config" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/trace/noop" "go.uber.org/atomic" @@ -65,7 +66,7 @@ func testNewEventstore(ctx context.Context, t *testing.T) (eventstore.EventStore func cleanUpToSnapshot(ctx context.Context, t *testing.T, store eventstore.EventStore, evs []eventstore.Event) { for _, event := range evs { if err := store.RemoveUpToVersion(ctx, []eventstore.VersionQuery{{GroupID: event.GroupID(), AggregateID: event.AggregateID(), Version: event.Version()}}); err != nil && !errors.Is(err, eventstore.ErrNotSupported) { - require.NoError(t, err) + assert.NoError(t, err) } fmt.Printf("snapshot at version %v\n", event.Version()) } @@ -82,8 +83,8 @@ func TestParallelRequest(t *testing.T) { href := "/test/resource/1" newAggregate := func(deviceID, href string) *aggregate.Aggregate { - a, err := aggregate.NewAggregate(deviceID, commands.NewResourceID(deviceID, href).ToUUID().String(), aggregate.NewDefaultRetryFunc(64), store, func(context.Context) (aggregate.AggregateModel, error) { - ev := events.NewResourceStateSnapshotTakenForCommand("test", "test", "hubID") + a, err := aggregate.NewAggregate(deviceID, commands.NewResourceID(deviceID, href).ToUUID().String(), aggregate.NewDefaultRetryFunc(128), store, func(context.Context, string, string) (aggregate.AggregateModel, error) { + ev := events.NewResourceStateSnapshotTakenForCommand("test", "test", "hubID", nil) ev.ResourceId = commands.NewResourceID(deviceID, href) return ev, nil }, nil) diff --git a/resource-aggregate/cqrs/aggregate/aggregate_test.go b/resource-aggregate/cqrs/aggregate/aggregate_test.go index 4b5a8a092..aed8ee43c 100644 --- a/resource-aggregate/cqrs/aggregate/aggregate_test.go +++ b/resource-aggregate/cqrs/aggregate/aggregate_test.go @@ -75,7 +75,7 @@ func TestAggregate(t *testing.T) { } newAggregate := func(deviceID, href string) *aggregate.Aggregate { - a, err := aggregate.NewAggregate(deviceID, commands.NewResourceID(deviceID, href).ToUUID().String(), aggregate.NewDefaultRetryFunc(1), store, func(context.Context) (aggregate.AggregateModel, error) { + a, err := aggregate.NewAggregate(deviceID, commands.NewResourceID(deviceID, href).ToUUID().String(), aggregate.NewDefaultRetryFunc(1), store, func(context.Context, string, string) (aggregate.AggregateModel, error) { return &raTest.Snapshot{DeviceId: deviceID, Href: href, IsPublished: true}, nil }, nil) require.NoError(t, err) @@ -134,10 +134,8 @@ func TestAggregate(t *testing.T) { require.NoError(t, err) concurrencyExcepTestA := newAggregate(commandPub1.GetDeviceId(), commandPub1.GetHref()) - model, err := concurrencyExcepTestA.FactoryModel(ctx) - require.NoError(t, err) - amodel, err := aggregate.NewAggregateModel(ctx, a.GroupID(), a.AggregateID(), store, a.LogDebugfFunc, model) + amodel, err := aggregate.NewAggregateModel(ctx, a.GroupID(), a.AggregateID(), store, a.LogDebugfFunc, concurrencyExcepTestA.FactoryModel) require.NoError(t, err) ev, concurrencyException, err := a.HandleCommandWithAggregateModelWrapper(ctx, &commandPub1, amodel) diff --git a/resource-aggregate/cqrs/aggregate/test/aggregate.go b/resource-aggregate/cqrs/aggregate/test/aggregate.go index 54b714aba..7c1078d12 100644 --- a/resource-aggregate/cqrs/aggregate/test/aggregate.go +++ b/resource-aggregate/cqrs/aggregate/test/aggregate.go @@ -13,45 +13,48 @@ import ( type Command = interface{} -func (e *Published) Version() uint64 { return e.EventVersion } +func (e *Published) Version() uint64 { return e.GetEventVersion() } func (e *Published) EventType() string { return "published" } func (e *Published) Marshal() ([]byte, error) { return proto.Marshal(e) } func (e *Published) Unmarshal(b []byte) error { return proto.Unmarshal(b, e) } func (e *Published) AggregateID() string { - return commands.NewResourceID(e.DeviceId, e.Href).ToUUID().String() + return commands.NewResourceID(e.GetDeviceId(), e.GetHref()).ToUUID().String() } -func (e *Published) GroupID() string { return e.DeviceId } +func (e *Published) GroupID() string { return e.GetDeviceId() } func (e *Published) IsSnapshot() bool { return false } -func (e *Published) Timestamp() time.Time { return time.Unix(0, e.EventTimestamp) } +func (e *Published) Timestamp() time.Time { return time.Unix(0, e.GetEventTimestamp()) } func (e *Published) ETag() *eventstore.ETagData { return nil } func (e *Published) ServiceID() (string, bool) { return "", false } +func (e *Published) Types() []string { return nil } -func (e *Unpublished) Version() uint64 { return e.EventVersion } +func (e *Unpublished) Version() uint64 { return e.GetEventVersion() } func (e *Unpublished) EventType() string { return "unpublished" } func (e *Unpublished) Marshal() ([]byte, error) { return proto.Marshal(e) } func (e *Unpublished) Unmarshal(b []byte) error { return proto.Unmarshal(b, e) } func (e *Unpublished) AggregateID() string { - return commands.NewResourceID(e.DeviceId, e.Href).ToUUID().String() + return commands.NewResourceID(e.GetDeviceId(), e.GetHref()).ToUUID().String() } -func (e *Unpublished) GroupID() string { return e.DeviceId } +func (e *Unpublished) GroupID() string { return e.GetDeviceId() } func (e *Unpublished) IsSnapshot() bool { return false } -func (e *Unpublished) Timestamp() time.Time { return time.Unix(0, e.EventTimestamp) } +func (e *Unpublished) Timestamp() time.Time { return time.Unix(0, e.GetEventTimestamp()) } func (e *Unpublished) ETag() *eventstore.ETagData { return nil } func (e *Unpublished) ServiceID() (string, bool) { return "", false } +func (e *Unpublished) Types() []string { return nil } -func (e *Snapshot) Version() uint64 { return e.EventVersion } +func (e *Snapshot) Version() uint64 { return e.GetEventVersion() } func (e *Snapshot) EventType() string { return "snapshot" } func (e *Snapshot) Marshal() ([]byte, error) { return proto.Marshal(e) } func (e *Snapshot) Unmarshal(b []byte) error { return proto.Unmarshal(b, e) } func (e *Snapshot) AggregateID() string { - return commands.NewResourceID(e.DeviceId, e.Href).ToUUID().String() + return commands.NewResourceID(e.GetDeviceId(), e.GetHref()).ToUUID().String() } -func (e *Snapshot) GroupId() string { return e.DeviceId } -func (e *Snapshot) GroupID() string { return e.DeviceId } +func (e *Snapshot) GroupId() string { return e.GetDeviceId() } +func (e *Snapshot) GroupID() string { return e.GetDeviceId() } func (e *Snapshot) IsSnapshot() bool { return true } -func (e *Snapshot) Timestamp() time.Time { return time.Unix(0, e.EventTimestamp) } +func (e *Snapshot) Timestamp() time.Time { return time.Unix(0, e.GetEventTimestamp()) } func (e *Snapshot) ETag() *eventstore.ETagData { return nil } func (e *Snapshot) ServiceID() (string, bool) { return "", false } +func (e *Snapshot) Types() []string { return nil } func (e *Snapshot) handleEvent(eu eventstore.EventUnmarshaler) error { if eu.EventType() == "" { @@ -106,13 +109,13 @@ func (e *Snapshot) HandleCommand(_ context.Context, cmd Command, newVersion uint switch req := cmd.(type) { case *Publish: e.IsPublished = true - return []eventstore.Event{&Published{DeviceId: req.DeviceId, Href: req.Href, EventVersion: newVersion}}, nil + return []eventstore.Event{&Published{DeviceId: req.GetDeviceId(), Href: req.GetHref(), EventVersion: newVersion}}, nil case *Unpublish: - if !e.IsPublished { - return nil, fmt.Errorf("not allowed to unpublish twice in tests") + if !e.GetIsPublished() { + return nil, errors.New("not allowed to unpublish twice in tests") } e.IsPublished = false - return []eventstore.Event{&Unpublished{DeviceId: req.DeviceId, Href: req.Href, EventVersion: newVersion}}, nil + return []eventstore.Event{&Unpublished{DeviceId: req.GetDeviceId(), Href: req.GetHref(), EventVersion: newVersion}}, nil } return nil, fmt.Errorf("unknown command %T", cmd) } diff --git a/resource-aggregate/cqrs/eventbus/event.go b/resource-aggregate/cqrs/eventbus/event.go index b8af0c925..0c096f11b 100644 --- a/resource-aggregate/cqrs/eventbus/event.go +++ b/resource-aggregate/cqrs/eventbus/event.go @@ -17,6 +17,7 @@ type Event = interface { Timestamp() time.Time ETag() *eventstore.ETagData ServiceID() (string, bool) + Types() []string } // EventUnmarshaler provides event. diff --git a/resource-aggregate/cqrs/eventbus/goroutinePoolHandler_test.go b/resource-aggregate/cqrs/eventbus/goroutinePoolHandler_test.go index 9212bd802..6d81ccad1 100644 --- a/resource-aggregate/cqrs/eventbus/goroutinePoolHandler_test.go +++ b/resource-aggregate/cqrs/eventbus/goroutinePoolHandler_test.go @@ -3,11 +3,11 @@ package eventbus import ( "context" "errors" - "fmt" "testing" "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) type mockEventHandler struct { @@ -241,19 +241,19 @@ func TestGoroutinePoolHandler_Handle(t *testing.T) { eh, func(err error) { if wantErr { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } }) err := ep.Handle(tt.args.ctx, tt.args.iter) - assert.NoError(t, err) + require.NoError(t, err) select { case <-eh.processed: assert.Equal(t, tt.want, eh.events) case <-time.After(time.Millisecond * 100): if !tt.wantTimeout { - assert.NoError(t, fmt.Errorf("timeout")) + require.Fail(t, "timeout") } } }) diff --git a/resource-aggregate/cqrs/eventbus/nats/publisher/publisher_test.go b/resource-aggregate/cqrs/eventbus/nats/publisher/publisher_test.go index 312f30b67..8f95fca5c 100644 --- a/resource-aggregate/cqrs/eventbus/nats/publisher/publisher_test.go +++ b/resource-aggregate/cqrs/eventbus/nats/publisher/publisher_test.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "errors" - "fmt" "testing" "time" @@ -44,7 +43,7 @@ func TestPublisher(t *testing.T) { FlusherTimeout: time.Second * 30, }, }, fileWatcher, logger, publisher.WithMarshaler(json.Marshal)) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, publisher) defer func() { publisher.Close() @@ -55,7 +54,7 @@ func TestPublisher(t *testing.T) { logger, subscriber.WithGoPool(func(f func()) error { go f(); return nil }), subscriber.WithUnmarshaler(json.Unmarshal)) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, subscriber) defer func() { subscriber.Close() @@ -114,7 +113,7 @@ func TestPublisherJetStream(t *testing.T) { logger, subscriber.WithGoPool(func(f func()) error { go f(); return nil }), subscriber.WithUnmarshaler(json.Unmarshal)) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, subscriber) defer func() { subscriber.Close() @@ -133,6 +132,7 @@ type mockEvent struct { timestamp int64 Data string ETagI []byte + TypesI []string } func (e mockEvent) Version() uint64 { @@ -173,6 +173,10 @@ func (e mockEvent) ServiceID() (string, bool) { return "", false } +func (e mockEvent) Types() []string { + return e.TypesI +} + type mockEventHandler struct { newEvent chan mockEvent } @@ -207,7 +211,7 @@ func (eh *mockEventHandler) waitForEvent(timeout time.Duration) (mockEvent, erro case e := <-eh.newEvent: return e, nil case <-time.After(timeout): - return mockEvent{}, fmt.Errorf("timeout") + return mockEvent{}, errors.New("timeout") } } @@ -218,7 +222,7 @@ func testWaitForAnyEvent(timeout time.Duration, eh1 *mockEventHandler, eh2 *mock case e := <-eh2.newEvent: return e, nil case <-time.After(timeout): - return mockEvent{}, fmt.Errorf("timeout") + return mockEvent{}, errors.New("timeout") } } @@ -226,7 +230,7 @@ func testNewSubscription(ctx context.Context, t *testing.T, subscriber eventbus. t.Log("Subscribe to testNewSubscription") m := newMockEventHandler() ob, err := subscriber.Subscribe(ctx, subscriptionID, topics, m) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, ob) if ob == nil { return nil, nil @@ -306,11 +310,11 @@ func acceptanceTest(ctx context.Context, t *testing.T, timeout time.Duration, wa }, } - assert.Equal(t, 2, len(topics)) + require.Len(t, topics, 2) t.Log("Without subscription") err := publisher.Publish(ctx, topics[0:1], aggregateID1Path.GroupID, aggregateID1Path.AggregateID, eventsToPublish[0]) - assert.NoError(t, err) + require.NoError(t, err) time.Sleep(waitForSubscription) // Add handlers and observers. @@ -319,66 +323,66 @@ func acceptanceTest(ctx context.Context, t *testing.T, timeout time.Duration, wa time.Sleep(waitForSubscription) err = publisher.Publish(ctx, topics[0:1], aggregateID1Path.GroupID, aggregateID1Path.AggregateID, eventsToPublish[1]) - assert.NoError(t, err) + require.NoError(t, err) event0, err := m0.waitForEvent(timeout) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, eventsToPublish[1], event0) err = ob0.Close() - assert.NoError(t, err) + require.NoError(t, err) t.Log("Subscribe more observers") m1, ob1 := testNewSubscription(ctx, t, subscriber, "sub-1", topics[1:2]) defer func() { err = ob1.Close() - assert.NoError(t, err) + require.NoError(t, err) }() m2, ob2 := testNewSubscription(ctx, t, subscriber, "sub-2", topics[1:2]) defer func() { err = ob2.Close() - assert.NoError(t, err) + require.NoError(t, err) }() m3, ob3 := testNewSubscription(ctx, t, subscriber, "sub-shared", topics[0:1]) defer func() { err = ob3.Close() - assert.NoError(t, err) + require.NoError(t, err) }() m4, ob4 := testNewSubscription(ctx, t, subscriber, "sub-shared", topics[0:1]) defer func() { err = ob4.Close() - assert.NoError(t, err) + require.NoError(t, err) }() time.Sleep(waitForSubscription) err = publisher.Publish(ctx, topics, aggregateID1Path.GroupID, aggregateID1Path.AggregateID, eventsToPublish[2]) - assert.NoError(t, err) + require.NoError(t, err) event1, err := m1.waitForEvent(timeout) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, eventsToPublish[2], event1) event2, err := m2.waitForEvent(timeout) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, eventsToPublish[2], event2) event3, err := testWaitForAnyEvent(timeout, m3, m4) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, eventsToPublish[2], event3) topic := "test.new_topic_" + uuid.Must(uuid.NewRandom()).String() topics = append(topics, topic) err = ob4.SetTopics(ctx, topics) time.Sleep(waitForSubscription) - assert.NoError(t, err) + require.NoError(t, err) err = publisher.Publish(ctx, []string{topic}, aggregateID1Path.GroupID, aggregateID1Path.AggregateID, eventsToPublish[3]) - assert.NoError(t, err) + require.NoError(t, err) event4, err := m4.waitForEvent(timeout) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, eventsToPublish[3], event4) err = ob4.SetTopics(ctx, nil) - assert.NoError(t, err) + require.NoError(t, err) } diff --git a/resource-aggregate/cqrs/eventbus/nats/subscriber/reconnect_test.go b/resource-aggregate/cqrs/eventbus/nats/subscriber/reconnect_test.go index 57702f655..b5ec6891d 100644 --- a/resource-aggregate/cqrs/eventbus/nats/subscriber/reconnect_test.go +++ b/resource-aggregate/cqrs/eventbus/nats/subscriber/reconnect_test.go @@ -3,7 +3,7 @@ package subscriber_test import ( "context" "encoding/json" - "fmt" + "errors" "testing" "time" @@ -95,7 +95,7 @@ func TestSubscriberReconnect(t *testing.T) { select { case <-ch: case <-ctx.Done(): - require.NoError(t, fmt.Errorf("Timeout")) + require.NoError(t, errors.New("Timeout")) } naClient1, pub1, err := natsTest.NewClientAndPublisher(config.MakePublisherConfig(), fileWatcher, logger, publisher.WithMarshaler(json.Marshal)) require.NoError(t, err) diff --git a/resource-aggregate/cqrs/eventbus/nats/subscriber/subscriber.go b/resource-aggregate/cqrs/eventbus/nats/subscriber/subscriber.go index a7a49434a..64a6ef115 100644 --- a/resource-aggregate/cqrs/eventbus/nats/subscriber/subscriber.go +++ b/resource-aggregate/cqrs/eventbus/nats/subscriber/subscriber.go @@ -3,6 +3,7 @@ package subscriber import ( "context" "encoding/json" + "errors" "fmt" "sync" "time" @@ -146,7 +147,7 @@ func New(conn *nats.Conn, pendingLimits natsClient.PendingLimitsConfig, logger l } if cfg.dataUnmarshaler == nil { - return nil, fmt.Errorf("invalid eventUnmarshaler") + return nil, errors.New("invalid eventUnmarshaler") } s := &Subscriber{ @@ -293,7 +294,7 @@ func (o *Observer) handleMsg(msg *nats.Msg) { hasNext: true, e: &e, dataUnmarshaler: func(v interface{}) error { - return o.dataUnmarshaler(e.Data, v) + return o.dataUnmarshaler(e.GetData(), v) }, } diff --git a/resource-aggregate/cqrs/eventbus/nats/subscriber/subscriber_test.go b/resource-aggregate/cqrs/eventbus/nats/subscriber/subscriber_test.go index 4eb45c9ae..c65db81d4 100644 --- a/resource-aggregate/cqrs/eventbus/nats/subscriber/subscriber_test.go +++ b/resource-aggregate/cqrs/eventbus/nats/subscriber/subscriber_test.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "errors" - "fmt" "testing" "time" @@ -67,6 +66,7 @@ type mockEvent struct { timestamp int64 Data string ETagI []byte + TypesI []string } func (e mockEvent) Version() uint64 { @@ -107,6 +107,10 @@ func (e mockEvent) ServiceID() (string, bool) { return "", false } +func (e mockEvent) Types() []string { + return e.TypesI +} + type mockEventHandler struct { newEvent chan mockEvent } @@ -141,7 +145,7 @@ func (eh *mockEventHandler) waitForEvent(timeout time.Duration) (mockEvent, erro case e := <-eh.newEvent: return e, nil case <-time.After(timeout): - return mockEvent{}, fmt.Errorf("timeout") + return mockEvent{}, errors.New("timeout") } } @@ -152,7 +156,7 @@ func testWaitForAnyEvent(timeout time.Duration, eh1 *mockEventHandler, eh2 *mock case e := <-eh2.newEvent: return e, nil case <-time.After(timeout): - return mockEvent{}, fmt.Errorf("timeout") + return mockEvent{}, errors.New("timeout") } } @@ -243,7 +247,7 @@ func acceptanceTest(ctx context.Context, t *testing.T, timeout time.Duration, pu }, } - require.Equal(t, 2, len(publishTopics)) + require.Len(t, publishTopics, 2) t.Log("Without subscription") err := publisher.Publish(ctx, publishTopics[0:1], aggregateID1Path.GroupID, aggregateID1Path.AggregateID, eventsToPublish[0]) diff --git a/resource-aggregate/cqrs/eventstore/cqldb/delete.go b/resource-aggregate/cqrs/eventstore/cqldb/delete.go index 0a82de4d3..a5240ba60 100644 --- a/resource-aggregate/cqrs/eventstore/cqldb/delete.go +++ b/resource-aggregate/cqrs/eventstore/cqldb/delete.go @@ -2,7 +2,7 @@ package cqldb import ( "context" - "fmt" + "errors" "strings" "github.com/plgd-dev/hub/v2/pkg/cqldb" @@ -30,7 +30,7 @@ func getDeviceIDFilter(queries []eventstore.DeleteQuery) string { func (s *EventStore) Delete(ctx context.Context, queries []eventstore.DeleteQuery) error { deviceIDFilter := getDeviceIDFilter(queries) if len(deviceIDFilter) == 0 { - return fmt.Errorf("failed to delete documents: invalid query") + return errors.New("failed to delete documents: invalid query") } return s.client.Session().Query("delete from " + s.Table() + " " + cqldb.WhereClause + " " + deviceIDKey + " in (" + deviceIDFilter + ");").WithContext(ctx).Exec() diff --git a/resource-aggregate/cqrs/eventstore/cqldb/delete_test.go b/resource-aggregate/cqrs/eventstore/cqldb/delete_test.go index 51c512505..27158b12b 100644 --- a/resource-aggregate/cqrs/eventstore/cqldb/delete_test.go +++ b/resource-aggregate/cqrs/eventstore/cqldb/delete_test.go @@ -61,7 +61,7 @@ func TestEventStore_Delete(t *testing.T) { ctx := context.Background() store, err := NewTestEventStore(ctx, fileWatcher, logger) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, store) defer func() { t.Log("clearing db") diff --git a/resource-aggregate/cqrs/eventstore/cqldb/eventstore.go b/resource-aggregate/cqrs/eventstore/cqldb/eventstore.go index 5e5565a72..bd9fc0a9f 100644 --- a/resource-aggregate/cqrs/eventstore/cqldb/eventstore.go +++ b/resource-aggregate/cqrs/eventstore/cqldb/eventstore.go @@ -17,7 +17,7 @@ import ( // Document const ( - // cqldbdb has all keys in in lowercase + // cqldbdb has all keys in lowercase idKey = "id" versionKey = "version" snapshotKey = "snapshot" @@ -138,11 +138,11 @@ func encodeToBlob(data []byte) string { func getLatestEventsSnapshot(events []eventstore.Event, marshaler MarshalerFunc) (eventstore.Event, []byte, error) { if len(events) == 0 { - return nil, nil, fmt.Errorf("empty events") + return nil, nil, errors.New("empty events") } lastEvent := events[len(events)-1] if !lastEvent.IsSnapshot() { - return nil, nil, fmt.Errorf("the last event must be a snapshot") + return nil, nil, errors.New("the last event must be a snapshot") } // Marshal event data if there is any. snapshot, err := marshaler(lastEvent) diff --git a/resource-aggregate/cqrs/eventstore/cqldb/getLatestDeviceETags.go b/resource-aggregate/cqrs/eventstore/cqldb/getLatestDeviceETags.go index 147203c83..b3f052fcb 100644 --- a/resource-aggregate/cqrs/eventstore/cqldb/getLatestDeviceETags.go +++ b/resource-aggregate/cqrs/eventstore/cqldb/getLatestDeviceETags.go @@ -3,7 +3,6 @@ package cqldb import ( "context" "errors" - "fmt" "sort" "strings" @@ -40,7 +39,7 @@ func (a etagTimestamps) toETags(limit int) [][]byte { // Get latest ETags for device resources from event store for batch observing func (s *EventStore) GetLatestDeviceETags(ctx context.Context, deviceID string, limit uint32) ([][]byte, error) { if deviceID == "" { - return nil, fmt.Errorf("deviceID is invalid") + return nil, errors.New("deviceID is invalid") } var q strings.Builder q.WriteString(cqldb.SelectCommand) diff --git a/resource-aggregate/cqrs/eventstore/cqldb/getLatestDeviceETags_test.go b/resource-aggregate/cqrs/eventstore/cqldb/getLatestDeviceETags_test.go index 3147f11e1..b390d6355 100644 --- a/resource-aggregate/cqrs/eventstore/cqldb/getLatestDeviceETags_test.go +++ b/resource-aggregate/cqrs/eventstore/cqldb/getLatestDeviceETags_test.go @@ -23,7 +23,7 @@ func TestGetLatestDeviceETAG(t *testing.T) { ctx := context.Background() store, err := NewTestEventStore(ctx, fileWatcher, logger) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, store) defer func() { t.Log("clearing db") diff --git a/resource-aggregate/cqrs/eventstore/cqldb/load.go b/resource-aggregate/cqrs/eventstore/cqldb/load.go index 05172685b..d0d1b57d9 100644 --- a/resource-aggregate/cqrs/eventstore/cqldb/load.go +++ b/resource-aggregate/cqrs/eventstore/cqldb/load.go @@ -3,6 +3,7 @@ package cqldb import ( "context" "fmt" + "strconv" "strings" "github.com/gocql/gocql" @@ -166,7 +167,7 @@ func snapshotQueriesToFilter(deviceID string, queries []eventstore.SnapshotQuery } filter.WriteString(timestampKey) filter.WriteString(">=") - filter.WriteString(fmt.Sprintf("%v", timestamp)) + filter.WriteString(strconv.FormatInt(timestamp, 10)) filter.WriteString(" ALLOW FILTERING") } return filter.String() diff --git a/resource-aggregate/cqrs/eventstore/cqldb/loadDeviceMetadataByServiceIDs.go b/resource-aggregate/cqrs/eventstore/cqldb/loadDeviceMetadataByServiceIDs.go index 4319e4ec3..efb8c78f0 100644 --- a/resource-aggregate/cqrs/eventstore/cqldb/loadDeviceMetadataByServiceIDs.go +++ b/resource-aggregate/cqrs/eventstore/cqldb/loadDeviceMetadataByServiceIDs.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "strconv" "strings" "github.com/gocql/gocql" @@ -19,7 +20,7 @@ func (s *EventStore) LoadDeviceMetadataByServiceIDs(ctx context.Context, service return nil, status.Errorf(codes.InvalidArgument, "invalid serviceIDs") } serviceIDs = pkgStrings.Unique(serviceIDs) - q := cqldb.SelectCommand + " " + deviceIDKey + "," + serviceIDKey + " " + cqldb.FromClause + " " + s.Table() + " " + cqldb.WhereClause + " " + serviceIDKey + " in (" + strings.Join(serviceIDs, ",") + ") LIMIT " + fmt.Sprintf("%v", limit) + ";" + q := cqldb.SelectCommand + " " + deviceIDKey + "," + serviceIDKey + " " + cqldb.FromClause + " " + s.Table() + " " + cqldb.WhereClause + " " + serviceIDKey + " in (" + strings.Join(serviceIDs, ",") + ") LIMIT " + strconv.FormatInt(limit, 10) + ";" iter := s.client.Session().Query(q).WithContext(ctx).Iter() if iter == nil { return nil, errors.New("cannot create iterator") diff --git a/resource-aggregate/cqrs/eventstore/cqldb/loadDeviceMetadataByServiceIDs_test.go b/resource-aggregate/cqrs/eventstore/cqldb/loadDeviceMetadataByServiceIDs_test.go index dc08689c5..e6f1af5c3 100644 --- a/resource-aggregate/cqrs/eventstore/cqldb/loadDeviceMetadataByServiceIDs_test.go +++ b/resource-aggregate/cqrs/eventstore/cqldb/loadDeviceMetadataByServiceIDs_test.go @@ -23,7 +23,7 @@ func TestLoadDeviceMetadataByServiceIDs(t *testing.T) { ctx := context.Background() store, err := NewTestEventStore(ctx, fileWatcher, logger) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, store) defer func() { t.Log("clearing db") diff --git a/resource-aggregate/cqrs/eventstore/cqldb/save.go b/resource-aggregate/cqrs/eventstore/cqldb/save.go index 64742c06e..9d2255c35 100644 --- a/resource-aggregate/cqrs/eventstore/cqldb/save.go +++ b/resource-aggregate/cqrs/eventstore/cqldb/save.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "strconv" "strings" "github.com/gocql/gocql" @@ -54,8 +55,8 @@ func eventToKeyValues(event eventstore.Event, insert bool, data []byte) keyValue kvs = append(kvs, keyValue{Key: idKey, Val: event.AggregateID()}) kvs = append(kvs, keyValue{Key: eventTypeKey, Val: "'" + event.EventType() + "'"}) } - kvs = append(kvs, keyValue{Key: versionKey, Val: fmt.Sprintf("%v", event.Version())}) - kvs = append(kvs, keyValue{Key: timestampKey, Val: fmt.Sprintf("%v", event.Timestamp().UnixNano())}) + kvs = append(kvs, keyValue{Key: versionKey, Val: strconv.FormatUint(event.Version(), 10)}) + kvs = append(kvs, keyValue{Key: timestampKey, Val: strconv.FormatInt(event.Timestamp().UnixNano(), 10)}) kvs = append(kvs, keyValue{Key: snapshotKey, Val: encodeToBlob(data)}) serviceID, ok := event.ServiceID() if ok { @@ -69,7 +70,7 @@ func eventToKeyValues(event eventstore.Event, insert bool, data []byte) keyValue etagData := event.ETag() if etagData != nil { kvs = append(kvs, keyValue{Key: etagKey, Val: encodeToBlob(etagData.ETag)}) - kvs = append(kvs, keyValue{Key: etagTimestampKey, Val: fmt.Sprintf("%v", etagData.Timestamp)}) + kvs = append(kvs, keyValue{Key: etagTimestampKey, Val: strconv.FormatInt(etagData.Timestamp, 10)}) } else { kvs = append(kvs, keyValue{Key: etagKey, Val: "null"}) kvs = append(kvs, keyValue{Key: etagTimestampKey, Val: "0"}) @@ -88,7 +89,7 @@ func (s *EventStore) saveEvent(ctx context.Context, events []eventstore.Event) ( return eventstore.Fail, err } setters := eventsToCQLSetValue(lastEvent, snapshotBinary) - q := "update " + s.client.Keyspace() + "." + s.config.Table + " set " + setters + " " + cqldb.WhereClause + " " + deviceIDKey + "=" + lastEvent.GroupID() + " and " + idKey + "=" + lastEvent.AggregateID() + " if " + versionKey + "=" + fmt.Sprintf("%v", events[0].Version()-1) + ";" + q := "update " + s.client.Keyspace() + "." + s.config.Table + " set " + setters + " " + cqldb.WhereClause + " " + deviceIDKey + "=" + lastEvent.GroupID() + " and " + idKey + "=" + lastEvent.AggregateID() + " if " + versionKey + "=" + strconv.FormatUint(events[0].Version()-1, 10) + ";" ok, err := s.client.Session().Query(q).WithContext(ctx).ScanCAS(nil) if err != nil { return eventstore.Fail, fmt.Errorf("cannot update snapshot event('%v'): %w", events, err) @@ -108,27 +109,27 @@ func (s *EventStore) Save(ctx context.Context, events ...eventstore.Event) (even if err := eventstore.ValidateEventsBeforeSave(events); err != nil { return eventstore.Fail, err } - if events[0].Version() == 0 { - lastEvent, data, err := getLatestEventsSnapshot(events, s.config.marshalerFunc) - if err != nil { - return eventstore.Fail, err - } - kvs := eventToKeyValues(lastEvent, true, data) - keys := kvs.Keys() - values := kvs.Values() + if events[0].Version() != 0 { + return s.saveEvent(ctx, events) + } + lastEvent, data, err := getLatestEventsSnapshot(events, s.config.marshalerFunc) + if err != nil { + return eventstore.Fail, err + } + kvs := eventToKeyValues(lastEvent, true, data) + keys := kvs.Keys() + values := kvs.Values() - q := "insert into " + s.Table() + " (" + strings.Join(keys, ",") + ") values (" + strings.Join(values, ",") + ") if not exists;" - ok, err := s.client.Session().Query(q).WithContext(ctx).ScanCAS(nil, nil, nil, nil, nil, nil, nil, nil, nil) - if err != nil { - if errors.Is(err, gocql.ErrNotFound) { - return eventstore.Ok, nil - } - return eventstore.Fail, fmt.Errorf("cannot insert first events('%v'): %w", events, err) - } - if !ok { - return eventstore.ConcurrencyException, nil + q := "insert into " + s.Table() + " (" + strings.Join(keys, ",") + ") values (" + strings.Join(values, ",") + ") if not exists;" + ok, err := s.client.Session().Query(q).WithContext(ctx).ScanCAS(nil, nil, nil, nil, nil, nil, nil, nil, nil) + if err != nil { + if errors.Is(err, gocql.ErrNotFound) { + return eventstore.Ok, nil } - return eventstore.Ok, nil + return eventstore.Fail, fmt.Errorf("cannot insert first events('%v'): %w", events, err) } - return s.saveEvent(ctx, events) + if !ok { + return eventstore.ConcurrencyException, nil + } + return eventstore.Ok, nil } diff --git a/resource-aggregate/cqrs/eventstore/event.go b/resource-aggregate/cqrs/eventstore/event.go index 68d4d3e18..10065bf49 100644 --- a/resource-aggregate/cqrs/eventstore/event.go +++ b/resource-aggregate/cqrs/eventstore/event.go @@ -2,6 +2,7 @@ package eventstore import ( "context" + "errors" "fmt" "time" @@ -23,6 +24,7 @@ type Event = interface { ServiceID() (string, bool) Timestamp() time.Time ETag() *ETagData + Types() []string } // EventUnmarshaler provides event. @@ -153,7 +155,7 @@ func validateFirstEvent(event Event) error { } if event.Timestamp().IsZero() { - return fmt.Errorf("invalid zero events[0].Timestamp") + return errors.New("invalid zero events[0].Timestamp") } return nil diff --git a/resource-aggregate/cqrs/eventstore/event_test.go b/resource-aggregate/cqrs/eventstore/event_test.go index 3d3ce3179..7b2c3348a 100644 --- a/resource-aggregate/cqrs/eventstore/event_test.go +++ b/resource-aggregate/cqrs/eventstore/event_test.go @@ -14,6 +14,7 @@ type mockEvent struct { etagData *ETagData isSnapshot bool serviceID string + types []string } func (e *mockEvent) AggregateID() string { @@ -51,6 +52,10 @@ func (e *mockEvent) IsSnapshot() bool { return e.isSnapshot } +func (e *mockEvent) Types() []string { + return e.types +} + func TestValidateEventsBeforeSave(t *testing.T) { ev := mockEvent{ aggregateID: "d9e7e4a0-49b7-4e6e-8f00-9ebefb3f6f5d", diff --git a/resource-aggregate/cqrs/eventstore/eventstore.go b/resource-aggregate/cqrs/eventstore/eventstore.go index a67bd9c31..c6f0a2e02 100644 --- a/resource-aggregate/cqrs/eventstore/eventstore.go +++ b/resource-aggregate/cqrs/eventstore/eventstore.go @@ -2,7 +2,7 @@ package eventstore import ( "context" - "fmt" + "errors" ) // VersionQuery used to load events from version. @@ -14,15 +14,17 @@ type VersionQuery struct { // SnapshotQuery used to load events from snapshot. type SnapshotQuery struct { - GroupID string // filter by group ID - AggregateID string // filter to certain aggregateID, groupID is required + GroupID string // filter by group ID + AggregateID string // filter to certain aggregateID, groupID is required + Types []string // filter to certain event types, optional } // Get events with given attributes. // All filtering options are optional, if none are given then all events are returned, type GetEventsQuery struct { - GroupID string // filter by group ID, optional - AggregateID string // filter to certain aggregateID, optional + GroupID string // filter by group ID, optional + AggregateID string // filter to certain aggregateID, optional + Types []string // filter to certain event types, optional } // Delete documents with given group id @@ -69,4 +71,4 @@ type EventStore interface { } // ErrNotSupported is returned when the operation is not supported. -var ErrNotSupported = fmt.Errorf("not supported") +var ErrNotSupported = errors.New("not supported") diff --git a/resource-aggregate/cqrs/eventstore/mongodb/acceptance_testing_events_test.go b/resource-aggregate/cqrs/eventstore/mongodb/acceptance_testing_events_test.go index 4c62467b8..15a0aa328 100644 --- a/resource-aggregate/cqrs/eventstore/mongodb/acceptance_testing_events_test.go +++ b/resource-aggregate/cqrs/eventstore/mongodb/acceptance_testing_events_test.go @@ -3,7 +3,6 @@ package mongodb_test import ( "context" "errors" - "fmt" "reflect" "sort" "sync" @@ -20,7 +19,7 @@ var ( UserId: "userId", } - errCannotUnmarshalEvent = fmt.Errorf("cannot unmarshal event") + errCannotUnmarshalEvent = errors.New("cannot unmarshal event") ) func MakeResourceLinksPublishedEvent(resources []*commands.Resource, deviceID string, eventMetadata *events.EventMetadata) eventstore.EventUnmarshaler { @@ -423,15 +422,16 @@ func MakeDeviceMetadata(deviceID string, deviceMetadataUpdated *events.DeviceMet } type MockEvent struct { - VersionI uint64 `bson:"version"` - EventTypeI string `bson:"eventtype"` - IsSnapshotI bool `bson:"issnapshot"` - AggregateIDI string `bson:"aggregateid"` - GroupIDI string `bson:"groupid"` - DataI []byte `bson:"data"` - TimestampI int64 `bson:"timestamp"` - ETagI []byte `bson:"etag"` - ServiceIDI string `bson:"serviceid"` + VersionI uint64 `bson:"version"` + EventTypeI string `bson:"eventtype"` + IsSnapshotI bool `bson:"issnapshot"` + AggregateIDI string `bson:"aggregateid"` + GroupIDI string `bson:"groupid"` + DataI []byte `bson:"data"` + TimestampI int64 `bson:"timestamp"` + ETagI []byte `bson:"etag"` + ServiceIDI string `bson:"serviceid"` + ResourceTypesI []string `bson:"resourcetypes"` } func (e MockEvent) Version() uint64 { @@ -472,6 +472,10 @@ func (e MockEvent) ServiceID() (string, bool) { return e.ServiceIDI, true } +func (e MockEvent) Types() []string { + return e.ResourceTypesI +} + type MockEventHandler struct { lock sync.Mutex events map[string]map[string][]eventstore.Event @@ -481,6 +485,14 @@ func NewMockEventHandler() *MockEventHandler { return &MockEventHandler{events: make(map[string]map[string][]eventstore.Event)} } +func (eh *MockEventHandler) PopEvents() map[string]map[string][]eventstore.Event { + eh.lock.Lock() + defer eh.lock.Unlock() + events := eh.events + eh.events = make(map[string]map[string][]eventstore.Event) + return events +} + func (eh *MockEventHandler) SetElement(groupID, aggregateID string, e MockEvent) { var device map[string][]eventstore.Event var ok bool diff --git a/resource-aggregate/cqrs/eventstore/mongodb/acceptance_testing_eventstore_test.go b/resource-aggregate/cqrs/eventstore/mongodb/acceptance_testing_eventstore_test.go index 90bebaac3..8135ac720 100644 --- a/resource-aggregate/cqrs/eventstore/mongodb/acceptance_testing_eventstore_test.go +++ b/resource-aggregate/cqrs/eventstore/mongodb/acceptance_testing_eventstore_test.go @@ -3,7 +3,6 @@ package mongodb_test import ( "context" "errors" - "fmt" "github.com/plgd-dev/hub/v2/resource-aggregate/cqrs/eventstore" ) @@ -132,7 +131,7 @@ func (s *MockEventStore) LoadFromSnapshot(ctx context.Context, queries []eventst ret = append(ret, q) } if len(ret) == 0 { - return fmt.Errorf("cannot load events: not found") + return errors.New("cannot load events: not found") } return s.LoadFromVersion(ctx, ret, eventHandler) diff --git a/resource-aggregate/cqrs/eventstore/mongodb/acceptance_testing_test.go b/resource-aggregate/cqrs/eventstore/mongodb/acceptance_testing_test.go index e228170d8..229540a5c 100644 --- a/resource-aggregate/cqrs/eventstore/mongodb/acceptance_testing_test.go +++ b/resource-aggregate/cqrs/eventstore/mongodb/acceptance_testing_test.go @@ -298,7 +298,7 @@ func AcceptanceTest(ctx context.Context, t *testing.T, store eventstore.EventSto eh1 := NewMockEventHandler() err = store.LoadFromSnapshot(ctx, []eventstore.SnapshotQuery{{GroupID: "notExist"}}, eh1) require.NoError(t, err) - require.Equal(t, 0, len(eh1.events)) + require.Empty(t, eh1.events) t.Log("load events") eh2 := NewMockEventHandler() @@ -392,7 +392,7 @@ func AcceptanceTest(ctx context.Context, t *testing.T, store eventstore.EventSto }, }, eh8) require.NoError(t, err) - require.Equal(t, 0, len(eh8.events[aggregateID1Path.GroupID][aggregateID1Path.AggregateID])) + require.Empty(t, eh8.events[aggregateID1Path.GroupID][aggregateID1Path.AggregateID]) t.Log("load events up to version without version specified") eh9 := NewMockEventHandler() @@ -403,7 +403,7 @@ func AcceptanceTest(ctx context.Context, t *testing.T, store eventstore.EventSto }, }, eh9) require.NoError(t, err) - require.Equal(t, 0, len(eh9.events[aggregateID1Path.GroupID][aggregateID1Path.AggregateID])) + require.Empty(t, eh9.events[aggregateID1Path.GroupID][aggregateID1Path.AggregateID]) t.Log("test projection all") model := NewMockEventHandler() diff --git a/resource-aggregate/cqrs/eventstore/mongodb/delete.go b/resource-aggregate/cqrs/eventstore/mongodb/delete.go index cb73ab476..80bf60d53 100644 --- a/resource-aggregate/cqrs/eventstore/mongodb/delete.go +++ b/resource-aggregate/cqrs/eventstore/mongodb/delete.go @@ -2,7 +2,7 @@ package mongodb import ( "context" - "fmt" + "errors" "github.com/plgd-dev/hub/v2/resource-aggregate/cqrs/eventstore" "github.com/plgd-dev/kit/v2/strings" @@ -29,7 +29,7 @@ func getDeviceIDFilter(queries []eventstore.DeleteQuery) bson.A { func (s *EventStore) Delete(ctx context.Context, queries []eventstore.DeleteQuery) error { deviceIDFilter := getDeviceIDFilter(queries) if len(deviceIDFilter) == 0 { - return fmt.Errorf("failed to delete documents: invalid query") + return errors.New("failed to delete documents: invalid query") } col := s.client().Database(s.DBName()).Collection(getEventCollectionName()) diff --git a/resource-aggregate/cqrs/eventstore/mongodb/delete_test.go b/resource-aggregate/cqrs/eventstore/mongodb/delete_test.go index 5aa075fa6..259c65511 100644 --- a/resource-aggregate/cqrs/eventstore/mongodb/delete_test.go +++ b/resource-aggregate/cqrs/eventstore/mongodb/delete_test.go @@ -62,7 +62,7 @@ func TestEventStoreDelete(t *testing.T) { ctx := context.Background() store, err := NewTestEventStore(ctx, fileWatcher, logger) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, store) defer func() { t.Log("clearing db") diff --git a/resource-aggregate/cqrs/eventstore/mongodb/eventstore.go b/resource-aggregate/cqrs/eventstore/mongodb/eventstore.go index 78d5e8247..7d84b0c52 100644 --- a/resource-aggregate/cqrs/eventstore/mongodb/eventstore.go +++ b/resource-aggregate/cqrs/eventstore/mongodb/eventstore.go @@ -49,6 +49,7 @@ const ( isActiveKey = "isactive" latestETagKey = "latestetag" etagKey = "etag" + typesKey = "types" latestETagKeyTimestampKey = latestETagKey + "." + timestampKey serviceIDKey = "serviceid" ) @@ -94,6 +95,12 @@ var groupIDETagLatestTimestampQueryIndex = bson.D{ {Key: latestETagKeyTimestampKey, Value: -1}, } +var groupIDTypesQueryIndex = bson.D{ + {Key: groupIDKey, Value: 1}, + {Key: typesKey, Value: 1}, + {Key: isActiveKey, Value: 1}, +} + type signOperator string const ( @@ -168,7 +175,7 @@ func newEventStoreWithClient(ctx context.Context, store *pkgMongo.Store, dbPrefi } if logDebugfFunc == nil { - logDebugfFunc = func(fmt string, args ...interface{}) { + logDebugfFunc = func(string, ...interface{}) { // no-op if not set } } @@ -206,6 +213,7 @@ func newEventStoreWithClient(ctx context.Context, store *pkgMongo.Store, dbPrefi aggregateIDLatestTimestampQueryIndex, groupIDETagLatestTimestampQueryIndex, serviceIDQueryIndex, + groupIDTypesQueryIndex, ) if err != nil { return nil, fmt.Errorf("cannot save events: %w", err) @@ -252,7 +260,7 @@ func getDocID(event eventstore.Event) string { } func getLatestSnapshotVersion(events []eventstore.Event) (uint64, error) { - err := fmt.Errorf("not found") + err := errors.New("not found") var latestSnapshotVersion uint64 for _, e := range events { if e.IsSnapshot() { @@ -290,7 +298,7 @@ func tryToSetServiceID(doc bson.M, events []eventstore.Event) bson.M { } func makeDBDoc(events []eventstore.Event, marshaler MarshalerFunc) (bson.M, error) { - etag, e, err := makeDBEventsAndGetETag(events, marshaler) + etag, types, e, err := makeDBEventsAndGetETag(events, marshaler) if err != nil { return nil, fmt.Errorf("cannot insert first events('%v'): %w", events, err) } @@ -313,6 +321,9 @@ func makeDBDoc(events []eventstore.Event, marshaler MarshalerFunc) (bson.M, erro if etag != nil { d[etagKey] = etag.ETag } + if len(types) > 0 { + d[typesKey] = types + } return tryToSetServiceID(d, events), nil } @@ -354,14 +365,15 @@ func (s *EventStore) Close(ctx context.Context) error { } // newDBEvent returns a new dbEvent for an eventstore. -func makeDBEventsAndGetETag(events []eventstore.Event, marshaler MarshalerFunc) (*eventstore.ETagData, []bson.M, error) { +func makeDBEventsAndGetETag(events []eventstore.Event, marshaler MarshalerFunc) (*eventstore.ETagData, []string, []bson.M, error) { dbEvents := make([]bson.M, 0, len(events)) var etag *eventstore.ETagData + var types []string for idx, event := range events { // Marshal event data if there is any. raw, err := marshaler(event) if err != nil { - return nil, nil, fmt.Errorf("cannot create db event from event[%v]: %w", idx, err) + return nil, nil, nil, fmt.Errorf("cannot create db event from event[%v]: %w", idx, err) } dbEvents = append(dbEvents, bson.M{ versionKey: event.Version(), @@ -374,6 +386,9 @@ func makeDBEventsAndGetETag(events []eventstore.Event, marshaler MarshalerFunc) if et != nil { etag = et } + if len(event.Types()) > 0 { + types = event.Types() + } } - return etag, dbEvents, nil + return etag, types, dbEvents, nil } diff --git a/resource-aggregate/cqrs/eventstore/mongodb/eventstore_test.go b/resource-aggregate/cqrs/eventstore/mongodb/eventstore_test.go index c556233be..2409bc29b 100644 --- a/resource-aggregate/cqrs/eventstore/mongodb/eventstore_test.go +++ b/resource-aggregate/cqrs/eventstore/mongodb/eventstore_test.go @@ -44,7 +44,7 @@ func TestEventStore(t *testing.T) { ctx := context.Background() store, err := NewTestEventStore(ctx, fileWatcher, logger) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, store) defer func() { t.Log("clearing db") diff --git a/resource-aggregate/cqrs/eventstore/mongodb/getLatestDeviceETags.go b/resource-aggregate/cqrs/eventstore/mongodb/getLatestDeviceETags.go index decf83cb7..d8f3bcf98 100644 --- a/resource-aggregate/cqrs/eventstore/mongodb/getLatestDeviceETags.go +++ b/resource-aggregate/cqrs/eventstore/mongodb/getLatestDeviceETags.go @@ -36,7 +36,7 @@ func decodeETag(cur *mongo.Cursor) ([]byte, error) { return nil, fmt.Errorf("cannot convert etag %T to primitive.Binary", etagRaw) } if len(etag.Data) == 0 { - return nil, fmt.Errorf("etag is empty") + return nil, errors.New("etag is empty") } return etag.Data, nil } @@ -49,7 +49,7 @@ func (s *EventStore) GetLatestDeviceETags(ctx context.Context, deviceID string, s.LogDebugfFunc("mongodb.Evenstore.GetLatestETag takes %v", time.Since(t)) }() if deviceID == "" { - return nil, fmt.Errorf("deviceID is invalid") + return nil, errors.New("deviceID is invalid") } filter := bson.D{ bson.E{Key: groupIDKey, Value: deviceID}, diff --git a/resource-aggregate/cqrs/eventstore/mongodb/getLatestDeviceETags_test.go b/resource-aggregate/cqrs/eventstore/mongodb/getLatestDeviceETags_test.go index 3847cf617..6cb6ed468 100644 --- a/resource-aggregate/cqrs/eventstore/mongodb/getLatestDeviceETags_test.go +++ b/resource-aggregate/cqrs/eventstore/mongodb/getLatestDeviceETags_test.go @@ -23,7 +23,7 @@ func TestGetLatestDeviceETAG(t *testing.T) { ctx := context.Background() store, err := NewTestEventStore(ctx, fileWatcher, logger) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, store) defer func() { t.Log("clearing db") diff --git a/resource-aggregate/cqrs/eventstore/mongodb/getevents.go b/resource-aggregate/cqrs/eventstore/mongodb/getevents.go index 777ea40d1..f9a77bd4b 100644 --- a/resource-aggregate/cqrs/eventstore/mongodb/getevents.go +++ b/resource-aggregate/cqrs/eventstore/mongodb/getevents.go @@ -2,7 +2,7 @@ package mongodb import ( "context" - "fmt" + "errors" "time" "github.com/hashicorp/go-multierror" @@ -107,7 +107,7 @@ func getEventsQueriesToMongoQuery(groupID string, queries []eventstore.GetEvents func (s *EventStore) getEvents(ctx context.Context, groupID string, queries []eventstore.GetEventsQuery, timestamp int64, eventHandler eventstore.Handler) error { filter, opts := getEventsQueriesToMongoQuery(groupID, queries, timestamp) - return s.loadEventsQuery(ctx, eventHandler, nil, filter, opts) + return s.loadEventsQuery(ctx, eventHandler, nil, []mongoQuery{{filter: filter, options: opts}}) } type ResourceIdFilter struct { @@ -163,7 +163,7 @@ func (s *EventStore) GetEvents(ctx context.Context, queries []eventstore.GetEven s.LogDebugfFunc("mongodb.Evenstore.GetEvents takes %v", time.Since(t)) }() if len(queries) == 0 { - return fmt.Errorf("not supported") + return errors.New("not supported") } eventFilter := GetNormalizedGetEventsFilter(queries) diff --git a/resource-aggregate/cqrs/eventstore/mongodb/getevents_test.go b/resource-aggregate/cqrs/eventstore/mongodb/getevents_test.go index d826e2cd6..3d093163b 100644 --- a/resource-aggregate/cqrs/eventstore/mongodb/getevents_test.go +++ b/resource-aggregate/cqrs/eventstore/mongodb/getevents_test.go @@ -55,6 +55,10 @@ func getETag(deviceIndex int, resourceIndex int) []byte { return []byte("device" + strconv.Itoa(deviceIndex) + ".resource" + strconv.Itoa(resourceIndex)) } +func getTypes(resourceIndex int) []string { + return []string{"type" + strconv.Itoa(resourceIndex%3), "type" + strconv.Itoa((resourceIndex%3)+3)} +} + func getNLatestETag(deviceIndex int, limit int) [][]byte { if limit == 0 { limit = getEventsResourceCount / getEventsDeviceCount @@ -88,6 +92,7 @@ func addEventsForGetEventsToDB(ctx context.Context, t *testing.T, store *mongodb TimestampI: 1 + resourceTimestamp[resourceIndex], ETagI: getETag(deviceIndex, resourceIndex), ServiceIDI: getServiceID(serviceIndex), + TypesI: getTypes(resourceIndex), }) resourceVersion[resourceIndex]++ @@ -309,7 +314,7 @@ func Test_getNormalizedGetEventsFilter(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := mongodb.GetNormalizedGetEventsFilter(tt.args.queries) - require.Equal(t, got, tt.want) + require.Equal(t, tt.want, got) }) } } diff --git a/resource-aggregate/cqrs/eventstore/mongodb/load.go b/resource-aggregate/cqrs/eventstore/mongodb/load.go index 15a341678..3826c16b2 100644 --- a/resource-aggregate/cqrs/eventstore/mongodb/load.go +++ b/resource-aggregate/cqrs/eventstore/mongodb/load.go @@ -52,7 +52,7 @@ func (i *iterator) parseDocument() bool { return false } if len(i.events) == 0 { - i.err = fmt.Errorf("invalid data, no events found") + i.err = errors.New("invalid data, no events found") return false } i.groupID, ok = doc[groupIDKey].(string) @@ -199,22 +199,29 @@ func (r *queryResolver) check(aggregateID string, version int64) bool { } // Create mongodb find query to load events -func (s *EventStore) loadEventsQuery(ctx context.Context, eh eventstore.Handler, queryResolver *queryResolver, filter interface{}, opts ...*options.FindOptions) error { +func (s *EventStore) loadEventsQuery(ctx context.Context, eh eventstore.Handler, queryResolver *queryResolver, queries []mongoQuery) error { col := s.client().Database(s.DBName()).Collection(getEventCollectionName()) - iter, err := col.Find(ctx, filter, opts...) - if errors.Is(err, mongo.ErrNilDocument) { - return nil - } - if err != nil { - return err - } - i := newIterator(iter, queryResolver, s.dataUnmarshaler, s.LogDebugfFunc) - err = eh.Handle(ctx, i) - errClose := iter.Close(ctx) - if err == nil { - return errClose + var errs *multierror.Error + for _, q := range queries { + iter, err := col.Find(ctx, q.filter, q.options) + if errors.Is(err, mongo.ErrNilDocument) { + continue + } + if err != nil { + errs = multierror.Append(errs, err) + continue + } + i := newIterator(iter, queryResolver, s.dataUnmarshaler, s.LogDebugfFunc) + err = eh.Handle(ctx, i) + if err != nil { + errs = multierror.Append(errs, err) + } + err = iter.Close(ctx) + if err != nil { + errs = multierror.Append(errs, err) + } } - return err + return errs.ErrorOrNil() } func (r *queryResolver) toMongoQuery(maxVersionKey string) (filter bson.M, hint bson.D) { @@ -288,10 +295,15 @@ func (s *EventStore) loadMongoQuery(ctx context.Context, eh eventstore.Handler, filter, hint := queryResolver.toMongoQuery(maxVersionKey) opts := options.Find() opts.SetHint(hint) - return s.loadEventsQuery(ctx, eh, queryResolver, filter, opts) + return s.loadEventsQuery(ctx, eh, queryResolver, []mongoQuery{{filter: filter, options: opts}}) +} + +type mongoQuery struct { + filter interface{} + options *options.FindOptions } -func snapshotQueriesToMongoQuery(groupID string, queries []eventstore.SnapshotQuery) (interface{}, *options.FindOptions) { +func snapshotQueriesToMongoQuery(groupID string, queries []eventstore.SnapshotQuery) []mongoQuery { opts := options.Find() opts.SetAllowDiskUse(true) opts.SetProjection(bson.M{ @@ -309,57 +321,183 @@ func snapshotQueriesToMongoQuery(groupID string, queries []eventstore.SnapshotQu }) if len(queries) == 0 { opts.SetHint(groupIDQueryIndex) - return bson.D{ - {Key: groupIDKey, Value: groupID}, {Key: isActiveKey, Value: true}, - }, opts + return []mongoQuery{ + { + filter: bson.D{ + {Key: groupIDKey, Value: groupID}, {Key: isActiveKey, Value: true}, + }, + options: opts, + }, + } } - opts.SetHint(groupIDaggregateIDQueryIndex) - orQueries := make([]bson.D, 0, 32) + optsTypes := options.Find() + // create a copy of the options + *optsTypes = *opts + resourceQueries := make([]bson.D, 0, 32) + typeQueries := make([]bson.D, 0, 32) for _, q := range queries { + if q.AggregateID == "" && len(q.Types) == 0 { + opts.SetHint(groupIDQueryIndex) + return []mongoQuery{ + { + filter: bson.D{ + {Key: groupIDKey, Value: groupID}, {Key: isActiveKey, Value: true}, + }, + options: opts, + }, + } + } + if q.AggregateID == "" && len(q.Types) > 0 { + optsTypes.SetHint(groupIDTypesQueryIndex) + typeQueries = append(typeQueries, bson.D{{Key: groupIDKey, Value: groupID}, {Key: typesKey, Value: bson.M{"$all": q.Types}}, {Key: isActiveKey, Value: true}}) + continue + } + if q.AggregateID != "" { - orQueries = append(orQueries, bson.D{{Key: groupIDKey, Value: groupID}, {Key: aggregateIDKey, Value: q.AggregateID}, {Key: isActiveKey, Value: true}}) + opts.SetHint(groupIDaggregateIDQueryIndex) + resourceQueries = append(resourceQueries, bson.D{{Key: groupIDKey, Value: groupID}, {Key: aggregateIDKey, Value: q.AggregateID}, {Key: isActiveKey, Value: true}}) } } - return bson.M{ - "$or": orQueries, - }, opts + + r := make([]mongoQuery, 0, 2) + if len(resourceQueries) > 0 { + r = append(r, mongoQuery{ + filter: bson.M{"$or": resourceQueries}, + options: opts, + }) + } + if len(typeQueries) > 0 { + r = append(r, mongoQuery{ + filter: bson.M{"$or": typeQueries}, + options: optsTypes, + }) + } + return r } func (s *EventStore) loadFromSnapshot(ctx context.Context, groupID string, queries []eventstore.SnapshotQuery, eventHandler eventstore.Handler) error { - filter, opts := snapshotQueriesToMongoQuery(groupID, queries) - return s.loadEventsQuery(ctx, eventHandler, nil, filter, opts) + mongoQueries := snapshotQueriesToMongoQuery(groupID, queries) + if len(mongoQueries) > 1 { + return errors.New("too many types of queries") + } + return s.loadEventsQuery(ctx, eventHandler, nil, mongoQueries) } -// LoadFromSnapshot loads events from the last snapshot eventstore. -func (s *EventStore) LoadFromSnapshot(ctx context.Context, queries []eventstore.SnapshotQuery, eventHandler eventstore.Handler) error { - s.LogDebugfFunc("mongodb.Evenstore.LoadFromSnapshot start") - t := time.Now() - defer func() { - s.LogDebugfFunc("mongodb.Evenstore.LoadFromSnapshot takes %v", time.Since(t)) - }() - if len(queries) == 0 { - return fmt.Errorf("not supported") +func resourceTypesIsSubset(slice, subset []string) bool { + if len(slice) == 0 { + return false + } + if len(slice) > len(subset) { + return false + } + set := make(map[string]struct{}, len(slice)) + for _, s := range slice { + set[s] = struct{}{} + } + for _, s := range subset { + delete(set, s) + if len(set) == 0 { + return true + } + } + return false +} + +func uniqueQueryWithEmptyAggregateID(queries []eventstore.SnapshotQuery, query eventstore.SnapshotQuery) []eventstore.SnapshotQuery { + if len(query.Types) == 0 { + // get all events without filter + return []eventstore.SnapshotQuery{query} + } + for idx, q := range queries { + if resourceTypesIsSubset(query.Types, q.Types) { + // get all events with the certain type, replace the queries with the more or equal general one + queries[idx] = query + queries = removeDuplicates(queries, idx, func(qa eventstore.SnapshotQuery) bool { return resourceTypesIsSubset(query.Types, qa.Types) }) + return queries + } + } + return append(queries, query) +} + +func uniqueQueryHandleAtIndex(queries []eventstore.SnapshotQuery, query eventstore.SnapshotQuery, idx int) ([]eventstore.SnapshotQuery, bool) { + q := queries[idx] + if q.AggregateID == "" && len(q.Types) == 0 || resourceTypesIsSubset(q.Types, query.Types) { + return queries, true // No need to add more specific one if there's a general query + } + + if q.AggregateID == query.AggregateID { + if len(query.Types) == 0 { + queries[idx] = query + queries = removeDuplicates(queries, idx, func(qa eventstore.SnapshotQuery) bool { return qa.AggregateID == query.AggregateID }) + return queries, true + } + if len(q.Types) == 0 || resourceTypesIsSubset(q.Types, query.Types) { + return queries, true // No need to add more general one if there's a more specific query + } + if resourceTypesIsSubset(query.Types, q.Types) { + // replace query with the more general one + queries[idx] = query + queries = removeDuplicates(queries, idx, func(qa eventstore.SnapshotQuery) bool { return resourceTypesIsSubset(qa.Types, query.Types) }) + return queries, true + } + } + return queries, false +} + +func uniqueQuery(queries []eventstore.SnapshotQuery, query eventstore.SnapshotQuery) []eventstore.SnapshotQuery { + if query.AggregateID == "" { + return uniqueQueryWithEmptyAggregateID(queries, query) + } + + for idx := range queries { + newQueries, handled := uniqueQueryHandleAtIndex(queries, query, idx) + if handled { + return newQueries + } } - normalizeQuery := make(map[string][]eventstore.SnapshotQuery) + return append(queries, query) +} + +func removeDuplicates(queries []eventstore.SnapshotQuery, startIdx int, remove func(q eventstore.SnapshotQuery) bool) []eventstore.SnapshotQuery { + for i := startIdx + 1; i < len(queries); i++ { + if remove(queries[i]) { + queries = append(queries[:i], queries[i+1:]...) + i-- + } + } + return queries +} + +func normalizeSnapshotQuery(queries []eventstore.SnapshotQuery) map[string][]eventstore.SnapshotQuery { + normalizedQuery := make(map[string][]eventstore.SnapshotQuery, len(queries)) + // split queries by groupID for _, query := range queries { if query.GroupID == "" { continue } - if query.AggregateID == "" { - normalizeQuery[query.GroupID] = make([]eventstore.SnapshotQuery, 0, 1) - continue - } - v, ok := normalizeQuery[query.GroupID] + v, ok := normalizedQuery[query.GroupID] if !ok { v = make([]eventstore.SnapshotQuery, 0, 4) - } else if len(v) == 0 { - continue } - v = append(v, query) - normalizeQuery[query.GroupID] = v + normalizedQuery[query.GroupID] = uniqueQuery(v, query) } + return normalizedQuery +} + +// LoadFromSnapshot loads events from the last snapshot eventstore. +func (s *EventStore) LoadFromSnapshot(ctx context.Context, queries []eventstore.SnapshotQuery, eventHandler eventstore.Handler) error { + s.LogDebugfFunc("mongodb.Evenstore.LoadFromSnapshot start") + t := time.Now() + defer func() { + s.LogDebugfFunc("mongodb.Evenstore.LoadFromSnapshot takes %v", time.Since(t)) + }() + if len(queries) == 0 { + return errors.New("not supported") + } + + normalizeQuery := normalizeSnapshotQuery(queries) var errors *multierror.Error for groupID, queries := range normalizeQuery { diff --git a/resource-aggregate/cqrs/eventstore/mongodb/loadDeviceMetadataByServiceIDs.go b/resource-aggregate/cqrs/eventstore/mongodb/loadDeviceMetadataByServiceIDs.go index 331b0811d..03a613c25 100644 --- a/resource-aggregate/cqrs/eventstore/mongodb/loadDeviceMetadataByServiceIDs.go +++ b/resource-aggregate/cqrs/eventstore/mongodb/loadDeviceMetadataByServiceIDs.go @@ -20,7 +20,7 @@ func (s *EventStore) LoadDeviceMetadataByServiceIDs(ctx context.Context, service s.LogDebugfFunc("mongodb.Evenstore.LoadDocMetadataFromByServiceIDs takes %v", time.Since(t)) }() if len(serviceIDs) == 0 { - return nil, fmt.Errorf("not supported") + return nil, errors.New("not supported") } serviceIDs = strings.Unique(serviceIDs) diff --git a/resource-aggregate/cqrs/eventstore/mongodb/loadDeviceMetadataByServiceIDs_test.go b/resource-aggregate/cqrs/eventstore/mongodb/loadDeviceMetadataByServiceIDs_test.go index 2cda0511d..cf865b675 100644 --- a/resource-aggregate/cqrs/eventstore/mongodb/loadDeviceMetadataByServiceIDs_test.go +++ b/resource-aggregate/cqrs/eventstore/mongodb/loadDeviceMetadataByServiceIDs_test.go @@ -23,7 +23,7 @@ func TestLoadDeviceMetadataByServiceIDs(t *testing.T) { ctx := context.Background() store, err := NewTestEventStore(ctx, fileWatcher, logger) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, store) defer func() { t.Log("clearing db") diff --git a/resource-aggregate/cqrs/eventstore/mongodb/load_internal_test.go b/resource-aggregate/cqrs/eventstore/mongodb/load_internal_test.go new file mode 100644 index 000000000..e91bf1ece --- /dev/null +++ b/resource-aggregate/cqrs/eventstore/mongodb/load_internal_test.go @@ -0,0 +1,163 @@ +package mongodb + +import ( + "testing" + + "github.com/plgd-dev/hub/v2/resource-aggregate/cqrs/eventstore" + "github.com/stretchr/testify/require" +) + +func TestUniqueQuery(t *testing.T) { + type args struct { + queries []eventstore.SnapshotQuery + query eventstore.SnapshotQuery + } + test := []struct { + name string + args args + want []eventstore.SnapshotQuery + }{ + { + name: "first query", + args: args{ + queries: nil, + query: eventstore.SnapshotQuery{AggregateID: "1", Types: []string{"type1"}}, + }, + want: []eventstore.SnapshotQuery{ + {AggregateID: "1", Types: []string{"type1"}}, + }, + }, + { + name: "same query", + args: args{ + queries: []eventstore.SnapshotQuery{ + {AggregateID: "1", Types: []string{"type1"}}, + }, + query: eventstore.SnapshotQuery{AggregateID: "1", Types: []string{"type1"}}, + }, + want: []eventstore.SnapshotQuery{ + {AggregateID: "1", Types: []string{"type1"}}, + }, + }, + { + name: "two queries", + args: args{ + queries: []eventstore.SnapshotQuery{ + {AggregateID: "1", Types: []string{"type1"}}, + }, + query: eventstore.SnapshotQuery{Types: []string{"type2"}}, + }, + want: []eventstore.SnapshotQuery{ + {AggregateID: "1", Types: []string{"type1"}}, + {Types: []string{"type2"}}, + }, + }, + { + name: "most general query", + args: args{ + queries: []eventstore.SnapshotQuery{ + {AggregateID: "1", Types: []string{"type1"}}, + }, + query: eventstore.SnapshotQuery{}, + }, + want: []eventstore.SnapshotQuery{ + {}, + }, + }, + { + name: "replace a query with more general query", + args: args{ + queries: []eventstore.SnapshotQuery{ + {AggregateID: "1", Types: []string{"type1"}}, + {AggregateID: "1", Types: []string{"type2"}}, + {AggregateID: "2", Types: []string{"type2"}}, + }, + query: eventstore.SnapshotQuery{Types: []string{"type2"}}, + }, + want: []eventstore.SnapshotQuery{ + {AggregateID: "1", Types: []string{"type1"}}, + {Types: []string{"type2"}}, + }, + }, + { + name: "replace more queries with more general query with the aggregateID", + args: args{ + queries: []eventstore.SnapshotQuery{ + {AggregateID: "1", Types: []string{"type1"}}, + {AggregateID: "1", Types: []string{"type2"}}, + }, + query: eventstore.SnapshotQuery{AggregateID: "1"}, + }, + want: []eventstore.SnapshotQuery{ + {AggregateID: "1"}, + }, + }, + { + name: "replace a query with more general query with the aggregateID", + args: args{ + queries: []eventstore.SnapshotQuery{ + {AggregateID: "1", Types: []string{"type2", "type1"}}, + }, + query: eventstore.SnapshotQuery{AggregateID: "1", Types: []string{"type1"}}, + }, + want: []eventstore.SnapshotQuery{ + {AggregateID: "1", Types: []string{"type1"}}, + }, + }, + { + name: "use general query instead of more specific query with the aggregateID", + args: args{ + queries: []eventstore.SnapshotQuery{ + {AggregateID: "1", Types: []string{"type1"}}, + }, + query: eventstore.SnapshotQuery{AggregateID: "1", Types: []string{"type1", "type2"}}, + }, + want: []eventstore.SnapshotQuery{ + {AggregateID: "1", Types: []string{"type1"}}, + }, + }, + { + name: "use general query instead of more specific query with multiple type and the aggregateID ", + args: args{ + queries: []eventstore.SnapshotQuery{ + {AggregateID: "1", Types: []string{"type1", "type2", "type3"}}, + }, + query: eventstore.SnapshotQuery{AggregateID: "1", Types: []string{"type1", "type3"}}, + }, + want: []eventstore.SnapshotQuery{ + {AggregateID: "1", Types: []string{"type1", "type3"}}, + }, + }, + { + name: "use general query instead of more specific query", + args: args{ + queries: []eventstore.SnapshotQuery{ + {Types: []string{"type1"}}, + }, + query: eventstore.SnapshotQuery{AggregateID: "1", Types: []string{"type1", "type2"}}, + }, + want: []eventstore.SnapshotQuery{ + {Types: []string{"type1"}}, + }, + }, + { + name: "general and specific query with types", + args: args{ + queries: []eventstore.SnapshotQuery{ + {Types: []string{"type2", "type1"}}, + }, + query: eventstore.SnapshotQuery{AggregateID: "1", Types: []string{"type1"}}, + }, + want: []eventstore.SnapshotQuery{ + {Types: []string{"type2", "type1"}}, + {AggregateID: "1", Types: []string{"type1"}}, + }, + }, + } + for _, tt := range test { + t.Run(tt.name, func(t *testing.T) { + got := uniqueQuery(tt.args.queries, tt.args.query) + require.Equal(t, tt.want, got) + }) + } +} diff --git a/resource-aggregate/cqrs/eventstore/mongodb/load_test.go b/resource-aggregate/cqrs/eventstore/mongodb/load_test.go new file mode 100644 index 000000000..3f6c376f5 --- /dev/null +++ b/resource-aggregate/cqrs/eventstore/mongodb/load_test.go @@ -0,0 +1,126 @@ +package mongodb_test + +import ( + "context" + "testing" + + "github.com/plgd-dev/hub/v2/pkg/fsnotify" + "github.com/plgd-dev/hub/v2/pkg/log" + "github.com/plgd-dev/hub/v2/resource-aggregate/cqrs/eventstore" + "github.com/stretchr/testify/require" +) + +func TestLoadFromSnapshot(t *testing.T) { + type args struct { + queries []eventstore.SnapshotQuery + } + type device struct { + ID string + NumResources int + } + tests := []struct { + name string + args args + want []device + wantErr bool + }{ + { + name: "All group events", + args: args{ + queries: []eventstore.SnapshotQuery{ + { + GroupID: getDeviceID(0), + }, + }, + }, + want: []device{ + { + ID: getDeviceID(0), + NumResources: 20, + }, + }, + }, + { + name: "Group events with aggregateID", + args: args{ + queries: []eventstore.SnapshotQuery{ + { + GroupID: getDeviceID(0), + AggregateID: getAggregateID(0), + }, + }, + }, + want: []device{ + { + ID: getDeviceID(0), + NumResources: 1, + }, + }, + }, + { + name: "Group events with type filter", + args: args{ + queries: []eventstore.SnapshotQuery{ + { + GroupID: getDeviceID(0), + Types: getTypes(0), + }, + { + GroupID: getDeviceID(1), + Types: getTypes(3)[0:1], + }, + }, + }, + want: []device{ + { + ID: getDeviceID(0), + NumResources: 7, + }, + { + ID: getDeviceID(1), + NumResources: 6, + }, + }, + }, + } + logger := log.NewLogger(log.MakeDefaultConfig()) + fileWatcher, err := fsnotify.NewWatcher(logger) + require.NoError(t, err) + defer func() { + errC := fileWatcher.Close() + require.NoError(t, errC) + }() + + ctx := context.Background() + store, err := NewTestEventStore(ctx, fileWatcher, logger) + require.NoError(t, err) + require.NotNil(t, store) + defer func() { + t.Log("clearing db") + err = store.Clear(ctx) + require.NoError(t, err) + err := store.Close(ctx) + require.NoError(t, err) + }() + + _ = addEventsForGetEventsToDB(ctx, t, store) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + h := NewMockEventHandler() + err := store.LoadFromSnapshot(ctx, tt.args.queries, h) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + events := h.PopEvents() + require.Len(t, events, len(tt.want)) + for _, want := range tt.want { + ags, ok := events[want.ID] + require.True(t, ok) + require.Len(t, ags, want.NumResources) + } + }) + } +} diff --git a/resource-aggregate/cqrs/eventstore/mongodb/maintenance_test.go b/resource-aggregate/cqrs/eventstore/mongodb/maintenance_test.go index 8acf362f3..7aa5ea00d 100644 --- a/resource-aggregate/cqrs/eventstore/mongodb/maintenance_test.go +++ b/resource-aggregate/cqrs/eventstore/mongodb/maintenance_test.go @@ -75,7 +75,7 @@ func TestMaintenance(t *testing.T) { mongodb.WithMarshaler(bson.Marshal), mongodb.WithUnmarshaler(bson.Unmarshal), ) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, store) defer func() { t.Log("clearing db") @@ -161,5 +161,5 @@ func TestMaintenance(t *testing.T) { eh3 := newMockRecordHandler() err = store.Query(ctx, 777, eh3) require.NoError(t, err) - require.Equal(t, 0, len(eh3.tasks)) + require.Empty(t, eh3.tasks) } diff --git a/resource-aggregate/cqrs/eventstore/mongodb/save.go b/resource-aggregate/cqrs/eventstore/mongodb/save.go index 2e3978574..a95f1f192 100644 --- a/resource-aggregate/cqrs/eventstore/mongodb/save.go +++ b/resource-aggregate/cqrs/eventstore/mongodb/save.go @@ -39,7 +39,7 @@ func IsDup(err error) bool { } func (s *EventStore) saveEvent(ctx context.Context, col *mongo.Collection, events []eventstore.Event) (status eventstore.SaveStatus, err error) { - etag, e, err := makeDBEventsAndGetETag(events, s.dataMarshaler) + etag, types, e, err := makeDBEventsAndGetETag(events, s.dataMarshaler) if err != nil { return eventstore.Fail, err } @@ -57,6 +57,9 @@ func (s *EventStore) saveEvent(ctx context.Context, col *mongo.Collection, event updateSet[latestETagKey] = makeDBETag(etag) } tryToSetServiceID(updateSet, events) + if len(types) > 0 { + updateSet[typesKey] = types + } // find document of aggregate with previous version // latestVersion shall be lower by 1 as new event otherwise other event was stored (occ). diff --git a/resource-aggregate/cqrs/eventstore/projection.go b/resource-aggregate/cqrs/eventstore/projection.go index 5f7d8ed58..505f1f4bc 100644 --- a/resource-aggregate/cqrs/eventstore/projection.go +++ b/resource-aggregate/cqrs/eventstore/projection.go @@ -71,7 +71,7 @@ type Projection struct { // NewProjection projection over eventstore. func NewProjection(store EventStore, factoryModel FactoryModelFunc, logDebugfFunc LogDebugfFunc) *Projection { if logDebugfFunc == nil { - logDebugfFunc = func(fmt string, args ...interface{}) {} + logDebugfFunc = func(string, ...interface{}) {} } return &Projection{ store: store, @@ -97,7 +97,7 @@ func (i *iterator) RewindToNextAggregateEvent(ctx context.Context) EventUnmarsha if nextAggregateEvent != nil { return nextAggregateEvent } - if snapshot == nil && nextAggregateEvent == nil { + if snapshot == nil { return nil } } @@ -137,37 +137,37 @@ func (i *iterator) RewindIgnore(ctx context.Context) (EventUnmarshaler, bool) { } func (i *iterator) Next(ctx context.Context) (EventUnmarshaler, bool) { - if i.firstEvent != nil { - tmp := i.firstEvent - i.firstEvent = nil - ignore, reload := i.model.Update(tmp) - i.model.LogDebugfFunc("projection.iterator.next: GroupID %v: AggregateID %v: Version %v, EvenType %v, ignore %v reload %v", tmp.GroupID, tmp.AggregateID, tmp.Version, tmp.EventType, ignore, reload) - if reload { - snapshot, nextAggregateEvent := i.RewindToSnapshot(ctx) - if snapshot == nil { - i.nextEventToProcess = nextAggregateEvent - i.reload = &VersionQuery{GroupID: tmp.GroupID(), AggregateID: tmp.AggregateID(), Version: i.model.version} - return nil, false - } - tmp = snapshot - ignore, reload = i.model.Update(tmp) - if reload { - i.nextEventToProcess = i.RewindToNextAggregateEvent(ctx) - i.reload = &VersionQuery{GroupID: tmp.GroupID(), AggregateID: tmp.AggregateID(), Version: i.model.version} - return nil, false - } - } - if ignore { - return i.RewindIgnore(ctx) + if i.firstEvent == nil { + e, ok := i.RewindIgnore(ctx) + if ok { + i.model.LogDebugfFunc("projection.iterator.next: GroupID %v: AggregateID %v: Version %v, EvenType %v", e.GroupID, e.AggregateID, e.Version, e.EventType) } - return tmp, true + return e, ok } - e, ok := i.RewindIgnore(ctx) - if ok { - i.model.LogDebugfFunc("projection.iterator.next: GroupID %v: AggregateID %v: Version %v, EvenType %v", e.GroupID, e.AggregateID, e.Version, e.EventType) + tmp := i.firstEvent + i.firstEvent = nil + ignore, reload := i.model.Update(tmp) + i.model.LogDebugfFunc("projection.iterator.next: GroupID %v: AggregateID %v: Version %v, EvenType %v, ignore %v reload %v", tmp.GroupID, tmp.AggregateID, tmp.Version, tmp.EventType, ignore, reload) + if reload { + snapshot, nextAggregateEvent := i.RewindToSnapshot(ctx) + if snapshot == nil { + i.nextEventToProcess = nextAggregateEvent + i.reload = &VersionQuery{GroupID: tmp.GroupID(), AggregateID: tmp.AggregateID(), Version: i.model.version} + return nil, false + } + tmp = snapshot + ignore, reload = i.model.Update(tmp) + if reload { + i.nextEventToProcess = i.RewindToNextAggregateEvent(ctx) + i.reload = &VersionQuery{GroupID: tmp.GroupID(), AggregateID: tmp.AggregateID(), Version: i.model.version} + return nil, false + } + } + if ignore { + return i.RewindIgnore(ctx) } - return e, ok + return tmp, true } func (i *iterator) Err() error { diff --git a/resource-aggregate/cqrs/eventstore/test/acceptance_testing.go b/resource-aggregate/cqrs/eventstore/test/acceptance_testing.go index 072764543..ddc1fdafe 100644 --- a/resource-aggregate/cqrs/eventstore/test/acceptance_testing.go +++ b/resource-aggregate/cqrs/eventstore/test/acceptance_testing.go @@ -287,7 +287,7 @@ func AcceptanceTest(ctx context.Context, t *testing.T, store eventstore.EventSto eh1 := NewMockEventHandler() err = store.LoadFromSnapshot(ctx, []eventstore.SnapshotQuery{{GroupID: uuid.Nil.String()}}, eh1) require.NoError(t, err) - require.Equal(t, 0, len(eh1.events)) + require.Empty(t, eh1.events) t.Log("load events") eh2 := NewMockEventHandler() diff --git a/resource-aggregate/cqrs/eventstore/test/events.go b/resource-aggregate/cqrs/eventstore/test/events.go index 126df9e2f..95747c70d 100644 --- a/resource-aggregate/cqrs/eventstore/test/events.go +++ b/resource-aggregate/cqrs/eventstore/test/events.go @@ -3,7 +3,6 @@ package test import ( "context" "errors" - "fmt" "reflect" "sort" "sync" @@ -20,7 +19,7 @@ var ( UserId: "userId", } - errCannotUnmarshalEvent = fmt.Errorf("cannot unmarshal event") + errCannotUnmarshalEvent = errors.New("cannot unmarshal event") ) func MakeResourceLinksPublishedEvent(resources []*commands.Resource, deviceID string, eventMetadata *events.EventMetadata) eventstore.EventUnmarshaler { @@ -101,24 +100,23 @@ func MakeAuditContext(userID string, correlationID string) *commands.AuditContex } } -func MakeResourceUpdatePending(resourceID *commands.ResourceId, content *commands.Content, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext, validUntil time.Time) eventstore.EventUnmarshaler { - e := events.ResourceUpdatePending{ - ResourceId: resourceID, - Content: content, - AuditContext: auditContext, - EventMetadata: eventMetadata, - ValidUntil: pkgTime.UnixNano(validUntil), - } +func newResourceEvent[T interface { + GetResourceId() *commands.ResourceId + EventType() string + CopyData(e T) + GetEventMetadata() *events.EventMetadata + IsSnapshot() bool +}](e T) eventstore.EventUnmarshaler { return eventstore.NewLoadedEvent( e.GetEventMetadata().GetVersion(), - (&events.ResourceUpdatePending{}).EventType(), + e.EventType(), e.GetResourceId().ToUUID().String(), e.GetResourceId().GetDeviceId(), - false, + e.IsSnapshot(), time.Unix(0, e.GetEventMetadata().GetTimestamp()), func(v interface{}) error { - if x, ok := v.(*events.ResourceUpdatePending); ok { - x.CopyData(&e) + if x, ok := v.(T); ok { + x.CopyData(e) return nil } return errCannotUnmarshalEvent @@ -126,226 +124,121 @@ func MakeResourceUpdatePending(resourceID *commands.ResourceId, content *command ) } -func MakeResourceUpdated(resourceID *commands.ResourceId, status commands.Status, content *commands.Content, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext) eventstore.EventUnmarshaler { +func MakeResourceUpdatePending(resourceID *commands.ResourceId, content *commands.Content, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext, validUntil time.Time, resourceTypes []string) eventstore.EventUnmarshaler { + e := events.ResourceUpdatePending{ + ResourceId: resourceID, + Content: content, + AuditContext: auditContext, + EventMetadata: eventMetadata, + ValidUntil: pkgTime.UnixNano(validUntil), + ResourceTypes: resourceTypes, + } + return newResourceEvent(&e) +} + +func MakeResourceUpdated(resourceID *commands.ResourceId, status commands.Status, content *commands.Content, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext, resourceTypes []string) eventstore.EventUnmarshaler { e := events.ResourceUpdated{ ResourceId: resourceID, Content: content, Status: status, AuditContext: auditContext, EventMetadata: eventMetadata, + ResourceTypes: resourceTypes, } - return eventstore.NewLoadedEvent( - e.GetEventMetadata().GetVersion(), - (&events.ResourceUpdated{}).EventType(), - e.GetResourceId().ToUUID().String(), - e.GetResourceId().GetDeviceId(), - false, - time.Unix(0, e.GetEventMetadata().GetTimestamp()), - func(v interface{}) error { - if x, ok := v.(*events.ResourceUpdated); ok { - x.CopyData(&e) - return nil - } - return errCannotUnmarshalEvent - }, - ) + return newResourceEvent(&e) } -func MakeResourceCreatePending(resourceID *commands.ResourceId, content *commands.Content, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext, validUntil time.Time) eventstore.EventUnmarshaler { +func MakeResourceCreatePending(resourceID *commands.ResourceId, content *commands.Content, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext, validUntil time.Time, resourceTypes []string) eventstore.EventUnmarshaler { e := events.ResourceCreatePending{ ResourceId: resourceID, Content: content, AuditContext: auditContext, EventMetadata: eventMetadata, ValidUntil: pkgTime.UnixNano(validUntil), + ResourceTypes: resourceTypes, } - return eventstore.NewLoadedEvent( - e.GetEventMetadata().GetVersion(), - (&events.ResourceCreatePending{}).EventType(), - e.GetResourceId().ToUUID().String(), - e.GetResourceId().GetDeviceId(), - false, - time.Unix(0, e.GetEventMetadata().GetTimestamp()), - func(v interface{}) error { - if x, ok := v.(*events.ResourceCreatePending); ok { - x.CopyData(&e) - return nil - } - return errCannotUnmarshalEvent - }, - ) + return newResourceEvent(&e) } -func MakeResourceCreated(resourceID *commands.ResourceId, status commands.Status, content *commands.Content, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext) eventstore.EventUnmarshaler { +func MakeResourceCreated(resourceID *commands.ResourceId, status commands.Status, content *commands.Content, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext, resourceTypes []string) eventstore.EventUnmarshaler { e := events.ResourceCreated{ ResourceId: resourceID, Content: content, Status: status, AuditContext: auditContext, EventMetadata: eventMetadata, + ResourceTypes: resourceTypes, } - return eventstore.NewLoadedEvent( - e.GetEventMetadata().GetVersion(), - (&events.ResourceCreated{}).EventType(), - e.GetResourceId().ToUUID().String(), - e.GetResourceId().GetDeviceId(), - false, - time.Unix(0, e.GetEventMetadata().GetTimestamp()), - func(v interface{}) error { - if x, ok := v.(*events.ResourceCreated); ok { - x.CopyData(&e) - return nil - } - return errCannotUnmarshalEvent - }, - ) + return newResourceEvent(&e) } -func MakeResourceChangedEvent(resourceID *commands.ResourceId, content *commands.Content, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext) eventstore.EventUnmarshaler { +func MakeResourceChangedEvent(resourceID *commands.ResourceId, content *commands.Content, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext, resourceTypes []string) eventstore.EventUnmarshaler { e := events.ResourceChanged{ ResourceId: resourceID, AuditContext: auditContext, Content: content, EventMetadata: eventMetadata, + ResourceTypes: resourceTypes, } - return eventstore.NewLoadedEvent( - e.GetEventMetadata().GetVersion(), - (&events.ResourceChanged{}).EventType(), - e.GetResourceId().ToUUID().String(), - e.GetResourceId().GetDeviceId(), - false, - time.Unix(0, e.GetEventMetadata().GetTimestamp()), - func(v interface{}) error { - if x, ok := v.(*events.ResourceChanged); ok { - x.CopyData(&e) - return nil - } - return errCannotUnmarshalEvent - }, - ) + return newResourceEvent(&e) } -func MakeResourceRetrievePending(resourceID *commands.ResourceId, resourceInterface string, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext, validUntil time.Time) eventstore.EventUnmarshaler { +func MakeResourceRetrievePending(resourceID *commands.ResourceId, resourceInterface string, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext, validUntil time.Time, resourceTypes []string) eventstore.EventUnmarshaler { e := events.ResourceRetrievePending{ ResourceId: resourceID, ResourceInterface: resourceInterface, AuditContext: auditContext, EventMetadata: eventMetadata, ValidUntil: pkgTime.UnixNano(validUntil), + ResourceTypes: resourceTypes, } - return eventstore.NewLoadedEvent( - e.GetEventMetadata().GetVersion(), - (&events.ResourceRetrievePending{}).EventType(), - e.GetResourceId().ToUUID().String(), - e.GetResourceId().GetDeviceId(), - false, - time.Unix(0, e.GetEventMetadata().GetTimestamp()), - func(v interface{}) error { - if x, ok := v.(*events.ResourceRetrievePending); ok { - x.CopyData(&e) - return nil - } - return errCannotUnmarshalEvent - }, - ) + return newResourceEvent(&e) } -func MakeResourceRetrieved(resourceID *commands.ResourceId, status commands.Status, content *commands.Content, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext) eventstore.EventUnmarshaler { +func MakeResourceRetrieved(resourceID *commands.ResourceId, status commands.Status, content *commands.Content, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext, resourceTypes []string) eventstore.EventUnmarshaler { e := events.ResourceRetrieved{ ResourceId: resourceID, Content: content, Status: status, AuditContext: auditContext, EventMetadata: eventMetadata, + ResourceTypes: resourceTypes, } - return eventstore.NewLoadedEvent( - e.GetEventMetadata().GetVersion(), - (&events.ResourceRetrieved{}).EventType(), - e.GetResourceId().ToUUID().String(), - e.GetResourceId().GetDeviceId(), - false, - time.Unix(0, e.GetEventMetadata().GetTimestamp()), - func(v interface{}) error { - if x, ok := v.(*events.ResourceRetrieved); ok { - x.CopyData(&e) - return nil - } - return errCannotUnmarshalEvent - }, - ) + return newResourceEvent(&e) } -func MakeResourceDeletePending(resourceID *commands.ResourceId, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext, validUntil time.Time) eventstore.EventUnmarshaler { +func MakeResourceDeletePending(resourceID *commands.ResourceId, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext, validUntil time.Time, resourceTypes []string) eventstore.EventUnmarshaler { e := events.ResourceDeletePending{ ResourceId: resourceID, AuditContext: auditContext, EventMetadata: eventMetadata, ValidUntil: pkgTime.UnixNano(validUntil), + ResourceTypes: resourceTypes, } - return eventstore.NewLoadedEvent( - e.GetEventMetadata().GetVersion(), - (&events.ResourceDeletePending{}).EventType(), - e.GetResourceId().ToUUID().String(), - e.GetResourceId().GetDeviceId(), - false, - time.Unix(0, e.GetEventMetadata().GetTimestamp()), - func(v interface{}) error { - if x, ok := v.(*events.ResourceDeletePending); ok { - x.CopyData(&e) - return nil - } - return errCannotUnmarshalEvent - }, - ) + return newResourceEvent(&e) } -func MakeResourceDeleted(resourceID *commands.ResourceId, status commands.Status, content *commands.Content, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext) eventstore.EventUnmarshaler { +func MakeResourceDeleted(resourceID *commands.ResourceId, status commands.Status, content *commands.Content, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext, resourceTypes []string) eventstore.EventUnmarshaler { e := events.ResourceDeleted{ ResourceId: resourceID, Content: content, Status: status, AuditContext: auditContext, EventMetadata: eventMetadata, + ResourceTypes: resourceTypes, } - return eventstore.NewLoadedEvent( - e.GetEventMetadata().GetVersion(), - (&events.ResourceDeleted{}).EventType(), - e.GetResourceId().ToUUID().String(), - e.GetResourceId().GetDeviceId(), - false, - time.Unix(0, e.GetEventMetadata().GetTimestamp()), - func(v interface{}) error { - if x, ok := v.(*events.ResourceDeleted); ok { - x.CopyData(&e) - return nil - } - return errCannotUnmarshalEvent - }, - ) + return newResourceEvent(&e) } -func MakeResourceStateSnapshotTaken(resourceID *commands.ResourceId, latestResourceChange *events.ResourceChanged, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext) eventstore.EventUnmarshaler { +func MakeResourceStateSnapshotTaken(resourceID *commands.ResourceId, latestResourceChange *events.ResourceChanged, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext, resourceTypes []string) eventstore.EventUnmarshaler { e := events.NewResourceStateSnapshotTaken() e.ResourceId = resourceID e.LatestResourceChange = latestResourceChange e.EventMetadata = eventMetadata e.AuditContext = auditContext + e.ResourceTypes = resourceTypes - return eventstore.NewLoadedEvent( - e.GetEventMetadata().GetVersion(), - (&events.ResourceStateSnapshotTaken{}).EventType(), - e.GetResourceId().ToUUID().String(), - e.GetResourceId().GetDeviceId(), - true, - time.Unix(0, e.GetEventMetadata().GetTimestamp()), - func(v interface{}) error { - if x, ok := v.(*events.ResourceStateSnapshotTaken); ok { - x.CopyData(e) - return nil - } - return errCannotUnmarshalEvent - }, - ) + return newResourceEvent(e) } func MakeDeviceMetadataUpdatePending(deviceID string, twinEnabled *events.DeviceMetadataUpdatePending_TwinEnabled, eventMetadata *events.EventMetadata, auditContext *commands.AuditContext, validUntil time.Time) eventstore.EventUnmarshaler { @@ -423,15 +316,16 @@ func MakeDeviceMetadata(deviceID string, deviceMetadataUpdated *events.DeviceMet } type MockEvent struct { - VersionI uint64 `bson:"version"` - EventTypeI string `bson:"eventtype"` - IsSnapshotI bool `bson:"issnapshot"` - AggregateIDI string `bson:"aggregateid"` - GroupIDI string `bson:"groupid"` - DataI []byte `bson:"data"` - TimestampI int64 `bson:"timestamp"` - ETagI []byte `bson:"etag"` - ServiceIDI string `bson:"serviceid"` + VersionI uint64 `bson:"version"` + EventTypeI string `bson:"eventtype"` + IsSnapshotI bool `bson:"issnapshot"` + AggregateIDI string `bson:"aggregateid"` + GroupIDI string `bson:"groupid"` + DataI []byte `bson:"data"` + TimestampI int64 `bson:"timestamp"` + ETagI []byte `bson:"etag"` + ServiceIDI string `bson:"serviceid"` + TypesI []string `bson:"resourcetypes"` } func (e MockEvent) Version() uint64 { @@ -472,6 +366,10 @@ func (e MockEvent) ServiceID() (string, bool) { return e.ServiceIDI, true } +func (e MockEvent) Types() []string { + return e.TypesI +} + type MockEventHandler struct { lock sync.Mutex events map[string]map[string][]eventstore.Event diff --git a/resource-aggregate/cqrs/eventstore/test/eventstore.go b/resource-aggregate/cqrs/eventstore/test/eventstore.go index 3148c449b..54dcc707a 100644 --- a/resource-aggregate/cqrs/eventstore/test/eventstore.go +++ b/resource-aggregate/cqrs/eventstore/test/eventstore.go @@ -3,7 +3,6 @@ package test import ( "context" "errors" - "fmt" "github.com/plgd-dev/hub/v2/resource-aggregate/cqrs/eventstore" ) @@ -132,7 +131,7 @@ func (s *MockEventStore) LoadFromSnapshot(ctx context.Context, queries []eventst ret = append(ret, q) } if len(ret) == 0 { - return fmt.Errorf("cannot load events: not found") + return errors.New("cannot load events: not found") } return s.LoadFromVersion(ctx, ret, eventHandler) diff --git a/resource-aggregate/cqrs/projection/projection.go b/resource-aggregate/cqrs/projection/projection.go index a888d820d..e1ce3361b 100644 --- a/resource-aggregate/cqrs/projection/projection.go +++ b/resource-aggregate/cqrs/projection/projection.go @@ -24,7 +24,7 @@ type Projection struct { // NewProjection creates new resource projection. func NewProjection(ctx context.Context, name string, store eventstore.EventStore, subscriber eventbus.Subscriber, factoryModel eventstore.FactoryModelFunc) (*Projection, error) { - cqrsProjection, err := newProjection(ctx, store, name, subscriber, factoryModel, func(template string, args ...interface{}) {}) + cqrsProjection, err := newProjection(ctx, store, name, subscriber, factoryModel, func(string, ...interface{}) {}) if err != nil { return nil, fmt.Errorf("cannot create Projection: %w", err) } @@ -52,7 +52,7 @@ func (p *Projection) Register(ctx context.Context, deviceID string) (created boo }, func() interface{} { return kitSync.NewRefCounter(&deviceProjection{ deviceID: deviceID, - }, func(ctx context.Context, data interface{}) error { + }, func(_ context.Context, data interface{}) error { d := data.(*deviceProjection) d.released = true return nil @@ -75,7 +75,7 @@ func (p *Projection) Register(ctx context.Context, deviceID string) (created boo return errors.ErrorOrNil() } if updateSubscriber { - err := p.cqrsProjection.SubscribeTo(topics) + err = p.cqrsProjection.SubscribeTo(topics) if err != nil { return false, releaseAndReturnError(deviceID, err) } @@ -151,7 +151,7 @@ func (p *Projection) ForceUpdate(ctx context.Context, resourceID *commands.Resou func (p *Projection) release(v *kitSync.RefCounter) error { data := v.Data().(*deviceProjection) deviceID := data.deviceID - p.refCountMap.ReplaceWithFunc(deviceID, func(oldValue interface{}, oldLoaded bool) (newValue interface{}, doDelete bool) { + p.refCountMap.ReplaceWithFunc(deviceID, func(oldValue interface{}, _ bool) (newValue interface{}, doDelete bool) { o := oldValue.(*kitSync.RefCounter) d := o.Data().(*deviceProjection) if err := o.Release(context.Background()); err != nil { diff --git a/resource-aggregate/cqrs/projection/projectionInternal.go b/resource-aggregate/cqrs/projection/projectionInternal.go index f6b928521..3e981af6e 100644 --- a/resource-aggregate/cqrs/projection/projectionInternal.go +++ b/resource-aggregate/cqrs/projection/projectionInternal.go @@ -69,7 +69,7 @@ func (p *projection) SubscribeTo(topics []string) error { } if p.observer == nil { if p.subscriber == nil { - return fmt.Errorf("projection doesn't support subscribe to topics") + return errors.New("projection doesn't support subscribe to topics") } observer, err := p.subscriber.Subscribe(p.ctx, p.subscriptionID, topics, p) if err != nil { diff --git a/resource-aggregate/cqrs/projection/projectionInternal_test.go b/resource-aggregate/cqrs/projection/projectionInternal_test.go index cc0350bcc..301ede29c 100644 --- a/resource-aggregate/cqrs/projection/projectionInternal_test.go +++ b/resource-aggregate/cqrs/projection/projectionInternal_test.go @@ -21,7 +21,6 @@ import ( "github.com/plgd-dev/hub/v2/resource-aggregate/events" "github.com/plgd-dev/hub/v2/test" "github.com/plgd-dev/hub/v2/test/config" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/trace/noop" ) @@ -59,7 +58,7 @@ func TestProjection(t *testing.T) { naPubClient, publisher, err := natsTest.NewClientAndPublisher(config.MakePublisherConfig(), fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) - assert.NotNil(t, publisher) + require.NotNil(t, publisher) defer func() { publisher.Close() naPubClient.Close() @@ -74,8 +73,8 @@ func TestProjection(t *testing.T) { subscriber.WithGoPool(pool.Submit), subscriber.WithUnmarshaler(utils.Unmarshal), ) - assert.NoError(t, err) - assert.NotNil(t, subscriber) + require.NoError(t, err) + require.NotNil(t, subscriber) defer func() { subscriber.Close() naSubClient.Close() @@ -133,8 +132,8 @@ func TestProjection(t *testing.T) { CommandMetadata: &commands.CommandMetadata{}, } - a1, err := aggregate.NewAggregate(res1.DeviceId, res1.ToUUID().String(), aggregate.NewDefaultRetryFunc(1), store, func(context.Context) (aggregate.AggregateModel, error) { - s := events.NewResourceStateSnapshotTakenForCommand("test", "test", "hubID") + a1, err := aggregate.NewAggregate(res1.GetDeviceId(), res1.ToUUID().String(), aggregate.NewDefaultRetryFunc(1), store, func(context.Context, string, string) (aggregate.AggregateModel, error) { + s := events.NewResourceStateSnapshotTakenForCommand("test", "test", "hubID", nil) s.ResourceId = &res1 return s, nil }, nil) @@ -144,8 +143,8 @@ func TestProjection(t *testing.T) { require.NoError(t, err) require.NotNil(t, evs) - a2, err := aggregate.NewAggregate(res2.DeviceId, res2.ToUUID().String(), aggregate.NewDefaultRetryFunc(1), store, func(context.Context) (aggregate.AggregateModel, error) { - s := events.NewResourceStateSnapshotTakenForCommand("test", "test", "hubID") + a2, err := aggregate.NewAggregate(res2.GetDeviceId(), res2.ToUUID().String(), aggregate.NewDefaultRetryFunc(1), store, func(context.Context, string, string) (aggregate.AggregateModel, error) { + s := events.NewResourceStateSnapshotTakenForCommand("test", "test", "hubID", nil) s.ResourceId = &res2 return s, nil }, nil) @@ -159,7 +158,7 @@ func TestProjection(t *testing.T) { require.NoError(t, err) err = projection.Project(ctx, []eventstore.SnapshotQuery{{ - GroupID: res1.DeviceId, + GroupID: res1.GetDeviceId(), AggregateID: res1.ToUUID().String(), }}) require.NoError(t, err) @@ -168,10 +167,10 @@ func TestProjection(t *testing.T) { models = append(models, m) return true }) - require.Equal(t, 1, len(models)) + require.Len(t, models, 1) err = projection.Project(ctx, []eventstore.SnapshotQuery{{ - GroupID: res2.DeviceId, + GroupID: res2.GetDeviceId(), AggregateID: res2.ToUUID().String(), }}) require.NoError(t, err) @@ -180,15 +179,15 @@ func TestProjection(t *testing.T) { models = append(models, m) return true }) - require.Equal(t, 2, len(models)) + require.Len(t, models, 2) err = projection.SubscribeTo(topics) require.NoError(t, err) time.Sleep(waitForSubscription) - a3, err := aggregate.NewAggregate(res3.DeviceId, res3.ToUUID().String(), aggregate.NewDefaultRetryFunc(1), store, func(context.Context) (aggregate.AggregateModel, error) { - s := events.NewResourceStateSnapshotTakenForCommand("test", "test", "hubID") + a3, err := aggregate.NewAggregate(res3.GetDeviceId(), res3.ToUUID().String(), aggregate.NewDefaultRetryFunc(1), store, func(context.Context, string, string) (aggregate.AggregateModel, error) { + s := events.NewResourceStateSnapshotTakenForCommand("test", "test", "hubID", nil) s.ResourceId = &res3 return s, nil }, nil) @@ -198,7 +197,7 @@ func TestProjection(t *testing.T) { require.NoError(t, err) require.NotNil(t, evs) for _, e := range evs { - err = publisher.Publish(ctx, topics, res3.DeviceId, res3.ToUUID().String(), e) + err = publisher.Publish(ctx, topics, res3.GetDeviceId(), res3.ToUUID().String(), e) require.NoError(t, err) } time.Sleep(time.Second) @@ -208,7 +207,7 @@ func TestProjection(t *testing.T) { models = append(models, m) return true }) - require.Equal(t, 3, len(models)) + require.Len(t, models, 3) err = projection.SubscribeTo(topics[0:1]) require.NoError(t, err) @@ -216,7 +215,7 @@ func TestProjection(t *testing.T) { time.Sleep(waitForSubscription) err = projection.Forget([]eventstore.SnapshotQuery{{ - GroupID: res3.DeviceId, + GroupID: res3.GetDeviceId(), AggregateID: res3.ToUUID().String(), }}) require.NoError(t, err) @@ -229,7 +228,7 @@ func TestProjection(t *testing.T) { models = append(models, m) return true }) - require.Equal(t, 2, len(models)) + require.Len(t, models, 2) projection.lock.Unlock() err = projection.SubscribeTo(nil) @@ -243,7 +242,7 @@ func TestProjection(t *testing.T) { require.NoError(t, err) require.NotNil(t, evs) for _, e := range evs { - err = publisher.Publish(ctx, topics, res1.DeviceId, res1.ToUUID().String(), e) + err = publisher.Publish(ctx, topics, res1.GetDeviceId(), res1.ToUUID().String(), e) require.NoError(t, err) } @@ -253,6 +252,6 @@ func TestProjection(t *testing.T) { models = append(models, m) return true }) - require.Equal(t, 2, len(models)) + require.Len(t, models, 2) projection.lock.Unlock() } diff --git a/resource-aggregate/cqrs/projection/projection_test.go b/resource-aggregate/cqrs/projection/projection_test.go index 97ec0ffab..089b16191 100644 --- a/resource-aggregate/cqrs/projection/projection_test.go +++ b/resource-aggregate/cqrs/projection/projection_test.go @@ -22,68 +22,81 @@ var ( ) var d1res1 = commands.Resource{ - DeviceId: dev1, - Href: "/res1", + DeviceId: dev1, + Href: "/res1", + ResourceTypes: []string{"typeDev1Res"}, } var d1res2 = commands.Resource{ - DeviceId: dev1, - Href: "/res2", + DeviceId: dev1, + Href: "/res2", + ResourceTypes: []string{"typeDev1Res2"}, } var d1res3 = commands.Resource{ - DeviceId: dev1, - Href: "/res3", + DeviceId: dev1, + Href: "/res3", + ResourceTypes: []string{"typeDev1Res3"}, } var d1res4 = commands.Resource{ - DeviceId: dev1, - Href: "/res4", + DeviceId: dev1, + Href: "/res4", + ResourceTypes: []string{"typeDev1Res4"}, } var d1res5 = commands.Resource{ - DeviceId: dev1, - Href: "/res5", + DeviceId: dev1, + Href: "/res5", + ResourceTypes: []string{"typeDev1Res5"}, } var d2res1 = commands.Resource{ - DeviceId: dev2, - Href: "/res1", + DeviceId: dev2, + Href: "/res1", + ResourceTypes: []string{"typeDev2Res1"}, } var d2res2 = commands.Resource{ - DeviceId: dev2, - Href: "/res2", + DeviceId: dev2, + Href: "/res2", + ResourceTypes: []string{"typeDev2Res2"}, } var d3res1 = commands.Resource{ - DeviceId: "dev3", - Href: "/res1", + DeviceId: "dev3", + Href: "/res1", + ResourceTypes: []string{"typeDev3Res1"}, } var d3res2 = commands.Resource{ - DeviceId: "dev3", - Href: "/res2", + DeviceId: "dev3", + Href: "/res2", + ResourceTypes: []string{"typeDev3Res2"}, } var d4res1 = commands.Resource{ - DeviceId: "dev4", - Href: "/res1", + DeviceId: "dev4", + Href: "/res1", + ResourceTypes: []string{"typeDev4Res1"}, } var d4res2 = commands.Resource{ - DeviceId: "dev4", - Href: "/res2", + DeviceId: "dev4", + Href: "/res2", + ResourceTypes: []string{"typeDev4Res2"}, } var d5res1 = commands.Resource{ - DeviceId: "dev5", - Href: "/res1", + DeviceId: "dev5", + Href: "/res1", + ResourceTypes: []string{"typeDev5Res1"}, } var d5res2 = commands.Resource{ - DeviceId: "dev5", - Href: "/res2", + DeviceId: "dev5", + Href: "/res2", + ResourceTypes: []string{"typeDev5Res2"}, } func makeEventMeta(connectionID string, version uint64) *events.EventMetadata { @@ -95,32 +108,32 @@ func makeEventMeta(connectionID string, version uint64) *events.EventMetadata { func prepareResourceLinksEventstore() *mockEvents.MockEventStore { eventstore := mockEvents.NewMockEventStore() - d1resID := commands.MakeLinksResourceUUID(d1res1.DeviceId) - eventstore.Append(d1res1.DeviceId, d1resID.String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{&d1res1}, d1res1.DeviceId, makeEventMeta("a", 0))) - eventstore.Append(d1res2.DeviceId, d1resID.String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{&d1res2, &d1res3}, d1res2.DeviceId, makeEventMeta("a", 1))) - eventstore.Append(d1res2.DeviceId, d1resID.String(), mockEvents.MakeResourceLinksUnpublishedEvent([]string{d1res2.Href}, d1res2.DeviceId, makeEventMeta("a", 2))) - eventstore.Append(d1res4.DeviceId, d1resID.String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{&d1res4, &d1res5}, d1res4.DeviceId, makeEventMeta("a", 3))) - - d2resID := commands.MakeLinksResourceUUID(d2res1.DeviceId) - eventstore.Append(d2res1.DeviceId, d2resID.String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{&d2res1, &d2res2}, d2res1.DeviceId, makeEventMeta("a", 0))) - eventstore.Append(d2res1.DeviceId, d2resID.String(), mockEvents.MakeResourceLinksUnpublishedEvent([]string{d2res1.Href}, d2res1.DeviceId, makeEventMeta("a", 1))) - eventstore.Append(d2res2.DeviceId, d2resID.String(), mockEvents.MakeResourceLinksUnpublishedEvent([]string{d2res2.Href}, d2res2.DeviceId, makeEventMeta("a", 2))) - eventstore.Append(d2res2.DeviceId, d2resID.String(), mockEvents.MakeResourceLinksUnpublishedEvent([]string{d2res2.Href}, d2res2.DeviceId, makeEventMeta("a", 3))) - eventstore.Append(d2res1.DeviceId, d2resID.String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{&d2res1, &d2res1}, d2res1.DeviceId, makeEventMeta("a", 4))) - eventstore.Append(d2res2.DeviceId, d2resID.String(), mockEvents.MakeResourceLinksUnpublishedEvent([]string{d2res2.Href, d2res2.Href}, d2res2.DeviceId, makeEventMeta("a", 5))) - eventstore.Append(d2res1.DeviceId, d2resID.String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{&d2res1}, d2res1.DeviceId, makeEventMeta("a", 6))) - - d3resID := commands.MakeLinksResourceUUID(d3res1.DeviceId) - eventstore.Append(d3res1.DeviceId, d3resID.String(), mockEvents.MakeResourceLinksSnapshotTaken(map[string]*commands.Resource{d3res1.Href: &d3res1, d3res2.Href: &d3res2}, d3res1.DeviceId, makeEventMeta("a", 0))) - eventstore.Append(d3res1.DeviceId, d3resID.String(), mockEvents.MakeResourceLinksUnpublishedEvent([]string{d3res1.Href}, d3res1.DeviceId, makeEventMeta("a", 1))) - - d4resID := commands.MakeLinksResourceUUID(d4res1.DeviceId) - eventstore.Append(d4res1.DeviceId, d4resID.String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{&d4res1, &d4res2}, d4res1.DeviceId, makeEventMeta("a", 0))) - eventstore.Append(d4res1.DeviceId, d4resID.String(), mockEvents.MakeResourceLinksUnpublishedEvent([]string{}, d4res1.DeviceId, makeEventMeta("a", 1))) - - d5resID := commands.MakeLinksResourceUUID(d5res1.DeviceId) - eventstore.Append(d5res1.DeviceId, d5resID.String(), mockEvents.MakeResourceLinksSnapshotTaken(map[string]*commands.Resource{d5res1.Href: &d5res1, d5res2.Href: &d5res2}, d5res1.DeviceId, makeEventMeta("a", 0))) - eventstore.Append(d5res1.DeviceId, d5resID.String(), mockEvents.MakeResourceLinksUnpublishedEvent([]string{}, d5res1.DeviceId, makeEventMeta("a", 1))) + d1resID := commands.MakeLinksResourceUUID(d1res1.GetDeviceId()) + eventstore.Append(d1res1.GetDeviceId(), d1resID.String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{&d1res1}, d1res1.GetDeviceId(), makeEventMeta("a", 0))) + eventstore.Append(d1res2.GetDeviceId(), d1resID.String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{&d1res2, &d1res3}, d1res2.GetDeviceId(), makeEventMeta("a", 1))) + eventstore.Append(d1res2.GetDeviceId(), d1resID.String(), mockEvents.MakeResourceLinksUnpublishedEvent([]string{d1res2.GetHref()}, d1res2.GetDeviceId(), makeEventMeta("a", 2))) + eventstore.Append(d1res4.GetDeviceId(), d1resID.String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{&d1res4, &d1res5}, d1res4.GetDeviceId(), makeEventMeta("a", 3))) + + d2resID := commands.MakeLinksResourceUUID(d2res1.GetDeviceId()) + eventstore.Append(d2res1.GetDeviceId(), d2resID.String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{&d2res1, &d2res2}, d2res1.GetDeviceId(), makeEventMeta("a", 0))) + eventstore.Append(d2res1.GetDeviceId(), d2resID.String(), mockEvents.MakeResourceLinksUnpublishedEvent([]string{d2res1.GetHref()}, d2res1.GetDeviceId(), makeEventMeta("a", 1))) + eventstore.Append(d2res2.GetDeviceId(), d2resID.String(), mockEvents.MakeResourceLinksUnpublishedEvent([]string{d2res2.GetHref()}, d2res2.GetDeviceId(), makeEventMeta("a", 2))) + eventstore.Append(d2res2.GetDeviceId(), d2resID.String(), mockEvents.MakeResourceLinksUnpublishedEvent([]string{d2res2.GetHref()}, d2res2.GetDeviceId(), makeEventMeta("a", 3))) + eventstore.Append(d2res1.GetDeviceId(), d2resID.String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{&d2res1, &d2res1}, d2res1.GetDeviceId(), makeEventMeta("a", 4))) + eventstore.Append(d2res2.GetDeviceId(), d2resID.String(), mockEvents.MakeResourceLinksUnpublishedEvent([]string{d2res2.GetHref(), d2res2.GetHref()}, d2res2.GetDeviceId(), makeEventMeta("a", 5))) + eventstore.Append(d2res1.GetDeviceId(), d2resID.String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{&d2res1}, d2res1.GetDeviceId(), makeEventMeta("a", 6))) + + d3resID := commands.MakeLinksResourceUUID(d3res1.GetDeviceId()) + eventstore.Append(d3res1.GetDeviceId(), d3resID.String(), mockEvents.MakeResourceLinksSnapshotTaken(map[string]*commands.Resource{d3res1.GetHref(): &d3res1, d3res2.GetHref(): &d3res2}, d3res1.GetDeviceId(), makeEventMeta("a", 0))) + eventstore.Append(d3res1.GetDeviceId(), d3resID.String(), mockEvents.MakeResourceLinksUnpublishedEvent([]string{d3res1.GetHref()}, d3res1.GetDeviceId(), makeEventMeta("a", 1))) + + d4resID := commands.MakeLinksResourceUUID(d4res1.GetDeviceId()) + eventstore.Append(d4res1.GetDeviceId(), d4resID.String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{&d4res1, &d4res2}, d4res1.GetDeviceId(), makeEventMeta("a", 0))) + eventstore.Append(d4res1.GetDeviceId(), d4resID.String(), mockEvents.MakeResourceLinksUnpublishedEvent([]string{}, d4res1.GetDeviceId(), makeEventMeta("a", 1))) + + d5resID := commands.MakeLinksResourceUUID(d5res1.GetDeviceId()) + eventstore.Append(d5res1.GetDeviceId(), d5resID.String(), mockEvents.MakeResourceLinksSnapshotTaken(map[string]*commands.Resource{d5res1.GetHref(): &d5res1, d5res2.GetHref(): &d5res2}, d5res1.GetDeviceId(), makeEventMeta("a", 0))) + eventstore.Append(d5res1.GetDeviceId(), d5resID.String(), mockEvents.MakeResourceLinksUnpublishedEvent([]string{}, d5res1.GetDeviceId(), makeEventMeta("a", 1))) return eventstore } @@ -129,38 +142,38 @@ func prepareResourceStateEventstore() *mockEvents.MockEventStore { eventstore := mockEvents.NewMockEventStore() resourceChangedEventMetadata := makeEventMeta("", 0) - d1r1 := commands.NewResourceID(d1res1.DeviceId, d1res1.Href) - eventstore.Append(d1res1.DeviceId, d1r1.ToUUID().String(), mockEvents.MakeResourceStateSnapshotTaken(d1r1, &events.ResourceChanged{Content: &commands.Content{}, EventMetadata: resourceChangedEventMetadata}, makeEventMeta("a", 0), mockEvents.MakeAuditContext("userId", "0"))) - eventstore.Append(d1res1.DeviceId, d1r1.ToUUID().String(), mockEvents.MakeResourceUpdatePending(d1r1, &commands.Content{}, makeEventMeta("a", 1), mockEvents.MakeAuditContext("userId", "1"), time.Time{})) - eventstore.Append(d1res1.DeviceId, d1r1.ToUUID().String(), mockEvents.MakeResourceUpdatePending(d1r1, &commands.Content{}, makeEventMeta("a", 2), mockEvents.MakeAuditContext("userId", "2"), time.Time{})) - eventstore.Append(d1res1.DeviceId, d1r1.ToUUID().String(), mockEvents.MakeResourceUpdated(d1r1, commands.Status_OK, &commands.Content{}, makeEventMeta("a", 3), mockEvents.MakeAuditContext("userId", "1"))) - eventstore.Append(d1res1.DeviceId, d1r1.ToUUID().String(), mockEvents.MakeResourceUpdatePending(d1r1, &commands.Content{}, makeEventMeta("a", 4), mockEvents.MakeAuditContext("userId", "3"), time.Time{})) - - d1r2 := commands.NewResourceID(d1res2.DeviceId, d1res2.Href) - eventstore.Append(d1res2.DeviceId, d1r2.ToUUID().String(), mockEvents.MakeResourceStateSnapshotTaken(d1r2, &events.ResourceChanged{Content: &commands.Content{}, EventMetadata: resourceChangedEventMetadata}, makeEventMeta("a", 0), mockEvents.MakeAuditContext("userId", "2"))) - - d1r3 := commands.NewResourceID(d1res3.DeviceId, d1res3.Href) - eventstore.Append(d1res3.DeviceId, d1r3.ToUUID().String(), mockEvents.MakeResourceStateSnapshotTaken(d1r3, &events.ResourceChanged{Content: &commands.Content{}, EventMetadata: resourceChangedEventMetadata}, makeEventMeta("a", 0), mockEvents.MakeAuditContext("userId", "2"))) - eventstore.Append(d1res3.DeviceId, d1r3.ToUUID().String(), mockEvents.MakeResourceUpdatePending(d1r3, &commands.Content{}, makeEventMeta("a", 1), mockEvents.MakeAuditContext("userId", "3"), time.Time{})) - - d1r4 := commands.NewResourceID(d1res4.DeviceId, d1res4.Href) - eventstore.Append(d1res4.DeviceId, d1r4.ToUUID().String(), mockEvents.MakeResourceStateSnapshotTaken(d1r4, &events.ResourceChanged{Content: &commands.Content{}, EventMetadata: resourceChangedEventMetadata}, makeEventMeta("a", 0), mockEvents.MakeAuditContext("userId", "0"))) - eventstore.Append(d1res4.DeviceId, d1r4.ToUUID().String(), mockEvents.MakeResourceUpdatePending(d1r4, &commands.Content{}, makeEventMeta("a", 1), mockEvents.MakeAuditContext("userId", "1"), time.Time{})) - eventstore.Append(d1res4.DeviceId, d1r4.ToUUID().String(), mockEvents.MakeResourceUpdatePending(d1r4, &commands.Content{}, makeEventMeta("a", 2), mockEvents.MakeAuditContext("userId", "2"), time.Time{})) - eventstore.Append(d1res4.DeviceId, d1r4.ToUUID().String(), mockEvents.MakeResourceUpdated(d1r4, commands.Status_OK, &commands.Content{}, makeEventMeta("a", 3), mockEvents.MakeAuditContext("userId", "1"))) - eventstore.Append(d1res4.DeviceId, d1r4.ToUUID().String(), mockEvents.MakeResourceStateSnapshotTaken(d1r4, &events.ResourceChanged{Content: &commands.Content{}, EventMetadata: resourceChangedEventMetadata}, makeEventMeta("a", 4), mockEvents.MakeAuditContext("userId", "3"))) - eventstore.Append(d1res4.DeviceId, d1r4.ToUUID().String(), mockEvents.MakeResourceUpdatePending(d1r4, &commands.Content{}, makeEventMeta("a", 5), mockEvents.MakeAuditContext("userId", "4"), time.Time{})) - - d2r1 := commands.NewResourceID(d2res1.DeviceId, d2res1.Href) - eventstore.Append(d2res1.DeviceId, d2r1.ToUUID().String(), mockEvents.MakeResourceStateSnapshotTaken(d2r1, &events.ResourceChanged{Content: &commands.Content{}, EventMetadata: resourceChangedEventMetadata}, makeEventMeta("a", 0), mockEvents.MakeAuditContext("userId", "0"))) - eventstore.Append(d2res1.DeviceId, d2r1.ToUUID().String(), mockEvents.MakeResourceUpdatePending(d2r1, &commands.Content{}, makeEventMeta("a", 1), mockEvents.MakeAuditContext("userId", "1"), time.Time{})) - eventstore.Append(d2res1.DeviceId, d2r1.ToUUID().String(), mockEvents.MakeResourceUpdated(d2r1, commands.Status_OK, &commands.Content{}, makeEventMeta("a", 2), mockEvents.MakeAuditContext("userId", "1"))) - - d2r2 := commands.NewResourceID(d2res2.DeviceId, d2res2.Href) - eventstore.Append(d2res2.DeviceId, d2r2.ToUUID().String(), mockEvents.MakeResourceStateSnapshotTaken(d2r2, &events.ResourceChanged{Content: &commands.Content{}, EventMetadata: resourceChangedEventMetadata}, makeEventMeta("a", 0), mockEvents.MakeAuditContext("userId", "0"))) - eventstore.Append(d2res2.DeviceId, d2r2.ToUUID().String(), mockEvents.MakeResourceUpdatePending(d2r2, &commands.Content{}, makeEventMeta("a", 1), mockEvents.MakeAuditContext("userId", "1"), time.Time{})) - eventstore.Append(d2res2.DeviceId, d2r2.ToUUID().String(), mockEvents.MakeResourceUpdated(d2r2, commands.Status_OK, &commands.Content{}, makeEventMeta("a", 2), mockEvents.MakeAuditContext("userId", "1"))) - eventstore.Append(d2res2.DeviceId, d2r2.ToUUID().String(), mockEvents.MakeResourceChangedEvent(d2r2, &commands.Content{}, makeEventMeta("a", 3), mockEvents.MakeAuditContext("userId", "2"))) + d1r1 := commands.NewResourceID(d1res1.GetDeviceId(), d1res1.GetHref()) + eventstore.Append(d1res1.GetDeviceId(), d1r1.ToUUID().String(), mockEvents.MakeResourceStateSnapshotTaken(d1r1, &events.ResourceChanged{Content: &commands.Content{}, EventMetadata: resourceChangedEventMetadata, ResourceTypes: d1res1.GetResourceTypes()}, makeEventMeta("a", 0), mockEvents.MakeAuditContext("userId", "0"), d1res1.GetResourceTypes())) + eventstore.Append(d1res1.GetDeviceId(), d1r1.ToUUID().String(), mockEvents.MakeResourceUpdatePending(d1r1, &commands.Content{}, makeEventMeta("a", 1), mockEvents.MakeAuditContext("userId", "1"), time.Time{}, d1res1.GetResourceTypes())) + eventstore.Append(d1res1.GetDeviceId(), d1r1.ToUUID().String(), mockEvents.MakeResourceUpdatePending(d1r1, &commands.Content{}, makeEventMeta("a", 2), mockEvents.MakeAuditContext("userId", "2"), time.Time{}, d1res1.GetResourceTypes())) + eventstore.Append(d1res1.GetDeviceId(), d1r1.ToUUID().String(), mockEvents.MakeResourceUpdated(d1r1, commands.Status_OK, &commands.Content{}, makeEventMeta("a", 3), mockEvents.MakeAuditContext("userId", "1"), d1res1.GetResourceTypes())) + eventstore.Append(d1res1.GetDeviceId(), d1r1.ToUUID().String(), mockEvents.MakeResourceUpdatePending(d1r1, &commands.Content{}, makeEventMeta("a", 4), mockEvents.MakeAuditContext("userId", "3"), time.Time{}, d1res1.GetResourceTypes())) + + d1r2 := commands.NewResourceID(d1res2.GetDeviceId(), d1res2.GetHref()) + eventstore.Append(d1res2.GetDeviceId(), d1r2.ToUUID().String(), mockEvents.MakeResourceStateSnapshotTaken(d1r2, &events.ResourceChanged{Content: &commands.Content{}, EventMetadata: resourceChangedEventMetadata, ResourceTypes: d1res2.GetResourceTypes()}, makeEventMeta("a", 0), mockEvents.MakeAuditContext("userId", "2"), d1res2.GetResourceTypes())) + + d1r3 := commands.NewResourceID(d1res3.GetDeviceId(), d1res3.GetHref()) + eventstore.Append(d1res3.GetDeviceId(), d1r3.ToUUID().String(), mockEvents.MakeResourceStateSnapshotTaken(d1r3, &events.ResourceChanged{Content: &commands.Content{}, EventMetadata: resourceChangedEventMetadata, ResourceTypes: d1res3.GetResourceTypes()}, makeEventMeta("a", 0), mockEvents.MakeAuditContext("userId", "2"), d1res3.GetResourceTypes())) + eventstore.Append(d1res3.GetDeviceId(), d1r3.ToUUID().String(), mockEvents.MakeResourceUpdatePending(d1r3, &commands.Content{}, makeEventMeta("a", 1), mockEvents.MakeAuditContext("userId", "3"), time.Time{}, d1res3.GetResourceTypes())) + + d1r4 := commands.NewResourceID(d1res4.GetDeviceId(), d1res4.GetHref()) + eventstore.Append(d1res4.GetDeviceId(), d1r4.ToUUID().String(), mockEvents.MakeResourceStateSnapshotTaken(d1r4, &events.ResourceChanged{Content: &commands.Content{}, EventMetadata: resourceChangedEventMetadata, ResourceTypes: d1res4.GetResourceTypes()}, makeEventMeta("a", 0), mockEvents.MakeAuditContext("userId", "0"), d1res4.GetResourceTypes())) + eventstore.Append(d1res4.GetDeviceId(), d1r4.ToUUID().String(), mockEvents.MakeResourceUpdatePending(d1r4, &commands.Content{}, makeEventMeta("a", 1), mockEvents.MakeAuditContext("userId", "1"), time.Time{}, d1res4.GetResourceTypes())) + eventstore.Append(d1res4.GetDeviceId(), d1r4.ToUUID().String(), mockEvents.MakeResourceUpdatePending(d1r4, &commands.Content{}, makeEventMeta("a", 2), mockEvents.MakeAuditContext("userId", "2"), time.Time{}, d1res4.GetResourceTypes())) + eventstore.Append(d1res4.GetDeviceId(), d1r4.ToUUID().String(), mockEvents.MakeResourceUpdated(d1r4, commands.Status_OK, &commands.Content{}, makeEventMeta("a", 3), mockEvents.MakeAuditContext("userId", "1"), d1res4.GetResourceTypes())) + eventstore.Append(d1res4.GetDeviceId(), d1r4.ToUUID().String(), mockEvents.MakeResourceStateSnapshotTaken(d1r4, &events.ResourceChanged{Content: &commands.Content{}, EventMetadata: resourceChangedEventMetadata}, makeEventMeta("a", 4), mockEvents.MakeAuditContext("userId", "3"), d1res4.GetResourceTypes())) + eventstore.Append(d1res4.GetDeviceId(), d1r4.ToUUID().String(), mockEvents.MakeResourceUpdatePending(d1r4, &commands.Content{}, makeEventMeta("a", 5), mockEvents.MakeAuditContext("userId", "4"), time.Time{}, d1res4.GetResourceTypes())) + + d2r1 := commands.NewResourceID(d2res1.GetDeviceId(), d2res1.GetHref()) + eventstore.Append(d2res1.GetDeviceId(), d2r1.ToUUID().String(), mockEvents.MakeResourceStateSnapshotTaken(d2r1, &events.ResourceChanged{Content: &commands.Content{}, EventMetadata: resourceChangedEventMetadata, ResourceTypes: d2res1.GetResourceTypes()}, makeEventMeta("a", 0), mockEvents.MakeAuditContext("userId", "0"), d2res1.GetResourceTypes())) + eventstore.Append(d2res1.GetDeviceId(), d2r1.ToUUID().String(), mockEvents.MakeResourceUpdatePending(d2r1, &commands.Content{}, makeEventMeta("a", 1), mockEvents.MakeAuditContext("userId", "1"), time.Time{}, d2res1.GetResourceTypes())) + eventstore.Append(d2res1.GetDeviceId(), d2r1.ToUUID().String(), mockEvents.MakeResourceUpdated(d2r1, commands.Status_OK, &commands.Content{}, makeEventMeta("a", 2), mockEvents.MakeAuditContext("userId", "1"), d2res1.GetResourceTypes())) + + d2r2 := commands.NewResourceID(d2res2.GetDeviceId(), d2res2.GetHref()) + eventstore.Append(d2res2.GetDeviceId(), d2r2.ToUUID().String(), mockEvents.MakeResourceStateSnapshotTaken(d2r2, &events.ResourceChanged{Content: &commands.Content{}, EventMetadata: resourceChangedEventMetadata, ResourceTypes: d2res2.GetResourceTypes()}, makeEventMeta("a", 0), mockEvents.MakeAuditContext("userId", "0"), d2res2.GetResourceTypes())) + eventstore.Append(d2res2.GetDeviceId(), d2r2.ToUUID().String(), mockEvents.MakeResourceUpdatePending(d2r2, &commands.Content{}, makeEventMeta("a", 1), mockEvents.MakeAuditContext("userId", "1"), time.Time{}, d2res2.GetResourceTypes())) + eventstore.Append(d2res2.GetDeviceId(), d2r2.ToUUID().String(), mockEvents.MakeResourceUpdated(d2r2, commands.Status_OK, &commands.Content{}, makeEventMeta("a", 2), mockEvents.MakeAuditContext("userId", "1"), d2res2.GetResourceTypes())) + eventstore.Append(d2res2.GetDeviceId(), d2r2.ToUUID().String(), mockEvents.MakeResourceChangedEvent(d2r2, &commands.Content{}, makeEventMeta("a", 3), mockEvents.MakeAuditContext("userId", "2"), d2res2.GetResourceTypes())) return eventstore } @@ -175,7 +188,7 @@ func TestResourceProjectionTestLoadParallelModels(t *testing.T) { for i := 0; i < numDevices; i++ { for j := 0; j < numResources; j++ { resourceID := commands.NewResourceID(fmt.Sprintf("dev-%v", i), fmt.Sprintf("res-%v", j)) - eventstore.Append(resourceID.GetDeviceId(), resourceID.ToUUID().String(), mockEvents.MakeResourceStateSnapshotTaken(resourceID, &events.ResourceChanged{Content: &commands.Content{}, EventMetadata: resourceChangedEventMetadata}, makeEventMeta("a", 0), mockEvents.MakeAuditContext("userId", "2"))) + eventstore.Append(resourceID.GetDeviceId(), resourceID.ToUUID().String(), mockEvents.MakeResourceStateSnapshotTaken(resourceID, &events.ResourceChanged{Content: &commands.Content{}, EventMetadata: resourceChangedEventMetadata}, makeEventMeta("a", 0), mockEvents.MakeAuditContext("userId", "2"), []string{"type1", "type2"})) } } @@ -185,7 +198,7 @@ func TestResourceProjectionTestLoadParallelModels(t *testing.T) { "test", eventstore, nil, - func(ctx context.Context, groupID, aggregateID string) (cqrsEventStore.Model, error) { + func(_ context.Context, _, _ string) (cqrsEventStore.Model, error) { return events.NewResourceLinksSnapshotTaken(), nil }, ) @@ -201,7 +214,7 @@ func TestResourceProjectionTestLoadParallelModels(t *testing.T) { go func(v int) { defer wg.Done() n := time.Now() - p.cqrsProjection.Models(nil, func(m cqrsEventStore.Model) (wantNext bool) { return true }) + p.cqrsProjection.Models(nil, func(cqrsEventStore.Model) (wantNext bool) { return true }) diff := -1 * time.Until(n) t.Logf("%v models time %v\n", v, diff) }(i) @@ -222,14 +235,14 @@ func TestResourceProjection_Register(t *testing.T) { { name: "first valid", args: args{ - deviceID: d1res1.DeviceId, + deviceID: d1res1.GetDeviceId(), }, wantLoaded: true, }, { name: "second valid", args: args{ - deviceID: d1res1.DeviceId, + deviceID: d1res1.GetDeviceId(), }, }, { @@ -248,11 +261,11 @@ func TestResourceProjection_Register(t *testing.T) { "test", eventstore, nil, - func(ctx context.Context, groupID, aggregateID string) (cqrsEventStore.Model, error) { + func(_ context.Context, _, _ string) (cqrsEventStore.Model, error) { return events.NewResourceLinksSnapshotTaken(), nil }, ) - assert.NoError(t, err) + require.NoError(t, err) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -263,9 +276,9 @@ func TestResourceProjection_Register(t *testing.T) { assert.False(t, gotLoaded) } if tt.wantErr { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } }) } @@ -283,26 +296,26 @@ func TestResourceProjection_Unregister(t *testing.T) { { name: "first time", args: args{ - deviceID: d1res1.DeviceId, + deviceID: d1res1.GetDeviceId(), }, }, { - name: "second second", + name: "second time", args: args{ - deviceID: d1res1.DeviceId, + deviceID: d1res1.GetDeviceId(), }, }, { name: "third error", args: args{ - deviceID: d1res1.DeviceId, + deviceID: d1res1.GetDeviceId(), }, wantErr: true, }, { name: "not registered", args: args{ - deviceID: d2res1.DeviceId, + deviceID: d2res1.GetDeviceId(), }, wantErr: true, }, @@ -315,23 +328,23 @@ func TestResourceProjection_Unregister(t *testing.T) { "test", eventstore, nil, - func(ctx context.Context, groupID, aggregateID string) (cqrsEventStore.Model, error) { + func(_ context.Context, _, _ string) (cqrsEventStore.Model, error) { return events.NewResourceLinksSnapshotTaken(), nil }, ) - assert.NoError(t, err) - _, err = p.Register(ctx, d1res1.DeviceId) - assert.NoError(t, err) - _, err = p.Register(ctx, d1res1.DeviceId) - assert.NoError(t, err) + require.NoError(t, err) + _, err = p.Register(ctx, d1res1.GetDeviceId()) + require.NoError(t, err) + _, err = p.Register(ctx, d1res1.GetDeviceId()) + require.NoError(t, err) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := p.Unregister(tt.args.deviceID) if tt.wantErr { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } }) } @@ -349,17 +362,17 @@ func TestResourceLinksProjection_Models(t *testing.T) { { name: "valid dev1", args: args{ - deviceID: d1res1.DeviceId, + deviceID: d1res1.GetDeviceId(), }, want: []cqrsEventStore.Model{ &events.ResourceLinksSnapshotTaken{ Resources: map[string]*commands.Resource{ - d1res1.Href: &d1res1, - d1res3.Href: &d1res3, - d1res4.Href: &d1res4, - d1res5.Href: &d1res5, + d1res1.GetHref(): &d1res1, + d1res3.GetHref(): &d1res3, + d1res4.GetHref(): &d1res4, + d1res5.GetHref(): &d1res5, }, - DeviceId: d1res1.DeviceId, + DeviceId: d1res1.GetDeviceId(), EventMetadata: &events.EventMetadata{ Version: 3, Timestamp: 12345, @@ -372,14 +385,14 @@ func TestResourceLinksProjection_Models(t *testing.T) { { name: "valid dev2", args: args{ - deviceID: d2res1.DeviceId, + deviceID: d2res1.GetDeviceId(), }, want: []cqrsEventStore.Model{ &events.ResourceLinksSnapshotTaken{ Resources: map[string]*commands.Resource{ - d2res1.Href: &d2res1, + d2res1.GetHref(): &d2res1, }, - DeviceId: d2res1.DeviceId, + DeviceId: d2res1.GetDeviceId(), EventMetadata: &events.EventMetadata{ Version: 6, Timestamp: 12345, @@ -392,14 +405,14 @@ func TestResourceLinksProjection_Models(t *testing.T) { { name: "valid dev3", args: args{ - deviceID: d3res2.DeviceId, + deviceID: d3res2.GetDeviceId(), }, want: []cqrsEventStore.Model{ &events.ResourceLinksSnapshotTaken{ Resources: map[string]*commands.Resource{ - d3res2.Href: &d3res2, + d3res2.GetHref(): &d3res2, }, - DeviceId: d3res2.DeviceId, + DeviceId: d3res2.GetDeviceId(), EventMetadata: &events.EventMetadata{ Version: 1, Timestamp: 12345, @@ -412,12 +425,12 @@ func TestResourceLinksProjection_Models(t *testing.T) { { name: "valid dev4", args: args{ - deviceID: d4res1.DeviceId, + deviceID: d4res1.GetDeviceId(), }, want: []cqrsEventStore.Model{ &events.ResourceLinksSnapshotTaken{ Resources: map[string]*commands.Resource{}, - DeviceId: d4res1.DeviceId, + DeviceId: d4res1.GetDeviceId(), EventMetadata: &events.EventMetadata{ Version: 1, Timestamp: 12345, @@ -430,12 +443,12 @@ func TestResourceLinksProjection_Models(t *testing.T) { { name: "valid dev5", args: args{ - deviceID: d5res1.DeviceId, + deviceID: d5res1.GetDeviceId(), }, want: []cqrsEventStore.Model{ &events.ResourceLinksSnapshotTaken{ Resources: map[string]*commands.Resource{}, - DeviceId: d5res1.DeviceId, + DeviceId: d5res1.GetDeviceId(), EventMetadata: &events.EventMetadata{ Version: 1, Timestamp: 12345, @@ -454,16 +467,16 @@ func TestResourceLinksProjection_Models(t *testing.T) { "test", eventstore, nil, - func(ctx context.Context, groupID, aggregateID string) (cqrsEventStore.Model, error) { + func(_ context.Context, _, _ string) (cqrsEventStore.Model, error) { return events.NewResourceLinksSnapshotTaken(), nil }, ) - assert.NoError(t, err) + require.NoError(t, err) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err = p.Register(ctx, tt.args.deviceID) - assert.NoError(t, err) + require.NoError(t, err) got := []cqrsEventStore.Model{} p.Models(func(m cqrsEventStore.Model) (wantNext bool) { got = append(got, m) @@ -486,7 +499,7 @@ func TestResourceLinksProjection_Models(t *testing.T) { } } -func TestResourceStateProjection_Models(t *testing.T) { +func TestResourceStateProjectionModels(t *testing.T) { type args struct { resourceID *commands.ResourceId } @@ -498,16 +511,17 @@ func TestResourceStateProjection_Models(t *testing.T) { { name: "valid dev1r1", args: args{ - resourceID: commands.NewResourceID(d1res1.DeviceId, d1res1.Href), + resourceID: commands.NewResourceID(d1res1.GetDeviceId(), d1res1.GetHref()), }, want: []cqrsEventStore.Model{ &events.ResourceStateSnapshotTaken{ - ResourceId: commands.NewResourceID(d1res1.DeviceId, d1res1.Href), + ResourceId: commands.NewResourceID(d1res1.GetDeviceId(), d1res1.GetHref()), LatestResourceChange: &events.ResourceChanged{ Content: &commands.Content{}, EventMetadata: &events.EventMetadata{ Timestamp: 12345, }, + ResourceTypes: d1res1.GetResourceTypes(), }, EventMetadata: &events.EventMetadata{ Version: 4, @@ -520,7 +534,7 @@ func TestResourceStateProjection_Models(t *testing.T) { }, ResourceUpdatePendings: []*events.ResourceUpdatePending{ { - ResourceId: commands.NewResourceID(d1res1.DeviceId, d1res1.Href), + ResourceId: commands.NewResourceID(d1res1.GetDeviceId(), d1res1.GetHref()), Content: &commands.Content{}, AuditContext: &commands.AuditContext{ UserId: "userId", @@ -531,9 +545,10 @@ func TestResourceStateProjection_Models(t *testing.T) { Timestamp: 12345, ConnectionId: "a", }, + ResourceTypes: d1res1.GetResourceTypes(), }, { - ResourceId: commands.NewResourceID(d1res1.DeviceId, d1res1.Href), + ResourceId: commands.NewResourceID(d1res1.GetDeviceId(), d1res1.GetHref()), Content: &commands.Content{}, AuditContext: &commands.AuditContext{ UserId: "userId", @@ -544,47 +559,52 @@ func TestResourceStateProjection_Models(t *testing.T) { Timestamp: 12345, ConnectionId: "a", }, + ResourceTypes: d1res1.GetResourceTypes(), }, }, + ResourceTypes: d1res1.GetResourceTypes(), }, }, }, { name: "valid dev1r2", args: args{ - resourceID: commands.NewResourceID(d1res2.DeviceId, d1res2.Href), + resourceID: commands.NewResourceID(d1res2.GetDeviceId(), d1res2.GetHref()), }, want: []cqrsEventStore.Model{ &events.ResourceStateSnapshotTaken{ - ResourceId: commands.NewResourceID(d1res2.DeviceId, d1res2.Href), + ResourceId: commands.NewResourceID(d1res2.GetDeviceId(), d1res2.GetHref()), LatestResourceChange: &events.ResourceChanged{ Content: &commands.Content{}, EventMetadata: &events.EventMetadata{ Timestamp: 12345, }, + ResourceTypes: d1res2.GetResourceTypes(), }, EventMetadata: &events.EventMetadata{ Version: 0, Timestamp: 12345, ConnectionId: "a", }, - AuditContext: commands.NewAuditContext("userId", "2", ""), + AuditContext: commands.NewAuditContext("userId", "2", ""), + ResourceTypes: d1res2.GetResourceTypes(), }, }, }, { name: "valid dev1r3", args: args{ - resourceID: commands.NewResourceID(d1res3.DeviceId, d1res3.Href), + resourceID: commands.NewResourceID(d1res3.GetDeviceId(), d1res3.GetHref()), }, want: []cqrsEventStore.Model{ &events.ResourceStateSnapshotTaken{ - ResourceId: commands.NewResourceID(d1res3.DeviceId, d1res3.Href), + ResourceId: commands.NewResourceID(d1res3.GetDeviceId(), d1res3.GetHref()), LatestResourceChange: &events.ResourceChanged{ Content: &commands.Content{}, EventMetadata: &events.EventMetadata{ Timestamp: 12345, }, + ResourceTypes: d1res3.GetResourceTypes(), }, EventMetadata: &events.EventMetadata{ Version: 1, @@ -597,7 +617,7 @@ func TestResourceStateProjection_Models(t *testing.T) { }, ResourceUpdatePendings: []*events.ResourceUpdatePending{ { - ResourceId: commands.NewResourceID(d1res3.DeviceId, d1res3.Href), + ResourceId: commands.NewResourceID(d1res3.GetDeviceId(), d1res3.GetHref()), Content: &commands.Content{}, AuditContext: &commands.AuditContext{ UserId: "userId", @@ -608,8 +628,10 @@ func TestResourceStateProjection_Models(t *testing.T) { Timestamp: 12345, ConnectionId: "a", }, + ResourceTypes: d1res3.GetResourceTypes(), }, }, + ResourceTypes: d1res3.GetResourceTypes(), }, }, }, @@ -617,11 +639,11 @@ func TestResourceStateProjection_Models(t *testing.T) { { name: "valid dev1r4", args: args{ - resourceID: commands.NewResourceID(d1res4.DeviceId, d1res4.Href), + resourceID: commands.NewResourceID(d1res4.GetDeviceId(), d1res4.GetHref()), }, want: []cqrsEventStore.Model{ &events.ResourceStateSnapshotTaken{ - ResourceId: commands.NewResourceID(d1res4.DeviceId, d1res4.Href), + ResourceId: commands.NewResourceID(d1res4.GetDeviceId(), d1res4.GetHref()), LatestResourceChange: &events.ResourceChanged{ Content: &commands.Content{}, EventMetadata: &events.EventMetadata{ @@ -639,7 +661,7 @@ func TestResourceStateProjection_Models(t *testing.T) { }, ResourceUpdatePendings: []*events.ResourceUpdatePending{ { - ResourceId: commands.NewResourceID(d1res4.DeviceId, d1res4.Href), + ResourceId: commands.NewResourceID(d1res4.GetDeviceId(), d1res4.GetHref()), Content: &commands.Content{}, AuditContext: &commands.AuditContext{ UserId: "userId", @@ -650,24 +672,27 @@ func TestResourceStateProjection_Models(t *testing.T) { Timestamp: 12345, ConnectionId: "a", }, + ResourceTypes: d1res4.GetResourceTypes(), }, }, + ResourceTypes: d1res4.GetResourceTypes(), }, }, }, { name: "valid dev2r1", args: args{ - resourceID: commands.NewResourceID(d2res1.DeviceId, d2res1.Href), + resourceID: commands.NewResourceID(d2res1.GetDeviceId(), d2res1.GetHref()), }, want: []cqrsEventStore.Model{ &events.ResourceStateSnapshotTaken{ - ResourceId: commands.NewResourceID(d2res1.DeviceId, d2res1.Href), + ResourceId: commands.NewResourceID(d2res1.GetDeviceId(), d2res1.GetHref()), LatestResourceChange: &events.ResourceChanged{ Content: &commands.Content{}, EventMetadata: &events.EventMetadata{ Timestamp: 12345, }, + ResourceTypes: d2res1.GetResourceTypes(), }, EventMetadata: &events.EventMetadata{ Version: 2, @@ -679,19 +704,20 @@ func TestResourceStateProjection_Models(t *testing.T) { CorrelationId: "1", }, ResourceUpdatePendings: []*events.ResourceUpdatePending{}, + ResourceTypes: d2res1.GetResourceTypes(), }, }, }, { name: "valid dev2r2", args: args{ - resourceID: commands.NewResourceID(d2res2.DeviceId, d2res2.Href), + resourceID: commands.NewResourceID(d2res2.GetDeviceId(), d2res2.GetHref()), }, want: []cqrsEventStore.Model{ &events.ResourceStateSnapshotTaken{ - ResourceId: commands.NewResourceID(d2res2.DeviceId, d2res2.Href), + ResourceId: commands.NewResourceID(d2res2.GetDeviceId(), d2res2.GetHref()), LatestResourceChange: &events.ResourceChanged{ - ResourceId: commands.NewResourceID(d2res2.DeviceId, d2res2.Href), + ResourceId: commands.NewResourceID(d2res2.GetDeviceId(), d2res2.GetHref()), Content: &commands.Content{}, AuditContext: &commands.AuditContext{UserId: "userId", CorrelationId: "2"}, EventMetadata: &events.EventMetadata{ @@ -699,6 +725,7 @@ func TestResourceStateProjection_Models(t *testing.T) { Timestamp: 12345, ConnectionId: "a", }, + ResourceTypes: d2res2.GetResourceTypes(), }, EventMetadata: &events.EventMetadata{ Version: 3, @@ -710,6 +737,7 @@ func TestResourceStateProjection_Models(t *testing.T) { CorrelationId: "2", }, ResourceUpdatePendings: []*events.ResourceUpdatePending{}, + ResourceTypes: d2res2.GetResourceTypes(), }, }, }, @@ -722,16 +750,16 @@ func TestResourceStateProjection_Models(t *testing.T) { "test", eventstore, nil, - func(ctx context.Context, groupID, aggregateID string) (cqrsEventStore.Model, error) { + func(_ context.Context, _, _ string) (cqrsEventStore.Model, error) { return events.NewResourceStateSnapshotTaken(), nil }, ) - assert.NoError(t, err) + require.NoError(t, err) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { _, err = p.Register(ctx, tt.args.resourceID.GetDeviceId()) - assert.NoError(t, err) + require.NoError(t, err) got := []cqrsEventStore.Model{} p.Models(func(m cqrsEventStore.Model) (wantNext bool) { got = append(got, m) @@ -766,7 +794,7 @@ func TestResourceProjection_ForceUpdate(t *testing.T) { { name: "valid", args: args{ - resourceID: commands.NewResourceID(d1res1.DeviceId, d1res1.Href), + resourceID: commands.NewResourceID(d1res1.GetDeviceId(), d1res1.GetHref()), }, }, { @@ -785,20 +813,20 @@ func TestResourceProjection_ForceUpdate(t *testing.T) { "test", eventstore, nil, - func(ctx context.Context, groupID, aggregateID string) (cqrsEventStore.Model, error) { + func(_ context.Context, _, _ string) (cqrsEventStore.Model, error) { return events.NewResourceStateSnapshotTaken(), nil }, ) - assert.NoError(t, err) - _, err = p.Register(ctx, d1res1.DeviceId) - assert.NoError(t, err) + require.NoError(t, err) + _, err = p.Register(ctx, d1res1.GetDeviceId()) + require.NoError(t, err) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := p.ForceUpdate(ctx, tt.args.resourceID) if tt.wantErr { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } }) } diff --git a/resource-aggregate/cqrs/utils/utils_test.go b/resource-aggregate/cqrs/utils/utils_test.go index 2c91ac99b..75d294e57 100644 --- a/resource-aggregate/cqrs/utils/utils_test.go +++ b/resource-aggregate/cqrs/utils/utils_test.go @@ -7,6 +7,7 @@ import ( "github.com/plgd-dev/hub/v2/resource-aggregate/cqrs/utils" "github.com/plgd-dev/hub/v2/resource-aggregate/events" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestDummyForCoverage(t *testing.T) { @@ -21,30 +22,30 @@ func TestDummyForCoverage(t *testing.T) { hubID := "h" em := events.MakeEventMeta(connID, sequence, version, hubID) - assert.Equal(t, connID, em.ConnectionId) - assert.Equal(t, sequence, em.Sequence) - assert.Equal(t, version, em.Version) - assert.Equal(t, hubID, em.HubId) + assert.Equal(t, connID, em.GetConnectionId()) + assert.Equal(t, sequence, em.GetSequence()) + assert.Equal(t, version, em.GetVersion()) + assert.Equal(t, hubID, em.GetHubId()) ac := commands.NewAuditContext(userID, corID, userID) - assert.Equal(t, corID, ac.CorrelationId) - assert.Equal(t, userID, ac.UserId) + assert.Equal(t, corID, ac.GetCorrelationId()) + assert.Equal(t, userID, ac.GetUserId()) } func TestProtobufMarshaler(t *testing.T) { req := events.ResourceChanged{} out, err := utils.Marshal(&req) - assert.NoError(t, err) + require.NoError(t, err) assert.NotEmpty(t, out) a := struct{}{} _, err = utils.Marshal(a) - assert.Error(t, err) + require.Error(t, err) resp := events.ResourceChanged{} err = utils.Unmarshal(out, &resp) - assert.NoError(t, err) + require.NoError(t, err) err = utils.Unmarshal(out, a) - assert.Error(t, err) + require.Error(t, err) } diff --git a/resource-aggregate/events/deviceMetadataSnapshotTaken.go b/resource-aggregate/events/deviceMetadataSnapshotTaken.go index e213803da..2e896fe01 100644 --- a/resource-aggregate/events/deviceMetadataSnapshotTaken.go +++ b/resource-aggregate/events/deviceMetadataSnapshotTaken.go @@ -58,6 +58,10 @@ func (d *DeviceMetadataSnapshotTaken) ServiceID() (string, bool) { return d.GetDeviceMetadataUpdated().GetConnection().GetServiceId(), true } +func (d *DeviceMetadataSnapshotTaken) Types() []string { + return nil +} + func (d *DeviceMetadataSnapshotTaken) CopyData(event *DeviceMetadataSnapshotTaken) { d.DeviceId = event.GetDeviceId() d.DeviceMetadataUpdated = event.GetDeviceMetadataUpdated() @@ -74,7 +78,8 @@ func (d *DeviceMetadataSnapshotTaken) CheckInitialized() bool { func (d *DeviceMetadataSnapshotTaken) HandleDeviceMetadataUpdated(_ context.Context, upd *DeviceMetadataUpdated, confirm bool) (bool, error) { index := -1 - for i, event := range d.GetUpdatePendings() { + updatePendings := d.GetUpdatePendings() + for i, event := range updatePendings { if event.GetAuditContext().GetCorrelationId() == upd.GetAuditContext().GetCorrelationId() { index = i break @@ -84,9 +89,10 @@ func (d *DeviceMetadataSnapshotTaken) HandleDeviceMetadataUpdated(_ context.Cont return false, status.Errorf(codes.InvalidArgument, "cannot find twin synchronization update pending event with correlationId('%v')", upd.GetAuditContext().GetCorrelationId()) } if index >= 0 { - d.UpdatePendings = append(d.UpdatePendings[:index], d.UpdatePendings[index+1:]...) + updatePendings = append(updatePendings[:index], updatePendings[index+1:]...) + d.UpdatePendings = updatePendings } - if d.DeviceMetadataUpdated.Equal(upd) { + if d.GetDeviceMetadataUpdated().Equal(upd) { return false, nil } @@ -321,9 +327,9 @@ func (d *DeviceMetadataSnapshotTaken) getTwinSynchronizationForConnectedDevice(r func (d *DeviceMetadataSnapshotTaken) getTwinSynchronizationForDisconnectedDevice(req *commands.UpdateDeviceMetadataRequest) (*commands.TwinSynchronization, error) { twinSynchronization := d.prepareTwinSynchronization() - if d.DeviceMetadataUpdated.GetConnection().IsOnline() && !req.GetConnection().IsOnline() && d.DeviceMetadataUpdated.GetConnection().GetId() != req.GetConnection().GetId() { + if d.GetDeviceMetadataUpdated().GetConnection().IsOnline() && !req.GetConnection().IsOnline() && d.GetDeviceMetadataUpdated().GetConnection().GetId() != req.GetConnection().GetId() { // if previous status was online and new status is offline, the connectionId must be the same - return nil, status.Errorf(codes.InvalidArgument, "cannot update connection status online(id='%v') to offline(id='%v'): connectionId mismatch", d.DeviceMetadataUpdated.GetConnection().GetId(), req.GetConnection().GetId()) + return nil, status.Errorf(codes.InvalidArgument, "cannot update connection status online(id='%v') to offline(id='%v'): connectionId mismatch", d.GetDeviceMetadataUpdated().GetConnection().GetId(), req.GetConnection().GetId()) } if d.GetDeviceMetadataUpdated().GetTwinEnabled() { twinSynchronization.State = commands.TwinSynchronization_OUT_OF_SYNC @@ -356,6 +362,11 @@ func (d *DeviceMetadataSnapshotTaken) updateDeviceConnection(ctx context.Context req.GetConnection().ConnectedAt = lastConnectedAt } + // keep local endpoints from the previous event + if len(req.GetConnection().GetLocalEndpoints()) == 0 { + req.GetConnection().LocalEndpoints = d.GetDeviceMetadataUpdated().GetConnection().GetLocalEndpoints() + } + getTwinSynchronization := d.getTwinSynchronizationForDisconnectedDevice if req.GetConnection().IsOnline() { getTwinSynchronization = d.getTwinSynchronizationForConnectedDevice diff --git a/resource-aggregate/events/deviceMetadataSnapshotTaken_test.go b/resource-aggregate/events/deviceMetadataSnapshotTaken_test.go index 444e765fe..eb7189ce4 100644 --- a/resource-aggregate/events/deviceMetadataSnapshotTaken_test.go +++ b/resource-aggregate/events/deviceMetadataSnapshotTaken_test.go @@ -16,13 +16,16 @@ import ( "google.golang.org/protobuf/proto" ) +var localEndpoints = []string{"coap://localhost:5683"} + var testEventDeviceMetadataSnapshotTaken events.DeviceMetadataSnapshotTaken = events.DeviceMetadataSnapshotTaken{ DeviceId: dev1, DeviceMetadataUpdated: &events.DeviceMetadataUpdated{ DeviceId: dev1, Connection: &commands.Connection{ - Status: commands.Connection_ONLINE, - Id: "con1", + Status: commands.Connection_ONLINE, + Id: "con1", + LocalEndpoints: localEndpoints, }, TwinEnabled: true, AuditContext: &commands.AuditContext{ @@ -170,9 +173,10 @@ func TestDeviceMetadataSnapshotTakenHandleCommand(t *testing.T) { CorrelationId: correlationID, Update: &commands.UpdateDeviceMetadataRequest_Connection{ Connection: &commands.Connection{ - Status: commands.Connection_ONLINE, - ConnectedAt: connectedAt, - Protocol: commands.Connection_COAPS, + Status: commands.Connection_ONLINE, + ConnectedAt: connectedAt, + Protocol: commands.Connection_COAPS, + LocalEndpoints: localEndpoints, }, }, }, @@ -181,9 +185,10 @@ func TestDeviceMetadataSnapshotTakenHandleCommand(t *testing.T) { pb.ToEvent(&events.DeviceMetadataUpdated{ DeviceId: deviceID, Connection: &commands.Connection{ - Status: commands.Connection_ONLINE, - ConnectedAt: connectedAt, - Protocol: commands.Connection_COAPS, + Status: commands.Connection_ONLINE, + ConnectedAt: connectedAt, + Protocol: commands.Connection_COAPS, + LocalEndpoints: localEndpoints, }, TwinEnabled: true, TwinSynchronization: &commands.TwinSynchronization{}, @@ -230,9 +235,10 @@ func TestDeviceMetadataSnapshotTakenHandleCommand(t *testing.T) { pb.ToEvent(&events.DeviceMetadataUpdated{ DeviceId: deviceID, Connection: &commands.Connection{ - Status: commands.Connection_OFFLINE, - ConnectedAt: connectedAt, - Protocol: commands.Connection_COAPS, + Status: commands.Connection_OFFLINE, + ConnectedAt: connectedAt, + Protocol: commands.Connection_COAPS, + LocalEndpoints: localEndpoints, }, TwinEnabled: true, TwinSynchronization: &commands.TwinSynchronization{}, diff --git a/resource-aggregate/events/deviceMetadataUpdatePending.go b/resource-aggregate/events/deviceMetadataUpdatePending.go index 2506b5d59..6d86da1c3 100644 --- a/resource-aggregate/events/deviceMetadataUpdatePending.go +++ b/resource-aggregate/events/deviceMetadataUpdatePending.go @@ -47,6 +47,10 @@ func (d *DeviceMetadataUpdatePending) ETag() *eventstore.ETagData { return nil } +func (d *DeviceMetadataUpdatePending) Types() []string { + return nil +} + func (d *DeviceMetadataUpdatePending) CopyData(event *DeviceMetadataUpdatePending) { d.DeviceId = event.GetDeviceId() d.UpdatePending = event.GetUpdatePending() diff --git a/resource-aggregate/events/deviceMetadataUpdated.go b/resource-aggregate/events/deviceMetadataUpdated.go index 86c56fd19..e446f3660 100644 --- a/resource-aggregate/events/deviceMetadataUpdated.go +++ b/resource-aggregate/events/deviceMetadataUpdated.go @@ -6,6 +6,7 @@ import ( pkgTime "github.com/plgd-dev/hub/v2/pkg/time" commands "github.com/plgd-dev/hub/v2/resource-aggregate/commands" "github.com/plgd-dev/hub/v2/resource-aggregate/cqrs/eventstore" + "golang.org/x/exp/slices" "google.golang.org/protobuf/proto" ) @@ -51,6 +52,10 @@ func (d *DeviceMetadataUpdated) ServiceID() (string, bool) { return d.GetConnection().GetServiceId(), true } +func (d *DeviceMetadataUpdated) Types() []string { + return nil +} + func (d *DeviceMetadataUpdated) CopyData(event *DeviceMetadataUpdated) { d.DeviceId = event.GetDeviceId() d.Connection = event.GetConnection() @@ -74,6 +79,7 @@ func (d *DeviceMetadataUpdated) Equal(upd *DeviceMetadataUpdated) bool { return d.GetConnection().GetStatus() == upd.GetConnection().GetStatus() && d.GetConnection().GetId() == upd.GetConnection().GetId() && d.GetConnection().GetProtocol() == upd.GetConnection().GetProtocol() && + slices.Equal(d.GetConnection().GetLocalEndpoints(), upd.GetConnection().GetLocalEndpoints()) && d.GetCanceled() == upd.GetCanceled() && d.GetTwinEnabled() == upd.GetTwinEnabled() && d.GetAuditContext().GetUserId() == upd.GetAuditContext().GetUserId() && diff --git a/resource-aggregate/events/deviceMetadataUpdated_test.go b/resource-aggregate/events/deviceMetadataUpdated_test.go index c5af85f82..c5f0a394c 100644 --- a/resource-aggregate/events/deviceMetadataUpdated_test.go +++ b/resource-aggregate/events/deviceMetadataUpdated_test.go @@ -48,9 +48,9 @@ func TestDeviceMetadataUpdated_Equal(t *testing.T) { { name: "Identity", fields: fields{ - Connection: upd.Connection, - TwinEnabled: upd.TwinEnabled, - AuditContext: upd.AuditContext, + Connection: upd.GetConnection(), + TwinEnabled: upd.GetTwinEnabled(), + AuditContext: upd.GetAuditContext(), }, args: args{upd}, want: true, @@ -61,8 +61,8 @@ func TestDeviceMetadataUpdated_Equal(t *testing.T) { Connection: &commands.Connection{ Status: commands.Connection_OFFLINE, }, - TwinEnabled: upd.TwinEnabled, - AuditContext: upd.AuditContext, + TwinEnabled: upd.GetTwinEnabled(), + AuditContext: upd.GetAuditContext(), }, args: args{upd}, want: false, @@ -70,9 +70,9 @@ func TestDeviceMetadataUpdated_Equal(t *testing.T) { { name: "Changed TwinSynchronization", fields: fields{ - Connection: upd.Connection, + Connection: upd.GetConnection(), TwinEnabled: false, - AuditContext: upd.AuditContext, + AuditContext: upd.GetAuditContext(), }, args: args{upd}, want: false, @@ -80,11 +80,11 @@ func TestDeviceMetadataUpdated_Equal(t *testing.T) { { name: "Changed AuditContext.UserId", fields: fields{ - Connection: upd.Connection, - TwinEnabled: upd.TwinEnabled, + Connection: upd.GetConnection(), + TwinEnabled: upd.GetTwinEnabled(), AuditContext: &commands.AuditContext{ - UserId: upd.AuditContext.UserId + "0", - CorrelationId: upd.AuditContext.CorrelationId, + UserId: upd.GetAuditContext().GetUserId() + "0", + CorrelationId: upd.GetAuditContext().GetCorrelationId(), }, }, args: args{upd}, @@ -93,11 +93,11 @@ func TestDeviceMetadataUpdated_Equal(t *testing.T) { { name: "Changed AuditContext.CorrelationId", fields: fields{ - Connection: upd.Connection, - TwinEnabled: upd.TwinEnabled, + Connection: upd.GetConnection(), + TwinEnabled: upd.GetTwinEnabled(), AuditContext: &commands.AuditContext{ - UserId: upd.AuditContext.UserId, - CorrelationId: upd.AuditContext.CorrelationId + "0", + UserId: upd.GetAuditContext().GetUserId(), + CorrelationId: upd.GetAuditContext().GetCorrelationId() + "0", }, }, args: args{upd}, diff --git a/resource-aggregate/events/events.pb.go b/resource-aggregate/events/events.pb.go index c5c2d3ac4..53edbf02c 100644 --- a/resource-aggregate/events/events.pb.go +++ b/resource-aggregate/events/events.pb.go @@ -344,6 +344,7 @@ type ResourceChanged struct { AuditContext *commands.AuditContext `protobuf:"bytes,4,opt,name=audit_context,json=auditContext,proto3" json:"audit_context,omitempty"` EventMetadata *EventMetadata `protobuf:"bytes,5,opt,name=event_metadata,json=eventMetadata,proto3" json:"event_metadata,omitempty"` Etag []byte `protobuf:"bytes,6,opt,name=etag,proto3" json:"etag,omitempty"` // etag of the resource used by twin synchronization + ResourceTypes []string `protobuf:"bytes,7,rep,name=resource_types,json=resourceTypes,proto3" json:"resource_types,omitempty"` // Open telemetry data propagated to asynchronous events OpenTelemetryCarrier map[string]string `protobuf:"bytes,100,rep,name=open_telemetry_carrier,json=openTelemetryCarrier,proto3" json:"open_telemetry_carrier,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } @@ -422,6 +423,13 @@ func (x *ResourceChanged) GetEtag() []byte { return nil } +func (x *ResourceChanged) GetResourceTypes() []string { + if x != nil { + return x.ResourceTypes + } + return nil +} + func (x *ResourceChanged) GetOpenTelemetryCarrier() map[string]string { if x != nil { return x.OpenTelemetryCarrier @@ -440,6 +448,7 @@ type ResourceUpdatePending struct { AuditContext *commands.AuditContext `protobuf:"bytes,4,opt,name=audit_context,json=auditContext,proto3" json:"audit_context,omitempty"` EventMetadata *EventMetadata `protobuf:"bytes,5,opt,name=event_metadata,json=eventMetadata,proto3" json:"event_metadata,omitempty"` ValidUntil int64 `protobuf:"varint,6,opt,name=valid_until,json=validUntil,proto3" json:"valid_until,omitempty"` // unix timestamp in nanoseconds (https://golang.org/pkg/time/#Time.UnixNano) when pending event is considered as expired. 0 means forever. + ResourceTypes []string `protobuf:"bytes,7,rep,name=resource_types,json=resourceTypes,proto3" json:"resource_types,omitempty"` // Open telemetry data propagated to asynchronous events OpenTelemetryCarrier map[string]string `protobuf:"bytes,100,rep,name=open_telemetry_carrier,json=openTelemetryCarrier,proto3" json:"open_telemetry_carrier,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } @@ -518,6 +527,13 @@ func (x *ResourceUpdatePending) GetValidUntil() int64 { return 0 } +func (x *ResourceUpdatePending) GetResourceTypes() []string { + if x != nil { + return x.ResourceTypes + } + return nil +} + func (x *ResourceUpdatePending) GetOpenTelemetryCarrier() map[string]string { if x != nil { return x.OpenTelemetryCarrier @@ -535,6 +551,7 @@ type ResourceUpdated struct { Content *commands.Content `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` AuditContext *commands.AuditContext `protobuf:"bytes,4,opt,name=audit_context,json=auditContext,proto3" json:"audit_context,omitempty"` EventMetadata *EventMetadata `protobuf:"bytes,5,opt,name=event_metadata,json=eventMetadata,proto3" json:"event_metadata,omitempty"` + ResourceTypes []string `protobuf:"bytes,6,rep,name=resource_types,json=resourceTypes,proto3" json:"resource_types,omitempty"` // Open telemetry data propagated to asynchronous events OpenTelemetryCarrier map[string]string `protobuf:"bytes,100,rep,name=open_telemetry_carrier,json=openTelemetryCarrier,proto3" json:"open_telemetry_carrier,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } @@ -606,6 +623,13 @@ func (x *ResourceUpdated) GetEventMetadata() *EventMetadata { return nil } +func (x *ResourceUpdated) GetResourceTypes() []string { + if x != nil { + return x.ResourceTypes + } + return nil +} + func (x *ResourceUpdated) GetOpenTelemetryCarrier() map[string]string { if x != nil { return x.OpenTelemetryCarrier @@ -624,6 +648,7 @@ type ResourceRetrievePending struct { EventMetadata *EventMetadata `protobuf:"bytes,4,opt,name=event_metadata,json=eventMetadata,proto3" json:"event_metadata,omitempty"` ValidUntil int64 `protobuf:"varint,5,opt,name=valid_until,json=validUntil,proto3" json:"valid_until,omitempty"` // unix timestamp in nanoseconds (https://golang.org/pkg/time/#Time.UnixNano) when pending event is considered as expired. 0 means forever. Etag [][]byte `protobuf:"bytes,6,rep,name=etag,proto3" json:"etag,omitempty"` + ResourceTypes []string `protobuf:"bytes,7,rep,name=resource_types,json=resourceTypes,proto3" json:"resource_types,omitempty"` // Open telemetry data propagated to asynchronous events OpenTelemetryCarrier map[string]string `protobuf:"bytes,100,rep,name=open_telemetry_carrier,json=openTelemetryCarrier,proto3" json:"open_telemetry_carrier,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } @@ -702,6 +727,13 @@ func (x *ResourceRetrievePending) GetEtag() [][]byte { return nil } +func (x *ResourceRetrievePending) GetResourceTypes() []string { + if x != nil { + return x.ResourceTypes + } + return nil +} + func (x *ResourceRetrievePending) GetOpenTelemetryCarrier() map[string]string { if x != nil { return x.OpenTelemetryCarrier @@ -720,6 +752,7 @@ type ResourceRetrieved struct { AuditContext *commands.AuditContext `protobuf:"bytes,4,opt,name=audit_context,json=auditContext,proto3" json:"audit_context,omitempty"` EventMetadata *EventMetadata `protobuf:"bytes,5,opt,name=event_metadata,json=eventMetadata,proto3" json:"event_metadata,omitempty"` Etag []byte `protobuf:"bytes,6,opt,name=etag,proto3" json:"etag,omitempty"` + ResourceTypes []string `protobuf:"bytes,7,rep,name=resource_types,json=resourceTypes,proto3" json:"resource_types,omitempty"` // Open telemetry data propagated to asynchronous events OpenTelemetryCarrier map[string]string `protobuf:"bytes,100,rep,name=open_telemetry_carrier,json=openTelemetryCarrier,proto3" json:"open_telemetry_carrier,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } @@ -798,6 +831,13 @@ func (x *ResourceRetrieved) GetEtag() []byte { return nil } +func (x *ResourceRetrieved) GetResourceTypes() []string { + if x != nil { + return x.ResourceTypes + } + return nil +} + func (x *ResourceRetrieved) GetOpenTelemetryCarrier() map[string]string { if x != nil { return x.OpenTelemetryCarrier @@ -815,6 +855,7 @@ type ResourceDeletePending struct { EventMetadata *EventMetadata `protobuf:"bytes,3,opt,name=event_metadata,json=eventMetadata,proto3" json:"event_metadata,omitempty"` ValidUntil int64 `protobuf:"varint,4,opt,name=valid_until,json=validUntil,proto3" json:"valid_until,omitempty"` // unix timestamp in nanoseconds (https://golang.org/pkg/time/#Time.UnixNano) when pending event is considered as expired. 0 means forever. ResourceInterface string `protobuf:"bytes,5,opt,name=resource_interface,json=resourceInterface,proto3" json:"resource_interface,omitempty"` + ResourceTypes []string `protobuf:"bytes,6,rep,name=resource_types,json=resourceTypes,proto3" json:"resource_types,omitempty"` // Open telemetry data propagated to asynchronous events OpenTelemetryCarrier map[string]string `protobuf:"bytes,100,rep,name=open_telemetry_carrier,json=openTelemetryCarrier,proto3" json:"open_telemetry_carrier,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } @@ -886,6 +927,13 @@ func (x *ResourceDeletePending) GetResourceInterface() string { return "" } +func (x *ResourceDeletePending) GetResourceTypes() []string { + if x != nil { + return x.ResourceTypes + } + return nil +} + func (x *ResourceDeletePending) GetOpenTelemetryCarrier() map[string]string { if x != nil { return x.OpenTelemetryCarrier @@ -903,6 +951,7 @@ type ResourceDeleted struct { Content *commands.Content `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` AuditContext *commands.AuditContext `protobuf:"bytes,4,opt,name=audit_context,json=auditContext,proto3" json:"audit_context,omitempty"` EventMetadata *EventMetadata `protobuf:"bytes,5,opt,name=event_metadata,json=eventMetadata,proto3" json:"event_metadata,omitempty"` + ResourceTypes []string `protobuf:"bytes,6,rep,name=resource_types,json=resourceTypes,proto3" json:"resource_types,omitempty"` // Open telemetry data propagated to asynchronous events OpenTelemetryCarrier map[string]string `protobuf:"bytes,100,rep,name=open_telemetry_carrier,json=openTelemetryCarrier,proto3" json:"open_telemetry_carrier,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } @@ -974,6 +1023,13 @@ func (x *ResourceDeleted) GetEventMetadata() *EventMetadata { return nil } +func (x *ResourceDeleted) GetResourceTypes() []string { + if x != nil { + return x.ResourceTypes + } + return nil +} + func (x *ResourceDeleted) GetOpenTelemetryCarrier() map[string]string { if x != nil { return x.OpenTelemetryCarrier @@ -991,6 +1047,7 @@ type ResourceCreatePending struct { AuditContext *commands.AuditContext `protobuf:"bytes,3,opt,name=audit_context,json=auditContext,proto3" json:"audit_context,omitempty"` EventMetadata *EventMetadata `protobuf:"bytes,4,opt,name=event_metadata,json=eventMetadata,proto3" json:"event_metadata,omitempty"` ValidUntil int64 `protobuf:"varint,5,opt,name=valid_until,json=validUntil,proto3" json:"valid_until,omitempty"` // unix timestamp in nanoseconds (https://golang.org/pkg/time/#Time.UnixNano) when pending event is considered as expired. 0 means forever. + ResourceTypes []string `protobuf:"bytes,6,rep,name=resource_types,json=resourceTypes,proto3" json:"resource_types,omitempty"` // Open telemetry data propagated to asynchronous events OpenTelemetryCarrier map[string]string `protobuf:"bytes,100,rep,name=open_telemetry_carrier,json=openTelemetryCarrier,proto3" json:"open_telemetry_carrier,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } @@ -1062,6 +1119,13 @@ func (x *ResourceCreatePending) GetValidUntil() int64 { return 0 } +func (x *ResourceCreatePending) GetResourceTypes() []string { + if x != nil { + return x.ResourceTypes + } + return nil +} + func (x *ResourceCreatePending) GetOpenTelemetryCarrier() map[string]string { if x != nil { return x.OpenTelemetryCarrier @@ -1079,6 +1143,7 @@ type ResourceCreated struct { Content *commands.Content `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` AuditContext *commands.AuditContext `protobuf:"bytes,4,opt,name=audit_context,json=auditContext,proto3" json:"audit_context,omitempty"` EventMetadata *EventMetadata `protobuf:"bytes,5,opt,name=event_metadata,json=eventMetadata,proto3" json:"event_metadata,omitempty"` + ResourceTypes []string `protobuf:"bytes,6,rep,name=resource_types,json=resourceTypes,proto3" json:"resource_types,omitempty"` // Open telemetry data propagated to asynchronous events OpenTelemetryCarrier map[string]string `protobuf:"bytes,100,rep,name=open_telemetry_carrier,json=openTelemetryCarrier,proto3" json:"open_telemetry_carrier,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } @@ -1150,6 +1215,13 @@ func (x *ResourceCreated) GetEventMetadata() *EventMetadata { return nil } +func (x *ResourceCreated) GetResourceTypes() []string { + if x != nil { + return x.ResourceTypes + } + return nil +} + func (x *ResourceCreated) GetOpenTelemetryCarrier() map[string]string { if x != nil { return x.OpenTelemetryCarrier @@ -1170,6 +1242,7 @@ type ResourceStateSnapshotTaken struct { ResourceDeletePendings []*ResourceDeletePending `protobuf:"bytes,6,rep,name=resource_delete_pendings,json=resourceDeletePendings,proto3" json:"resource_delete_pendings,omitempty"` // expired events will be removed by creating a new snapshot. AuditContext *commands.AuditContext `protobuf:"bytes,7,opt,name=audit_context,json=auditContext,proto3" json:"audit_context,omitempty"` EventMetadata *EventMetadata `protobuf:"bytes,8,opt,name=event_metadata,json=eventMetadata,proto3" json:"event_metadata,omitempty"` + ResourceTypes []string `protobuf:"bytes,9,rep,name=resource_types,json=resourceTypes,proto3" json:"resource_types,omitempty"` } func (x *ResourceStateSnapshotTaken) Reset() { @@ -1260,6 +1333,13 @@ func (x *ResourceStateSnapshotTaken) GetEventMetadata() *EventMetadata { return nil } +func (x *ResourceStateSnapshotTaken) GetResourceTypes() []string { + if x != nil { + return x.ResourceTypes + } + return nil +} + type DeviceMetadataUpdated struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1897,7 +1977,7 @@ var file_resource_aggregate_pb_events_proto_rawDesc = []byte{ 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xac, 0x04, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd3, 0x04, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, @@ -1920,28 +2000,69 @@ var file_resource_aggregate_pb_events_proto_rawDesc = []byte{ 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x04, 0x65, 0x74, 0x61, 0x67, 0x12, 0x75, 0x0a, 0x16, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x74, - 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, - 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x2e, 0x4f, 0x70, - 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, - 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x6f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, - 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x1a, 0x47, 0x0a, - 0x19, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, - 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xbe, 0x04, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, - 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x11, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, - 0x63, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, + 0x52, 0x04, 0x65, 0x74, 0x61, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x75, 0x0a, + 0x16, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, + 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, + 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x64, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, + 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, + 0x6f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, + 0x72, 0x69, 0x65, 0x72, 0x1a, 0x47, 0x0a, 0x19, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xe5, 0x04, + 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, + 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, + 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, + 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x4a, 0x0a, 0x0e, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x5f, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, + 0x7b, 0x0a, 0x16, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, + 0x79, 0x5f, 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x45, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x4f, 0x70, 0x65, + 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, + 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x6f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x1a, 0x47, 0x0a, 0x19, + 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, + 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xbf, 0x04, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, + 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, + 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x47, 0x0a, 0x0d, 0x61, @@ -1954,13 +2075,122 @@ var file_resource_aggregate_pb_events_proto_rawDesc = []byte{ 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x74, 0x69, - 0x6c, 0x12, 0x7b, 0x0a, 0x16, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, + 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x75, 0x0a, 0x16, 0x6f, 0x70, 0x65, 0x6e, 0x5f, + 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, + 0x72, 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x4f, + 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, + 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x6f, 0x70, 0x65, 0x6e, 0x54, 0x65, + 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x1a, 0x47, + 0x0a, 0x19, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, + 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc4, 0x04, 0x0a, 0x17, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x50, 0x65, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x4a, + 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x5f, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x65, + 0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x04, 0x65, 0x74, 0x61, 0x67, 0x12, + 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x7d, 0x0a, 0x16, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x74, + 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, + 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x47, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x50, 0x65, + 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, + 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x14, 0x6f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, + 0x72, 0x72, 0x69, 0x65, 0x72, 0x1a, 0x47, 0x0a, 0x19, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, + 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd7, + 0x04, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x74, 0x72, 0x69, + 0x65, 0x76, 0x65, 0x64, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, + 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, + 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, + 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, + 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, + 0x4a, 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x65, + 0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x65, 0x74, 0x61, 0x67, 0x12, + 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x77, 0x0a, 0x16, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x74, + 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, + 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x64, 0x2e, + 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, + 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x6f, 0x70, 0x65, 0x6e, 0x54, + 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x1a, + 0x47, 0x0a, 0x19, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, + 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xac, 0x04, 0x0a, 0x15, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x4a, + 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x5f, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x12, 0x2d, 0x0a, 0x12, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x73, 0x12, 0x7b, 0x0a, 0x16, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x4f, + 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x6f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x1a, 0x47, @@ -1968,8 +2198,8 @@ var file_resource_aggregate_pb_events_proto_rawDesc = []byte{ 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x98, 0x04, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x41, 0x0a, 0x0b, 0x72, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xbf, 0x04, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, @@ -1990,11 +2220,49 @@ var file_resource_aggregate_pb_events_proto_rawDesc = []byte{ 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x12, 0x75, 0x0a, 0x16, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, - 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x18, 0x64, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, - 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, + 0x74, 0x61, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x75, 0x0a, 0x16, 0x6f, 0x70, 0x65, + 0x6e, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x72, 0x72, + 0x69, 0x65, 0x72, 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, + 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, + 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, + 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x6f, 0x70, 0x65, 0x6e, + 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, + 0x1a, 0x47, 0x0a, 0x19, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, + 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb6, 0x04, 0x0a, 0x15, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, + 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, + 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, + 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x4a, 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x75, 0x6e, + 0x74, 0x69, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x7b, 0x0a, 0x16, + 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, + 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x6f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x1a, 0x47, 0x0a, 0x19, 0x4f, 0x70, 0x65, @@ -2002,33 +2270,124 @@ var file_resource_aggregate_pb_events_proto_rawDesc = []byte{ 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x9d, 0x04, 0x0a, 0x17, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, - 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x41, - 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, - 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, - 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, - 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, - 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, - 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x4a, 0x0a, 0x0e, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, - 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x75, - 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, 0x18, 0x06, - 0x20, 0x03, 0x28, 0x0c, 0x52, 0x04, 0x65, 0x74, 0x61, 0x67, 0x12, 0x7d, 0x0a, 0x16, 0x6f, 0x70, - 0x65, 0x6e, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x72, - 0x72, 0x69, 0x65, 0x72, 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x47, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, - 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, - 0x76, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, + 0x38, 0x01, 0x22, 0xbf, 0x04, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, + 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, + 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, + 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x12, 0x4a, 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, + 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x25, 0x0a, + 0x0e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, + 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x73, 0x12, 0x75, 0x0a, 0x16, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x74, 0x65, 0x6c, + 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x18, 0x64, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x4f, 0x70, 0x65, 0x6e, + 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x6f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, + 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x1a, 0x47, 0x0a, 0x19, 0x4f, + 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, + 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9a, 0x06, 0x0a, 0x1a, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x54, 0x61, + 0x6b, 0x65, 0x6e, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x16, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, + 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x14, 0x6c, + 0x61, 0x74, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x12, 0x65, 0x0a, 0x18, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x52, 0x16, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x6b, 0x0a, 0x1a, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x5f, + 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, + 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, + 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x18, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x50, + 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x65, 0x0a, 0x18, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x72, 0x65, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, + 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, + 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x16, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x65, + 0x0a, 0x18, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2b, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x16, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x6e, + 0x64, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x4a, + 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x73, 0x22, 0xf4, 0x04, 0x0a, 0x15, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x40, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, + 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x5c, 0x0a, 0x14, 0x74, 0x77, + 0x69, 0x6e, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, + 0x54, 0x77, 0x69, 0x6e, 0x53, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x74, 0x77, 0x69, 0x6e, 0x53, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, + 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x77, 0x69, 0x6e, + 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, + 0x74, 0x77, 0x69, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x47, 0x0a, 0x0d, 0x61, + 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x12, 0x4a, 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x08, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x12, 0x7b, 0x0a, 0x16, + 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, + 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x6f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x1a, 0x47, 0x0a, 0x19, 0x4f, 0x70, 0x65, @@ -2036,66 +2395,33 @@ var file_resource_aggregate_pb_events_proto_rawDesc = []byte{ 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0xb0, 0x04, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, - 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x64, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, - 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, - 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, - 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, - 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, - 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x38, 0x01, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0xba, 0x04, 0x0a, 0x1b, 0x44, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0c, 0x74, 0x77, 0x69, 0x6e, 0x5f, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0b, 0x74, + 0x77, 0x69, 0x6e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x3e, 0x0a, 0x1a, 0x74, 0x77, + 0x69, 0x6e, 0x5f, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, + 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, + 0x52, 0x18, 0x74, 0x77, 0x69, 0x6e, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x68, + 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, + 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x4a, 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x12, 0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x65, - 0x74, 0x61, 0x67, 0x12, 0x77, 0x0a, 0x16, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x74, 0x65, 0x6c, 0x65, - 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x18, 0x64, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, - 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x64, 0x2e, 0x4f, 0x70, 0x65, - 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, - 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x6f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, - 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x1a, 0x47, 0x0a, 0x19, - 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, - 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x85, 0x04, 0x0a, 0x15, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, - 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x49, 0x64, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, - 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, - 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x4a, 0x0a, 0x0e, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, - 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, 0x64, - 0x5f, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x7b, 0x0a, 0x16, 0x6f, 0x70, 0x65, 0x6e, 0x5f, - 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, - 0x72, 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x6e, + 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, + 0x12, 0x81, 0x01, 0x0a, 0x16, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, + 0x74, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x18, 0x64, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x4b, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x6f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, @@ -2103,221 +2429,66 @@ var file_resource_aggregate_pb_events_proto_rawDesc = []byte{ 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x98, 0x04, - 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x64, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, - 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, - 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, - 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x4a, 0x0a, 0x0e, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x75, 0x0a, 0x16, 0x6f, 0x70, 0x65, 0x6e, - 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x72, 0x72, 0x69, - 0x65, 0x72, 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, - 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x2e, - 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, - 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x6f, 0x70, 0x65, 0x6e, 0x54, - 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x1a, - 0x47, 0x0a, 0x19, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, - 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8f, 0x04, 0x0a, 0x15, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x47, - 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, - 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, - 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x4a, 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, - 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x75, 0x6e, 0x74, - 0x69, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, - 0x6e, 0x74, 0x69, 0x6c, 0x12, 0x7b, 0x0a, 0x16, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x74, 0x65, 0x6c, - 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x18, 0x64, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, - 0x67, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, - 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x6f, 0x70, 0x65, - 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, - 0x72, 0x1a, 0x47, 0x0a, 0x19, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, - 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x98, 0x04, 0x0a, 0x0f, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x41, - 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, - 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, - 0x64, 0x12, 0x34, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x1c, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, - 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, - 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, - 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, - 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x4a, 0x0a, 0x0e, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, - 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x75, 0x0a, 0x16, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x74, 0x65, - 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x18, - 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x4f, 0x70, 0x65, - 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, - 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x6f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, - 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x1a, 0x47, 0x0a, 0x19, - 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, - 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf3, 0x05, 0x0a, 0x1a, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x54, - 0x61, 0x6b, 0x65, 0x6e, 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, - 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x16, 0x6c, 0x61, 0x74, 0x65, 0x73, - 0x74, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x52, 0x14, - 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x12, 0x65, 0x0a, 0x18, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x52, 0x16, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x6b, 0x0a, 0x1a, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, - 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, - 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, - 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x18, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, - 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x65, 0x0a, 0x18, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, - 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x16, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x12, - 0x65, 0x0a, 0x18, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x64, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x2b, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, - 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x16, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x65, - 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, - 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, - 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, - 0x4a, 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0xf4, 0x04, 0x0a, 0x15, - 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x10, 0x0a, + 0x0e, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4a, + 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0xc7, 0x02, 0x0a, 0x1b, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, + 0x54, 0x61, 0x6b, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x49, 0x64, 0x12, 0x40, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x5c, 0x0a, 0x14, 0x74, 0x77, 0x69, 0x6e, 0x5f, 0x73, 0x79, 0x6e, - 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, - 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x77, 0x69, 0x6e, 0x53, 0x79, - 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x74, - 0x77, 0x69, 0x6e, 0x53, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x77, 0x69, 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x74, 0x77, 0x69, 0x6e, 0x45, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, - 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, - 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, - 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x4a, - 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, - 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x63, 0x61, - 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x12, 0x7b, 0x0a, 0x16, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x74, - 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, - 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, - 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x64, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, - 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x6f, - 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, - 0x69, 0x65, 0x72, 0x1a, 0x47, 0x0a, 0x19, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, - 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x4a, 0x04, 0x08, 0x03, - 0x10, 0x04, 0x22, 0xba, 0x04, 0x0a, 0x1b, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, - 0x23, 0x0a, 0x0c, 0x74, 0x77, 0x69, 0x6e, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0b, 0x74, 0x77, 0x69, 0x6e, 0x45, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x64, 0x12, 0x3e, 0x0a, 0x1a, 0x74, 0x77, 0x69, 0x6e, 0x5f, 0x66, 0x6f, 0x72, - 0x63, 0x65, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x18, 0x74, 0x77, 0x69, 0x6e, - 0x46, 0x6f, 0x72, 0x63, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, + 0x49, 0x64, 0x12, 0x63, 0x0a, 0x17, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, + 0x52, 0x15, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x5a, 0x0a, 0x0f, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x31, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, + 0x69, 0x6e, 0x67, 0x52, 0x0e, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x73, 0x12, 0x4a, 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, - 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, - 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x4a, 0x0a, - 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x5f, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x12, 0x81, 0x01, 0x0a, 0x16, 0x6f, - 0x70, 0x65, 0x6e, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x61, - 0x72, 0x72, 0x69, 0x65, 0x72, 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4b, 0x2e, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, - 0x70, 0x62, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x2e, 0x4f, + 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, + 0xf6, 0x01, 0x0a, 0x11, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x48, 0x65, 0x61, 0x72, + 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x47, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x73, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x2e, 0x48, 0x65, + 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x4b, + 0x0a, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x31, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x48, + 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, + 0x61, 0x74, 0x52, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x1a, 0x4b, 0x0a, 0x09, 0x48, + 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x5f, 0x75, 0x6e, 0x74, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x74, 0x69, 0x6c, 0x22, 0xcc, 0x03, 0x0a, 0x16, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x12, 0x56, 0x0a, 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x5f, + 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x27, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x48, + 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x11, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x73, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x4a, 0x0a, 0x0e, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, + 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, + 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, + 0x12, 0x7c, 0x0a, 0x16, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, + 0x72, 0x79, 0x5f, 0x63, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x46, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x6f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x1a, 0x47, @@ -2325,91 +2496,25 @@ var file_resource_aggregate_pb_events_proto_rawDesc = []byte{ 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x10, 0x0a, 0x0e, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x5f, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, - 0xc7, 0x02, 0x0a, 0x1b, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x54, 0x61, 0x6b, 0x65, 0x6e, 0x12, - 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x63, 0x0a, 0x17, - 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, - 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x52, 0x15, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x64, 0x12, 0x5a, 0x0a, 0x0f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x72, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, - 0x62, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x0e, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4a, 0x0a, - 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0xf6, 0x01, 0x0a, 0x11, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, - 0x47, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, - 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, - 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x48, 0x65, - 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, - 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x4b, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x69, - 0x72, 0x65, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, - 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, - 0x61, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x07, 0x65, 0x78, - 0x70, 0x69, 0x72, 0x65, 0x64, 0x1a, 0x4b, 0x0a, 0x09, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, - 0x61, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, - 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x75, 0x6e, 0x74, 0x69, 0x6c, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x74, - 0x69, 0x6c, 0x22, 0xcc, 0x03, 0x0a, 0x16, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x56, 0x0a, - 0x12, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x5f, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, - 0x65, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x72, 0x65, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, - 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, - 0x61, 0x74, 0x52, 0x11, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x48, 0x65, 0x61, 0x72, - 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x4a, 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, - 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, - 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x12, 0x47, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, - 0x41, 0x75, 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0c, 0x61, 0x75, - 0x64, 0x69, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x7c, 0x0a, 0x16, 0x6f, 0x70, - 0x65, 0x6e, 0x5f, 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x61, 0x72, - 0x72, 0x69, 0x65, 0x72, 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x46, 0x2e, 0x72, 0x65, 0x73, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd2, 0x01, 0x0a, 0x1c, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x53, 0x6e, 0x61, 0x70, 0x73, + 0x68, 0x6f, 0x74, 0x54, 0x61, 0x6b, 0x65, 0x6e, 0x12, 0x66, 0x0a, 0x18, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, - 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x14, 0x6f, 0x70, 0x65, 0x6e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, - 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, 0x1a, 0x47, 0x0a, 0x19, 0x4f, 0x70, 0x65, 0x6e, - 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x43, 0x61, 0x72, 0x72, 0x69, 0x65, 0x72, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0xd2, 0x01, 0x0a, 0x1c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x54, 0x61, 0x6b, - 0x65, 0x6e, 0x12, 0x66, 0x0a, 0x18, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x6d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x64, 0x52, 0x16, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x4a, 0x0a, 0x0e, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, - 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6c, 0x67, 0x64, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x68, 0x75, - 0x62, 0x2f, 0x76, 0x32, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2d, 0x61, 0x67, - 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x3b, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x52, 0x16, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, + 0x12, 0x4a, 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x3d, 0x5a, 0x3b, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6c, 0x67, 0x64, 0x2d, + 0x64, 0x65, 0x76, 0x2f, 0x68, 0x75, 0x62, 0x2f, 0x76, 0x32, 0x2f, 0x72, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x2d, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2f, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x73, 0x3b, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( diff --git a/resource-aggregate/events/resourceChanged.go b/resource-aggregate/events/resourceChanged.go index f4c694d12..91cb5d649 100644 --- a/resource-aggregate/events/resourceChanged.go +++ b/resource-aggregate/events/resourceChanged.go @@ -7,6 +7,7 @@ import ( pkgTime "github.com/plgd-dev/hub/v2/pkg/time" commands "github.com/plgd-dev/hub/v2/resource-aggregate/commands" "github.com/plgd-dev/hub/v2/resource-aggregate/cqrs/eventstore" + "golang.org/x/exp/slices" "google.golang.org/protobuf/proto" ) @@ -58,6 +59,10 @@ func (rc *ResourceChanged) ServiceID() (string, bool) { return "", false } +func (rc *ResourceChanged) Types() []string { + return rc.GetResourceTypes() +} + func (rc *ResourceChanged) CopyData(event *ResourceChanged) { rc.ResourceId = event.GetResourceId() rc.Content = event.GetContent() @@ -66,6 +71,7 @@ func (rc *ResourceChanged) CopyData(event *ResourceChanged) { rc.Status = event.GetStatus() rc.OpenTelemetryCarrier = event.GetOpenTelemetryCarrier() rc.Etag = event.GetEtag() + rc.ResourceTypes = event.GetResourceTypes() } func (rc *ResourceChanged) CheckInitialized() bool { @@ -90,6 +96,9 @@ func (rc *ResourceChanged) Equal(changed *ResourceChanged) bool { if rc.GetAuditContext().GetUserId() != changed.GetAuditContext().GetUserId() { return false } + if !slices.Equal(rc.GetResourceTypes(), changed.GetResourceTypes()) { + return false + } return true } diff --git a/resource-aggregate/events/resourceChanged_test.go b/resource-aggregate/events/resourceChanged_test.go index 500204d94..bda33bf20 100644 --- a/resource-aggregate/events/resourceChanged_test.go +++ b/resource-aggregate/events/resourceChanged_test.go @@ -30,10 +30,11 @@ var testEventResourceChanged events.ResourceChanged = events.ResourceChanged{ ConnectionId: "con1", Sequence: 1, }, - Status: commands.Status_ACCEPTED, + Status: commands.Status_ACCEPTED, + ResourceTypes: []string{"type1", "type2"}, } -func TestResourceChanged_CopyData(t *testing.T) { +func TestResourceChangedCopyData(t *testing.T) { type args struct { event *events.ResourceChanged } diff --git a/resource-aggregate/events/resourceCreatePending.go b/resource-aggregate/events/resourceCreatePending.go index 3934a7c01..87c7d43a4 100644 --- a/resource-aggregate/events/resourceCreatePending.go +++ b/resource-aggregate/events/resourceCreatePending.go @@ -50,6 +50,10 @@ func (e *ResourceCreatePending) ServiceID() (string, bool) { return "", false } +func (e *ResourceCreatePending) Types() []string { + return e.GetResourceTypes() +} + func (e *ResourceCreatePending) CopyData(event *ResourceCreatePending) { e.ResourceId = event.GetResourceId() e.Content = event.GetContent() @@ -57,6 +61,7 @@ func (e *ResourceCreatePending) CopyData(event *ResourceCreatePending) { e.EventMetadata = event.GetEventMetadata() e.ValidUntil = event.GetValidUntil() e.OpenTelemetryCarrier = event.GetOpenTelemetryCarrier() + e.ResourceTypes = event.GetResourceTypes() } func (e *ResourceCreatePending) CheckInitialized() bool { diff --git a/resource-aggregate/events/resourceCreatePending_test.go b/resource-aggregate/events/resourceCreatePending_test.go index a2a45790f..5858142d6 100644 --- a/resource-aggregate/events/resourceCreatePending_test.go +++ b/resource-aggregate/events/resourceCreatePending_test.go @@ -30,6 +30,7 @@ var testEventResourceCreatePending events.ResourceCreatePending = events.Resourc ConnectionId: "con1", Sequence: 1, }, + ResourceTypes: []string{"type1", "type2"}, } func TestResourceCreatePending_CopyData(t *testing.T) { diff --git a/resource-aggregate/events/resourceCreated.go b/resource-aggregate/events/resourceCreated.go index c826136ea..1bc4cb380 100644 --- a/resource-aggregate/events/resourceCreated.go +++ b/resource-aggregate/events/resourceCreated.go @@ -51,6 +51,10 @@ func (e *ResourceCreated) ServiceID() (string, bool) { return "", false } +func (e *ResourceCreated) Types() []string { + return e.GetResourceTypes() +} + func (e *ResourceCreated) CopyData(event *ResourceCreated) { e.ResourceId = event.GetResourceId() e.Status = event.GetStatus() @@ -58,6 +62,7 @@ func (e *ResourceCreated) CopyData(event *ResourceCreated) { e.AuditContext = event.GetAuditContext() e.EventMetadata = event.GetEventMetadata() e.OpenTelemetryCarrier = event.GetOpenTelemetryCarrier() + e.ResourceTypes = event.GetResourceTypes() } func (e *ResourceCreated) CheckInitialized() bool { diff --git a/resource-aggregate/events/resourceCreated_test.go b/resource-aggregate/events/resourceCreated_test.go index 93efddb2a..e2983d14d 100644 --- a/resource-aggregate/events/resourceCreated_test.go +++ b/resource-aggregate/events/resourceCreated_test.go @@ -31,9 +31,10 @@ var testEventResourceCreated events.ResourceCreated = events.ResourceCreated{ ConnectionId: "con1", Sequence: 1, }, + ResourceTypes: []string{"type1", "type2"}, } -func TestResourceCreated_CopyData(t *testing.T) { +func TestResourceCreatedCopyData(t *testing.T) { type args struct { event *events.ResourceCreated } diff --git a/resource-aggregate/events/resourceDeletePending.go b/resource-aggregate/events/resourceDeletePending.go index ef12ee02f..477fee130 100644 --- a/resource-aggregate/events/resourceDeletePending.go +++ b/resource-aggregate/events/resourceDeletePending.go @@ -50,6 +50,10 @@ func (e *ResourceDeletePending) ServiceID() (string, bool) { return "", false } +func (e *ResourceDeletePending) Types() []string { + return e.GetResourceTypes() +} + func (e *ResourceDeletePending) CopyData(event *ResourceDeletePending) { e.ResourceId = event.GetResourceId() e.AuditContext = event.GetAuditContext() @@ -57,6 +61,7 @@ func (e *ResourceDeletePending) CopyData(event *ResourceDeletePending) { e.ValidUntil = event.GetValidUntil() e.ResourceInterface = event.GetResourceInterface() e.OpenTelemetryCarrier = event.GetOpenTelemetryCarrier() + e.ResourceTypes = event.GetResourceTypes() } func (e *ResourceDeletePending) CheckInitialized() bool { diff --git a/resource-aggregate/events/resourceDeletePending_test.go b/resource-aggregate/events/resourceDeletePending_test.go index 1a629c707..a6964408b 100644 --- a/resource-aggregate/events/resourceDeletePending_test.go +++ b/resource-aggregate/events/resourceDeletePending_test.go @@ -26,9 +26,10 @@ var testEventResourceDeletePending events.ResourceDeletePending = events.Resourc Sequence: 1, }, ResourceInterface: interfaces.OC_IF_BASELINE, + ResourceTypes: []string{"type1", "type2"}, } -func TestResourceDeletePending_CopyData(t *testing.T) { +func TestResourceDeletePendingCopyData(t *testing.T) { type args struct { event *events.ResourceDeletePending } diff --git a/resource-aggregate/events/resourceDeleted.go b/resource-aggregate/events/resourceDeleted.go index feb9047e0..1f4ab2d3b 100644 --- a/resource-aggregate/events/resourceDeleted.go +++ b/resource-aggregate/events/resourceDeleted.go @@ -51,6 +51,10 @@ func (e *ResourceDeleted) ServiceID() (string, bool) { return "", false } +func (e *ResourceDeleted) Types() []string { + return e.GetResourceTypes() +} + func (e *ResourceDeleted) CopyData(event *ResourceDeleted) { e.ResourceId = event.GetResourceId() e.Status = event.GetStatus() @@ -58,6 +62,7 @@ func (e *ResourceDeleted) CopyData(event *ResourceDeleted) { e.AuditContext = event.GetAuditContext() e.EventMetadata = event.GetEventMetadata() e.OpenTelemetryCarrier = event.GetOpenTelemetryCarrier() + e.ResourceTypes = event.GetResourceTypes() } func (e *ResourceDeleted) CheckInitialized() bool { diff --git a/resource-aggregate/events/resourceDeleted_test.go b/resource-aggregate/events/resourceDeleted_test.go index 89fe66bb0..c34b44244 100644 --- a/resource-aggregate/events/resourceDeleted_test.go +++ b/resource-aggregate/events/resourceDeleted_test.go @@ -31,6 +31,7 @@ var testEventResourceDeleted events.ResourceDeleted = events.ResourceDeleted{ ConnectionId: "con1", Sequence: 1, }, + ResourceTypes: []string{"type1", "type2"}, } func TestResourceDeleted_CopyData(t *testing.T) { diff --git a/resource-aggregate/events/resourceLinksPublished.go b/resource-aggregate/events/resourceLinksPublished.go index 3c361dfc8..e3e32426e 100644 --- a/resource-aggregate/events/resourceLinksPublished.go +++ b/resource-aggregate/events/resourceLinksPublished.go @@ -51,6 +51,10 @@ func (e *ResourceLinksPublished) ServiceID() (string, bool) { return "", false } +func (e *ResourceLinksPublished) Types() []string { + return nil +} + func (e *ResourceLinksPublished) CopyData(event *ResourceLinksPublished) { e.Resources = event.GetResources() e.DeviceId = event.GetDeviceId() diff --git a/resource-aggregate/events/resourceLinksSnapshotTaken.go b/resource-aggregate/events/resourceLinksSnapshotTaken.go index a4a46c59b..654b1bdec 100644 --- a/resource-aggregate/events/resourceLinksSnapshotTaken.go +++ b/resource-aggregate/events/resourceLinksSnapshotTaken.go @@ -58,6 +58,10 @@ func (e *ResourceLinksSnapshotTaken) ServiceID() (string, bool) { return "", false } +func (e *ResourceLinksSnapshotTaken) Types() []string { + return nil +} + func (e *ResourceLinksSnapshotTaken) CloneData(event *ResourceLinksSnapshotTaken) { e.DeviceId = event.GetDeviceId() e.Resources = commands.CloneResourcesMap(event.GetResources()) @@ -89,7 +93,7 @@ func (e *ResourceLinksSnapshotTaken) GetNewPublishedLinks(pub *ResourceLinksPubl published := make([]*commands.Resource, 0, len(pub.GetResources())) for _, resPub := range pub.GetResources() { - resSnap, ok := e.Resources[resPub.Href] + resSnap, ok := e.GetResources()[resPub.GetHref()] if !ok || !EqualResource(resPub, resSnap) { published = append(published, resPub) } @@ -226,7 +230,7 @@ func (e *ResourceLinksSnapshotTakenForCommand) HandleCommand(ctx context.Context if newVersion == 0 { return nil, status.Errorf(codes.NotFound, errInvalidVersion) } - if req.CommandMetadata == nil { + if req.GetCommandMetadata() == nil { return nil, status.Errorf(codes.InvalidArgument, errInvalidCommandMetadata) } diff --git a/resource-aggregate/events/resourceLinksSnapshotTaken_test.go b/resource-aggregate/events/resourceLinksSnapshotTaken_test.go index 749eb4353..e049d4ad7 100644 --- a/resource-aggregate/events/resourceLinksSnapshotTaken_test.go +++ b/resource-aggregate/events/resourceLinksSnapshotTaken_test.go @@ -30,8 +30,8 @@ func TestResourceLinksSnapshotTakenGetNewPublishedLinks(t *testing.T) { } res1Upd := commands.Resource{ - Href: res.Href, - DeviceId: res.Href + "-upd", + Href: res.GetHref(), + DeviceId: res.GetHref() + "-upd", } tests := []struct { @@ -66,7 +66,7 @@ func TestResourceLinksSnapshotTakenGetNewPublishedLinks(t *testing.T) { name: "Identical", fields: fields{ Resources: map[string]*commands.Resource{ - res.Href: &res, + res.GetHref(): &res, }, }, args: args{ @@ -92,7 +92,7 @@ func TestResourceLinksSnapshotTakenGetNewPublishedLinks(t *testing.T) { name: "New published resource (2)", fields: fields{ Resources: map[string]*commands.Resource{ - res.Href: &res, + res.GetHref(): &res, }, }, args: args{ @@ -106,8 +106,8 @@ func TestResourceLinksSnapshotTakenGetNewPublishedLinks(t *testing.T) { name: "Updated resource", fields: fields{ Resources: map[string]*commands.Resource{ - res.Href: &res, - resHref.Href: &resHref, + res.GetHref(): &res, + resHref.GetHref(): &resHref, }, }, args: args{ @@ -369,7 +369,7 @@ func TestResourceLinksSnapshotTakenHandleEventResourceLinksUnpublished(t *testin Hrefs: tt.args.hrefs, }) if len(tt.want) == 0 { - require.Len(t, got, 0) + require.Empty(t, got) return } sort.Strings(tt.want) diff --git a/resource-aggregate/events/resourceLinksUnpublished.go b/resource-aggregate/events/resourceLinksUnpublished.go index 52e4a0379..2ca78c775 100644 --- a/resource-aggregate/events/resourceLinksUnpublished.go +++ b/resource-aggregate/events/resourceLinksUnpublished.go @@ -51,6 +51,10 @@ func (e *ResourceLinksUnpublished) ServiceID() (string, bool) { return "", false } +func (e *ResourceLinksUnpublished) Types() []string { + return nil +} + func (e *ResourceLinksUnpublished) CopyData(event *ResourceLinksUnpublished) { e.Hrefs = event.GetHrefs() e.DeviceId = event.GetDeviceId() diff --git a/resource-aggregate/events/resourceRetrievePending.go b/resource-aggregate/events/resourceRetrievePending.go index 73e5ccd87..fc2821bcf 100644 --- a/resource-aggregate/events/resourceRetrievePending.go +++ b/resource-aggregate/events/resourceRetrievePending.go @@ -50,6 +50,10 @@ func (e *ResourceRetrievePending) ServiceID() (string, bool) { return "", false } +func (e *ResourceRetrievePending) Types() []string { + return e.GetResourceTypes() +} + func (e *ResourceRetrievePending) CopyData(event *ResourceRetrievePending) { e.ResourceId = event.GetResourceId() e.ResourceInterface = event.GetResourceInterface() @@ -57,6 +61,7 @@ func (e *ResourceRetrievePending) CopyData(event *ResourceRetrievePending) { e.EventMetadata = event.GetEventMetadata() e.ValidUntil = event.GetValidUntil() e.OpenTelemetryCarrier = event.GetOpenTelemetryCarrier() + e.ResourceTypes = event.GetResourceTypes() } func (e *ResourceRetrievePending) CheckInitialized() bool { diff --git a/resource-aggregate/events/resourceRetrievePending_test.go b/resource-aggregate/events/resourceRetrievePending_test.go index 0c9b56dcc..356519e09 100644 --- a/resource-aggregate/events/resourceRetrievePending_test.go +++ b/resource-aggregate/events/resourceRetrievePending_test.go @@ -25,9 +25,10 @@ var testEventResourceRetrievePending events.ResourceRetrievePending = events.Res ConnectionId: "con1", Sequence: 1, }, + ResourceTypes: []string{"type1", "type2"}, } -func TestResourceRetrievePending_CopyData(t *testing.T) { +func TestResourceRetrievePendingCopyData(t *testing.T) { type args struct { event *events.ResourceRetrievePending } diff --git a/resource-aggregate/events/resourceRetrieved.go b/resource-aggregate/events/resourceRetrieved.go index e1b539df9..f84e4639c 100644 --- a/resource-aggregate/events/resourceRetrieved.go +++ b/resource-aggregate/events/resourceRetrieved.go @@ -51,6 +51,10 @@ func (e *ResourceRetrieved) ServiceID() (string, bool) { return "", false } +func (e *ResourceRetrieved) Types() []string { + return e.GetResourceTypes() +} + func (e *ResourceRetrieved) CopyData(event *ResourceRetrieved) { e.ResourceId = event.GetResourceId() e.Content = event.GetContent() @@ -58,6 +62,7 @@ func (e *ResourceRetrieved) CopyData(event *ResourceRetrieved) { e.EventMetadata = event.GetEventMetadata() e.Status = event.GetStatus() e.OpenTelemetryCarrier = event.GetOpenTelemetryCarrier() + e.ResourceTypes = event.GetResourceTypes() } func (e *ResourceRetrieved) CheckInitialized() bool { diff --git a/resource-aggregate/events/resourceRetrieved_test.go b/resource-aggregate/events/resourceRetrieved_test.go index b86d62d79..2d175d992 100644 --- a/resource-aggregate/events/resourceRetrieved_test.go +++ b/resource-aggregate/events/resourceRetrieved_test.go @@ -30,10 +30,11 @@ var testEventResourceRetrieved events.ResourceRetrieved = events.ResourceRetriev ConnectionId: "con1", Sequence: 1, }, - Status: commands.Status_ACCEPTED, + Status: commands.Status_ACCEPTED, + ResourceTypes: []string{"type1", "type2"}, } -func TestResourceRetrieved_CopyData(t *testing.T) { +func TestResourceRetrievedCopyData(t *testing.T) { type args struct { event *events.ResourceRetrieved } diff --git a/resource-aggregate/events/resourceStateSnapshotTaken.go b/resource-aggregate/events/resourceStateSnapshotTaken.go index 14996d6a1..1fb060e94 100644 --- a/resource-aggregate/events/resourceStateSnapshotTaken.go +++ b/resource-aggregate/events/resourceStateSnapshotTaken.go @@ -66,6 +66,10 @@ func (e *ResourceStateSnapshotTaken) ServiceID() (string, bool) { return "", false } +func (e *ResourceStateSnapshotTaken) Types() []string { + return e.GetResourceTypes() +} + func (e *ResourceStateSnapshotTaken) CopyData(event *ResourceStateSnapshotTaken) { e.ResourceId = event.GetResourceId() e.LatestResourceChange = event.GetLatestResourceChange() @@ -75,6 +79,7 @@ func (e *ResourceStateSnapshotTaken) CopyData(event *ResourceStateSnapshotTaken) e.ResourceDeletePendings = event.GetResourceDeletePendings() e.AuditContext = event.GetAuditContext() e.EventMetadata = event.GetEventMetadata() + e.ResourceTypes = event.GetResourceTypes() } func (e *ResourceStateSnapshotTaken) CheckInitialized() bool { @@ -155,9 +160,17 @@ func (e *ResourceStateSnapshotTaken) handleEventResourceCreatePending(createPend e.EventMetadata = createPending.GetEventMetadata() e.ResourceCreatePendings = append(e.ResourceCreatePendings, createPending) e.AuditContext = createPending.GetAuditContext() + e.setResourceTypes(createPending.GetResourceTypes()) return nil } +func (e *ResourceStateSnapshotTaken) setResourceTypes(resourceTypes []string) { + if len(resourceTypes) == 0 { + return + } + e.ResourceTypes = resourceTypes +} + func (e *ResourceStateSnapshotTaken) handleEventResourceUpdatePending(updatePending *ResourceUpdatePending) error { now := time.Now() if ok := e.processValidUntil(updatePending, now); !ok { @@ -171,6 +184,7 @@ func (e *ResourceStateSnapshotTaken) handleEventResourceUpdatePending(updatePend e.EventMetadata = updatePending.GetEventMetadata() e.ResourceUpdatePendings = append(e.ResourceUpdatePendings, updatePending) e.AuditContext = updatePending.GetAuditContext() + e.setResourceTypes(updatePending.GetResourceTypes()) return nil } @@ -187,6 +201,7 @@ func (e *ResourceStateSnapshotTaken) handleEventResourceRetrievePending(retrieve e.EventMetadata = retrievePending.GetEventMetadata() e.ResourceRetrievePendings = append(e.ResourceRetrievePendings, retrievePending) e.AuditContext = retrievePending.GetAuditContext() + e.setResourceTypes(retrievePending.GetResourceTypes()) return nil } @@ -203,6 +218,7 @@ func (e *ResourceStateSnapshotTaken) handleEventResourceDeletePending(deletePend e.EventMetadata = deletePending.GetEventMetadata() e.ResourceDeletePendings = append(e.ResourceDeletePendings, deletePending) e.AuditContext = deletePending.GetAuditContext() + e.setResourceTypes(deletePending.GetResourceTypes()) return nil } @@ -212,7 +228,8 @@ func RemoveIndex(s []int, index int) []int { func (e *ResourceStateSnapshotTaken) handleEventResourceCreated(created *ResourceCreated) error { index := -1 - for i, event := range e.GetResourceCreatePendings() { + resourceCreatePendings := e.GetResourceCreatePendings() + for i, event := range resourceCreatePendings { if event.GetAuditContext().GetCorrelationId() == created.GetAuditContext().GetCorrelationId() { index = i break @@ -223,14 +240,17 @@ func (e *ResourceStateSnapshotTaken) handleEventResourceCreated(created *Resourc } e.ResourceId = created.GetResourceId() e.EventMetadata = created.GetEventMetadata() - e.ResourceCreatePendings = append(e.ResourceCreatePendings[:index], e.ResourceCreatePendings[index+1:]...) + resourceCreatePendings = append(resourceCreatePendings[:index], resourceCreatePendings[index+1:]...) + e.ResourceCreatePendings = resourceCreatePendings e.AuditContext = created.GetAuditContext() + e.setResourceTypes(created.GetResourceTypes()) return nil } func (e *ResourceStateSnapshotTaken) handleEventResourceUpdated(updated *ResourceUpdated) error { index := -1 - for i, event := range e.GetResourceUpdatePendings() { + resourceUpdatePendings := e.GetResourceUpdatePendings() + for i, event := range resourceUpdatePendings { if event.GetAuditContext().GetCorrelationId() == updated.GetAuditContext().GetCorrelationId() { index = i break @@ -241,14 +261,17 @@ func (e *ResourceStateSnapshotTaken) handleEventResourceUpdated(updated *Resourc } e.ResourceId = updated.GetResourceId() e.EventMetadata = updated.GetEventMetadata() - e.ResourceUpdatePendings = append(e.ResourceUpdatePendings[:index], e.ResourceUpdatePendings[index+1:]...) + resourceUpdatePendings = append(resourceUpdatePendings[:index], resourceUpdatePendings[index+1:]...) + e.ResourceUpdatePendings = resourceUpdatePendings e.AuditContext = updated.GetAuditContext() + e.setResourceTypes(updated.GetResourceTypes()) return nil } func (e *ResourceStateSnapshotTaken) handleEventResourceRetrieved(retrieved *ResourceRetrieved) error { index := -1 - for i, event := range e.GetResourceRetrievePendings() { + resourceRetrievePendings := e.GetResourceRetrievePendings() + for i, event := range resourceRetrievePendings { if event.GetAuditContext().GetCorrelationId() == retrieved.GetAuditContext().GetCorrelationId() { index = i break @@ -259,8 +282,10 @@ func (e *ResourceStateSnapshotTaken) handleEventResourceRetrieved(retrieved *Res } e.ResourceId = retrieved.GetResourceId() e.EventMetadata = retrieved.GetEventMetadata() - e.ResourceRetrievePendings = append(e.ResourceRetrievePendings[:index], e.ResourceRetrievePendings[index+1:]...) + resourceRetrievePendings = append(resourceRetrievePendings[:index], resourceRetrievePendings[index+1:]...) + e.ResourceRetrievePendings = resourceRetrievePendings e.AuditContext = retrieved.GetAuditContext() + e.setResourceTypes(retrieved.GetResourceTypes()) return nil } @@ -281,6 +306,7 @@ func (e *ResourceStateSnapshotTaken) handleEventResourceChanged(changed *Resourc e.EventMetadata = changed.GetEventMetadata() e.LatestResourceChange = changed e.AuditContext = changed.GetAuditContext() + e.setResourceTypes(changed.GetResourceTypes()) return true } return false @@ -294,7 +320,8 @@ func (e *ResourceStateSnapshotTaken) handleEventResourceDeleted(deleted *Resourc e.ResourceUpdatePendings = nil } else { index := -1 - for i, event := range e.GetResourceDeletePendings() { + resourceDeletePendings := e.GetResourceDeletePendings() + for i, event := range resourceDeletePendings { if event.GetAuditContext().GetCorrelationId() == deleted.GetAuditContext().GetCorrelationId() { index = i break @@ -303,11 +330,13 @@ func (e *ResourceStateSnapshotTaken) handleEventResourceDeleted(deleted *Resourc if index < 0 { return status.Errorf(codes.InvalidArgument, "cannot find resource delete pending event with correlationId('%v')", deleted.GetAuditContext().GetCorrelationId()) } - e.ResourceDeletePendings = append(e.ResourceDeletePendings[:index], e.ResourceDeletePendings[index+1:]...) + resourceDeletePendings = append(resourceDeletePendings[:index], resourceDeletePendings[index+1:]...) + e.ResourceDeletePendings = resourceDeletePendings } e.ResourceId = deleted.GetResourceId() e.EventMetadata = deleted.GetEventMetadata() e.AuditContext = deleted.GetAuditContext() + e.setResourceTypes(deleted.GetResourceTypes()) return nil } @@ -315,76 +344,84 @@ func (e *ResourceStateSnapshotTaken) handleEventResourceStateSnapshotTaken(snaps e.CopyData(snapshot) } +//nolint:gocyclo +func (e *ResourceStateSnapshotTaken) handleByEvent(eu eventstore.EventUnmarshaler) error { + if eu.EventType() == "" { + return status.Errorf(codes.Internal, "cannot determine type of event") + } + switch eu.EventType() { + case (&ResourceStateSnapshotTaken{}).EventType(): + var s ResourceStateSnapshotTaken + if err := eu.Unmarshal(&s); err != nil { + return status.Errorf(codes.Internal, "%v", err) + } + e.handleEventResourceStateSnapshotTaken(&s) + case (&ResourceUpdatePending{}).EventType(): + var s ResourceUpdatePending + if err := eu.Unmarshal(&s); err != nil { + return status.Errorf(codes.Internal, "%v", err) + } + _ = e.handleEventResourceUpdatePending(&s) + case (&ResourceUpdated{}).EventType(): + var s ResourceUpdated + if err := eu.Unmarshal(&s); err != nil { + return status.Errorf(codes.Internal, "%v", err) + } + _ = e.handleEventResourceUpdated(&s) + case (&ResourceCreatePending{}).EventType(): + var s ResourceCreatePending + if err := eu.Unmarshal(&s); err != nil { + return status.Errorf(codes.Internal, "%v", err) + } + _ = e.handleEventResourceCreatePending(&s) + case (&ResourceCreated{}).EventType(): + var s ResourceCreated + if err := eu.Unmarshal(&s); err != nil { + return status.Errorf(codes.Internal, "%v", err) + } + _ = e.handleEventResourceCreated(&s) + case (&ResourceChanged{}).EventType(): + var s ResourceChanged + if err := eu.Unmarshal(&s); err != nil { + return status.Errorf(codes.Internal, "%v", err) + } + _ = e.handleEventResourceChanged(&s) + case (&ResourceDeleted{}).EventType(): + var s ResourceDeleted + if err := eu.Unmarshal(&s); err != nil { + return status.Errorf(codes.Internal, "%v", err) + } + _ = e.handleEventResourceDeleted(&s) + case (&ResourceDeletePending{}).EventType(): + var s ResourceDeletePending + if err := eu.Unmarshal(&s); err != nil { + return status.Errorf(codes.Internal, "%v", err) + } + _ = e.handleEventResourceDeletePending(&s) + case (&ResourceRetrieved{}).EventType(): + var s ResourceRetrieved + if err := eu.Unmarshal(&s); err != nil { + return status.Errorf(codes.Internal, "%v", err) + } + _ = e.handleEventResourceRetrieved(&s) + case (&ResourceRetrievePending{}).EventType(): + var s ResourceRetrievePending + if err := eu.Unmarshal(&s); err != nil { + return status.Errorf(codes.Internal, "%v", err) + } + _ = e.handleEventResourceRetrievePending(&s) + } + return nil +} + func (e *ResourceStateSnapshotTaken) Handle(ctx context.Context, iter eventstore.Iter) error { for { eu, ok := iter.Next(ctx) if !ok { break } - if eu.EventType() == "" { - return status.Errorf(codes.Internal, "cannot determine type of event") - } - switch eu.EventType() { - case (&ResourceStateSnapshotTaken{}).EventType(): - var s ResourceStateSnapshotTaken - if err := eu.Unmarshal(&s); err != nil { - return status.Errorf(codes.Internal, "%v", err) - } - e.handleEventResourceStateSnapshotTaken(&s) - case (&ResourceUpdatePending{}).EventType(): - var s ResourceUpdatePending - if err := eu.Unmarshal(&s); err != nil { - return status.Errorf(codes.Internal, "%v", err) - } - _ = e.handleEventResourceUpdatePending(&s) - case (&ResourceUpdated{}).EventType(): - var s ResourceUpdated - if err := eu.Unmarshal(&s); err != nil { - return status.Errorf(codes.Internal, "%v", err) - } - _ = e.handleEventResourceUpdated(&s) - case (&ResourceCreatePending{}).EventType(): - var s ResourceCreatePending - if err := eu.Unmarshal(&s); err != nil { - return status.Errorf(codes.Internal, "%v", err) - } - _ = e.handleEventResourceCreatePending(&s) - case (&ResourceCreated{}).EventType(): - var s ResourceCreated - if err := eu.Unmarshal(&s); err != nil { - return status.Errorf(codes.Internal, "%v", err) - } - _ = e.handleEventResourceCreated(&s) - case (&ResourceChanged{}).EventType(): - var s ResourceChanged - if err := eu.Unmarshal(&s); err != nil { - return status.Errorf(codes.Internal, "%v", err) - } - _ = e.handleEventResourceChanged(&s) - case (&ResourceDeleted{}).EventType(): - var s ResourceDeleted - if err := eu.Unmarshal(&s); err != nil { - return status.Errorf(codes.Internal, "%v", err) - } - _ = e.handleEventResourceDeleted(&s) - case (&ResourceDeletePending{}).EventType(): - var s ResourceDeletePending - if err := eu.Unmarshal(&s); err != nil { - return status.Errorf(codes.Internal, "%v", err) - } - _ = e.handleEventResourceDeletePending(&s) - case (&ResourceRetrieved{}).EventType(): - var s ResourceRetrieved - if err := eu.Unmarshal(&s); err != nil { - return status.Errorf(codes.Internal, "%v", err) - } - _ = e.handleEventResourceRetrieved(&s) - case (&ResourceRetrievePending{}).EventType(): - var s ResourceRetrievePending - if err := eu.Unmarshal(&s); err != nil { - return status.Errorf(codes.Internal, "%v", err) - } - _ = e.handleEventResourceRetrievePending(&s) + if err := e.handleByEvent(eu); err != nil { + return err } } return iter.Err() @@ -407,15 +444,11 @@ func convertContent(content *commands.Content, supportedContentType string) (new encode = cbor.Encode coapContentFormat = int32(message.AppCBOR) case message.AppOcfCbor.String(): - if encode == nil { - encode = cbor.Encode - coapContentFormat = int32(message.AppOcfCbor) - } + encode = cbor.Encode + coapContentFormat = int32(message.AppOcfCbor) case message.AppJSON.String(): - if encode == nil { - encode = json.Encode - coapContentFormat = int32(message.AppJSON) - } + encode = json.Encode + coapContentFormat = int32(message.AppJSON) } if encode == nil { @@ -450,7 +483,7 @@ func convertContent(content *commands.Content, supportedContentType string) (new } func (e *ResourceStateSnapshotTakenForCommand) confirmResourceUpdateRequest(ctx context.Context, req *commands.ConfirmResourceUpdateRequest, newVersion uint64) ([]eventstore.Event, error) { - if req.CommandMetadata == nil { + if req.GetCommandMetadata() == nil { return nil, status.Errorf(codes.InvalidArgument, errInvalidCommandMetadata) } @@ -463,6 +496,7 @@ func (e *ResourceStateSnapshotTakenForCommand) confirmResourceUpdateRequest(ctx Content: req.GetContent(), Status: req.GetStatus(), OpenTelemetryCarrier: propagation.TraceFromCtx(ctx), + ResourceTypes: e.resolveResourceTypes(req.GetResourceId().GetHref(), nil), } if err := e.handleEventResourceUpdated(&rc); err != nil { return nil, err @@ -485,6 +519,7 @@ func (e *ResourceStateSnapshotTakenForCommand) confirmResourceRetrieveRequest(ct Status: req.GetStatus(), OpenTelemetryCarrier: propagation.TraceFromCtx(ctx), Etag: req.GetEtag(), + ResourceTypes: e.resolveResourceTypes(req.GetResourceId().GetHref(), nil), } if err := e.handleEventResourceRetrieved(&rc); err != nil { return nil, err @@ -506,6 +541,7 @@ func (e *ResourceStateSnapshotTakenForCommand) confirmResourceDeleteRequest(ctx Content: req.GetContent(), Status: req.GetStatus(), OpenTelemetryCarrier: propagation.TraceFromCtx(ctx), + ResourceTypes: e.resolveResourceTypes(req.GetResourceId().GetHref(), nil), } if err := e.handleEventResourceDeleted(&rc); err != nil { return nil, err @@ -527,7 +563,9 @@ func (e *ResourceStateSnapshotTakenForCommand) confirmResourceCreateRequest(ctx Content: req.GetContent(), Status: req.GetStatus(), OpenTelemetryCarrier: propagation.TraceFromCtx(ctx), + ResourceTypes: e.resolveResourceTypes(req.GetResourceId().GetHref(), nil), } + if err := e.handleEventResourceCreated(&rc); err != nil { return nil, err } @@ -633,7 +671,7 @@ func (e *ResourceStateSnapshotTakenForCommand) CancelPendingCommandsRequest(ctx } func (e *ResourceStateSnapshotTakenForCommand) handleNotifyResourceChangedRequest(ctx context.Context, req *commands.NotifyResourceChangedRequest, newVersion uint64) ([]eventstore.Event, error) { - if req.CommandMetadata == nil { + if req.GetCommandMetadata() == nil { return nil, status.Errorf(codes.InvalidArgument, errInvalidCommandMetadata) } @@ -648,6 +686,7 @@ func (e *ResourceStateSnapshotTakenForCommand) handleNotifyResourceChangedReques Status: req.GetStatus(), OpenTelemetryCarrier: propagation.TraceFromCtx(ctx), Etag: req.GetEtag(), + ResourceTypes: e.resolveResourceTypes(req.GetResourceId().GetHref(), req.GetResourceTypes()), } if e.handleEventResourceChanged(&rc) { @@ -676,6 +715,7 @@ func (e *ResourceStateSnapshotTakenForCommand) handleUpdateResourceRequest(ctx c Content: content, ValidUntil: timeToLive2ValidUntil(req.GetTimeToLive()), OpenTelemetryCarrier: propagation.TraceFromCtx(ctx), + ResourceTypes: e.resolveResourceTypes(req.GetResourceId().GetHref(), nil), } if err = e.handleEventResourceUpdatePending(&rc); err != nil { @@ -700,6 +740,7 @@ func (e *ResourceStateSnapshotTakenForCommand) handleRetrieveResourceRequest(ctx ValidUntil: timeToLive2ValidUntil(req.GetTimeToLive()), OpenTelemetryCarrier: propagation.TraceFromCtx(ctx), Etag: req.GetEtag(), + ResourceTypes: e.resolveResourceTypes(req.GetResourceId().GetHref(), nil), } if err := e.handleEventResourceRetrievePending(&rc); err != nil { @@ -723,6 +764,7 @@ func (e *ResourceStateSnapshotTakenForCommand) handleDeleteResourceRequest(ctx c ValidUntil: timeToLive2ValidUntil(req.GetTimeToLive()), ResourceInterface: req.GetResourceInterface(), OpenTelemetryCarrier: propagation.TraceFromCtx(ctx), + ResourceTypes: e.resolveResourceTypes(req.GetResourceId().GetHref(), nil), } if err := e.handleEventResourceDeletePending(&rc); err != nil { @@ -731,6 +773,29 @@ func (e *ResourceStateSnapshotTakenForCommand) handleDeleteResourceRequest(ctx c return []eventstore.Event{&rc}, nil } +func (e *ResourceStateSnapshotTakenForCommand) resolveResourceTypes(href string, resourceTypes []string) []string { + if len(resourceTypes) > 0 { + // resourceTypes from command has higher priority + return resourceTypes + } + if e.resourceLinks == nil { + // if no resourceLinks, return resourceTypes from snapshot + return e.GetResourceTypes() + } + resources := e.resourceLinks.GetResources() + if len(resources) == 0 { + // if no resourceLinks, return resourceTypes from snapshot + return e.GetResourceTypes() + } + link, ok := e.resourceLinks.GetResources()[href] + if !ok { + // if resourceLinks doesn't contain resource, return resourceTypes from snapshot + return e.GetResourceTypes() + } + // return resourceTypes from link + return link.GetResourceTypes() +} + func (e *ResourceStateSnapshotTakenForCommand) handleCreateResourceRequest(ctx context.Context, req *commands.CreateResourceRequest, newVersion uint64) ([]eventstore.Event, error) { if req.GetCommandMetadata() == nil { return nil, status.Errorf(codes.InvalidArgument, errInvalidCommandMetadata) @@ -749,6 +814,7 @@ func (e *ResourceStateSnapshotTakenForCommand) handleCreateResourceRequest(ctx c EventMetadata: em, ValidUntil: timeToLive2ValidUntil(req.GetTimeToLive()), OpenTelemetryCarrier: propagation.TraceFromCtx(ctx), + ResourceTypes: e.resolveResourceTypes(req.GetResourceId().GetHref(), nil), } if err := e.handleEventResourceCreatePending(&rc); err != nil { @@ -799,22 +865,25 @@ func (e *ResourceStateSnapshotTaken) TakeSnapshot(version uint64) (eventstore.Ev ResourceRetrievePendings: e.GetResourceRetrievePendings(), ResourceDeletePendings: e.GetResourceDeletePendings(), AuditContext: e.GetAuditContext(), + ResourceTypes: e.GetResourceTypes(), }, true } type ResourceStateSnapshotTakenForCommand struct { - owner string - hubID string - userID string + owner string + hubID string + userID string + resourceLinks *ResourceLinksSnapshotTakenForCommand *ResourceStateSnapshotTaken } -func NewResourceStateSnapshotTakenForCommand(userID string, owner string, hubID string) *ResourceStateSnapshotTakenForCommand { +func NewResourceStateSnapshotTakenForCommand(userID string, owner string, hubID string, resourceLinks *ResourceLinksSnapshotTakenForCommand) *ResourceStateSnapshotTakenForCommand { return &ResourceStateSnapshotTakenForCommand{ ResourceStateSnapshotTaken: NewResourceStateSnapshotTaken(), userID: userID, owner: owner, hubID: hubID, + resourceLinks: resourceLinks, } } diff --git a/resource-aggregate/events/resourceStateSnapshotTaken_test.go b/resource-aggregate/events/resourceStateSnapshotTaken_test.go index 383d3f343..18af0389c 100644 --- a/resource-aggregate/events/resourceStateSnapshotTaken_test.go +++ b/resource-aggregate/events/resourceStateSnapshotTaken_test.go @@ -39,7 +39,41 @@ func (i *iterator) Err() error { return nil } -func TestResourceStateSnapshotTaken_Handle(t *testing.T) { +func TestResourceStateSnapshotTakenResourceTypes(t *testing.T) { + const ( + href = "/a" + deviceID = "a" + hubID = "hubID" + userID = "userID" + ) + resourceTypes := []string{"type1", "type2"} + + e := events.NewResourceStateSnapshotTaken() + require.Empty(t, e.Types()) + err := e.Handle(context.TODO(), newIterator([]eventstore.EventUnmarshaler{test.MakeResourceChangedEvent(commands.NewResourceID(deviceID, href), &commands.Content{}, events.MakeEventMeta("", 0, 0, hubID), commands.NewAuditContext(userID, "0", userID), resourceTypes)})) + require.NoError(t, err) + require.Equal(t, resourceTypes, e.Types()) + nextEvents := newIterator([]eventstore.EventUnmarshaler{ + test.MakeResourceCreatePending(commands.NewResourceID(deviceID, href), &commands.Content{}, events.MakeEventMeta("", 0, 1, hubID), commands.NewAuditContext(userID, "0", userID), time.Now().Add(-time.Second), resourceTypes), + test.MakeResourceCreated(commands.NewResourceID(deviceID, href), commands.Status_OK, &commands.Content{}, events.MakeEventMeta("", 0, 2, hubID), commands.NewAuditContext(userID, "0", userID), resourceTypes), + test.MakeResourceRetrievePending(commands.NewResourceID(deviceID, href), "", events.MakeEventMeta("", 0, 3, hubID), commands.NewAuditContext(userID, "1", userID), time.Now().Add(-time.Second), resourceTypes), + test.MakeResourceRetrieved(commands.NewResourceID(deviceID, href), commands.Status_OK, &commands.Content{}, events.MakeEventMeta("", 0, 4, hubID), commands.NewAuditContext(userID, "1", userID), resourceTypes), + test.MakeResourceUpdatePending(commands.NewResourceID(deviceID, href), &commands.Content{}, events.MakeEventMeta("", 0, 5, hubID), commands.NewAuditContext(userID, "2", userID), time.Now().Add(-time.Second), resourceTypes), + test.MakeResourceUpdated(commands.NewResourceID(deviceID, href), commands.Status_OK, &commands.Content{}, events.MakeEventMeta("", 0, 6, hubID), commands.NewAuditContext(userID, "2", userID), resourceTypes), + test.MakeResourceDeletePending(commands.NewResourceID(deviceID, href), events.MakeEventMeta("", 0, 7, hubID), commands.NewAuditContext(userID, "3", userID), time.Now().Add(-time.Second), resourceTypes), + test.MakeResourceDeleted(commands.NewResourceID(deviceID, href), commands.Status_OK, &commands.Content{}, events.MakeEventMeta("", 0, 8, hubID), commands.NewAuditContext(userID, "3", userID), resourceTypes), + }) + err = e.Handle(context.TODO(), nextEvents) + require.NoError(t, err) + require.Equal(t, resourceTypes, e.Types()) + resourceTypes = append(resourceTypes, "type3") + err = e.Handle(context.TODO(), newIterator([]eventstore.EventUnmarshaler{test.MakeResourceChangedEvent(commands.NewResourceID(deviceID, href), &commands.Content{}, events.MakeEventMeta("", 1, 9, hubID), commands.NewAuditContext(userID, "0", userID), resourceTypes)})) + require.NoError(t, err) + require.Equal(t, resourceTypes, e.Types()) +} + +func TestResourceStateSnapshotTakenHandle(t *testing.T) { + resourceTypes := []string{"type1", "type2"} type args struct { events *iterator } @@ -52,8 +86,8 @@ func TestResourceStateSnapshotTaken_Handle(t *testing.T) { name: "createPending, created", args: args{ events: newIterator([]eventstore.EventUnmarshaler{ - test.MakeResourceCreatePending(commands.NewResourceID("a", "/a"), &commands.Content{}, events.MakeEventMeta("", 0, 0, "hubID"), commands.NewAuditContext("userID", "0", "userID"), time.Now().Add(-time.Second)), - test.MakeResourceCreated(commands.NewResourceID("a", "/a"), commands.Status_OK, &commands.Content{}, events.MakeEventMeta("", 0, 0, "hubID"), commands.NewAuditContext("userID", "0", "userID")), + test.MakeResourceCreatePending(commands.NewResourceID("a", "/a"), &commands.Content{}, events.MakeEventMeta("", 0, 0, "hubID"), commands.NewAuditContext("userID", "0", "userID"), time.Now().Add(-time.Second), resourceTypes), + test.MakeResourceCreated(commands.NewResourceID("a", "/a"), commands.Status_OK, &commands.Content{}, events.MakeEventMeta("", 0, 0, "hubID"), commands.NewAuditContext("userID", "0", "userID"), resourceTypes), }), }, }, @@ -61,8 +95,8 @@ func TestResourceStateSnapshotTaken_Handle(t *testing.T) { name: "retrievePending, retrieved", args: args{ events: newIterator([]eventstore.EventUnmarshaler{ - test.MakeResourceRetrievePending(commands.NewResourceID("a", "/a"), "", events.MakeEventMeta("", 0, 0, "hubID"), commands.NewAuditContext("userID", "1", "userID"), time.Now().Add(-time.Second)), - test.MakeResourceRetrieved(commands.NewResourceID("a", "/a"), commands.Status_OK, &commands.Content{}, events.MakeEventMeta("", 0, 0, "hubID"), commands.NewAuditContext("userID", "1", "userID")), + test.MakeResourceRetrievePending(commands.NewResourceID("a", "/a"), "", events.MakeEventMeta("", 0, 0, "hubID"), commands.NewAuditContext("userID", "1", "userID"), time.Now().Add(-time.Second), resourceTypes), + test.MakeResourceRetrieved(commands.NewResourceID("a", "/a"), commands.Status_OK, &commands.Content{}, events.MakeEventMeta("", 0, 0, "hubID"), commands.NewAuditContext("userID", "1", "userID"), resourceTypes), }), }, }, @@ -70,17 +104,17 @@ func TestResourceStateSnapshotTaken_Handle(t *testing.T) { name: "updatePending, updated", args: args{ events: newIterator([]eventstore.EventUnmarshaler{ - test.MakeResourceUpdatePending(commands.NewResourceID("a", "/a"), &commands.Content{}, events.MakeEventMeta("", 0, 0, "hubID"), commands.NewAuditContext("userID", "2", "userID"), time.Now().Add(-time.Second)), - test.MakeResourceUpdated(commands.NewResourceID("a", "/a"), commands.Status_OK, &commands.Content{}, events.MakeEventMeta("", 0, 0, "hubID"), commands.NewAuditContext("userID", "2", "userID")), + test.MakeResourceUpdatePending(commands.NewResourceID("a", "/a"), &commands.Content{}, events.MakeEventMeta("", 0, 0, "hubID"), commands.NewAuditContext("userID", "2", "userID"), time.Now().Add(-time.Second), resourceTypes), + test.MakeResourceUpdated(commands.NewResourceID("a", "/a"), commands.Status_OK, &commands.Content{}, events.MakeEventMeta("", 0, 0, "hubID"), commands.NewAuditContext("userID", "2", "userID"), resourceTypes), }), }, }, { - name: "retrievePending, retrieved", + name: "deletePending, deleted", args: args{ events: newIterator([]eventstore.EventUnmarshaler{ - test.MakeResourceDeletePending(commands.NewResourceID("a", "/a"), events.MakeEventMeta("", 0, 0, "hubID"), commands.NewAuditContext("userID", "3", "userID"), time.Now().Add(-time.Second)), - test.MakeResourceDeleted(commands.NewResourceID("a", "/a"), commands.Status_OK, &commands.Content{}, events.MakeEventMeta("", 0, 0, "hubID"), commands.NewAuditContext("userID", "3", "userID")), + test.MakeResourceDeletePending(commands.NewResourceID("a", "/a"), events.MakeEventMeta("", 0, 0, "hubID"), commands.NewAuditContext("userID", "3", "userID"), time.Now().Add(-time.Second), resourceTypes), + test.MakeResourceDeleted(commands.NewResourceID("a", "/a"), commands.Status_OK, &commands.Content{}, events.MakeEventMeta("", 0, 0, "hubID"), commands.NewAuditContext("userID", "3", "userID"), resourceTypes), }), }, }, @@ -117,21 +151,21 @@ func TestEqual(t *testing.T) { ContentType: "text", CoapContentFormat: int32(message.TextPlain), }, - AuditContext: res.AuditContext, - Status: res.Status, + AuditContext: res.GetAuditContext(), + Status: res.GetStatus(), } resWithChangedAuditContext := events.ResourceChanged{ - Content: res.Content, + Content: res.GetContent(), AuditContext: &commands.AuditContext{ UserId: "502", }, - Status: res.Status, + Status: res.GetStatus(), } resWithChangedStatus := events.ResourceChanged{ - Content: res.Content, - AuditContext: res.AuditContext, + Content: res.GetContent(), + AuditContext: res.GetAuditContext(), Status: commands.Status_ERROR, } @@ -195,7 +229,8 @@ var testEventResourceStateSnapshotTaken events.ResourceStateSnapshotTaken = even DeviceId: "devLatest", Href: "/devLatest", }, - Content: &commands.Content{}, + Content: &commands.Content{}, + ResourceTypes: []string{"type1", "type2"}, }, ResourceCreatePendings: []*events.ResourceCreatePending{ { @@ -203,6 +238,7 @@ var testEventResourceStateSnapshotTaken events.ResourceStateSnapshotTaken = even DeviceId: "devCreate", Href: "/devCreate", }, + ResourceTypes: []string{"type1", "type2"}, }, }, ResourceRetrievePendings: []*events.ResourceRetrievePending{ @@ -211,6 +247,7 @@ var testEventResourceStateSnapshotTaken events.ResourceStateSnapshotTaken = even DeviceId: "devRetrieve", Href: "/devRetrieve", }, + ResourceTypes: []string{"type1", "type2"}, }, }, ResourceUpdatePendings: []*events.ResourceUpdatePending{ @@ -219,6 +256,7 @@ var testEventResourceStateSnapshotTaken events.ResourceStateSnapshotTaken = even DeviceId: "devUpdate", Href: "/devUpdate", }, + ResourceTypes: []string{"type1", "type2"}, }, }, ResourceDeletePendings: []*events.ResourceDeletePending{ @@ -227,6 +265,7 @@ var testEventResourceStateSnapshotTaken events.ResourceStateSnapshotTaken = even DeviceId: "devDelete", Href: "/devDelete", }, + ResourceTypes: []string{"type1", "type2"}, }, }, AuditContext: &commands.AuditContext{ @@ -239,9 +278,10 @@ var testEventResourceStateSnapshotTaken events.ResourceStateSnapshotTaken = even ConnectionId: "con1", Sequence: 1, }, + ResourceTypes: []string{"type1", "type2"}, } -func TestResourceStateSnapshotTaken_CopyData(t *testing.T) { +func TestResourceStateSnapshotTakenCopyData(t *testing.T) { type args struct { event *events.ResourceStateSnapshotTaken } diff --git a/resource-aggregate/events/resourceUpdatePending.go b/resource-aggregate/events/resourceUpdatePending.go index 0fdbc5013..45c51e072 100644 --- a/resource-aggregate/events/resourceUpdatePending.go +++ b/resource-aggregate/events/resourceUpdatePending.go @@ -50,6 +50,10 @@ func (e *ResourceUpdatePending) ServiceID() (string, bool) { return "", false } +func (e *ResourceUpdatePending) Types() []string { + return e.GetResourceTypes() +} + func (e *ResourceUpdatePending) CopyData(event *ResourceUpdatePending) { e.ResourceId = event.GetResourceId() e.ResourceInterface = event.GetResourceInterface() @@ -58,6 +62,7 @@ func (e *ResourceUpdatePending) CopyData(event *ResourceUpdatePending) { e.EventMetadata = event.GetEventMetadata() e.ValidUntil = event.GetValidUntil() e.OpenTelemetryCarrier = event.GetOpenTelemetryCarrier() + e.ResourceTypes = event.GetResourceTypes() } func (e *ResourceUpdatePending) CheckInitialized() bool { diff --git a/resource-aggregate/events/resourceUpdatePending_test.go b/resource-aggregate/events/resourceUpdatePending_test.go index 4a823a9f7..9bd0d050e 100644 --- a/resource-aggregate/events/resourceUpdatePending_test.go +++ b/resource-aggregate/events/resourceUpdatePending_test.go @@ -34,9 +34,10 @@ var testEventResourceUpdatePending events.ResourceUpdatePending = events.Resourc ConnectionId: "con1", Sequence: 1, }, + ResourceTypes: []string{"type1", "type2"}, } -func TestResourceUpdatePending_CopyData(t *testing.T) { +func TestResourceUpdatePendingCopyData(t *testing.T) { type args struct { event *events.ResourceUpdatePending } diff --git a/resource-aggregate/events/resourceUpdated.go b/resource-aggregate/events/resourceUpdated.go index 6845923c0..6c7bac5c8 100644 --- a/resource-aggregate/events/resourceUpdated.go +++ b/resource-aggregate/events/resourceUpdated.go @@ -51,6 +51,10 @@ func (e *ResourceUpdated) ServiceID() (string, bool) { return "", false } +func (e *ResourceUpdated) Types() []string { + return e.GetResourceTypes() +} + func (e *ResourceUpdated) CopyData(event *ResourceUpdated) { e.ResourceId = event.GetResourceId() e.Status = event.GetStatus() @@ -58,6 +62,7 @@ func (e *ResourceUpdated) CopyData(event *ResourceUpdated) { e.AuditContext = event.GetAuditContext() e.EventMetadata = event.GetEventMetadata() e.OpenTelemetryCarrier = event.GetOpenTelemetryCarrier() + e.ResourceTypes = event.GetResourceTypes() } func (e *ResourceUpdated) CheckInitialized() bool { diff --git a/resource-aggregate/events/resourceUpdated_test.go b/resource-aggregate/events/resourceUpdated_test.go index e18e8edf1..e0631614c 100644 --- a/resource-aggregate/events/resourceUpdated_test.go +++ b/resource-aggregate/events/resourceUpdated_test.go @@ -31,9 +31,10 @@ var testEventResourceUpdated events.ResourceUpdated = events.ResourceUpdated{ ConnectionId: "con1", Sequence: 1, }, + ResourceTypes: []string{"type1", "type2"}, } -func TestResourceUpdated_CopyData(t *testing.T) { +func TestResourceUpdatedCopyData(t *testing.T) { type args struct { event *events.ResourceUpdated } diff --git a/resource-aggregate/events/serviceMetadataSnapshotTaken.go b/resource-aggregate/events/serviceMetadataSnapshotTaken.go index 8ea150e85..4110973eb 100644 --- a/resource-aggregate/events/serviceMetadataSnapshotTaken.go +++ b/resource-aggregate/events/serviceMetadataSnapshotTaken.go @@ -2,6 +2,7 @@ package events import ( "context" + "errors" "fmt" "sort" "time" @@ -62,6 +63,10 @@ func (d *ServiceMetadataSnapshotTaken) ServiceID() (string, bool) { return "", false } +func (d *ServiceMetadataSnapshotTaken) Types() []string { + return nil +} + func (d *ServiceMetadataSnapshotTaken) CopyData(event *ServiceMetadataSnapshotTaken) { d.ServiceMetadataUpdated = event.GetServiceMetadataUpdated() d.EventMetadata = event.GetEventMetadata() @@ -73,7 +78,7 @@ func (d *ServiceMetadataSnapshotTaken) CheckInitialized() bool { } func (d *ServiceMetadataSnapshotTaken) HandleServiceMetadataUpdated(_ context.Context, upd *ServiceMetadataUpdated) (bool, error) { - if d.ServiceMetadataUpdated.Equal(upd) { + if d.GetServiceMetadataUpdated().Equal(upd) { return false, nil } valid := make(map[string]*ServicesHeartbeat_Heartbeat, len(d.GetServiceMetadataUpdated().GetServicesHeartbeat().GetValid())+len(upd.GetServicesHeartbeat().GetValid())) @@ -322,7 +327,7 @@ func (d *ServiceMetadataSnapshotTaken) confirmExpiredServices(ctx context.Contex d.EventMetadata = em snapshot, ok := d.TakeSnapshot(em.GetVersion()) if !ok { - return nil, fmt.Errorf("cannot take snapshot") + return nil, errors.New("cannot take snapshot") } return []eventstore.Event{snapshot}, nil } diff --git a/resource-aggregate/events/serviceMetadataUpdated.go b/resource-aggregate/events/serviceMetadataUpdated.go index d79755a53..81db2566b 100644 --- a/resource-aggregate/events/serviceMetadataUpdated.go +++ b/resource-aggregate/events/serviceMetadataUpdated.go @@ -51,12 +51,17 @@ func (d *ServiceMetadataUpdated) ServiceID() (string, bool) { return "", false } +func (d *ServiceMetadataUpdated) Types() []string { + return nil +} + func (d *ServiceMetadataUpdated) CopyData(event *ServiceMetadataUpdated) { d.EventMetadata = event.GetEventMetadata() d.OpenTelemetryCarrier = event.GetOpenTelemetryCarrier() - d.ServicesHeartbeat = &ServicesHeartbeat{} - d.ServicesHeartbeat.CopyData(event.GetServicesHeartbeat()) + sh := &ServicesHeartbeat{} + sh.CopyData(event.GetServicesHeartbeat()) + d.ServicesHeartbeat = sh } func (d *ServiceMetadataUpdated) CheckInitialized() bool { diff --git a/resource-aggregate/events/utils.go b/resource-aggregate/events/utils.go index de67bbeb6..6f4a97125 100644 --- a/resource-aggregate/events/utils.go +++ b/resource-aggregate/events/utils.go @@ -18,15 +18,15 @@ func MakeEventMeta(connectionID string, sequence, version uint64, hubID string) } func EqualResource(x, y *commands.Resource) bool { - return x.DeviceId == y.DeviceId && - EqualStringSlice(x.ResourceTypes, y.ResourceTypes) && - EqualStringSlice(x.Interfaces, y.Interfaces) && - x.Anchor == y.Anchor && - x.Title == y.Title && - EqualStringSlice(x.SupportedContentTypes, y.SupportedContentTypes) && - x.ValidUntil == y.ValidUntil && - ((x.Policy == nil && y.Policy == nil) || - (x.Policy != nil && y.Policy != nil && x.Policy.BitFlags == y.Policy.BitFlags)) + return x.GetDeviceId() == y.GetDeviceId() && + EqualStringSlice(x.GetResourceTypes(), y.GetResourceTypes()) && + EqualStringSlice(x.GetInterfaces(), y.GetInterfaces()) && + x.GetAnchor() == y.GetAnchor() && + x.GetTitle() == y.GetTitle() && + EqualStringSlice(x.GetSupportedContentTypes(), y.GetSupportedContentTypes()) && + x.GetValidUntil() == y.GetValidUntil() && + ((x.GetPolicy() == nil && y.GetPolicy() == nil) || + (x.GetPolicy() != nil && y.GetPolicy() != nil && x.GetPolicy().GetBitFlags() == y.GetPolicy().GetBitFlags())) } func EqualStringSlice(x, y []string) bool { diff --git a/resource-aggregate/events/utils_test.go b/resource-aggregate/events/utils_test.go index 6d5a3e625..f9dee92f6 100644 --- a/resource-aggregate/events/utils_test.go +++ b/resource-aggregate/events/utils_test.go @@ -94,8 +94,9 @@ func TestEqualResource(t *testing.T) { resInterfacesNil.Interfaces = nil resInterfaces2 := createResource() - resInterfaces2.Interfaces = make([]string, 1) - copy(resInterfaces2.Interfaces, res.Interfaces) + interfaces := make([]string, 1) + copy(interfaces, res.GetInterfaces()) + resInterfaces2.Interfaces = interfaces resAnchor := createResource() resAnchor.Anchor = "Anchor2" diff --git a/resource-aggregate/pb/commands.proto b/resource-aggregate/pb/commands.proto index dcac3dd94..41bf7b09e 100644 --- a/resource-aggregate/pb/commands.proto +++ b/resource-aggregate/pb/commands.proto @@ -95,6 +95,7 @@ message NotifyResourceChangedRequest { CommandMetadata command_metadata = 3; Status status = 4; bytes etag = 5; + repeated string resource_types = 6; } message NotifyResourceChangedResponse { @@ -443,6 +444,7 @@ message Connection { } Protocol protocol = 5; // application protocol. It need to be set when the status is ONLINE. string service_id = 6; // The service.ID, which identify the device being served, must be set when the status is ONLINE. However, during an OFFLINE event, they will be sed to empty values. + repeated string local_endpoints = 7; // The last local endpoints of the device, and it is set when the status is ONLINE. } //******************************************************************************************************************************************************* // Update Device Metadata - Twin Synchronization State diff --git a/resource-aggregate/pb/events.proto b/resource-aggregate/pb/events.proto index 1cd534e45..080eb74b5 100644 --- a/resource-aggregate/pb/events.proto +++ b/resource-aggregate/pb/events.proto @@ -54,6 +54,7 @@ message ResourceChanged { AuditContext audit_context = 4; EventMetadata event_metadata = 5; bytes etag = 6; // etag of the resource used by twin synchronization + repeated string resource_types = 7; // Open telemetry data propagated to asynchronous events map open_telemetry_carrier = 100; @@ -66,6 +67,7 @@ message ResourceUpdatePending { AuditContext audit_context = 4; EventMetadata event_metadata = 5; int64 valid_until = 6; // unix timestamp in nanoseconds (https://golang.org/pkg/time/#Time.UnixNano) when pending event is considered as expired. 0 means forever. + repeated string resource_types = 7; // Open telemetry data propagated to asynchronous events map open_telemetry_carrier = 100; @@ -77,6 +79,7 @@ message ResourceUpdated { Content content = 3; AuditContext audit_context = 4; EventMetadata event_metadata = 5; + repeated string resource_types = 6; // Open telemetry data propagated to asynchronous events map open_telemetry_carrier = 100; @@ -89,6 +92,7 @@ message ResourceRetrievePending { EventMetadata event_metadata = 4; int64 valid_until = 5; // unix timestamp in nanoseconds (https://golang.org/pkg/time/#Time.UnixNano) when pending event is considered as expired. 0 means forever. repeated bytes etag = 6; + repeated string resource_types = 7; // Open telemetry data propagated to asynchronous events map open_telemetry_carrier = 100; @@ -101,6 +105,7 @@ message ResourceRetrieved { AuditContext audit_context = 4; EventMetadata event_metadata = 5; bytes etag = 6; + repeated string resource_types = 7; // Open telemetry data propagated to asynchronous events map open_telemetry_carrier = 100; @@ -112,6 +117,7 @@ message ResourceDeletePending { EventMetadata event_metadata = 3; int64 valid_until = 4; // unix timestamp in nanoseconds (https://golang.org/pkg/time/#Time.UnixNano) when pending event is considered as expired. 0 means forever. string resource_interface = 5; + repeated string resource_types = 6; // Open telemetry data propagated to asynchronous events map open_telemetry_carrier = 100; @@ -123,6 +129,7 @@ message ResourceDeleted { Content content = 3; AuditContext audit_context = 4; EventMetadata event_metadata = 5; + repeated string resource_types = 6; // Open telemetry data propagated to asynchronous events map open_telemetry_carrier = 100; @@ -134,6 +141,7 @@ message ResourceCreatePending { AuditContext audit_context = 3; EventMetadata event_metadata = 4; int64 valid_until = 5; // unix timestamp in nanoseconds (https://golang.org/pkg/time/#Time.UnixNano) when pending event is considered as expired. 0 means forever. + repeated string resource_types = 6; // Open telemetry data propagated to asynchronous events map open_telemetry_carrier = 100; @@ -145,6 +153,7 @@ message ResourceCreated { Content content = 3; AuditContext audit_context = 4; EventMetadata event_metadata = 5; + repeated string resource_types = 6; // Open telemetry data propagated to asynchronous events map open_telemetry_carrier = 100; @@ -159,6 +168,7 @@ message ResourceStateSnapshotTaken { repeated ResourceDeletePending resource_delete_pendings = 6; // expired events will be removed by creating a new snapshot. AuditContext audit_context = 7; EventMetadata event_metadata = 8; + repeated string resource_types = 9; // Open telemetry data propagated to asynchronous events // map open_telemetry_carrier = 100; diff --git a/resource-aggregate/service/aggregate.go b/resource-aggregate/service/aggregate.go index 63722234a..f25bdad35 100644 --- a/resource-aggregate/service/aggregate.go +++ b/resource-aggregate/service/aggregate.go @@ -21,43 +21,60 @@ type Aggregate struct { eventstore eventstore.EventStore } -func NewResourceStateFactoryModel(userID, owner, hubID string) func(ctx context.Context) (cqrsAggregate.AggregateModel, error) { - return func(ctx context.Context) (cqrsAggregate.AggregateModel, error) { - return events.NewResourceStateSnapshotTakenForCommand(userID, owner, hubID), nil +func NewResourceStateFactoryModel(userID, owner, hubID string) func(context.Context, string, string) (cqrsAggregate.AggregateModel, error) { + resourceLinks := events.NewResourceLinksSnapshotTakenForCommand(userID, owner, hubID) + resourceState := events.NewResourceStateSnapshotTakenForCommand(userID, owner, hubID, resourceLinks) + return func(_ context.Context, groupID string, aggregateID string) (cqrsAggregate.AggregateModel, error) { + resID := commands.NewResourceID(groupID, commands.ResourceLinksHref) + if aggregateID == resID.ToUUID().String() { + return resourceLinks, nil + } + return resourceState, nil } } -func NewResourceLinksFactoryModel(userID, owner, hubID string) func(ctx context.Context) (cqrsAggregate.AggregateModel, error) { - return func(ctx context.Context) (cqrsAggregate.AggregateModel, error) { +func NewResourceLinksFactoryModel(userID, owner, hubID string) func(context.Context, string, string) (cqrsAggregate.AggregateModel, error) { + return func(context.Context, string, string) (cqrsAggregate.AggregateModel, error) { return events.NewResourceLinksSnapshotTakenForCommand(userID, owner, hubID), nil } } -func NewDeviceMetadataFactoryModel(userID, owner, hubID string) func(ctx context.Context) (cqrsAggregate.AggregateModel, error) { - return func(ctx context.Context) (cqrsAggregate.AggregateModel, error) { +func NewDeviceMetadataFactoryModel(userID, owner, hubID string) func(context.Context, string, string) (cqrsAggregate.AggregateModel, error) { + return func(context.Context, string, string) (cqrsAggregate.AggregateModel, error) { return events.NewDeviceMetadataSnapshotTakenForCommand(userID, owner, hubID), nil } } -func NewServicesMetadataFactoryModel(userID, owner, hubID string) func(ctx context.Context) (cqrsAggregate.AggregateModel, error) { - return func(ctx context.Context) (cqrsAggregate.AggregateModel, error) { +func NewServicesMetadataFactoryModel(userID, owner, hubID string) func(context.Context, string, string) (cqrsAggregate.AggregateModel, error) { + return func(context.Context, string, string) (cqrsAggregate.AggregateModel, error) { return events.NewServiceMetadataSnapshotTakenForCommand(userID, owner, hubID), nil } } -// NewAggregate creates new resource aggreate - it must be created for every run command. -func NewAggregate(resourceID *commands.ResourceId, store eventstore.EventStore, factoryModel cqrsAggregate.FactoryModelFunc, retry cqrsAggregate.RetryFunc) (*Aggregate, error) { +// NewResourceAggregate for creating new resource aggregate. +func NewResourceAggregate(resourceID *commands.ResourceId, store eventstore.EventStore, factoryModel cqrsAggregate.FactoryModelFunc, retry cqrsAggregate.RetryFunc, addLinkedResources bool) (*Aggregate, error) { a := &Aggregate{ eventstore: store, } + addLink := make([]cqrsAggregate.AdditionalModel, 0, 1) + if addLinkedResources { + addLink = append(addLink, cqrsAggregate.AdditionalModel{ + GroupID: resourceID.GetDeviceId(), + AggregateID: commands.NewResourceID(resourceID.GetDeviceId(), commands.ResourceLinksHref).ToUUID().String(), + }) + } + cqrsAg, err := cqrsAggregate.NewAggregate(resourceID.GetDeviceId(), resourceID.ToUUID().String(), retry, store, factoryModel, - func(template string, args ...interface{}) { - // TODO: add debug log - }) + func(string, ...interface{}) { + // no-op - we don't want to log debug/trace messages + }, + // load also links state + addLink..., + ) if err != nil { return nil, fmt.Errorf("cannot create aggregate for resource: %w", err) } @@ -65,6 +82,11 @@ func NewAggregate(resourceID *commands.ResourceId, store eventstore.EventStore, return a, nil } +// NewAggregate creates new resource aggreate - it must be created for every run command. +func NewAggregate(resourceID *commands.ResourceId, store eventstore.EventStore, factoryModel cqrsAggregate.FactoryModelFunc, retry cqrsAggregate.RetryFunc) (*Aggregate, error) { + return NewResourceAggregate(resourceID, store, factoryModel, retry, false) +} + func (a *Aggregate) HandleCommand(ctx context.Context, cmd cqrsAggregate.Command) ([]eventstore.Event, error) { events, err := a.ag.HandleCommand(ctx, cmd) if err == nil { @@ -82,18 +104,20 @@ var ( ) func validatePublish(request *commands.PublishResourceLinksRequest) error { - if len(request.Resources) == 0 { + resources := request.GetResources() + if len(resources) == 0 { return status.Errorf(codes.InvalidArgument, "empty publish is not accepted") } - for _, res := range request.Resources { - if len(res.Href) <= 1 || res.Href[:1] != "/" { + for _, res := range resources { + href := res.GetHref() + if len(href) <= 1 || href[:1] != "/" { return status.Errorf(codes.InvalidArgument, "invalid resource href") } - if res.DeviceId == "" { + if res.GetDeviceId() == "" { return status.Errorf(codes.InvalidArgument, "invalid device id") } } - if request.DeviceId == "" { + if request.GetDeviceId() == "" { return errInvalidDeviceID } return nil @@ -103,7 +127,7 @@ func validateUnpublish(request *commands.UnpublishResourceLinksRequest) error { if request.GetDeviceId() == "" { return errInvalidDeviceID } - for _, href := range request.Hrefs { + for _, href := range request.GetHrefs() { if href == "" { return status.Errorf(codes.InvalidArgument, "invalid resource id") } @@ -112,7 +136,7 @@ func validateUnpublish(request *commands.UnpublishResourceLinksRequest) error { } func validateNotifyContentChanged(request *commands.NotifyResourceChangedRequest) error { - if request.Content == nil { + if request.GetContent() == nil { return errInvalidContent } if request.GetResourceId().GetDeviceId() == "" { @@ -128,7 +152,7 @@ func validateUpdateResourceContent(request *commands.UpdateResourceRequest) erro if err := checkTimeToLive(request.GetTimeToLive()); err != nil { return err } - if request.Content == nil { + if request.GetContent() == nil { return errInvalidContent } if request.GetResourceId().GetDeviceId() == "" { @@ -160,7 +184,7 @@ func validateRetrieveResource(request *commands.RetrieveResourceRequest) error { } func validateConfirmResourceUpdate(request *commands.ConfirmResourceUpdateRequest) error { - if request.Content == nil { + if request.GetContent() == nil { return errInvalidContent } if request.GetResourceId().GetDeviceId() == "" { @@ -177,7 +201,7 @@ func validateConfirmResourceUpdate(request *commands.ConfirmResourceUpdateReques } func validateConfirmResourceRetrieve(request *commands.ConfirmResourceRetrieveRequest) error { - if request.Content == nil { + if request.GetContent() == nil { return errInvalidContent } if request.GetResourceId().GetDeviceId() == "" { @@ -276,9 +300,9 @@ func (a *Aggregate) cleanUpToSnapshot(ctx context.Context, events []eventstore.E err := a.eventstore.RemoveUpToVersion(ctx, []eventstore.VersionQuery{{GroupID: event.GroupID(), AggregateID: event.AggregateID(), Version: event.Version()}}) if err != nil && !errors.Is(err, eventstore.ErrNotSupported) { if ru, ok := event.(interface{ GetResourceId() *commands.ResourceId }); ok { - log.Info("unable to remove events up to snapshot with version('%v') for resource('%v')", event.Version(), ru.GetResourceId()) + log.Infof("unable to remove events up to snapshot with version('%v') for resource('%v')", event.Version(), ru.GetResourceId()) } else { - log.Info("unable to remove events up to snapshot(%v) with version('%v') of deviceId('%v')", event.EventType(), event.Version(), event.GroupID()) + log.Infof("unable to remove events up to snapshot(%v) with version('%v') of deviceId('%v')", event.EventType(), event.Version(), event.GroupID()) } } } diff --git a/resource-aggregate/service/aggregate_test.go b/resource-aggregate/service/aggregate_test.go index 7885f9aaa..91b19faf4 100644 --- a/resource-aggregate/service/aggregate_test.go +++ b/resource-aggregate/service/aggregate_test.go @@ -2,7 +2,7 @@ package service_test import ( "context" - "fmt" + "errors" "testing" "time" @@ -27,7 +27,6 @@ import ( raTest "github.com/plgd-dev/hub/v2/resource-aggregate/test" hubTest "github.com/plgd-dev/hub/v2/test" "github.com/plgd-dev/hub/v2/test/config" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/trace/noop" "google.golang.org/grpc/codes" @@ -99,12 +98,12 @@ func TestAggregateHandlePublishResourceLinks(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(cfg.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -113,7 +112,7 @@ func TestAggregateHandlePublishResourceLinks(t *testing.T) { naClient.Close() }() - assert.NoError(t, err) + require.NoError(t, err) for _, tt := range test { tfunc := func(t *testing.T) { ag, err := service.NewAggregate(commands.NewResourceID(tt.args.request.GetDeviceId(), commands.ResourceLinksHref), eventstore, service.NewResourceLinksFactoryModel(tt.args.userID, tt.args.owner, cfg.HubID), cqrsAggregate.NewDefaultRetryFunc(1)) @@ -123,7 +122,7 @@ func TestAggregateHandlePublishResourceLinks(t *testing.T) { require.Error(t, err) s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) require.True(t, ok) - assert.Equal(t, tt.want, s.Code()) + require.Equal(t, tt.want, s.Code()) } else { require.NoError(t, err) service.PublishEvents(publisher, tt.args.userID, tt.args.request.GetDeviceId(), ag.ResourceID(), events, logger) @@ -137,7 +136,7 @@ func testHandlePublishResource(ctx context.Context, t *testing.T, publisher *pub pc := testMakePublishResourceRequest(deviceID, hrefs) ag, err := service.NewAggregate(commands.NewResourceID(pc.GetDeviceId(), commands.ResourceLinksHref), eventstore, service.NewResourceLinksFactoryModel(userID, owner, hubID), cqrsAggregate.NewDefaultRetryFunc(1)) - assert.NoError(t, err) + require.NoError(t, err) events, err := ag.PublishResourceLinks(ctx, pc) require.NoError(t, err) service.PublishEvents(publisher, userID, deviceID, ag.ResourceID(), events, log.Get()) @@ -150,7 +149,7 @@ func TestAggregateDuplicitPublishResource(t *testing.T) { const owner = "owner" pool, err := ants.NewPool(16) - assert.NoError(t, err) + require.NoError(t, err) defer pool.Release() cfg := raTest.MakeConfig(t) @@ -172,7 +171,7 @@ func TestAggregateDuplicitPublishResource(t *testing.T) { require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() ag, err := service.NewAggregate(commands.NewResourceID(deviceID, commands.ResourceLinksHref), eventstore, service.NewResourceLinksFactoryModel(userID, owner, cfg.HubID), cqrsAggregate.NewDefaultRetryFunc(1)) @@ -181,21 +180,21 @@ func TestAggregateDuplicitPublishResource(t *testing.T) { events, err := ag.PublishResourceLinks(ctx, pc1) require.NoError(t, err) - assert.Equal(t, 1, len(events)) + require.Len(t, events, 1) ag2, err := service.NewAggregate(commands.NewResourceID(deviceID, commands.ResourceLinksHref), eventstore, service.NewResourceLinksFactoryModel(userID, owner, cfg.HubID), cqrsAggregate.NewDefaultRetryFunc(1)) require.NoError(t, err) pc2 := testMakePublishResourceRequest(deviceID, []string{resourceID}) events, err = ag2.PublishResourceLinks(ctx, pc2) require.NoError(t, err) - assert.Empty(t, events) + require.Empty(t, events) ag3, err := service.NewAggregate(commands.NewResourceID(deviceID, commands.ResourceLinksHref), eventstore, service.NewResourceLinksFactoryModel(userID, owner, cfg.HubID), cqrsAggregate.NewDefaultRetryFunc(1)) require.NoError(t, err) pc3 := testMakePublishResourceRequest(deviceID, []string{resourceID, resourceID, resourceID}) events, err = ag3.PublishResourceLinks(ctx, pc3) require.NoError(t, err) - assert.Empty(t, events) + require.Empty(t, events) } func TestAggregateHandleUnpublishResource(t *testing.T) { @@ -224,12 +223,12 @@ func TestAggregateHandleUnpublishResource(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(cfg.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -243,14 +242,14 @@ func TestAggregateHandleUnpublishResource(t *testing.T) { pc := testMakeUnpublishResourceRequest(deviceID, []string{resourceID}) ag, err := service.NewAggregate(commands.NewResourceID(pc.GetDeviceId(), commands.ResourceLinksHref), eventstore, service.NewResourceLinksFactoryModel(userID, owner, cfg.HubID), cqrsAggregate.NewDefaultRetryFunc(1)) - assert.NoError(t, err) + require.NoError(t, err) events, err := ag.UnpublishResourceLinks(ctx, pc) - assert.NoError(t, err) + require.NoError(t, err) service.PublishEvents(publisher, userID, deviceID, ag.ResourceID(), events, logger) _, err = ag.UnpublishResourceLinks(ctx, pc) - assert.NoError(t, err) + require.NoError(t, err) } func TestAggregateHandleUnpublishAllResources(t *testing.T) { @@ -278,12 +277,12 @@ func TestAggregateHandleUnpublishAllResources(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(cfg.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -297,14 +296,14 @@ func TestAggregateHandleUnpublishAllResources(t *testing.T) { pc := testMakeUnpublishResourceRequest(deviceID, []string{}) ag, err := service.NewAggregate(commands.NewResourceID(pc.GetDeviceId(), commands.ResourceLinksHref), eventstore, service.NewResourceLinksFactoryModel(userID, owner, cfg.HubID), cqrsAggregate.NewDefaultRetryFunc(1)) - assert.NoError(t, err) + require.NoError(t, err) events, err := ag.UnpublishResourceLinks(ctx, pc) - assert.NoError(t, err) - assert.Equal(t, 1, len(events)) + require.NoError(t, err) + require.Len(t, events, 1) - unpublishedResourceLinks := (events[0].(*raEvents.ResourceLinksUnpublished)).Hrefs - assert.Equal(t, 3, len(unpublishedResourceLinks)) - assert.Contains(t, unpublishedResourceLinks, resourceID1, resourceID2, resourceID3) + unpublishedResourceLinks := (events[0].(*raEvents.ResourceLinksUnpublished)).GetHrefs() + require.Len(t, unpublishedResourceLinks, 3) + require.Contains(t, unpublishedResourceLinks, resourceID1, resourceID2, resourceID3) service.PublishEvents(publisher, userID, deviceID, ag.ResourceID(), events, logger) @@ -339,12 +338,12 @@ func TestAggregateHandleUnpublishResourceSubset(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(cfg.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -356,20 +355,20 @@ func TestAggregateHandleUnpublishResourceSubset(t *testing.T) { testHandlePublishResource(ctx, t, publisher, eventstore, userID, deviceID, owner, cfg.HubID, []string{resourceID1, resourceID2, resourceID3, resourceID4}) ag, err := service.NewAggregate(commands.NewResourceID(deviceID, commands.ResourceLinksHref), eventstore, service.NewResourceLinksFactoryModel(userID, owner, cfg.HubID), cqrsAggregate.NewDefaultRetryFunc(1)) - assert.NoError(t, err) + require.NoError(t, err) pc := testMakeUnpublishResourceRequest(deviceID, []string{resourceID1, resourceID3}) events, err := ag.UnpublishResourceLinks(ctx, pc) - assert.NoError(t, err) - assert.Equal(t, 1, len(events)) - assert.Equal(t, []string{resourceID1, resourceID3}, (events[0].(*raEvents.ResourceLinksUnpublished)).Hrefs) + require.NoError(t, err) + require.Len(t, events, 1) + require.Equal(t, []string{resourceID1, resourceID3}, (events[0].(*raEvents.ResourceLinksUnpublished)).GetHrefs()) service.PublishEvents(publisher, userID, deviceID, ag.ResourceID(), events, logger) pc = testMakeUnpublishResourceRequest(deviceID, []string{resourceID1, resourceID4, resourceID4}) events, err = ag.UnpublishResourceLinks(ctx, pc) - assert.NoError(t, err) - assert.Equal(t, 1, len(events)) - assert.Equal(t, []string{resourceID4}, (events[0].(*raEvents.ResourceLinksUnpublished)).Hrefs) + require.NoError(t, err) + require.Len(t, events, 1) + require.Equal(t, []string{resourceID4}, (events[0].(*raEvents.ResourceLinksUnpublished)).GetHrefs()) } func testMakePublishResourceRequest(deviceID string, href []string) *commands.PublishResourceLinksRequest { @@ -652,12 +651,12 @@ func TestAggregateHandleNotifyContentChanged(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(cfg.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -669,7 +668,7 @@ func TestAggregateHandleNotifyContentChanged(t *testing.T) { testHandlePublishResource(ctx, t, publisher, eventstore, userID, deviceID, owner, cfg.HubID, []string{resourceID}) ag, err := service.NewAggregate(commands.NewResourceID(deviceID, resourceID), eventstore, service.NewResourceStateFactoryModel(userID, owner, cfg.HubID), cqrsAggregate.NewDefaultRetryFunc(1)) - assert.NoError(t, err) + require.NoError(t, err) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -678,12 +677,12 @@ func TestAggregateHandleNotifyContentChanged(t *testing.T) { require.Error(t, err) s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) require.True(t, ok) - assert.Equal(t, tt.wantStatusCode, s.Code()) + require.Equal(t, tt.wantStatusCode, s.Code()) return } require.NoError(t, err) if tt.wantEvents { - assert.NotEmpty(t, gotEvents) + require.NotEmpty(t, gotEvents) } }) } @@ -770,16 +769,16 @@ func TestAggregateHandleUpdateResourceContent(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() ag, err := service.NewAggregate(commands.NewResourceID(deviceID, resourceID), eventstore, service.NewResourceStateFactoryModel(userID, owner, cfg.HubID), cqrsAggregate.NewDefaultRetryFunc(1)) - assert.NoError(t, err) + require.NoError(t, err) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -793,12 +792,12 @@ func TestAggregateHandleUpdateResourceContent(t *testing.T) { require.Error(t, err) s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) require.True(t, ok) - assert.Equal(t, tt.wantStatusCode, s.Code()) + require.Equal(t, tt.wantStatusCode, s.Code()) return } require.NoError(t, err) if tt.wantEvents { - assert.NotEmpty(t, gotEvents) + require.NotEmpty(t, gotEvents) } time.Sleep(tt.args.sleep) }) @@ -857,12 +856,12 @@ func TestAggregateHandleConfirmResourceUpdate(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() ag, err := service.NewAggregate(commands.NewResourceID(deviceID, resourceID), eventstore, service.NewResourceStateFactoryModel(userID, owner, cfg.HubID), cqrsAggregate.NewDefaultRetryFunc(1)) @@ -884,12 +883,12 @@ func TestAggregateHandleConfirmResourceUpdate(t *testing.T) { require.Error(t, err) s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) require.True(t, ok) - assert.Equal(t, tt.wantStatusCode, s.Code()) + require.Equal(t, tt.wantStatusCode, s.Code()) return } require.NoError(t, err) if tt.wantEvents { - assert.NotEmpty(t, gotEvents) + require.NotEmpty(t, gotEvents) } }) } @@ -967,16 +966,16 @@ func TestAggregateHandleRetrieveResource(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() ag, err := service.NewAggregate(commands.NewResourceID(deviceID, resourceID), eventstore, service.NewResourceStateFactoryModel(userID, owner, cfg.HubID), cqrsAggregate.NewDefaultRetryFunc(1)) - assert.NoError(t, err) + require.NoError(t, err) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -989,12 +988,12 @@ func TestAggregateHandleRetrieveResource(t *testing.T) { require.Error(t, err) s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) require.True(t, ok) - assert.Equal(t, tt.wantStatusCode, s.Code()) + require.Equal(t, tt.wantStatusCode, s.Code()) return } require.NoError(t, err) if tt.wantEvents { - assert.NotEmpty(t, gotEvents) + require.NotEmpty(t, gotEvents) } time.Sleep(tt.args.sleep) }) @@ -1053,16 +1052,16 @@ func TestAggregateHandleNotifyResourceContentResourceProcessed(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() ag, err := service.NewAggregate(commands.NewResourceID(deviceID, resourceID), eventstore, service.NewResourceStateFactoryModel(userID, owner, cfg.HubID), cqrsAggregate.NewDefaultRetryFunc(1)) - assert.NoError(t, err) + require.NoError(t, err) _, err = ag.NotifyResourceChanged(ctx, testMakeNotifyResourceChangedRequest(deviceID, resourceID, 0)) require.NoError(t, err) @@ -1080,12 +1079,12 @@ func TestAggregateHandleNotifyResourceContentResourceProcessed(t *testing.T) { require.Error(t, err) s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) require.True(t, ok) - assert.Equal(t, tt.wantStatusCode, s.Code()) + require.Equal(t, tt.wantStatusCode, s.Code()) return } require.NoError(t, err) if tt.wantEvents { - assert.NotEmpty(t, gotEvents) + require.NotEmpty(t, gotEvents) } }) } @@ -1093,7 +1092,7 @@ func TestAggregateHandleNotifyResourceContentResourceProcessed(t *testing.T) { func testListDevicesOfUserFunc(userID string) ([]string, codes.Code, error) { if userID == testUnauthorizedUser { - return nil, codes.Unauthenticated, fmt.Errorf("unauthorized access") + return nil, codes.Unauthenticated, errors.New("unauthorized access") } return testUserDevices, codes.OK, nil } @@ -1171,16 +1170,16 @@ func TestAggregateHandleDeleteResource(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() ag, err := service.NewAggregate(commands.NewResourceID(deviceID, resourceID), eventstore, service.NewResourceStateFactoryModel(userID, owner, cfg.HubID), cqrsAggregate.NewDefaultRetryFunc(1)) - assert.NoError(t, err) + require.NoError(t, err) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -1193,12 +1192,12 @@ func TestAggregateHandleDeleteResource(t *testing.T) { require.Error(t, err) s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) require.True(t, ok) - assert.Equal(t, tt.wantStatusCode, s.Code()) + require.Equal(t, tt.wantStatusCode, s.Code()) return } require.NoError(t, err) if tt.wantEvents { - assert.NotEmpty(t, gotEvents) + require.NotEmpty(t, gotEvents) } time.Sleep(tt.args.sleep) }) @@ -1258,16 +1257,16 @@ func TestAggregateHandleConfirmResourceDelete(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() ag, err := service.NewAggregate(commands.NewResourceID(deviceID, resourceID), eventstore, service.NewResourceStateFactoryModel(userID, owner, cfg.HubID), cqrsAggregate.NewDefaultRetryFunc(1)) - assert.NoError(t, err) + require.NoError(t, err) _, err = ag.NotifyResourceChanged(ctx, testMakeNotifyResourceChangedRequest(deviceID, resourceID, 0)) require.NoError(t, err) @@ -1285,13 +1284,13 @@ func TestAggregateHandleConfirmResourceDelete(t *testing.T) { require.Error(t, err) s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) require.True(t, ok) - assert.Equal(t, tt.wantStatusCode, s.Code()) + require.Equal(t, tt.wantStatusCode, s.Code()) return } require.NoError(t, err) if tt.wantEvents { - assert.NotEmpty(t, gotEvents) + require.NotEmpty(t, gotEvents) } }) } @@ -1369,16 +1368,16 @@ func TestAggregateHandleCreateResource(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() ag, err := service.NewAggregate(commands.NewResourceID(deviceID, resourceID), eventstore, service.NewResourceStateFactoryModel(userID, owner, cfg.HubID), cqrsAggregate.NewDefaultRetryFunc(1)) - assert.NoError(t, err) + require.NoError(t, err) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -1391,12 +1390,12 @@ func TestAggregateHandleCreateResource(t *testing.T) { require.Error(t, err) s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) require.True(t, ok) - assert.Equal(t, tt.wantStatusCode, s.Code()) + require.Equal(t, tt.wantStatusCode, s.Code()) return } require.NoError(t, err) if tt.wantEvents { - assert.NotEmpty(t, gotEvents) + require.NotEmpty(t, gotEvents) } time.Sleep(tt.args.sleep) }) @@ -1456,16 +1455,16 @@ func TestAggregateHandleConfirmResourceCreate(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() ag, err := service.NewAggregate(commands.NewResourceID(deviceID, resourceID), eventstore, service.NewResourceStateFactoryModel(userID, owner, cfg.HubID), cqrsAggregate.NewDefaultRetryFunc(1)) - assert.NoError(t, err) + require.NoError(t, err) _, err = ag.NotifyResourceChanged(ctx, testMakeNotifyResourceChangedRequest(deviceID, resourceID, 0)) require.NoError(t, err) @@ -1483,13 +1482,13 @@ func TestAggregateHandleConfirmResourceCreate(t *testing.T) { require.Error(t, err) s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) require.True(t, ok) - assert.Equal(t, tt.wantStatusCode, s.Code()) + require.Equal(t, tt.wantStatusCode, s.Code()) return } require.NoError(t, err) if tt.wantEvents { - assert.NotEmpty(t, gotEvents) + require.NotEmpty(t, gotEvents) } }) } diff --git a/resource-aggregate/service/cancelDeviceMetadataUpdates_test.go b/resource-aggregate/service/cancelDeviceMetadataUpdates_test.go index ec3501ab5..d4cdf4088 100644 --- a/resource-aggregate/service/cancelDeviceMetadataUpdates_test.go +++ b/resource-aggregate/service/cancelDeviceMetadataUpdates_test.go @@ -89,12 +89,12 @@ func TestAggregateHandleCancelPendingMetadataUpdates(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(cfg.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -212,12 +212,12 @@ func TestRequestHandlerCancelPendingMetadataUpdates(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(cfg.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -244,10 +244,10 @@ func TestRequestHandlerCancelPendingMetadataUpdates(t *testing.T) { for _, tt := range test { tfunc := func(t *testing.T) { - ctx := kitNetGrpc.CtxWithIncomingToken(ctx, config.CreateJwtToken(t, jwt.MapClaims{ + cpmuCtx := kitNetGrpc.CtxWithIncomingToken(ctx, config.CreateJwtToken(t, jwt.MapClaims{ "sub": tt.args.userID, })) - want, err := requestHandler.CancelPendingMetadataUpdates(ctx, tt.args.request) + want, err := requestHandler.CancelPendingMetadataUpdates(cpmuCtx, tt.args.request) if tt.wantErr { require.Error(t, err) s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) diff --git a/resource-aggregate/service/cancelResourceCommands_test.go b/resource-aggregate/service/cancelResourceCommands_test.go index 614021c52..76bdd1ed0 100644 --- a/resource-aggregate/service/cancelResourceCommands_test.go +++ b/resource-aggregate/service/cancelResourceCommands_test.go @@ -162,12 +162,12 @@ func TestRequestHandlerCancelPendingCommands(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(cfg.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) diff --git a/resource-aggregate/service/confirmDeviceMetadataUpdate.go b/resource-aggregate/service/confirmDeviceMetadataUpdate.go index f8687cd8d..821279b1a 100644 --- a/resource-aggregate/service/confirmDeviceMetadataUpdate.go +++ b/resource-aggregate/service/confirmDeviceMetadataUpdate.go @@ -47,7 +47,7 @@ func (r RequestHandler) ConfirmDeviceMetadataUpdate(ctx context.Context, request return nil, log.LogAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, "cannot validate user access: %v", err)) } - resID := commands.NewResourceID(request.DeviceId, commands.StatusHref) + resID := commands.NewResourceID(request.GetDeviceId(), commands.StatusHref) aggregate, err := NewAggregate(resID, r.eventstore, NewDeviceMetadataFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry)) if err != nil { return nil, log.LogAndReturnError(kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "cannot confirm device('%v') metadata update: %v", request.GetDeviceId(), err)) diff --git a/resource-aggregate/service/confirmDeviceMetadataUpdate_test.go b/resource-aggregate/service/confirmDeviceMetadataUpdate_test.go index 31eb7d9bc..52fa9987b 100644 --- a/resource-aggregate/service/confirmDeviceMetadataUpdate_test.go +++ b/resource-aggregate/service/confirmDeviceMetadataUpdate_test.go @@ -84,12 +84,12 @@ func TestAggregateHandleConfirmDeviceMetadataUpdate(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(cfg.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -187,12 +187,12 @@ func TestRequestHandlerConfirmDeviceMetadataUpdate(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, config.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(config.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -220,7 +220,7 @@ func TestRequestHandlerConfirmDeviceMetadataUpdate(t *testing.T) { } require.NoError(t, err) if tt.want != nil { - assert.Equal(t, tt.want.AuditContext, response.AuditContext) + assert.Equal(t, tt.want.GetAuditContext(), response.GetAuditContext()) } } t.Run(tt.name, tfunc) diff --git a/resource-aggregate/service/deleteDevices.go b/resource-aggregate/service/deleteDevices.go index 849f728cd..df4c77ffe 100644 --- a/resource-aggregate/service/deleteDevices.go +++ b/resource-aggregate/service/deleteDevices.go @@ -13,7 +13,7 @@ import ( func getUniqueDeviceIdsFromDeleteRequest(request *commands.DeleteDevicesRequest) []string { deviceIDs := make(strings.Set) - for _, deviceID := range request.DeviceIds { + for _, deviceID := range request.GetDeviceIds() { if deviceID != "" { deviceIDs.Add(deviceID) } diff --git a/resource-aggregate/service/deleteDevices_test.go b/resource-aggregate/service/deleteDevices_test.go index efab95c4d..0f24b59dd 100644 --- a/resource-aggregate/service/deleteDevices_test.go +++ b/resource-aggregate/service/deleteDevices_test.go @@ -139,10 +139,10 @@ func TestRequestHandler_DeleteDevices(t *testing.T) { } require.NoError(t, err) if tt.want != nil { - sort.Strings(tt.want.DeviceIds) - sort.Strings(response.DeviceIds) - require.Equal(t, tt.want.DeviceIds, response.DeviceIds) - require.Equal(t, tt.want.AuditContext, response.AuditContext) + sort.Strings(tt.want.GetDeviceIds()) + sort.Strings(response.GetDeviceIds()) + require.Equal(t, tt.want.GetDeviceIds(), response.GetDeviceIds()) + require.Equal(t, tt.want.GetAuditContext(), response.GetAuditContext()) } }) } diff --git a/resource-aggregate/service/grpcApi.go b/resource-aggregate/service/grpcApi.go index 5e8f8798a..fcecaf4ff 100644 --- a/resource-aggregate/service/grpcApi.go +++ b/resource-aggregate/service/grpcApi.go @@ -118,7 +118,7 @@ func (r RequestHandler) PublishResourceLinks(ctx context.Context, request *comma return nil, log.LogAndReturnError(cannotValidateAccessError(err)) } - resID := commands.NewResourceID(request.DeviceId, commands.ResourceLinksHref) + resID := commands.NewResourceID(request.GetDeviceId(), commands.ResourceLinksHref) aggregate, err := NewAggregate(resID, r.eventstore, NewResourceLinksFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry)) if err != nil { return nil, log.LogAndReturnError(grpc.ForwardErrorf(codes.InvalidArgument, "cannot publish resource links: %v", err)) @@ -139,7 +139,7 @@ func newPublishResourceLinksResponse(events []eventstore.Event, deviceID string, if rlp, ok := event.(*raEvents.ResourceLinksPublished); ok { return &commands.PublishResourceLinksResponse{ AuditContext: auditContext, - PublishedResources: rlp.Resources, + PublishedResources: rlp.GetResources(), DeviceId: deviceID, } } @@ -156,7 +156,7 @@ func (r RequestHandler) UnpublishResourceLinks(ctx context.Context, request *com return nil, log.LogAndReturnError(cannotValidateAccessError(err)) } - resID := commands.NewResourceID(request.DeviceId, commands.ResourceLinksHref) + resID := commands.NewResourceID(request.GetDeviceId(), commands.ResourceLinksHref) aggregate, err := NewAggregate(resID, r.eventstore, NewResourceLinksFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry)) if err != nil { return nil, log.LogAndReturnError(grpc.ForwardErrorf(codes.InvalidArgument, "cannot unpublish resource links: %v", err)) @@ -193,7 +193,7 @@ func newUnpublishResourceLinksResponse(events []eventstore.Event, deviceID strin if rlu, ok := event.(*raEvents.ResourceLinksUnpublished); ok { return &commands.UnpublishResourceLinksResponse{ AuditContext: auditContext, - UnpublishedHrefs: rlu.Hrefs, + UnpublishedHrefs: rlu.GetHrefs(), DeviceId: deviceID, } } @@ -205,7 +205,7 @@ func newUnpublishResourceLinksResponse(events []eventstore.Event, deviceID strin } func (r RequestHandler) notifyResourceChanged(ctx context.Context, request *commands.NotifyResourceChangedRequest, userID, owner string) error { - aggregate, err := NewAggregate(request.ResourceId, r.eventstore, NewResourceStateFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry)) + aggregate, err := NewResourceAggregate(request.GetResourceId(), r.eventstore, NewResourceStateFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry), len(request.GetResourceTypes()) == 0) if err != nil { return log.LogAndReturnError(grpc.ForwardErrorf(codes.InvalidArgument, "cannot notify about resource content change: %v", err)) } @@ -243,7 +243,7 @@ func (r RequestHandler) UpdateResource(ctx context.Context, request *commands.Up } request.TimeToLive = checkTimeToLiveForDefault(r.config.Clients.Eventstore.DefaultCommandTimeToLive, request.GetTimeToLive()) - aggregate, err := NewAggregate(request.ResourceId, r.eventstore, NewResourceStateFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry)) + aggregate, err := NewResourceAggregate(request.GetResourceId(), r.eventstore, NewResourceStateFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry), true) if err != nil { return nil, log.LogAndReturnError(grpc.ForwardErrorf(codes.InvalidArgument, "cannot update resource content: %v", err)) } @@ -275,7 +275,7 @@ func (r RequestHandler) ConfirmResourceUpdate(ctx context.Context, request *comm if err != nil { return nil, log.LogAndReturnError(cannotValidateAccessError(err)) } - aggregate, err := NewAggregate(request.ResourceId, r.eventstore, NewResourceStateFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry)) + aggregate, err := NewResourceAggregate(request.GetResourceId(), r.eventstore, NewResourceStateFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry), true) if err != nil { return nil, log.LogAndReturnError(grpc.ForwardErrorf(codes.InvalidArgument, "cannot confirm resource content update: %v", err)) } @@ -299,7 +299,7 @@ func (r RequestHandler) RetrieveResource(ctx context.Context, request *commands. } request.TimeToLive = checkTimeToLiveForDefault(r.config.Clients.Eventstore.DefaultCommandTimeToLive, request.GetTimeToLive()) - aggregate, err := NewAggregate(request.ResourceId, r.eventstore, NewResourceStateFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry)) + aggregate, err := NewResourceAggregate(request.GetResourceId(), r.eventstore, NewResourceStateFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry), true) if err != nil { return nil, log.LogAndReturnError(grpc.ForwardErrorf(codes.InvalidArgument, "cannot retrieve resource content: %v", err)) } @@ -331,7 +331,7 @@ func (r RequestHandler) ConfirmResourceRetrieve(ctx context.Context, request *co if err != nil { return nil, log.LogAndReturnError(cannotValidateAccessError(err)) } - aggregate, err := NewAggregate(request.ResourceId, r.eventstore, NewResourceStateFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry)) + aggregate, err := NewResourceAggregate(request.GetResourceId(), r.eventstore, NewResourceStateFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry), true) if err != nil { return nil, log.LogAndReturnError(grpc.ForwardErrorf(codes.InvalidArgument, "ccannot confirm resource content retrieve: %v", err)) } @@ -356,7 +356,7 @@ func (r RequestHandler) DeleteResource(ctx context.Context, request *commands.De } request.TimeToLive = checkTimeToLiveForDefault(r.config.Clients.Eventstore.DefaultCommandTimeToLive, request.GetTimeToLive()) - aggregate, err := NewAggregate(request.ResourceId, r.eventstore, NewResourceStateFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry)) + aggregate, err := NewResourceAggregate(request.GetResourceId(), r.eventstore, NewResourceStateFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry), true) if err != nil { return nil, log.LogAndReturnError(grpc.ForwardErrorf(codes.InvalidArgument, "cannot delete resource: %v", err)) } @@ -389,7 +389,7 @@ func (r RequestHandler) ConfirmResourceDelete(ctx context.Context, request *comm return nil, log.LogAndReturnError(cannotValidateAccessError(err)) } - aggregate, err := NewAggregate(request.ResourceId, r.eventstore, NewResourceStateFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry)) + aggregate, err := NewResourceAggregate(request.GetResourceId(), r.eventstore, NewResourceStateFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry), true) if err != nil { return nil, log.LogAndReturnError(grpc.ForwardErrorf(codes.InvalidArgument, "cannot confirm resource deletion: %v", err)) } @@ -414,7 +414,7 @@ func (r RequestHandler) CreateResource(ctx context.Context, request *commands.Cr } request.TimeToLive = checkTimeToLiveForDefault(r.config.Clients.Eventstore.DefaultCommandTimeToLive, request.GetTimeToLive()) - aggregate, err := NewAggregate(request.ResourceId, r.eventstore, NewResourceStateFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry)) + aggregate, err := NewResourceAggregate(request.GetResourceId(), r.eventstore, NewResourceStateFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry), true) if err != nil { return nil, log.LogAndReturnError(grpc.ForwardErrorf(codes.InvalidArgument, "cannot create resource: %v", err)) } @@ -447,7 +447,7 @@ func (r RequestHandler) ConfirmResourceCreate(ctx context.Context, request *comm return nil, log.LogAndReturnError(cannotValidateAccessError(err)) } - aggregate, err := NewAggregate(request.ResourceId, r.eventstore, NewResourceStateFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry)) + aggregate, err := NewResourceAggregate(request.GetResourceId(), r.eventstore, NewResourceStateFactoryModel(userID, owner, r.config.HubID), cqrsAggregate.NewDefaultRetryFunc(r.config.Clients.Eventstore.ConcurrencyExceptionMaxRetry), true) if err != nil { return nil, log.LogAndReturnError(grpc.ForwardErrorf(codes.InvalidArgument, "cannot confirm resource creation: %v", err)) } diff --git a/resource-aggregate/service/grpcApi_test.go b/resource-aggregate/service/grpcApi_test.go index bd3441083..50ae3c3c7 100644 --- a/resource-aggregate/service/grpcApi_test.go +++ b/resource-aggregate/service/grpcApi_test.go @@ -113,12 +113,12 @@ func TestRequestHandlerPublishResource(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, config.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(config.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -136,12 +136,12 @@ func TestRequestHandlerPublishResource(t *testing.T) { tfunc := func(t *testing.T) { response, err := requestHandler.PublishResourceLinks(ctx, tt.args.request) if tt.wantError { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } if tt.want != nil { - assert.Equal(t, tt.want.AuditContext, response.AuditContext) + require.Equal(t, tt.want.GetAuditContext(), response.GetAuditContext()) } } t.Run(tt.name, tfunc) @@ -232,12 +232,12 @@ func TestRequestHandlerUnpublishResource(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(cfg.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -253,7 +253,7 @@ func TestRequestHandlerUnpublishResource(t *testing.T) { pubReq := testMakePublishResourceRequest(deviceID, []string{href}) _, err = requestHandler.PublishResourceLinks(ctx, pubReq) - assert.NoError(t, err) + require.NoError(t, err) for _, tt := range test { tfunc := func(t *testing.T) { @@ -262,9 +262,9 @@ func TestRequestHandlerUnpublishResource(t *testing.T) { })) response, err := requestHandler.UnpublishResourceLinks(ctx, tt.args.request) if tt.wantError { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } assert.Equal(t, tt.want, response) } @@ -321,12 +321,12 @@ func TestRequestHandlerNotifyResourceChanged(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, config.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(config.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -344,9 +344,9 @@ func TestRequestHandlerNotifyResourceChanged(t *testing.T) { tfunc := func(t *testing.T) { response, err := requestHandler.NotifyResourceChanged(ctx, tt.args.request) if tt.wantError { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } assert.Equal(t, tt.want, response) } @@ -422,12 +422,12 @@ func TestRequestHandlerUpdateResourceContent(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, config.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(config.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -448,7 +448,7 @@ func TestRequestHandlerUpdateResourceContent(t *testing.T) { } response, err := requestHandler.UpdateResource(ctx, tt.args.request) if tt.wantError { - assert.Error(t, err) + require.Error(t, err) return } require.NoError(t, err) @@ -519,12 +519,12 @@ func TestRequestHandlerConfirmResourceUpdate(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, config.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(config.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -547,9 +547,9 @@ func TestRequestHandlerConfirmResourceUpdate(t *testing.T) { tfunc := func(t *testing.T) { response, err := requestHandler.ConfirmResourceUpdate(ctx, tt.args.request) if tt.wantError { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } assert.Equal(t, tt.want, response) } @@ -614,12 +614,12 @@ func TestRequestHandlerRetrieveResource(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, config.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(config.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -640,7 +640,7 @@ func TestRequestHandlerRetrieveResource(t *testing.T) { } response, err := requestHandler.RetrieveResource(ctx, tt.args.request) if tt.wantError { - assert.Error(t, err) + require.Error(t, err) return } require.NoError(t, err) @@ -711,12 +711,12 @@ func TestRequestHandlerConfirmResourceRetrieve(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, config.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(config.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -742,9 +742,9 @@ func TestRequestHandlerConfirmResourceRetrieve(t *testing.T) { } response, err := requestHandler.ConfirmResourceRetrieve(ctx, tt.args.request) if tt.wantError { - assert.Error(t, err) + require.Error(t, err) } else { - assert.NoError(t, err) + require.NoError(t, err) } assert.Equal(t, tt.want, response) } @@ -809,12 +809,12 @@ func TestRequestHandlerDeleteResource(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, config.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(config.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -836,7 +836,7 @@ func TestRequestHandlerDeleteResource(t *testing.T) { } response, err := requestHandler.DeleteResource(ctx, tt.args.request) if tt.wantError { - assert.Error(t, err) + require.Error(t, err) return } require.NoError(t, err) @@ -907,12 +907,12 @@ func TestRequestHandlerConfirmResourceDelete(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, config.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(config.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -938,7 +938,7 @@ func TestRequestHandlerConfirmResourceDelete(t *testing.T) { } response, err := requestHandler.ConfirmResourceDelete(ctx, tt.args.request) if tt.wantError { - assert.Error(t, err) + require.Error(t, err) return } require.NoError(t, err) @@ -1005,12 +1005,12 @@ func TestRequestHandlerCreateResource(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, config.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(config.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -1032,7 +1032,7 @@ func TestRequestHandlerCreateResource(t *testing.T) { } response, err := requestHandler.CreateResource(ctx, tt.args.request) if tt.wantError { - assert.Error(t, err) + require.Error(t, err) return } require.NoError(t, err) @@ -1103,12 +1103,12 @@ func TestRequestHandlerConfirmResourceCreate(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, config.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(config.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -1134,7 +1134,7 @@ func TestRequestHandlerConfirmResourceCreate(t *testing.T) { } response, err := requestHandler.ConfirmResourceCreate(ctx, tt.args.request) if tt.wantError { - assert.Error(t, err) + require.Error(t, err) return } require.NoError(t, err) diff --git a/resource-aggregate/service/service.go b/resource-aggregate/service/service.go index 059ae09f1..bd96873e4 100644 --- a/resource-aggregate/service/service.go +++ b/resource-aggregate/service/service.go @@ -164,7 +164,7 @@ func NewService(ctx context.Context, config Config, fileWatcher *fsnotify.Watche serviceHeartbeat := NewServiceHeartbeat(config, eventStore, publisher, logger) grpcServer.AddCloseFunc(serviceHeartbeat.Close) - requestHandler := NewRequestHandler(config, eventStore, publisher, func(getCtx context.Context, owner string, deviceIDs []string) ([]string, error) { + requestHandler := NewRequestHandler(config, eventStore, publisher, func(getCtx context.Context, _ string, deviceIDs []string) ([]string, error) { getAllDevices := len(deviceIDs) == 0 if !getAllDevices { return ownerCache.GetSelectedDevices(getCtx, deviceIDs) diff --git a/resource-aggregate/service/updateDeviceMetadata.go b/resource-aggregate/service/updateDeviceMetadata.go index 9df379bc6..74cdea693 100644 --- a/resource-aggregate/service/updateDeviceMetadata.go +++ b/resource-aggregate/service/updateDeviceMetadata.go @@ -70,7 +70,7 @@ func (r RequestHandler) updateDeviceMetadata(ctx context.Context, request *comma resID := commands.NewResourceID(request.GetDeviceId(), commands.StatusHref) var latestSnapshot *events.DeviceMetadataSnapshotTakenForCommand - deviceMetadataFactoryModel := func(ctx context.Context) (cqrsAggregate.AggregateModel, error) { + deviceMetadataFactoryModel := func(context.Context, string, string) (cqrsAggregate.AggregateModel, error) { latestSnapshot = events.NewDeviceMetadataSnapshotTakenForCommand(userID, owner, r.config.HubID) return latestSnapshot, nil } diff --git a/resource-aggregate/service/updateDeviceMetadata_test.go b/resource-aggregate/service/updateDeviceMetadata_test.go index 5ca336f71..3f8077b0d 100644 --- a/resource-aggregate/service/updateDeviceMetadata_test.go +++ b/resource-aggregate/service/updateDeviceMetadata_test.go @@ -20,7 +20,6 @@ import ( "github.com/plgd-dev/hub/v2/resource-aggregate/service" raTest "github.com/plgd-dev/hub/v2/resource-aggregate/test" "github.com/plgd-dev/hub/v2/test/config" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/trace/noop" "google.golang.org/grpc/codes" @@ -105,12 +104,12 @@ func TestAggregateHandleUpdateDeviceMetadata(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, cfg.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(cfg.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -119,7 +118,7 @@ func TestAggregateHandleUpdateDeviceMetadata(t *testing.T) { naClient.Close() }() - assert.NoError(t, err) + require.NoError(t, err) for _, tt := range test { tfunc := func(t *testing.T) { ag, err := service.NewAggregate(commands.NewResourceID(tt.args.request.GetDeviceId(), commands.StatusHref), eventstore, service.NewDeviceMetadataFactoryModel(userID, owner, cfg.HubID), cqrsAggregate.NewDefaultRetryFunc(1)) @@ -129,7 +128,7 @@ func TestAggregateHandleUpdateDeviceMetadata(t *testing.T) { require.Error(t, err) s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) require.True(t, ok) - assert.Equal(t, tt.want, s.Code()) + require.Equal(t, tt.want, s.Code()) return } require.NoError(t, err) @@ -235,12 +234,12 @@ func TestRequestHandlerUpdateDeviceMetadata(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, config.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(config.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -263,12 +262,12 @@ func TestRequestHandlerUpdateDeviceMetadata(t *testing.T) { } require.NoError(t, err) if tt.want != nil { - assert.Equal(t, tt.want.AuditContext, response.AuditContext) + require.Equal(t, tt.want.GetAuditContext(), response.GetAuditContext()) } if tt.want.GetValidUntil() == 0 { - assert.Equal(t, tt.want.ValidUntil, response.GetValidUntil()) + require.Equal(t, tt.want.GetValidUntil(), response.GetValidUntil()) } else { - assert.Less(t, tt.want.ValidUntil, response.GetValidUntil()) + require.Less(t, tt.want.GetValidUntil(), response.GetValidUntil()) } time.Sleep(tt.args.sleep) } diff --git a/resource-aggregate/service/updateServiceHeartbeat.go b/resource-aggregate/service/updateServiceHeartbeat.go index 84ee82dc0..83e0b6a74 100644 --- a/resource-aggregate/service/updateServiceHeartbeat.go +++ b/resource-aggregate/service/updateServiceHeartbeat.go @@ -2,6 +2,7 @@ package service import ( "context" + "errors" "fmt" "sync" "time" @@ -197,7 +198,7 @@ func (s *ServiceHeartbeat) updateServiceMetadata(aggregate *Aggregate, r UpdateS func (s *ServiceHeartbeat) processRequest(r UpdateServiceMetadataReqResp) time.Time { resID := commands.NewResourceID(s.config.HubID, commands.ServicesResourceHref) var snapshot *events.ServiceMetadataSnapshotTakenForCommand - newServicesMetadataFactoryModelFunc := func(ctx context.Context) (cqrsAggregate.AggregateModel, error) { + newServicesMetadataFactoryModelFunc := func(context.Context, string, string) (cqrsAggregate.AggregateModel, error) { snapshot = events.NewServiceMetadataSnapshotTakenForCommand(ServiceUserID, ServiceUserID, s.config.HubID) return snapshot, nil } @@ -336,10 +337,10 @@ func (s *ServiceHeartbeat) pop() []UpdateServiceMetadataReqResp { func (s *ServiceHeartbeat) ProcessRequest(r UpdateServiceMetadataReqResp) error { if r.Request == nil { - return fmt.Errorf("invalid request") + return errors.New("invalid request") } if r.ResponseChan == nil { - return fmt.Errorf("invalid response channel") + return errors.New("invalid response channel") } s.push(r) s.wakeUp() @@ -372,7 +373,7 @@ func (s *ServiceHeartbeat) updateDeviceToExpired(ctx context.Context, serviceID, resID := commands.NewResourceID(deviceID, commands.StatusHref) var latestSnapshot *events.DeviceMetadataSnapshotTakenForCommand - deviceMetadataFactoryModel := func(ctx context.Context) (cqrsAggregate.AggregateModel, error) { + deviceMetadataFactoryModel := func(context.Context, string, string) (cqrsAggregate.AggregateModel, error) { latestSnapshot = events.NewDeviceMetadataSnapshotTakenForCommand(userID, "", s.config.HubID) return latestSnapshot, nil } diff --git a/resource-aggregate/service/updateServiceHeartbeat_test.go b/resource-aggregate/service/updateServiceHeartbeat_test.go index d97a03997..88aa7b3f5 100644 --- a/resource-aggregate/service/updateServiceHeartbeat_test.go +++ b/resource-aggregate/service/updateServiceHeartbeat_test.go @@ -37,12 +37,12 @@ func TestNewServiceHeartbeat(t *testing.T) { err = eventstore.Clear(ctx) require.NoError(t, err) err = eventstore.Close(ctx) - assert.NoError(t, err) + require.NoError(t, err) eventstore, err = mongodb.New(ctx, config.Clients.Eventstore.Connection.MongoDB, fileWatcher, logger, noop.NewTracerProvider(), mongodb.WithUnmarshaler(utils.Unmarshal), mongodb.WithMarshaler(utils.Marshal)) require.NoError(t, err) defer func() { errC := eventstore.Close(ctx) - assert.NoError(t, errC) + require.NoError(t, errC) }() naClient, publisher, err := natsTest.NewClientAndPublisher(config.Clients.Eventbus.NATS, fileWatcher, logger, publisher.WithMarshaler(utils.Marshal)) require.NoError(t, err) @@ -87,15 +87,15 @@ func TestNewServiceHeartbeat(t *testing.T) { chosen, value, ok := reflect.Select(cases) if ok { if chosen == 0 { - assert.Fail(t, "context canceled") + require.Fail(t, "context canceled") } if chosen != 0 { data := value.Interface().(service.UpdateServiceMetadataResponseChanData) - assert.NoError(t, data.Err) + require.NoError(t, data.Err) } } if !ok { - assert.Fail(t, "channel closed") + require.Fail(t, "channel closed") break } } diff --git a/resource-directory/service/deviceDirectory.go b/resource-directory/service/deviceDirectory.go index 31c4764f0..2e44ccffb 100644 --- a/resource-directory/service/deviceDirectory.go +++ b/resource-directory/service/deviceDirectory.go @@ -1,6 +1,7 @@ package service import ( + "errors" "fmt" "github.com/plgd-dev/device/v2/schema/device" @@ -52,7 +53,7 @@ func NewDeviceDirectory(projection *Projection, deviceIds []string) *DeviceDirec func decodeContent(content *commands.Content, v interface{}) error { if content == nil { - return fmt.Errorf("cannot parse empty content") + return errors.New("cannot parse empty content") } var decoder func([]byte, interface{}) error @@ -131,10 +132,10 @@ func filterDevices(deviceIds strings.Set, deviceIDsFilter []string) strings.Set func (dd *DeviceDirectory) sendDevices(deviceIDs strings.Set, req *pb.GetDevicesRequest, srv pb.GrpcGateway_GetDevicesServer, toReloadDevices strings.Set) (err error) { typeFilter := make(strings.Set) - typeFilter.Add(req.TypeFilter...) + typeFilter.Add(req.GetTypeFilter()...) return dd.projection.LoadDevicesMetadata(deviceIDs, toReloadDevices, func(m *deviceMetadataProjection) error { deviceMetadataUpdated := m.GetDeviceMetadataUpdated() - if !hasMatchingStatus(deviceMetadataUpdated.GetConnection().IsOnline(), req.StatusFilter) { + if !hasMatchingStatus(deviceMetadataUpdated.GetConnection().IsOnline(), req.GetStatusFilter()) { return nil } resourceIdFilter := []*commands.ResourceId{commands.NewResourceID(m.GetDeviceID(), device.ResourceURI)} @@ -160,7 +161,7 @@ func (dd *DeviceDirectory) sendDevices(deviceIDs strings.Set, req *pb.GetDevices } func (dd *DeviceDirectory) GetDevices(req *pb.GetDevicesRequest, srv pb.GrpcGateway_GetDevicesServer) (err error) { - deviceIDs := filterDevices(dd.userDeviceIds, req.DeviceIdFilter) + deviceIDs := filterDevices(dd.userDeviceIds, req.GetDeviceIdFilter()) if len(deviceIDs) == 0 { log.Debug("DeviceDirectory.GetDevices.filterDevices returns empty deviceIDs") return nil diff --git a/resource-directory/service/deviceDirectory_test.go b/resource-directory/service/deviceDirectory_test.go index f2c1d009a..b43de0d09 100644 --- a/resource-directory/service/deviceDirectory_test.go +++ b/resource-directory/service/deviceDirectory_test.go @@ -47,7 +47,7 @@ func TestDeviceDirectoryGetDevices(t *testing.T) { }, wantStatusCode: codes.OK, wantResponse: map[string]*pb.Device{ - ddResource2.Resource.DeviceId: testMakeDeviceResouceProtobuf(ddResource2.Resource.DeviceId, deviceResourceTypes, true), + ddResource2.Resource.GetDeviceId(): testMakeDeviceResouceProtobuf(ddResource2.Resource.GetDeviceId(), deviceResourceTypes, true), }, }, @@ -60,7 +60,7 @@ func TestDeviceDirectoryGetDevices(t *testing.T) { }, wantStatusCode: codes.OK, wantResponse: map[string]*pb.Device{ - ddResource1.Resource.DeviceId: testMakeDeviceResouceProtobuf(ddResource1.Resource.DeviceId, deviceResourceTypes, false), + ddResource1.Resource.GetDeviceId(): testMakeDeviceResouceProtobuf(ddResource1.Resource.GetDeviceId(), deviceResourceTypes, false), }, }, { @@ -72,8 +72,8 @@ func TestDeviceDirectoryGetDevices(t *testing.T) { }, wantStatusCode: codes.OK, wantResponse: map[string]*pb.Device{ - ddResource1.Resource.DeviceId: testMakeDeviceResouceProtobuf(ddResource1.Resource.DeviceId, deviceResourceTypes, false), - ddResource2.Resource.DeviceId: testMakeDeviceResouceProtobuf(ddResource2.Resource.DeviceId, deviceResourceTypes, true), + ddResource1.Resource.GetDeviceId(): testMakeDeviceResouceProtobuf(ddResource1.Resource.GetDeviceId(), deviceResourceTypes, false), + ddResource2.Resource.GetDeviceId(): testMakeDeviceResouceProtobuf(ddResource2.Resource.GetDeviceId(), deviceResourceTypes, true), }, }, { @@ -94,20 +94,20 @@ func TestDeviceDirectoryGetDevices(t *testing.T) { }, wantStatusCode: codes.OK, wantResponse: map[string]*pb.Device{ - ddResource1.Resource.DeviceId: testMakeDeviceResouceProtobuf(ddResource1.Resource.DeviceId, deviceResourceTypes, false), - ddResource2.Resource.DeviceId: testMakeDeviceResouceProtobuf(ddResource2.Resource.DeviceId, deviceResourceTypes, true), + ddResource1.Resource.GetDeviceId(): testMakeDeviceResouceProtobuf(ddResource1.Resource.GetDeviceId(), deviceResourceTypes, false), + ddResource2.Resource.GetDeviceId(): testMakeDeviceResouceProtobuf(ddResource2.Resource.GetDeviceId(), deviceResourceTypes, true), }, }, { name: "project_one_device", args: args{ request: &pb.GetDevicesRequest{ - DeviceIdFilter: []string{ddResource1.Resource.DeviceId}, + DeviceIdFilter: []string{ddResource1.Resource.GetDeviceId()}, }, }, wantStatusCode: codes.OK, wantResponse: map[string]*pb.Device{ - ddResource1.Resource.DeviceId: testMakeDeviceResouceProtobuf(ddResource1.Resource.DeviceId, deviceResourceTypes, false), + ddResource1.Resource.GetDeviceId(): testMakeDeviceResouceProtobuf(ddResource1.Resource.GetDeviceId(), deviceResourceTypes, false), }, }, } @@ -269,20 +269,20 @@ func testCreateResourceDeviceEventstores() (resourceEventStore *mockEvents.MockE resourceEventStore = mockEvents.NewMockEventStore() // without cloud state - resourceEventStore.Append(ddResource0.DeviceId, commands.MakeLinksResourceUUID(ddResource0.DeviceId).String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{ddResource0.Resource}, ddResource0.GetDeviceId(), events.MakeEventMeta("a", 0, 0, "hubID"))) - resourceEventStore.Append(ddResource0.DeviceId, ddResource0.Resource.ToUUID().String(), mockEvents.MakeResourceChangedEvent(ddResource0.Resource.GetResourceID(), ddResource0.Content, events.MakeEventMeta("a", 0, 0, "hubID"), mockEvents.MakeAuditContext("userId", "0"))) + resourceEventStore.Append(ddResource0.GetDeviceId(), commands.MakeLinksResourceUUID(ddResource0.GetDeviceId()).String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{ddResource0.Resource}, ddResource0.GetDeviceId(), events.MakeEventMeta("a", 0, 0, "hubID"))) + resourceEventStore.Append(ddResource0.GetDeviceId(), ddResource0.Resource.ToUUID().String(), mockEvents.MakeResourceChangedEvent(ddResource0.Resource.GetResourceID(), ddResource0.Content, events.MakeEventMeta("a", 0, 0, "hubID"), mockEvents.MakeAuditContext("userId", "0"), []string{"type0"})) - resourceEventStore.Append(ddResource1.DeviceId, commands.MakeLinksResourceUUID(ddResource1.DeviceId).String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{ddResource1.Resource}, ddResource1.GetDeviceId(), events.MakeEventMeta("a", 0, 0, "hubID"))) - resourceEventStore.Append(ddResource1.DeviceId, ddResource1.Resource.ToUUID().String(), mockEvents.MakeResourceChangedEvent(ddResource1.Resource.GetResourceID(), ddResource1.Content, events.MakeEventMeta("a", 0, 0, "hubID"), mockEvents.MakeAuditContext("userId", "0"))) - resourceEventStore.Append(ddResource1Cloud.DeviceId, ddResource1Cloud.AggregateID(), mockEvents.MakeDeviceMetadata(ddResource1Cloud.DeviceId, ddResource1Cloud, events.MakeEventMeta("a", 0, 0, "hubID"))) + resourceEventStore.Append(ddResource1.GetDeviceId(), commands.MakeLinksResourceUUID(ddResource1.GetDeviceId()).String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{ddResource1.Resource}, ddResource1.GetDeviceId(), events.MakeEventMeta("a", 0, 0, "hubID"))) + resourceEventStore.Append(ddResource1.GetDeviceId(), ddResource1.Resource.ToUUID().String(), mockEvents.MakeResourceChangedEvent(ddResource1.Resource.GetResourceID(), ddResource1.Content, events.MakeEventMeta("a", 0, 0, "hubID"), mockEvents.MakeAuditContext("userId", "0"), []string{"type1"})) + resourceEventStore.Append(ddResource1Cloud.GetDeviceId(), ddResource1Cloud.AggregateID(), mockEvents.MakeDeviceMetadata(ddResource1Cloud.GetDeviceId(), ddResource1Cloud, events.MakeEventMeta("a", 0, 0, "hubID"))) // with cloud state - online - resourceEventStore.Append(ddResource2.DeviceId, commands.MakeLinksResourceUUID(ddResource2.DeviceId).String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{ddResource2.Resource}, ddResource2.Resource.GetDeviceId(), events.MakeEventMeta("a", 0, 0, "hubID"))) - resourceEventStore.Append(ddResource2.DeviceId, ddResource2.Resource.ToUUID().String(), mockEvents.MakeResourceChangedEvent(ddResource2.Resource.GetResourceID(), ddResource2.Content, events.MakeEventMeta("a", 0, 0, "hubID"), mockEvents.MakeAuditContext("userId", "0"))) - resourceEventStore.Append(ddResource2Cloud.DeviceId, ddResource2Cloud.AggregateID(), mockEvents.MakeDeviceMetadata(ddResource2Cloud.DeviceId, ddResource2Cloud, events.MakeEventMeta("a", 0, 0, "hubID"))) + resourceEventStore.Append(ddResource2.GetDeviceId(), commands.MakeLinksResourceUUID(ddResource2.GetDeviceId()).String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{ddResource2.Resource}, ddResource2.Resource.GetDeviceId(), events.MakeEventMeta("a", 0, 0, "hubID"))) + resourceEventStore.Append(ddResource2.GetDeviceId(), ddResource2.Resource.ToUUID().String(), mockEvents.MakeResourceChangedEvent(ddResource2.Resource.GetResourceID(), ddResource2.Content, events.MakeEventMeta("a", 0, 0, "hubID"), mockEvents.MakeAuditContext("userId", "0"), []string{"type2"})) + resourceEventStore.Append(ddResource2Cloud.GetDeviceId(), ddResource2Cloud.AggregateID(), mockEvents.MakeDeviceMetadata(ddResource2Cloud.GetDeviceId(), ddResource2Cloud, events.MakeEventMeta("a", 0, 0, "hubID"))) // without device resource - resourceEventStore.Append(ddResource4Cloud.DeviceId, ddResource4Cloud.AggregateID(), mockEvents.MakeDeviceMetadata(ddResource4Cloud.DeviceId, ddResource2Cloud, events.MakeEventMeta("a", 0, 1, "hubID"))) + resourceEventStore.Append(ddResource4Cloud.GetDeviceId(), ddResource4Cloud.AggregateID(), mockEvents.MakeDeviceMetadata(ddResource4Cloud.GetDeviceId(), ddResource2Cloud, events.MakeEventMeta("a", 0, 1, "hubID"))) return resourceEventStore } @@ -300,6 +300,6 @@ func (s *testGrpcGateway_GetDevicesServer) Send(d *pb.Device) error { if s.got == nil { s.got = make(map[string]*pb.Device) } - s.got[d.Id] = d + s.got[d.GetId()] = d return nil } diff --git a/resource-directory/service/getDevicesMetadata_test.go b/resource-directory/service/getDevicesMetadata_test.go index 88ffa2fd5..db36dedf6 100644 --- a/resource-directory/service/getDevicesMetadata_test.go +++ b/resource-directory/service/getDevicesMetadata_test.go @@ -125,7 +125,7 @@ func TestRequestHandlerGetDevicesMetadata(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/resource-directory/service/getDevices_test.go b/resource-directory/service/getDevices_test.go index c3ec3b128..a3e81b10e 100644 --- a/resource-directory/service/getDevices_test.go +++ b/resource-directory/service/getDevices_test.go @@ -48,7 +48,7 @@ func TestRequestHandlerGetDevicesParallel(t *testing.T) { ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) virtualdevice.CreateDevices(ctx, t, numDevices, numResources, test.StringToApplicationProtocol(config.ACTIVE_COAP_SCHEME)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -135,7 +135,7 @@ func TestRequestHandlerGetDevices(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -161,11 +161,12 @@ func TestRequestHandlerGetDevices(t *testing.T) { break } require.NoError(t, err) - assert.NotEmpty(t, dev.ProtocolIndependentId) + assert.NotEmpty(t, dev.GetProtocolIndependentId()) assert.NotEmpty(t, dev.GetData().GetContent().GetData()) dev.ProtocolIndependentId = "" dev.Metadata.Connection.Id = "" dev.Metadata.Connection.ConnectedAt = 0 + dev.Metadata.Connection.LocalEndpoints = nil dev.Metadata.Connection.ServiceId = "" dev.Metadata.TwinSynchronization.SyncingAt = 0 dev.Metadata.TwinSynchronization.InSyncAt = 0 diff --git a/resource-directory/service/getEvents.go b/resource-directory/service/getEvents.go index b76f00d72..c942a38f2 100644 --- a/resource-directory/service/getEvents.go +++ b/resource-directory/service/getEvents.go @@ -285,7 +285,7 @@ func (p *resourceEvent) Handle(ctx context.Context, iter eventstore.Iter) error } func getDeviceQueries(deviceIDFilter []string, userDeviceIDs strings.Set) []eventstore.GetEventsQuery { - var queries []eventstore.GetEventsQuery + queries := make([]eventstore.GetEventsQuery, 0, len(deviceIDFilter)) for _, deviceID := range deviceIDFilter { if _, ok := userDeviceIDs[deviceID]; !ok { log.Debugf("permission denied, device with id %v skipped", deviceID) @@ -299,7 +299,7 @@ func getDeviceQueries(deviceIDFilter []string, userDeviceIDs strings.Set) []even } func getResourceQueries(resourceFilter []*pb.ResourceIdFilter, userDeviceIDs strings.Set) []eventstore.GetEventsQuery { - var queries []eventstore.GetEventsQuery + queries := make([]eventstore.GetEventsQuery, 0, len(resourceFilter)) for _, filter := range resourceFilter { if !userDeviceIDs.HasOneOf(filter.GetResourceId().GetDeviceId()) { log.Debugf("permission denied, resource belonging to device %v skipped", filter.GetResourceId().GetDeviceId()) @@ -314,7 +314,7 @@ func getResourceQueries(resourceFilter []*pb.ResourceIdFilter, userDeviceIDs str } func getUserDeviceQueries(userDeviceIds strings.Set) []eventstore.GetEventsQuery { - var queries []eventstore.GetEventsQuery + queries := make([]eventstore.GetEventsQuery, 0, len(userDeviceIds)) for device := range userDeviceIds { queries = append(queries, eventstore.GetEventsQuery{ GroupID: device, @@ -342,13 +342,13 @@ func (r *RequestHandler) GetEvents(req *pb.GetEventsRequest, srv pb.GrpcGateway_ } // for backward compatibility and http api - req.ResourceIdFilter = append(req.ResourceIdFilter, req.ConvertHTTPResourceIDFilter()...) + req.ResourceIdFilter = append(req.GetResourceIdFilter(), req.ConvertHTTPResourceIDFilter()...) var queries []eventstore.GetEventsQuery - if len(req.DeviceIdFilter) == 0 && len(req.GetResourceIdFilter()) == 0 { + if len(req.GetDeviceIdFilter()) == 0 && len(req.GetResourceIdFilter()) == 0 { queries = getUserDeviceQueries(mapUserDeviceIDs) } else { - queries = getDeviceQueries(req.DeviceIdFilter, mapUserDeviceIDs) + queries = getDeviceQueries(req.GetDeviceIdFilter(), mapUserDeviceIDs) queries = append(queries, getResourceQueries(req.GetResourceIdFilter(), mapUserDeviceIDs)...) if len(queries) == 0 { log.Debugf("None of the filters are satisfied for user %v", owner) @@ -356,7 +356,7 @@ func (r *RequestHandler) GetEvents(req *pb.GetEventsRequest, srv pb.GrpcGateway_ } } - err = r.eventStore.GetEvents(srv.Context(), queries, req.TimestampFilter, &resourceEvent{srv: srv}) + err = r.eventStore.GetEvents(srv.Context(), queries, req.GetTimestampFilter(), &resourceEvent{srv: srv}) if err != nil { return log.LogAndReturnError(status.Errorf(status.Convert(err).Code(), "cannot get events: %v", err)) } diff --git a/resource-directory/service/getEventsSnapshot_test.go b/resource-directory/service/getEventsSnapshot_test.go index 5e265f62c..8a5ca80e9 100644 --- a/resource-directory/service/getEventsSnapshot_test.go +++ b/resource-directory/service/getEventsSnapshot_test.go @@ -51,7 +51,7 @@ func TestRequestHandlerGetEventsStateSnapshot(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -106,8 +106,9 @@ func TestRequestHandlerGetEventsStateSnapshot(t *testing.T) { case *events.ResourceStateSnapshotTaken: pbTest.CmpResourceStateSnapshotTaken(t, &events.ResourceStateSnapshotTaken{ ResourceId: commands.NewResourceID(deviceID, lightHref), - LatestResourceChange: pbTest.MakeResourceChanged(t, deviceID, lightHref, "", makeLightData(0)), + LatestResourceChange: pbTest.MakeResourceChanged(t, deviceID, lightHref, test.TestResourceLightInstanceResourceTypes, "", makeLightData(0)), AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + ResourceTypes: test.TestResourceLightInstanceResourceTypes, }, event) default: assert.Fail(t, "unexpected event", "event: %v", ev) diff --git a/resource-directory/service/getEvents_test.go b/resource-directory/service/getEvents_test.go index ec346e0c1..87b84f743 100644 --- a/resource-directory/service/getEvents_test.go +++ b/resource-directory/service/getEvents_test.go @@ -52,8 +52,9 @@ func getAllOnboardEvents(t *testing.T, deviceID string, links []schema.ResourceL Type: &pb.GetEventsResponse_ResourceStateSnapshotTaken{ ResourceStateSnapshotTaken: &events.ResourceStateSnapshotTaken{ ResourceId: rid, - LatestResourceChange: pbTest.MakeResourceChanged(t, deviceID, rid.GetHref(), "", r.Representation), + LatestResourceChange: pbTest.MakeResourceChanged(t, deviceID, rid.GetHref(), r.ResourceTypes, "", r.Representation), AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, "", oauthService.DeviceUserID), + ResourceTypes: r.ResourceTypes, }, }, }) @@ -83,7 +84,7 @@ func TestRequestHandlerGetEventsOnOnboard(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/resource-directory/service/getHubConfiguration_test.go b/resource-directory/service/getHubConfiguration_test.go index 10ff190cb..b93961302 100644 --- a/resource-directory/service/getHubConfiguration_test.go +++ b/resource-directory/service/getHubConfiguration_test.go @@ -35,7 +35,7 @@ func TestRequestHandlerGetHubConfiguration(t *testing.T) { tearDown := service.SetUp(ctx, t) defer tearDown() - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -52,9 +52,9 @@ func TestRequestHandlerGetHubConfiguration(t *testing.T) { require.Error(t, err) } else { require.NoError(t, err) - require.NotEmpty(t, got.CertificateAuthorities) + require.NotEmpty(t, got.GetCertificateAuthorities()) got.CertificateAuthorities = "" - require.NotEqual(t, int64(0), got.CurrentTime) + require.NotEqual(t, int64(0), got.GetCurrentTime()) got.CurrentTime = 0 test.CheckProtobufs(t, tt.want, got, test.RequireToCheckFunc(require.Equal)) } diff --git a/resource-directory/service/getPendingCommands_test.go b/resource-directory/service/getPendingCommands_test.go index 5193f3573..b99d882fd 100644 --- a/resource-directory/service/getPendingCommands_test.go +++ b/resource-directory/service/getPendingCommands_test.go @@ -69,7 +69,8 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { "power": 1, }), }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceLightInstanceResourceTypes, }, }, }, @@ -101,7 +102,8 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { DeviceId: deviceID, Href: platform.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: []string{platform.ResourceType}, }, }, }, @@ -119,7 +121,8 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { "power": 1, }), }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceDeviceResourceTypes, }, }, }, @@ -130,7 +133,8 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { DeviceId: deviceID, Href: device.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceDeviceResourceTypes, }, }, }, @@ -148,7 +152,8 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { "power": 1, }), }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceLightInstanceResourceTypes, }, }, }, @@ -169,7 +174,8 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { DeviceId: deviceID, Href: platform.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: []string{platform.ResourceType}, }, }, }, @@ -197,7 +203,8 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { "power": 1, }), }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceDeviceResourceTypes, }, }, }, @@ -218,7 +225,8 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { DeviceId: deviceID, Href: device.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceDeviceResourceTypes, }, }, }, @@ -246,7 +254,8 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { "power": 1, }), }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceLightInstanceResourceTypes, }, }, }, @@ -274,7 +283,8 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { "power": 1, }), }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceDeviceResourceTypes, }, }, }, @@ -285,7 +295,8 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { DeviceId: deviceID, Href: device.ResourceURI, }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, "", service.DeviceUserID), + ResourceTypes: test.TestResourceDeviceResourceTypes, }, }, }, @@ -335,7 +346,7 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -350,9 +361,9 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { secureGWShutdown() createFn := func(timeToLive time.Duration) { - ctx, cancel := context.WithTimeout(ctx, time.Second) + createCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.CreateResource(ctx, &pb.CreateResourceRequest{ + _, errC := c.CreateResource(createCtx, &pb.CreateResourceRequest{ ResourceId: commands.NewResourceID(deviceID, device.ResourceURI), Content: &pb.Content{ ContentType: message.AppOcfCbor.String(), @@ -362,27 +373,27 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { }, TimeToLive: int64(timeToLive), }) - require.Error(t, err) + require.Error(t, errC) } createFn(time.Millisecond * 500) // for test expired event createFn(0) retrieveFn := func(timeToLive time.Duration) { - ctx, cancel := context.WithTimeout(ctx, time.Second) + retrieveCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.GetResourceFromDevice(ctx, &pb.GetResourceFromDeviceRequest{ + _, errG := c.GetResourceFromDevice(retrieveCtx, &pb.GetResourceFromDeviceRequest{ ResourceId: commands.NewResourceID(deviceID, platform.ResourceURI), TimeToLive: int64(timeToLive), }) - require.Error(t, err) + require.Error(t, errG) } retrieveFn(time.Millisecond * 500) // for test expired event retrieveFn(0) updateFn := func(timeToLive time.Duration) { - ctx, cancel := context.WithTimeout(ctx, time.Second) + updateCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.UpdateResource(ctx, &pb.UpdateResourceRequest{ + _, errU := c.UpdateResource(updateCtx, &pb.UpdateResourceRequest{ ResourceId: commands.NewResourceID(deviceID, test.TestResourceLightInstanceHref("1")), Content: &pb.Content{ ContentType: message.AppOcfCbor.String(), @@ -392,32 +403,32 @@ func TestRequestHandlerGetPendingCommands(t *testing.T) { }, TimeToLive: int64(timeToLive), }) - require.Error(t, err) + require.Error(t, errU) } updateFn(time.Millisecond * 500) // for test expired event updateFn(0) deleteFn := func(timeToLive time.Duration) { - ctx, cancel := context.WithTimeout(ctx, time.Second) + deleteCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.DeleteResource(ctx, &pb.DeleteResourceRequest{ + _, errD := c.DeleteResource(deleteCtx, &pb.DeleteResourceRequest{ ResourceId: commands.NewResourceID(deviceID, device.ResourceURI), TimeToLive: int64(timeToLive), }) - require.Error(t, err) + require.Error(t, errD) } deleteFn(time.Millisecond * 500) // for test expired event deleteFn(0) updateDeviceMetadata := func(timeToLive time.Duration) { - ctx, cancel := context.WithTimeout(ctx, time.Second) + updateCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.UpdateDeviceMetadata(ctx, &pb.UpdateDeviceMetadataRequest{ + _, errU := c.UpdateDeviceMetadata(updateCtx, &pb.UpdateDeviceMetadataRequest{ DeviceId: deviceID, TwinEnabled: false, TimeToLive: int64(timeToLive), }) - require.Error(t, err) + require.Error(t, errU) } updateDeviceMetadata(time.Millisecond * 500) // for test expired event updateDeviceMetadata(0) // for test expired event diff --git a/resource-directory/service/getResourceLinks_test.go b/resource-directory/service/getResourceLinks_test.go index b6fb18c75..5a622a298 100644 --- a/resource-directory/service/getResourceLinks_test.go +++ b/resource-directory/service/getResourceLinks_test.go @@ -56,7 +56,7 @@ func TestRequestHandlerGetResourceLinks(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/resource-directory/service/getResources_test.go b/resource-directory/service/getResources_test.go index 86b74bb2c..f0ac7a214 100644 --- a/resource-directory/service/getResources_test.go +++ b/resource-directory/service/getResources_test.go @@ -46,7 +46,7 @@ func TestRequestHandlerGetResources(t *testing.T) { want: []*pb.Resource{ { Types: []string{types.CORE_LIGHT}, - Data: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceLightInstanceHref("1"), "", + Data: pbTest.MakeResourceChanged(t, deviceID, test.TestResourceLightInstanceHref("1"), test.TestResourceLightInstanceResourceTypes, "", map[string]interface{}{ "state": false, "power": uint64(0), @@ -65,7 +65,7 @@ func TestRequestHandlerGetResources(t *testing.T) { defer tearDown() ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/resource-directory/service/grpcApi.go b/resource-directory/service/grpcApi.go index ca473238c..14a9765d4 100644 --- a/resource-directory/service/grpcApi.go +++ b/resource-directory/service/grpcApi.go @@ -195,7 +195,7 @@ func NewRequestHandler( } func NewEventStoreModelFactory() func(context.Context, string, string) (eventstore.Model, error) { - return func(ctx context.Context, deviceID, resourceID string) (eventstore.Model, error) { + return func(_ context.Context, deviceID, resourceID string) (eventstore.Model, error) { switch resourceID { case commands.MakeLinksResourceUUID(deviceID).String(): return NewResourceLinksProjection(deviceID), nil diff --git a/resource-directory/service/projection.go b/resource-directory/service/projection.go index 73e408e9f..cde5507b1 100644 --- a/resource-directory/service/projection.go +++ b/resource-directory/service/projection.go @@ -151,14 +151,14 @@ func (p *Projection) wantToReloadDevice(rl *resourceLinksProjection, hrefFilter if len(hrefFilter) > 0 && !hrefFilter[res.GetHref()] { return true } - if !hasMatchingType(res.ResourceTypes, typeFilter) { + if !hasMatchingType(res.GetResourceTypes(), typeFilter) { return true } reload := true p.Models(func(eventstore.Model) (wantNext bool) { reload = false return true - }, commands.NewResourceID(rl.GetDeviceID(), res.Href)) + }, commands.NewResourceID(rl.GetDeviceID(), res.GetHref())) if reload { finalReload = true return false @@ -173,7 +173,7 @@ func (p *Projection) loadResourceWithLinks(deviceID string, hrefFilter map[strin if len(hrefFilter) > 0 && !hrefFilter[res.GetHref()] { return false } - if !hasMatchingType(res.ResourceTypes, typeFilter) { + if !hasMatchingType(res.GetResourceTypes(), typeFilter) { return false } return true @@ -204,7 +204,7 @@ func (p *Projection) loadResourceWithLinks(deviceID string, hrefFilter map[strin Resource: res, }) return err == nil - }, commands.NewResourceID(rl.GetDeviceID(), res.Href)) + }, commands.NewResourceID(rl.GetDeviceID(), res.GetHref())) return true }) return err diff --git a/resource-directory/service/resourceDirectory.go b/resource-directory/service/resourceDirectory.go index e85466553..045d8fb9d 100644 --- a/resource-directory/service/resourceDirectory.go +++ b/resource-directory/service/resourceDirectory.go @@ -35,14 +35,14 @@ func (rd *ResourceDirectory) sendResourceLinks(srv pb.GrpcGateway_GetResourceLin } func (rd *ResourceDirectory) GetResourceLinks(in *pb.GetResourceLinksRequest, srv pb.GrpcGateway_GetResourceLinksServer) error { - deviceIDs := filterDevices(rd.userDeviceIds, in.DeviceIdFilter) + deviceIDs := filterDevices(rd.userDeviceIds, in.GetDeviceIdFilter()) if len(deviceIDs) == 0 { log.Debug("ResourceDirectory.GetResourceLinks.filterDevices returns empty deviceIDs") return nil } typeFilter := make(strings.Set) - typeFilter.Add(in.TypeFilter...) + typeFilter.Add(in.GetTypeFilter()...) toReloadDevices := make(strings.Set) err := rd.sendResourceLinks(srv, deviceIDs, typeFilter, toReloadDevices) diff --git a/resource-directory/service/resourceDirectory_test.go b/resource-directory/service/resourceDirectory_test.go index 02218cfdd..64e0a116c 100644 --- a/resource-directory/service/resourceDirectory_test.go +++ b/resource-directory/service/resourceDirectory_test.go @@ -20,7 +20,6 @@ import ( "github.com/plgd-dev/hub/v2/test" "github.com/plgd-dev/hub/v2/test/config" pbTest "github.com/plgd-dev/hub/v2/test/pb" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc" ) @@ -86,7 +85,7 @@ func TestResourceDirectoryGetResourceLinks(t *testing.T) { var s testGrpcGateway_GetResourceLinksServer err := rd.GetResourceLinks(tt.args.request, &s) require.NoError(t, err) - test.CheckProtobufs(t, tt.want, s.got, test.AssertToCheckFunc(assert.Equal)) + test.CheckProtobufs(t, tt.want, s.got, test.RequireToCheckFunc(require.Equal)) } t.Run(tt.name, fn) } @@ -112,10 +111,10 @@ func testCreateEventstore() *mockEvents.MockEventStore { store.Append(Resource1.DeviceId, commands.MakeLinksResourceUUID(Resource1.DeviceId).String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{Resource1.Resource}, Resource1.GetDeviceId(), events.MakeEventMeta("a", 0, 0, "hubID"))) store.Append(Resource2.DeviceId, commands.MakeLinksResourceUUID(Resource2.DeviceId).String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{Resource2.Resource}, Resource2.GetDeviceId(), events.MakeEventMeta("a", 0, 0, "hubID"))) store.Append(Resource3.DeviceId, commands.MakeLinksResourceUUID(Resource3.DeviceId).String(), mockEvents.MakeResourceLinksPublishedEvent([]*commands.Resource{Resource3.Resource}, Resource3.GetDeviceId(), events.MakeEventMeta("a", 0, 0, "hubID"))) - store.Append(Resource0.DeviceId, Resource0.ToUUID().String(), mockEvents.MakeResourceChangedEvent(Resource0.Resource.GetResourceID(), Resource0.Content, events.MakeEventMeta("a", 0, 0, "hubID"), mockEvents.MakeAuditContext("userId", "0"))) - store.Append(Resource1.DeviceId, Resource1.ToUUID().String(), mockEvents.MakeResourceChangedEvent(Resource1.Resource.GetResourceID(), Resource1.Content, events.MakeEventMeta("a", 0, 0, "hubID"), mockEvents.MakeAuditContext("userId", "0"))) - store.Append(Resource2.DeviceId, Resource2.ToUUID().String(), mockEvents.MakeResourceChangedEvent(Resource2.Resource.GetResourceID(), Resource2.Content, events.MakeEventMeta("a", 0, 0, "hubID"), mockEvents.MakeAuditContext("userId", "0"))) - store.Append(Resource3.DeviceId, Resource3.ToUUID().String(), mockEvents.MakeResourceChangedEvent(Resource3.Resource.GetResourceID(), Resource3.Content, events.MakeEventMeta("a", 0, 0, "hubID"), mockEvents.MakeAuditContext("userId", "0"))) + store.Append(Resource0.DeviceId, Resource0.ToUUID().String(), mockEvents.MakeResourceChangedEvent(Resource0.Resource.GetResourceID(), Resource0.Content, events.MakeEventMeta("a", 0, 0, "hubID"), mockEvents.MakeAuditContext("userId", "0"), Resource0.ResourceTypes)) + store.Append(Resource1.DeviceId, Resource1.ToUUID().String(), mockEvents.MakeResourceChangedEvent(Resource1.Resource.GetResourceID(), Resource1.Content, events.MakeEventMeta("a", 0, 0, "hubID"), mockEvents.MakeAuditContext("userId", "0"), Resource1.ResourceTypes)) + store.Append(Resource2.DeviceId, Resource2.ToUUID().String(), mockEvents.MakeResourceChangedEvent(Resource2.Resource.GetResourceID(), Resource2.Content, events.MakeEventMeta("a", 0, 0, "hubID"), mockEvents.MakeAuditContext("userId", "0"), Resource2.ResourceTypes)) + store.Append(Resource3.DeviceId, Resource3.ToUUID().String(), mockEvents.MakeResourceChangedEvent(Resource3.Resource.GetResourceID(), Resource3.Content, events.MakeEventMeta("a", 0, 0, "hubID"), mockEvents.MakeAuditContext("userId", "0"), Resource3.ResourceTypes)) return store } @@ -132,6 +131,6 @@ func (s *testGrpcGateway_GetResourceLinksServer) Send(d *events.ResourceLinksPub if s.got == nil { s.got = make(map[string]*events.ResourceLinksPublished) } - s.got[d.DeviceId] = pbTest.CleanUpResourceLinksPublished(d, true) + s.got[d.GetDeviceId()] = pbTest.CleanUpResourceLinksPublished(d, true) return nil } diff --git a/resource-directory/service/resourceLinksProjection.go b/resource-directory/service/resourceLinksProjection.go index dd3a76515..de825dd6d 100644 --- a/resource-directory/service/resourceLinksProjection.go +++ b/resource-directory/service/resourceLinksProjection.go @@ -117,7 +117,7 @@ func (rlp *resourceLinksProjection) ToResourceLinksPublished(typeFilter strings. rlp.private.lock.RLock() defer rlp.private.lock.RUnlock() for _, resource := range rlp.private.snapshot.GetResources() { - if hasMatchingType(resource.ResourceTypes, typeFilter) { + if hasMatchingType(resource.GetResourceTypes(), typeFilter) { resources = append(resources, resource) } } diff --git a/resource-directory/service/resourceProjection.go b/resource-directory/service/resourceProjection.go index 6cf0bb8e2..e4ea61d9a 100644 --- a/resource-directory/service/resourceProjection.go +++ b/resource-directory/service/resourceProjection.go @@ -54,8 +54,8 @@ func (rp *resourceProjection) handleResourceStateSnapshotTakenLocked(eu eventsto if err := eu.Unmarshal(&s); err != nil { return err } - rp.private.resourceID = s.ResourceId - rp.private.content = s.LatestResourceChange + rp.private.resourceID = s.GetResourceId() + rp.private.content = s.GetLatestResourceChange() rp.private.onResourceChangedVersion = eu.Version() rp.private.resourceUpdatePendings = s.GetResourceUpdatePendings() rp.private.resourceCreatePendings = s.GetResourceCreatePendings() @@ -69,7 +69,7 @@ func (rp *resourceProjection) handleResourceChangedLocked(eu eventstore.EventUnm if err := eu.Unmarshal(&s); err != nil { return err } - rp.private.resourceID = s.ResourceId + rp.private.resourceID = s.GetResourceId() rp.private.content = &s rp.private.onResourceChangedVersion = eu.Version() return nil @@ -81,7 +81,7 @@ func (rp *resourceProjection) handleResourceUpdatePendingLocked(eu eventstore.Ev return err } rp.private.resourceUpdatePendings = append(rp.private.resourceUpdatePendings, &s) - rp.private.resourceID = s.ResourceId + rp.private.resourceID = s.GetResourceId() return nil } @@ -90,7 +90,7 @@ func (rp *resourceProjection) handleResourceUpdatedLocked(eu eventstore.EventUnm if err := eu.Unmarshal(&s); err != nil { return err } - rp.private.resourceID = s.ResourceId + rp.private.resourceID = s.GetResourceId() tmp := make([]*events.ResourceUpdatePending, 0, 16) var found bool for _, cu := range rp.private.resourceUpdatePendings { @@ -111,7 +111,7 @@ func (rp *resourceProjection) handleResourceRetrievePendingLocked(eu eventstore. if err := eu.Unmarshal(&s); err != nil { return err } - rp.private.resourceID = s.ResourceId + rp.private.resourceID = s.GetResourceId() rp.private.resourceRetrievePendings = append(rp.private.resourceRetrievePendings, &s) return nil } @@ -121,7 +121,7 @@ func (rp *resourceProjection) handleResourceDeletePendingLocked(eu eventstore.Ev if err := eu.Unmarshal(&s); err != nil { return err } - rp.private.resourceID = s.ResourceId + rp.private.resourceID = s.GetResourceId() rp.private.resourceDeletePendings = append(rp.private.resourceDeletePendings, &s) return nil } @@ -131,7 +131,7 @@ func (rp *resourceProjection) handleResourceRetrievedLocked(eu eventstore.EventU if err := eu.Unmarshal(&s); err != nil { return err } - rp.private.resourceID = s.ResourceId + rp.private.resourceID = s.GetResourceId() tmp := make([]*events.ResourceRetrievePending, 0, 16) var found bool for _, cu := range rp.private.resourceRetrievePendings { @@ -152,7 +152,7 @@ func (rp *resourceProjection) handleResourceDeletedLocked(eu eventstore.EventUnm if err := eu.Unmarshal(&s); err != nil { return err } - rp.private.resourceID = s.ResourceId + rp.private.resourceID = s.GetResourceId() tmp := make([]*events.ResourceDeletePending, 0, 16) var found bool for _, cu := range rp.private.resourceDeletePendings { @@ -174,7 +174,7 @@ func (rp *resourceProjection) handleResourceCreatePendingLocked(eu eventstore.Ev return err } rp.private.resourceCreatePendings = append(rp.private.resourceCreatePendings, &s) - rp.private.resourceID = s.ResourceId + rp.private.resourceID = s.GetResourceId() return nil } @@ -183,7 +183,7 @@ func (rp *resourceProjection) handleResourceCreatedLocked(eu eventstore.EventUnm if err := eu.Unmarshal(&s); err != nil { return err } - rp.private.resourceID = s.ResourceId + rp.private.resourceID = s.GetResourceId() tmp := make([]*events.ResourceCreatePending, 0, 16) var found bool for _, cu := range rp.private.resourceCreatePendings { diff --git a/resource-directory/service/resourceShadow.go b/resource-directory/service/resourceShadow.go index 99d4b1910..93b880c68 100644 --- a/resource-directory/service/resourceShadow.go +++ b/resource-directory/service/resourceShadow.go @@ -84,12 +84,13 @@ func updateContentForResponseForETag(v *pb.ResourceIdFilter, val *pb.Resource) b continue } rc := val.GetData() - val.Data = &events.ResourceChanged{} - val.Data.CopyData(rc) - val.Data.Status = commands.Status_NOT_MODIFIED - val.Data.Content = &commands.Content{ + data := &events.ResourceChanged{} + data.CopyData(rc) + data.Status = commands.Status_NOT_MODIFIED + data.Content = &commands.Content{ CoapContentFormat: int32(-1), } + val.Data = data return true } return false @@ -236,7 +237,7 @@ func (rd *ResourceTwin) sendDevicesMetadata(srv pb.GrpcGateway_GetDevicesMetadat } return nil } - if len(typeFilter) > 0 && !typeFilter.HasOneOf(res.ResourceTypes...) { + if len(typeFilter) > 0 && !typeFilter.HasOneOf(res.GetResourceTypes()...) { return nil } return rd.projection.LoadDevicesMetadata(strings.MakeSet(m.GetDeviceID()), toReloadDevices, func(m *deviceMetadataProjection) error { @@ -254,9 +255,9 @@ func (rd *ResourceTwin) sendDevicesMetadata(srv pb.GrpcGateway_GetDevicesMetadat } func (rd *ResourceTwin) GetDevicesMetadata(req *pb.GetDevicesMetadataRequest, srv pb.GrpcGateway_GetDevicesMetadataServer) error { - deviceIDs := filterDevices(rd.userDeviceIds, req.DeviceIdFilter) + deviceIDs := filterDevices(rd.userDeviceIds, req.GetDeviceIdFilter()) typeFilter := make(strings.Set) - typeFilter.Add(req.TypeFilter...) + typeFilter.Add(req.GetTypeFilter()...) toReloadDevices := make(strings.Set) err := rd.sendDevicesMetadata(srv, deviceIDs, typeFilter, toReloadDevices) if err != nil { diff --git a/resource-directory/service/resourceShadow_test.go b/resource-directory/service/resourceShadow_test.go index df326fe8e..087278913 100644 --- a/resource-directory/service/resourceShadow_test.go +++ b/resource-directory/service/resourceShadow_test.go @@ -19,7 +19,6 @@ import ( "github.com/plgd-dev/hub/v2/resource-directory/service" "github.com/plgd-dev/hub/v2/test" "github.com/plgd-dev/hub/v2/test/config" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc" ) @@ -63,7 +62,8 @@ func TestResourceTwinGetResources(t *testing.T) { DeviceId: Resource1.DeviceId, Href: Resource1.Href, }, - Content: Resource1.Content, + Content: Resource1.Content, + ResourceTypes: Resource1.ResourceTypes, }, Types: Resource1.ResourceTypes, }, @@ -73,7 +73,8 @@ func TestResourceTwinGetResources(t *testing.T) { DeviceId: Resource2.DeviceId, Href: Resource2.Href, }, - Content: Resource2.Content, + Content: Resource2.Content, + ResourceTypes: Resource2.ResourceTypes, }, Types: Resource2.ResourceTypes, }, @@ -94,7 +95,8 @@ func TestResourceTwinGetResources(t *testing.T) { DeviceId: Resource1.DeviceId, Href: Resource1.Href, }, - Content: Resource1.Content, + Content: Resource1.Content, + ResourceTypes: Resource1.ResourceTypes, }, Types: Resource1.ResourceTypes, }, @@ -104,7 +106,8 @@ func TestResourceTwinGetResources(t *testing.T) { DeviceId: Resource3.DeviceId, Href: Resource3.Href, }, - Content: Resource3.Content, + Content: Resource3.Content, + ResourceTypes: Resource3.ResourceTypes, }, Types: Resource3.ResourceTypes, }, @@ -125,7 +128,8 @@ func TestResourceTwinGetResources(t *testing.T) { DeviceId: Resource1.DeviceId, Href: Resource1.Href, }, - Content: Resource1.Content, + Content: Resource1.Content, + ResourceTypes: Resource1.ResourceTypes, }, Types: Resource1.ResourceTypes, }, @@ -135,7 +139,8 @@ func TestResourceTwinGetResources(t *testing.T) { DeviceId: Resource2.DeviceId, Href: Resource2.Href, }, - Content: Resource2.Content, + Content: Resource2.Content, + ResourceTypes: Resource2.ResourceTypes, }, Types: Resource2.ResourceTypes, }, @@ -157,7 +162,8 @@ func TestResourceTwinGetResources(t *testing.T) { DeviceId: Resource1.DeviceId, Href: Resource1.Href, }, - Content: Resource1.Content, + Content: Resource1.Content, + ResourceTypes: Resource1.ResourceTypes, }, Types: Resource1.ResourceTypes, }, @@ -176,7 +182,8 @@ func TestResourceTwinGetResources(t *testing.T) { DeviceId: Resource1.DeviceId, Href: Resource1.Href, }, - Content: Resource1.Content, + Content: Resource1.Content, + ResourceTypes: Resource1.ResourceTypes, }, Types: Resource1.ResourceTypes, }, @@ -186,7 +193,8 @@ func TestResourceTwinGetResources(t *testing.T) { DeviceId: Resource2.DeviceId, Href: Resource2.Href, }, - Content: Resource2.Content, + Content: Resource2.Content, + ResourceTypes: Resource2.ResourceTypes, }, Types: Resource2.ResourceTypes, }, @@ -196,7 +204,8 @@ func TestResourceTwinGetResources(t *testing.T) { DeviceId: Resource3.DeviceId, Href: Resource3.Href, }, - Content: Resource3.Content, + Content: Resource3.Content, + ResourceTypes: Resource3.ResourceTypes, }, Types: Resource3.ResourceTypes, }, @@ -235,8 +244,8 @@ func TestResourceTwinGetResources(t *testing.T) { fmt.Println(tt.name) var s testGrpcGateway_GetResourcesServer err := rd.GetResources(tt.args.req, &s) - assert.NoError(t, err) - test.CheckProtobufs(t, tt.want, s.got, test.AssertToCheckFunc(assert.Equal)) + require.NoError(t, err) + test.CheckProtobufs(t, tt.want, s.got, test.RequireToCheckFunc(require.Equal)) }) } } diff --git a/snapshot-service/service/config.go b/snapshot-service/service/config.go index dd2d211be..67e6998d4 100644 --- a/snapshot-service/service/config.go +++ b/snapshot-service/service/config.go @@ -5,7 +5,7 @@ import ( "net" "time" - gocron "github.com/go-co-op/gocron" + "github.com/go-co-op/gocron/v2" "github.com/google/uuid" "github.com/plgd-dev/hub/v2/pkg/config" "github.com/plgd-dev/hub/v2/pkg/log" @@ -80,20 +80,22 @@ func (c *StorageConfig) Validate() error { if c.CleanUpRecords == "" { return nil } - s := gocron.NewScheduler(time.Local) - if c.ExtendCronParserBySeconds { - s = s.CronWithSeconds(c.CleanUpRecords) - } else { - s = s.Cron(c.CleanUpRecords) + s, err := gocron.NewScheduler(gocron.WithLocation(time.Local)) //nolint:gosmopolitan + if err != nil { + return fmt.Errorf("cannot create cron job: %w", err) } - _, err := s.Do(func() { - // do nothing - }) + defer func() { + if errS := s.Shutdown(); errS != nil { + log.Errorf("failed to shutdown cron job: %w", errS) + } + }() + _, err = s.NewJob(gocron.CronJob(c.CleanUpRecords, c.ExtendCronParserBySeconds), + gocron.NewTask(func() { + // do nothing + })) if err != nil { return fmt.Errorf("cleanUpRecords('%v') - %w", c.CleanUpRecords, err) } - s.Clear() - s.Stop() return nil } diff --git a/snapshot-service/service/service.go b/snapshot-service/service/service.go index 78ad2fdfa..82e87789f 100644 --- a/snapshot-service/service/service.go +++ b/snapshot-service/service/service.go @@ -5,7 +5,7 @@ import ( "fmt" "time" - gocron "github.com/go-co-op/gocron" + "github.com/go-co-op/gocron/v2" "github.com/plgd-dev/hub/v2/pkg/config/database" "github.com/plgd-dev/hub/v2/pkg/fn" "github.com/plgd-dev/hub/v2/pkg/fsnotify" @@ -51,35 +51,36 @@ func newStore(ctx context.Context, config StorageConfig, fileWatcher *fsnotify.W return nil, nil, err } fl.AddFunc(func() { - if err := db.Close(ctx); err != nil { - log.Errorf("failed to close mongodb store: %w", err) + if errC := db.Close(ctx); errC != nil { + log.Errorf("failed to close mongodb store: %w", errC) } }) if config.CleanUpRecords == "" { return db, fl.ToFunction(), nil } - s := gocron.NewScheduler(time.Local) - if config.ExtendCronParserBySeconds { - s = s.CronWithSeconds(config.CleanUpRecords) - } else { - s = s.Cron(config.CleanUpRecords) + s, err := gocron.NewScheduler(gocron.WithLocation(time.Local)) //nolint:gosmopolitan + if err != nil { + fl.Execute() + return nil, nil, fmt.Errorf("cannot create cron job: %w", err) } - _, err = s.Do(func() { + _, err = s.NewJob(gocron.CronJob(config.CleanUpRecords, config.ExtendCronParserBySeconds), gocron.NewTask(func() { /* _, errDel := db.DeleteNonDeviceExpiredRecords(ctx, time.Now()) if errDel != nil && !errors.Is(errDel, store.ErrNotSupported) { log.Errorf("failed to delete expired signing records: %w", errDel) } */ - }) + })) if err != nil { fl.Execute() return nil, nil, fmt.Errorf("cannot create cron job: %w", err) } - fl.AddFunc(s.Clear) - fl.AddFunc(s.Stop) - s.StartAsync() - + fl.AddFunc(func() { + if errS := s.Shutdown(); errS != nil { + log.Errorf("failed to shutdown cron job: %w", errS) + } + }) + s.Start() return db, fl.ToFunction(), nil } diff --git a/snapshot-service/store/mongodb/store.go b/snapshot-service/store/mongodb/store.go index e124bee5d..c4265d6b9 100644 --- a/snapshot-service/store/mongodb/store.go +++ b/snapshot-service/store/mongodb/store.go @@ -28,9 +28,7 @@ func New(ctx context.Context, cfg *Config, fileWatcher *fsnotify.Watcher, logger return nil, err } s := Store{Store: m} - s.SetOnClear(func(c context.Context) error { - return s.clearDatabases(ctx) - }) + s.SetOnClear(s.clearDatabases) s.AddCloseFunc(certManager.Close) return &s, nil } diff --git a/test/bridge-device/bridge-device.jsonld b/test/bridge-device/bridge-device.jsonld new file mode 100644 index 000000000..4b72a02b6 --- /dev/null +++ b/test/bridge-device/bridge-device.jsonld @@ -0,0 +1,8 @@ +{ + "@context": "https://www.w3.org/2019/wot/td/v1", + "@type": [ + "Thing" + ], + "id": "urn:uuid:bridge-test", + "properties": {} +} \ No newline at end of file diff --git a/test/bridge-device/config.yaml b/test/bridge-device/config.yaml new file mode 100644 index 000000000..5be18ae10 --- /dev/null +++ b/test/bridge-device/config.yaml @@ -0,0 +1,19 @@ +apis: + coap: + id: 8f596b43-29c0-4147-8b40-e99268ab30f7 + name: "bridge-test" + externalAddresses: + - "127.0.0.1:15683" + - "[::1]:15683" + maxMessageSize: 2097152 +log: + level: "info" +cloud: + enabled: true +credential: + enabled: true +thingDescription: + enabled: true + file: "bridge-device.jsonld" +numGeneratedBridgedDevices: 3 +numResourcesPerDevice: 3 diff --git a/test/cbor.go b/test/cbor.go index 8c23ee146..0e0d48571 100644 --- a/test/cbor.go +++ b/test/cbor.go @@ -5,13 +5,13 @@ import ( "github.com/fxamacker/cbor/v2" pkgCbor "github.com/plgd-dev/kit/v2/codec/cbor" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/assert" ) func DecodeCbor(t *testing.T, data []byte) interface{} { var v interface{} err := pkgCbor.Decode(data, &v) - require.NoError(t, err) + assert.NoError(t, err) return v } @@ -25,6 +25,6 @@ func EncodeToSortedCbor(v interface{}) ([]byte, error) { func EncodeToCbor(t *testing.T, v interface{}) []byte { d, err := EncodeToSortedCbor(v) - require.NoError(t, err) + assert.NoError(t, err) return d } diff --git a/test/cloud-server/Dockerfile b/test/cloud-server/Dockerfile index fa37f1da6..a4542f439 100644 --- a/test/cloud-server/Dockerfile +++ b/test/cloud-server/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1 -FROM golang:1.20.13-alpine AS build +FROM golang:1.22.3-alpine AS build ARG VERSION ARG COMMIT_DATE ARG SHORT_COMMIT @@ -10,7 +10,8 @@ WORKDIR $GOPATH/src/github.com/plgd-dev/hub COPY go.mod go.sum ./ RUN go mod download COPY . . -RUN ( cd /usr/local/go && patch -p1 < $GOPATH/src/github.com/plgd-dev/hub/tools/docker/patches/shrink_tls_conn.patch ) +WORKDIR /usr/local/go +RUN ( patch -p1 < "$GOPATH/src/github.com/plgd-dev/hub/tools/docker/patches/shrink_tls_conn.patch" ) ARG root_directory=$GOPATH/src/github.com/plgd-dev/hub #cert-tool @@ -18,6 +19,12 @@ ARG tool=cert-tool WORKDIR $root_directory/tools/$tool RUN go build -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" -o /go/bin/cert-tool +#coap-gateway +#the "device_integration" tag should ensure that only integration tests with a device simulator are compiled +ARG service=coap-gateway +WORKDIR $root_directory/$service/service +RUN go test -p 1 -c -tags=device_integration -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" -o /go/bin/coap-gateway.test + #grpc-gateway ARG service=grpc-gateway WORKDIR $root_directory/$service/service @@ -49,17 +56,20 @@ RUN unzip ./nats.zip -d ./nats RUN cp ./nats/*/nats /go/bin/nats FROM ubuntu:22.04 AS service -RUN apt update # iproute2 -> ip utility in run.sh # netcat -> nc utility in run.sh # nginx -> nginx server in run.sh # openssl -> openssl utility in run.sh -RUN apt-get install -y --no-install-recommends ca-certificates gnupg iproute2 netcat nginx openssl wget +RUN apt update \ + && apt-get install -y --no-install-recommends ca-certificates gnupg iproute2 netcat nginx openssl wget \ + && apt-get clean RUN wget -qO - https://pgp.mongodb.com/server-6.0.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/mongodb-6.0.gpg RUN echo "deb [ arch=$(dpkg --print-architecture) ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-6.0.list -RUN apt update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends mongodb-org mongodb-org-server +RUN apt update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends mongodb-org mongodb-org-server \ + && apt-get clean COPY --from=build /go/bin/cert-tool /usr/local/bin/cert-tool +COPY --from=build /go/bin/coap-gateway.test /usr/local/bin/coap-gateway.test COPY --from=build /go/bin/grpc-gateway.test /usr/local/bin/grpc-gateway.test COPY --from=build /go/bin/test-iotivity-lite.test /usr/local/bin/test-iotivity-lite.test COPY --from=build /go/bin/nats-server /usr/local/bin/nats-server @@ -68,6 +78,8 @@ COPY test/cloud-server/run.sh /usr/local/bin/run.sh # global ENV FQDN="localhost" +ENV PREPARE_ENV="true" +ENV RUN="true" # global - certificates generated by cert-tool ENV CERT_TOOL_SIGN_ALG=ECDSA-SHA256 diff --git a/test/cloud-server/run.sh b/test/cloud-server/run.sh index ad49eb459..a11d74811 100755 --- a/test/cloud-server/run.sh +++ b/test/cloud-server/run.sh @@ -6,119 +6,91 @@ umask 0000 # Configure services export PATH="/usr/local/bin:$PATH" -export COAP_GATEWAY_CLOUD_ID="adebc667-1f2b-41e3-bf5c-6d6eabc68cc6" +if [ -z "${COAP_GATEWAY_CLOUD_ID}" ]; then + COAP_GATEWAY_CLOUD_ID="adebc667-1f2b-41e3-bf5c-6d6eabc68cc6" +fi -export CERTIFICATES_PATH="/data/certs" -export OAUTH_KEYS_PATH="/data/oauth/keys" -export OAUTH_SECRETS_PATH="/data/oauth/secrets" -export LOGS_PATH="/data/log" -export MONGO_PATH="/data/db" +CERTIFICATES_PATH="/data/certs" +LOGS_PATH="/data/log" +MONGO_PATH="/data/db" -export INTERNAL_CERT_DIR_PATH="$CERTIFICATES_PATH/internal" -export GRPC_INTERNAL_CERT_NAME="endpoint.crt" -export GRPC_INTERNAL_CERT_KEY_NAME="endpoint.key" +INTERNAL_CERT_DIR_PATH="$CERTIFICATES_PATH/internal" +GRPC_INTERNAL_CERT_NAME="endpoint.crt" +GRPC_INTERNAL_CERT_KEY_NAME="endpoint.key" -export EXTERNAL_CERT_DIR_PATH="$CERTIFICATES_PATH/external" -export COAP_GATEWAY_FILE_CERT_NAME="coap-gateway.crt" -export COAP_GATEWAY_FILE_CERT_KEY_NAME="coap-gateway.key" +EXTERNAL_CERT_DIR_PATH="$CERTIFICATES_PATH/external" +COAP_GATEWAY_FILE_CERT_NAME="coap-gateway.crt" +COAP_GATEWAY_FILE_CERT_KEY_NAME="coap-gateway.key" # ROOT CERTS -export CA_POOL_DIR="$CERTIFICATES_PATH" -export CA_POOL_NAME_PREFIX="root_ca" -export CA_POOL_CERT_PATH="$CA_POOL_DIR/$CA_POOL_NAME_PREFIX.crt" -export CA_POOL_CERT_KEY_PATH="$CA_POOL_DIR/$CA_POOL_NAME_PREFIX.key" +CA_POOL_DIR="$CERTIFICATES_PATH" +CA_POOL_NAME_PREFIX="root_ca" +CA_POOL_CERT_PATH="$CA_POOL_DIR/$CA_POOL_NAME_PREFIX.crt" +CA_POOL_CERT_KEY_PATH="$CA_POOL_DIR/$CA_POOL_NAME_PREFIX.key" # DIAL CERTS -export DIAL_FILE_CA_POOL="$CA_POOL_CERT_PATH" -export DIAL_FILE_CERT_DIR_PATH="$INTERNAL_CERT_DIR_PATH" -export DIAL_FILE_CERT_NAME="$GRPC_INTERNAL_CERT_NAME" -export DIAL_FILE_CERT_KEY_NAME="$GRPC_INTERNAL_CERT_KEY_NAME" - -#LISTEN CERTS -export LISTEN_FILE_CA_POOL="$CA_POOL_CERT_PATH" -export LISTEN_FILE_CERT_DIR_PATH="$INTERNAL_CERT_DIR_PATH" -export LISTEN_FILE_CERT_NAME="$GRPC_INTERNAL_CERT_NAME" -export LISTEN_FILE_CERT_KEY_NAME="$GRPC_INTERNAL_CERT_KEY_NAME" - -#SECRETS -export SECRETS_DIRECTORY=/data/secrets - -#OAUTH-SEVER KEYS -export OAUTH_ID_TOKEN_KEY_PATH=${OAUTH_KEYS_PATH}/id-token.pem -export OAUTH_ACCESS_TOKEN_KEY_PATH=${OAUTH_KEYS_PATH}/access-token.pem - -export OAUTH_DEVICE_SECRET_PATH=${OAUTH_SECRETS_PATH}/device.secret - -#ENDPOINTS -export MONGODB_HOST="localhost:$MONGO_PORT" -export MONGODB_URI="mongodb://$MONGODB_HOST" -export NATS_HOST="localhost:$NATS_PORT" -export NATS_URL="nats://${NATS_HOST}" - -# needed by grpc-gateway.test -export TEST_COAP_GW_CERT_FILE="${EXTERNAL_CERT_DIR_PATH}/${COAP_GATEWAY_FILE_CERT_NAME}" -export TEST_COAP_GW_KEY_FILE="${EXTERNAL_CERT_DIR_PATH}/${COAP_GATEWAY_FILE_CERT_KEY_NAME}" -export TEST_ROOT_CA_CERT="${CA_POOL_CERT_PATH}" -export TEST_ROOT_CA_KEY="${CA_POOL_CERT_KEY_PATH}" -export TEST_CLOUD_SID="${COAP_GATEWAY_CLOUD_ID}" -export TEST_OAUTH_SERVER_ID_TOKEN_PRIVATE_KEY="${OAUTH_ID_TOKEN_KEY_PATH}" -export TEST_OAUTH_SERVER_ACCESS_TOKEN_PRIVATE_KEY="${OAUTH_ACCESS_TOKEN_KEY_PATH}" -export TEST_COAP_GATEWAY_UDP_ENABLED="${COAP_GATEWAY_UDP_ENABLED}" - -mkdir -p ${OAUTH_SECRETS_PATH} -if [ -z "${OAUTH_CLIENT_SECRET}" ] -then - export OAUTH_CLIENT_SECRET="secret" -fi -echo -n ${OAUTH_CLIENT_SECRET} > ${OAUTH_DEVICE_SECRET_PATH} +DIAL_FILE_CERT_DIR_PATH="$INTERNAL_CERT_DIR_PATH" +DIAL_FILE_CERT_NAME="$GRPC_INTERNAL_CERT_NAME" +DIAL_FILE_CERT_KEY_NAME="$GRPC_INTERNAL_CERT_KEY_NAME" -mkdir -p $CA_POOL_DIR -mkdir -p $INTERNAL_CERT_DIR_PATH -mkdir -p $EXTERNAL_CERT_DIR_PATH -mkdir -p ${SECRETS_DIRECTORY} -ln -s ${SECRETS_DIRECTORY} /secrets - -export CA_POOL=$CA_POOL_CERT_PATH -export CERT_FILE=$DIAL_FILE_CERT_DIR_PATH/$DIAL_FILE_CERT_NAME -export KEY_FILE=$DIAL_FILE_CERT_DIR_PATH/$DIAL_FILE_CERT_KEY_NAME +# OAUTH-SEVER KEYS +OAUTH_KEYS_PATH="/data/oauth/keys" +OAUTH_ID_TOKEN_KEY_PATH=${OAUTH_KEYS_PATH}/id-token.pem +OAUTH_ACCESS_TOKEN_KEY_PATH=${OAUTH_KEYS_PATH}/access-token.pem CERT_TOOL_SIGN_ALG=${CERT_TOOL_SIGN_ALG:-ECDSA-SHA256} CERT_TOOL_ELLIPTIC_CURVE=${CERT_TOOL_ELLIPTIC_CURVE:-P256} -fqdnSAN="--cert.san.domain=$FQDN" -if ip route get $FQDN 2>/dev/null >/dev/null; then - fqdnSAN="--cert.san.ip=$FQDN" -fi -echo "generating CA cert" -cert-tool --cmd.generateRootCA --outCert=$CA_POOL_CERT_PATH --outKey=$CA_POOL_CERT_KEY_PATH --cert.subject.cn="Root CA" \ - --cert.signatureAlgorithm=${CERT_TOOL_SIGN_ALG} --cert.ellipticCurve=${CERT_TOOL_ELLIPTIC_CURVE} -echo "generating GRPC internal cert" -cert-tool --cmd.generateCertificate --outCert=$DIAL_FILE_CERT_DIR_PATH/$DIAL_FILE_CERT_NAME \ - --outKey=$DIAL_FILE_CERT_DIR_PATH/$DIAL_FILE_CERT_KEY_NAME --cert.subject.cn="localhost" --cert.san.domain="localhost" \ - --cert.san.ip="0.0.0.0" --cert.san.ip="127.0.0.1" $fqdnSAN --signerCert=$CA_POOL_CERT_PATH --signerKey=$CA_POOL_CERT_KEY_PATH \ - --cert.signatureAlgorithm=${CERT_TOOL_SIGN_ALG} --cert.ellipticCurve=${CERT_TOOL_ELLIPTIC_CURVE} -echo "generating COAP-GW cert" -cert-tool --cmd.generateIdentityCertificate=$COAP_GATEWAY_CLOUD_ID --outCert=$EXTERNAL_CERT_DIR_PATH/$COAP_GATEWAY_FILE_CERT_NAME \ - --outKey=$EXTERNAL_CERT_DIR_PATH/$COAP_GATEWAY_FILE_CERT_KEY_NAME --cert.san.domain=$COAP_GATEWAY_FQDN \ - --signerCert=$CA_POOL_CERT_PATH --signerKey=$CA_POOL_CERT_KEY_PATH --cert.signatureAlgorithm=${CERT_TOOL_SIGN_ALG} \ - --cert.ellipticCurve=${CERT_TOOL_ELLIPTIC_CURVE} -echo "generating NGINX cert" -cert-tool --cmd.generateCertificate --outCert=$EXTERNAL_CERT_DIR_PATH/$DIAL_FILE_CERT_NAME \ - --outKey=$EXTERNAL_CERT_DIR_PATH/$DIAL_FILE_CERT_KEY_NAME --cert.subject.cn="localhost" --cert.san.domain="localhost" \ - --cert.san.ip="0.0.0.0" --cert.san.ip="127.0.0.1" $fqdnSAN --signerCert=$CA_POOL_CERT_PATH --signerKey=$CA_POOL_CERT_KEY_PATH \ - --cert.signatureAlgorithm=${CERT_TOOL_SIGN_ALG} --cert.ellipticCurve=${CERT_TOOL_ELLIPTIC_CURVE} - -mkdir -p ${OAUTH_KEYS_PATH} -openssl genrsa -out ${OAUTH_ID_TOKEN_KEY_PATH} 4096 -openssl ecparam -name prime256v1 -genkey -noout -out ${OAUTH_ACCESS_TOKEN_KEY_PATH} - -mkdir -p $MONGO_PATH -mkdir -p $CERTIFICATES_PATH -mkdir -p $LOGS_PATH - -# nats -echo "starting nats-server" -cat > /data/nats.config < ${OAUTH_DEVICE_SECRET_PATH} + + mkdir -p $CERTIFICATES_PATH + mkdir -p $CA_POOL_DIR + mkdir -p $INTERNAL_CERT_DIR_PATH + mkdir -p $EXTERNAL_CERT_DIR_PATH + + fqdnSAN="--cert.san.domain=$FQDN" + if ip route get $FQDN 2>/dev/null >/dev/null; then + fqdnSAN="--cert.san.ip=$FQDN" + fi + echo "generating CA cert" + cert-tool --cmd.generateRootCA --outCert=$CA_POOL_CERT_PATH --outKey=$CA_POOL_CERT_KEY_PATH --cert.subject.cn="Root CA" \ + --cert.signatureAlgorithm=${CERT_TOOL_SIGN_ALG} --cert.ellipticCurve=${CERT_TOOL_ELLIPTIC_CURVE} + echo "generating GRPC internal cert" + cert-tool --cmd.generateCertificate --outCert=$DIAL_FILE_CERT_DIR_PATH/$DIAL_FILE_CERT_NAME \ + --outKey=$DIAL_FILE_CERT_DIR_PATH/$DIAL_FILE_CERT_KEY_NAME --cert.subject.cn="localhost" --cert.san.domain="localhost" \ + --cert.san.ip="0.0.0.0" --cert.san.ip="127.0.0.1" $fqdnSAN --signerCert=$CA_POOL_CERT_PATH --signerKey=$CA_POOL_CERT_KEY_PATH \ + --cert.signatureAlgorithm=${CERT_TOOL_SIGN_ALG} --cert.ellipticCurve=${CERT_TOOL_ELLIPTIC_CURVE} + echo "generating COAP-GW cert" + cert-tool --cmd.generateIdentityCertificate=$COAP_GATEWAY_CLOUD_ID --outCert=$EXTERNAL_CERT_DIR_PATH/$COAP_GATEWAY_FILE_CERT_NAME \ + --outKey=$EXTERNAL_CERT_DIR_PATH/$COAP_GATEWAY_FILE_CERT_KEY_NAME --cert.san.domain=$COAP_GATEWAY_FQDN \ + --signerCert=$CA_POOL_CERT_PATH --signerKey=$CA_POOL_CERT_KEY_PATH --cert.signatureAlgorithm=${CERT_TOOL_SIGN_ALG} \ + --cert.ellipticCurve=${CERT_TOOL_ELLIPTIC_CURVE} + echo "generating NGINX cert" + cert-tool --cmd.generateCertificate --outCert=$EXTERNAL_CERT_DIR_PATH/$DIAL_FILE_CERT_NAME \ + --outKey=$EXTERNAL_CERT_DIR_PATH/$DIAL_FILE_CERT_KEY_NAME --cert.subject.cn="localhost" --cert.san.domain="localhost" \ + --cert.san.ip="0.0.0.0" --cert.san.ip="127.0.0.1" $fqdnSAN --signerCert=$CA_POOL_CERT_PATH --signerKey=$CA_POOL_CERT_KEY_PATH \ + --cert.signatureAlgorithm=${CERT_TOOL_SIGN_ALG} --cert.ellipticCurve=${CERT_TOOL_ELLIPTIC_CURVE} + + mkdir -p ${OAUTH_KEYS_PATH} + openssl genrsa -out ${OAUTH_ID_TOKEN_KEY_PATH} 4096 + openssl ecparam -name prime256v1 -genkey -noout -out ${OAUTH_ACCESS_TOKEN_KEY_PATH} + + # nats + cat > /data/nats.config <$LOGS_PATH/nats-server.log 2>&1 & -status=$? -nats_server_pid=$! -if [ $status -ne 0 ]; then - echo "Failed to start nats-server: $status" - sync - cat $LOGS_PATH/nats-server.log - exit $status + # mongo + mkdir -p $MONGO_PATH + cat $DIAL_FILE_CERT_DIR_PATH/$DIAL_FILE_CERT_NAME > $DIAL_FILE_CERT_DIR_PATH/mongo.key + cat $DIAL_FILE_CERT_DIR_PATH/$DIAL_FILE_CERT_KEY_NAME >> $DIAL_FILE_CERT_DIR_PATH/mongo.key fi -# waiting for nats. Without wait, sometimes auth service didn't connect. -i=0 -while true; do - i=$((i+1)) - if nc -z localhost $NATS_PORT; then - break +if [ "${RUN}" == "true" ]; then + # nats + export NATS_HOST="localhost:$NATS_PORT" + export NATS_URL="nats://${NATS_HOST}" + + echo "starting nats-server" + nats-server -c /data/nats.config >$LOGS_PATH/nats-server.log 2>&1 & + status=$? + nats_server_pid=$! + if [ $status -ne 0 ]; then + echo "Failed to start nats-server: $status" + sync + cat $LOGS_PATH/nats-server.log + exit $status + fi + + # waiting for nats. Without wait, sometimes auth service didn't connect. + i=0 + while true; do + i=$((i+1)) + if nc -z localhost $NATS_PORT; then + break + fi + echo "Try to reconnect to nats(${NATS_HOST}) $i" + sleep 1 + done + + # mongo + export MONGODB_HOST="localhost:$MONGO_PORT" + export MONGODB_URI="mongodb://$MONGODB_HOST" + + echo "starting mongod" + mongod --setParameter maxNumActiveUserIndexBuilds=64 --port $MONGO_PORT --dbpath $MONGO_PATH \ + --sslMode requireSSL --sslCAFile $CA_POOL_CERT_PATH --sslPEMKeyFile $DIAL_FILE_CERT_DIR_PATH/mongo.key \ + >$LOGS_PATH/mongod.log 2>&1 & + status=$? + mongo_pid=$! + if [ $status -ne 0 ]; then + echo "Failed to start mongod: $status" + sync + cat $LOGS_PATH/mongod.log + exit $status fi - echo "Try to reconnect to nats(${NATS_HOST}) $i" - sleep 1 -done - -# mongo -echo "starting mongod" -cat $DIAL_FILE_CERT_DIR_PATH/$DIAL_FILE_CERT_NAME > $DIAL_FILE_CERT_DIR_PATH/mongo.key -cat $DIAL_FILE_CERT_DIR_PATH/$DIAL_FILE_CERT_KEY_NAME >> $DIAL_FILE_CERT_DIR_PATH/mongo.key -mongod --setParameter maxNumActiveUserIndexBuilds=64 --port $MONGO_PORT --dbpath $MONGO_PATH --sslMode requireSSL --sslCAFile $CA_POOL_CERT_PATH --sslPEMKeyFile $DIAL_FILE_CERT_DIR_PATH/mongo.key >$LOGS_PATH/mongod.log 2>&1 & -status=$? -mongo_pid=$! -if [ $status -ne 0 ]; then - echo "Failed to start mongod: $status" - sync - cat $LOGS_PATH/mongod.log - exit $status -fi -# waiting for mongo DB. Without wait, sometimes auth service didn't connect. -i=0 -while true; do - i=$((i+1)) - if openssl s_client -connect ${MONGODB_HOST} -cert ${INTERNAL_CERT_DIR_PATH}/${DIAL_FILE_CERT_NAME} -key ${INTERNAL_CERT_DIR_PATH}/${DIAL_FILE_CERT_KEY_NAME} <<< "Q" 2>/dev/null > /dev/null; then - break + # waiting for mongo DB. Without wait, sometimes auth service didn't connect. + i=0 + while [ $i -lt 60 ]; do + i=$((i+1)) + + if [ $i -eq 60 ]; then + echo "Failed to start mongod: timeout" + sync + cat $LOGS_PATH/mongod.log + exit 1 + fi + + if openssl s_client -connect ${MONGODB_HOST} -cert ${INTERNAL_CERT_DIR_PATH}/${DIAL_FILE_CERT_NAME} \ + -key ${INTERNAL_CERT_DIR_PATH}/${DIAL_FILE_CERT_KEY_NAME} <<< "Q" 2>/dev/null > /dev/null; then + break + fi + echo "Try to reconnect to mongodb(${MONGODB_HOST}) $i" + sleep 1 + done + + # needed by coap-gateway.test and grpc-gateway.test + ## LISTEN CERTS + export LISTEN_FILE_CA_POOL="$CA_POOL_CERT_PATH" + export LISTEN_FILE_CERT_DIR_PATH="$INTERNAL_CERT_DIR_PATH" + export LISTEN_FILE_CERT_NAME="$GRPC_INTERNAL_CERT_NAME" + export LISTEN_FILE_CERT_KEY_NAME="$GRPC_INTERNAL_CERT_KEY_NAME" + ## OTHER + export TEST_COAP_GW_CERT_FILE="${EXTERNAL_CERT_DIR_PATH}/${COAP_GATEWAY_FILE_CERT_NAME}" + export TEST_COAP_GW_KEY_FILE="${EXTERNAL_CERT_DIR_PATH}/${COAP_GATEWAY_FILE_CERT_KEY_NAME}" + export TEST_ROOT_CA_CERT="${CA_POOL_CERT_PATH}" + export TEST_ROOT_CA_KEY="${CA_POOL_CERT_KEY_PATH}" + export TEST_CLOUD_SID="${COAP_GATEWAY_CLOUD_ID}" + export TEST_OAUTH_SERVER_ID_TOKEN_PRIVATE_KEY="${OAUTH_ID_TOKEN_KEY_PATH}" + export TEST_OAUTH_SERVER_ACCESS_TOKEN_PRIVATE_KEY="${OAUTH_ACCESS_TOKEN_KEY_PATH}" + export TEST_COAP_GATEWAY_UDP_ENABLED="${COAP_GATEWAY_UDP_ENABLED}" + + if [ "${COAP_GATEWAY_TEST_DISABLED}" != "1" ]; then + opts=() + if [ -n "${COAP_GATEWAY_TEST_RUN}" ]; then + opts+=("-test.run" "${COAP_GATEWAY_TEST_RUN}") + fi + echo "starting coap-gateway test" + coap-gateway.test -test.v -test.timeout 600s -test.parallel 1 ${opts[@]} fi - echo "Try to reconnect to mongodb(${MONGODB_HOST}) $i" - sleep 1 -done -echo "starting grpc-gateway test" -grpc-gateway.test -test.v -test.timeout 600s -test.parallel 1 + if [ "${GRPC_GATEWAY_TEST_DISABLED}" != "1" ]; then + opts=() + if [ -n "${GRPC_GATEWAY_TEST_RUN}" ]; then + opts+=("-test.run" "${GRPC_GATEWAY_TEST_RUN}") + fi + echo "starting grpc-gateway test" + grpc-gateway.test -test.v -test.timeout 600s -test.parallel 1 ${opts[@]} + fi -echo "starting test/iotivity-lite test" -test-iotivity-lite.test -test.v -test.timeout 600s -test.parallel 1 + if [ "${IOTIVITY_LITE_TEST_DISABLED}" != "1" ]; then + opts=() + if [ -n "${IOTIVITY_LITE_TEST_RUN}" ]; then + opts+=("-test.run" "${IOTIVITY_LITE_TEST_RUN}") + fi + echo "starting test/iotivity-lite test" + test-iotivity-lite.test -test.v -test.timeout 600s -test.parallel 1 ${opts[@]} + fi +fi diff --git a/test/coap-gateway/service/resourceDirectory.go b/test/coap-gateway/service/resourceDirectory.go index 3188fe7e9..d14a81d0d 100644 --- a/test/coap-gateway/service/resourceDirectory.go +++ b/test/coap-gateway/service/resourceDirectory.go @@ -1,6 +1,7 @@ package service import ( + "errors" "fmt" "net/url" "regexp" @@ -58,7 +59,7 @@ func resourceDirectoryPublishHandler(req *mux.Message, client *Client) { } p.SequenceNumber = req.Sequence() - if err := client.handler.PublishResources(p); err != nil { + if err = client.handler.PublishResources(p); err != nil { client.logAndWriteErrorResponse(err, coapCodes.InternalServerError, req.Token()) return } @@ -99,7 +100,7 @@ func parseUnpublishRequestFromQuery(queries []string) (UnpublishRequest, error) } if req.DeviceID == "" { - return UnpublishRequest{}, fmt.Errorf("deviceID not found") + return UnpublishRequest{}, errors.New("deviceID not found") } return req, nil diff --git a/test/coap-gateway/service/service.go b/test/coap-gateway/service/service.go index 9d407c989..9586308ef 100644 --- a/test/coap-gateway/service/service.go +++ b/test/coap-gateway/service/service.go @@ -2,6 +2,7 @@ package service import ( "context" + "errors" "fmt" "os" "os/signal" @@ -154,7 +155,7 @@ func validateCommand(s mux.ResponseWriter, req *mux.Message, server *Service, fn fnc(req, client) case coapCodes.Empty: if !ok { - client.logAndWriteErrorResponse(fmt.Errorf("cannot handle command: client not found"), coapCodes.InternalServerError, req.Token()) + client.logAndWriteErrorResponse(errors.New("cannot handle command: client not found"), coapCodes.InternalServerError, req.Token()) closeClient(client) return } diff --git a/test/coap-gateway/test/defaultHandler.go b/test/coap-gateway/test/defaultHandler.go index 12a2fec30..65c6f4078 100644 --- a/test/coap-gateway/test/defaultHandler.go +++ b/test/coap-gateway/test/defaultHandler.go @@ -100,15 +100,15 @@ func (h *DefaultObserverHandler) RefreshToken(req coapgwService.CoapRefreshToken }, nil } -func (h *DefaultObserverHandler) OnObserveResource(_ context.Context, deviceID, resourceHref string, notification *pool.Message) error { - log.Debugf("OnObserveResource: %v%v", deviceID, resourceHref) +func (h *DefaultObserverHandler) OnObserveResource(_ context.Context, deviceID, resourceHref string, resourceTypes []string, notification *pool.Message) error { + log.Debugf("OnObserveResource: %v%v %v", deviceID, resourceHref, resourceTypes) msg := message.ToJson(notification, true, true) log.Get().With("notification", msg).Debug("RECEIVED-OBSERVE") return nil } -func (h *DefaultObserverHandler) OnGetResourceContent(_ context.Context, deviceID, resourceHref string, notification *pool.Message) error { - log.Debugf("OnGetResourceContent: %v%v", deviceID, resourceHref) +func (h *DefaultObserverHandler) OnGetResourceContent(_ context.Context, deviceID, resourceHref string, resourceTypes []string, notification *pool.Message) error { + log.Debugf("OnGetResourceContent: %v%v %v", deviceID, resourceHref, resourceTypes) msg := message.ToJson(notification, true, false) log.Get().With("notification", msg).Debug("RECEIVED-GET") return nil diff --git a/test/device/bridge/device.go b/test/device/bridge/device.go new file mode 100644 index 000000000..80af77d79 --- /dev/null +++ b/test/device/bridge/device.go @@ -0,0 +1,102 @@ +/**************************************************************************** + * + * Copyright (c) 2024 plgd.dev s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"), + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +package bridge + +import ( + "time" + + "github.com/plgd-dev/device/v2/bridge/resources/thingDescription" + bridgeDevice "github.com/plgd-dev/device/v2/cmd/bridge-device/device" + "github.com/plgd-dev/device/v2/schema" + schemaDevice "github.com/plgd-dev/device/v2/schema/device" + "github.com/plgd-dev/device/v2/schema/interfaces" + "github.com/plgd-dev/device/v2/schema/maintenance" + "github.com/plgd-dev/hub/v2/test/device" + "github.com/plgd-dev/hub/v2/test/sdk" +) + +var TestResources = []schema.ResourceLink{ + { + Href: schemaDevice.ResourceURI, + ResourceTypes: []string{bridgeDevice.DeviceResourceType, schemaDevice.ResourceType}, + Interfaces: []string{interfaces.OC_IF_BASELINE, interfaces.OC_IF_R}, + Policy: &schema.Policy{ + BitMask: schema.Discoverable, + }, + }, + { + Href: maintenance.ResourceURI, + ResourceTypes: []string{maintenance.ResourceType}, + Interfaces: []string{interfaces.OC_IF_BASELINE, interfaces.OC_IF_RW}, + Policy: &schema.Policy{ + BitMask: schema.Discoverable, + }, + }, +} + +type Device struct { + device.BaseDevice + testResources int // number of test resources + tdEnabled bool // thingDescription resource enabled +} + +func NewDevice(id, name string, testResources int, tdEnabled bool) *Device { + return &Device{ + BaseDevice: device.MakeBaseDevice(id, name), + testResources: testResources, + tdEnabled: tdEnabled, + } +} + +func (d *Device) GetType() device.Type { + return device.Bridged +} + +func (d *Device) GetSDKClientOptions() []sdk.Option { + return []sdk.Option{sdk.WithUseDeviceIDInQuery(true)} +} + +func (d *Device) GetRetryInterval(int) time.Duration { + return time.Second * 10 +} + +func (d *Device) GetDefaultResources() schema.ResourceLinks { + testResources := TestResources + for i := 0; i < d.testResources; i++ { + testResources = append(testResources, schema.ResourceLink{ + Href: bridgeDevice.GetTestResourceHref(i), + ResourceTypes: []string{bridgeDevice.TestResourceType}, + Interfaces: []string{interfaces.OC_IF_BASELINE, interfaces.OC_IF_RW}, + Policy: &schema.Policy{ + BitMask: schema.Discoverable | schema.Observable, + }, + }) + } + if d.tdEnabled { + testResources = append(testResources, schema.ResourceLink{ + Href: thingDescription.ResourceURI, + ResourceTypes: []string{thingDescription.ResourceType}, + Interfaces: []string{interfaces.OC_IF_BASELINE, interfaces.OC_IF_R}, + Policy: &schema.Policy{ + BitMask: schema.Discoverable | schema.Observable, + }, + }) + } + return testResources +} diff --git a/test/device/device.go b/test/device/device.go new file mode 100644 index 000000000..de3e9d92c --- /dev/null +++ b/test/device/device.go @@ -0,0 +1,164 @@ +/**************************************************************************** + * + * Copyright (c) 2024 plgd.dev s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"), + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +package device + +import ( + "context" + "errors" + "fmt" + "log" + "sync/atomic" + "time" + + "github.com/plgd-dev/device/v2/client/core" + deviceCoap "github.com/plgd-dev/device/v2/pkg/net/coap" + "github.com/plgd-dev/device/v2/schema" + "github.com/plgd-dev/device/v2/schema/device" + "github.com/plgd-dev/hub/v2/test/sdk" +) + +type Type int + +const ( + OCF Type = iota + Bridged +) + +type Device interface { + // GetType returns device type + GetType() Type + + // GetID returns device ID + GetID() string + + // SetID sets device ID + SetID(id string) + + // GetName returns device name + GetName() string + + // GetRetryInterval returns retry interval of the device before retrying provisioning + GetRetryInterval(attempt int) time.Duration + + // GetDefaultResources returns default device resources + GetDefaultResources() schema.ResourceLinks + + // GetSDKClientOptions returns options for the SDK client used with this device + GetSDKClientOptions() []sdk.Option +} + +type BaseDevice struct { + id string + name string +} + +func MakeBaseDevice(id, name string) BaseDevice { + return BaseDevice{ + id: id, + name: name, + } +} + +func (bd *BaseDevice) GetID() string { + return bd.id +} + +func (bd *BaseDevice) SetID(id string) { + bd.id = id +} + +func (bd *BaseDevice) GetName() string { + return bd.name +} + +func (bd *BaseDevice) GetSDKClientOptions() []sdk.Option { + return nil +} + +type GetResourceOpts func(*core.Device) deviceCoap.OptionFunc + +func FindDeviceByName(ctx context.Context, name string, getResourceOpts ...GetResourceOpts) (deviceID string, _ error) { + client := core.NewClient() + + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + h := findDeviceIDByNameHandler{ + name: name, + cancel: cancel, + getResourceOptions: getResourceOpts, + } + + err := client.GetDevicesByMulticast(ctx, core.DefaultDiscoveryConfiguration(), &h) + if err != nil { + return "", fmt.Errorf("could not find the device named %s: %w", name, err) + } + id, ok := h.id.Load().(string) + if !ok || id == "" { + return "", fmt.Errorf("could not find the device named %s: not found", name) + } + return id, nil +} + +type findDeviceIDByNameHandler struct { + id atomic.Value + name string + cancel context.CancelFunc + getResourceOptions []GetResourceOpts +} + +func (h *findDeviceIDByNameHandler) Handle(ctx context.Context, dev *core.Device) { + defer func() { + if errC := dev.Close(ctx); errC != nil { + h.Error(errC) + } + }() + deviceLinks, err := dev.GetResourceLinks(ctx, dev.GetEndpoints()) + if err != nil { + h.Error(err) + return + } + l, ok := deviceLinks.GetResourceLink(device.ResourceURI) + if !ok { + return + } + var d device.Device + var getResourceOpts []deviceCoap.OptionFunc + if h.getResourceOptions != nil { + for _, opts := range h.getResourceOptions { + getResourceOpts = append(getResourceOpts, opts(dev)) + } + } + err = dev.GetResource(ctx, l, &d, getResourceOpts...) + if err != nil { + h.Error(err) + return + } + if d.Name == h.name { + h.id.Store(d.ID) + h.cancel() + } +} + +func (h *findDeviceIDByNameHandler) Error(err error) { + if errors.Is(err, context.Canceled) { + return + } + log.Printf("find device ID by name handler error: %v", err.Error()) +} diff --git a/test/device/ocf/device.go b/test/device/ocf/device.go new file mode 100644 index 000000000..3000bfea4 --- /dev/null +++ b/test/device/ocf/device.go @@ -0,0 +1,133 @@ +/**************************************************************************** + * + * Copyright (c) 2024 plgd.dev s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"), + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +package ocf + +import ( + "math" + "time" + + "github.com/plgd-dev/device/v2/schema" + "github.com/plgd-dev/device/v2/schema/collection" + "github.com/plgd-dev/device/v2/schema/configuration" + schemaDevice "github.com/plgd-dev/device/v2/schema/device" + "github.com/plgd-dev/device/v2/schema/interfaces" + "github.com/plgd-dev/device/v2/schema/maintenance" + "github.com/plgd-dev/device/v2/schema/platform" + "github.com/plgd-dev/device/v2/schema/plgdtime" + "github.com/plgd-dev/device/v2/schema/softwareupdate" + "github.com/plgd-dev/device/v2/test/resource/types" + "github.com/plgd-dev/hub/v2/test/device" +) + +var TestResources = []schema.ResourceLink{ + { + Href: platform.ResourceURI, + ResourceTypes: []string{platform.ResourceType}, + Interfaces: []string{interfaces.OC_IF_R, interfaces.OC_IF_BASELINE}, + Policy: &schema.Policy{ + BitMask: 3, + }, + }, + + { + Href: schemaDevice.ResourceURI, + ResourceTypes: []string{types.DEVICE_CLOUD, schemaDevice.ResourceType}, + Interfaces: []string{interfaces.OC_IF_R, interfaces.OC_IF_BASELINE}, + Policy: &schema.Policy{ + BitMask: 3, + }, + }, + + { + Href: configuration.ResourceURI, + ResourceTypes: []string{configuration.ResourceType}, + Interfaces: []string{interfaces.OC_IF_RW, interfaces.OC_IF_BASELINE}, + Policy: &schema.Policy{ + BitMask: 3, + }, + }, + + { + Href: "/light/1", + ResourceTypes: []string{types.CORE_LIGHT}, + Interfaces: []string{interfaces.OC_IF_RW, interfaces.OC_IF_BASELINE}, + Policy: &schema.Policy{ + BitMask: 3, + }, + }, + + { + Href: "/switches", + ResourceTypes: []string{collection.ResourceType}, + Interfaces: []string{interfaces.OC_IF_LL, interfaces.OC_IF_CREATE, interfaces.OC_IF_B, interfaces.OC_IF_BASELINE}, + Policy: &schema.Policy{ + BitMask: 3, + }, + }, + + { + Href: maintenance.ResourceURI, + ResourceTypes: []string{maintenance.ResourceType}, + Interfaces: []string{interfaces.OC_IF_RW, interfaces.OC_IF_BASELINE}, + Policy: &schema.Policy{ + BitMask: 1, + }, + }, + + { + Href: plgdtime.ResourceURI, + ResourceTypes: []string{plgdtime.ResourceType}, + Interfaces: []string{interfaces.OC_IF_RW, interfaces.OC_IF_BASELINE}, + Policy: &schema.Policy{ + BitMask: 3, + }, + }, + + { + Href: softwareupdate.ResourceURI, + ResourceTypes: []string{softwareupdate.ResourceType}, + Interfaces: []string{interfaces.OC_IF_RW, interfaces.OC_IF_BASELINE}, + Policy: &schema.Policy{ + BitMask: 3, + }, + }, +} + +type Device struct { + device.BaseDevice +} + +func NewDevice(id, name string) *Device { + return &Device{ + BaseDevice: device.MakeBaseDevice(id, name), + } +} + +func (d *Device) GetType() device.Type { + return device.OCF +} + +func (d *Device) GetRetryInterval(attempt int) time.Duration { + /* [2s, 4s, 8s, 16s, 32s, 64s] */ + return time.Duration(math.Exp2(float64(attempt))) * time.Second +} + +func (d *Device) GetDefaultResources() schema.ResourceLinks { + return TestResources +} diff --git a/test/http/request.go b/test/http/request.go index 96f6ef4da..17f64a5a8 100644 --- a/test/http/request.go +++ b/test/http/request.go @@ -3,6 +3,7 @@ package http import ( "context" "crypto/tls" + "errors" "fmt" "io" "net/http" @@ -42,7 +43,7 @@ func NewHTTPRequest(method, url string, body io.Reader) *HTTPRequestBuilder { } func (c *HTTPRequestBuilder) AuthToken(token string) *HTTPRequestBuilder { - c.header["Authorization"] = fmt.Sprintf("bearer %s", token) + c.header["Authorization"] = "bearer " + token return c } @@ -143,8 +144,8 @@ func DoHTTPRequest(t *testing.T, req *http.Request) *http.Response { } func ReadHTTPResponse(t *testing.T, w io.Reader, contentType string, data interface{}) { - readFrom := func(w io.Reader, v interface{}) error { - return fmt.Errorf("not supported") + readFrom := func(_ io.Reader, _ interface{}) error { + return errors.New("not supported") } switch contentType { case message.AppJSON.String(): @@ -159,7 +160,7 @@ func ReadHTTPResponse(t *testing.T, w io.Reader, contentType string, data interf } val := reflect.ValueOf(v) if val.Kind() != reflect.Ptr { - return fmt.Errorf("some: check must be a pointer") + return errors.New("some: check must be a pointer") } val.Elem().Set(reflect.ValueOf(string(b))) return nil diff --git a/test/iotivity-lite/service/deleteResource_test.go b/test/iotivity-lite/service/deleteResource_test.go index 090083ac6..185fdd616 100644 --- a/test/iotivity-lite/service/deleteResource_test.go +++ b/test/iotivity-lite/service/deleteResource_test.go @@ -20,6 +20,7 @@ import ( "github.com/plgd-dev/hub/v2/test/config" iotService "github.com/plgd-dev/hub/v2/test/iotivity-lite/service" oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test" + "github.com/plgd-dev/hub/v2/test/sdk" "github.com/plgd-dev/hub/v2/test/service" "github.com/stretchr/testify/require" "golang.org/x/exp/maps" @@ -142,7 +143,7 @@ func TestBatchDeleteResources(t *testing.T) { }, func(unpublished map[int64]struct{}) bool { return len(unpublished) == numSwitches }) - getHandler := func(s *coapgwTestService.Service, opts ...coapgwTestService.Option) coapgwTestService.ServiceHandler { + getHandler := func(*coapgwTestService.Service, ...coapgwTestService.Option) coapgwTestService.ServiceHandler { return bh } @@ -161,7 +162,7 @@ func TestBatchDeleteResources(t *testing.T) { ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -173,13 +174,13 @@ func TestBatchDeleteResources(t *testing.T) { // TODO: copy services initialization from the real coap-gw to the mock coap-gw, // for now we must force TCP when mock coap-gw is used // _, _ = test.OnboardDevSim(ctx, t, c, deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, nil) - _, _ = test.OnboardDevSim(ctx, t, c, deviceID, string(schema.TCPSecureScheme)+"://"+config.COAP_GW_HOST, nil) + _, shutdown := test.OnboardDevSim(ctx, t, c, deviceID, string(schema.TCPSecureScheme)+"://"+config.COAP_GW_HOST, nil) require.True(t, bh.WaitForFirstSignIn(time.Second*20)) t.Cleanup(func() { - test.DisownDevice(t, deviceID) + shutdown() }) - devClient, err := test.NewSDKClient() + devClient, err := sdk.NewClient() require.NoError(t, err) defer func() { _ = devClient.Close(ctx) diff --git a/test/iotivity-lite/service/offboard_test.go b/test/iotivity-lite/service/offboard_test.go index 6aec76563..7a44b2571 100644 --- a/test/iotivity-lite/service/offboard_test.go +++ b/test/iotivity-lite/service/offboard_test.go @@ -3,7 +3,7 @@ package service_test import ( "context" "crypto/tls" - "fmt" + "errors" "strings" "sync" "testing" @@ -21,7 +21,6 @@ import ( iotService "github.com/plgd-dev/hub/v2/test/iotivity-lite/service" oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test" "github.com/plgd-dev/hub/v2/test/service" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/atomic" "google.golang.org/grpc" @@ -30,7 +29,7 @@ import ( // signed in -> deregister by sending DELETE request func TestOffboard(t *testing.T) { - deviceID := test.MustFindDeviceByName(test.TestDeviceName) + d := test.MustFindTestDevice() deadline := time.Now().Add(time.Minute) ctx, cancel := context.WithDeadline(context.Background(), deadline) @@ -41,7 +40,7 @@ func TestOffboard(t *testing.T) { defer tearDown() ch := iotService.NewCoapHandlerWithCounter(0) - makeHandler := func(s *coapgwTestService.Service, opts ...coapgwTestService.Option) coapgwTestService.ServiceHandler { + makeHandler := func(*coapgwTestService.Service, ...coapgwTestService.Option) coapgwTestService.ServiceHandler { return ch } @@ -50,13 +49,13 @@ func TestOffboard(t *testing.T) { log.Debugf("%+v", h.CallCounter.Data) signInCount, ok := h.CallCounter.Data[iotService.SignInKey] require.True(t, ok) - require.True(t, signInCount > 0) + require.Greater(t, signInCount, 0) publishCount, ok := h.CallCounter.Data[iotService.PublishKey] require.True(t, ok) require.Equal(t, 1, publishCount) singOffCount, ok := h.CallCounter.Data[iotService.SignOffKey] require.True(t, ok) - require.True(t, singOffCount > 0) + require.Greater(t, singOffCount, 0) } coapShutdown := coapgwTest.SetUp(t, makeHandler, validateHandler) @@ -64,7 +63,7 @@ func TestOffboard(t *testing.T) { ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -75,14 +74,14 @@ func TestOffboard(t *testing.T) { // TODO: copy services initialization from the real coap-gw to the mock coap-gw, // for now we must force TCP when mock coap-gw is used - // _, _ = test.OnboardDevSim(ctx, t, c, deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, nil) - _, _ = test.OnboardDevSim(ctx, t, c, deviceID, string(schema.TCPSecureScheme)+"://"+config.COAP_GW_HOST, nil) + // shutdown := test.OnboardDevice(ctx, t, c, d, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, nil) + shutdown := test.OnboardDevice(ctx, t, c, d, string(schema.TCPSecureScheme)+"://"+config.COAP_GW_HOST, nil) require.True(t, ch.WaitForFirstSignIn(time.Second*20)) t.Cleanup(func() { - test.DisownDevice(t, deviceID) + shutdown() }) - test.OffBoardDevSim(ctx, t, deviceID) + test.OffboardDevice(ctx, t, d) require.True(t, ch.WaitForFirstSignOff(time.Second*20)) } @@ -120,7 +119,7 @@ func (sh *switchableHandler) blockSignInChannel() chan struct{} { func (sh *switchableHandler) SignIn(req coapgwService.CoapSignInReq) (coapgwService.CoapSignInResp, error) { resp, err := sh.CoapHandlerWithCounter.SignIn(req) if sh.failSignIn.Load() { - return coapgwService.CoapSignInResp{}, fmt.Errorf("sign in disabled") + return coapgwService.CoapSignInResp{}, errors.New("sign in disabled") } b := sh.blockSignInChannel() if b != nil { @@ -146,7 +145,7 @@ func (sh *switchableHandler) blockSignOff() chan struct{} { func (sh *switchableHandler) SignOff() error { err := sh.CoapHandlerWithCounter.SignOff() if sh.failSignOff.Load() { - return fmt.Errorf("sign off disabled") + return errors.New("sign off disabled") } b := sh.blockSignOffChannel() if b != nil { @@ -172,7 +171,7 @@ func (sh *switchableHandler) blockRefresh() chan struct{} { func (sh *switchableHandler) RefreshToken(req coapgwService.CoapRefreshTokenReq) (coapgwService.CoapRefreshTokenResp, error) { resp, err := sh.CoapHandlerWithCounter.RefreshToken(req) if sh.failRefreshToken.Load() { - return coapgwService.CoapRefreshTokenResp{}, fmt.Errorf("refresh token disabled") + return coapgwService.CoapRefreshTokenResp{}, errors.New("refresh token disabled") } b := sh.blockRefreshChannel() if b != nil { @@ -183,7 +182,7 @@ func (sh *switchableHandler) RefreshToken(req coapgwService.CoapRefreshTokenReq) // not signed in, with permanent and short access-token -> deregister by sending DELETE request with access token func TestOffboardWithoutSignIn(t *testing.T) { - deviceID := test.MustFindDeviceByName(test.TestDeviceName) + d := test.MustFindTestDevice() deadline := time.Now().Add(time.Minute) ctx, cancel := context.WithDeadline(context.Background(), deadline) @@ -195,7 +194,7 @@ func TestOffboardWithoutSignIn(t *testing.T) { sh := NewSwitchableHandler(-1) sh.failSignIn.Store(true) - makeHandler := func(s *coapgwTestService.Service, opts ...coapgwTestService.Option) coapgwTestService.ServiceHandler { + makeHandler := func(*coapgwTestService.Service, ...coapgwTestService.Option) coapgwTestService.ServiceHandler { return sh } @@ -206,19 +205,8 @@ func TestOffboardWithoutSignIn(t *testing.T) { log.Debugf("%+v", h.CallCounter.Data) signInCount, ok := h.CallCounter.Data[iotService.SignInKey] require.True(t, ok) - // sometimes the first sign-in attempt fails, so we allow 2 attempts - /* - === RUN TestOffboardWithoutSignIn - offboard_test.go:205: - Error Trace: /src/github.com/plgd-dev/hub/test/iotivity-lite/service/offboard_test.go:205 - /src/github.com/plgd-dev/hub/test/coap-gateway/test/test.go:59 - /src/github.com/plgd-dev/hub/test/iotivity-lite/service/offboard_test.go:236 - Error: Not equal: - expected: 1 - actual : 2 - Test: TestOffboardWithoutSignIn - */ - require.True(t, signInCount >= 1 || signInCount <= 2) + // depending on the timing of the test, the sign-in may be called once or twice + require.True(t, signInCount >= 1 && signInCount <= 2) _, ok = h.CallCounter.Data[iotService.RefreshTokenKey] require.False(t, ok) signOffCount, ok := h.CallCounter.Data[iotService.SignOffKey] @@ -231,7 +219,7 @@ func TestOffboardWithoutSignIn(t *testing.T) { ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -240,22 +228,22 @@ func TestOffboardWithoutSignIn(t *testing.T) { }() c := pb.NewGrpcGatewayClient(conn) - // deviceID, _ = test.OnboardDevSim(ctx, t, c, deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, nil) - deviceID, _ = test.OnboardDevSim(ctx, t, c, deviceID, string(schema.TCPSecureScheme)+"://"+config.COAP_GW_HOST, nil) + // shutdown := test.OnboardDevSim(ctx, t, c, d, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, nil) + shutdown := test.OnboardDevice(ctx, t, c, d, string(schema.TCPSecureScheme)+"://"+config.COAP_GW_HOST, nil) t.Cleanup(func() { - test.DisownDevice(t, deviceID) + shutdown() }) require.True(t, sh.WaitForFirstSignIn(time.Second*20)) // first retry after failure is after 2 seconds, so hopefully it doesn't trigger, if this test // behaves flakily then we will have to update simulator to have a configurable retry sh.failSignIn.Store(false) - // wait for sign-in to be called again - time.Sleep(time.Second * 3) - test.OffBoardDevSim(ctx, t, deviceID) + time.Sleep(d.GetRetryInterval(1) + time.Second) + test.OffboardDevice(ctx, t, d) require.True(t, sh.WaitForFirstSignOff(time.Second*20)) } // not signed in, with permanent but long access-token -> try to sign in and then deregister without access token +// OCF device specific behavior func TestOffboardWithSignIn(t *testing.T) { deviceID := test.MustFindDeviceByName(test.TestDeviceName) @@ -270,7 +258,7 @@ func TestOffboardWithSignIn(t *testing.T) { sh := NewSwitchableHandler(-1) sh.failSignIn.Store(true) sh.SetAccessToken(strings.Repeat("this-access-token-is-so-long-that-its-size-is-longer-than-the-allowed-request-header-size", 5)) - makeHandler := func(s *coapgwTestService.Service, opts ...coapgwTestService.Option) coapgwTestService.ServiceHandler { + makeHandler := func(*coapgwTestService.Service, ...coapgwTestService.Option) coapgwTestService.ServiceHandler { return sh } @@ -294,7 +282,7 @@ func TestOffboardWithSignIn(t *testing.T) { ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -303,10 +291,10 @@ func TestOffboardWithSignIn(t *testing.T) { }() c := pb.NewGrpcGatewayClient(conn) - // deviceID, _ = test.OnboardDevSim(ctx, t, c, deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, nil) - deviceID, _ = test.OnboardDevSim(ctx, t, c, deviceID, string(schema.TCPSecureScheme)+"://"+config.COAP_GW_HOST, nil) + // deviceID, shutdown := test.OnboardDevSim(ctx, t, c, deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, nil) + deviceID, shutdown := test.OnboardDevSim(ctx, t, c, deviceID, string(schema.TCPSecureScheme)+"://"+config.COAP_GW_HOST, nil) t.Cleanup(func() { - test.DisownDevice(t, deviceID) + shutdown() }) require.True(t, sh.WaitForFirstSignIn(time.Second*20)) @@ -320,6 +308,7 @@ func TestOffboardWithSignIn(t *testing.T) { } // not signed up, with refresh token -> try to login and then deregister without access token +// OCF device specific behavior func TestOffboardWithSignInByRefreshToken(t *testing.T) { deviceID := test.MustFindDeviceByName(test.TestDeviceName) @@ -334,7 +323,7 @@ func TestOffboardWithSignInByRefreshToken(t *testing.T) { sh := NewSwitchableHandler(20) sh.failSignIn.Store(true) sh.SetAccessToken(strings.Repeat("this-access-token-is-so-long-that-its-size-is-longer-than-the-allowed-request-header-size", 5)) - makeHandler := func(s *coapgwTestService.Service, opts ...coapgwTestService.Option) coapgwTestService.ServiceHandler { + makeHandler := func(*coapgwTestService.Service, ...coapgwTestService.Option) coapgwTestService.ServiceHandler { return sh } @@ -344,21 +333,21 @@ func TestOffboardWithSignInByRefreshToken(t *testing.T) { defer h.CallCounter.Lock.Unlock() log.Debugf("%+v", h.CallCounter.Data) signInCount, ok := h.CallCounter.Data[iotService.SignInKey] - assert.True(t, ok) - assert.True(t, signInCount > 1) + require.True(t, ok) + require.Greater(t, signInCount, 1) refreshCount, ok := h.CallCounter.Data[iotService.RefreshTokenKey] - assert.True(t, ok) - assert.True(t, refreshCount > 0) + require.True(t, ok) + require.Greater(t, refreshCount, 0) signOffCount, ok := h.CallCounter.Data[iotService.SignOffKey] - assert.True(t, ok) - assert.Equal(t, 1, signOffCount) + require.True(t, ok) + require.Equal(t, 1, signOffCount) } - coapShutdown := coapgwTest.SetUp(t, makeHandler, func(handler coapgwTestService.ServiceHandler) {}) + coapShutdown := coapgwTest.SetUp(t, makeHandler, func(coapgwTestService.ServiceHandler) {}) ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -369,9 +358,9 @@ func TestOffboardWithSignInByRefreshToken(t *testing.T) { // register device first time // deviceID, _ = test.OnboardDevSim(ctx, t, c, deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, nil) - deviceID, _ = test.OnboardDevSim(ctx, t, c, deviceID, string(schema.TCPSecureScheme)+"://"+config.COAP_GW_HOST, nil) + deviceID, shutdown := test.OnboardDevSim(ctx, t, c, deviceID, string(schema.TCPSecureScheme)+"://"+config.COAP_GW_HOST, nil) t.Cleanup(func() { - test.DisownDevice(t, deviceID) + shutdown() }) require.True(t, sh.WaitForFirstSignIn(time.Second*20)) @@ -398,7 +387,7 @@ func TestOffboardWithSignInByRefreshToken(t *testing.T) { // Multiple offboard attempts should be ignored func TestOffboardWithRepeat(t *testing.T) { - deviceID := test.MustFindDeviceByName(test.TestDeviceName) + d := test.MustFindTestDevice() deadline := time.Now().Add(time.Minute) ctx, cancel := context.WithDeadline(context.Background(), deadline) @@ -410,7 +399,7 @@ func TestOffboardWithRepeat(t *testing.T) { sh := NewSwitchableHandler(-1) sh.blockSignOff() - makeHandler := func(s *coapgwTestService.Service, opts ...coapgwTestService.Option) coapgwTestService.ServiceHandler { + makeHandler := func(*coapgwTestService.Service, ...coapgwTestService.Option) coapgwTestService.ServiceHandler { return sh } @@ -429,7 +418,7 @@ func TestOffboardWithRepeat(t *testing.T) { ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -439,17 +428,17 @@ func TestOffboardWithRepeat(t *testing.T) { c := pb.NewGrpcGatewayClient(conn) // deviceID, _ = test.OnboardDevSim(ctx, t, c, deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, nil) - deviceID, _ = test.OnboardDevSim(ctx, t, c, deviceID, string(schema.TCPSecureScheme)+"://"+config.COAP_GW_HOST, nil) + shutdown := test.OnboardDevice(ctx, t, c, d, string(schema.TCPSecureScheme)+"://"+config.COAP_GW_HOST, nil) t.Cleanup(func() { - test.DisownDevice(t, deviceID) + shutdown() }) require.True(t, sh.WaitForFirstSignIn(time.Second*20)) time.Sleep(time.Second) - test.OffBoardDevSim(ctx, t, deviceID) - test.OffBoardDevSim(ctx, t, deviceID) - test.OffBoardDevSim(ctx, t, deviceID) + test.OffboardDevice(ctx, t, d) + test.OffboardDevice(ctx, t, d) + test.OffboardDevice(ctx, t, d) require.True(t, sh.WaitForFirstSignOff(time.Second*20)) // first SignOff should timeout after 10 secs, we wait 10 additional seconds for the other @@ -458,6 +447,7 @@ func TestOffboardWithRepeat(t *testing.T) { } // Onboarding should interrupt an ongoing offboarding +// OCF device specific behavior func TestOffboardInterrupt(t *testing.T) { deviceID := test.MustFindDeviceByName(test.TestDeviceName) @@ -476,7 +466,7 @@ func TestOffboardInterrupt(t *testing.T) { // off-boarding will try login by refresh token, but block the sending of response blockRefreshCh := sh.blockRefresh() - makeHandler := func(s *coapgwTestService.Service, opts ...coapgwTestService.Option) coapgwTestService.ServiceHandler { + makeHandler := func(*coapgwTestService.Service, ...coapgwTestService.Option) coapgwTestService.ServiceHandler { return sh } @@ -493,7 +483,7 @@ func TestOffboardInterrupt(t *testing.T) { ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) diff --git a/test/iotivity-lite/service/republish_test.go b/test/iotivity-lite/service/republish_test.go index 70d7f0bec..9e43def74 100644 --- a/test/iotivity-lite/service/republish_test.go +++ b/test/iotivity-lite/service/republish_test.go @@ -23,7 +23,7 @@ import ( ) func TestRepublishAfterRefresh(t *testing.T) { - deviceID := test.MustFindDeviceByName(test.TestDeviceName) + d := test.MustFindTestDevice() atLifetime := time.Second * 20 deadline := time.Now().Add(time.Minute) @@ -34,7 +34,7 @@ func TestRepublishAfterRefresh(t *testing.T) { tearDown := service.SetUpServices(ctx, t, services) defer tearDown() - makeHandler := func(s *coapgwTestService.Service, opts ...coapgwTestService.Option) coapgwTestService.ServiceHandler { + makeHandler := func(*coapgwTestService.Service, ...coapgwTestService.Option) coapgwTestService.ServiceHandler { return iotService.NewCoapHandlerWithCounter(int64(atLifetime.Seconds())) } validateHandler := func(handler coapgwTestService.ServiceHandler) { @@ -42,10 +42,10 @@ func TestRepublishAfterRefresh(t *testing.T) { log.Debugf("%+v", h.CallCounter.Data) signInCount, ok := h.CallCounter.Data[iotService.SignInKey] require.True(t, ok) - require.True(t, signInCount > 1) + require.Greater(t, signInCount, 1) refreshCount, ok := h.CallCounter.Data[iotService.RefreshTokenKey] require.True(t, ok) - require.True(t, refreshCount > 0) + require.Greater(t, refreshCount, 0) publishCount, ok := h.CallCounter.Data[iotService.PublishKey] require.True(t, ok) require.Equal(t, 1, publishCount) @@ -56,7 +56,7 @@ func TestRepublishAfterRefresh(t *testing.T) { ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -65,8 +65,8 @@ func TestRepublishAfterRefresh(t *testing.T) { }() c := pb.NewGrpcGatewayClient(conn) - // _, shutdownDevSim := test.OnboardDevSim(ctx, t, c, deviceID, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, nil) - _, shutdownDevSim := test.OnboardDevSim(ctx, t, c, deviceID, string(schema.TCPSecureScheme)+"://"+config.COAP_GW_HOST, nil) + // shutdownDevSim := test.OnboardDevice(ctx, t, c, d, config.ACTIVE_COAP_SCHEME+"://"+config.COAP_GW_HOST, nil) + shutdownDevSim := test.OnboardDevice(ctx, t, c, d, string(schema.TCPSecureScheme)+"://"+config.COAP_GW_HOST, nil) defer shutdownDevSim() for { diff --git a/test/oauth-server/service/getJWKs_test.go b/test/oauth-server/service/getJWKs_test.go index 8a7994372..2c3e10792 100644 --- a/test/oauth-server/service/getJWKs_test.go +++ b/test/oauth-server/service/getJWKs_test.go @@ -29,6 +29,6 @@ func getJWKs(t *testing.T) map[string]interface{} { err := json.ReadFrom(res.Body, &body) require.NoError(t, err) require.NotEmpty(t, body["keys"]) - require.Equal(t, 2, len(body["keys"].([]interface{}))) + require.Len(t, body["keys"].([]interface{}), 2) return body } diff --git a/test/oauth-server/service/loadKeys.go b/test/oauth-server/service/loadKeys.go index 908199024..683dadf40 100644 --- a/test/oauth-server/service/loadKeys.go +++ b/test/oauth-server/service/loadKeys.go @@ -3,7 +3,7 @@ package service import ( "crypto/x509" "encoding/pem" - "fmt" + "errors" "github.com/plgd-dev/hub/v2/pkg/config/property/urischeme" ) @@ -15,7 +15,7 @@ func LoadPrivateKey(path urischeme.URIScheme) (interface{}, error) { } certDERBlock, _ := pem.Decode(certPEMBlock) if certDERBlock == nil { - return nil, fmt.Errorf("cannot decode pem block") + return nil, errors.New("cannot decode pem block") } if key, err := x509.ParsePKCS8PrivateKey(certDERBlock.Bytes); err == nil { @@ -27,5 +27,5 @@ func LoadPrivateKey(path urischeme.URIScheme) (interface{}, error) { if key, err := x509.ParsePKCS1PrivateKey(certDERBlock.Bytes); err == nil { return key, nil } - return nil, fmt.Errorf("unknown type") + return nil, errors.New("unknown type") } diff --git a/test/oauth-server/service/token.go b/test/oauth-server/service/token.go index cd53a91d5..50cd3e0e2 100644 --- a/test/oauth-server/service/token.go +++ b/test/oauth-server/service/token.go @@ -4,6 +4,7 @@ import ( "crypto/rand" "crypto/rsa" "encoding/hex" + "errors" "fmt" "net/http" "strings" @@ -234,7 +235,7 @@ func (requestHandler *RequestHandler) getToken(w http.ResponseWriter, r *http.Re if clientID == "" { clientID, _, ok = r.BasicAuth() if !ok { - writeError(w, fmt.Errorf("authorization header is not set"), http.StatusBadRequest) + writeError(w, errors.New("authorization header is not set"), http.StatusBadRequest) return } } @@ -303,7 +304,7 @@ func (requestHandler *RequestHandler) validateTokenRequest(clientCfg *Client, to return fmt.Errorf("client(%v) not found", tokenReq.ClientID) } if clientCfg.ClientSecret != "" && clientCfg.ClientSecret != tokenReq.Password { - return fmt.Errorf("invalid client secret") + return errors.New("invalid client secret") } if clientCfg.RequiredRedirectURI != "" && clientCfg.RequiredRedirectURI != tokenReq.RedirectURI { return fmt.Errorf("invalid redirect uri(%v)", tokenReq.RedirectURI) diff --git a/test/oauth-server/service/token_test.go b/test/oauth-server/service/token_test.go index 2a148195e..a2ebf51c1 100644 --- a/test/oauth-server/service/token_test.go +++ b/test/oauth-server/service/token_test.go @@ -63,7 +63,7 @@ func TestRequestHandlerGetTokenWithDefaultScopes(t *testing.T) { token := getToken(t, test.ClientTest, "", "", "", code, "", "", "", service.AllowedGrantType_AUTHORIZATION_CODE, http.StatusOK) require.NotEmpty(t, token["access_token"]) - require.Equal(t, token["scope"], service.DefaultScope) + require.Equal(t, service.DefaultScope, token["scope"]) validator := test.GetJWTValidator(fmt.Sprintf("https://%s%s", config.OAUTH_SERVER_HOST, uri.JWKs)) accessToken, err := validator.Parse(token["access_token"]) require.NoError(t, err) @@ -78,7 +78,7 @@ func TestRequestHandlerGetTokenWithCuscomScopes(t *testing.T) { token := getToken(t, test.ClientTest, "", "", "", code, "", "", "", service.AllowedGrantType_AUTHORIZATION_CODE, http.StatusOK) require.NotEmpty(t, token["access_token"]) - require.Equal(t, token["scope"], "r:* w:*") + require.Equal(t, "r:* w:*", token["scope"]) validator := test.GetJWTValidator(fmt.Sprintf("https://%s%s", config.OAUTH_SERVER_HOST, uri.JWKs)) accessToken, err := validator.Parse(token["access_token"]) require.NoError(t, err) @@ -125,7 +125,7 @@ func TestRequestHandlerGetTokenWithValidRequiredParams(t *testing.T) { code := getAuthorize(t, test.ClientTestRequiredParams, "", "http://localhost:7777", "", "r:*", "code", http.StatusFound, false, false) token := getToken(t, test.ClientTestRequiredParams, test.ClientTestRequiredParamsSecret, "", "http://localhost:7777", code, "", "", "", service.AllowedGrantType_AUTHORIZATION_CODE, http.StatusOK) require.NotEmpty(t, token["access_token"]) - require.Equal(t, token["scope"], "r:*") + require.Equal(t, "r:*", token["scope"]) validator := test.GetJWTValidator(fmt.Sprintf("https://%s%s", config.OAUTH_SERVER_HOST, uri.JWKs)) accessToken, err := validator.Parse(token["access_token"]) require.NoError(t, err) @@ -153,7 +153,7 @@ func TestRequestHandlerGetTokenWithValidRefreshToken(t *testing.T) { token := getToken(t, test.ClientTest, test.ClientTestRequiredParamsSecret, "invalidRefreshToken", "http://localhost:7777", "", "refresh-token", "", "", service.AllowedGrantType_REFRESH_TOKEN, http.StatusOK) require.NotEmpty(t, token["access_token"]) - require.Equal(t, token["scope"], service.DefaultScope) + require.Equal(t, service.DefaultScope, token["scope"]) validator := test.GetJWTValidator(fmt.Sprintf("https://%s%s", config.OAUTH_SERVER_HOST, uri.JWKs)) accessToken, err := validator.Parse(token["access_token"]) require.NoError(t, err) @@ -229,12 +229,12 @@ func TestGetRequestHandlerGetTokenWithDeviceIDAndOwnerClaim(t *testing.T) { if tt.wantDeviceID == "" { require.Empty(t, claims[uri.DeviceIDClaimKey]) } else { - require.Equal(t, claims[uri.DeviceIDClaimKey], tt.wantDeviceID) + require.Equal(t, tt.wantDeviceID, claims[uri.DeviceIDClaimKey]) } if tt.wantOwner == "" { require.Empty(t, claims[uri.OwnerClaimKey]) } else { - require.Equal(t, claims[uri.OwnerClaimKey], tt.wantOwner) + require.Equal(t, tt.wantOwner, claims[uri.OwnerClaimKey]) } }) } diff --git a/test/oauth-server/test/test.go b/test/oauth-server/test/test.go index 2f6871671..e91be0e8c 100644 --- a/test/oauth-server/test/test.go +++ b/test/oauth-server/test/test.go @@ -197,7 +197,7 @@ func HTTPDo(t require.TestingT, req *http.Request, followRedirect bool) *http.Re Transport: trans, } if !followRedirect { - c.CheckRedirect = func(req *http.Request, via []*http.Request) error { + c.CheckRedirect = func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse } } diff --git a/test/pb/device.go b/test/pb/device.go index a5c8b641c..652fef0ea 100644 --- a/test/pb/device.go +++ b/test/pb/device.go @@ -18,11 +18,12 @@ func CmpDeviceValues(t *testing.T, expected, got []*pbGrpc.Device) { dev.ProtocolIndependentId = "" dev.Metadata.Connection.Id = "" dev.Metadata.Connection.ConnectedAt = 0 + dev.Metadata.Connection.LocalEndpoints = nil dev.Metadata.Connection.ServiceId = "" - if dev.Metadata.TwinSynchronization != nil { - dev.Metadata.TwinSynchronization.SyncingAt = 0 - dev.Metadata.TwinSynchronization.InSyncAt = 0 - dev.Metadata.TwinSynchronization.CommandMetadata = nil + if dev.GetMetadata().GetTwinSynchronization() != nil { + dev.GetMetadata().GetTwinSynchronization().SyncingAt = 0 + dev.GetMetadata().GetTwinSynchronization().InSyncAt = 0 + dev.GetMetadata().GetTwinSynchronization().CommandMetadata = nil } dev.Data = nil } @@ -61,6 +62,7 @@ func CleanUpDeviceMetadataUpdated(e *events.DeviceMetadataUpdated, resetCorrelat } e.GetConnection().Id = "" e.GetConnection().ConnectedAt = 0 + e.GetConnection().LocalEndpoints = nil } if e.GetTwinSynchronization() != nil { e.GetTwinSynchronization().CommandMetadata = nil @@ -71,7 +73,7 @@ func CleanUpDeviceMetadataUpdated(e *events.DeviceMetadataUpdated, resetCorrelat } func CleanUpDeviceMetadataSnapshotTaken(e *events.DeviceMetadataSnapshotTaken, resetCorrelationID bool) { - CleanUpDeviceMetadataUpdated(e.DeviceMetadataUpdated, resetCorrelationID) + CleanUpDeviceMetadataUpdated(e.GetDeviceMetadataUpdated(), resetCorrelationID) e.EventMetadata = nil } diff --git a/test/pb/event.go b/test/pb/event.go index d56d53812..e32c9d45f 100644 --- a/test/pb/event.go +++ b/test/pb/event.go @@ -13,6 +13,7 @@ import ( "github.com/plgd-dev/hub/v2/resource-aggregate/events" "github.com/plgd-dev/hub/v2/test" oauthService "github.com/plgd-dev/hub/v2/test/oauth-server/service" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -180,10 +181,10 @@ var cleanupEventFn = map[string]func(ev *pb.Event){ getTypeName(&pb.Event_ResourceChanged{}): func(ev *pb.Event) { CleanUpResourceChanged(ev.GetResourceChanged(), false) }, - getTypeName(&pb.Event_OperationProcessed_{}): func(ev *pb.Event) { + getTypeName(&pb.Event_OperationProcessed_{}): func(*pb.Event) { // nothing to do }, - getTypeName(&pb.Event_SubscriptionCanceled_{}): func(ev *pb.Event) { + getTypeName(&pb.Event_SubscriptionCanceled_{}): func(*pb.Event) { // nothing to do }, getTypeName(&pb.Event_ResourceUpdatePending{}): func(ev *pb.Event) { @@ -283,34 +284,34 @@ var compareEventFn = map[string]func(t *testing.T, e, g *pb.Event, cmpInterface getTypeName(&pb.Event_ResourceChanged{}): func(t *testing.T, e, g *pb.Event, cmpInterface string) { CmpResourceChanged(t, e.GetResourceChanged(), g.GetResourceChanged(), cmpInterface) }, - getTypeName(&pb.Event_ResourceUpdatePending{}): func(t *testing.T, e, g *pb.Event, cmpInterface string) { //nolint:unparam + getTypeName(&pb.Event_ResourceUpdatePending{}): func(t *testing.T, e, g *pb.Event, _ string) { CmpResourceUpdatePending(t, e.GetResourceUpdatePending(), g.GetResourceUpdatePending()) }, - getTypeName(&pb.Event_ResourceUpdated{}): func(t *testing.T, e, g *pb.Event, cmpInterface string) { //nolint:unparam + getTypeName(&pb.Event_ResourceUpdated{}): func(t *testing.T, e, g *pb.Event, _ string) { CmpResourceUpdated(t, e.GetResourceUpdated(), g.GetResourceUpdated()) }, - getTypeName(&pb.Event_ResourceRetrievePending{}): func(t *testing.T, e, g *pb.Event, cmpInterface string) { //nolint:unparam + getTypeName(&pb.Event_ResourceRetrievePending{}): func(t *testing.T, e, g *pb.Event, _ string) { CmpResourceRetrievePending(t, e.GetResourceRetrievePending(), g.GetResourceRetrievePending()) }, - getTypeName(&pb.Event_ResourceRetrieved{}): func(t *testing.T, e, g *pb.Event, cmpInterface string) { //nolint:unparam + getTypeName(&pb.Event_ResourceRetrieved{}): func(t *testing.T, e, g *pb.Event, _ string) { CmpResourceRetrieved(t, e.GetResourceRetrieved(), g.GetResourceRetrieved()) }, - getTypeName(&pb.Event_ResourceDeletePending{}): func(t *testing.T, e, g *pb.Event, cmpInterface string) { //nolint:unparam + getTypeName(&pb.Event_ResourceDeletePending{}): func(t *testing.T, e, g *pb.Event, _ string) { CmpResourceDeletePending(t, e.GetResourceDeletePending(), g.GetResourceDeletePending()) }, - getTypeName(&pb.Event_ResourceDeleted{}): func(t *testing.T, e, g *pb.Event, cmpInterface string) { //nolint:unparam + getTypeName(&pb.Event_ResourceDeleted{}): func(t *testing.T, e, g *pb.Event, _ string) { CmpResourceDeleted(t, e.GetResourceDeleted(), g.GetResourceDeleted()) }, - getTypeName(&pb.Event_ResourceCreatePending{}): func(t *testing.T, e, g *pb.Event, cmpInterface string) { //nolint:unparam + getTypeName(&pb.Event_ResourceCreatePending{}): func(t *testing.T, e, g *pb.Event, _ string) { CmpResourceCreatePending(t, e.GetResourceCreatePending(), g.GetResourceCreatePending()) }, - getTypeName(&pb.Event_ResourceCreated{}): func(t *testing.T, e, g *pb.Event, cmpInterface string) { //nolint:unparam + getTypeName(&pb.Event_ResourceCreated{}): func(t *testing.T, e, g *pb.Event, _ string) { CmpResourceCreated(t, e.GetResourceCreated(), g.GetResourceCreated()) }, - getTypeName(&pb.Event_DeviceMetadataUpdatePending{}): func(t *testing.T, e, g *pb.Event, cmpInterface string) { //nolint:unparam + getTypeName(&pb.Event_DeviceMetadataUpdatePending{}): func(t *testing.T, e, g *pb.Event, _ string) { CmpDeviceMetadataUpdatePending(t, e.GetDeviceMetadataUpdatePending(), g.GetDeviceMetadataUpdatePending()) }, - getTypeName(&pb.Event_DeviceMetadataUpdated{}): func(t *testing.T, e, g *pb.Event, cmpInterface string) { //nolint:unparam + getTypeName(&pb.Event_DeviceMetadataUpdated{}): func(t *testing.T, e, g *pb.Event, _ string) { CmpDeviceMetadataUpdated(t, e.GetDeviceMetadataUpdated(), g.GetDeviceMetadataUpdated()) }, } @@ -319,7 +320,7 @@ func CmpEvent(t *testing.T, expected, got *pb.Event, cmpInterface string) { require.Equal(t, GetEventType(expected), GetEventType(got)) cmp, ok := compareEventFn[GetEventType(expected)] if !ok { - cmp = func(t *testing.T, e, g *pb.Event, cmpInterface string) { + cmp = func(t *testing.T, e, g *pb.Event, _ string) { CleanUpEvent(t, e) CleanUpEvent(t, g) test.CheckProtobufs(t, e, g, test.RequireToCheckFunc(require.Equal)) @@ -329,6 +330,26 @@ func CmpEvent(t *testing.T, expected, got *pb.Event, cmpInterface string) { cmp(t, expected, got, cmpInterface) } +func AssertCmpEvents(t *testing.T, expected, got []*pb.Event) { + assert.Len(t, got, len(expected)) + + // normalize + for i := range expected { + expected[i].SubscriptionId = "" + got[i].SubscriptionId = "" + CleanUpEvent(t, expected[i]) + CleanUpEvent(t, got[i]) + } + + // compare + for _, gotV := range got { + test.CheckProtobufs(t, expected, gotV, test.AssertToCheckFunc(assert.Contains)) + } + for _, expectedV := range expected { + test.CheckProtobufs(t, got, expectedV, test.AssertToCheckFunc(assert.Contains)) + } +} + func CmpEvents(t *testing.T, expected, got []*pb.Event) { require.Len(t, got, len(expected)) diff --git a/test/pb/pendingCommand.go b/test/pb/pendingCommand.go index bbaa9cc58..54d5f9753 100644 --- a/test/pb/pendingCommand.go +++ b/test/pb/pendingCommand.go @@ -93,7 +93,7 @@ func InitPendingEvents(ctx context.Context, t *testing.T) (pb.GrpcGatewayClient, token := oauthTest.GetDefaultAccessToken(t) ctx = kitNetGrpc.CtxWithToken(ctx, token) - conn, err := grpc.Dial(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + conn, err := grpc.NewClient(config.GRPC_GW_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -108,9 +108,9 @@ func InitPendingEvents(ctx context.Context, t *testing.T) (pb.GrpcGatewayClient, secureGWShutdown() createFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + createCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.CreateResource(ctx, &pb.CreateResourceRequest{ + _, errC := c.CreateResource(createCtx, &pb.CreateResourceRequest{ ResourceId: commands.NewResourceID(deviceID, test.TestResourceLightInstanceHref("1")), Content: &pb.Content{ ContentType: message.AppOcfCbor.String(), @@ -119,22 +119,22 @@ func InitPendingEvents(ctx context.Context, t *testing.T) (pb.GrpcGatewayClient, }), }, }) - require.Error(t, err) + require.Error(t, errC) } createFn() retrieveFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + retrieveCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.GetResourceFromDevice(ctx, &pb.GetResourceFromDeviceRequest{ + _, errG := c.GetResourceFromDevice(retrieveCtx, &pb.GetResourceFromDeviceRequest{ ResourceId: commands.NewResourceID(deviceID, test.TestResourceLightInstanceHref("1")), }) - require.Error(t, err) + require.Error(t, errG) } retrieveFn() updateFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + updateCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.UpdateResource(ctx, &pb.UpdateResourceRequest{ + _, errU := c.UpdateResource(updateCtx, &pb.UpdateResourceRequest{ ResourceId: commands.NewResourceID(deviceID, test.TestResourceLightInstanceHref("1")), Content: &pb.Content{ ContentType: message.AppOcfCbor.String(), @@ -143,26 +143,26 @@ func InitPendingEvents(ctx context.Context, t *testing.T) (pb.GrpcGatewayClient, }), }, }) - require.Error(t, err) + require.Error(t, errU) } updateFn() deleteFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + deleteCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.DeleteResource(ctx, &pb.DeleteResourceRequest{ + _, errD := c.DeleteResource(deleteCtx, &pb.DeleteResourceRequest{ ResourceId: commands.NewResourceID(deviceID, test.TestResourceLightInstanceHref("1")), }) - require.Error(t, err) + require.Error(t, errD) } deleteFn() updateDeviceMetadataFn := func() { - ctx, cancel := context.WithTimeout(ctx, time.Second) + updateCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() - _, err := c.UpdateDeviceMetadata(ctx, &pb.UpdateDeviceMetadataRequest{ + _, errU := c.UpdateDeviceMetadata(updateCtx, &pb.UpdateDeviceMetadataRequest{ DeviceId: deviceID, TwinEnabled: false, }) - require.Error(t, err) + require.Error(t, errU) } updateDeviceMetadataFn() updateDeviceMetadataFn() @@ -243,9 +243,9 @@ func CmpPendingCmds(t *testing.T, want []*pb.PendingCommand, got []*pb.PendingCo } func CmpCancelPendingCmdResponses(t *testing.T, want *pb.CancelPendingCommandsResponse, got *pb.CancelPendingCommandsResponse) { - sort.Strings(want.CorrelationIds) - sort.Strings(got.CorrelationIds) - require.Equal(t, want.CorrelationIds, got.CorrelationIds) + sort.Strings(want.GetCorrelationIds()) + sort.Strings(got.GetCorrelationIds()) + require.Equal(t, want.GetCorrelationIds(), got.GetCorrelationIds()) } func CleanUpResourceCreatePending(e *events.ResourceCreatePending, resetCorrelationID bool) *events.ResourceCreatePending { @@ -273,7 +273,7 @@ func CmpResourceCreatePending(t *testing.T, expected, got *events.ResourceCreate test.CheckProtobufs(t, expected, got, test.RequireToCheckFunc(require.Equal)) } -func MakeResourceCreatePending(t *testing.T, deviceID, href, correlationID string, data interface{}) *events.ResourceCreatePending { +func MakeResourceCreatePending(t *testing.T, deviceID, href string, resourceTypes []string, correlationID string, data interface{}) *events.ResourceCreatePending { return &events.ResourceCreatePending{ ResourceId: &commands.ResourceId{ DeviceId: deviceID, @@ -284,7 +284,8 @@ func MakeResourceCreatePending(t *testing.T, deviceID, href, correlationID strin CoapContentFormat: -1, Data: test.EncodeToCbor(t, data), }, - AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, correlationID, oauthService.DeviceUserID), + AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, correlationID, oauthService.DeviceUserID), + ResourceTypes: resourceTypes, } } @@ -313,7 +314,7 @@ func CmpResourceUpdatePending(t *testing.T, expected, got *events.ResourceUpdate test.CheckProtobufs(t, expected, got, test.RequireToCheckFunc(require.Equal)) } -func MakeResourceUpdatePending(t *testing.T, deviceID, href, correlationID string, data interface{}) *events.ResourceUpdatePending { +func MakeResourceUpdatePending(t *testing.T, deviceID, href string, resourceTypes []string, correlationID string, data interface{}) *events.ResourceUpdatePending { return &events.ResourceUpdatePending{ ResourceId: &commands.ResourceId{ DeviceId: deviceID, @@ -324,7 +325,8 @@ func MakeResourceUpdatePending(t *testing.T, deviceID, href, correlationID strin CoapContentFormat: -1, Data: test.EncodeToCbor(t, data), }, - AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, correlationID, oauthService.DeviceUserID), + AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, correlationID, oauthService.DeviceUserID), + ResourceTypes: resourceTypes, } } @@ -346,13 +348,14 @@ func CmpResourceRetrievePending(t *testing.T, expected, got *events.ResourceRetr test.CheckProtobufs(t, expected, got, test.RequireToCheckFunc(require.Equal)) } -func MakeResourceRetrievePending(deviceID, href, correlationID string) *events.ResourceRetrievePending { +func MakeResourceRetrievePending(deviceID, href string, resourceTypes []string, correlationID string) *events.ResourceRetrievePending { return &events.ResourceRetrievePending{ ResourceId: &commands.ResourceId{ DeviceId: deviceID, Href: href, }, - AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, correlationID, oauthService.DeviceUserID), + AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, correlationID, oauthService.DeviceUserID), + ResourceTypes: resourceTypes, } } @@ -374,10 +377,11 @@ func CmpResourceDeletePending(t *testing.T, expected, got *events.ResourceDelete test.CheckProtobufs(t, expected, got, test.RequireToCheckFunc(require.Equal)) } -func MakeResourceDeletePending(deviceID, href, correlationID string) *events.ResourceDeletePending { +func MakeResourceDeletePending(deviceID, href string, resourceTypes []string, correlationID string) *events.ResourceDeletePending { return &events.ResourceDeletePending{ - ResourceId: &commands.ResourceId{DeviceId: deviceID, Href: href}, - AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, correlationID, oauthService.DeviceUserID), + ResourceId: &commands.ResourceId{DeviceId: deviceID, Href: href}, + AuditContext: commands.NewAuditContext(oauthService.DeviceUserID, correlationID, oauthService.DeviceUserID), + ResourceTypes: resourceTypes, } } diff --git a/test/pb/resource.go b/test/pb/resource.go index de5b1b4d3..da32f6f4f 100644 --- a/test/pb/resource.go +++ b/test/pb/resource.go @@ -54,7 +54,7 @@ func MakeCreateSwitchResourceResponseData(id string) map[string]interface{} { } } -func MakeResourceCreated(t *testing.T, deviceID, href, correlationID string, data map[string]interface{}) *events.ResourceCreated { +func MakeResourceCreated(t *testing.T, deviceID, href string, resourceTypes []string, correlationID string, data map[string]interface{}) *events.ResourceCreated { return &events.ResourceCreated{ ResourceId: commands.NewResourceID(deviceID, href), Status: commands.Status_CREATED, @@ -68,7 +68,8 @@ func MakeResourceCreated(t *testing.T, deviceID, href, correlationID string, dat return test.EncodeToCbor(t, data) }(), }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, correlationID, service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, correlationID, service.DeviceUserID), + ResourceTypes: resourceTypes, } } @@ -180,26 +181,28 @@ func makeCborContent(t *testing.T, data interface{}) *commands.Content { } } -func MakeResourceChanged(t *testing.T, deviceID, href, correlationID string, data interface{}) *events.ResourceChanged { +func MakeResourceChanged(t *testing.T, deviceID, href string, resourceTypes []string, correlationID string, data interface{}) *events.ResourceChanged { return &events.ResourceChanged{ ResourceId: &commands.ResourceId{ DeviceId: deviceID, Href: href, }, - Status: commands.Status_OK, - Content: makeCborContent(t, data), - AuditContext: commands.NewAuditContext(service.DeviceUserID, correlationID, service.DeviceUserID), + Status: commands.Status_OK, + Content: makeCborContent(t, data), + AuditContext: commands.NewAuditContext(service.DeviceUserID, correlationID, service.DeviceUserID), + ResourceTypes: resourceTypes, } } -func MakeResourceDeleted(deviceID, href, correlationID string) *events.ResourceDeleted { +func MakeResourceDeleted(deviceID, href string, resourceTypes []string, correlationID string) *events.ResourceDeleted { return &events.ResourceDeleted{ ResourceId: commands.NewResourceID(deviceID, href), Status: commands.Status_OK, Content: &commands.Content{ CoapContentFormat: int32(-1), }, - AuditContext: commands.NewAuditContext(service.DeviceUserID, correlationID, service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, correlationID, service.DeviceUserID), + ResourceTypes: resourceTypes, } } @@ -262,15 +265,16 @@ func CmpResourceDeleted(t *testing.T, expected, got *events.ResourceDeleted) { test.CheckProtobufs(t, expected, got, test.RequireToCheckFunc(require.Equal)) } -func MakeResourceRetrieved(t *testing.T, deviceID, href, correlationID string, data interface{}) *events.ResourceRetrieved { +func MakeResourceRetrieved(t *testing.T, deviceID, href string, resourceTypes []string, correlationID string, data interface{}) *events.ResourceRetrieved { return &events.ResourceRetrieved{ ResourceId: &commands.ResourceId{ DeviceId: deviceID, Href: href, }, - Status: commands.Status_OK, - Content: makeCborContent(t, data), - AuditContext: commands.NewAuditContext(service.DeviceUserID, correlationID, service.DeviceUserID), + Status: commands.Status_OK, + Content: makeCborContent(t, data), + AuditContext: commands.NewAuditContext(service.DeviceUserID, correlationID, service.DeviceUserID), + ResourceTypes: resourceTypes, } } @@ -294,7 +298,7 @@ func CmpResourceRetrieved(t *testing.T, expected, got *events.ResourceRetrieved) test.CheckProtobufs(t, expected, got, test.RequireToCheckFunc(require.Equal)) } -func MakeResourceUpdated(t *testing.T, deviceID, href, correlationID string, data interface{}) *events.ResourceUpdated { +func MakeResourceUpdated(t *testing.T, deviceID, href string, resourceTypes []string, correlationID string, data interface{}) *events.ResourceUpdated { return &events.ResourceUpdated{ ResourceId: &commands.ResourceId{ DeviceId: deviceID, @@ -309,7 +313,8 @@ func MakeResourceUpdated(t *testing.T, deviceID, href, correlationID string, dat } return makeCborContent(t, data) }(), - AuditContext: commands.NewAuditContext(service.DeviceUserID, correlationID, service.DeviceUserID), + AuditContext: commands.NewAuditContext(service.DeviceUserID, correlationID, service.DeviceUserID), + ResourceTypes: resourceTypes, } } @@ -412,9 +417,9 @@ func CmpResourceValuesBasic(t *testing.T, expected, got []*pb.Resource) { } func CmpHubConfigurationResponse(t *testing.T, expected, got *pb.HubConfigurationResponse) { - require.NotEmpty(t, got.CertificateAuthorities) + require.NotEmpty(t, got.GetCertificateAuthorities()) got.CertificateAuthorities = "" - require.NotEqual(t, int64(0), got.CurrentTime) + require.NotEqual(t, int64(0), got.GetCurrentTime()) got.CurrentTime = 0 test.CheckProtobufs(t, expected, got, test.RequireToCheckFunc(require.Equal)) } diff --git a/test/pb/snapshot.go b/test/pb/snapshot.go index a8fa34d77..bf9c22aa0 100644 --- a/test/pb/snapshot.go +++ b/test/pb/snapshot.go @@ -15,7 +15,7 @@ func CleanUpResourceStateSnapshotTaken(e *events.ResourceStateSnapshotTaken, res if e.GetAuditContext() != nil && resetCorrelationID { e.GetAuditContext().CorrelationId = "" } - CleanUpResourceChanged(e.LatestResourceChange, resetCorrelationID) + CleanUpResourceChanged(e.GetLatestResourceChange(), resetCorrelationID) if e.GetLatestResourceChange().GetContent().GetData() != nil { e.LatestResourceChange.Content.Data = nil } diff --git a/test/sdkclient.go b/test/sdk/client.go similarity index 91% rename from test/sdkclient.go rename to test/sdk/client.go index 9343e9c4d..598511952 100644 --- a/test/sdkclient.go +++ b/test/sdk/client.go @@ -1,4 +1,4 @@ -package test +package sdk import ( "context" @@ -6,6 +6,7 @@ import ( "crypto/tls" "crypto/x509" "encoding/pem" + "errors" "fmt" "os" "time" @@ -22,7 +23,7 @@ type testSetupSecureClient struct { mfgCert tls.Certificate } -var errNotSet = fmt.Errorf("not set") +var errNotSet = errors.New("not set") func (c *testSetupSecureClient) GetManufacturerCertificate() (tls.Certificate, error) { if c.mfgCert.PrivateKey == nil { @@ -55,6 +56,8 @@ type sdkConfig struct { // TODO: replace by notBefore and notAfter validFrom string // RFC3339, or relative time such as now-1m validFor string // string parsable by time.ParseDuration + + useDeviceIDInQuery bool } // Option interface used for setting optional sdkConfig properties. @@ -92,6 +95,12 @@ func WithValidity(validFrom, validFor string) Option { }) } +func WithUseDeviceIDInQuery(useDeviceIDInQuery bool) Option { + return optionFunc(func(cfg *sdkConfig) { + cfg.useDeviceIDInQuery = useDeviceIDInQuery + }) +} + func getSDKConfig(opts ...Option) (*sdkConfig, error) { c := &sdkConfig{ id: CertIdentity, @@ -116,7 +125,7 @@ func getSDKConfig(opts ...Option) (*sdkConfig, error) { return c, nil } -func NewSDKClient(opts ...Option) (*client.Client, error) { +func NewClient(opts ...Option) (*client.Client, error) { c, err := getSDKConfig(opts...) if err != nil { return nil, err @@ -124,7 +133,7 @@ func NewSDKClient(opts ...Option) (*client.Client, error) { mfgTrustedCABlock, _ := pem.Decode(MfgTrustedCA) if mfgTrustedCABlock == nil { - return nil, fmt.Errorf("mfgTrustedCABlock is empty") + return nil, errors.New("mfgTrustedCABlock is empty") } mfgCA, err := x509.ParseCertificates(mfgTrustedCABlock.Bytes) if err != nil { @@ -137,16 +146,16 @@ func NewSDKClient(opts ...Option) (*client.Client, error) { identityIntermediateCABlock, _ := pem.Decode(identityIntermediateCA) if identityIntermediateCABlock == nil { - return nil, fmt.Errorf("identityIntermediateCABlock is empty") + return nil, errors.New("identityIntermediateCABlock is empty") } identityIntermediateCAKeyBlock, _ := pem.Decode(identityIntermediateCAKey) if identityIntermediateCAKeyBlock == nil { - return nil, fmt.Errorf("identityIntermediateCAKeyBlock is empty") + return nil, errors.New("identityIntermediateCAKeyBlock is empty") } identityTrustedCABlock, _ := pem.Decode(identityTrustedCA) if identityTrustedCABlock == nil { - return nil, fmt.Errorf("identityTrustedCABlock is empty") + return nil, errors.New("identityTrustedCABlock is empty") } identityTrustedCACert, err := x509.ParseCertificates(identityTrustedCABlock.Bytes) if err != nil { @@ -172,6 +181,7 @@ func NewSDKClient(opts ...Option) (*client.Client, error) { cfg := client.Config{ DisablePeerTCPSignalMessageCSMs: true, DeviceOwnershipSDK: devCfg, + UseDeviceIDInQuery: c.useDeviceIDInQuery, } client, err := client.NewClientFromConfig(&cfg, &testSetupSecureClient{ diff --git a/test/security/jwk.go b/test/security/jwk.go index d90d2563c..f373d487b 100644 --- a/test/security/jwk.go +++ b/test/security/jwk.go @@ -70,7 +70,7 @@ func NewTestJwks(t *testing.T) JWKServer { require.NoError(t, err) mux := http.NewServeMux() - mux.HandleFunc(jwksUri, func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc(jwksUri, func(w http.ResponseWriter, _ *http.Request) { if _, err := io.WriteString(w, string(jwks)); err != nil { log.Debugf("failed to write jwks: %v", err) } diff --git a/test/test.go b/test/test.go index 83b370bb5..605679d74 100644 --- a/test/test.go +++ b/test/test.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "log" "net" "os" "os/exec" @@ -15,12 +14,14 @@ import ( deviceClient "github.com/plgd-dev/device/v2/client" "github.com/plgd-dev/device/v2/client/core" + bridgeDevice "github.com/plgd-dev/device/v2/cmd/bridge-device/device" + deviceCoap "github.com/plgd-dev/device/v2/pkg/net/coap" "github.com/plgd-dev/device/v2/schema" "github.com/plgd-dev/device/v2/schema/acl" "github.com/plgd-dev/device/v2/schema/cloud" "github.com/plgd-dev/device/v2/schema/collection" "github.com/plgd-dev/device/v2/schema/configuration" - "github.com/plgd-dev/device/v2/schema/device" + schemaDevice "github.com/plgd-dev/device/v2/schema/device" "github.com/plgd-dev/device/v2/schema/interfaces" "github.com/plgd-dev/device/v2/schema/maintenance" "github.com/plgd-dev/device/v2/schema/platform" @@ -37,16 +38,20 @@ import ( "github.com/plgd-dev/hub/v2/resource-aggregate/commands" "github.com/plgd-dev/hub/v2/resource-aggregate/events" "github.com/plgd-dev/hub/v2/test/config" + "github.com/plgd-dev/hub/v2/test/device" + "github.com/plgd-dev/hub/v2/test/device/bridge" + "github.com/plgd-dev/hub/v2/test/device/ocf" oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test" + "github.com/plgd-dev/hub/v2/test/sdk" "github.com/plgd-dev/kit/v2/codec/json" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/ugorji/go/codec" - "go.uber.org/atomic" ) type ResourceLinkRepresentation struct { Href string /*`json:"href"`*/ + ResourceTypes []string /*`json:"rt"`*/ Representation interface{} /*`json:"rep"`*/ } @@ -59,57 +64,85 @@ func (d *ResourceLinkRepresentation) MarshalJSON() ([]byte, error) { } func (d *ResourceLinkRepresentation) UnmarshalJSON(data []byte) error { - reps := map[string]func(data []byte) (interface{}, error){ - configuration.ResourceURI: func(data []byte) (interface{}, error) { - var r configuration.Configuration - err := json.Decode(data, &r) - return r, err - }, - device.ResourceURI: func(data []byte) (interface{}, error) { - var r device.Device - err := json.Decode(data, &r) - r.ProtocolIndependentID = "" - return r, err - }, - platform.ResourceURI: func(data []byte) (interface{}, error) { - var r platform.Platform - err := json.Decode(data, &r) - r.PlatformIdentifier = "" - return r, err - }, - maintenance.ResourceURI: func(data []byte) (interface{}, error) { - var r MaintenanceResourceRepresentation - err := json.Decode(data, &r) - return r, err + type representation struct { + decode func(data []byte) (interface{}, error) + resourceTypes []string + } + reps := map[string]representation{ + configuration.ResourceURI: { + decode: func(data []byte) (interface{}, error) { + var r configuration.Configuration + err := json.Decode(data, &r) + return r, err + }, + resourceTypes: []string{configuration.ResourceType}, + }, + schemaDevice.ResourceURI: { + decode: func(data []byte) (interface{}, error) { + var r schemaDevice.Device + err := json.Decode(data, &r) + r.ProtocolIndependentID = "" + return r, err + }, + resourceTypes: TestResourceDeviceResourceTypes, + }, + platform.ResourceURI: { + decode: func(data []byte) (interface{}, error) { + var r platform.Platform + err := json.Decode(data, &r) + r.PlatformIdentifier = "" + return r, err + }, + resourceTypes: []string{platform.ResourceType}, }, - plgdtime.ResourceURI: func(data []byte) (interface{}, error) { - var r PlgdTimeResourceRepresentation - err := json.Decode(data, &r) - return r, err + maintenance.ResourceURI: { + decode: func(data []byte) (interface{}, error) { + var r MaintenanceResourceRepresentation + err := json.Decode(data, &r) + return r, err + }, + resourceTypes: []string{maintenance.ResourceType}, }, - TestResourceLightInstanceHref("1"): func(data []byte) (interface{}, error) { - var r LightResourceRepresentation - err := json.Decode(data, &r) - return r, err + plgdtime.ResourceURI: { + decode: func(data []byte) (interface{}, error) { + var r PlgdTimeResourceRepresentation + err := json.Decode(data, &r) + return r, err + }, + resourceTypes: []string{plgdtime.ResourceType}, }, - TestResourceSwitchesHref: func(data []byte) (interface{}, error) { - var r schema.ResourceLinks - err := json.Decode(data, &r) - if err != nil { - return nil, err - } - r.Sort() - for i := range r { - r[i].Endpoints = nil - r[i].InstanceID = 0 - } - - return r, err + TestResourceLightInstanceHref("1"): { + decode: func(data []byte) (interface{}, error) { + var r LightResourceRepresentation + err := json.Decode(data, &r) + return r, err + }, + resourceTypes: TestResourceLightInstanceResourceTypes, + }, + TestResourceSwitchesHref: { + decode: func(data []byte) (interface{}, error) { + var r schema.ResourceLinks + err := json.Decode(data, &r) + if err != nil { + return nil, err + } + r.Sort() + for i := range r { + r[i].Endpoints = nil + r[i].InstanceID = 0 + } + + return r, err + }, + resourceTypes: TestResourceSwitchesResourceTypes, }, - TestResourceSwitchesInstanceHref("1"): func(data []byte) (interface{}, error) { - var r SwitchResourceRepresentation - err := json.Decode(data, &r) - return r, err + TestResourceSwitchesInstanceHref("1"): { + decode: func(data []byte) (interface{}, error) { + var r SwitchResourceRepresentation + err := json.Decode(data, &r) + return r, err + }, + resourceTypes: TestResourceLightInstanceResourceTypes, }, } var rep struct { @@ -120,10 +153,12 @@ func (d *ResourceLinkRepresentation) UnmarshalJSON(data []byte) error { if err != nil { return err } - dec := func(data []byte) (interface{}, error) { - var r interface{} - err := json.Decode(data, &r) - return r, err + dec := representation{ + decode: func(data []byte) (interface{}, error) { + var r interface{} + errD := json.Decode(data, &r) + return r, errD + }, } for k, v := range reps { if strings.HasSuffix(rep.Href, k) { @@ -132,7 +167,8 @@ func (d *ResourceLinkRepresentation) UnmarshalJSON(data []byte) error { } } d.Href = rep.Href - d.Representation, err = dec(rep.Rep) + d.ResourceTypes = dec.resourceTypes + d.Representation, err = dec.decode(rep.Rep) if err != nil { return err } @@ -144,9 +180,12 @@ var ( TestDeviceNameWithOicResObservable string TestDeviceModelNumber = "CS-0" TestDeviceSoftwareVersion = "1.0.1-rc1" + TestDeviceType device.Type - TestDevsimResources []schema.ResourceLink - TestDevsimBackendResources []schema.ResourceLink + TestResourceSwitchesInstanceResourceTypes = []string{types.BINARY_SWITCH} + TestResourceSwitchesResourceTypes = []string{collection.ResourceType} + TestResourceLightInstanceResourceTypes = []string{types.CORE_LIGHT} + TestResourceDeviceResourceTypes = []string{types.DEVICE_CLOUD, schemaDevice.ResourceType} testIovityLiteVersion *sync.Map[string, uint32] ) @@ -163,6 +202,10 @@ func TestResourceSwitchesInstanceHref(id string) string { return TestResourceSwitchesHref + "/" + id } +func TestBridgeDeviceInstanceName(id string) string { + return "bridged-device-" + id +} + type LightResourceRepresentation struct { Name string `json:"name"` Power uint64 `json:"power"` @@ -190,93 +233,27 @@ type CollectionLinkRepresentation struct { type CollectionLinkRepresentations []CollectionLinkRepresentation func init() { - TestDeviceName = "devsim-" + MustGetHostname() + if name := os.Getenv("TEST_DEVICE_NAME"); name != "" { + TestDeviceName = name + } else { + TestDeviceName = "devsim-" + MustGetHostname() + } TestDeviceNameWithOicResObservable = "devsim-resobs-" + MustGetHostname() - - // when adding new resource, add also representation to GetAllBackendResourceRepresentations func - TestDevsimResources = []schema.ResourceLink{ - { - Href: platform.ResourceURI, - ResourceTypes: []string{platform.ResourceType}, - Interfaces: []string{interfaces.OC_IF_R, interfaces.OC_IF_BASELINE}, - Policy: &schema.Policy{ - BitMask: 3, - }, - }, - - { - Href: device.ResourceURI, - ResourceTypes: []string{types.DEVICE_CLOUD, device.ResourceType}, - Interfaces: []string{interfaces.OC_IF_R, interfaces.OC_IF_BASELINE}, - Policy: &schema.Policy{ - BitMask: 3, - }, - }, - - { - Href: configuration.ResourceURI, - ResourceTypes: []string{configuration.ResourceType}, - Interfaces: []string{interfaces.OC_IF_RW, interfaces.OC_IF_BASELINE}, - Policy: &schema.Policy{ - BitMask: 3, - }, - }, - - { - Href: TestResourceLightInstanceHref("1"), - ResourceTypes: []string{types.CORE_LIGHT}, - Interfaces: []string{interfaces.OC_IF_RW, interfaces.OC_IF_BASELINE}, - Policy: &schema.Policy{ - BitMask: 3, - }, - }, - - { - Href: TestResourceSwitchesHref, - ResourceTypes: []string{collection.ResourceType}, - Interfaces: []string{interfaces.OC_IF_LL, interfaces.OC_IF_CREATE, interfaces.OC_IF_B, interfaces.OC_IF_BASELINE}, - Policy: &schema.Policy{ - BitMask: 3, - }, - }, - - { - Href: maintenance.ResourceURI, - ResourceTypes: []string{maintenance.ResourceType}, - Interfaces: []string{interfaces.OC_IF_RW, interfaces.OC_IF_BASELINE}, - Policy: &schema.Policy{ - BitMask: 1, - }, - }, - - { - Href: plgdtime.ResourceURI, - ResourceTypes: []string{plgdtime.ResourceType}, - Interfaces: []string{interfaces.OC_IF_RW, interfaces.OC_IF_BASELINE}, - Policy: &schema.Policy{ - BitMask: 3, - }, - }, - - { - Href: softwareupdate.ResourceURI, - ResourceTypes: []string{softwareupdate.ResourceType}, - Interfaces: []string{interfaces.OC_IF_RW, interfaces.OC_IF_BASELINE}, - Policy: &schema.Policy{ - BitMask: 3, - }, - }, + if dtype := os.Getenv("TEST_DEVICE_TYPE"); dtype == "bridged" { + TestDeviceType = device.Bridged + } else { + TestDeviceType = device.OCF } testIovityLiteVersion = sync.NewMap[string, uint32]() } -func GetDeviceResourceRepresentation(deviceID, deviceName string) device.Device { - return device.Device{ +func GetDeviceResourceRepresentation(deviceID, deviceName string) schemaDevice.Device { + return schemaDevice.Device{ ID: deviceID, Interfaces: []string{interfaces.OC_IF_R, interfaces.OC_IF_BASELINE}, Name: deviceName, - ResourceTypes: []string{types.DEVICE_CLOUD, device.ResourceType}, + ResourceTypes: TestResourceDeviceResourceTypes, DataModelVersion: "ocf.res.1.3.0", SpecificationVersion: "ocf.2.0.5", ModelNumber: TestDeviceModelNumber, @@ -306,20 +283,24 @@ func GetAllBackendResourceRepresentations(t *testing.T, deviceID, deviceName str Power: 0, State: false, }, + ResourceTypes: TestResourceLightInstanceResourceTypes, }, { Href: "/" + commands.NewResourceID(deviceID, configuration.ResourceURI).ToString(), Representation: configuration.Configuration{ Name: deviceName, }, + ResourceTypes: []string{configuration.ResourceType}, }, { - Href: "/" + commands.NewResourceID(deviceID, device.ResourceURI).ToString(), + Href: "/" + commands.NewResourceID(deviceID, schemaDevice.ResourceURI).ToString(), Representation: dev, + ResourceTypes: TestResourceDeviceResourceTypes, }, { Href: "/" + commands.NewResourceID(deviceID, maintenance.ResourceURI).ToString(), Representation: MaintenanceResourceRepresentation{}, + ResourceTypes: []string{maintenance.ResourceType}, }, { Href: "/" + commands.NewResourceID(deviceID, platform.ResourceURI).ToString(), @@ -327,14 +308,17 @@ func GetAllBackendResourceRepresentations(t *testing.T, deviceID, deviceName str ManufacturerName: "ocfcloud.com", Version: iotVersion, }, + ResourceTypes: []string{platform.ResourceType}, }, { Href: "/" + commands.NewResourceID(deviceID, TestResourceSwitchesHref).ToString(), Representation: schema.ResourceLinks{}, + ResourceTypes: []string{collection.ResourceType}, }, { Href: "/" + commands.NewResourceID(deviceID, plgdtime.ResourceURI).ToString(), Representation: PlgdTimeResourceRepresentation{}, + ResourceTypes: []string{plgdtime.ResourceType}, }, { Href: "/" + commands.NewResourceID(deviceID, softwareupdate.ResourceURI).ToString(), @@ -347,6 +331,7 @@ func GetAllBackendResourceRepresentations(t *testing.T, deviceID, deviceName str "swupdateresult": uint64(0), "updatetime": "1970-01-01T00:00:00Z", }, + ResourceTypes: []string{softwareupdate.ResourceType}, }, } } @@ -365,7 +350,7 @@ func DefaultSwitchResourceLink(deviceID, id string) schema.ResourceLink { return schema.ResourceLink{ DeviceID: deviceID, Href: TestResourceSwitchesInstanceHref(id), - ResourceTypes: []string{types.BINARY_SWITCH}, + ResourceTypes: TestResourceSwitchesInstanceResourceTypes, Interfaces: []string{interfaces.OC_IF_A, interfaces.OC_IF_BASELINE}, Policy: &schema.Policy{ BitMask: schema.Discoverable | schema.Observable, @@ -461,25 +446,32 @@ func AddDeviceSwitchResources(ctx context.Context, t *testing.T, deviceID string return links } -func setAccessForCloud(ctx context.Context, t *testing.T, c *deviceClient.Client, deviceID string) { - cloudSID := config.HubID() - require.NotEmpty(t, cloudSID) - +func setAccessForCloud(ctx context.Context, c *deviceClient.Client, deviceID, cloudSID string) error { d, links, err := c.GetDevice(ctx, deviceID) - require.NoError(t, err) - + if err != nil { + return err + } defer func() { - errC := d.Close(ctx) - require.NoError(t, errC) + _ = d.Close(ctx) }() + + // skip setting of ACLs for insecure devices + if !d.IsSecured() { + return nil + } + p, err := d.Provision(ctx, links) - require.NoError(t, err) + if err != nil { + return err + } defer func() { _ = p.Close(ctx) }() link, err := core.GetResourceLink(links, acl.ResourceURI) - require.NoError(t, err) + if err != nil { + return err + } confResources := acl.AllResources for _, href := range links.GetResourceHrefs(maintenance.ResourceType) { confResources = append(confResources, acl.Resource{ @@ -507,37 +499,56 @@ func setAccessForCloud(ctx context.Context, t *testing.T, c *deviceClient.Client }, } - err = p.UpdateResource(ctx, link, setAcl, nil) + return p.UpdateResource(ctx, link, setAcl, nil) +} + +func disownDevice(t *testing.T, d device.Device) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*8) + defer cancel() + client, err := sdk.NewClient(d.GetSDKClientOptions()...) + require.NoError(t, err) + defer func() { + _ = client.Close(ctx) + }() + err = client.DisownDevice(ctx, d.GetID()) require.NoError(t, err) + time.Sleep(time.Second * 2) } -func OnboardDevSimForClient(ctx context.Context, t *testing.T, c pb.GrpcGatewayClient, clientID, deviceID, hubEndpoint string, expectedResources []schema.ResourceLink) (string, func()) { +func OnboardDevice(ctx context.Context, t *testing.T, c pb.GrpcGatewayClient, d device.Device, hubEndpoint string, expectedResources []schema.ResourceLink) func() { + return OnboardDeviceForClient(ctx, t, c, d, config.OAUTH_MANAGER_CLIENT_ID, hubEndpoint, expectedResources) +} + +func OnboardDeviceForClient(ctx context.Context, t *testing.T, c pb.GrpcGatewayClient, d device.Device, clientID, hubEndpoint string, expectedResources []schema.ResourceLink) func() { cloudSID := config.HubID() require.NotEmpty(t, cloudSID) - devClient, err := NewSDKClient() + devClient, err := sdk.NewClient(d.GetSDKClientOptions()...) require.NoError(t, err) defer func() { _ = devClient.Close(ctx) }() - deviceID, err = devClient.OwnDevice(ctx, deviceID, deviceClient.WithOTM(deviceClient.OTMType_JustWorks)) + + deviceID, err := devClient.OwnDevice(ctx, d.GetID(), deviceClient.WithOTM(deviceClient.OTMType_JustWorks)) require.NoError(t, err) + d.SetID(deviceID) - setAccessForCloud(ctx, t, devClient, deviceID) + err = setAccessForCloud(ctx, devClient, d.GetID(), cloudSID) + require.NoError(t, err) - code := oauthTest.GetAuthorizationCode(t, config.OAUTH_SERVER_HOST, clientID, deviceID, "") + code := oauthTest.GetAuthorizationCode(t, config.OAUTH_SERVER_HOST, clientID, d.GetID(), "") onboard := func() { var cloudRes cloud.Configuration - err = devClient.GetResource(ctx, deviceID, cloud.ResourceURI, &cloudRes) + err = devClient.GetResource(ctx, d.GetID(), cloud.ResourceURI, &cloudRes) require.NoError(t, err) if cloudRes.ProvisioningStatus != cloud.ProvisioningStatus_UNINITIALIZED { // device cloud is configured so we need to remove it first - err = devClient.OffboardDevice(ctx, deviceID) + err = devClient.OffboardDevice(ctx, d.GetID()) require.NoError(t, err) } - err = devClient.OnboardDevice(ctx, deviceID, config.DEVICE_PROVIDER, hubEndpoint, code, cloudSID) + err = devClient.OnboardDevice(ctx, d.GetID(), config.DEVICE_PROVIDER, hubEndpoint, code, cloudSID) require.NoError(t, err) } if len(expectedResources) > 0 { @@ -553,7 +564,7 @@ func OnboardDevSimForClient(ctx context.Context, t *testing.T, c pb.GrpcGatewayC ev, err := subClient.Recv() require.NoError(t, err) expectedEvent := &pb.Event{ - SubscriptionId: ev.SubscriptionId, + SubscriptionId: ev.GetSubscriptionId(), CorrelationId: "allEvents", Type: &pb.Event_OperationProcessed_{ OperationProcessed: &pb.Event_OperationProcessed{ @@ -565,47 +576,43 @@ func OnboardDevSimForClient(ctx context.Context, t *testing.T, c pb.GrpcGatewayC } CheckProtobufs(t, expectedEvent, ev, RequireToCheckFunc(require.Equal)) onboard() - WaitForDevice(t, subClient, deviceID, ev.GetSubscriptionId(), ev.GetCorrelationId(), expectedResources) + WaitForDevice(t, subClient, d, ev.GetSubscriptionId(), ev.GetCorrelationId(), expectedResources) err = subClient.CloseSend() require.NoError(t, err) } else { onboard() } - return deviceID, func() { - DisownDevice(t, deviceID) + return func() { + disownDevice(t, d) } } +func OnboardDevSimForClient(ctx context.Context, t *testing.T, c pb.GrpcGatewayClient, clientID, deviceID, hubEndpoint string, expectedResources []schema.ResourceLink) (string, func()) { + d := ocf.NewDevice(deviceID, TestDeviceName) + cleanup := OnboardDeviceForClient(ctx, t, c, d, clientID, hubEndpoint, expectedResources) + return d.GetID(), cleanup +} + func OnboardDevSim(ctx context.Context, t *testing.T, c pb.GrpcGatewayClient, deviceID, hubEndpoint string, expectedResources []schema.ResourceLink) (string, func()) { return OnboardDevSimForClient(ctx, t, c, config.OAUTH_MANAGER_CLIENT_ID, deviceID, hubEndpoint, expectedResources) } -func OffBoardDevSim(ctx context.Context, t *testing.T, deviceID string) { - devClient, err := NewSDKClient() +func OffboardDevice(ctx context.Context, t *testing.T, d device.Device) { + devClient, err := sdk.NewClient(d.GetSDKClientOptions()...) require.NoError(t, err) defer func() { _ = devClient.Close(ctx) }() - - err = devClient.OffboardDevice(ctx, deviceID) + err = devClient.OffboardDevice(ctx, d.GetID()) require.NoError(t, err) } -func DisownDevice(t *testing.T, deviceID string) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*8) - defer cancel() - client, err := NewSDKClient() - require.NoError(t, err) - defer func() { - _ = client.Close(ctx) - }() - err = client.DisownDevice(ctx, deviceID) - require.NoError(t, err) - time.Sleep(time.Second * 2) +func OffBoardDevSim(ctx context.Context, t *testing.T, deviceID string) { + OffboardDevice(ctx, t, ocf.NewDevice(deviceID, TestDeviceName)) } -func WaitForDevice(t *testing.T, client pb.GrpcGateway_SubscribeToEventsClient, deviceID, subID, correlationID string, expectedResources []schema.ResourceLink) { +func WaitForDevice(t *testing.T, client pb.GrpcGateway_SubscribeToEventsClient, dev device.Device, subID, correlationID string, expectedResources []schema.ResourceLink) { getID := func(ev *pb.Event) string { switch v := ev.GetType().(type) { case *pb.Event_DeviceRegistered_: @@ -637,6 +644,11 @@ func WaitForDevice(t *testing.T, client pb.GrpcGateway_SubscribeToEventsClient, val.DeviceMetadataUpdated.GetConnection().ConnectedAt = 0 require.NotEmpty(t, val.DeviceMetadataUpdated.GetConnection().GetServiceId()) val.DeviceMetadataUpdated.GetConnection().ServiceId = "" + if dev.GetType() != device.Bridged && !config.COAP_GATEWAY_UDP_ENABLED { + // TODO: fix bug in iotivity-lite, for DTLS it is not fill endpoints + require.NotEmpty(t, val.DeviceMetadataUpdated.GetConnection().GetLocalEndpoints()) + } + val.DeviceMetadataUpdated.GetConnection().LocalEndpoints = nil } if val.DeviceMetadataUpdated.GetTwinSynchronization() != nil { val.DeviceMetadataUpdated.GetTwinSynchronization().CommandMetadata = nil @@ -669,7 +681,7 @@ func WaitForDevice(t *testing.T, client pb.GrpcGateway_SubscribeToEventsClient, CorrelationId: correlationID, Type: &pb.Event_DeviceRegistered_{ DeviceRegistered: &pb.Event_DeviceRegistered{ - DeviceIds: []string{deviceID}, + DeviceIds: []string{dev.GetID()}, EventMetadata: &isEvents.EventMetadata{ HubId: config.HubID(), }, @@ -689,7 +701,7 @@ func WaitForDevice(t *testing.T, client pb.GrpcGateway_SubscribeToEventsClient, CorrelationId: correlationID, Type: &pb.Event_DeviceMetadataUpdated{ DeviceMetadataUpdated: &events.DeviceMetadataUpdated{ - DeviceId: deviceID, + DeviceId: dev.GetID(), Connection: &commands.Connection{ Status: commands.Connection_ONLINE, Protocol: StringToApplicationProtocol(config.ACTIVE_COAP_SCHEME), @@ -717,7 +729,7 @@ func WaitForDevice(t *testing.T, client pb.GrpcGateway_SubscribeToEventsClient, CorrelationId: correlationID, Type: &pb.Event_DeviceMetadataUpdated{ DeviceMetadataUpdated: &events.DeviceMetadataUpdated{ - DeviceId: deviceID, + DeviceId: dev.GetID(), Connection: &commands.Connection{ Status: commands.Connection_ONLINE, Protocol: StringToApplicationProtocol(config.ACTIVE_COAP_SCHEME), @@ -745,7 +757,7 @@ func WaitForDevice(t *testing.T, client pb.GrpcGateway_SubscribeToEventsClient, CorrelationId: correlationID, Type: &pb.Event_DeviceMetadataUpdated{ DeviceMetadataUpdated: &events.DeviceMetadataUpdated{ - DeviceId: deviceID, + DeviceId: dev.GetID(), Connection: &commands.Connection{ Status: commands.Connection_ONLINE, Protocol: StringToApplicationProtocol(config.ACTIVE_COAP_SCHEME), @@ -762,8 +774,8 @@ func WaitForDevice(t *testing.T, client pb.GrpcGateway_SubscribeToEventsClient, CorrelationId: correlationID, Type: &pb.Event_ResourcePublished{ ResourcePublished: &events.ResourceLinksPublished{ - DeviceId: deviceID, - Resources: ResourceLinksToResources(deviceID, expectedResources), + DeviceId: dev.GetID(), + Resources: ResourceLinksToResources(dev.GetID(), expectedResources), }, }, }, @@ -771,15 +783,16 @@ func WaitForDevice(t *testing.T, client pb.GrpcGateway_SubscribeToEventsClient, for _, r := range expectedResources { expectedEvents[getID(&pb.Event{Type: &pb.Event_ResourceChanged{ ResourceChanged: &events.ResourceChanged{ - ResourceId: commands.NewResourceID(deviceID, r.Href), + ResourceId: commands.NewResourceID(dev.GetID(), r.Href), }, }})] = &pb.Event{ SubscriptionId: subID, CorrelationId: correlationID, Type: &pb.Event_ResourceChanged{ ResourceChanged: &events.ResourceChanged{ - ResourceId: commands.NewResourceID(deviceID, r.Href), - Status: commands.Status_OK, + ResourceId: commands.NewResourceID(dev.GetID(), r.Href), + Status: commands.Status_OK, + ResourceTypes: r.ResourceTypes, }, }, } @@ -817,12 +830,12 @@ func MustGetHostname() string { return n } -func MustFindDeviceByName(name string) (deviceID string) { +func MustFindDeviceByName(name string, getResourceOpts ...device.GetResourceOpts) (deviceID string) { var err error for i := 0; i < 3; i++ { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() - deviceID, err = FindDeviceByName(ctx, name) + deviceID, err = device.FindDeviceByName(ctx, name, getResourceOpts...) if err == nil { return deviceID } @@ -830,70 +843,49 @@ func MustFindDeviceByName(name string) (deviceID string) { panic(err) } -type findDeviceIDByNameHandler struct { - id atomic.Value - name string - cancel context.CancelFunc -} - -func (h *findDeviceIDByNameHandler) Handle(ctx context.Context, dev *core.Device) { - defer func() { - if errC := dev.Close(ctx); errC != nil { - h.Error(errC) - } - }() - deviceLinks, err := dev.GetResourceLinks(ctx, dev.GetEndpoints()) - if err != nil { - h.Error(err) - return - } - l, ok := deviceLinks.GetResourceLink(device.ResourceURI) - if !ok { - return - } - var d device.Device - err = dev.GetResource(ctx, l, &d) - if err != nil { - h.Error(err) - return - } - if d.Name == h.name { - h.id.Store(d.ID) - h.cancel() +func GetBridgeDeviceConfig() (bridgeDevice.Config, error) { + cfgFile := os.Getenv("TEST_BRIDGE_DEVICE_CONFIG") + if cfgFile == "" { + return bridgeDevice.Config{}, errors.New("TEST_BRIDGE_DEVICE_CONFIG not set") } + return bridgeDevice.LoadConfig(cfgFile) } -func (h *findDeviceIDByNameHandler) Error(err error) { - if errors.Is(err, context.Canceled) { - return +func MustFindTestDevice() device.Device { + var getResourceOpts []device.GetResourceOpts + if TestDeviceType == device.Bridged { + getResourceOpts = append(getResourceOpts, func(d *core.Device) deviceCoap.OptionFunc { + return deviceCoap.WithQuery("di=" + d.DeviceID()) + }) } - log.Printf("find device ID by name handler error: %v", err.Error()) -} -func FindDeviceByName(ctx context.Context, name string) (deviceID string, _ error) { - client := core.NewClient() - - ctx, cancel := context.WithCancel(ctx) - defer cancel() - - h := findDeviceIDByNameHandler{ - name: name, - cancel: cancel, + var deviceID string + var err error + for i := 0; i < 3; i++ { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + deviceID, err = device.FindDeviceByName(ctx, TestDeviceName, getResourceOpts...) + if err == nil { + break + } } - err := client.GetDevicesByMulticast(ctx, core.DefaultDiscoveryConfiguration(), &h) if err != nil { - return "", fmt.Errorf("could not find the device named %s: %w", name, err) + panic(err) } - id, ok := h.id.Load().(string) - if !ok || id == "" { - return "", fmt.Errorf("could not find the device named %s: not found", name) + + if TestDeviceType == device.Bridged { + bridgeDeviceCfg, err := GetBridgeDeviceConfig() + if err != nil { + panic(err) + } + return bridge.NewDevice(deviceID, TestDeviceName, bridgeDeviceCfg.NumResourcesPerDevice, true) } - return id, nil + return ocf.NewDevice(deviceID, TestDeviceName) } func IsDiscoveryResourceBatchObservable(ctx context.Context, t *testing.T, deviceID string) bool { - devClient, err := NewSDKClient() + devClient, err := sdk.NewClient() require.NoError(t, err) defer func() { _ = devClient.Close(ctx) @@ -913,15 +905,16 @@ func IsDiscoveryResourceBatchObservable(ctx context.Context, t *testing.T, devic return false } -func GetResource(ctx context.Context, deviceID, resourceURI, resourceType string, resp interface{}) error { - devClient, err := NewSDKClient() +func GetResource(ctx context.Context, deviceID, resourceURI, resourceType string, resp interface{}, opts ...deviceClient.GetOption) error { + devClient, err := sdk.NewClient() defer func() { _ = devClient.Close(ctx) }() if err != nil { return err } - err = devClient.GetResource(ctx, deviceID, resourceURI, resp, deviceClient.WithResourceTypes(resourceType)) + opts = append(opts, deviceClient.WithResourceTypes(resourceType)) + err = devClient.GetResource(ctx, deviceID, resourceURI, resp, opts...) if err != nil { return err } @@ -944,15 +937,15 @@ func GetIotivityLiteVersion(t *testing.T, deviceID string) uint32 { func DeviceIsBatchObservable(ctx context.Context, t *testing.T, deviceID string) bool { var links schema.ResourceLinks - err := GetResource(ctx, deviceID, resources.ResourceURI, resources.ResourceType, &links) + err := GetResource(ctx, deviceID, resources.ResourceURI, resources.ResourceType, &links, deviceClient.WithQuery("di="+deviceID)) require.NoError(t, err) - require.Equal(t, 1, len(links)) + require.Len(t, links, 1) return links[0].Policy.BitMask.Has(schema.Observable) && pkgStrings.Contains(links[0].Interfaces, interfaces.OC_IF_B) } func GetAllBackendResourceLinks() schema.ResourceLinks { - return append(TestDevsimResources, TestDevsimBackendResources...) + return ocf.TestResources } func ProtobufToInterface(t *testing.T, val interface{}) interface{} { @@ -966,13 +959,13 @@ func ProtobufToInterface(t *testing.T, val interface{}) interface{} { func RequireToCheckFunc(checFunc func(t require.TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{})) func(t *testing.T, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { return func(t *testing.T, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - checFunc(t, expected, actual, msgAndArgs) + checFunc(t, expected, actual, msgAndArgs...) } } func AssertToCheckFunc(checFunc func(t assert.TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool) func(t *testing.T, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { return func(t *testing.T, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - checFunc(t, expected, actual, msgAndArgs) + checFunc(t, expected, actual, msgAndArgs...) } } diff --git a/test/thingDescription.go b/test/thingDescription.go new file mode 100644 index 000000000..bbe9e8a11 --- /dev/null +++ b/test/thingDescription.go @@ -0,0 +1,33 @@ +package test + +import ( + "sort" + "testing" + + "github.com/stretchr/testify/require" + wotTD "github.com/web-of-things-open-source/thingdescription-go/thingDescription" +) + +func CmpThingDescription(t *testing.T, expected, got *wotTD.ThingDescription) { + sort.Strings(expected.Type.StringArray) + sort.Strings(got.Type.StringArray) + sortProperties := func(td *wotTD.ThingDescription) { + for key, prop := range td.Properties { + for idx, form := range prop.Forms { + if form.Op.StringArray == nil { + continue + } + sort.Strings(form.Op.StringArray) + prop.Forms[idx] = form + } + if prop.Type == nil { + continue + } + sort.Strings(prop.Type.StringArray) + td.Properties[key] = prop + } + } + sortProperties(expected) + sortProperties(got) + require.Equal(t, expected, got) +} diff --git a/test/virtual-device/cmd/main.go b/test/virtual-device/cmd/main.go index d1fe26855..c4fc551ba 100644 --- a/test/virtual-device/cmd/main.go +++ b/test/virtual-device/cmd/main.go @@ -9,6 +9,7 @@ import ( "crypto/tls" "crypto/x509" "encoding/pem" + "errors" "flag" "fmt" "os" @@ -73,9 +74,9 @@ func generateIdentityCert(deviceID string, signerCert []*x509.Certificate, signe } func makeVerifyCertificate(signerCert []*x509.Certificate) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { - return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { + return func(rawCerts [][]byte, _ [][]*x509.Certificate) error { if len(rawCerts) == 0 { - return fmt.Errorf("empty certificates chain") + return errors.New("empty certificates chain") } intermediateCAPool := x509.NewCertPool() certs := make([]*x509.Certificate, 0, len(rawCerts)) @@ -191,7 +192,7 @@ func signUpDevice(ctx context.Context, deviceID string, co *client.Conn) (servic return service.CoapSignUpResponse{}, err } if signUpResp.AccessToken == "" { - return service.CoapSignUpResponse{}, fmt.Errorf("cannot sign up device: empty access token") + return service.CoapSignUpResponse{}, errors.New("cannot sign up device: empty access token") } return signUpResp, nil } @@ -286,12 +287,12 @@ func publishResources(ctx context.Context, deviceID string, co *client.Conn, num }) } - wkRd := wkRd{ + wk := wkRd{ DeviceID: deviceID, Links: links, TimeToLive: 0, } - inputCbor, err := cbor.Encode(wkRd) + inputCbor, err := cbor.Encode(wk) if err != nil { return err } @@ -341,7 +342,7 @@ func encodePlatformResource(w *responsewriter.ResponseWriter[*client.Conn]) []by pCbor, err := cbor.Encode(p) if err != nil { fmt.Printf("cannot encode platform: %v", err) - err := w.SetResponse(codes.Content, message.AppOcfCbor, bytes.NewReader([]byte{})) + err = w.SetResponse(codes.Content, message.AppOcfCbor, bytes.NewReader([]byte{})) if err != nil { fmt.Printf(errSetResponseFmt, err) } @@ -426,7 +427,7 @@ func processBatchResourceLinks(w *responsewriter.ResponseWriter[*client.Conn], d inputCbor, err := cbor.Encode(data) if err != nil { fmt.Printf("cannot encode resource data: %v", err) - err := w.SetResponse(codes.Content, message.AppOcfCbor, bytes.NewReader([]byte{})) + err = w.SetResponse(codes.Content, message.AppOcfCbor, bytes.NewReader([]byte{})) if err != nil { fmt.Printf(errSetResponseFmt, err) } @@ -514,7 +515,7 @@ func processGetResourceLinks(w *responsewriter.ResponseWriter[*client.Conn], dev inputCbor, err := cbor.Encode(links) if err != nil { fmt.Printf("cannot encode resource links: %v", err) - err := w.SetResponse(codes.Content, message.AppOcfCbor, bytes.NewReader([]byte{})) + err = w.SetResponse(codes.Content, message.AppOcfCbor, bytes.NewReader([]byte{})) if err != nil { fmt.Printf(errSetResponseFmt, err) } diff --git a/test/virtual-device/virtualDevice.go b/test/virtual-device/virtualDevice.go index bbbfaf423..54b23096a 100644 --- a/test/virtual-device/virtualDevice.go +++ b/test/virtual-device/virtualDevice.go @@ -8,6 +8,7 @@ import ( "time" "github.com/google/uuid" + "github.com/plgd-dev/device/v2/bridge/resources/thingDescription" "github.com/plgd-dev/device/v2/schema" "github.com/plgd-dev/device/v2/schema/device" "github.com/plgd-dev/device/v2/schema/interfaces" @@ -20,6 +21,7 @@ import ( "github.com/plgd-dev/hub/v2/test" "github.com/plgd-dev/hub/v2/test/config" oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/sync/semaphore" "google.golang.org/grpc" @@ -28,7 +30,7 @@ import ( "google.golang.org/grpc/status" ) -func CreateDeviceResourceLinks(deviceID string, numResources int) []*commands.Resource { +func CreateDeviceResourceLinks(deviceID string, numResources int, tdEnabled bool) []*commands.Resource { resources := make([]*commands.Resource, 0, numResources) for i := 0; i < numResources; i++ { resources = append(resources, &commands.Resource{ @@ -59,10 +61,23 @@ func CreateDeviceResourceLinks(deviceID string, numResources int) []*commands.Re BitFlags: int32(schema.Observable | schema.Discoverable), }, }) + + if tdEnabled { + resources = append(resources, &commands.Resource{ + Href: thingDescription.ResourceURI, + DeviceId: deviceID, + ResourceTypes: []string{thingDescription.ResourceType}, + Interfaces: []string{interfaces.OC_IF_BASELINE, interfaces.OC_IF_R}, + Policy: &commands.Policy{ + BitFlags: int32(schema.Discoverable), + }, + }) + } + return resources } -func CreateDevice(ctx context.Context, t *testing.T, name string, deviceID string, numResources int, protocol commands.Connection_Protocol, isClient pb.IdentityStoreClient, raClient raPb.ResourceAggregateClient) { +func CreateDevice(ctx context.Context, t *testing.T, name string, deviceID string, numResources int, tdEnabled bool, protocol commands.Connection_Protocol, isClient pb.IdentityStoreClient, raClient raPb.ResourceAggregateClient) { const connID = "conn-Id" var conSeq uint64 incSeq := func() uint64 { @@ -72,7 +87,7 @@ func CreateDevice(ctx context.Context, t *testing.T, name string, deviceID strin _, err := isClient.AddDevice(ctx, &pb.AddDeviceRequest{ DeviceId: deviceID, }) - require.NoError(t, err) + assert.NoError(t, err) //nolint:testifylint for { _, err = raClient.UpdateDeviceMetadata(ctx, &commands.UpdateDeviceMetadataRequest{ @@ -80,10 +95,11 @@ func CreateDevice(ctx context.Context, t *testing.T, name string, deviceID strin CorrelationId: uuid.NewString(), Update: &commands.UpdateDeviceMetadataRequest_Connection{ Connection: &commands.Connection{ - Status: commands.Connection_ONLINE, - ConnectedAt: time.Now().UnixNano(), - Protocol: protocol, - ServiceId: "a0000000-0000-0000-0000-000000000099", + Status: commands.Connection_ONLINE, + ConnectedAt: time.Now().UnixNano(), + Protocol: protocol, + ServiceId: "a0000000-0000-0000-0000-000000000099", + LocalEndpoints: []string{"coaps+tcp://localhost:5684"}, }, }, TimeToLive: time.Now().Add(time.Hour).UnixNano(), @@ -100,10 +116,10 @@ func CreateDevice(ctx context.Context, t *testing.T, name string, deviceID strin // device is still not loaded to owner in resource-aggregate continue } - require.NoError(t, err) + assert.NoError(t, err) //nolint:testifylint } - resources := CreateDeviceResourceLinks(deviceID, numResources) + resources := CreateDeviceResourceLinks(deviceID, numResources, tdEnabled) pub := commands.PublishResourceLinksRequest{ DeviceId: deviceID, Resources: resources, @@ -113,7 +129,7 @@ func CreateDevice(ctx context.Context, t *testing.T, name string, deviceID strin }, } _, err = raClient.PublishResourceLinks(ctx, &pub) - require.NoError(t, err) + assert.NoError(t, err) //nolint:testifylint _, err = raClient.UpdateDeviceMetadata(ctx, &commands.UpdateDeviceMetadataRequest{ DeviceId: deviceID, @@ -130,7 +146,8 @@ func CreateDevice(ctx context.Context, t *testing.T, name string, deviceID strin Sequence: incSeq(), }, }) - require.NoError(t, err) + assert.NoError(t, err) //nolint:testifylint + for i := 0; i < numResources; i++ { _, err = raClient.NotifyResourceChanged(ctx, &commands.NotifyResourceChangedRequest{ ResourceId: commands.NewResourceID(deviceID, fmt.Sprintf("/res-%v", i)), @@ -144,8 +161,9 @@ func CreateDevice(ctx context.Context, t *testing.T, name string, deviceID strin }, Status: commands.Status_OK, }) - require.NoError(t, err) + assert.NoError(t, err) //nolint:testifylint } + _, err = raClient.NotifyResourceChanged(ctx, &commands.NotifyResourceChangedRequest{ ResourceId: commands.NewResourceID(deviceID, "/oic/d"), CommandMetadata: &commands.CommandMetadata{ @@ -158,7 +176,7 @@ func CreateDevice(ctx context.Context, t *testing.T, name string, deviceID strin }, Status: commands.Status_OK, }) - require.NoError(t, err) + assert.NoError(t, err) //nolint:testifylint _, err = raClient.NotifyResourceChanged(ctx, &commands.NotifyResourceChangedRequest{ ResourceId: commands.NewResourceID(deviceID, "/oic/p"), @@ -172,7 +190,7 @@ func CreateDevice(ctx context.Context, t *testing.T, name string, deviceID strin }, Status: commands.Status_OK, }) - require.NoError(t, err) + assert.NoError(t, err) //nolint:testifylint _, err = raClient.UpdateDeviceMetadata(ctx, &commands.UpdateDeviceMetadataRequest{ DeviceId: deviceID, @@ -189,13 +207,13 @@ func CreateDevice(ctx context.Context, t *testing.T, name string, deviceID strin Sequence: incSeq(), }, }) - require.NoError(t, err) + assert.NoError(t, err) } func CreateDevices(ctx context.Context, t *testing.T, numDevices int, numResourcesPerDevice int, protocol commands.Connection_Protocol) { ctx = kitNetGrpc.CtxWithToken(ctx, oauthTest.GetDefaultAccessToken(t)) - isConn, err := grpc.Dial(config.IDENTITY_STORE_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + isConn, err := grpc.NewClient(config.IDENTITY_STORE_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -204,7 +222,7 @@ func CreateDevices(ctx context.Context, t *testing.T, numDevices int, numResourc }() isClient := pb.NewIdentityStoreClient(isConn) - raConn, err := grpc.Dial(config.RESOURCE_AGGREGATE_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + raConn, err := grpc.NewClient(config.RESOURCE_AGGREGATE_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ RootCAs: test.GetRootCertificatePool(t), }))) require.NoError(t, err) @@ -219,7 +237,7 @@ func CreateDevices(ctx context.Context, t *testing.T, numDevices int, numResourc err = sem.Acquire(ctx, 1) require.NoError(t, err) go func(i int) { - CreateDevice(ctx, t, fmt.Sprintf("dev-%v", i), uuid.NewString(), numResourcesPerDevice, protocol, isClient, raClient) + CreateDevice(ctx, t, fmt.Sprintf("dev-%v", i), uuid.NewString(), numResourcesPerDevice, false, protocol, isClient, raClient) sem.Release(1) }(i) } diff --git a/tools/cert-tool/Dockerfile b/tools/cert-tool/Dockerfile index 18eb73a6a..73dd69940 100644 --- a/tools/cert-tool/Dockerfile +++ b/tools/cert-tool/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.20.13-alpine AS build +FROM golang:1.22.3-alpine AS build ARG DIRECTORY ARG NAME ARG VERSION @@ -11,9 +11,13 @@ WORKDIR $GOPATH/src/github.com/plgd-dev/hub COPY go.mod go.sum ./ RUN go mod download COPY . . -RUN ( cd /usr/local/go && patch -p1 < $GOPATH/src/github.com/plgd-dev/hub/tools/docker/patches/shrink_tls_conn.patch ) +WORKDIR /usr/local/go +RUN ( patch -p1 < "$GOPATH/src/github.com/plgd-dev/hub/tools/docker/patches/shrink_tls_conn.patch" ) WORKDIR $GOPATH/src/github.com/plgd-dev/hub/tools/cert-tool -RUN CGO_ENABLED=0 go build -ldflags "-X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" -o /go/bin/cert-tool ./ +RUN CGO_ENABLED=0 go build \ + -ldflags "-X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" \ + -o /go/bin/cert-tool \ + ./ FROM alpine:3.19 AS security-provider RUN addgroup -S nonroot \ @@ -23,4 +27,4 @@ FROM scratch AS service COPY --from=security-provider /etc/passwd /etc/passwd COPY --from=build /go/bin/cert-tool /usr/local/bin/cert-tool USER nonroot -ENTRYPOINT [ "/usr/local/bin/cert-tool" ] \ No newline at end of file +ENTRYPOINT [ "/usr/local/bin/cert-tool" ] diff --git a/tools/docker/Dockerfile.in b/tools/docker/Dockerfile.in index c120f554e..46e1e79b4 100644 --- a/tools/docker/Dockerfile.in +++ b/tools/docker/Dockerfile.in @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1 -FROM golang:1.20.13-alpine AS build +FROM golang:1.22.3-alpine AS build ARG VERSION ARG COMMIT_DATE ARG SHORT_COMMIT @@ -11,10 +11,16 @@ COPY go.mod go.sum ./ RUN go mod download COPY . . RUN go mod vendor -RUN ( cd /usr/local/go && patch -p1 < $GOPATH/src/github.com/plgd-dev/hub/tools/docker/patches/shrink_tls_conn.patch ) -RUN ( cd ./vendor/golang.org/x/oauth2 && patch -p1 < $GOPATH/src/github.com/plgd-dev/hub/tools/docker/patches/golang_org_x_oauth2_propagate_error.patch ) +WORKDIR /usr/local/go +RUN ( patch -p1 < "$GOPATH/src/github.com/plgd-dev/hub/tools/docker/patches/shrink_tls_conn.patch" ) +WORKDIR $GOPATH/src/github.com/plgd-dev/hub/vendor/golang.org/x/oauth2 +RUN ( patch -p1 < "$GOPATH/src/github.com/plgd-dev/hub/tools/docker/patches/golang_org_x_oauth2_propagate_error.patch" ) WORKDIR $GOPATH/src/github.com/plgd-dev/hub/@DIRECTORY@ -RUN CGO_ENABLED=0 go build -mod=vendor -ldflags "-X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" -o /go/bin/@NAME@ ./cmd/service +RUN CGO_ENABLED=0 go build \ + -mod=vendor \ + -ldflags "-X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" \ + -o /go/bin/@NAME@ \ + ./cmd/service FROM alpine:3.19 AS security-provider RUN apk add -U --no-cache ca-certificates @@ -26,4 +32,4 @@ COPY --from=security-provider /etc/passwd /etc/passwd COPY --from=security-provider /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=build /go/bin/@NAME@ /usr/local/bin/@NAME@ USER nonroot -ENTRYPOINT [ "/usr/local/bin/@NAME@" ] \ No newline at end of file +ENTRYPOINT [ "/usr/local/bin/@NAME@" ] diff --git a/tools/nats-server-config-reloader/Dockerfile b/tools/nats-server-config-reloader/Dockerfile deleted file mode 100644 index 20aaf5bfd..000000000 --- a/tools/nats-server-config-reloader/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -FROM golang:1.20.13-alpine AS build -RUN apk add --no-cache curl git build-base -WORKDIR $GOPATH/src/github.com/plgd-dev/hub -COPY go.mod go.sum ./ -RUN go mod download -COPY . . -RUN go mod vendor -RUN ( cd /usr/local/go && patch -p1 < $GOPATH/src/github.com/plgd-dev/hub/tools/docker/patches/shrink_tls_conn.patch ) -RUN ( cd ./vendor/golang.org/x/oauth2 && patch -p1 < $GOPATH/src/github.com/plgd-dev/hub/tools/docker/patches/golang_org_x_oauth2_propagate_error.patch ) -WORKDIR $GOPATH/src/github.com/plgd-dev/hub/tools/nats-server-config-reloader -RUN CGO_ENABLED=0 go build -mod=vendor -o /go/bin/nats-server-config-reloader . - -FROM alpine:3.19 AS security-provider -RUN apk add -U --no-cache ca-certificates - -FROM scratch AS service -COPY --from=security-provider /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -COPY --from=build /go/bin/nats-server-config-reloader /usr/local/bin/nats-server-config-reloader -ENTRYPOINT [ /usr/local/bin/nats-server-config-reloader ] \ No newline at end of file diff --git a/tools/nats-server-config-reloader/main.go b/tools/nats-server-config-reloader/main.go deleted file mode 100644 index 8eba9a121..000000000 --- a/tools/nats-server-config-reloader/main.go +++ /dev/null @@ -1,119 +0,0 @@ -package main - -import ( - "context" - "errors" - "flag" - "fmt" - "log" - "os" - "os/signal" - "strings" - "syscall" - - "github.com/plgd-dev/hub/v2/tools/nats-server-config-reloader/pkg/natsreloader" -) - -var ( - BuildTime = "build-time-not-set" - GitInfo = "gitinfo-not-set" - Version = "version-not-set" -) - -const errorFmt = natsreloader.ErrorFmt - -// StringSet is a wrapper for []string to allow using it with the flags package. -type StringSet []string - -func (s *StringSet) String() string { - return strings.Join([]string(*s), ", ") -} - -// Set appends the value provided to the list of strings. -func (s *StringSet) Set(val string) error { - *s = append(*s, val) - return nil -} - -func main() { - fs := flag.NewFlagSet("nats-server-config-reloader", flag.ExitOnError) - flag.Usage = func() { - fmt.Fprintf(os.Stderr, "Usage: nats-server-config-reloader [options...]\n\n") - fs.PrintDefaults() - fmt.Fprintf(os.Stderr, "\n") - } - - // Help and version - var ( - showHelp bool - showVersion bool - fileSet StringSet - customSignal int - ) - - nconfig := &natsreloader.Config{} - fs.BoolVar(&showHelp, "h", false, "Show help") - fs.BoolVar(&showHelp, "help", false, "Show help") - fs.BoolVar(&showVersion, "v", false, "Show version") - fs.BoolVar(&showVersion, "version", false, "Show version") - - fs.StringVar(&nconfig.PidFile, "P", "/var/run/nats/gnatsd.pid", "NATS Server Pid File") - fs.StringVar(&nconfig.PidFile, "pid", "/var/run/nats/gnatsd.pid", "NATS Server Pid File") - fs.Var(&fileSet, "c", "NATS Server Config File (may be repeated to specify more than one)") - fs.Var(&fileSet, "config", "NATS Server Config File (may be repeated to specify more than one)") - fs.IntVar(&nconfig.MaxRetries, "max-retries", 5, "Max attempts to trigger reload") - fs.IntVar(&nconfig.RetryWaitSecs, "retry-wait-secs", 2, "Time to back off when reloading fails before retrying") - fs.IntVar(&customSignal, "signal", 1, "Signal to send to the NATS Server process (default SIGHUP 1)") - - err := fs.Parse(os.Args[1:]) - if err != nil { - fmt.Fprintf(os.Stderr, errorFmt, err) - os.Exit(1) - } - - nconfig.ConfigFiles = fileSet - if len(fileSet) == 0 { - nconfig.ConfigFiles = []string{"/etc/nats-config/gnatsd.conf"} - } - nconfig.Signal = syscall.Signal(customSignal) - - switch { - case showHelp: - flag.Usage() - os.Exit(0) - case showVersion: - fmt.Fprintf(os.Stderr, "NATS Server Config Reloader v%s (%s, %s)\n", Version, GitInfo, BuildTime) - os.Exit(0) - } - r, err := natsreloader.NewReloader(nconfig) - if err != nil { - fmt.Fprintf(os.Stderr, errorFmt, err) - os.Exit(1) - } - - // Signal handling. - go func() { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) - - for sig := range c { - log.Printf("Trapped \"%v\" signal\n", sig) - switch sig { - case syscall.SIGINT: - log.Println("Exiting...") - os.Exit(0) - return - case syscall.SIGTERM: - _ = r.Stop() - return - } - } - }() - - log.Printf("Starting NATS Server Reloader v%s\n", Version) - err = r.Run(context.Background()) - if err != nil && !errors.Is(err, context.Canceled) { - fmt.Fprintf(os.Stderr, errorFmt, err.Error()) - os.Exit(1) - } -} diff --git a/tools/nats-server-config-reloader/pkg/natsreloader/natsreloader.go b/tools/nats-server-config-reloader/pkg/natsreloader/natsreloader.go deleted file mode 100644 index 51c4c60bd..000000000 --- a/tools/nats-server-config-reloader/pkg/natsreloader/natsreloader.go +++ /dev/null @@ -1,355 +0,0 @@ -package natsreloader - -import ( - "bytes" - "context" - "crypto/sha256" - "fmt" - "io" - "log" - "math/rand" - "os" - "path/filepath" - "sort" - "strconv" - "time" - - "github.com/fsnotify/fsnotify" -) - -const ErrorFmt = "Error: %s\n" - -// Config represents the configuration of the reloader. -type Config struct { - PidFile string - ConfigFiles []string - MaxRetries int - RetryWaitSecs int - Signal os.Signal -} - -// Reloader monitors the state from a single server config file -// and sends signal on updates. -type Reloader struct { - *Config - - // proc represents the NATS Server process which will - // be signaled. - proc *os.Process - - // pid is the last known PID from the NATS Server. - pid int - - // quit shutsdown the reloader. - quit func() -} - -func (r *Reloader) waitForProcess() error { - var proc *os.Process - var pid int - attempts := 0 - - startTime := time.Now() - for { - pidfile, err := os.ReadFile(r.PidFile) - if err != nil { - goto WaitAndRetry - } - - pid, err = strconv.Atoi(string(pidfile)) - if err != nil { - goto WaitAndRetry - } - - proc, err = os.FindProcess(pid) - if err != nil { - goto WaitAndRetry - } - break - - WaitAndRetry: - log.Printf(ErrorFmt, err) - attempts++ - if attempts > r.MaxRetries { - return fmt.Errorf("too many errors attempting to find server process") - } - time.Sleep(time.Duration(r.RetryWaitSecs) * time.Second) - } - - if attempts > 0 { - log.Printf("found pid from pidfile %q after %v failed attempts (%v time after start)", - r.PidFile, attempts, time.Since(startTime)) - } - - r.pid = pid - r.proc = proc - return nil -} - -func removeDuplicateStrings(s []string) []string { - if len(s) < 1 { - return s - } - - sort.Strings(s) - prev := 1 - for curr := 1; curr < len(s); curr++ { - if s[curr-1] != s[curr] { - s[prev] = s[curr] - prev++ - } - } - - return s[:prev] -} - -func calcDigest(filePath string) ([]byte, error) { - h := sha256.New() - f, err := os.Open(filePath) - if err != nil { - return nil, err - } - defer func() { - _ = f.Close() - }() - if _, err := io.Copy(h, f); err != nil { - return nil, err - } - return h.Sum(nil), nil -} - -func handleEvent(event fsnotify.Event, lastConfigAppliedCache map[string][]byte, updatedFiles, deletedFiles []string) ([]string, []string) { - if event.Op&fsnotify.Remove == fsnotify.Remove { - // We don't get a Remove event for the directory itself, so - // we need to detect that separately. - return updatedFiles, append(deletedFiles, event.Name) - } - _, err := os.Stat(event.Name) - if err != nil { - // Beware that this means that we won't reconfigure if a file - // is permanently removed. We want to support transient - // disappearance, waiting for the new content, and have not set - // up any sort of longer-term timers to detect permanent - // deletion. - // If you really need this, then switch a file to be empty - // before removing if afterwards. - return updatedFiles, deletedFiles - } - - if len(updatedFiles) > 0 { - return updatedFiles, deletedFiles - } - digest, err := calcDigest(event.Name) - if err != nil { - log.Printf(ErrorFmt, err) - return updatedFiles, deletedFiles - } - - lastConfigHash, ok := lastConfigAppliedCache[event.Name] - if ok && bytes.Equal(lastConfigHash, digest) { - return updatedFiles, deletedFiles - } - - log.Printf("changed config; file=%q existing=%v total-files=%d", - event.Name, ok, len(lastConfigAppliedCache)) - lastConfigAppliedCache[event.Name] = digest - return append(updatedFiles, event.Name), deletedFiles -} - -// handleEvents handles all events in the queue. It returns the updated and deleted files and can contain duplicates. -func handleEvents(configWatcher *fsnotify.Watcher, event fsnotify.Event, lastConfigAppliedCache map[string][]byte) ([]string, []string) { - updatedFiles, deletedFiles := handleEvent(event, lastConfigAppliedCache, make([]string, 0, 16), make([]string, 0, 16)) - for { - select { - case event := <-configWatcher.Events: - updatedFiles, deletedFiles = handleEvent(event, lastConfigAppliedCache, updatedFiles, deletedFiles) - default: - return updatedFiles, deletedFiles - } - } -} - -func handleDeletedFiles(deletedFiles []string, configWatcher *fsnotify.Watcher, lastConfigAppliedCache map[string][]byte) ([]string, []string) { - log.Printf("Ticker is running with deletedFiles %v", deletedFiles) - newDeletedFiles := make([]string, 0, len(deletedFiles)) - updated := make([]string, 0, len(deletedFiles)) - for _, f := range deletedFiles { - if err := configWatcher.Add(f); err != nil { - newDeletedFiles = append(newDeletedFiles, f) - } else { - updated, _ = handleEvent(fsnotify.Event{Name: f, Op: fsnotify.Create}, lastConfigAppliedCache, updated, nil) - } - } - return removeDuplicateStrings(updated), newDeletedFiles -} - -func (r *Reloader) init() (*fsnotify.Watcher, map[string][]byte, error) { - err := r.waitForProcess() - if err != nil { - return nil, nil, err - } - - configWatcher, err := fsnotify.NewWatcher() - if err != nil { - return nil, nil, err - } - - // Follow configuration updates in the directory where - // the config file is located and trigger reload when - // it is either recreated or written into. - for i := range r.ConfigFiles { - // Ensure our paths are canonical - r.ConfigFiles[i], _ = filepath.Abs(r.ConfigFiles[i]) - } - r.ConfigFiles = removeDuplicateStrings(r.ConfigFiles) - // Follow configuration file updates and trigger reload when - // it is either recreated or written into. - for i := range r.ConfigFiles { - // Watch files individually for https://github.com/kubernetes/kubernetes/issues/112677 - if err := configWatcher.Add(r.ConfigFiles[i]); err != nil { - _ = configWatcher.Close() - return nil, nil, err - } - log.Printf("watching file: %v", r.ConfigFiles[i]) - } - - // lastConfigAppliedCache is the last config update - // applied by us - lastConfigAppliedCache := make(map[string][]byte) - - // Preload config hashes, so we know their digests - // up front and avoid potentially reloading when unnecessary. - for _, configFile := range r.ConfigFiles { - digest, err := calcDigest(configFile) - if err != nil { - _ = configWatcher.Close() - return nil, nil, err - } - lastConfigAppliedCache[configFile] = digest - } - - // If the two pids don't match then os.FindProcess() has done something - // rather hinkier than we expect, but log them both just in case on some - // future platform there's a weird namespace issue, as a difference will - // help with debugging. - log.Printf("Live, ready to kick pid %v (live, from %v spec) based on any of %v files", - r.proc.Pid, r.pid, len(lastConfigAppliedCache)) - - if len(lastConfigAppliedCache) == 0 { - log.Printf("Error: no watched config files cached; input spec was: %#v", - r.ConfigFiles) - } - return configWatcher, lastConfigAppliedCache, nil -} - -func (r *Reloader) reload(updatedFiles []string) error { - attempts := 0 - for { - log.Printf("Sending signal '%s' to server to reload configuration due to: %s", r.Signal.String(), updatedFiles) - err := r.proc.Signal(r.Signal) - if err == nil { - return nil - } - log.Printf("Error during reload: %s\n", err) - if attempts > r.MaxRetries { - return fmt.Errorf("too many errors (%v) attempting to signal server to reload: %w", attempts, err) - } - delay := retryJitter(time.Duration(r.RetryWaitSecs) * time.Second) - log.Printf("Wait and retrying after some time [%v] ...", delay) - time.Sleep(delay) - attempts++ - } -} - -// Run starts the main loop. -func (r *Reloader) Run(ctx context.Context) error { - ctx, cancel := context.WithCancel(ctx) - r.quit = func() { - cancel() - } - - configWatcher, lastConfigAppliedCache, err := r.init() - if err != nil { - return err - } - defer func() { - _ = configWatcher.Close() - }() - - // We use a ticker to re-add deleted files to the watcher - t := time.NewTicker(time.Second) - t.Stop() - defer t.Stop() - var tickerRunning bool - var deletedFiles []string - var updatedFiles []string - - for { - select { - case <-ctx.Done(): - return nil - case <-t.C: - updatedFiles, deletedFiles = handleDeletedFiles(deletedFiles, configWatcher, lastConfigAppliedCache) - if len(deletedFiles) == 0 { - // No more deleted files, stop the ticker - t.Stop() - tickerRunning = false - } - if len(updatedFiles) > 0 { - // Send signal to reload the config - log.Printf("Updated files: %v", updatedFiles) - break - } - continue - // Check if the process is still alive - case event := <-configWatcher.Events: - updated, deleted := handleEvents(configWatcher, event, lastConfigAppliedCache) - updatedFiles = removeDuplicateStrings(updated) - deletedFiles = removeDuplicateStrings(append(deletedFiles, deleted...)) - if !tickerRunning { - // Start the ticker to re-add deleted files - t.Reset(time.Second) - tickerRunning = true - } - if len(updatedFiles) > 0 { - // Send signal to reload the config - log.Printf("Updated files: %v", updatedFiles) - break - } - continue - case err := <-configWatcher.Errors: - log.Printf(ErrorFmt, err) - continue - } - // Configuration was updated, try to do reload for a few times - // otherwise give up and wait for next event. - err := r.reload(updatedFiles) - if err != nil { - return err - } - updatedFiles = nil - } -} - -// Stop shutsdown the process. -func (r *Reloader) Stop() error { - log.Println("Shutting down...") - r.quit() - return nil -} - -// NewReloader returns a configured NATS server reloader. -func NewReloader(config *Config) (*Reloader, error) { - return &Reloader{ - Config: config, - }, nil -} - -// retryJitter helps avoid trying things at synchronized times, thus improving -// resiliency in aggregate. -func retryJitter(base time.Duration) time.Duration { - b := float64(base) - // 10% +/- - offset := rand.Float64()*0.2 - 0.1 - return time.Duration(b + offset) -} diff --git a/tools/nats-server-config-reloader/pkg/natsreloader/natsreloadertest/natsreloader_test.go b/tools/nats-server-config-reloader/pkg/natsreloader/natsreloadertest/natsreloader_test.go deleted file mode 100644 index d25772ffc..000000000 --- a/tools/nats-server-config-reloader/pkg/natsreloader/natsreloadertest/natsreloader_test.go +++ /dev/null @@ -1,125 +0,0 @@ -package natsreloadertest - -import ( - "context" - "errors" - "fmt" - "os" - "os/signal" - "sync" - "syscall" - "testing" - "time" - - "github.com/plgd-dev/hub/v2/tools/nats-server-config-reloader/pkg/natsreloader" -) - -var ( - configContents = `port = 2222` - newConfigContents = `port = 2222 -someOtherThing = "bar" -` -) - -func TestReloader(t *testing.T) { - // Setup a pidfile that points to us - pid := os.Getpid() - pidfile, err := os.CreateTemp(os.TempDir(), "nats-pid-") - if err != nil { - t.Fatal(err) - } - - p := fmt.Sprintf("%d", pid) - if _, err := pidfile.WriteString(p); err != nil { - t.Fatal(err) - } - defer os.Remove(pidfile.Name()) - - // Create tempfile with contents, then update it - nconfig := &natsreloader.Config{ - PidFile: pidfile.Name(), - ConfigFiles: []string{}, - Signal: syscall.SIGHUP, - } - - var configFiles []*os.File - for i := 0; i < 2; i++ { - configFile, err := os.CreateTemp(os.TempDir(), "nats-conf-") - if err != nil { - t.Fatal(err) - } - defer os.Remove(configFile.Name()) - - if _, err := configFile.WriteString(configContents); err != nil { - t.Fatal(err) - } - configFiles = append(configFiles, configFile) - nconfig.ConfigFiles = append(nconfig.ConfigFiles, configFile.Name()) - } - - r, err := natsreloader.NewReloader(nconfig) - if err != nil { - t.Fatal(err) - } - - signals := 0 - - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - var sigsMu sync.Mutex - - // Signal handling. - go func() { - c := make(chan os.Signal, 1) - signal.Notify(c, syscall.SIGHUP) - - // Success when receiving the first signal - for range c { - sigsMu.Lock() - signals++ - sigsMu.Unlock() - } - }() - - go func() { - // This is terrible, but we need this thread to wait until r.Run(ctx) has finished starting up - // before we start mucking with the file. - // There isn't any other good way to synchronize on this happening. - time.Sleep(100 * time.Millisecond) - for _, configfile := range configFiles { - for i := 0; i < 5; i++ { - // Append some more stuff to the config - if _, err := configfile.WriteAt([]byte(newConfigContents), 0); err != nil { - return - } - time.Sleep(10 * time.Millisecond) - } - } - - // Create some random file in the same directory, shouldn't trigger an - // additional server signal. - configFile, err := os.CreateTemp(os.TempDir(), "foo") - if err != nil { - t.Log(err) - return - } - defer os.Remove(configFile.Name()) - time.Sleep(100 * time.Millisecond) - - cancel() - }() - - err = r.Run(ctx) - if err != nil && !errors.Is(err, context.Canceled) { - t.Fatal(err) - } - // We should have gotten only one signal for each configuration file - sigsMu.Lock() - got := signals - sigsMu.Unlock() - expected := len(configFiles) - if got != expected { - t.Fatalf("Wrong number of signals received. Expected: %v, got: %v", expected, got) - } -}