diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..85a5d42bf --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +.git +.github +.gitignore +.travis.yml +Makefile +Dockerfile +README.md +LICENSE +renovate.json diff --git a/.gitignore b/.gitignore index 66fd13c90..f7764ccb3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,12 +4,20 @@ *.dll *.so *.dylib +debug +.tmp/ +authsvc.db -# Test binary, built with `go test -c` +# Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out +coverage.txt +.codecov -# Dependency directories (remove the comment below to include it) -# vendor/ +#goland +.idea/ + +#vendor +vendor/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..02362d2bd --- /dev/null +++ b/.travis.yml @@ -0,0 +1,18 @@ +language: minimal + +services: + - docker + +jobs: + include: + - stage: "Test" + if: type = pull_request + script: + - make test + - stage: "Test & Publish" + if: branch = master AND type != pull_request + script: + - make test + - bash <(curl -s https://codecov.io/bash) + - echo "$DOCKERHUB_TOKEN" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin + - make push diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..a1605c1b6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,17 @@ +{ + "cSpell.words": [ + "Iotivity" + ], + "go.testEnvVars": { + "DIAL_ACME_CA_POOL":"${workspaceFolder}/.tmp/step-ca/data/certs/root_ca.crt", + "DIAL_ACME_DOMAINS":"localhost", + "DIAL_ACME_DIRECTORY_URL":"https://localhost:10443/acme/acme/directory", + "LISTEN_ACME_CA_POOL":"${workspaceFolder}/.tmp/step-ca/data/certs/root_ca.crt", + "LISTEN_ACME_DOMAINS":"localhost", + "LISTEN_ACME_DEVICE_ID":"adebc667-1f2b-41e3-bf5c-6d6eabc68cc6", + "LISTEN_ACME_DIRECTORY_URL":"https://localhost:10443/acme/acme/directory", + "TEST_COAP_GW_OVERWRITE_LISTEN_ACME_DIRECTORY_URL": "https://localhost:10443/acme/ocf.gw/directory", + // "GOFLAGS":"-mod=vendor", + }, + "go.testTimeout": "180s" +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..22c696091 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,5 @@ +FROM golang:1.13.5-alpine3.10 AS test-build +RUN apk add --no-cache curl git build-base +WORKDIR $GOPATH/src/github.com/go-ocf/cloud +COPY . . +RUN go mod download \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..d7b6e2e0f --- /dev/null +++ b/Makefile @@ -0,0 +1,92 @@ +SHELL = /bin/bash +SIMULATOR_NAME_SUFFIX ?= $(shell hostname) + +SUBDIRS := resource-aggregate authorization resource-directory openapi-connector openapi-gateway coap-gateway grpc-gateway certificate-authority portal-webapi bundle +.PHONY: $(SUBDIRS) push proto/generate clean build test env make-mongo make-nats make-ca test-build + +default: build + +make-ca: + docker pull ocfcloud/step-ca:vnext + if [ "${TRAVIS_OS_NAME}" == "linux" ]; then \ + sudo sh -c 'echo net.ipv4.ip_unprivileged_port_start=0 > /etc/sysctl.d/50-unprivileged-ports.conf'; \ + sudo sysctl --system; \ + fi + mkdir -p ./.tmp/step-ca/data/secrets + echo "password" > ./.tmp/step-ca/data/secrets/password + docker run \ + -it \ + -v "$(shell pwd)"/.tmp/step-ca/data:/home/step --user $(shell id -u):$(shell id -g) \ + ocfcloud/step-ca:vnext \ + /bin/bash -c "step ca init -dns localhost -address=:10443 -provisioner=test@localhost -name test -password-file ./secrets/password && step ca provisioner add acme --type ACME && step ca provisioner add ocf.gw --type ACME" + docker run \ + -d \ + --network=host \ + --name=step-ca-test \ + -v /etc/nsswitch.conf:/etc/nsswitch.conf \ + -v "$(shell pwd)"/.tmp/step-ca/data:/home/step --user $(shell id -u):$(shell id -g) \ + ocfcloud/step-ca:vnext + +make-nats: + sleep 1 + docker exec -it step-ca-test /bin/bash -c "mkdir -p certs/nats && step ca certificate localhost certs/nats/nats.crt certs/nats/nats.key --provisioner acme" + docker run \ + -d \ + --network=host \ + --name=nats \ + -v $(shell pwd)/.tmp/step-ca/data/certs:/certs \ + nats --tls --tlsverify --tlscert=/certs/nats/nats.crt --tlskey=/certs/nats/nats.key --tlscacert=/certs/root_ca.crt + +make-mongo: + sleep 1 + mkdir -p $(shell pwd)/.tmp/mongo + docker exec -it step-ca-test /bin/bash -c "mkdir -p certs/mongo && step ca certificate localhost certs/mongo/mongo.crt certs/mongo/mongo.key --provisioner acme && cat certs/mongo/mongo.crt >> certs/mongo/mongo.key" + docker run \ + -d \ + --network=host \ + --name=mongo \ + -v $(shell pwd)/.tmp/mongo:/data/db \ + -v $(shell pwd)/.tmp/step-ca/data/certs:/certs --user $(shell id -u):$(shell id -g) \ + mongo --tlsMode requireTLS --tlsCAFile /certs/root_ca.crt --tlsCertificateKeyFile certs/mongo/mongo.key + +env: clean make-ca make-nats make-mongo + docker build ./device-simulator --network=host -t device-simulator --target service + docker run -d --name=devsim --network=host -t device-simulator devsim-$(SIMULATOR_NAME_SUFFIX) + +test-build: + docker build \ + --network=host \ + --tag test-build \ + . + +test: env test-build + docker run \ + --network=host \ + -v $(shell pwd)/.tmp/step-ca/data/certs/root_ca.crt:/root_ca.crt \ + -e DIAL_ACME_CA_POOL=/root_ca.crt \ + -e DIAL_ACME_DOMAINS="localhost" \ + -e DIAL_ACME_DIRECTORY_URL="https://localhost:10443/acme/acme/directory" \ + -e LISTEN_ACME_CA_POOL=/root_ca.crt \ + -e LISTEN_ACME_DOMAINS="localhost" \ + -e LISTEN_ACME_DEVICE_ID="adebc667-1f2b-41e3-bf5c-6d6eabc68cc6" \ + -e LISTEN_ACME_DIRECTORY_URL="https://localhost:10443/acme/acme/directory" \ + -e TEST_COAP_GW_OVERWRITE_LISTEN_ACME_DIRECTORY_URL="https://localhost:10443/acme/ocf.gw/directory" \ + --mount type=bind,source="$(shell pwd)",target=/shared \ + test-build \ + go test -p 1 -v ./... -covermode=atomic -coverprofile=/shared/coverage.txt + +build: $(SUBDIRS) + +clean: + docker rm -f step-ca-test || true + docker rm -f mongo || true + docker rm -f nats || true + docker rm -f devsim || true + rm -rf ./.tmp/step-ca || true + rm -rf ./.tmp/mongo || true + +proto/generate: $(SUBDIRS) +push: $(SUBDIRS) + +$(SUBDIRS): + $(MAKE) -C $@ $(MAKECMDGOALS) diff --git a/README.md b/README.md new file mode 100644 index 000000000..f11572556 --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +[![Build Status](https://travis-ci.com/go-ocf/cloud.svg?branch=master)](https://travis-ci.com/go-ocf/cloud) +[![codecov](https://codecov.io/gh/go-ocf/cloud/branch/master/graph/badge.svg)](https://codecov.io/gh/go-ocf/cloud) +[![Go Report](https://goreportcard.com/badge/github.com/go-ocf/cloud)](https://goreportcard.com/report/github.com/go-ocf/cloud) +[![Gitter](https://badges.gitter.im/ocfcloud/Lobby.svg)](https://gitter.im/ocfcloud/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +# cloud diff --git a/authorization/.dockerignore b/authorization/.dockerignore new file mode 100644 index 000000000..0fc07e00b --- /dev/null +++ b/authorization/.dockerignore @@ -0,0 +1,25 @@ +cmd/authorization-web/authorization-web +cmd/authorization-sevice/authorization-sevice + +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# go CLI +*.test +coverage.txt + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# IDE +.vscode/ +debug + +# Dep's guards against upstream overwrites +# https://golang.github.io/dep/docs/FAQ.html#should-i-commit-my-vendor-directory +vendor/ + diff --git a/authorization/Dockerfile b/authorization/Dockerfile new file mode 100644 index 000000000..40c0d9487 --- /dev/null +++ b/authorization/Dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.13.5-alpine3.10 AS build +RUN apk add --no-cache curl git build-base +WORKDIR $GOPATH/src/github.com/go-ocf/cloud/authorization +COPY . . +RUN go mod download +RUN go build -o /go/bin/service ./cmd/service + +FROM alpine:3.11 as service +RUN apk add --no-cache ca-certificates +COPY --from=build /go/bin/service /usr/local/bin/service +ENTRYPOINT ["/usr/local/bin/service"] \ No newline at end of file diff --git a/authorization/Makefile b/authorization/Makefile new file mode 100644 index 000000000..084b8969c --- /dev/null +++ b/authorization/Makefile @@ -0,0 +1,31 @@ +SHELL = /bin/bash +SERVICE_NAME = $(notdir $(CURDIR)) +LATEST_TAG = vnext +VERSION_TAG = vnext-$(shell git rev-parse --short=7 --verify HEAD) + +default: build + +define build-docker-image + docker build \ + --network=host \ + --tag ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) \ + --tag ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) \ + --target $(1) \ + --file Dockerfile \ + . +endef + +build-servicecontainer: + $(call build-docker-image,service) + +build: build-servicecontainer + +push: build-servicecontainer + docker push ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) + docker push ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) + +proto/generate: + protoc -I=. -I=${GOPATH}/src -I=./pb --gogofaster_out=${GOPATH}/src pb/auth.proto + protoc -I=. -I=${GOPATH}/src -I=./pb --go_out=plugins=grpc:${GOPATH}/src pb/service.proto + +.PHONY: build-servicecontainer build push clean proto/generate diff --git a/authorization/README.md b/authorization/README.md new file mode 100644 index 000000000..ea9a07677 --- /dev/null +++ b/authorization/README.md @@ -0,0 +1,90 @@ +[![GoDoc](https://godoc.org/github.com/go-ocf/cloud/authorization?status.svg)](https://godoc.org/github.com/go-ocf/cloud/authorization) +[![Go Report Card](https://goreportcard.com/badge/go-ocf/authorization)](https://goreportcard.com/report/go-ocf/authorization) +[![Gitter](https://badges.gitter.im/ocfcloud/Lobby.svg)](https://gitter.im/ocfcloud/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +# Authorization + +- [Specification](https://wiki.iotivity.org/coapnativecloud#authorization_bounded_context) + +## Authorization Web + +The authorization web provides forms for the following manual actions: +- Obtain **Authorization Code** from GitHub +- Demonstrate the exchange for an **Access Token** + +Run the web server as follows and open http://127.0.0.1:7000/ in your browser. + +```bash +docker build . --network=host -t authorization:web --target web +docker run --network=host authorization:web +``` + +The application can be configured using [environment variables](web/config.go) + +## Authorization Service + +The authorization service exposes Protobuf via HTTP/1.1 ([Open API](openapi.yaml)). + +```bash +docker build . --network=host -t authorization:service --target service +docker run -e CLIENT_ID='my id' -e CLIENT_SECRET='my secret' --network=host authorization:service +``` + +The application can be configured using [environment variables](service/config.go) + +To obtain the **client ID** and **secret**, register your application at +[GitHub](https://github.com/settings/applications) +and set the callback to `/oauth_callback` (e.g. `http://127.0.0.1:7000/oauth_callback`). + +# Limitations + +This reference implementation lacks the following features: +- Shared access to devices by mutliple users +- Deletion of expired tokens +- Encryption in transit and at rest +- Clustered deployment + +# Build + +## Docker + +```sh +make build-servicecontainer +``` +## Local machine + +```sh +go build ./cmd/service/ +``` + +## Configuration +| Option | ENV variable | Type | Description | Default | +| ------ | --------- | ----------- | ------- | ------- | +| `-` | `ADDRESS` | string | tbd | `"0.0.0.0:9100"` | +| `-` | `DEVICE_PROVIDER` | string | `value which comes from the device during the sign-up ("apn")` | `"github"` | +| `-` | `DEVICE_OAUTH_CLIENT_ID` | string | tbd | `""` | +| `-` | `DEVICE_OAUTH_CLIENT_SECRET` | string | tbd | `""` | +| `-` | `DEVICE_OAUTH_REDIRECT_URL` | string | tbd | `""` | +| `-` | `DEVICE_OAUTH_ENDPOINT_AUTH_URL` | string | tbd | `""` | +| `-` | `DEVICE_OAUTH_ENDPOINT_TOKEN_URL` | string | tbd | `""` | +| `-` | `DEVICE_OAUTH_SCOPES` | string | Comma separated list of required scopes | `""` | +| `-` | `DEVICE_OAUTH_RESPONSE_MODE` | string | one of "query/post_form" | `"query"` | +| `-` | `SDK_OAUTH_CLIENT_ID` | string | tbd | `""` | +| `-` | `SDK_OAUTH_REDIRECT_URL` | string | tbd | `""` | +| `-` | `SDK_OAUTH_ENDPOINT_AUTH_URL` | string | tbd | `""` | +| `-` | `SDK_OAUTH_AUDIENCE` | string | tbd | `""` | +| `-` | `SDK_OAUTH_SCOPES` | string | Comma separated list of required scopes | `""` | +| `-` | `SDK_OAUTH_RESPONSE_MODE` | string | one of "query/post_form" | `"query"` | +| `-` | `LISTEN_TYPE` | string | tbd | `"acme"` | +| `-` | `LISTEN_ACME_CA_POOL` | string | tbd | `""` | +| `-` | `LISTEN_ACME_DIRECTORY_URL` | string | tbd | `""` | +| `-` | `LISTEN_ACME_DOMAINS` | string | tbd | `""` | +| `-` | `LISTEN_ACME_REGISTRATION_EMAIL` | string | tbd | `""` | +| `-` | `LISTEN_ACME_TICK_FREQUENCY` | string | tbd | `""` | +| `-` | `LISTEN_FILE_CA_POOL` | string | tbd | `""` | +| `-` | `LISTEN_FILE_CERT_KEY_NAME` | string | tbd | `""` | +| `-` | `LISTEN_FILE_CERT_DIR_PATH` | string | tbd | `""` | +| `-` | `LISTEN_FILE_CERT_NAME` | string | tbd | `""` | +| `-` | `LOG_ENABLE_DEBUG` | bool | tbd | `false` | +| `-` | `MONGODB_URI` | string | tbd | `"mongodb://localhost:27017"` | +| `-` | `MONGODB_DATABASE` | string | tbd | `"authorization"` | diff --git a/authorization/client/userDevices.go b/authorization/client/userDevices.go new file mode 100644 index 000000000..fb6b35fd5 --- /dev/null +++ b/authorization/client/userDevices.go @@ -0,0 +1,341 @@ +package service + +import ( + "context" + "fmt" + "io" + "sync" + "time" + + "golang.org/x/sync/semaphore" + + kitSync "github.com/go-ocf/kit/sync" + pbAS "github.com/go-ocf/cloud/authorization/pb" + "google.golang.org/grpc/status" +) + +// UserDevicesManager provides notification mechanism about devices. +type UserDevicesManager struct { + fn TriggerFunc + asClient pbAS.AuthorizationServiceClient + errFunc ErrFunc + + lock sync.RWMutex + users map[string]*kitSync.RefCounter + done chan struct{} + trigger chan triggerUserDevice + doneWg sync.WaitGroup +} + +// TriggerFunc notifies users remove/add device. +type TriggerFunc = func(ctx context.Context, userID string, addedDevices, removedDevices, currentDevices map[string]bool) + +// ErrFunc reports errors +type ErrFunc func(err error) + +// NewUserDevicesManager creates userID devices manager. +func NewUserDevicesManager(fn TriggerFunc, asClient pbAS.AuthorizationServiceClient, tickFrequency, expiration time.Duration, errFunc ErrFunc) *UserDevicesManager { + m := &UserDevicesManager{ + fn: fn, + asClient: asClient, + done: make(chan struct{}), + trigger: make(chan triggerUserDevice, 32), + users: make(map[string]*kitSync.RefCounter), + errFunc: errFunc, + } + m.doneWg.Add(1) + go m.run(tickFrequency, expiration) + return m +} + +func (d *UserDevicesManager) getRef(userID string, create bool) (_ *kitSync.RefCounter, created bool) { + d.lock.Lock() + defer d.lock.Unlock() + u, ok := d.users[userID] + created = false + if !ok && create { + u = kitSync.NewRefCounter(&userDevices{ + devices: make(map[string]bool), + userID: userID, + lock: semaphore.NewWeighted(1), + validTo: time.Now().Add(time.Hour * 24), + }, func(ctx context.Context, data interface{}) error { + u := data.(*userDevices) + d.fn(ctx, u.userID, nil, u.getDevices(), nil) + return nil + }) + d.users[userID] = u + created = true + } else if ok { + u.Acquire() + } + return u, created +} + +// Acquire acquires reference counter by 1 for userID. +func (d *UserDevicesManager) Acquire(ctx context.Context, userID string) error { + v, created := d.getRef(userID, true) + if created { + userDevices, err := getUsersDevices(ctx, d.asClient, []string{userID}) + if err != nil { + v.Release(ctx) + return err + } + d.trigger <- triggerUserDevice{ + userID: userID, + userDevices: userDevices, + create: true, + } + } else { + d.trigger <- triggerUserDevice{ + userID: userID, + } + } + + return nil +} + +func (d *UserDevicesManager) IsUserDevice(userID, deviceID string) bool { + v, _ := d.getRef(userID, false) + if v == nil { + return false + } + defer v.Release(context.Background()) // getRef increase ref counter + return v.Data().(*userDevices).isUserDevice(deviceID) +} + +// Release releases reference counter by 1 over userID. +func (d *UserDevicesManager) release(userID string) *kitSync.RefCounter { + d.lock.Lock() + defer d.lock.Unlock() + u, ok := d.users[userID] + if !ok { + return nil + } + if 1 == u.Count() { + delete(d.users, userID) + } + return u +} + +// Release releases reference counter by 1 over userID. +func (d *UserDevicesManager) Release(userID string) error { + u := d.release(userID) + if u != nil { + return u.Release(context.Background()) + } + return nil +} + +func (d *UserDevicesManager) updateDevices(ctx context.Context, userID string, deviceIDs []string, validTo time.Time) (added, removed, allDevices map[string]bool) { + v, _ := d.getRef(userID, false) + if v == nil { + return + } + defer func() { + err := v.Release(ctx) + if err != nil { + d.errFunc(fmt.Errorf("cannot release userID %v devices: %v", userID, err)) + } + }() + + return v.Data().(*userDevices).updateDevices(deviceIDs, validTo) +} + +func (d *UserDevicesManager) getDevices(ctx context.Context, userID string) map[string]bool { + v, _ := d.getRef(userID, false) + if v == nil { + return nil + } + defer func() { + err := v.Release(ctx) + if err != nil { + d.errFunc(fmt.Errorf("cannot release userID %v devices: %v", userID, err)) + } + }() + return v.Data().(*userDevices).getDevices() +} + +func (d *UserDevicesManager) getUsers(triggerTime time.Time) []string { + d.lock.Lock() + defer d.lock.Unlock() + users := make([]string, 0, len(d.users)) + for u, v := range d.users { + if v.Data().(*userDevices).isExpired(triggerTime) { + users = append(users, u) + } + } + return users +} + +func getUsersDevices(ctx context.Context, asClient pbAS.AuthorizationServiceClient, usedIDs []string) (map[string][]string, error) { + getUserDevicesClient, err := asClient.GetUserDevices(ctx, &pbAS.GetUserDevicesRequest{ + UserIdsFilter: usedIDs, + }) + if err != nil { + return nil, status.Errorf(status.Convert(err).Code(), "cannot get users devices: %v", err) + } + defer getUserDevicesClient.CloseSend() + userDevices := make(map[string][]string) + for _, userID := range usedIDs { + userDevices[userID] = make([]string, 0, 32) + } + for { + userDevice, err := getUserDevicesClient.Recv() + if err == io.EOF { + break + } + if err != nil { + return nil, status.Errorf(status.Convert(err).Code(), "cannot get users devices: %v", err) + } + devices, ok := userDevices[userDevice.UserId] + if ok { + userDevices[userDevice.UserId] = append(devices, userDevice.DeviceId) + } + } + return userDevices, nil +} + +func (d *UserDevicesManager) onTick(timeout time.Duration, expiration time.Duration, triggerTime time.Time) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + users := d.getUsers(triggerTime) + if len(users) == 0 { + return + } + usersDevices, err := getUsersDevices(ctx, d.asClient, users) + if err != nil { + d.errFunc(fmt.Errorf("cannot get user devices: %v", err)) + return + } + for userID, devices := range usersDevices { + added, removed, all := d.updateDevices(ctx, userID, devices, time.Now().Add(expiration)) + if len(added) != 0 || len(removed) != 0 { + d.fn(ctx, userID, added, removed, all) + } + } +} + +func (d *UserDevicesManager) onTrigger(timeout time.Duration, expiration time.Duration, t triggerUserDevice) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + if t.create { + for userID, devices := range t.userDevices { + added, removed, all := d.updateDevices(ctx, userID, devices, time.Now().Add(expiration)) + d.fn(ctx, t.userID, added, removed, all) + } + } else { + all := d.getDevices(ctx, t.userID) + d.fn(ctx, t.userID, nil, nil, all) + } +} + +func (d *UserDevicesManager) run(tickFrequency, expiration time.Duration) { + ticker := time.NewTicker(tickFrequency) + defer d.doneWg.Done() + defer ticker.Stop() + for { + select { + case <-d.done: + return + case triggerTime := <-ticker.C: + d.onTick(tickFrequency, expiration, triggerTime) + case t := <-d.trigger: + d.onTrigger(tickFrequency, expiration, t) + } + } +} + +// Close stops userID manager goroutine. +func (d *UserDevicesManager) Close() { + close(d.done) + d.doneWg.Wait() +} + +type triggerUserDevice struct { + userID string + userDevices map[string][]string + create bool +} + +type userDevices struct { + lock *semaphore.Weighted + userID string + devices map[string]bool + validTo time.Time +} + +func (u *userDevices) isExpired(now time.Time) bool { + if !u.lock.TryAcquire(1) { + return false + } + defer u.lock.Release(1) + if u.validTo.Sub(now) <= 0 { + return true + } + return false +} + +func (u *userDevices) getDevices() map[string]bool { + u.lock.Acquire(context.Background(), 1) + defer u.lock.Release(1) + devices := make(map[string]bool) + for deviceID := range u.devices { + devices[deviceID] = true + } + return devices +} + +func (u *userDevices) isUserDevice(deviceID string) bool { + u.lock.Acquire(context.Background(), 1) + defer u.lock.Release(1) + return u.devices[deviceID] +} + +func (u *userDevices) setDevices(deviceIDs []string) { + devices := make(map[string]bool) + for _, deviceID := range deviceIDs { + devices[deviceID] = true + } + u.lock.Acquire(context.Background(), 1) + defer u.lock.Release(1) + u.devices = devices +} + +func (u *userDevices) updateDevices(deviceIDs []string, validTo time.Time) (added, removed, allDevices map[string]bool) { + + added = make(map[string]bool) + u.lock.Acquire(context.Background(), 1) + defer u.lock.Release(1) + for _, deviceID := range deviceIDs { + _, ok := u.devices[deviceID] + if !ok { + added[deviceID] = true + } + } + + removed = make(map[string]bool) + for deviceID := range u.devices { + removed[deviceID] = true + } + for _, deviceID := range deviceIDs { + _, ok := removed[deviceID] + if ok { + delete(removed, deviceID) + } + } + + devices := make(map[string]bool) + for _, deviceID := range deviceIDs { + devices[deviceID] = true + } + u.devices = devices + u.validTo = validTo + + allDevices = make(map[string]bool) + for _, deviceID := range deviceIDs { + allDevices[deviceID] = true + } + + return added, removed, allDevices +} diff --git a/authorization/client/userDevices_test.go b/authorization/client/userDevices_test.go new file mode 100644 index 000000000..ce1d4c16d --- /dev/null +++ b/authorization/client/userDevices_test.go @@ -0,0 +1,301 @@ +package service + +import ( + "context" + "fmt" + "github.com/go-ocf/kit/security/certManager" + "testing" + "time" + + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/cloud/authorization/service" + testService "github.com/go-ocf/cloud/authorization/test/service" + + _ "github.com/mattn/go-sqlite3" // sql driver +) + +type testTrigger struct { + addedDevices map[string]map[string]bool + removedDevices map[string]map[string]bool + allDevices map[string]map[string]bool +} + +func newTestTrigger() *testTrigger { + return &testTrigger{} +} + +func (t *testTrigger) Trigger(ctx context.Context, userID string, addedDevices, removedDevices, allDevices map[string]bool) { + if len(addedDevices) > 0 { + if t.addedDevices == nil { + t.addedDevices = make(map[string]map[string]bool) + } + devices, ok := t.addedDevices[userID] + if !ok { + devices = make(map[string]bool) + t.addedDevices[userID] = devices + } + for deviceID := range addedDevices { + devices[deviceID] = true + } + } + if len(removedDevices) > 0 { + if t.removedDevices == nil { + t.removedDevices = make(map[string]map[string]bool) + } + devices, ok := t.removedDevices[userID] + if !ok { + devices = make(map[string]bool) + t.removedDevices[userID] = devices + } + for deviceID := range removedDevices { + devices[deviceID] = true + } + } + if len(allDevices) == 0 { + t.allDevices = nil + return + } + if t.allDevices == nil { + t.allDevices = make(map[string]map[string]bool) + } + devices := make(map[string]bool) + t.allDevices[userID] = devices + + for deviceID := range allDevices { + devices[deviceID] = true + } +} + +func TestAddDeviceAfterRegister(t *testing.T) { + trigger := newTestTrigger() + + var cfg service.Config + err := envconfig.Process("", &cfg) + require.NoError(t, err) + cfg.Addr = "localhost:1234" + + shutdown := testService.NewAuthServer(t, cfg) + defer shutdown() + + var acmeCfg certManager.Config + err = envconfig.Process("LISTEN", &acmeCfg) + require.NoError(t, err) + certMgr, err := certManager.NewCertManager(acmeCfg) + require.NoError(t, err) + tlsCfg := certMgr.GetClientTLSConfig() + + conn, err := grpc.Dial(cfg.Addr, grpc.WithTransportCredentials(credentials.NewTLS(&tlsCfg))) + require.NoError(t, err) + c := pb.NewAuthorizationServiceClient(conn) + + m := NewUserDevicesManager(trigger.Trigger, c, time.Millisecond*200, time.Millisecond*500, func(err error) { fmt.Println(err) }) + err = m.Acquire(context.Background(), t.Name()) + require.NoError(t, err) + + _, err = c.AddDevice(context.Background(), &pb.AddDeviceRequest{ + UserId: t.Name(), + DeviceId: "deviceId_" + t.Name(), + }) + + time.Sleep(time.Second * 2) + require.Equal(t, map[string]map[string]bool{ + t.Name(): map[string]bool{ + "deviceId_" + t.Name(): true, + }, + }, trigger.allDevices) + + _, err = c.RemoveDevice(context.Background(), &pb.RemoveDeviceRequest{ + UserId: t.Name(), + DeviceId: "deviceId_" + t.Name(), + }) + + time.Sleep(time.Second * 2) + require.Equal(t, map[string]map[string]bool(nil), trigger.allDevices) + + err = m.Release(t.Name()) + require.NoError(t, err) +} + +func TestUserDevicesManager_Acquire(t *testing.T) { + type fields struct { + trigger *testTrigger + } + type args struct { + userID string + } + tests := []struct { + name string + fields fields + args args + wantErr bool + want *testTrigger + }{ + { + name: "empty - user not exist", + fields: fields{ + trigger: newTestTrigger(), + }, + args: args{ + userID: "notExist", + }, + want: &testTrigger{}, + }, + { + name: "valid", + fields: fields{ + trigger: newTestTrigger(), + }, + args: args{ + userID: t.Name(), + }, + want: &testTrigger{ + addedDevices: map[string]map[string]bool{ + t.Name(): map[string]bool{ + "deviceId_" + t.Name(): true, + }, + }, + allDevices: map[string]map[string]bool{ + t.Name(): map[string]bool{ + "deviceId_" + t.Name(): true, + }, + }, + }, + }, + } + + var cfg service.Config + err := envconfig.Process("", &cfg) + require.NoError(t, err) + cfg.Addr = "localhost:1234" + + shutdown := testService.NewAuthServer(t, cfg) + defer shutdown() + + var acmeCfg certManager.Config + err = envconfig.Process("LISTEN", &acmeCfg) + require.NoError(t, err) + certMgr, err := certManager.NewCertManager(acmeCfg) + require.NoError(t, err) + tlsCfg := certMgr.GetClientTLSConfig() + + conn, err := grpc.Dial(cfg.Addr, grpc.WithTransportCredentials(credentials.NewTLS(&tlsCfg))) + require.NoError(t, err) + c := pb.NewAuthorizationServiceClient(conn) + + _, err = c.AddDevice(context.Background(), &pb.AddDeviceRequest{ + UserId: t.Name(), + DeviceId: "deviceId_" + t.Name(), + }) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := NewUserDevicesManager(tt.fields.trigger.Trigger, c, time.Millisecond*200, time.Second, func(err error) { fmt.Println(err) }) + err := m.Acquire(context.Background(), tt.args.userID) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + time.Sleep(time.Second) + require.Equal(t, tt.want, tt.fields.trigger) + err := m.Release(tt.args.userID) + require.NoError(t, err) + } + }) + } +} + +func TestUserDevicesManager_Release(t *testing.T) { + type fields struct { + trigger *testTrigger + } + type args struct { + userID string + } + tests := []struct { + name string + fields fields + args args + wantErr bool + want *testTrigger + wantMgmtSize int + }{ + { + name: "empty - user not exist", + fields: fields{ + trigger: newTestTrigger(), + }, + args: args{ + userID: "notExist", + }, + want: &testTrigger{}, + }, + { + name: "valid", + fields: fields{ + trigger: newTestTrigger(), + }, + args: args{ + userID: t.Name(), + }, + want: &testTrigger{ + addedDevices: map[string]map[string]bool{ + t.Name(): map[string]bool{ + "deviceId_" + t.Name(): true, + }, + }, + removedDevices: map[string]map[string]bool{ + t.Name(): map[string]bool{ + "deviceId_" + t.Name(): true, + }, + }, + }, + wantMgmtSize: 0, + }, + } + + var cfg service.Config + err := envconfig.Process("", &cfg) + require.NoError(t, err) + cfg.Addr = "localhost:1234" + + shutdown := testService.NewAuthServer(t, cfg) + defer shutdown() + + var acmeCfg certManager.Config + err = envconfig.Process("LISTEN", &acmeCfg) + require.NoError(t, err) + certMgr, err := certManager.NewCertManager(acmeCfg) + require.NoError(t, err) + tlsCfg := certMgr.GetClientTLSConfig() + + conn, err := grpc.Dial(cfg.Addr, grpc.WithTransportCredentials(credentials.NewTLS(&tlsCfg))) + require.NoError(t, err) + c := pb.NewAuthorizationServiceClient(conn) + + _, err = c.AddDevice(context.Background(), &pb.AddDeviceRequest{ + UserId: t.Name(), + DeviceId: "deviceId_" + t.Name(), + }) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := NewUserDevicesManager(tt.fields.trigger.Trigger, c, time.Millisecond*200, time.Millisecond*500, func(err error) { fmt.Println(err) }) + err := m.Acquire(context.Background(), tt.args.userID) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + time.Sleep(time.Second) + err := m.Release(tt.args.userID) + require.NoError(t, err) + require.Equal(t, tt.want, tt.fields.trigger) + require.Equal(t, tt.wantMgmtSize, len(m.users)) + } + }) + } +} diff --git a/authorization/cmd/service/main.go b/authorization/cmd/service/main.go new file mode 100644 index 000000000..a11c4afc5 --- /dev/null +++ b/authorization/cmd/service/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "context" + + "github.com/go-ocf/cloud/authorization/persistence/mongodb" + "github.com/go-ocf/cloud/authorization/provider" + "github.com/go-ocf/cloud/authorization/service" + "github.com/go-ocf/kit/log" + "github.com/go-ocf/kit/security/certManager" + "github.com/kelseyhightower/envconfig" + + _ "github.com/mattn/go-sqlite3" // sql driver +) + +func main() { + var cfg service.Config + if err := envconfig.Process("", &cfg); err != nil { + log.Fatalf("cannot parse config: %v", err) + } + + log.Setup(cfg.Log) + log.Info(cfg.String()) + + dialCertManager, err := certManager.NewCertManager(cfg.Dial) + if err != nil { + log.Fatalf("cannot parse config: %v", err) + } + + tlsConfig := dialCertManager.GetClientTLSConfig() + + persistence, err := mongodb.NewStore(context.Background(), cfg.MongoDB, mongodb.WithTLS(&tlsConfig)) + if err != nil { + log.Fatalf("cannot parse config: %v", err) + } + if cfg.Device.OAuth2.AccessType == "" { + cfg.Device.OAuth2.AccessType = "offline" + } + if cfg.SDK.AccessType == "" { + cfg.SDK.AccessType = "online" + } + if cfg.Device.OAuth2.ResponseType == "" { + cfg.Device.OAuth2.ResponseType = "code" + } + if cfg.Device.OAuth2.ResponseMode == "" { + cfg.Device.OAuth2.ResponseMode = "query" + } + if cfg.SDK.ResponseType == "" { + cfg.SDK.ResponseType = "token" + } + if cfg.SDK.ResponseMode == "" { + cfg.SDK.ResponseMode = "query" + } + deviceProvider := provider.New(cfg.Device) + sdkProvider := provider.New(provider.Config{ + Provider: "generic", + OAuth2: cfg.SDK, + }) + s, err := service.New(cfg, persistence, deviceProvider, sdkProvider) + if err != nil { + log.Fatalf("cannot parse config: %v", err) + } + s.Serve() +} diff --git a/authorization/openapi.yaml b/authorization/openapi.yaml new file mode 100644 index 000000000..11bb21d06 --- /dev/null +++ b/authorization/openapi.yaml @@ -0,0 +1,25 @@ +openapi: 3.0.0 +info: + description: > + DRAFT: + This is a KiConnect Authorization Service API. + version: 1.0.0 + title: Authorization Service +paths: + /api/signup: + post: + summary: TODO + description: > + TODO + requestBody: + content: + POST: + schema: + $ref: 'https://github.com/go-ocf/cloud/authorization/blob/master/protobuf/auth/auth.proto#/SignUpRequest' + responses: + '200': + description: OK + content: + ocf.cloud.auth: + schema: + $ref: 'https://github.com/go-ocf/cloud/authorization/blob/master/protobuf/auth/auth.proto#/SignUpResponse' diff --git a/authorization/pb/auth.pb.go b/authorization/pb/auth.pb.go new file mode 100644 index 000000000..a553b5440 --- /dev/null +++ b/authorization/pb/auth.pb.go @@ -0,0 +1,3619 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: pb/auth.proto + +package pb + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.account.raml#L27 +type SignUpRequest struct { + DeviceId string `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` + AuthorizationCode string `protobuf:"bytes,2,opt,name=authorization_code,json=authorizationCode,proto3" json:"authorization_code,omitempty"` + AuthorizationProvider string `protobuf:"bytes,3,opt,name=authorization_provider,json=authorizationProvider,proto3" json:"authorization_provider,omitempty"` +} + +func (m *SignUpRequest) Reset() { *m = SignUpRequest{} } +func (m *SignUpRequest) String() string { return proto.CompactTextString(m) } +func (*SignUpRequest) ProtoMessage() {} +func (*SignUpRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c43ab4a3390919ea, []int{0} +} +func (m *SignUpRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignUpRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignUpRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SignUpRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignUpRequest.Merge(m, src) +} +func (m *SignUpRequest) XXX_Size() int { + return m.Size() +} +func (m *SignUpRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SignUpRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SignUpRequest proto.InternalMessageInfo + +func (m *SignUpRequest) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +func (m *SignUpRequest) GetAuthorizationCode() string { + if m != nil { + return m.AuthorizationCode + } + return "" +} + +func (m *SignUpRequest) GetAuthorizationProvider() string { + if m != nil { + return m.AuthorizationProvider + } + return "" +} + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.account.raml#L40 +type SignUpResponse struct { + AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` + UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + RefreshToken string `protobuf:"bytes,3,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"` + ExpiresIn int64 `protobuf:"varint,4,opt,name=expires_in,json=expiresIn,proto3" json:"expires_in,omitempty"` + RedirectUri string `protobuf:"bytes,5,opt,name=redirect_uri,json=redirectUri,proto3" json:"redirect_uri,omitempty"` +} + +func (m *SignUpResponse) Reset() { *m = SignUpResponse{} } +func (m *SignUpResponse) String() string { return proto.CompactTextString(m) } +func (*SignUpResponse) ProtoMessage() {} +func (*SignUpResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c43ab4a3390919ea, []int{1} +} +func (m *SignUpResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignUpResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignUpResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SignUpResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignUpResponse.Merge(m, src) +} +func (m *SignUpResponse) XXX_Size() int { + return m.Size() +} +func (m *SignUpResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SignUpResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SignUpResponse proto.InternalMessageInfo + +func (m *SignUpResponse) GetAccessToken() string { + if m != nil { + return m.AccessToken + } + return "" +} + +func (m *SignUpResponse) GetUserId() string { + if m != nil { + return m.UserId + } + return "" +} + +func (m *SignUpResponse) GetRefreshToken() string { + if m != nil { + return m.RefreshToken + } + return "" +} + +func (m *SignUpResponse) GetExpiresIn() int64 { + if m != nil { + return m.ExpiresIn + } + return 0 +} + +func (m *SignUpResponse) GetRedirectUri() string { + if m != nil { + return m.RedirectUri + } + return "" +} + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.account.raml#L55 +type SignOffRequest struct { + DeviceId string `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` + UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` +} + +func (m *SignOffRequest) Reset() { *m = SignOffRequest{} } +func (m *SignOffRequest) String() string { return proto.CompactTextString(m) } +func (*SignOffRequest) ProtoMessage() {} +func (*SignOffRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c43ab4a3390919ea, []int{2} +} +func (m *SignOffRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignOffRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignOffRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SignOffRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignOffRequest.Merge(m, src) +} +func (m *SignOffRequest) XXX_Size() int { + return m.Size() +} +func (m *SignOffRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SignOffRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SignOffRequest proto.InternalMessageInfo + +func (m *SignOffRequest) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +func (m *SignOffRequest) GetUserId() string { + if m != nil { + return m.UserId + } + return "" +} + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.account.raml#L60 +type SignOffResponse struct { +} + +func (m *SignOffResponse) Reset() { *m = SignOffResponse{} } +func (m *SignOffResponse) String() string { return proto.CompactTextString(m) } +func (*SignOffResponse) ProtoMessage() {} +func (*SignOffResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c43ab4a3390919ea, []int{3} +} +func (m *SignOffResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignOffResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignOffResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SignOffResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignOffResponse.Merge(m, src) +} +func (m *SignOffResponse) XXX_Size() int { + return m.Size() +} +func (m *SignOffResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SignOffResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SignOffResponse proto.InternalMessageInfo + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.session.raml#L27 +type SignInRequest struct { + DeviceId string `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` + UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` +} + +func (m *SignInRequest) Reset() { *m = SignInRequest{} } +func (m *SignInRequest) String() string { return proto.CompactTextString(m) } +func (*SignInRequest) ProtoMessage() {} +func (*SignInRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c43ab4a3390919ea, []int{4} +} +func (m *SignInRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignInRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignInRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SignInRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignInRequest.Merge(m, src) +} +func (m *SignInRequest) XXX_Size() int { + return m.Size() +} +func (m *SignInRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SignInRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SignInRequest proto.InternalMessageInfo + +func (m *SignInRequest) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +func (m *SignInRequest) GetUserId() string { + if m != nil { + return m.UserId + } + return "" +} + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.session.raml#L39 +type SignInResponse struct { + ExpiresIn int64 `protobuf:"varint,1,opt,name=expires_in,json=expiresIn,proto3" json:"expires_in,omitempty"` +} + +func (m *SignInResponse) Reset() { *m = SignInResponse{} } +func (m *SignInResponse) String() string { return proto.CompactTextString(m) } +func (*SignInResponse) ProtoMessage() {} +func (*SignInResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c43ab4a3390919ea, []int{5} +} +func (m *SignInResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignInResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignInResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SignInResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignInResponse.Merge(m, src) +} +func (m *SignInResponse) XXX_Size() int { + return m.Size() +} +func (m *SignInResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SignInResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SignInResponse proto.InternalMessageInfo + +func (m *SignInResponse) GetExpiresIn() int64 { + if m != nil { + return m.ExpiresIn + } + return 0 +} + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.session.raml #Specification CR needed +type SignOutRequest struct { + DeviceId string `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` + UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + CorrelationId string `protobuf:"bytes,4,opt,name=correlation_id,json=correlationId,proto3" json:"correlation_id,omitempty"` +} + +func (m *SignOutRequest) Reset() { *m = SignOutRequest{} } +func (m *SignOutRequest) String() string { return proto.CompactTextString(m) } +func (*SignOutRequest) ProtoMessage() {} +func (*SignOutRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c43ab4a3390919ea, []int{6} +} +func (m *SignOutRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignOutRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignOutRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SignOutRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignOutRequest.Merge(m, src) +} +func (m *SignOutRequest) XXX_Size() int { + return m.Size() +} +func (m *SignOutRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SignOutRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SignOutRequest proto.InternalMessageInfo + +func (m *SignOutRequest) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +func (m *SignOutRequest) GetUserId() string { + if m != nil { + return m.UserId + } + return "" +} + +func (m *SignOutRequest) GetCorrelationId() string { + if m != nil { + return m.CorrelationId + } + return "" +} + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.session.raml #Specification CR needed +type SignOutResponse struct { +} + +func (m *SignOutResponse) Reset() { *m = SignOutResponse{} } +func (m *SignOutResponse) String() string { return proto.CompactTextString(m) } +func (*SignOutResponse) ProtoMessage() {} +func (*SignOutResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c43ab4a3390919ea, []int{7} +} +func (m *SignOutResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignOutResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignOutResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SignOutResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignOutResponse.Merge(m, src) +} +func (m *SignOutResponse) XXX_Size() int { + return m.Size() +} +func (m *SignOutResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SignOutResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SignOutResponse proto.InternalMessageInfo + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.tokenrefresh.raml#L27 +type RefreshTokenRequest struct { + DeviceId string `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` + UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + RefreshToken string `protobuf:"bytes,3,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"` +} + +func (m *RefreshTokenRequest) Reset() { *m = RefreshTokenRequest{} } +func (m *RefreshTokenRequest) String() string { return proto.CompactTextString(m) } +func (*RefreshTokenRequest) ProtoMessage() {} +func (*RefreshTokenRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c43ab4a3390919ea, []int{8} +} +func (m *RefreshTokenRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RefreshTokenRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RefreshTokenRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RefreshTokenRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RefreshTokenRequest.Merge(m, src) +} +func (m *RefreshTokenRequest) XXX_Size() int { + return m.Size() +} +func (m *RefreshTokenRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RefreshTokenRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RefreshTokenRequest proto.InternalMessageInfo + +func (m *RefreshTokenRequest) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +func (m *RefreshTokenRequest) GetUserId() string { + if m != nil { + return m.UserId + } + return "" +} + +func (m *RefreshTokenRequest) GetRefreshToken() string { + if m != nil { + return m.RefreshToken + } + return "" +} + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.tokenrefresh.raml#L40 +type RefreshTokenResponse struct { + AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"` + RefreshToken string `protobuf:"bytes,2,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"` + ExpiresIn int64 `protobuf:"varint,3,opt,name=expires_in,json=expiresIn,proto3" json:"expires_in,omitempty"` +} + +func (m *RefreshTokenResponse) Reset() { *m = RefreshTokenResponse{} } +func (m *RefreshTokenResponse) String() string { return proto.CompactTextString(m) } +func (*RefreshTokenResponse) ProtoMessage() {} +func (*RefreshTokenResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c43ab4a3390919ea, []int{9} +} +func (m *RefreshTokenResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RefreshTokenResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RefreshTokenResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RefreshTokenResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RefreshTokenResponse.Merge(m, src) +} +func (m *RefreshTokenResponse) XXX_Size() int { + return m.Size() +} +func (m *RefreshTokenResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RefreshTokenResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_RefreshTokenResponse proto.InternalMessageInfo + +func (m *RefreshTokenResponse) GetAccessToken() string { + if m != nil { + return m.AccessToken + } + return "" +} + +func (m *RefreshTokenResponse) GetRefreshToken() string { + if m != nil { + return m.RefreshToken + } + return "" +} + +func (m *RefreshTokenResponse) GetExpiresIn() int64 { + if m != nil { + return m.ExpiresIn + } + return 0 +} + +type GetUserDevicesRequest struct { + UserIdsFilter []string `protobuf:"bytes,1,rep,name=user_ids_filter,json=userIdsFilter,proto3" json:"user_ids_filter,omitempty"` + DeviceIdsFilter []string `protobuf:"bytes,2,rep,name=device_ids_filter,json=deviceIdsFilter,proto3" json:"device_ids_filter,omitempty"` +} + +func (m *GetUserDevicesRequest) Reset() { *m = GetUserDevicesRequest{} } +func (m *GetUserDevicesRequest) String() string { return proto.CompactTextString(m) } +func (*GetUserDevicesRequest) ProtoMessage() {} +func (*GetUserDevicesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c43ab4a3390919ea, []int{10} +} +func (m *GetUserDevicesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetUserDevicesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetUserDevicesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetUserDevicesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetUserDevicesRequest.Merge(m, src) +} +func (m *GetUserDevicesRequest) XXX_Size() int { + return m.Size() +} +func (m *GetUserDevicesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetUserDevicesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetUserDevicesRequest proto.InternalMessageInfo + +func (m *GetUserDevicesRequest) GetUserIdsFilter() []string { + if m != nil { + return m.UserIdsFilter + } + return nil +} + +func (m *GetUserDevicesRequest) GetDeviceIdsFilter() []string { + if m != nil { + return m.DeviceIdsFilter + } + return nil +} + +type UserDevice struct { + DeviceId string `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` + UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` +} + +func (m *UserDevice) Reset() { *m = UserDevice{} } +func (m *UserDevice) String() string { return proto.CompactTextString(m) } +func (*UserDevice) ProtoMessage() {} +func (*UserDevice) Descriptor() ([]byte, []int) { + return fileDescriptor_c43ab4a3390919ea, []int{11} +} +func (m *UserDevice) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UserDevice) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UserDevice.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UserDevice) XXX_Merge(src proto.Message) { + xxx_messageInfo_UserDevice.Merge(m, src) +} +func (m *UserDevice) XXX_Size() int { + return m.Size() +} +func (m *UserDevice) XXX_DiscardUnknown() { + xxx_messageInfo_UserDevice.DiscardUnknown(m) +} + +var xxx_messageInfo_UserDevice proto.InternalMessageInfo + +func (m *UserDevice) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +func (m *UserDevice) GetUserId() string { + if m != nil { + return m.UserId + } + return "" +} + +type AddDeviceRequest struct { + DeviceId string `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` + UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` +} + +func (m *AddDeviceRequest) Reset() { *m = AddDeviceRequest{} } +func (m *AddDeviceRequest) String() string { return proto.CompactTextString(m) } +func (*AddDeviceRequest) ProtoMessage() {} +func (*AddDeviceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c43ab4a3390919ea, []int{12} +} +func (m *AddDeviceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AddDeviceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AddDeviceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AddDeviceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AddDeviceRequest.Merge(m, src) +} +func (m *AddDeviceRequest) XXX_Size() int { + return m.Size() +} +func (m *AddDeviceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AddDeviceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_AddDeviceRequest proto.InternalMessageInfo + +func (m *AddDeviceRequest) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +func (m *AddDeviceRequest) GetUserId() string { + if m != nil { + return m.UserId + } + return "" +} + +type AddDeviceResponse struct { +} + +func (m *AddDeviceResponse) Reset() { *m = AddDeviceResponse{} } +func (m *AddDeviceResponse) String() string { return proto.CompactTextString(m) } +func (*AddDeviceResponse) ProtoMessage() {} +func (*AddDeviceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c43ab4a3390919ea, []int{13} +} +func (m *AddDeviceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AddDeviceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AddDeviceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AddDeviceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_AddDeviceResponse.Merge(m, src) +} +func (m *AddDeviceResponse) XXX_Size() int { + return m.Size() +} +func (m *AddDeviceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_AddDeviceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_AddDeviceResponse proto.InternalMessageInfo + +type RemoveDeviceRequest struct { + DeviceId string `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` + UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` +} + +func (m *RemoveDeviceRequest) Reset() { *m = RemoveDeviceRequest{} } +func (m *RemoveDeviceRequest) String() string { return proto.CompactTextString(m) } +func (*RemoveDeviceRequest) ProtoMessage() {} +func (*RemoveDeviceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c43ab4a3390919ea, []int{14} +} +func (m *RemoveDeviceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RemoveDeviceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveDeviceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RemoveDeviceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveDeviceRequest.Merge(m, src) +} +func (m *RemoveDeviceRequest) XXX_Size() int { + return m.Size() +} +func (m *RemoveDeviceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveDeviceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveDeviceRequest proto.InternalMessageInfo + +func (m *RemoveDeviceRequest) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +func (m *RemoveDeviceRequest) GetUserId() string { + if m != nil { + return m.UserId + } + return "" +} + +type RemoveDeviceResponse struct { +} + +func (m *RemoveDeviceResponse) Reset() { *m = RemoveDeviceResponse{} } +func (m *RemoveDeviceResponse) String() string { return proto.CompactTextString(m) } +func (*RemoveDeviceResponse) ProtoMessage() {} +func (*RemoveDeviceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c43ab4a3390919ea, []int{15} +} +func (m *RemoveDeviceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RemoveDeviceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RemoveDeviceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RemoveDeviceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RemoveDeviceResponse.Merge(m, src) +} +func (m *RemoveDeviceResponse) XXX_Size() int { + return m.Size() +} +func (m *RemoveDeviceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RemoveDeviceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_RemoveDeviceResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*SignUpRequest)(nil), "ocf.cloud.auth.pb.SignUpRequest") + proto.RegisterType((*SignUpResponse)(nil), "ocf.cloud.auth.pb.SignUpResponse") + proto.RegisterType((*SignOffRequest)(nil), "ocf.cloud.auth.pb.SignOffRequest") + proto.RegisterType((*SignOffResponse)(nil), "ocf.cloud.auth.pb.SignOffResponse") + proto.RegisterType((*SignInRequest)(nil), "ocf.cloud.auth.pb.SignInRequest") + proto.RegisterType((*SignInResponse)(nil), "ocf.cloud.auth.pb.SignInResponse") + proto.RegisterType((*SignOutRequest)(nil), "ocf.cloud.auth.pb.SignOutRequest") + proto.RegisterType((*SignOutResponse)(nil), "ocf.cloud.auth.pb.SignOutResponse") + proto.RegisterType((*RefreshTokenRequest)(nil), "ocf.cloud.auth.pb.RefreshTokenRequest") + proto.RegisterType((*RefreshTokenResponse)(nil), "ocf.cloud.auth.pb.RefreshTokenResponse") + proto.RegisterType((*GetUserDevicesRequest)(nil), "ocf.cloud.auth.pb.GetUserDevicesRequest") + proto.RegisterType((*UserDevice)(nil), "ocf.cloud.auth.pb.UserDevice") + proto.RegisterType((*AddDeviceRequest)(nil), "ocf.cloud.auth.pb.AddDeviceRequest") + proto.RegisterType((*AddDeviceResponse)(nil), "ocf.cloud.auth.pb.AddDeviceResponse") + proto.RegisterType((*RemoveDeviceRequest)(nil), "ocf.cloud.auth.pb.RemoveDeviceRequest") + proto.RegisterType((*RemoveDeviceResponse)(nil), "ocf.cloud.auth.pb.RemoveDeviceResponse") +} + +func init() { proto.RegisterFile("pb/auth.proto", fileDescriptor_c43ab4a3390919ea) } + +var fileDescriptor_c43ab4a3390919ea = []byte{ + // 528 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xc1, 0x8e, 0xd3, 0x30, + 0x10, 0x6d, 0x5a, 0x58, 0xe8, 0xb0, 0xdd, 0xd2, 0x2c, 0xbb, 0x44, 0x42, 0x44, 0x25, 0x08, 0xb4, + 0x42, 0x6a, 0x73, 0x40, 0x9c, 0x38, 0xb1, 0xc0, 0x42, 0xc5, 0x01, 0x54, 0xe8, 0x85, 0x4b, 0xd4, + 0xd8, 0x93, 0xd6, 0xda, 0x36, 0x0e, 0xb6, 0x53, 0x21, 0x24, 0xfe, 0x01, 0xf1, 0x2b, 0xfc, 0x04, + 0xc7, 0x3d, 0x72, 0x44, 0xed, 0x8f, 0x20, 0xc7, 0x6e, 0x69, 0x16, 0x21, 0x2a, 0xba, 0x47, 0x3f, + 0xcf, 0xcc, 0x7b, 0x6f, 0xfc, 0x64, 0x68, 0x64, 0x71, 0x38, 0xcc, 0xd5, 0xb8, 0x9b, 0x09, 0xae, + 0xb8, 0xdb, 0xe2, 0x24, 0xe9, 0x92, 0x09, 0xcf, 0x69, 0xd7, 0xa0, 0x71, 0xf0, 0xd5, 0x81, 0xc6, + 0x5b, 0x36, 0x4a, 0x07, 0x59, 0x1f, 0x3f, 0xe4, 0x28, 0x95, 0x7b, 0x0b, 0xea, 0x14, 0x67, 0x8c, + 0x60, 0xc4, 0xa8, 0xe7, 0xb4, 0x9d, 0xa3, 0x7a, 0xff, 0xaa, 0x01, 0x7a, 0xd4, 0xed, 0x80, 0xab, + 0x3b, 0xb9, 0x60, 0x9f, 0x86, 0x8a, 0xf1, 0x34, 0x22, 0x9c, 0xa2, 0x57, 0x2d, 0xaa, 0x5a, 0xa5, + 0x9b, 0xa7, 0x9c, 0xa2, 0xfb, 0x08, 0x0e, 0xcb, 0xe5, 0x99, 0xe0, 0x33, 0x46, 0x51, 0x78, 0xb5, + 0xa2, 0xe5, 0xa0, 0x74, 0xfb, 0xc6, 0x5e, 0x06, 0xdf, 0x1c, 0xd8, 0x5b, 0x8a, 0x92, 0x19, 0x4f, + 0x25, 0xba, 0x77, 0x60, 0x77, 0x48, 0x08, 0x4a, 0x19, 0x29, 0x7e, 0x8a, 0xa9, 0x15, 0x76, 0xcd, + 0x60, 0xef, 0x34, 0xe4, 0xde, 0x84, 0x2b, 0xb9, 0x44, 0xa1, 0x65, 0x1b, 0x41, 0x3b, 0xfa, 0xd8, + 0xa3, 0xee, 0x5d, 0x68, 0x08, 0x4c, 0x04, 0xca, 0xb1, 0x6d, 0x36, 0xe4, 0xbb, 0x16, 0x34, 0xdd, + 0xb7, 0x01, 0xf0, 0x63, 0xc6, 0x04, 0xca, 0x88, 0xa5, 0xde, 0xa5, 0xb6, 0x73, 0x54, 0xeb, 0xd7, + 0x2d, 0xd2, 0x4b, 0x35, 0xbf, 0x40, 0xca, 0x04, 0x12, 0x15, 0xe5, 0x82, 0x79, 0x97, 0x0d, 0xff, + 0x12, 0x1b, 0x08, 0x16, 0x9c, 0x18, 0xd1, 0xaf, 0x93, 0x64, 0xa3, 0x55, 0xfe, 0x4d, 0x6e, 0xd0, + 0x82, 0xe6, 0x6a, 0x8e, 0x71, 0x1f, 0x3c, 0x37, 0x8f, 0xd4, 0x4b, 0xb7, 0x9b, 0x1c, 0x1a, 0x85, + 0x7a, 0x8c, 0x5d, 0x6b, 0xd9, 0xb5, 0x73, 0xce, 0x75, 0x30, 0xb5, 0x96, 0x72, 0xb5, 0x15, 0xb1, + 0x7b, 0x0f, 0xf6, 0x08, 0x17, 0x02, 0x27, 0x26, 0x05, 0x8c, 0x16, 0x0b, 0xae, 0xf7, 0x1b, 0x6b, + 0xe8, 0x9a, 0x73, 0x4d, 0x67, 0x9d, 0x67, 0xb0, 0xdf, 0x5f, 0x7b, 0xa6, 0xed, 0x64, 0x6c, 0x12, + 0x84, 0xe0, 0x33, 0xdc, 0x28, 0x33, 0x6e, 0x9e, 0xc0, 0x3f, 0xe6, 0x57, 0xff, 0x19, 0xb4, 0xda, + 0xf9, 0x95, 0x9f, 0xc2, 0xc1, 0x0b, 0x54, 0x03, 0x89, 0xe2, 0x59, 0xe1, 0x47, 0x2e, 0x2d, 0xdf, + 0x87, 0xa6, 0x75, 0x25, 0xa3, 0x84, 0x4d, 0x14, 0x0a, 0xcf, 0x69, 0xd7, 0xf4, 0x12, 0x8d, 0x3b, + 0x79, 0x52, 0x80, 0xee, 0x03, 0x68, 0xad, 0x56, 0xb3, 0xaa, 0xac, 0x16, 0x95, 0xcd, 0xe5, 0x8a, + 0x6c, 0x6d, 0x70, 0x0c, 0xf0, 0x9b, 0xe9, 0x3f, 0x43, 0xf5, 0x12, 0xae, 0x3f, 0xa1, 0xd4, 0x8c, + 0xd8, 0x2e, 0x9e, 0xfb, 0xd0, 0x5a, 0x9b, 0x64, 0x03, 0xf0, 0x4a, 0x07, 0x60, 0xca, 0x67, 0x78, + 0x11, 0x0c, 0x87, 0xfa, 0x6d, 0xd7, 0x87, 0x19, 0x92, 0xe3, 0xde, 0xf7, 0xb9, 0xef, 0x9c, 0xcd, + 0x7d, 0xe7, 0xe7, 0xdc, 0x77, 0xbe, 0x2c, 0xfc, 0xca, 0xd9, 0xc2, 0xaf, 0xfc, 0x58, 0xf8, 0x95, + 0xf7, 0xe1, 0x88, 0xa9, 0x71, 0x1e, 0x77, 0x09, 0x9f, 0x86, 0x23, 0xde, 0xe1, 0x24, 0x09, 0x39, + 0x49, 0x3a, 0xc5, 0x27, 0x1a, 0x96, 0x7e, 0xaf, 0x30, 0x8b, 0x1f, 0x67, 0x71, 0xbc, 0x53, 0x7c, + 0xb5, 0x0f, 0x7f, 0x05, 0x00, 0x00, 0xff, 0xff, 0xe8, 0xe2, 0xb5, 0xaf, 0x7b, 0x05, 0x00, 0x00, +} + +func (m *SignUpRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SignUpRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignUpRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AuthorizationProvider) > 0 { + i -= len(m.AuthorizationProvider) + copy(dAtA[i:], m.AuthorizationProvider) + i = encodeVarintAuth(dAtA, i, uint64(len(m.AuthorizationProvider))) + i-- + dAtA[i] = 0x1a + } + if len(m.AuthorizationCode) > 0 { + i -= len(m.AuthorizationCode) + copy(dAtA[i:], m.AuthorizationCode) + i = encodeVarintAuth(dAtA, i, uint64(len(m.AuthorizationCode))) + i-- + dAtA[i] = 0x12 + } + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintAuth(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SignUpResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SignUpResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignUpResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.RedirectUri) > 0 { + i -= len(m.RedirectUri) + copy(dAtA[i:], m.RedirectUri) + i = encodeVarintAuth(dAtA, i, uint64(len(m.RedirectUri))) + i-- + dAtA[i] = 0x2a + } + if m.ExpiresIn != 0 { + i = encodeVarintAuth(dAtA, i, uint64(m.ExpiresIn)) + i-- + dAtA[i] = 0x20 + } + if len(m.RefreshToken) > 0 { + i -= len(m.RefreshToken) + copy(dAtA[i:], m.RefreshToken) + i = encodeVarintAuth(dAtA, i, uint64(len(m.RefreshToken))) + i-- + dAtA[i] = 0x1a + } + if len(m.UserId) > 0 { + i -= len(m.UserId) + copy(dAtA[i:], m.UserId) + i = encodeVarintAuth(dAtA, i, uint64(len(m.UserId))) + i-- + dAtA[i] = 0x12 + } + if len(m.AccessToken) > 0 { + i -= len(m.AccessToken) + copy(dAtA[i:], m.AccessToken) + i = encodeVarintAuth(dAtA, i, uint64(len(m.AccessToken))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SignOffRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SignOffRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignOffRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.UserId) > 0 { + i -= len(m.UserId) + copy(dAtA[i:], m.UserId) + i = encodeVarintAuth(dAtA, i, uint64(len(m.UserId))) + i-- + dAtA[i] = 0x12 + } + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintAuth(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SignOffResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SignOffResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignOffResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *SignInRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SignInRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignInRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.UserId) > 0 { + i -= len(m.UserId) + copy(dAtA[i:], m.UserId) + i = encodeVarintAuth(dAtA, i, uint64(len(m.UserId))) + i-- + dAtA[i] = 0x12 + } + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintAuth(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SignInResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SignInResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignInResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ExpiresIn != 0 { + i = encodeVarintAuth(dAtA, i, uint64(m.ExpiresIn)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *SignOutRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SignOutRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignOutRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.CorrelationId) > 0 { + i -= len(m.CorrelationId) + copy(dAtA[i:], m.CorrelationId) + i = encodeVarintAuth(dAtA, i, uint64(len(m.CorrelationId))) + i-- + dAtA[i] = 0x22 + } + if len(m.UserId) > 0 { + i -= len(m.UserId) + copy(dAtA[i:], m.UserId) + i = encodeVarintAuth(dAtA, i, uint64(len(m.UserId))) + i-- + dAtA[i] = 0x12 + } + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintAuth(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SignOutResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SignOutResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignOutResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *RefreshTokenRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RefreshTokenRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RefreshTokenRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.RefreshToken) > 0 { + i -= len(m.RefreshToken) + copy(dAtA[i:], m.RefreshToken) + i = encodeVarintAuth(dAtA, i, uint64(len(m.RefreshToken))) + i-- + dAtA[i] = 0x1a + } + if len(m.UserId) > 0 { + i -= len(m.UserId) + copy(dAtA[i:], m.UserId) + i = encodeVarintAuth(dAtA, i, uint64(len(m.UserId))) + i-- + dAtA[i] = 0x12 + } + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintAuth(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RefreshTokenResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RefreshTokenResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RefreshTokenResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ExpiresIn != 0 { + i = encodeVarintAuth(dAtA, i, uint64(m.ExpiresIn)) + i-- + dAtA[i] = 0x18 + } + if len(m.RefreshToken) > 0 { + i -= len(m.RefreshToken) + copy(dAtA[i:], m.RefreshToken) + i = encodeVarintAuth(dAtA, i, uint64(len(m.RefreshToken))) + i-- + dAtA[i] = 0x12 + } + if len(m.AccessToken) > 0 { + i -= len(m.AccessToken) + copy(dAtA[i:], m.AccessToken) + i = encodeVarintAuth(dAtA, i, uint64(len(m.AccessToken))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *GetUserDevicesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetUserDevicesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetUserDevicesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DeviceIdsFilter) > 0 { + for iNdEx := len(m.DeviceIdsFilter) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.DeviceIdsFilter[iNdEx]) + copy(dAtA[i:], m.DeviceIdsFilter[iNdEx]) + i = encodeVarintAuth(dAtA, i, uint64(len(m.DeviceIdsFilter[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.UserIdsFilter) > 0 { + for iNdEx := len(m.UserIdsFilter) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.UserIdsFilter[iNdEx]) + copy(dAtA[i:], m.UserIdsFilter[iNdEx]) + i = encodeVarintAuth(dAtA, i, uint64(len(m.UserIdsFilter[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *UserDevice) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UserDevice) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UserDevice) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.UserId) > 0 { + i -= len(m.UserId) + copy(dAtA[i:], m.UserId) + i = encodeVarintAuth(dAtA, i, uint64(len(m.UserId))) + i-- + dAtA[i] = 0x12 + } + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintAuth(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *AddDeviceRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AddDeviceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AddDeviceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.UserId) > 0 { + i -= len(m.UserId) + copy(dAtA[i:], m.UserId) + i = encodeVarintAuth(dAtA, i, uint64(len(m.UserId))) + i-- + dAtA[i] = 0x12 + } + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintAuth(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *AddDeviceResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AddDeviceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AddDeviceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *RemoveDeviceRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RemoveDeviceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RemoveDeviceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.UserId) > 0 { + i -= len(m.UserId) + copy(dAtA[i:], m.UserId) + i = encodeVarintAuth(dAtA, i, uint64(len(m.UserId))) + i-- + dAtA[i] = 0x12 + } + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintAuth(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RemoveDeviceResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RemoveDeviceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RemoveDeviceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintAuth(dAtA []byte, offset int, v uint64) int { + offset -= sovAuth(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *SignUpRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + l = len(m.AuthorizationCode) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + l = len(m.AuthorizationProvider) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + return n +} + +func (m *SignUpResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.AccessToken) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + l = len(m.UserId) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + l = len(m.RefreshToken) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + if m.ExpiresIn != 0 { + n += 1 + sovAuth(uint64(m.ExpiresIn)) + } + l = len(m.RedirectUri) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + return n +} + +func (m *SignOffRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + l = len(m.UserId) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + return n +} + +func (m *SignOffResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *SignInRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + l = len(m.UserId) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + return n +} + +func (m *SignInResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ExpiresIn != 0 { + n += 1 + sovAuth(uint64(m.ExpiresIn)) + } + return n +} + +func (m *SignOutRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + l = len(m.UserId) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + l = len(m.CorrelationId) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + return n +} + +func (m *SignOutResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *RefreshTokenRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + l = len(m.UserId) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + l = len(m.RefreshToken) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + return n +} + +func (m *RefreshTokenResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.AccessToken) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + l = len(m.RefreshToken) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + if m.ExpiresIn != 0 { + n += 1 + sovAuth(uint64(m.ExpiresIn)) + } + return n +} + +func (m *GetUserDevicesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.UserIdsFilter) > 0 { + for _, s := range m.UserIdsFilter { + l = len(s) + n += 1 + l + sovAuth(uint64(l)) + } + } + if len(m.DeviceIdsFilter) > 0 { + for _, s := range m.DeviceIdsFilter { + l = len(s) + n += 1 + l + sovAuth(uint64(l)) + } + } + return n +} + +func (m *UserDevice) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + l = len(m.UserId) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + return n +} + +func (m *AddDeviceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + l = len(m.UserId) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + return n +} + +func (m *AddDeviceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *RemoveDeviceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + l = len(m.UserId) + if l > 0 { + n += 1 + l + sovAuth(uint64(l)) + } + return n +} + +func (m *RemoveDeviceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovAuth(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozAuth(x uint64) (n int) { + return sovAuth(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *SignUpRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignUpRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignUpRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthorizationCode", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AuthorizationCode = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthorizationProvider", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AuthorizationProvider = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignUpResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignUpResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignUpResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AccessToken", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AccessToken = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UserId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RefreshToken", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RefreshToken = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExpiresIn", wireType) + } + m.ExpiresIn = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ExpiresIn |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RedirectUri", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RedirectUri = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignOffRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignOffRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignOffRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UserId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignOffResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignOffResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignOffResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignInRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignInRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignInRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UserId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignInResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignInResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignInResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExpiresIn", wireType) + } + m.ExpiresIn = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ExpiresIn |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignOutRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignOutRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignOutRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UserId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CorrelationId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CorrelationId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignOutResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignOutResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignOutResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RefreshTokenRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RefreshTokenRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RefreshTokenRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UserId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RefreshToken", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RefreshToken = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RefreshTokenResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RefreshTokenResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RefreshTokenResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AccessToken", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AccessToken = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RefreshToken", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RefreshToken = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ExpiresIn", wireType) + } + m.ExpiresIn = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ExpiresIn |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetUserDevicesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetUserDevicesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetUserDevicesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserIdsFilter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UserIdsFilter = append(m.UserIdsFilter, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceIdsFilter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceIdsFilter = append(m.DeviceIdsFilter, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UserDevice) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UserDevice: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UserDevice: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UserId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AddDeviceRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AddDeviceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AddDeviceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UserId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AddDeviceResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AddDeviceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AddDeviceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RemoveDeviceRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RemoveDeviceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RemoveDeviceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuth + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuth + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UserId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RemoveDeviceResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuth + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RemoveDeviceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RemoveDeviceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipAuth(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAuth + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAuth(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuth + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuth + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuth + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthAuth + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAuth + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthAuth + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthAuth = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAuth = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAuth = fmt.Errorf("proto: unexpected end of group") +) diff --git a/authorization/pb/auth.proto b/authorization/pb/auth.proto new file mode 100644 index 000000000..3886e320c --- /dev/null +++ b/authorization/pb/auth.proto @@ -0,0 +1,95 @@ +syntax = "proto3"; + +package ocf.cloud.auth.pb; + +option go_package = "github.com/go-ocf/cloud/authorization/pb;pb"; + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.account.raml#L27 +message SignUpRequest { + string device_id = 1; + string authorization_code = 2; + string authorization_provider = 3; +} + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.account.raml#L40 +message SignUpResponse { + string access_token = 1; + string user_id = 2; + string refresh_token = 3; + int64 expires_in = 4; + string redirect_uri = 5; +} + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.account.raml#L55 +message SignOffRequest { + string device_id = 1; + string user_id = 2; +} + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.account.raml#L60 +message SignOffResponse { +} + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.session.raml#L27 +message SignInRequest { + string device_id = 1; + string user_id = 2; +} + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.session.raml#L39 +message SignInResponse { + int64 expires_in = 1; +} + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.session.raml #Specification CR needed +message SignOutRequest { + string device_id = 1; + string user_id = 2; + string correlation_id = 4; +} + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.session.raml #Specification CR needed +message SignOutResponse { +} + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.tokenrefresh.raml#L27 +message RefreshTokenRequest { + string device_id = 1; + string user_id = 2; + string refresh_token = 3; +} + +// https://github.com/openconnectivityfoundation/security/blob/master/oic.r.tokenrefresh.raml#L40 +message RefreshTokenResponse { + string access_token = 1; + string refresh_token = 2; + int64 expires_in = 3; +} + +message GetUserDevicesRequest { + repeated string user_ids_filter = 1; + repeated string device_ids_filter = 2; +} + +message UserDevice { + string device_id = 1; + string user_id = 2; +} + + +message AddDeviceRequest { + string device_id = 1; + string user_id = 2; +} + +message AddDeviceResponse { +} + + +message RemoveDeviceRequest { + string device_id = 1; + string user_id = 2; +} + +message RemoveDeviceResponse { +} diff --git a/authorization/pb/service.pb.go b/authorization/pb/service.pb.go new file mode 100644 index 000000000..8626059c4 --- /dev/null +++ b/authorization/pb/service.pb.go @@ -0,0 +1,411 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: pb/service.proto + +package pb + +import ( + context "context" + fmt "fmt" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +func init() { proto.RegisterFile("pb/service.proto", fileDescriptor_6ff5ab49d8a5fcc4) } + +var fileDescriptor_6ff5ab49d8a5fcc4 = []byte{ + // 315 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x93, 0x4d, 0x4b, 0x3b, 0x31, + 0x10, 0xc6, 0xfb, 0x3f, 0xfc, 0x2b, 0x06, 0x11, 0x0d, 0x9e, 0x0a, 0x82, 0x56, 0x51, 0x2f, 0x4d, + 0x7c, 0x39, 0x7a, 0xaa, 0x08, 0xd2, 0x53, 0xa1, 0xb5, 0x20, 0x82, 0x87, 0x4d, 0x3a, 0xd9, 0x5d, + 0xb4, 0x99, 0xb8, 0x49, 0x7a, 0xf0, 0xcb, 0xfa, 0x55, 0x64, 0xbb, 0xdd, 0xed, 0x96, 0xee, 0xcb, + 0x2d, 0xcc, 0xfc, 0x9e, 0x67, 0x1e, 0x26, 0x09, 0x39, 0x32, 0x82, 0x5b, 0x48, 0x96, 0xb1, 0x04, + 0x66, 0x12, 0x74, 0x48, 0x8f, 0x51, 0x2a, 0x26, 0xbf, 0xd0, 0xcf, 0x59, 0xe0, 0x5d, 0xc4, 0x8c, + 0xe8, 0x91, 0xec, 0x90, 0xb6, 0xef, 0x7f, 0xff, 0x93, 0x93, 0xa1, 0x77, 0x11, 0x26, 0xf1, 0x4f, + 0xe0, 0x62, 0xd4, 0xd3, 0x4c, 0x4d, 0xc7, 0xa4, 0x3b, 0x8d, 0x43, 0x3d, 0x33, 0xf4, 0x8c, 0xed, + 0x58, 0xb0, 0xac, 0x35, 0x81, 0x6f, 0x0f, 0xd6, 0xf5, 0xce, 0x1b, 0x08, 0x6b, 0x50, 0x5b, 0xe8, + 0x77, 0xe8, 0x84, 0xec, 0xa5, 0xb5, 0xb1, 0x52, 0xb4, 0x8e, 0x1f, 0x2b, 0x95, 0x5b, 0xf6, 0x9b, + 0x90, 0xc2, 0x73, 0x1d, 0x72, 0xa4, 0x6b, 0x43, 0x8e, 0x74, 0x5b, 0xc8, 0x94, 0xd8, 0x09, 0xe9, + 0x5d, 0x7d, 0x48, 0xef, 0x5a, 0x43, 0xa6, 0x48, 0xe1, 0x19, 0x90, 0x83, 0x09, 0xa8, 0x04, 0x6c, + 0xf4, 0x8a, 0x9f, 0xa0, 0xe9, 0x55, 0x85, 0xaa, 0x0c, 0xe4, 0xee, 0xd7, 0xad, 0x5c, 0x31, 0xe2, + 0x83, 0x1c, 0xbe, 0x80, 0x9b, 0x59, 0x48, 0x9e, 0x21, 0xbd, 0x3d, 0x4b, 0x6f, 0x2a, 0xc4, 0xdb, + 0x48, 0x3e, 0xe6, 0xb4, 0x82, 0xdc, 0x60, 0xfd, 0xce, 0xed, 0x3f, 0xfa, 0x46, 0xf6, 0x87, 0xf3, + 0x79, 0x56, 0xa0, 0x17, 0x15, 0x7c, 0xd1, 0xcd, 0x4d, 0x2f, 0x9b, 0xa1, 0xed, 0xdd, 0x2c, 0x70, + 0x09, 0x6b, 0xf3, 0xea, 0xdd, 0x6c, 0x80, 0xe6, 0xdd, 0x94, 0xb9, 0x7c, 0xc4, 0xd3, 0xdd, 0x3b, + 0x0f, 0x63, 0x17, 0x79, 0xc1, 0x24, 0x2e, 0x78, 0x88, 0x03, 0x94, 0x8a, 0xa3, 0x54, 0x83, 0x95, + 0x9a, 0x07, 0xe5, 0xc7, 0xcf, 0x8d, 0x78, 0x34, 0x42, 0x74, 0x57, 0x7f, 0xe3, 0xe1, 0x2f, 0x00, + 0x00, 0xff, 0xff, 0x9f, 0x81, 0x97, 0xbe, 0x4e, 0x03, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// AuthorizationServiceClient is the client API for AuthorizationService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type AuthorizationServiceClient interface { + SignUp(ctx context.Context, in *SignUpRequest, opts ...grpc.CallOption) (*SignUpResponse, error) + SignOff(ctx context.Context, in *SignOffRequest, opts ...grpc.CallOption) (*SignOffResponse, error) + SignIn(ctx context.Context, in *SignInRequest, opts ...grpc.CallOption) (*SignInResponse, error) + SignOut(ctx context.Context, in *SignOutRequest, opts ...grpc.CallOption) (*SignOutResponse, error) + RefreshToken(ctx context.Context, in *RefreshTokenRequest, opts ...grpc.CallOption) (*RefreshTokenResponse, error) + GetUserDevices(ctx context.Context, in *GetUserDevicesRequest, opts ...grpc.CallOption) (AuthorizationService_GetUserDevicesClient, error) + AddDevice(ctx context.Context, in *AddDeviceRequest, opts ...grpc.CallOption) (*AddDeviceResponse, error) + RemoveDevice(ctx context.Context, in *RemoveDeviceRequest, opts ...grpc.CallOption) (*RemoveDeviceResponse, error) +} + +type authorizationServiceClient struct { + cc *grpc.ClientConn +} + +func NewAuthorizationServiceClient(cc *grpc.ClientConn) AuthorizationServiceClient { + return &authorizationServiceClient{cc} +} + +func (c *authorizationServiceClient) SignUp(ctx context.Context, in *SignUpRequest, opts ...grpc.CallOption) (*SignUpResponse, error) { + out := new(SignUpResponse) + err := c.cc.Invoke(ctx, "/ocf.cloud.auth.pb.AuthorizationService/SignUp", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *authorizationServiceClient) SignOff(ctx context.Context, in *SignOffRequest, opts ...grpc.CallOption) (*SignOffResponse, error) { + out := new(SignOffResponse) + err := c.cc.Invoke(ctx, "/ocf.cloud.auth.pb.AuthorizationService/SignOff", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *authorizationServiceClient) SignIn(ctx context.Context, in *SignInRequest, opts ...grpc.CallOption) (*SignInResponse, error) { + out := new(SignInResponse) + err := c.cc.Invoke(ctx, "/ocf.cloud.auth.pb.AuthorizationService/SignIn", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *authorizationServiceClient) SignOut(ctx context.Context, in *SignOutRequest, opts ...grpc.CallOption) (*SignOutResponse, error) { + out := new(SignOutResponse) + err := c.cc.Invoke(ctx, "/ocf.cloud.auth.pb.AuthorizationService/SignOut", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *authorizationServiceClient) RefreshToken(ctx context.Context, in *RefreshTokenRequest, opts ...grpc.CallOption) (*RefreshTokenResponse, error) { + out := new(RefreshTokenResponse) + err := c.cc.Invoke(ctx, "/ocf.cloud.auth.pb.AuthorizationService/RefreshToken", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *authorizationServiceClient) GetUserDevices(ctx context.Context, in *GetUserDevicesRequest, opts ...grpc.CallOption) (AuthorizationService_GetUserDevicesClient, error) { + stream, err := c.cc.NewStream(ctx, &_AuthorizationService_serviceDesc.Streams[0], "/ocf.cloud.auth.pb.AuthorizationService/GetUserDevices", opts...) + if err != nil { + return nil, err + } + x := &authorizationServiceGetUserDevicesClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type AuthorizationService_GetUserDevicesClient interface { + Recv() (*UserDevice, error) + grpc.ClientStream +} + +type authorizationServiceGetUserDevicesClient struct { + grpc.ClientStream +} + +func (x *authorizationServiceGetUserDevicesClient) Recv() (*UserDevice, error) { + m := new(UserDevice) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *authorizationServiceClient) AddDevice(ctx context.Context, in *AddDeviceRequest, opts ...grpc.CallOption) (*AddDeviceResponse, error) { + out := new(AddDeviceResponse) + err := c.cc.Invoke(ctx, "/ocf.cloud.auth.pb.AuthorizationService/AddDevice", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *authorizationServiceClient) RemoveDevice(ctx context.Context, in *RemoveDeviceRequest, opts ...grpc.CallOption) (*RemoveDeviceResponse, error) { + out := new(RemoveDeviceResponse) + err := c.cc.Invoke(ctx, "/ocf.cloud.auth.pb.AuthorizationService/RemoveDevice", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// AuthorizationServiceServer is the server API for AuthorizationService service. +type AuthorizationServiceServer interface { + SignUp(context.Context, *SignUpRequest) (*SignUpResponse, error) + SignOff(context.Context, *SignOffRequest) (*SignOffResponse, error) + SignIn(context.Context, *SignInRequest) (*SignInResponse, error) + SignOut(context.Context, *SignOutRequest) (*SignOutResponse, error) + RefreshToken(context.Context, *RefreshTokenRequest) (*RefreshTokenResponse, error) + GetUserDevices(*GetUserDevicesRequest, AuthorizationService_GetUserDevicesServer) error + AddDevice(context.Context, *AddDeviceRequest) (*AddDeviceResponse, error) + RemoveDevice(context.Context, *RemoveDeviceRequest) (*RemoveDeviceResponse, error) +} + +// UnimplementedAuthorizationServiceServer can be embedded to have forward compatible implementations. +type UnimplementedAuthorizationServiceServer struct { +} + +func (*UnimplementedAuthorizationServiceServer) SignUp(ctx context.Context, req *SignUpRequest) (*SignUpResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SignUp not implemented") +} +func (*UnimplementedAuthorizationServiceServer) SignOff(ctx context.Context, req *SignOffRequest) (*SignOffResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SignOff not implemented") +} +func (*UnimplementedAuthorizationServiceServer) SignIn(ctx context.Context, req *SignInRequest) (*SignInResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SignIn not implemented") +} +func (*UnimplementedAuthorizationServiceServer) SignOut(ctx context.Context, req *SignOutRequest) (*SignOutResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SignOut not implemented") +} +func (*UnimplementedAuthorizationServiceServer) RefreshToken(ctx context.Context, req *RefreshTokenRequest) (*RefreshTokenResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RefreshToken not implemented") +} +func (*UnimplementedAuthorizationServiceServer) GetUserDevices(req *GetUserDevicesRequest, srv AuthorizationService_GetUserDevicesServer) error { + return status.Errorf(codes.Unimplemented, "method GetUserDevices not implemented") +} +func (*UnimplementedAuthorizationServiceServer) AddDevice(ctx context.Context, req *AddDeviceRequest) (*AddDeviceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddDevice not implemented") +} +func (*UnimplementedAuthorizationServiceServer) RemoveDevice(ctx context.Context, req *RemoveDeviceRequest) (*RemoveDeviceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RemoveDevice not implemented") +} + +func RegisterAuthorizationServiceServer(s *grpc.Server, srv AuthorizationServiceServer) { + s.RegisterService(&_AuthorizationService_serviceDesc, srv) +} + +func _AuthorizationService_SignUp_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SignUpRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthorizationServiceServer).SignUp(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocf.cloud.auth.pb.AuthorizationService/SignUp", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthorizationServiceServer).SignUp(ctx, req.(*SignUpRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AuthorizationService_SignOff_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SignOffRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthorizationServiceServer).SignOff(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocf.cloud.auth.pb.AuthorizationService/SignOff", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthorizationServiceServer).SignOff(ctx, req.(*SignOffRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AuthorizationService_SignIn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SignInRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthorizationServiceServer).SignIn(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocf.cloud.auth.pb.AuthorizationService/SignIn", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthorizationServiceServer).SignIn(ctx, req.(*SignInRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AuthorizationService_SignOut_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SignOutRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthorizationServiceServer).SignOut(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocf.cloud.auth.pb.AuthorizationService/SignOut", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthorizationServiceServer).SignOut(ctx, req.(*SignOutRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AuthorizationService_RefreshToken_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RefreshTokenRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthorizationServiceServer).RefreshToken(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocf.cloud.auth.pb.AuthorizationService/RefreshToken", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthorizationServiceServer).RefreshToken(ctx, req.(*RefreshTokenRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AuthorizationService_GetUserDevices_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(GetUserDevicesRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(AuthorizationServiceServer).GetUserDevices(m, &authorizationServiceGetUserDevicesServer{stream}) +} + +type AuthorizationService_GetUserDevicesServer interface { + Send(*UserDevice) error + grpc.ServerStream +} + +type authorizationServiceGetUserDevicesServer struct { + grpc.ServerStream +} + +func (x *authorizationServiceGetUserDevicesServer) Send(m *UserDevice) error { + return x.ServerStream.SendMsg(m) +} + +func _AuthorizationService_AddDevice_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddDeviceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthorizationServiceServer).AddDevice(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocf.cloud.auth.pb.AuthorizationService/AddDevice", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthorizationServiceServer).AddDevice(ctx, req.(*AddDeviceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AuthorizationService_RemoveDevice_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RemoveDeviceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthorizationServiceServer).RemoveDevice(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocf.cloud.auth.pb.AuthorizationService/RemoveDevice", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthorizationServiceServer).RemoveDevice(ctx, req.(*RemoveDeviceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _AuthorizationService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ocf.cloud.auth.pb.AuthorizationService", + HandlerType: (*AuthorizationServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SignUp", + Handler: _AuthorizationService_SignUp_Handler, + }, + { + MethodName: "SignOff", + Handler: _AuthorizationService_SignOff_Handler, + }, + { + MethodName: "SignIn", + Handler: _AuthorizationService_SignIn_Handler, + }, + { + MethodName: "SignOut", + Handler: _AuthorizationService_SignOut_Handler, + }, + { + MethodName: "RefreshToken", + Handler: _AuthorizationService_RefreshToken_Handler, + }, + { + MethodName: "AddDevice", + Handler: _AuthorizationService_AddDevice_Handler, + }, + { + MethodName: "RemoveDevice", + Handler: _AuthorizationService_RemoveDevice_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "GetUserDevices", + Handler: _AuthorizationService_GetUserDevices_Handler, + ServerStreams: true, + }, + }, + Metadata: "pb/service.proto", +} diff --git a/authorization/pb/service.proto b/authorization/pb/service.proto new file mode 100644 index 000000000..e8c2a22ef --- /dev/null +++ b/authorization/pb/service.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package ocf.cloud.auth.pb; + +import "auth.proto"; + +option go_package = "github.com/go-ocf/cloud/authorization/pb;pb"; + +service AuthorizationService { + rpc SignUp (SignUpRequest) returns (SignUpResponse) {} + rpc SignOff (SignOffRequest) returns (SignOffResponse) {} + + rpc SignIn (SignInRequest) returns (SignInResponse) {} + rpc SignOut (SignOutRequest) returns (SignOutResponse) {} + + rpc RefreshToken (RefreshTokenRequest) returns (RefreshTokenResponse) {} + + rpc GetUserDevices (GetUserDevicesRequest) returns (stream UserDevice) {} + + rpc AddDevice(AddDeviceRequest) returns (AddDeviceResponse) {} + rpc RemoveDevice(RemoveDeviceRequest) returns (RemoveDeviceResponse) {} +} \ No newline at end of file diff --git a/authorization/persistence/mongodb/persist.go b/authorization/persistence/mongodb/persist.go new file mode 100644 index 000000000..4e0596250 --- /dev/null +++ b/authorization/persistence/mongodb/persist.go @@ -0,0 +1,233 @@ +package mongodb + +import ( + "context" + "fmt" + "github.com/go-ocf/cloud/authorization/persistence" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "time" +) + +const ( + deviceIDKey = "_id" + userIDKey = "userid" + accessTokenKey = "accesstoken" + refreshTokenKey = "refreshtoken" + expiryKey = "expiry" +) + +// PersistenceTx prevents data race for a sequence of read and write operations. +type PersistenceTx struct { + tx mongo.Session + dbname string + err error + ctx context.Context +} + +// NewTransaction creates a new transaction. +// A transaction must always be closed: +// tx := s.persistence.NewTransaction() +// defer tx.Close() +func (p *Store) NewTransaction(ctx context.Context) persistence.PersistenceTx { + tx, err := p.client.StartSession() + if err == nil { + err = tx.StartTransaction() + if err != nil { + tx.EndSession(ctx) + } + } + return &PersistenceTx{tx: tx, dbname: p.DBName(), err: err, ctx: ctx} +} + +// Retrieve device's authorization details. +func (p *PersistenceTx) Retrieve(deviceID, userID string) (_ *persistence.AuthorizedDevice, ok bool, err error) { + if p.err != nil { + err = p.err + return + } + + col := p.tx.Client().Database(p.dbname).Collection(userDevicesCName) + iter, err := col.Find(p.ctx, bson.M{deviceIDKey: deviceID, userIDKey: userID}, &options.FindOptions{ + Hint: userDeviceQueryIndex, + }) + + if err == mongo.ErrNilDocument { + err = nil + return + } + if err != nil { + return + } + + it := iterator{ + iter: iter, + ctx: p.ctx, + } + defer it.Close() + var d persistence.AuthorizedDevice + ok = it.Next(&d) + if it.Err() != nil { + err = it.Err() + return + } + + return &d, ok, nil +} + +// RetrieveByDevice device's authorization details. +func (p *PersistenceTx) RetrieveByDevice(deviceID string) (_ *persistence.AuthorizedDevice, ok bool, err error) { + if p.err != nil { + err = p.err + return + } + + col := p.tx.Client().Database(p.dbname).Collection(userDevicesCName) + iter, err := col.Find(p.ctx, bson.M{deviceIDKey: deviceID}) + + if err == mongo.ErrNilDocument { + err = nil + return + } + if err != nil { + return + } + + it := iterator{ + iter: iter, + ctx: p.ctx, + } + defer it.Close() + var d persistence.AuthorizedDevice + ok = it.Next(&d) + if it.Err() != nil { + err = it.Err() + return + } + + return &d, ok, nil +} + +// RetrieveAll retrieves all user's authorized devices. +func (p *PersistenceTx) RetrieveAll(userID string) persistence.Iterator { + if p.err != nil { + return &iterator{err: p.err} + } + + col := p.tx.Client().Database(p.dbname).Collection(userDevicesCName) + iter, err := col.Find(p.ctx, bson.M{userIDKey: userID}, &options.FindOptions{ + Hint: userDevicesQueryIndex, + }) + + if err == mongo.ErrNilDocument { + return &iterator{} + } + if err != nil { + return &iterator{err: fmt.Errorf("cannot load all devices subscription: %v", err)} + } + + return &iterator{ + iter: iter, + ctx: p.ctx, + } +} + +type iterator struct { + err error + iter *mongo.Cursor + ctx context.Context +} + +func (i *iterator) Next(s *persistence.AuthorizedDevice) bool { + if i.err != nil { + return false + } + + var sub bson.M + + if !i.iter.Next(i.ctx) { + return false + } + + err := i.iter.Decode(&sub) + if err != nil { + return false + } + s.DeviceID = sub[deviceIDKey].(string) + s.UserID = sub[userIDKey].(string) + s.AccessToken = sub[accessTokenKey].(string) + s.Expiry = time.Unix(sub[expiryKey].(int64), 0) + s.RefreshToken = sub[refreshTokenKey].(string) + + return true +} + +func (i *iterator) Err() error { + return i.iter.Err() +} + +func (i *iterator) Close() { + i.err = i.iter.Close(i.ctx) +} + +func makeRecord(d *persistence.AuthorizedDevice) bson.M { + return bson.M{ + deviceIDKey: d.DeviceID, + userIDKey: d.UserID, + accessTokenKey: d.AccessToken, + refreshTokenKey: d.RefreshToken, + expiryKey: d.Expiry.Unix(), + } +} + +// Persist device's authorization details. +func (p *PersistenceTx) Persist(d *persistence.AuthorizedDevice) error { + if p.err != nil { + return p.err + } + + record := makeRecord(d) + col := p.tx.Client().Database(p.dbname).Collection(userDevicesCName) + upsert := true + if _, err := col.UpdateOne(p.ctx, bson.M{deviceIDKey: d.DeviceID}, bson.M{"$set": record}, &options.UpdateOptions{ + Upsert: &upsert, + }); err != nil { + return err + } + + if err := p.tx.CommitTransaction(p.ctx); err != nil { + return fmt.Errorf("cannot commit transaction: %w", err) + } + return nil +} + +// Delete removes the device's authorization record. +func (p *PersistenceTx) Delete(deviceID, userID string) error { + if p.err != nil { + return p.err + } + col := p.tx.Client().Database(p.dbname).Collection(userDevicesCName) + res, err := col.DeleteOne(p.ctx, bson.M{ + deviceIDKey: deviceID, + userIDKey: userID, + }) + if err != nil { + return err + } + if res.DeletedCount == 0 { + return fmt.Errorf("not found") + } + if err := p.tx.CommitTransaction(p.ctx); err != nil { + return fmt.Errorf("cannot commit transaction: %w", err) + } + + return nil +} + +// Close must always be called (use defer immediately after NewTransaction). +func (p *PersistenceTx) Close() { + if p.tx != nil { + p.tx.EndSession(p.ctx) + } +} diff --git a/authorization/persistence/mongodb/store.go b/authorization/persistence/mongodb/store.go new file mode 100644 index 000000000..a822fa351 --- /dev/null +++ b/authorization/persistence/mongodb/store.go @@ -0,0 +1,131 @@ +package mongodb + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "strings" + "time" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/mongo/readpref" +) + +const userDevicesCName = "userdevices" + +var userDeviceQueryIndex = bson.D{ + {userIDKey, 1}, + {deviceIDKey, 1}, +} + +var userDevicesQueryIndex = bson.D{ + {userIDKey, 1}, +} + +// Store implements an Store for MongoDB. +type Store struct { + client *mongo.Client + dbPrefix string +} + +type Config struct { + URI string `envconfig:"URI" env:"URI" default:"mongodb://localhost:27017"` + Database string `envconfig:"DATABASE" env:"DATABASE" default:"authorization"` + tlsCfg *tls.Config +} + +// Option provides the means to use function call chaining +type Option func(Config) Config + +// WithTLS configures connection to use TLS +func WithTLS(cfg *tls.Config) Option { + return func(c Config) Config { + c.tlsCfg = cfg + return c + } +} + +// NewStore creates a new Store. +func NewStore(ctx context.Context, cfg Config, opts ...Option) (*Store, error) { + for _, o := range opts { + cfg = o(cfg) + } + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + + client, err := mongo.Connect(ctx, options.Client().ApplyURI(cfg.URI).SetTLSConfig(cfg.tlsCfg)) + if err != nil { + return nil, fmt.Errorf("could not dial database: %v", err) + } + ctx, _ = context.WithTimeout(ctx, 2*time.Second) + err = client.Ping(ctx, readpref.Primary()) + if err != nil { + return nil, fmt.Errorf("could not dial database: %v", err) + } + + return NewStoreWithSession(ctx, client, cfg.Database) +} + +// NewStoreWithSession creates a new Store with a session. +func NewStoreWithSession(ctx context.Context, client *mongo.Client, dbPrefix string) (*Store, error) { + if client == nil { + return nil, errors.New("no database session") + } + + if dbPrefix == "" { + dbPrefix = "default" + } + + s := &Store{ + client: client, + dbPrefix: dbPrefix, + } + + subCol := s.client.Database(s.DBName()).Collection(userDevicesCName) + err := ensureIndex(ctx, subCol, userDeviceQueryIndex, userDevicesQueryIndex) + if err != nil { + client.Disconnect(ctx) + return nil, fmt.Errorf("cannot ensure index for device subscription: %v", err) + } + + return s, nil +} + +func ensureIndex(ctx context.Context, col *mongo.Collection, indexes ...bson.D) error { + for _, keys := range indexes { + opts := &options.IndexOptions{} + opts.SetBackground(false) + index := mongo.IndexModel{ + Keys: keys, + Options: opts, + } + _, err := col.Indexes().CreateOne(ctx, index) + if err != nil { + if strings.HasPrefix(err.Error(), "(IndexKeySpecsConflict)") { + //index already exist, just skip error and continue + continue + } + return fmt.Errorf("cannot ensure indexes for eventstore: %v", err) + } + } + return nil +} + +// DBName returns db name +func (s *Store) DBName() string { + ns := "db" + return s.dbPrefix + "_" + ns +} + +// Clear clears the event storage. +func (s *Store) Clear(ctx context.Context) error { + return s.client.Database(s.DBName()).Drop(ctx) +} + +// Close closes the database session. +func (s *Store) Close(ctx context.Context) error { + return s.client.Disconnect(ctx) +} diff --git a/authorization/persistence/persist.go b/authorization/persistence/persist.go new file mode 100644 index 000000000..0df20f7d1 --- /dev/null +++ b/authorization/persistence/persist.go @@ -0,0 +1,29 @@ +package persistence + +import ( + "time" +) + +// AuthorizedDevice comprises device's authorization details. +type AuthorizedDevice struct { + DeviceID string `db:"device_id"` + UserID string `db:"user_id"` + AccessToken string `db:"access_token"` + RefreshToken string `db:"refresh_token"` + Expiry time.Time `db:"expiry"` +} + +type Iterator interface { + Err() error + Next(v *AuthorizedDevice) bool + Close() +} + +type PersistenceTx interface { + Retrieve(deviceID, userID string) (_ *AuthorizedDevice, ok bool, err error) + RetrieveByDevice(deviceID string) (_ *AuthorizedDevice, ok bool, err error) + RetrieveAll(userID string) Iterator + Persist(d *AuthorizedDevice) error + Delete(deviceID, userID string) error + Close() +} \ No newline at end of file diff --git a/authorization/provider/auth0.go b/authorization/provider/auth0.go new file mode 100644 index 000000000..a47720447 --- /dev/null +++ b/authorization/provider/auth0.go @@ -0,0 +1,127 @@ +package provider + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/url" + + "golang.org/x/oauth2" +) + +// NewAuth0Provider creates OAuth client +func NewAuth0Provider(config Config) *Auth0Provider { + oauth2 := config.OAuth2.ToOAuth2() + return &Auth0Provider{ + Config: config, + OAuth2: &oauth2, + NewHTTPClient: func() *http.Client { return http.DefaultClient }, + } +} + +// Auth0Provider configuration with new http client +type Auth0Provider struct { + Config Config + OAuth2 *oauth2.Config + NewHTTPClient func() *http.Client +} + +// GetProviderName returns unique name of the provider +func (p *Auth0Provider) GetProviderName() string { + return p.Config.Provider +} + +// AuthCodeURL returns URL for redirecting to the authentication web page +func (p *Auth0Provider) AuthCodeURL(csrfToken string) string { + return p.Config.OAuth2.AuthCodeURL(csrfToken) +} + +// LogoutURL to logout the user +func (p *Auth0Provider) LogoutURL(returnTo string) string { + URL, err := url.Parse(p.OAuth2.Endpoint.AuthURL + "/v2/logout") // todo parse root path + if err != nil { + panic("invalid OAuthEndpoint configured") + } + + parameters := url.Values{} + parameters.Add("returnTo", returnTo) + parameters.Add("client_id", p.OAuth2.ClientID) + URL.RawQuery = parameters.Encode() + + return URL.String() +} + +// Exchange Auth Code for Access Token via OAuth +func (p *Auth0Provider) Exchange(ctx context.Context, authorizationProvider, authorizationCode string) (*Token, error) { + if p.GetProviderName() != authorizationProvider { + return nil, fmt.Errorf("unsupported authorization provider") + } + ctx = context.WithValue(ctx, oauth2.HTTPClient, p.NewHTTPClient()) + + token, err := p.OAuth2.Exchange(ctx, authorizationCode) + if err != nil { + return nil, err + } + + oauthClient := p.OAuth2.Client(ctx, token) + resp, err := oauthClient.Get(p.OAuth2.Endpoint.AuthURL + "/userinfo") // todo parse root path + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + var profile map[string]interface{} + if err = json.NewDecoder(resp.Body).Decode(&profile); err != nil { + return nil, err + } + + userID, ok := profile["sub"].(string) + if !ok { + return nil, fmt.Errorf("cannot determine UserID") + } + + t := Token{ + AccessToken: token.AccessToken, + RefreshToken: token.RefreshToken, + Expiry: token.Expiry, + UserID: userID, + } + return &t, nil +} + +// Refresh gets new Access Token via OAuth. +func (p *Auth0Provider) Refresh(ctx context.Context, refreshToken string) (*Token, error) { + restoredToken := &oauth2.Token{ + RefreshToken: refreshToken, + } + tokenSource := p.OAuth2.TokenSource(ctx, restoredToken) + token, err := tokenSource.Token() + if err != nil { + return nil, err + } + + oauthClient := p.OAuth2.Client(ctx, token) + resp, err := oauthClient.Get(p.OAuth2.Endpoint.AuthURL + "/userinfo") // todo parse root path + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + var profile map[string]interface{} + if err = json.NewDecoder(resp.Body).Decode(&profile); err != nil { + return nil, err + } + + userID, _ := profile["sub"].(string) + // if it is not determined, request userId will used. + + return &Token{ + AccessToken: token.AccessToken, + RefreshToken: token.RefreshToken, + Expiry: token.Expiry, + UserID: userID, + }, nil +} diff --git a/authorization/provider/generic.go b/authorization/provider/generic.go new file mode 100644 index 000000000..f6a4f9fde --- /dev/null +++ b/authorization/provider/generic.go @@ -0,0 +1,127 @@ +package provider + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/url" + + "golang.org/x/oauth2" +) + +// NewGenericProvider creates OAuth client +func NewGenericProvider(config Config) *GenericProvider { + oauth2 := config.OAuth2.ToOAuth2() + return &GenericProvider{ + Config: config, + OAuth2: &oauth2, + NewHTTPClient: func() *http.Client { return http.DefaultClient }, + } +} + +// GenericProvider configuration with new http client +type GenericProvider struct { + Config Config + OAuth2 *oauth2.Config + NewHTTPClient func() *http.Client +} + +// GetProviderName returns unique name of the provider +func (p *GenericProvider) GetProviderName() string { + return p.Config.Provider +} + +// AuthCodeURL returns URL for redirecting to the authentication web page +func (p *GenericProvider) AuthCodeURL(csrfToken string) string { + return p.Config.OAuth2.AuthCodeURL(csrfToken) +} + +// LogoutURL to logout the user +func (p *GenericProvider) LogoutURL(returnTo string) string { + URL, err := url.Parse(p.OAuth2.Endpoint.AuthURL + "/logout") // todo parse root path + if err != nil { + panic("invalid OAuthEndpoint configured") + } + + parameters := url.Values{} + parameters.Add("returnTo", returnTo) + parameters.Add("client_id", p.OAuth2.ClientID) + URL.RawQuery = parameters.Encode() + + return URL.String() +} + +// Exchange Auth Code for Access Token via OAuth +func (p *GenericProvider) Exchange(ctx context.Context, authorizationProvider, authorizationCode string) (*Token, error) { + if p.GetProviderName() != authorizationProvider { + return nil, fmt.Errorf("unsupported authorization provider") + } + ctx = context.WithValue(ctx, oauth2.HTTPClient, p.NewHTTPClient()) + + token, err := p.OAuth2.Exchange(ctx, authorizationCode) + if err != nil { + return nil, err + } + + oauthClient := p.OAuth2.Client(ctx, token) + resp, err := oauthClient.Get(p.OAuth2.Endpoint.AuthURL + "/userinfo") + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + var profile map[string]interface{} + if err = json.NewDecoder(resp.Body).Decode(&profile); err != nil { + return nil, err + } + + userID, ok := profile["sub"].(string) + if !ok { + return nil, fmt.Errorf("cannot determine UserID") + } + + t := Token{ + AccessToken: token.AccessToken, + RefreshToken: token.RefreshToken, + Expiry: token.Expiry, + UserID: userID, + } + return &t, nil +} + +// Refresh gets new Access Token via OAuth. +func (p *GenericProvider) Refresh(ctx context.Context, refreshToken string) (*Token, error) { + restoredToken := &oauth2.Token{ + RefreshToken: refreshToken, + } + tokenSource := p.OAuth2.TokenSource(ctx, restoredToken) + token, err := tokenSource.Token() + if err != nil { + return nil, err + } + + oauthClient := p.OAuth2.Client(ctx, token) + resp, err := oauthClient.Get(p.OAuth2.Endpoint.AuthURL + "/userinfo") + if err != nil { + return nil, err + } + + defer resp.Body.Close() + + var profile map[string]interface{} + if err = json.NewDecoder(resp.Body).Decode(&profile); err != nil { + return nil, err + } + + userID, _ := profile["sub"].(string) + // if it is not determined, request userId will used. + + return &Token{ + AccessToken: token.AccessToken, + RefreshToken: token.RefreshToken, + Expiry: token.Expiry, + UserID: userID, + }, nil +} diff --git a/authorization/provider/github.go b/authorization/provider/github.go new file mode 100644 index 000000000..c91953836 --- /dev/null +++ b/authorization/provider/github.go @@ -0,0 +1,82 @@ +package provider + +import ( + "context" + "fmt" + "net/http" + "strconv" + + "github.com/google/go-github/github" + "golang.org/x/oauth2" + githubconf "golang.org/x/oauth2/github" +) + +// NewGitHubProvider creates GitHub OAuth client +func NewGitHubProvider(config Config) *GitHubProvider { + oauth2 := config.OAuth2.ToOAuth2() + if oauth2.Endpoint.AuthURL == "" || oauth2.Endpoint.TokenURL == "" { + oauth2.Endpoint = githubconf.Endpoint + } + oauth2.Scopes = []string{"user:email"} + + return &GitHubProvider{ + Config: config, + OAuth2: &oauth2, + NewGithubClient: github.NewClient, + NewHTTPClient: func() *http.Client { return http.DefaultClient }, + } +} + +// GitHubProvider configuration with client factories +type GitHubProvider struct { + Config Config + OAuth2 *oauth2.Config + NewGithubClient func(*http.Client) *github.Client + NewHTTPClient func() *http.Client +} + +// GetProviderName returns unique name of the provider +func (p *GitHubProvider) GetProviderName() string { + return p.Config.Provider +} + +// AuthCodeURL returns URL for redirecting to the GitHub authentication web page. +func (p *GitHubProvider) AuthCodeURL(csrfToken string) string { + return p.Config.OAuth2.AuthCodeURL(csrfToken) +} + +// Exchange Auth Code for Access Token via OAuth. +func (p *GitHubProvider) Exchange(ctx context.Context, authorizationProvider, authorizationCode string) (*Token, error) { + if p.GetProviderName() != authorizationProvider { + return nil, fmt.Errorf("unsupported authorization provider") + } + ctx = context.WithValue(ctx, oauth2.HTTPClient, p.NewHTTPClient()) + + token, err := p.OAuth2.Exchange(ctx, authorizationCode) + if err != nil { + return nil, err + } + + oauthClient := p.OAuth2.Client(ctx, token) + client := p.NewGithubClient(oauthClient) + + user, _, err := client.Users.Get(ctx, "") + if err != nil { + return nil, err + } + + t := Token{ + AccessToken: token.AccessToken, + RefreshToken: token.RefreshToken, + Expiry: token.Expiry, + UserID: strconv.FormatInt(user.GetID(), 10), + } + return &t, nil +} + +// Refresh gets new Access Token via OAuth. +// GitHub provides permanent AccessToken and no RefreshToken, +// thus refreshToken is not implemented. +func (p *GitHubProvider) Refresh(ctx context.Context, refreshToken string) (*Token, error) { + return nil, fmt.Errorf("not supported") +} diff --git a/authorization/provider/github_test.go b/authorization/provider/github_test.go new file mode 100644 index 000000000..9466e743a --- /dev/null +++ b/authorization/provider/github_test.go @@ -0,0 +1,143 @@ +package provider + +import ( + "context" + "net/http" + "net/http/httptest" + "net/url" + "testing" + "time" + + "io" + + "github.com/google/go-github/github" + "github.com/stretchr/testify/assert" +) + +const providerName = "github" + +func TestAuthCodeURL(t *testing.T) { + mux := http.NewServeMux() + server := httptest.NewServer(mux) + defer server.Close() + p := newGithubOAuth(server) + + token := "randomToken" + url := p.AuthCodeURL(token) + assert.Contains(t, url, token) +} + +func TestSignUpGitHubProvider(t *testing.T) { + mux := http.NewServeMux() + mux.HandleFunc("/oauth/access_token", makeJSONHandler(http.StatusOK, validAccessToken)) + mux.HandleFunc("/github/api/", makeJSONHandler(http.StatusOK, validUserID)) + server := httptest.NewServer(mux) + defer server.Close() + + p := newGithubOAuth(server) + ctx := context.Background() + token, err := p.Exchange(ctx, providerName, "authCode") + + assert := assert.New(t) + assert.Nil(err) + assert.Equal("TestAccessToken", token.AccessToken) + assert.Equal("TestRefreshToken", token.RefreshToken) + expiresIn := int(token.Expiry.Sub(time.Now()).Seconds()) + assert.True(3595 < expiresIn && expiresIn <= 3600) + assert.Equal("42", token.UserID) +} + +func TestRefreshTokenGitHubProvider(t *testing.T) { + mux := http.NewServeMux() + mux.HandleFunc("/oauth/access_token", makeJSONHandler(http.StatusOK, validAccessToken)) + mux.HandleFunc("/github/api/", makeJSONHandler(http.StatusOK, validUserID)) + server := httptest.NewServer(mux) + defer server.Close() + + p := newGithubOAuth(server) + ctx := context.Background() + n, err := p.Refresh(ctx, "refresh-token") + + assert := assert.New(t) + assert.Nil(n) + assert.Error(err) +} + +func TestOAuthExchangeFailure(t *testing.T) { + mux := http.NewServeMux() + server := httptest.NewServer(mux) + defer server.Close() + + p := newGithubOAuth(server) + ctx := context.Background() + token, err := p.Exchange(ctx, providerName, "authCode") + + assert := assert.New(t) + assert.Error(err) + assert.Nil(token) +} + +func TestGithubFailure(t *testing.T) { + mux := http.NewServeMux() + mux.HandleFunc("/oauth/access_token", makeJSONHandler(http.StatusOK, validAccessToken)) + server := httptest.NewServer(mux) + defer server.Close() + + p := newGithubOAuth(server) + ctx := context.Background() + token, err := p.Exchange(ctx, providerName, "authCode") + + assert := assert.New(t) + assert.Error(err) + assert.Nil(token) +} + +func newGithubOAuth(server *httptest.Server) Provider { + endpoint := Endpoint{ + AuthURL: server.URL + "/oauth/authorize", + TokenURL: server.URL + "/oauth/access_token", + } + Config := Config{ + Provider: "github", + OAuth2: OAuthConfig{ + ClientID: "clientId", + ClientSecret: "clientSecret", + RedirectURL: "", + Endpoint: endpoint, + }, + } + p := NewGitHubProvider(Config) + p.NewGithubClient = func(h *http.Client) *github.Client { + c := github.NewClient(h) + baseURL, _ := url.Parse(server.URL + "/github/api/") + c.BaseURL = baseURL + return c + } + p.NewHTTPClient = server.Client + + return p +} + +func makeJSONHandler(statuscode int, body string) func(http.ResponseWriter, *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(statuscode) + io.WriteString(w, body) + } +} + +// https://www.oauth.com/oauth2-servers/access-tokens/access-token-response/ +const validAccessToken = `{ + "access_token":"TestAccessToken", + "token_type":"TestTokenType", + "expires_in": 3600, + "refresh_token":"TestRefreshToken", + "scope":"TestScope" +} +` + +// https://developer.github.com/v3/users/ +const validUserID = `{ + "id": 42 +} +` diff --git a/authorization/provider/provider.go b/authorization/provider/provider.go new file mode 100644 index 000000000..9744ace57 --- /dev/null +++ b/authorization/provider/provider.go @@ -0,0 +1,88 @@ +package provider + +import ( + "context" + + "golang.org/x/oauth2" +) + +// Provider defines interface for authentification against auth service +type Provider = interface { + Exchange(ctx context.Context, authorizationProvider, authorizationCode string) (*Token, error) + Refresh(ctx context.Context, refreshToken string) (*Token, error) + AuthCodeURL(csrfToken string) string +} + +type Endpoint struct { + AuthURL string `envconfig:"AUTH_URL" env:"AUTH_URL"` + TokenURL string `envconfig:"TOKEN_URL" env:"AUTH_URL"` +} + +type OAuthConfig struct { + ClientID string `envconfig:"CLIENT_ID" env:"CLIENT_ID"` + ClientSecret string `envconfig:"CLIENT_SECRET" env:"CLIENT_SECRET"` + Scopes []string `envconfig:"SCOPES" env:"SCOPES"` + Endpoint Endpoint `envconfig:"ENDPOINT" env:"ENDPOINT"` + Audience string `envconfig:"AUDIENCE" env:"AUDIENCE"` + RedirectURL string `envconfig:"REDIRECT_URL" env:"REDIRECT_URL"` + AccessType string `envconfig:"ACCESS_TYPE" env:"ACCESS_TYPE"` + ResponseType string `envconfig:"RESPONSE_TYPE" env:"RESPONSE_TYPE"` + ResponseMode string `envconfig:"RESPONSE_MODE" env:"RESPONSE_MODE"` +} + +func (c OAuthConfig) ToOAuth2() oauth2.Config { + return oauth2.Config{ + ClientID: c.ClientID, + ClientSecret: c.ClientSecret, + RedirectURL: c.RedirectURL, + Scopes: c.Scopes, + Endpoint: oauth2.Endpoint{ + AuthURL: c.Endpoint.AuthURL, + TokenURL: c.Endpoint.TokenURL, + }, + } +} + +func (c OAuthConfig) AuthCodeURL(csrfToken string) string { + aud := c.Audience + accessType := c.AccessType + responseType := c.ResponseType + responseMode := c.ResponseMode + + opts := make([]oauth2.AuthCodeOption, 0, 2) + + if accessType != "" { + opts = append(opts, oauth2.SetAuthURLParam("access_type", accessType)) + } + if aud != "" { + opts = append(opts, oauth2.SetAuthURLParam("audience", aud)) + } + if responseType != "" { + opts = append(opts, oauth2.SetAuthURLParam("response_type", responseType)) + } + if responseMode != "" { + opts = append(opts, oauth2.SetAuthURLParam("response_mode", responseMode)) + } + auth := c.ToOAuth2() + return (&auth).AuthCodeURL(csrfToken, opts...) +} + +// Config general configuration +type Config struct { + Provider string `envconfig:"PROVIDER" env:"PROVIDER" default:"generic"` // value which comes from the device during the sign-up ("apn") + OAuth2 OAuthConfig `envconfig:"OAUTH" env:"OAUTH"` +} + +// New creates GitHub OAuth client +func New(config Config) Provider { + switch config.Provider { + case "github": + return NewGitHubProvider(config) + case "test": + return NewTestProvider() + case "auth0": + return NewAuth0Provider(config) + default: + return NewGenericProvider(config) + } +} diff --git a/authorization/provider/testProvider.go b/authorization/provider/testProvider.go new file mode 100644 index 000000000..cf8b2895e --- /dev/null +++ b/authorization/provider/testProvider.go @@ -0,0 +1,170 @@ +package provider + +import ( + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "fmt" + "log" + "time" + + "github.com/valyala/fasthttp" + + "github.com/go-ocf/kit/codec/json" + + "github.com/lestrrat-go/jwx/jwa" + "github.com/lestrrat-go/jwx/jwk" + "github.com/lestrrat-go/jwx/jws" + "github.com/lestrrat-go/jwx/jwt" +) + +var jwkPrivateKey *ecdsa.PrivateKey +var jwkKeyID = `QkY4MzFGMTdFMzMyN0NGQjEyOUFFMzE5Q0ZEMUYzQUQxNkNENTlEMg` +var jwkKey jwk.Key +var clientID = "test" +var UserToken = "" + +func init() { + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + log.Fatalf("failed to generate key: %s", err) + } + jwkPrivateKey = privateKey + key, err := jwk.New(&jwkPrivateKey.PublicKey) + if err != nil { + log.Fatalf("failed to create JWK: %s", err) + } + key.Set(jwk.KeyIDKey, jwkKeyID) + key.Set(jwk.AlgorithmKey, jwa.ES256.String()) + jwkKey = key + + token, err := generateToken() + if err != nil { + log.Fatal(err) + } + UserToken = token.AccessToken +} + +func generateToken() (*Token, error) { + t := Token{ + RefreshToken: "refresh-token", + Expiry: time.Now().Add(time.Hour * 24 * 365), + UserID: "1", + } + token := jwt.New() + token.Set(jwt.SubjectKey, t.UserID) + token.Set(jwt.AudienceKey, []string{"https://127.0.0.1", "https://localhost"}) + token.Set(jwt.IssuedAtKey, time.Now().Unix()) + token.Set(jwt.ExpirationKey, t.Expiry.Unix()) + token.Set(`scope`, `openid`) + token.Set(`client_id`, clientID) + token.Set(`email`, `test@test.com`) + token.Set(jwt.IssuerKey, "https://localhost/") + buf, err := json.Encode(token) + if err != nil { + return nil, fmt.Errorf("failed to encode token: %s", err) + } + + var hdr jws.StandardHeaders + hdr.Set(jws.AlgorithmKey, jwa.ES256.String()) + hdr.Set(jws.TypeKey, `JWT`) + hdr.Set(jws.KeyIDKey, jwkKeyID) + payload, err := jws.Sign(buf, jwa.ES256, jwkPrivateKey, jws.WithHeaders(&hdr)) + if err != nil { + return nil, fmt.Errorf("failed to create UserToken: %s", err) + } + t.AccessToken = string(payload) + return &t, nil +} + +// NewTestProvider creates GitHub Oauth client. +func NewTestProvider() *TestProvider { + return &TestProvider{} +} + +// TestProvider basic configuration. +type TestProvider struct { +} + +// GetProviderName provides provider name. +func (p *TestProvider) GetProviderName() string { + return clientID +} + +// Exchange Auth Code for Access Token via OAuth. +func (p *TestProvider) Exchange(ctx context.Context, authorizationProvider, authorizationCode string) (*Token, error) { + return generateToken() +} + +// Refresh gets new Access Token via OAuth. +func (p *TestProvider) Refresh(ctx context.Context, refreshToken string) (*Token, error) { + return generateToken() +} + +// AuthCodeURL returns URL for redirecting. +func (p *TestProvider) AuthCodeURL(csrfToken string) string { + return "redirect-url" +} + +func setErrorResponse(r *fasthttp.Response, code int, body string) { + r.Header.SetContentType("text/plain; charset=utf-8") + r.SetStatusCode(code) + r.SetBodyString(body) +} + +func (p *TestProvider) HandleAuthorizationCode(ctx *fasthttp.RequestCtx) { + resp := map[string]interface{}{ + "code": "test", + } + data, err := json.Encode(resp) + if err != nil { + setErrorResponse(&ctx.Response, fasthttp.StatusInternalServerError, err.Error()) + return + } + r := &ctx.Response + r.Header.SetContentType("text/html") + r.SetStatusCode(fasthttp.StatusOK) + r.SetBodyString(string(data)) +} + +func (p *TestProvider) HandleAccessToken(ctx *fasthttp.RequestCtx) { + token, err := generateToken() + if err != nil { + setErrorResponse(&ctx.Response, fasthttp.StatusInternalServerError, err.Error()) + return + } + resp := map[string]interface{}{ + "access_token": token.AccessToken, + "expires_in": int64(token.Expiry.Sub(time.Now()).Seconds()), + "scope": "openid", + "token_type": "Bearer", + } + data, err := json.Encode(resp) + if err != nil { + setErrorResponse(&ctx.Response, fasthttp.StatusInternalServerError, err.Error()) + return + } + r := &ctx.Response + r.Header.SetContentType("text/html") + r.SetStatusCode(fasthttp.StatusOK) + r.SetBodyString(string(data)) +} + +func (p *TestProvider) HandleJWKs(ctx *fasthttp.RequestCtx) { + resp := map[string]interface{}{ + "keys": []jwk.Key{ + jwkKey, + }, + } + data, err := json.Encode(resp) + if err != nil { + setErrorResponse(&ctx.Response, fasthttp.StatusInternalServerError, err.Error()) + return + } + + r := &ctx.Response + r.Header.SetContentType("application/json") + r.SetStatusCode(fasthttp.StatusOK) + r.SetBodyString(string(data)) +} diff --git a/authorization/provider/testProvider_test.go b/authorization/provider/testProvider_test.go new file mode 100644 index 000000000..87d7b3966 --- /dev/null +++ b/authorization/provider/testProvider_test.go @@ -0,0 +1,37 @@ +package provider + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestSignUpTestProvider(t *testing.T) { + p := NewTestProvider() + ctx := context.Background() + token, err := p.Exchange(ctx, "", "authCode") + + assert := assert.New(t) + assert.Nil(err) + assert.NotEmpty(token.AccessToken) + assert.Equal("refresh-token", token.RefreshToken) + expiresIn := int(token.Expiry.Sub(time.Now()).Seconds()) + assert.True(expiresIn > 60*60*60*60) + assert.Equal("1", token.UserID) +} + +func TestRefreshTokenTestProvider(t *testing.T) { + p := NewTestProvider() + ctx := context.Background() + token, err := p.Refresh(ctx, "refresh-token") + + assert := assert.New(t) + assert.Nil(err) + assert.NotEmpty(token.AccessToken) + assert.Equal("refresh-token", token.RefreshToken) + expiresIn := int(token.Expiry.Sub(time.Now()).Seconds()) + assert.True(expiresIn > 60*60*60*60) + assert.Equal("1", token.UserID) +} diff --git a/authorization/provider/token.go b/authorization/provider/token.go new file mode 100644 index 000000000..b6d753dfe --- /dev/null +++ b/authorization/provider/token.go @@ -0,0 +1,13 @@ +package provider + +import ( + "time" +) + +// Token provides access tokens and their attributes. +type Token struct { + AccessToken string + RefreshToken string + Expiry time.Time + UserID string +} diff --git a/authorization/service/addDevice.go b/authorization/service/addDevice.go new file mode 100644 index 000000000..51e19e724 --- /dev/null +++ b/authorization/service/addDevice.go @@ -0,0 +1,62 @@ +package service + +import ( + "context" + "time" + + "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/cloud/authorization/persistence" + grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// AddDevice adds a device to user. It is used by openapi connector. +func (s *Service) AddDevice(ctx context.Context, request *pb.AddDeviceRequest) (*pb.AddDeviceResponse, error) { + tx := s.persistence.NewTransaction(ctx) + defer tx.Close() + + userID := request.UserId + token, err := grpc_auth.AuthFromMD(ctx, "bearer") + if err != nil { + uid, err := parseSubFromJwtToken(token) + if err == nil { + userID = uid + } + } + + if userID == "" { + return nil, logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot add device: invalid UserId")) + } + + if request.DeviceId == "" { + return nil, logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot add device: invalid DeviceId")) + } + + // TODO validate jwt token -> only jwt token is supported + + dev, ok, err := tx.RetrieveByDevice(request.DeviceId) + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.Internal, "cannot add device: %v", err.Error())) + } + if ok { + if dev.UserID == userID { + return &pb.AddDeviceResponse{}, nil + } + return nil, logAndReturnError(status.Errorf(codes.Unauthenticated, "cannot add device: devices is owned by another user '%+v'", dev)) + } + + d := persistence.AuthorizedDevice{ + DeviceID: request.DeviceId, + UserID: userID, + AccessToken: "", + RefreshToken: "", + Expiry: time.Time{}, + } + + if err := tx.Persist(&d); err != nil { + return nil, logAndReturnError(status.Errorf(codes.Internal, "cannot add device up: %v", err.Error())) + } + + return &pb.AddDeviceResponse{}, nil +} diff --git a/authorization/service/addDevice_test.go b/authorization/service/addDevice_test.go new file mode 100644 index 000000000..4c1600d5f --- /dev/null +++ b/authorization/service/addDevice_test.go @@ -0,0 +1,89 @@ +package service + +import ( + "context" + "testing" + + "github.com/go-ocf/cloud/authorization/pb" + "github.com/stretchr/testify/require" +) + +func TestService_AddDevice(t *testing.T) { + type args struct { + ctx context.Context + request *pb.AddDeviceRequest + } + tests := []struct { + name string + args args + want *pb.AddDeviceResponse + wantErr bool + }{ + { + name: "invalid userId", + args: args{ + ctx: context.Background(), + request: &pb.AddDeviceRequest{}, + }, + wantErr: true, + }, + { + name: "invalid deviceId", + args: args{ + ctx: context.Background(), + request: &pb.AddDeviceRequest{ + UserId: "userId", + }, + }, + wantErr: true, + }, + { + name: "not belongs to user", + args: args{ + request: &pb.AddDeviceRequest{ + DeviceId: testDeviceID, + UserId: "aaa", + }, + }, + wantErr: true, + }, + { + name: "valid", + args: args{ + request: &pb.AddDeviceRequest{ + UserId: "userId", + DeviceId: "deviceId", + }, + }, + want: &pb.AddDeviceResponse{}, + }, + { + name: "duplicit", + args: args{ + request: &pb.AddDeviceRequest{ + UserId: "userId", + DeviceId: "deviceId", + }, + }, + want: &pb.AddDeviceResponse{}, + }, + // TODO: Add test cases. + } + + s, shutdown := newTestService(t) + defer shutdown() + defer s.cleanUp() + persistDevice(t, s.service.persistence, newTestDevice()) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := s.service.AddDevice(context.Background(), tt.args.request) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tt.want, got) + } + }) + } +} diff --git a/authorization/service/config.go b/authorization/service/config.go new file mode 100644 index 000000000..58a861492 --- /dev/null +++ b/authorization/service/config.go @@ -0,0 +1,31 @@ +package service + +import ( + "encoding/json" + "fmt" + + "github.com/go-ocf/cloud/authorization/persistence/mongodb" + "github.com/go-ocf/cloud/authorization/provider" + "github.com/go-ocf/kit/log" + "github.com/go-ocf/kit/security/certManager" +) + +// Config provides defaults and enables configuring via env variables. +type Config struct { + Log log.Config + + Device provider.Config `envconfig:"DEVICE" env:"DEVICE"` + SDK provider.OAuthConfig `envconfig:"SDK_OAUTH" env:"SDK_OAUTH"` + + MongoDB mongodb.Config `envconfig:"MONGODB" env:"MONGODB"` + Listen certManager.Config `envconfig:"LISTEN" env:"LISTEN"` + Dial certManager.Config `envconfig:"DIAL" env:"DIAL"` + Addr string `envconfig:"ADDRESS" env:"ADDRESS" default:"0.0.0.0:9100"` + HTTPAddr string `envconfig:"HTTP_ADDRESS" env:"HTTP_ADDRESS" default:"0.0.0.0:9200"` +} + +//String return string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} diff --git a/authorization/service/expiresIn.go b/authorization/service/expiresIn.go new file mode 100644 index 000000000..ce71de7f1 --- /dev/null +++ b/authorization/service/expiresIn.go @@ -0,0 +1,17 @@ +package service + +import "time" + +// ExpiresIn calculates the remaining time until expiration. +// No expiration is denoted by expiry = 0 for which -1 is returned. +// When expired, ok is false. +func ExpiresIn(expiry time.Time) (expiresInSeconds int64, ok bool) { + if expiry.IsZero() { + return -1, true // No expiration. + } + d := int64(expiry.Sub(time.Now()).Seconds()) + if d <= 0 { + return d, false + } + return d, true +} diff --git a/authorization/service/expiresIn_test.go b/authorization/service/expiresIn_test.go new file mode 100644 index 000000000..6a9d29377 --- /dev/null +++ b/authorization/service/expiresIn_test.go @@ -0,0 +1,25 @@ +package service + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestNoExpiration(t *testing.T) { + s, ok := ExpiresIn(time.Time{}) + assert.Equal(t, int64(-1), s) + assert.True(t, ok) +} + +func TestExpired(t *testing.T) { + _, ok := ExpiresIn(time.Now().Add(-time.Minute)) + assert.False(t, ok) +} + +func TestExpiresIn(t *testing.T) { + s, ok := ExpiresIn(time.Now().Add(time.Minute)) + assert.True(t, 55 < s && s <= 60) + assert.True(t, ok) +} diff --git a/authorization/service/getUserDevices.go b/authorization/service/getUserDevices.go new file mode 100644 index 000000000..253226496 --- /dev/null +++ b/authorization/service/getUserDevices.go @@ -0,0 +1,107 @@ +package service + +import ( + "fmt" + + "github.com/go-ocf/cloud/authorization/persistence" + + jwt "github.com/dgrijalva/jwt-go" + "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/kit/log" + grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// hasMatchDeviceID returns true if device id match filter. +// An empty deviceIdsFilter matches all device ids. +func hasMatchDeviceID(deviceId string, deviceIdsFilter map[string]bool) bool { + if len(deviceIdsFilter) == 0 { + return true + } + if _, ok := deviceIdsFilter[deviceId]; ok { + return true + } + return false +} + +type claims struct { + Subject string `json:"sub,omitempty"` +} + +func (c *claims) Valid() error { + return nil +} + +func logAndReturnError(err error) error { + log.Errorf("%v", err) + return err +} + +func parseSubFromJwtToken(rawJwtToken string) (string, error) { + parser := &jwt.Parser{ + SkipClaimsValidation: true, + } + + var claims claims + _, _, err := parser.ParseUnverified(rawJwtToken, &claims) + if err != nil { + return "", fmt.Errorf("cannot get subject from jwt token: %v", err) + } + + if claims.Subject != "" { + return claims.Subject, nil + } + + return "", fmt.Errorf("cannot get subject from jwt token: not found") +} + +// GetUserDevices returns a list of user's devices if the access token is valid. +func (s *Service) GetUserDevices(request *pb.GetUserDevicesRequest, srv pb.AuthorizationService_GetUserDevicesServer) error { + tx := s.persistence.NewTransaction(srv.Context()) + defer tx.Close() + + userIdsFilter := request.GetUserIdsFilter() + if len(userIdsFilter) == 0 { + token, err := grpc_auth.AuthFromMD(srv.Context(), "bearer") + if err != nil { + return logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot add device: %v", err)) + } + userID, err := parseSubFromJwtToken(token) + if err != nil { + log.Debugf("cannot parse user from jwt token: %v", err) + } + if userID == "" { + return logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot get user devices: invalid userIdsFilter")) + } + userIdsFilter = []string{userID} + } + + deviceIdsFilter := make(map[string]bool) + for _, deviceID := range request.GetDeviceIdsFilter() { + deviceIdsFilter[deviceID] = true + } + + for _, userID := range userIdsFilter { + var ids []string + it := tx.RetrieveAll(userID) + var d persistence.AuthorizedDevice + for it.Next(&d) { + if hasMatchDeviceID(d.DeviceID, deviceIdsFilter) { + ids = append(ids, d.DeviceID) + } + } + it.Close() + if it.Err() != nil { + return logAndReturnError(status.Errorf(codes.Internal, "cannot get user devices: %v", it.Err())) + } + + for _, deviceID := range ids { + err := srv.Send(&pb.UserDevice{DeviceId: deviceID, UserId: userID}) + if err != nil { + return logAndReturnError(status.Errorf(status.Convert(err).Code(), "cannot get user devices: %v", err)) + } + } + } + return nil +} diff --git a/authorization/service/getUserDevices_test.go b/authorization/service/getUserDevices_test.go new file mode 100644 index 000000000..85a5bf816 --- /dev/null +++ b/authorization/service/getUserDevices_test.go @@ -0,0 +1,85 @@ +package service + +import ( + "context" + "testing" + + "github.com/go-ocf/cloud/authorization/pb" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "google.golang.org/grpc" + + "github.com/stretchr/testify/assert" +) + +func TestUserDevicesList(t *testing.T) { + srv := newMockRetrieveResourcesValues(kitNetGrpc.CtxWithIncomingToken(context.Background(), testAccessToken)) + s, shutdown := newTestService(t) + defer shutdown() + defer s.cleanUp() + persistDevice(t, s.service.persistence, newTestDevice()) + err := s.service.GetUserDevices(newGetUserDevicesRequest(), srv) + assert.NoError(t, err) + r := map[string]*pb.UserDevice{ + testDeviceID: &pb.UserDevice{ + DeviceId: testDeviceID, + UserId: testUserID, + }, + } + assert.Equal(t, r, srv.resourceValues) +} + +func TestListingMoreDevices(t *testing.T) { + srv := newMockRetrieveResourcesValues(kitNetGrpc.CtxWithIncomingToken(context.Background(), testAccessToken)) + s, shutdown := newTestService(t) + defer shutdown() + defer s.cleanUp() + persistDevice(t, s.service.persistence, newTestDevice()) + d := newTestDevice() + d.DeviceID = "anotherDeviceID" + persistDevice(t, s.service.persistence, d) + + err := s.service.GetUserDevices(newGetUserDevicesRequest(), srv) + assert := assert.New(t) + assert.NoError(err) + r := map[string]*pb.UserDevice{ + testDeviceID: &pb.UserDevice{ + DeviceId: testDeviceID, + UserId: testUserID, + }, + d.DeviceID: &pb.UserDevice{ + DeviceId: d.DeviceID, + UserId: testUserID, + }, + } + assert.Equal(r, srv.resourceValues) +} + +func newGetUserDevicesRequest() *pb.GetUserDevicesRequest { + return &pb.GetUserDevicesRequest{ + UserIdsFilter: []string{testUserID}, + } +} + +type mockGetUserDevicesServer struct { + resourceValues map[string]*pb.UserDevice + ctx context.Context + grpc.ServerStream +} + +func newMockRetrieveResourcesValues(ctx context.Context) *mockGetUserDevicesServer { + return &mockGetUserDevicesServer{ + ctx: ctx, + } +} + +func (d *mockGetUserDevicesServer) Send(r *pb.UserDevice) error { + if d.resourceValues == nil { + d.resourceValues = make(map[string]*pb.UserDevice) + } + d.resourceValues[r.DeviceId] = r + return nil +} + +func (d *mockGetUserDevicesServer) Context() context.Context { + return d.ctx +} diff --git a/authorization/service/handleAccessToken.go b/authorization/service/handleAccessToken.go new file mode 100644 index 000000000..3123acf58 --- /dev/null +++ b/authorization/service/handleAccessToken.go @@ -0,0 +1,28 @@ +package service + +import ( + cache "github.com/patrickmn/go-cache" + "github.com/valyala/fasthttp" +) + +// HandleAccessToken requests the access token for the user +func (s *Service) HandleAccessToken(ctx *fasthttp.RequestCtx) { + if p, ok := s.deviceProvider.(interface { + HandleAccessToken(ctx *fasthttp.RequestCtx) + }); ok { + p.HandleAccessToken(ctx) + return + } + t, err := generateRandomString(32) + if err != nil { + setErrorResponse(&ctx.Response, fasthttp.StatusInternalServerError, "Random generator failed") + return + } + if err := s.csrfTokens.Add(t, nil, cache.DefaultExpiration); err != nil { + setErrorResponse(&ctx.Response, fasthttp.StatusInternalServerError, "Key collision") + return + } + + url := s.sdkProvider.AuthCodeURL(t) + ctx.Redirect(url, fasthttp.StatusTemporaryRedirect) +} diff --git a/authorization/service/handleAuthorizationCode.go b/authorization/service/handleAuthorizationCode.go new file mode 100644 index 000000000..9c49176c9 --- /dev/null +++ b/authorization/service/handleAuthorizationCode.go @@ -0,0 +1,40 @@ +package service + +import ( + "crypto/rand" + "encoding/base64" + + cache "github.com/patrickmn/go-cache" + "github.com/valyala/fasthttp" +) + +// HandleAuthorizationCode requests the authorization code for the device on behalf of the user +func (s *Service) HandleAuthorizationCode(ctx *fasthttp.RequestCtx) { + if p, ok := s.deviceProvider.(interface { + HandleAuthorizationCode(ctx *fasthttp.RequestCtx) + }); ok { + p.HandleAuthorizationCode(ctx) + return + } + + t, err := generateRandomString(32) + if err != nil { + setErrorResponse(&ctx.Response, fasthttp.StatusInternalServerError, "Random generator failed") + return + } + if err := s.csrfTokens.Add(t, nil, cache.DefaultExpiration); err != nil { + setErrorResponse(&ctx.Response, fasthttp.StatusInternalServerError, "Key collision") + return + } + + url := s.deviceProvider.AuthCodeURL(t) + ctx.Redirect(url, fasthttp.StatusTemporaryRedirect) +} + +func generateRandomString(n int) (string, error) { + b := make([]byte, n) + if _, err := rand.Read(b); err != nil { + return "", err + } + return base64.URLEncoding.EncodeToString(b), nil +} diff --git a/authorization/service/handleJWKs.go b/authorization/service/handleJWKs.go new file mode 100644 index 000000000..873296c85 --- /dev/null +++ b/authorization/service/handleJWKs.go @@ -0,0 +1,17 @@ +package service + +import ( + "fmt" + + "github.com/valyala/fasthttp" +) + +func (s *Service) HandleJWKs(ctx *fasthttp.RequestCtx) { + if p, ok := s.deviceProvider.(interface { + HandleJWKs(ctx *fasthttp.RequestCtx) + }); ok { + p.HandleJWKs(ctx) + return + } + setErrorResponse(&ctx.Response, fasthttp.StatusNotFound, fmt.Sprintf("not found")) +} diff --git a/authorization/service/healthcheck.go b/authorization/service/healthcheck.go new file mode 100644 index 000000000..91f5d2787 --- /dev/null +++ b/authorization/service/healthcheck.go @@ -0,0 +1,9 @@ +package service + +import ( + "github.com/valyala/fasthttp" +) + +func (s *Service) Healthcheck(ctx *fasthttp.RequestCtx) { + ctx.SetStatusCode(fasthttp.StatusOK) +} diff --git a/authorization/service/http.go b/authorization/service/http.go new file mode 100644 index 000000000..b43063f9f --- /dev/null +++ b/authorization/service/http.go @@ -0,0 +1,9 @@ +package service + +import "github.com/valyala/fasthttp" + +func setErrorResponse(r *fasthttp.Response, code int, body string) { + r.Header.SetContentType("text/plain; charset=utf-8") + r.SetStatusCode(code) + r.SetBodyString(body) +} diff --git a/authorization/service/oauthCallback.go b/authorization/service/oauthCallback.go new file mode 100644 index 000000000..c211a841b --- /dev/null +++ b/authorization/service/oauthCallback.go @@ -0,0 +1,45 @@ +package service + +import ( + "encoding/json" + + "github.com/valyala/fasthttp" +) + +func responseToMap(ctx *fasthttp.RequestCtx) map[string]interface{} { + res := make(map[string]interface{}) + ctx.QueryArgs().VisitAll(func(key, val []byte) { + res[string(key)] = string(val) + }) + ctx.PostArgs().VisitAll(func(key, val []byte) { + res[string(key)] = string(val) + }) + mf, err := ctx.MultipartForm() + if err == nil && mf.Value != nil { + for key, val := range mf.Value { + // only one value is stored under key: https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html + if len(val) == 1 { + res[key] = val[0] + } + } + } + return res +} + +func (s *Service) HandleOAuthCallback(ctx *fasthttp.RequestCtx) { + state := ctx.FormValue("state") + _, ok := s.csrfTokens.Get(string(state)) + if !ok { + setErrorResponse(&ctx.Response, fasthttp.StatusBadRequest, "invalid/expired OAuth state") + return + } + payload, _ := json.Marshal(responseToMap(ctx)) + body := string(payload) + setHTMLResponse(&ctx.Response, body) +} + +func setHTMLResponse(r *fasthttp.Response, html string) { + r.Header.SetContentType("text/html") // for IE - otherwise it want to download content as file + r.SetStatusCode(fasthttp.StatusOK) + r.SetBodyString(html) +} diff --git a/authorization/service/refresh.go b/authorization/service/refresh.go new file mode 100644 index 000000000..e5a315ec0 --- /dev/null +++ b/authorization/service/refresh.go @@ -0,0 +1,53 @@ +package service + +import ( + "context" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/cloud/authorization/persistence" +) + +// RefreshToken renews AccessToken using RefreshToken. +func (s *Service) RefreshToken(ctx context.Context, request *pb.RefreshTokenRequest) (*pb.RefreshTokenResponse, error) { + tx := s.persistence.NewTransaction(ctx) + defer tx.Close() + + token, err := s.deviceProvider.Refresh(ctx, request.RefreshToken) + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.Unauthenticated, "cannot refresh token: %v", err)) + } + + userID := token.UserID + if userID == "" { + userID = request.UserId + } + if userID == "" { + return nil, logAndReturnError(status.Errorf(codes.Unauthenticated, "cannot refresh token: cannot determine userId")) + } + + d := persistence.AuthorizedDevice{ + DeviceID: request.DeviceId, + UserID: userID, + AccessToken: token.AccessToken, + RefreshToken: token.RefreshToken, + Expiry: token.Expiry, + } + + if err := tx.Persist(&d); err != nil { + return nil, logAndReturnError(status.Errorf(codes.Internal, "cannot refresh token: err")) + } + + expiresIn, ok := ExpiresIn(token.Expiry) + if !ok { + return nil, logAndReturnError(status.Errorf(codes.Unauthenticated, "cannot refresh token: expired access token")) + } + + return &pb.RefreshTokenResponse{ + AccessToken: token.AccessToken, + RefreshToken: token.RefreshToken, + ExpiresIn: expiresIn, + }, nil +} diff --git a/authorization/service/refresh_test.go b/authorization/service/refresh_test.go new file mode 100644 index 000000000..2109e1f24 --- /dev/null +++ b/authorization/service/refresh_test.go @@ -0,0 +1,42 @@ +package service + +import ( + "context" + "errors" + "testing" + + "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/cloud/authorization/provider" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRefreshToken(t *testing.T) { + s, shutdown := newTestServiceWithProviders(t, provider.NewTestProvider(), provider.NewTestProvider()) + defer shutdown() + defer s.cleanUp() + r, err := s.service.RefreshToken(context.Background(), newRefreshInRequest()) + require.NoError(t, err) + assert := assert.New(t) + assert.NotEmpty(r.AccessToken) + assert.Equal("refresh-token", r.RefreshToken) + assert.True(r.ExpiresIn > 0) +} + +func TestUnauthorizedRefreshToken(t *testing.T) { + s, shutdown := newTestServiceWithProviders(t, provider.NewTestProvider(), provider.NewTestProvider()) + defer shutdown() + defer s.cleanUp() + s.service.deviceProvider = &providerT{t: nil, err: errors.New("unauthorized")} + _, err := s.service.RefreshToken(context.Background(), newRefreshInRequest()) + assert.Error(t, err) +} + +func newRefreshInRequest() *pb.RefreshTokenRequest { + return &pb.RefreshTokenRequest{ + DeviceId: "ignored", + UserId: "ignored", + RefreshToken: "ignored", + } +} diff --git a/authorization/service/removeDevice.go b/authorization/service/removeDevice.go new file mode 100644 index 000000000..1f7a531ef --- /dev/null +++ b/authorization/service/removeDevice.go @@ -0,0 +1,51 @@ +package service + +import ( + "context" + + grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth" + + "github.com/go-ocf/cloud/authorization/pb" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// RemoveDevice remove a device from user. It is used by openapi connector. +func (s *Service) RemoveDevice(ctx context.Context, request *pb.RemoveDeviceRequest) (*pb.RemoveDeviceResponse, error) { + tx := s.persistence.NewTransaction(ctx) + defer tx.Close() + + userID := request.UserId + token, err := grpc_auth.AuthFromMD(ctx, "bearer") + if err != nil { + uid, err := parseSubFromJwtToken(token) + if err == nil { + userID = uid + } + } + + if userID == "" { + return nil, logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot remove device: invalid UserId")) + } + + if request.DeviceId == "" { + return nil, logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot remove device: invalid DeviceId")) + } + + // TODO validate jwt token -> only jwt token is supported + + _, ok, err := tx.Retrieve(request.DeviceId, userID) + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.Internal, "cannot remove device: %v", err.Error())) + } + if !ok { + return nil, logAndReturnError(status.Errorf(codes.NotFound, "cannot remove device: not found")) + } + + err = tx.Delete(request.DeviceId, userID) + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.NotFound, "cannot remove device: not found")) + } + + return &pb.RemoveDeviceResponse{}, nil +} diff --git a/authorization/service/removeDevice_test.go b/authorization/service/removeDevice_test.go new file mode 100644 index 000000000..47619846a --- /dev/null +++ b/authorization/service/removeDevice_test.go @@ -0,0 +1,99 @@ +package service + +import ( + "context" + "testing" + + "github.com/go-ocf/cloud/authorization/pb" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/stretchr/testify/require" +) + +func TestService_RemoveDevice(t *testing.T) { + type args struct { + ctx context.Context + request *pb.RemoveDeviceRequest + } + tests := []struct { + name string + args args + want *pb.RemoveDeviceResponse + wantErr bool + }{ + { + name: "invalid userId", + args: args{ + request: &pb.RemoveDeviceRequest{}, + }, + wantErr: true, + }, + { + name: "invalid deviceId", + args: args{ + request: &pb.RemoveDeviceRequest{ + UserId: "userId", + }, + }, + wantErr: true, + }, + { + name: "invalid accesstoken", + args: args{ + request: &pb.RemoveDeviceRequest{ + UserId: "userId", + DeviceId: "deviceId", + }, + }, + wantErr: true, + }, + { + name: "not belongs to user", + args: args{ + request: &pb.RemoveDeviceRequest{ + DeviceId: testDeviceID, + UserId: "aaa", + }, + ctx: kitNetGrpc.CtxWithIncomingToken(context.Background(), testAccessToken), + }, + wantErr: true, + }, + { + name: "valid", + args: args{ + request: &pb.RemoveDeviceRequest{ + DeviceId: testDeviceID, + UserId: testUserID, + }, + ctx: kitNetGrpc.CtxWithIncomingToken(context.Background(), testAccessToken), + }, + want: &pb.RemoveDeviceResponse{}, + }, + { + name: "duplicit", + args: args{ + request: &pb.RemoveDeviceRequest{ + DeviceId: testDeviceID, + UserId: testUserID, + }, + ctx: kitNetGrpc.CtxWithIncomingToken(context.Background(), testAccessToken), + }, + wantErr: true, + }, + } + s, shutdown := newTestService(t) + defer shutdown() + defer s.cleanUp() + persistDevice(t, s.service.persistence, newTestDevice()) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := s.service.RemoveDevice(context.Background(), tt.args.request) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tt.want, got) + } + }) + } +} diff --git a/authorization/service/service.go b/authorization/service/service.go new file mode 100644 index 000000000..2123f8f1c --- /dev/null +++ b/authorization/service/service.go @@ -0,0 +1,125 @@ +package service + +import ( + "context" + "crypto/tls" + "fmt" + "net" + "time" + + "github.com/go-ocf/kit/security/certManager" + + "github.com/go-ocf/cloud/authorization/uri" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + "github.com/patrickmn/go-cache" + + "github.com/buaazp/fasthttprouter" + "github.com/valyala/fasthttp" + "golang.org/x/sync/errgroup" + + "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/cloud/authorization/persistence" + "github.com/go-ocf/cloud/authorization/provider" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" +) + +// Provider defines interface for authentification against auth service +type Provider = interface { + Exchange(ctx context.Context, authorizationProvider, authorizationCode string) (*provider.Token, error) + Refresh(ctx context.Context, refreshToken string) (*provider.Token, error) + AuthCodeURL(csrfToken string) string +} + +// Provider defines interface for authentification against auth service +type Persistence = interface { + NewTransaction(ctx context.Context) persistence.PersistenceTx + Clear(ctx context.Context) error + Close(ctx context.Context) error +} + +// Service holds dependencies of the authorization Service. +type Service struct { + deviceProvider Provider + sdkProvider Provider + persistence Persistence + csrfTokens *cache.Cache +} + +// Server is an HTTP server for the Service. +type Server struct { + service *Service + grpcServer *kitNetGrpc.Server + httpServer *fasthttp.Server + cfg Config + serverCertManager certManager.CertManager + listener net.Listener +} + +func newService(deviceProvider, sdkProvider Provider, persistence Persistence) *Service { + return &Service{ + deviceProvider: deviceProvider, + sdkProvider: sdkProvider, + persistence: persistence, + csrfTokens: cache.New(5*time.Minute, 10*time.Minute), + } +} + +// New creates the service's HTTP server. +func New(cfg Config, persistence Persistence, deviceProvider, sdkProvider provider.Provider) (*Server, error) { + serverCertManager, err := certManager.NewCertManager(cfg.Listen) + if err != nil { + return nil, fmt.Errorf("cannot create server cert manager %v", err) + } + httpServerTLSConfig := serverCertManager.GetServerTLSConfig() + httpServerTLSConfig.ClientAuth = tls.NoClientCert + listener, err := tls.Listen("tcp", cfg.HTTPAddr, &httpServerTLSConfig) + if err != nil { + return nil, fmt.Errorf("listening failed: %v", err) + } + + service := newService(deviceProvider, sdkProvider, persistence) + serverTLSConfig := serverCertManager.GetServerTLSConfig() + server, err := kitNetGrpc.NewServer(cfg.Addr, grpc.Creds(credentials.NewTLS(&serverTLSConfig))) + if err != nil { + return nil, err + } + + pb.RegisterAuthorizationServiceServer(server.Server, service) + + httpRouter := fasthttprouter.New() + httpRouter.GET(uri.AuthorizationCode, service.HandleAuthorizationCode) + httpRouter.POST(uri.AuthorizationCode, service.HandleAuthorizationCode) + httpRouter.GET(uri.AccessToken, service.HandleAccessToken) + httpRouter.POST(uri.AccessToken, service.HandleAccessToken) + httpRouter.GET(uri.OAuthCallback, service.HandleOAuthCallback) + httpRouter.POST(uri.OAuthCallback, service.HandleOAuthCallback) + httpRouter.GET(uri.Healthcheck, service.Healthcheck) + httpRouter.GET(uri.JWKs, service.HandleJWKs) + httpServer := &fasthttp.Server{ + Handler: httpRouter.Handler, + IdleTimeout: time.Second, + } + + return &Server{service: service, grpcServer: server, httpServer: httpServer, cfg: cfg, serverCertManager: serverCertManager, listener: listener}, nil +} + +// Serve starts the service's GRPC and HTTP server and blocks. +func (s *Server) Serve() error { + g := new(errgroup.Group) + g.Go(func() error { return s.grpcServer.Serve() }) + g.Go(func() error { return s.httpServer.Serve(s.listener) }) + + g.Wait() + return nil +} + +// Shutdown ends serving +func (s *Server) Shutdown() { + s.grpcServer.Stop() + s.httpServer.Shutdown() + s.listener.Close() + s.serverCertManager.Close() +} diff --git a/authorization/service/service_test.go b/authorization/service/service_test.go new file mode 100644 index 000000000..37c7eb537 --- /dev/null +++ b/authorization/service/service_test.go @@ -0,0 +1,103 @@ +package service + +import ( + "context" + "io/ioutil" + "os" + "sync" + "testing" + "time" + + "github.com/go-ocf/cloud/authorization/persistence" + "github.com/go-ocf/kit/security/certManager" + + "github.com/go-ocf/cloud/authorization/persistence/mongodb" + oauthProvider "github.com/go-ocf/cloud/authorization/provider" + "github.com/kelseyhightower/envconfig" + _ "github.com/mattn/go-sqlite3" // sql driver + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + testUserID = "testUserID" + testDeviceID = "testDeviceID" + testAccessToken = "testAccessToken" +) + +func newTestService(t *testing.T) (*Server, func()) { + return newTestServiceWithProviders(t, nil, nil) +} + +func newTestServiceWithProviders(t *testing.T, deviceProvider, sdkProvider Provider) (*Server, func()) { + os.Setenv("CLIENTID", "test client id") + os.Setenv("CLIENTSECRET", "test client secret") + os.Setenv("OAUTHENDPOINTDOMAIN", "") + os.Setenv("CALLBACKURL", "") + + var cfg Config + err := envconfig.Process("", &cfg) + require.NoError(t, err) + + t.Log(cfg.String()) + dialCertManager, err := certManager.NewCertManager(cfg.Dial) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + + if deviceProvider == nil { + deviceProvider = oauthProvider.NewTestProvider() + } + if sdkProvider == nil { + deviceProvider = oauthProvider.NewTestProvider() + } + persistence, err := mongodb.NewStore(context.Background(), cfg.MongoDB, mongodb.WithTLS(&tlsConfig)) + require.NoError(t, err) + + dir, err := ioutil.TempDir("", "gotesttmp") + require.NoError(t, err) + defer os.RemoveAll(dir) + + s, err := New(cfg, persistence, deviceProvider, sdkProvider) + require.NoError(t, err) + var wg sync.WaitGroup + wg.Add(1) + go func() { + s.Serve() + defer wg.Done() + }() + return s, func() { + s.Shutdown() + wg.Wait() + } +} + +func (s *Server) cleanUp() { + p := s.service.persistence + p.Clear(context.Background()) + p.Close(context.Background()) +} + +func newTestDevice() *persistence.AuthorizedDevice { + return &persistence.AuthorizedDevice{ + DeviceID: testDeviceID, + UserID: testUserID, + AccessToken: testAccessToken, + RefreshToken: "testRefreshToken", + Expiry: time.Now().Add(time.Hour), + } +} + +func retrieveDevice(t *testing.T, p Persistence, deviceID, userID string) (d *persistence.AuthorizedDevice, ok bool) { + tx := p.NewTransaction(context.Background()) + defer tx.Close() + d, ok, err := tx.Retrieve(deviceID, userID) + assert.Nil(t, err) + return +} + +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) +} diff --git a/authorization/service/signin.go b/authorization/service/signin.go new file mode 100644 index 000000000..b282727ff --- /dev/null +++ b/authorization/service/signin.go @@ -0,0 +1,47 @@ +package service + +import ( + "context" + + "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/kit/log" + grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// SignIn verifies device's AccessToken and Expiry required for signing in. +func (s *Service) SignIn(ctx context.Context, request *pb.SignInRequest) (*pb.SignInResponse, error) { + tx := s.persistence.NewTransaction(ctx) + defer tx.Close() + + token, err := grpc_auth.AuthFromMD(ctx, "bearer") + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot sign in: %v", err)) + } + + userID, err := parseSubFromJwtToken(token) + if err != nil { + log.Debugf("cannot parse user from jwt token: %v", err) + userID = request.UserId + } + + if userID == "" { + return nil, logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot sign in: invalid UserId")) + } + + d, ok, err := tx.Retrieve(request.DeviceId, userID) + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.Internal, "cannot sign in: %v", err.Error())) + } + if !ok { + return nil, logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot sign in: not found")) + } + + expiresIn, ok := ExpiresIn(d.Expiry) + if !ok { + return nil, logAndReturnError(status.Errorf(codes.Unauthenticated, "cannot sign in: expired access token")) + } + + return &pb.SignInResponse{ExpiresIn: expiresIn}, nil +} diff --git a/authorization/service/signin_test.go b/authorization/service/signin_test.go new file mode 100644 index 000000000..620e45071 --- /dev/null +++ b/authorization/service/signin_test.go @@ -0,0 +1,71 @@ +package service + +import ( + "context" + "testing" + "time" + + "github.com/go-ocf/cloud/authorization/pb" + + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSignIn(t *testing.T) { + s, shutdown := newTestService(t) + defer shutdown() + defer s.cleanUp() + persistDevice(t, s.service.persistence, newTestDevice()) + r, err := s.service.SignIn(kitNetGrpc.CtxWithIncomingToken(context.Background(), testAccessToken), newSignInRequest()) + require.NoError(t, err) + assert := assert.New(t) + assert.True(3595 < r.ExpiresIn && r.ExpiresIn <= 3600) + _, ok := retrieveDevice(t, s.service.persistence, testDeviceID, testUserID) + assert.True(ok) +} + +func TestPermanentTokensExpiration(t *testing.T) { + d := newTestDevice() + d.Expiry = time.Time{} + s, shutdown := newTestService(t) + defer shutdown() + defer s.cleanUp() + persistDevice(t, s.service.persistence, d) + + r, err := s.service.SignIn(kitNetGrpc.CtxWithIncomingToken(context.Background(), testAccessToken), newSignInRequest()) + + assert := assert.New(t) + assert.NoError(err) + assert.Equal(int64(-1), r.ExpiresIn) +} + +func TestUnauthorizedDevice(t *testing.T) { + s, shutdown := newTestService(t) + defer shutdown() + defer s.cleanUp() + + _, err := s.service.SignIn(kitNetGrpc.CtxWithIncomingToken(context.Background(), testAccessToken), newSignInRequest()) + assert := assert.New(t) + assert.Error(err) +} + +func TestExpiredAccessToken(t *testing.T) { + d := newTestDevice() + d.Expiry = time.Now().Add(-time.Minute) + s, shutdown := newTestService(t) + defer shutdown() + defer s.cleanUp() + persistDevice(t, s.service.persistence, d) + + _, err := s.service.SignIn(kitNetGrpc.CtxWithIncomingToken(context.Background(), testAccessToken), newSignInRequest()) + assert := assert.New(t) + assert.Error(err) +} + +func newSignInRequest() *pb.SignInRequest { + return &pb.SignInRequest{ + DeviceId: testDeviceID, + UserId: testUserID, + } +} diff --git a/authorization/service/signoff.go b/authorization/service/signoff.go new file mode 100644 index 000000000..90205e9ea --- /dev/null +++ b/authorization/service/signoff.go @@ -0,0 +1,49 @@ +package service + +import ( + "context" + + "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/kit/log" + grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// SignOff invalidates device's Access Token. +func (s *Service) SignOff(ctx context.Context, request *pb.SignOffRequest) (*pb.SignOffResponse, error) { + tx := s.persistence.NewTransaction(ctx) + defer tx.Close() + + token, err := grpc_auth.AuthFromMD(ctx, "bearer") + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot sign off: %v", err)) + } + userID, err := parseSubFromJwtToken(token) + if err != nil { + log.Debugf("cannot parse user from jwt token: %v", err) + userID = request.UserId + } + + if userID == "" { + return nil, logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot sign off: invalid UserId")) + } + + d, ok, err := tx.Retrieve(request.DeviceId, userID) + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.Internal, "cannot sign off: %v", err.Error())) + } + if !ok { + return nil, logAndReturnError(status.Errorf(codes.NotFound, "cannot sign off: not found")) + } + if d.AccessToken != token { + return nil, logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot sign off: unexpected access token")) + } + + err = tx.Delete(d.DeviceID, userID) + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.Internal, "cannot sign off: %v", err.Error())) + } + + return &pb.SignOffResponse{}, nil +} diff --git a/authorization/service/signoff_test.go b/authorization/service/signoff_test.go new file mode 100644 index 000000000..b177d651f --- /dev/null +++ b/authorization/service/signoff_test.go @@ -0,0 +1,54 @@ +package service + +import ( + "context" + "testing" + + "github.com/go-ocf/cloud/authorization/pb" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSignOff(t *testing.T) { + s, shutdown := newTestService(t) + defer shutdown() + defer s.cleanUp() + persistDevice(t, s.service.persistence, newTestDevice()) + + _, err := s.service.SignOff(kitNetGrpc.CtxWithIncomingToken(context.Background(), testAccessToken), newSignOffRequest()) + assert := assert.New(t) + assert.NoError(err) + _, ok := retrieveDevice(t, s.service.persistence, testDeviceID, testUserID) + assert.False(ok) +} + +func TestNonexistentDevice(t *testing.T) { + s, shutdown := newTestService(t) + defer shutdown() + defer s.cleanUp() + + _, err := s.service.SignOff(kitNetGrpc.CtxWithIncomingToken(context.Background(), testAccessToken), newSignOffRequest()) + require.Error(t, err) +} + +func TestUnexpectedAccessTokenOnSignOff(t *testing.T) { + d := newTestDevice() + d.AccessToken = "unexpected" + s, shutdown := newTestService(t) + defer shutdown() + defer s.cleanUp() + persistDevice(t, s.service.persistence, d) + + _, err := s.service.SignOff(kitNetGrpc.CtxWithIncomingToken(context.Background(), testAccessToken), newSignOffRequest()) + assert := assert.New(t) + assert.Error(err) +} + +func newSignOffRequest() *pb.SignOffRequest { + return &pb.SignOffRequest{ + DeviceId: testDeviceID, + UserId: testUserID, + } +} diff --git a/authorization/service/signout.go b/authorization/service/signout.go new file mode 100644 index 000000000..365619b9b --- /dev/null +++ b/authorization/service/signout.go @@ -0,0 +1,49 @@ +package service + +import ( + "context" + + "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/kit/log" + grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// SignOut verifies device's AccessToken and Expiry required for signing out. +func (s *Service) SignOut(ctx context.Context, request *pb.SignOutRequest) (*pb.SignOutResponse, error) { + tx := s.persistence.NewTransaction(ctx) + defer tx.Close() + + token, err := grpc_auth.AuthFromMD(ctx, "bearer") + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot sign out: %v", err)) + } + + userID, err := parseSubFromJwtToken(token) + if err != nil { + log.Debugf("cannot parse user from jwt token: %v", err) + userID = request.UserId + } + + if userID == "" { + return nil, logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot sign out: invalid UserId")) + } + + d, ok, err := tx.Retrieve(request.DeviceId, userID) + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.Internal, "cannot sign out: %v", err.Error())) + } + if !ok { + return nil, logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot sign out: not found")) + } + if d.AccessToken != token { + return nil, logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot sign out: unexpected access token")) + } + + _, ok = ExpiresIn(d.Expiry) + if !ok { + return nil, logAndReturnError(status.Errorf(codes.Unauthenticated, "cannot sign out: expired access token")) + } + return &pb.SignOutResponse{}, nil +} diff --git a/authorization/service/signout_test.go b/authorization/service/signout_test.go new file mode 100644 index 000000000..1e4c08d2c --- /dev/null +++ b/authorization/service/signout_test.go @@ -0,0 +1,70 @@ +package service + +import ( + "context" + "testing" + "time" + + "github.com/go-ocf/cloud/authorization/pb" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/stretchr/testify/assert" +) + +func TestSignOut(t *testing.T) { + s, shutdown := newTestService(t) + defer shutdown() + defer s.cleanUp() + d := newTestDevice() + persistDevice(t, s.service.persistence, d) + + r, err := s.service.SignOut(kitNetGrpc.CtxWithIncomingToken(context.Background(), testAccessToken), newSignOutRequest()) + assert := assert.New(t) + assert.NoError(err) + assert.Equal(&pb.SignOutResponse{}, r) + + _, ok := retrieveDevice(t, s.service.persistence, testDeviceID, testUserID) + assert.True(ok) +} + +func TestSigningOutUnknownDevice(t *testing.T) { + s, shutdown := newTestService(t) + defer shutdown() + defer s.cleanUp() + + _, err := s.service.SignOut(kitNetGrpc.CtxWithIncomingToken(context.Background(), testAccessToken), newSignOutRequest()) + assert := assert.New(t) + assert.Error(err) +} + +func TestUnexpectedAccessTokenOnSignOut(t *testing.T) { + d := newTestDevice() + d.AccessToken = "unexpected" + s, shutdown := newTestService(t) + defer shutdown() + defer s.cleanUp() + persistDevice(t, s.service.persistence, d) + + _, err := s.service.SignOut(kitNetGrpc.CtxWithIncomingToken(context.Background(), testAccessToken), newSignOutRequest()) + assert := assert.New(t) + assert.Error(err) +} + +func TestExpiredAccessTokenOnSignOut(t *testing.T) { + d := newTestDevice() + d.Expiry = time.Now().Add(-time.Minute) + s, shutdown := newTestService(t) + defer shutdown() + defer s.cleanUp() + persistDevice(t, s.service.persistence, d) + + _, err := s.service.SignOut(kitNetGrpc.CtxWithIncomingToken(context.Background(), testAccessToken), newSignOutRequest()) + assert := assert.New(t) + assert.Error(err) +} + +func newSignOutRequest() *pb.SignOutRequest { + return &pb.SignOutRequest{ + DeviceId: testDeviceID, + UserId: testUserID, + } +} diff --git a/authorization/service/signup.go b/authorization/service/signup.go new file mode 100644 index 000000000..b43981631 --- /dev/null +++ b/authorization/service/signup.go @@ -0,0 +1,62 @@ +package service + +import ( + "context" + + "github.com/go-ocf/kit/log" + + "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/cloud/authorization/persistence" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// SignUp exchanges Auth Code for Access Token via OAuth. +// The Access Token can be used for signing the device in/out, +// or for authorizing the device to act as the user. +func (s *Service) SignUp(ctx context.Context, request *pb.SignUpRequest) (*pb.SignUpResponse, error) { + tx := s.persistence.NewTransaction(ctx) + defer tx.Close() + + log.Debugf("Service.SignUp \"%v\"", request.AuthorizationCode) + + token, err := s.deviceProvider.Exchange(ctx, request.AuthorizationProvider, request.AuthorizationCode) + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.Unauthenticated, "cannot sign up: %v", err.Error())) + } + + dev, ok, err := tx.RetrieveByDevice(request.DeviceId) + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.Internal, "cannot sign up: %v", err.Error())) + } + if ok { + if dev.UserID != token.UserID { + return nil, logAndReturnError(status.Errorf(codes.Unauthenticated, "cannot sign up: devices is owned by another user")) + } + } + + d := persistence.AuthorizedDevice{ + DeviceID: request.DeviceId, + UserID: token.UserID, + AccessToken: token.AccessToken, + RefreshToken: token.RefreshToken, + Expiry: token.Expiry, + } + + if err := tx.Persist(&d); err != nil { + return nil, logAndReturnError(status.Errorf(codes.Internal, "cannot sign up: %v", err.Error())) + } + + expiresIn, ok := ExpiresIn(token.Expiry) + if !ok { + return nil, logAndReturnError(status.Errorf(codes.Unauthenticated, "cannot sign up: expired access token")) + } + + return &pb.SignUpResponse{ + AccessToken: token.AccessToken, + UserId: token.UserID, + RefreshToken: token.RefreshToken, + ExpiresIn: expiresIn, + RedirectUri: "", + }, nil +} diff --git a/authorization/service/signup_test.go b/authorization/service/signup_test.go new file mode 100644 index 000000000..8c223c4d7 --- /dev/null +++ b/authorization/service/signup_test.go @@ -0,0 +1,130 @@ +package service + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/cloud/authorization/provider" + "github.com/go-ocf/kit/net/http" + "github.com/stretchr/testify/assert" + "github.com/valyala/fasthttp" +) + +func TestSignUp(t *testing.T) { + s, o, shutdown := newSignUpTestService(t) + defer shutdown() + defer s.cleanUp() + + r, err := s.service.SignUp(context.Background(), newSignUpRequest()) + assert := assert.New(t) + assert.NoError(err) + + assert.Equal(o.t.AccessToken, r.AccessToken) + assert.Equal(o.t.RefreshToken, r.RefreshToken) + assert.True(3595 < r.ExpiresIn && r.ExpiresIn <= 3600) + assert.Equal(o.t.UserID, r.UserId) + _, ok := retrieveDevice(t, s.service.persistence, testDeviceID, r.UserId) + assert.True(ok) +} + +func TestUnknownProvider(t *testing.T) { + r := newSignUpRequest() + r.AuthorizationProvider = "unknown" + + s, _, shutdown := newSignUpTestService(t) + defer shutdown() + defer s.cleanUp() + s.service.deviceProvider = &providerT{t: nil, err: errors.New("invalid provider")} + + _, err := s.service.SignUp(context.Background(), r) + assert := assert.New(t) + assert.Error(err) +} + +func TestUnauthorizedSignUp(t *testing.T) { + s, _, shutdown := newSignUpTestService(t) + defer shutdown() + defer s.cleanUp() + s.service.deviceProvider = &providerT{t: nil, err: errors.New("unauthorized")} + _, err := s.service.SignUp(context.Background(), newSignUpRequest()) + assert := assert.New(t) + assert.Error(err) +} + +func TestPermanentToken(t *testing.T) { + s, o, shutdown := newSignUpTestService(t) + defer shutdown() + defer s.cleanUp() + o.t.Expiry = time.Time{} // 0 means permanent + + r, err := s.service.SignUp(context.Background(), newSignUpRequest()) + assert := assert.New(t) + assert.NoError(err) + assert.Equal(o.t.AccessToken, r.AccessToken) + assert.Equal(o.t.RefreshToken, r.RefreshToken) + assert.Equal(int64(-1), r.ExpiresIn) + assert.Equal(o.t.UserID, r.UserId) +} + +func TestExpiredToken(t *testing.T) { + var ctx fasthttp.RequestCtx + http.WriteRequest(newSignUpRequest(), &ctx.Request) + + s, o, shutdown := newSignUpTestService(t) + defer shutdown() + defer s.cleanUp() + o.t.Expiry = time.Now().Add(-time.Minute) + + _, err := s.service.SignUp(context.Background(), newSignUpRequest()) + assert := assert.New(t) + assert.Error(err) +} + +type providerT struct { + t *provider.Token + err error +} + +func (p *providerT) GetProviderName() string { + return "test" +} + +func (p *providerT) Exchange(ctx context.Context, authorizationProvider, authorizationCode string) (*provider.Token, error) { + return p.t, p.err +} + +func (p *providerT) Refresh(ctx context.Context, refreshToken string) (*provider.Token, error) { + return p.t, p.err +} + +func (p *providerT) AuthCodeURL(csrfToken string) string { + return "redirect-url" +} + +func newTestProvider() *providerT { + t := provider.Token{ + AccessToken: "test access token", + RefreshToken: "test refresh token", + Expiry: time.Now().Add(3600 * time.Second), + UserID: "test user id", + } + return &providerT{t: &t, err: nil} +} + +func newSignUpTestService(t *testing.T) (*Server, *providerT, func()) { + s, shutdown := newTestService(t) + o := newTestProvider() + s.service.deviceProvider = o + return s, o, shutdown +} + +func newSignUpRequest() *pb.SignUpRequest { + return &pb.SignUpRequest{ + DeviceId: testDeviceID, + AuthorizationCode: "authCode", + AuthorizationProvider: "test", + } +} diff --git a/authorization/test/service/service.go b/authorization/test/service/service.go new file mode 100644 index 000000000..d71caa86a --- /dev/null +++ b/authorization/test/service/service.go @@ -0,0 +1,53 @@ +package service + +import ( + "context" + "crypto/tls" + "fmt" + "sync" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/go-ocf/cloud/authorization/persistence/mongodb" + "github.com/go-ocf/cloud/authorization/provider" + "github.com/go-ocf/cloud/authorization/service" + "github.com/go-ocf/kit/security/certManager" +) + +func newService(config service.Config, tlsConfig *tls.Config) (*service.Server, error) { + oauth := provider.NewTestProvider() + persistence, err := mongodb.NewStore(context.Background(), config.MongoDB, mongodb.WithTLS(tlsConfig)) + if err != nil { + return nil, err + } + + s, err := service.New(config, persistence, oauth, oauth) + if err != nil { + return nil, fmt.Errorf("cannot create server cert manager %v", err) + } + + return s, nil +} + +func NewAuthServer(t *testing.T, config service.Config) func() { + dialCertManager, err := certManager.NewCertManager(config.Dial) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + + auth, err := newService(config, &tlsConfig) + require.NoError(t, err) + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + err = auth.Serve() + require.NoError(t, err) + }() + + return func() { + auth.Shutdown() + dialCertManager.Close() + wg.Wait() + } +} diff --git a/authorization/uri/uri.go b/authorization/uri/uri.go new file mode 100644 index 000000000..789b889b3 --- /dev/null +++ b/authorization/uri/uri.go @@ -0,0 +1,12 @@ +package uri + +const ( + Base string = "/api/authz" + + AuthorizationCode string = Base + "/code" + AccessToken string = Base + "/token" + OAuthCallback string = Base + "/callback" + JWKs string = "/.well-known/jwks.json" + + Healthcheck string = "/" +) diff --git a/bundle/Dockerfile b/bundle/Dockerfile new file mode 100644 index 000000000..0ed8dc61f --- /dev/null +++ b/bundle/Dockerfile @@ -0,0 +1,86 @@ +FROM golang:1.13.5-alpine3.10 AS build +RUN apk add --no-cache curl git build-base +WORKDIR $GOPATH/src/github.com/go-ocf/cloud +COPY . . +RUN go mod download + +ARG root_directory=$GOPATH/src/github.com/go-ocf/cloud + +#coap-gateway +ARG service=coap-gateway +WORKDIR $root_directory/$service +RUN go build -ldflags "-linkmode external -extldflags -static" -o /go/bin/$service ./cmd/service + +#grpc-gateway +ARG service=grpc-gateway +WORKDIR $root_directory/$service +RUN go build -ldflags "-linkmode external -extldflags -static" -o /go/bin/$service ./cmd/service + +#resource-directory +ARG service=resource-directory +WORKDIR $root_directory/$service +RUN go build -ldflags "-linkmode external -extldflags -static" -o /go/bin/$service ./cmd/service + +#resource-aggregate +ARG service=resource-aggregate +WORKDIR $root_directory/$service +RUN go build -ldflags "-linkmode external -extldflags -static" -o /go/bin/$service ./cmd/service + +#authorization +ARG service=authorization +WORKDIR $root_directory/$service +RUN go build -ldflags "-linkmode external -extldflags -static" -o /go/bin/$service ./cmd/service + +#certificate-generator +ARG service=kit +WORKDIR / +RUN cd $GOPATH/pkg/mod/github.com/go-ocf/kit* && go build -ldflags "-linkmode external -extldflags -static" -o /go/bin/certificate-generator ./cmd/certificate-generator + +#nats +WORKDIR $root_directory +RUN curl -L https://github.com/nats-io/nats-server/releases/download/v2.1.4/nats-server-v2.1.4-linux-amd64.zip -o ./nats-server.zip +RUN mkdir -p ./nats-server +RUN unzip ./nats-server.zip -d ./nats-server +RUN cp ./nats-server/*/nats-server /go/bin/nats-server + +FROM ubuntu:18.04 as service +RUN apt update +RUN apt install -y mongodb-server +COPY --from=build /go/bin/coap-gateway /usr/local/bin/coap-gateway +COPY --from=build /go/bin/grpc-gateway /usr/local/bin/grpc-gateway +COPY --from=build /go/bin/resource-directory /usr/local/bin/resource-directory +COPY --from=build /go/bin/resource-aggregate /usr/local/bin/resource-aggregate +COPY --from=build /go/bin/authorization /usr/local/bin/authorization +COPY --from=build /go/bin/certificate-generator /usr/local/bin/certificate-generator +COPY --from=build /go/bin/nats-server /usr/local/bin/nats-server +COPY run.sh /usr/local/bin/run.sh + +# coap-gateway +ENV COAP_GATEWAY_UNSECURE_PORT=5683 +ENV COAP_GATEWAY_UNSECURE_ADDRESS="0.0.0.0:$COAP_GATEWAY_UNSECURE_PORT" +ENV COAP_GATEWAY_UNSECURE_FQDN="localhost" +ENV COAP_GATEWAY_PORT=5684 +ENV COAP_GATEWAY_ADDRESS="0.0.0.0:$COAP_GATEWAY_PORT" +ENV COAP_GATEWAY_FQDN="localhost" +ENV COAP_GATEWAY_CLOUD_ID="00000000-0000-0000-0000-000000000001" +ENV COAP_GATEWAY_DISABLE_VERIFY_CLIENTS=true + +# grpc-gateway +ENV GRPC_GATEWAY_ADDRESS="0.0.0.0:9084" +ENV GRPC_GATEWAY_DISABLE_VERIFY_CLIENTS=true + +# authorization +ENV AUTHORIZATION_ADDRESS="localhost:9081" +ENV AUTHORIZATION_HTTP_ADDRESS="localhost:9085" + +ENV JWKS_URL="https://$AUTHORIZATION_HTTP_ADDRESS/.well-known/jwks.json" +ENV RESOURCE_AGGREGATE_ADDRESS="localhost:9083" +ENV RESOURCE_DIRECTORY_ADDRESS="localhost:9082" +ENV INITIALIZE_CERITIFICATES=true +ENV MONGO_PATH="/data/db" +ENV MONGO_PORT=10000 +ENV NATS_PORT=10001 +ENV CERITIFICATES_PATH="/data/certs" +ENV LOGS_PATH="/data/log" + +ENTRYPOINT ["/usr/local/bin/run.sh"] \ No newline at end of file diff --git a/bundle/Makefile b/bundle/Makefile new file mode 100644 index 000000000..70e4c90d5 --- /dev/null +++ b/bundle/Makefile @@ -0,0 +1,32 @@ +SHELL = /bin/bash +SERVICE_NAME = $(notdir $(CURDIR)) +LATEST_TAG = vnext +VERSION_TAG = vnext-$(shell git rev-parse --short=7 --verify HEAD) + +default: build + +define build-docker-image + docker build \ + --network=host \ + --tag ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) \ + --tag ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) \ + --target $(1) \ + --file Dockerfile \ + . +endef + + +build-servicecontainer: + $(call build-docker-image,service) + +build: build-servicecontainer + +push: build-servicecontainer + docker push ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) + docker push ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) + +proto/generate: + +.PHONY: build-servicecontainer build push proto/generate + + diff --git a/bundle/README.md b/bundle/README.md new file mode 100644 index 000000000..498a7d412 --- /dev/null +++ b/bundle/README.md @@ -0,0 +1,110 @@ +# Scalable OCF Cloud Hosting / Testing +Being [plugged.in](https://pluggedin.cloud) provides you a complete set of tools and services to manage your devices at scale. Allowing for the processing of real-time device data and interconnection of your devices and applications based on an interoperable standard. Interconnect, monitor and manage your devices in a cloud native way. + +# OCF Cloud Bundle +Provides a simple docker cloud image for **testing purpose**. + +## Features +- [OCF Native Cloud](https://openconnectivity.org/specs/OCF_Device_To_Cloud_Services_Specification_v2.1.0.pdf) +- OAUTH Athorization code is not verified +- [GRPC](https://github.com/go-ocf/cloud/blob/master/grpc-gateway/pb/service.proto) + +## Supported clients +- [iotivity v2+](https://github.com/iotivity/iotivity) +- [iotivity-lite v2+](https://github.com/iotivity/iotivity-lite) + + +## Pull the image +```bash +docker pull ocfcloud/cloud:vnext +``` + +## Configuration +Image can be configured via enviroment variables as argument `-e ENV=VALUE` of command `docker`: +| ENV variable | Type | Description | Default | +| --------- | ----------- | ------- | ------- | +| `COAP_GATEWAY_UNSECURE_PORT` | uint16 | exposed public port for coap-tcp | `"5683"` | +| `COAP_GATEWAY_UNSECURE_ADDRESS` | string | coap-tcp listen address | `"0.0.0.0:5683"` | +| `COAP_GATEWAY_UNSECURE_FQDN` | string | public FQDN for coap-tcp | `localhost` | +| `COAP_GATEWAY_PORT` | uint16 | exposed public port for coaps-tcp | `"5684"` | +| `COAP_GATEWAY_ADDRESS` | string | coaps-tcp listen address | `"0.0.0.0:5684"` | +| `COAP_GATEWAY_FQDN` | string | public FQDN for coaps-tcp | `"localhost"` | +| `COAP_GATEWAY_CLOUD_ID` | string | cloud id | `"00000000-0000-0000-0000-000000000001"` | +| `COAP_GATEWAY_DISABLE_VERIFY_CLIENTS`| bool | disable verifying coap clients certificates | `true` | +| `GRPC_GATEWAY_ADDRESS`| string | secure grpc-tcp listen address | `"0.0.0.0:9084"` | +| `GRPC_GATEWAY_DISABLE_VERIFY_CLIENTS`| bool | disable verifying grpc clients certificates | `true` | +| `INITIALIZE_CERITIFICATES` | bool | initialze certificates | `true` | +| `CERITIFICATES_PATH` | string | path to directory | `"/data/certs"` | +| `MONGO_PATH` | string | path to directory | `"/data/db"` | +| `MONGO_PORT` | uint16 | mongo listen port | `"10000"` | +| `NATS_PORT` | uint16 | nats listen port | `"10001"` | +| `LOGS_PATH` | string | path to directory | `"/data/log"` | + +## Run +```bash +docker run -d --network=host --name=cloud -t ocfcloud/cloud:vnext +``` + +## Device Onboarding +The onboarding values which should be set to the [coapcloudconf](https://github.com/openconnectivityfoundation/cloud-services/blob/c2c/swagger2.0/oic.r.coapcloudconf.swagger.json) device resource are: + +### Unsecured device +| Attribute | Value | +| --------- | ------| +| `apn` | `test` | +| `cis` | `coap+tcp://127.0.0.1:5683` | +| `sid` | `same as is set in COAP_GATEWAY_CLOUD_ID` | +| `at` | `test` | + +### Secured device +| Attribute | Value | +| --------- | ------| +| `apn` | `test`| +| `cis` | `coaps+tcp://127.0.0.1:5684` | +| `sid` | `same as is set in COAP_GATEWAY_CLOUD_ID` | +| `at` | `test` | + +#### Conditions: +- Device must be owned. +- Cloud CA must be set as TRUST CA with subject COAP_GATEWAY_CLOUD_ID in device. +- Cloud CA in PEM: + ```bash + docker exec -it cloud cat CERITIFICATES_PATH/root_ca.crt + ``` +- ACL for Cloud (Subject: COAP_GATEWAY_CLOUD_ID) must be set with full access to all published resources in device. + +## Build a sample device application +```bash +curl https://raw.githubusercontent.com/go-ocf/grpc-gateway/master/device-simulator/patches/devsim.diff -o devsim.diff +git clone https://github.com/iotivity/iotivity-lite.git +cd ./iotivity-lite/port/linux +patch -p1 < ../devsim.diff +make +make clean +make CLOUD=1 SECURE=0 cloud_server +./cloud_server "Test Device" coap+tcp://127.0.0.1:5683 test test +``` + +## Build a COAP client application +To build the client you need to have **golang v1.13+**. +```bash +cd ./client/coap +go build +./coap --help +# gets a resource links of the registered devices from cloud +./coap --signUp test --href /oic/res +``` + +## Build a GRPC client application +To build the client you need to have **golang v1.13+**. +```bash +cd ./client/grpc +go build +./grpc --help +# gets all resources with contents from cloud +./grpc +# gets resources of device with contents from cloud +./grpc --deviceid {deviceID} +# gets devices from cloud +./grpc --getdevices +``` diff --git a/bundle/client/coap/main.go b/bundle/client/coap/main.go new file mode 100644 index 000000000..de1f50905 --- /dev/null +++ b/bundle/client/coap/main.go @@ -0,0 +1,231 @@ +package main + +import ( + "bytes" + "crypto/tls" + "flag" + "fmt" + "log" + "net/url" + "os" + "os/signal" + "syscall" + + "github.com/go-ocf/kit/codec/json" + + "github.com/go-ocf/kit/net" + + coap "github.com/go-ocf/go-coap" + codes "github.com/go-ocf/go-coap/codes" + "github.com/go-ocf/kit/codec/cbor" +) + +type authReq struct { + Accesstoken string `json:"accesstoken"` + DeviceID string `json:"di"` + AuthProvider string `json:"authprovider"` +} + +type authResp struct { + Accesstoken string `json:"accesstoken"` + UID string `json:"uid"` + DeviceID string `json:"di"` + Login bool `json:"login"` +} + +func signUp(co *coap.ClientConn, authreq authReq) authResp { + bw := bytes.NewBuffer(make([]byte, 0, 1024)) + err := cbor.WriteTo(bw, authreq) + if err != nil { + log.Fatalf("cannt encode signup req: %v", err) + } + + resp, err := co.Post("/oic/sec/account", coap.AppCBOR, bw) + if err != nil { + log.Fatalf("error sending request to signup: %v", err) + } + if resp.Code() != codes.Changed { + log.Fatalf("error get coap code for signup: %v", resp.Code()) + } + + var authresp authResp + err = cbor.Decode(resp.Payload(), &authresp) + if err != nil { + log.Fatalf("cannot decode authresp: %v", err) + } + + return authresp +} + +func signIn(co *coap.ClientConn, authresp authResp) { + log.Printf("authresp: \n%v\n", toJSON(authresp)) + + bw := bytes.NewBuffer(make([]byte, 0, 1024)) + err := cbor.WriteTo(bw, authresp) + if err != nil { + log.Fatalf("cannt encode signin req: %v", err) + } + + resp, err := co.Post("/oic/sec/session", coap.AppCBOR, bw) + if err != nil { + log.Fatalf("error sending request to signin: %v", err) + } + if resp.Code() != codes.Changed { + log.Fatalf("error get coap code for sigin: %v", resp.Code()) + } + +} + +func toJSON(v interface{}) string { + d, err := json.Encode(v) + if err != nil { + log.Fatalf("cannot decode rd resp: %v", err) + } + return string(d) +} + +func decodePayload(resp coap.Message) { + buf := fmt.Sprint("-------------------COAP-RESPONSE------------------\n", + "Code: ", resp.Code(), "\n", + "ContentFormat: ", resp.Options(coap.ContentFormat), "\n", + "Payload: ", + ) + if mediaType, ok := resp.Option(coap.ContentFormat).(coap.MediaType); ok { + switch mediaType { + case coap.AppCBOR, coap.AppOcfCbor: + s, err := cbor.ToJSON(resp.Payload()) + if err != nil { + buf = buf + fmt.Sprintf("Cannot encode %v to JSON: %v", resp.Payload(), err) + } else { + buf = buf + fmt.Sprintf("%v\n", s) + } + case coap.TextPlain: + buf = buf + fmt.Sprintf("%v\n", string(resp.Payload())) + case coap.AppJSON: + buf = buf + fmt.Sprintf("%v\n", string(resp.Payload())) + case coap.AppXML: + buf = buf + fmt.Sprintf("%v\n", string(resp.Payload())) + default: + buf = buf + fmt.Sprintf("%v\n", resp.Payload()) + } + } else { + buf = buf + fmt.Sprintf("%v\n", resp.Payload()) + } + log.Printf(buf) +} + +func main() { + addr := flag.String("cis", "coap+tcp://127.0.0.1:5683", "address") + authCode := flag.String("signUp", "", "authcode") + accesstoken := flag.String("signIn", "", "accesstoken") + di := flag.String("di", "testUtility", "device id") + uid := flag.String("uid", "", "user id") + href := flag.String("href", "", "href") + get := flag.Bool("get", true, "get resource(default)") + discover := flag.Bool("discover", true, "discover resources in cloud") + discoverRt := flag.String("rt", "", "resource type") + observe := flag.Bool("observe", false, "observe resource") + update := flag.Bool("update", false, "update resource, content is expceted in stdin") + + contentFormat := flag.Int("contentFormat", int(coap.AppJSON), "contentFormat for update resource") + + flag.Parse() + + u, err := url.Parse(*addr) + if err != nil { + log.Fatalf("cannot parse url %v: %v", *addr, err) + } + address, err := net.ParseURL(u) + if err != nil { + log.Fatalf("cannot parse address %v: %v", *addr, err) + } + + var co *coap.ClientConn + switch address.GetScheme() { + case "coap+tcp": + co, err = coap.Dial("tcp", address.String()) + if err != nil { + log.Fatalf("Error dialing: %v", err) + } + case "coaps+tcp": + co, err = coap.DialTLS("tcp-tls", address.String(), &tls.Config{ + InsecureSkipVerify: true, + }) + if err != nil { + log.Fatalf("Error dialing: %v", err) + } + default: + log.Fatalf("invalid scheme %v of address %v", address.GetScheme(), address) + } + + if err != nil { + log.Fatalf("Error dialing: %v", err) + } + + if *authCode != "" { + authreq := authReq{ + Accesstoken: *authCode, + DeviceID: *di, + AuthProvider: "test", + } + authresp := signUp(co, authreq) + *accesstoken = authresp.Accesstoken + *uid = authresp.UID + authresp.DeviceID = *di + authresp.Login = true + } + if *accesstoken != "" && *uid != "" { + signInReq := authResp{ + Accesstoken: *accesstoken, + UID: *uid, + DeviceID: *di, + Login: true, + } + signIn(co, signInReq) + } + + switch { + case *update: + resp, err := co.Post(*href, coap.MediaType(*contentFormat), os.Stdin) + if err != nil { + log.Fatalf("cannot get value: %v", err) + } + decodePayload(resp) + case *observe: + obs, err := co.Observe(*href, func(req *coap.Request) { + decodePayload(req.Msg) + }) + if err != nil { + log.Fatalf("cannot observe value: %v", err) + } + defer obs.Cancel() + + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + <-sigs + fmt.Println("exiting") + case *discover: + req, err := co.NewGetRequest("/oic/res") + if err != nil { + log.Fatalf("cannot discover value: %v", err) + } + if *discoverRt != "" { + req.SetURIQuery("rt=" + *discoverRt) + } + resp, err := co.Exchange(req) + if err != nil { + log.Fatalf("cannot get value: %v", err) + } + decodePayload(resp) + case *get: + resp, err := co.Get(*href) + if err != nil { + log.Fatalf("cannot get value: %v", err) + } + decodePayload(resp) + default: + if err != nil { + log.Fatal("unknown command") + } + } +} diff --git a/bundle/client/grpc/main.go b/bundle/client/grpc/main.go new file mode 100644 index 000000000..4420a8c5e --- /dev/null +++ b/bundle/client/grpc/main.go @@ -0,0 +1,211 @@ +package main + +import ( + "context" + "crypto/tls" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "net/http" + "os" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + coap "github.com/go-ocf/go-coap" + pbGW "github.com/go-ocf/cloud/grpc-gateway/pb" + "github.com/go-ocf/kit/codec/json" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/kit/net/http/transport" +) + +func toJSON(v interface{}) string { + d, err := json.Encode(v) + if err != nil { + log.Fatalf("cannot decode rd resp: %v", err) + } + return string(d) +} + +func decodePayload(resp *pbGW.Content) { + /* + buf := fmt.Sprint("-------------------COAP-RESPONSE------------------\n", + "Code: ", resp.Code(), "\n", + "ContentFormat: ", resp.Options(coap.ContentFormat), "\n", + "Payload: ", + ) + if mediaType, ok := resp.Option(coap.ContentFormat).(coap.MediaType); ok { + switch mediaType { + case coap.AppCBOR, coap.AppOcfCbor: + var m interface{} + err := codec.NewDecoderBytes(resp.Payload(), new(codec.CborHandle)).Decode(&m) + bw := new(bytes.Buffer) + h := new(codec.JsonHandle) + h.BasicHandle.Canonical = true + err = codec.NewEncoder(bw, h).Encode(m) + if err != nil { + buf = buf + fmt.Sprintf("Cannot encode %v to JSON: %v", m, err) + } else { + buf = buf + fmt.Sprintf("%v\n", bw.String()) + } + case coap.TextPlain: + buf = buf + fmt.Sprintf("%v\n", string(resp.Payload())) + case coap.AppJSON: + buf = buf + fmt.Sprintf("%v\n", string(resp.Payload())) + case coap.AppXML: + buf = buf + fmt.Sprintf("%v\n", string(resp.Payload())) + default: + buf = buf + fmt.Sprintf("%v\n", resp.Payload()) + } + } else { + buf = buf + fmt.Sprintf("%v\n", resp.Payload()) + } + log.Printf(buf) + */ +} + +func main() { + addr := flag.String("addr", "localhost:9084", "address") + accesstoken := flag.String("accesstoken", "", "accesstoken") + authAddr := flag.String("authaddr", "localhost:9085", "authorization serivce address") + deviceID := flag.String("deviceid", "", "deviceID") + href := flag.String("href", "", "href") + get := flag.Bool("get", true, "get resources(default) filtered by deviceid and href") + getDevices := flag.Bool("getdevices", false, "get devices") + //observe := flag.Bool("observe", false, "observe resource") + update := flag.Bool("update", false, "update resource, content is expceted in stdin") + + contentFormat := flag.Int("contentFormat", int(coap.AppJSON), "contentFormat for update resource") + + flag.Parse() + + tlsCfg := tls.Config{ + InsecureSkipVerify: true, + } + if *accesstoken == "" { + t := transport.NewDefaultTransport() + t.TLSClientConfig = &tlsCfg + c := http.Client{ + Transport: t, + } + resp, err := c.Get("https://" + *authAddr + "/api/authz/token") + if err != nil { + log.Fatalf("cannot get access token: %v", err) + } + defer resp.Body.Close() + type at struct { + AccessToken string `json:"access_token"` + } + var a at + err = json.ReadFrom(resp.Body, &a) + if err != nil { + log.Fatalf("cannot read access token: %v", err) + } + *accesstoken = a.AccessToken + } + + conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(credentials.NewTLS(&tlsCfg))) + if err != nil { + log.Fatalf("Error dialing: %v", err) + } + + ocfGW := pbGW.NewGrpcGatewayClient(conn) + //ocfGWHelper := cloud.NewClient(ocfGW) + ctx := kitNetGrpc.CtxWithToken(context.Background(), *accesstoken) + switch { + case *update: + data, err := ioutil.ReadAll(os.Stdin) + if err != nil { + log.Fatalf("cannot read data for update value: %v", err) + } + resp, err := ocfGW.UpdateResourcesValues(ctx, &pbGW.UpdateResourceValuesRequest{ + ResourceId: &pbGW.ResourceId{ + DeviceId: *deviceID, + ResourceLinkHref: *href, + }, + Content: &pbGW.Content{ + ContentType: coap.MediaType(*contentFormat).String(), + Data: data, + }, + }) + if err != nil { + log.Fatalf("cannot update value: %v", err) + } + d, err := json.Encode(resp) + if err != nil { + log.Fatalf("cannot encode device: %v", err) + } + fmt.Println(string(d)) + /* + case *observe: + log.Fatalf("not implemented") + + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + <-sigs + fmt.Println("exiting") + */ + case *getDevices: + getClient, err := ocfGW.GetDevices(ctx, &pbGW.GetDevicesRequest{}) + if err != nil { + log.Fatalf("cannot get devices: %v", err) + } + devices := make([]*pbGW.Device, 0, 4) + for { + resp, err := getClient.Recv() + if err == io.EOF { + break + } + if err != nil { + log.Fatalf("cannot recv device: %v", err) + } + devices = append(devices, resp) + } + d, err := json.Encode(devices) + if err != nil { + log.Fatalf("cannot encode device: %v", err) + } + fmt.Println(string(d)) + case *get: + var deviceIdsFilter []string + if *deviceID != "" { + deviceIdsFilter = append(deviceIdsFilter, *deviceID) + } + var resourceIdsFilter []*pbGW.ResourceId + if *href != "" { + resourceIdsFilter = append(resourceIdsFilter, &pbGW.ResourceId{ + DeviceId: *deviceID, + ResourceLinkHref: *href, + }) + } + getClient, err := ocfGW.RetrieveResourcesValues(ctx, &pbGW.RetrieveResourcesValuesRequest{ + ResourceIdsFilter: resourceIdsFilter, + DeviceIdsFilter: deviceIdsFilter, + }) + if err != nil { + log.Fatalf("cannot retrieve values: %v", err) + } + resources := make([]*pbGW.ResourceValue, 0, 4) + for { + resp, err := getClient.Recv() + if err == io.EOF { + break + } + if err != nil { + log.Fatalf("cannot recv value: %v", err) + } + resources = append(resources, resp) + } + d, err := json.Encode(resources) + if err != nil { + log.Fatalf("cannot encode device: %v", err) + } + fmt.Println(string(d)) + default: + if err != nil { + log.Fatal("unknown command") + } + } +} diff --git a/bundle/run.sh b/bundle/run.sh new file mode 100755 index 000000000..4929da2b0 --- /dev/null +++ b/bundle/run.sh @@ -0,0 +1,224 @@ +#!/usr/bin/env bash +set -e + +# Configure services +export PATH="/usr/local/bin:$PATH" + +export INTERNAL_CERT_DIR_PATH="$CERITIFICATES_PATH/internal" +export GRPC_INTERNAL_CERT_NAME="endpoint.crt" +export GRPC_INTERNAL_CERT_KEY_NAME="endpoint.key" + +export EXTERNAL_CERT_DIR_PATH="$CERITIFICATES_PATH/external" +export COAP_GATEWAY_FILE_CERT_NAME="coap-gateway.crt" +export COAP_GATEWAY_FILE_CERT_KEY_NAME="coap-gateway.key" + +# ROOT CERTS +export CA_POOL_DIR="$CERITIFICATES_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" + +# DIAL CERTS +export DIAL_TYPE="file" +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_TYPE="file" +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" + +export MONGODB_URL="mongodb://localhost:$MONGO_PORT" +export MONGODB_URI="mongodb://localhost:$MONGO_PORT" +export MONGO_URI="mongodb://localhost:$MONGO_PORT" + +export NATS_URL="nats://localhost:$NATS_PORT" + +export AUTH_SERVER_ADDRESS=${AUTHORIZATION_ADDRESS} + +if [ "$INITIALIZE_CERITIFICATES" = "true" ]; then + mkdir -p $CA_POOL_DIR + mkdir -p $INTERNAL_CERT_DIR_PATH + mkdir -p $EXTERNAL_CERT_DIR_PATH + echo "generating CA cert" + certificate-generator --cmd.generateRootCA --outCert=$CA_POOL_CERT_PATH --outKey=$CA_POOL_CERT_KEY_PATH --cert.subject.cn="Root CA" + echo "generating GRPC internal cert" + certificate-generator --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" --signerCert=$CA_POOL_CERT_PATH --signerKey=$CA_POOL_CERT_KEY_PATH + echo "generating COAP-GW cert" + certificate-generator --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 +fi + +mkdir -p $MONGO_PATH +mkdir -p $CERITIFICATES_PATH +mkdir -p $LOGS_PATH + +# nats +nats-server --port $NATS_PORT --tls --tlsverify --tlscert=$DIAL_FILE_CERT_DIR_PATH/$DIAL_FILE_CERT_NAME --tlskey=$DIAL_FILE_CERT_DIR_PATH/$DIAL_FILE_CERT_KEY_NAME --tlscacert=$CA_POOL_CERT_PATH >$LOGS_PATH/nats-server.log 2>&1 & +status=$? +if [ $status -ne 0 ]; then + echo "Failed to start nats-server: $status" + sync + cat $LOGS_PATH/nats-server.log + exit $status +fi + +# mongo +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 --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=$? +if [ $status -ne 0 ]; then + echo "Failed to start mongod: $status" + sync + cat $LOGS_PATH/mongod.log + exit $status +fi + +# authorization +echo "starting authorization" +ADDRESS=${AUTHORIZATION_ADDRESS} HTTP_ADDRESS=${AUTHORIZATION_HTTP_ADDRESS} DEVICE_PROVIDER="test" authorization >$LOGS_PATH/authorization.log 2>&1 & +status=$? +if [ $status -ne 0 ]; then + echo "Failed to start authorization: $status" + sync + cat $LOGS_PATH/authorization.log + exit $status +fi + +# resource-aggregate +echo "starting resource-aggregate" +ADDRESS=${RESOURCE_AGGREGATE_ADDRESS} resource-aggregate >$LOGS_PATH/resource-aggregate.log 2>&1 & +status=$? +if [ $status -ne 0 ]; then + echo "Failed to start resource-aggregate: $status" + sync + cat $LOGS_PATH/resource-aggregate.log + exit $status +fi + +# resource-directory +echo "starting resource-directory" +ADDRESS=${RESOURCE_DIRECTORY_ADDRESS} resource-directory >$LOGS_PATH/resource-directory.log 2>&1 & +status=$? +if [ $status -ne 0 ]; then + echo "Failed to start resource-directory: $status" + sync + cat $LOGS_PATH/resource-directory.log + exit $status +fi + +# coap-gateway-unsecure +echo "starting coap-gateway-unsecure" +ADDRESS=${COAP_GATEWAY_UNSECURE_ADDRESS} LOG_ENABLE_DEBUG=true EXTERNAL_PORT=${COAP_GATEWAY_UNSECURE_PORT} FQDN=${COAP_GATEWAY_UNSECURE_FQDN} coap-gateway >$LOGS_PATH/coap-gateway-unsecure.log 2>&1 & +status=$? +if [ $status -ne 0 ]; then + echo "Failed to start coap-gateway-unsecure: $status" + sync + cat $LOGS_PATH/coap-gateway-unsecure.log + exit $status +fi +coap_gw_unsecure_pid=`ps -ef | grep coap-gateway | grep -v grep | awk '{print $2}'` +if [ "$coap_gw_unsecure_pid" = "" ]; then + echo "Failed to get pid coap-gateway-unsecured" + sync + cat $LOGS_PATH/coap-gateway-unsecure.log + exit 1 +fi + +# coap-gateway-secure +echo "starting coap-gateway-secure" +ADDRESS=${COAP_GATEWAY_ADDRESS} NETWORK="tcp-tls" LOG_ENABLE_DEBUG=true EXTERNAL_PORT=${COAP_GATEWAY_PORT} FQDN=${COAP_GATEWAY_FQDN} LISTEN_FILE_CERT_DIR_PATH=${EXTERNAL_CERT_DIR_PATH} LISTEN_FILE_CERT_NAME=${COAP_GATEWAY_FILE_CERT_NAME} LISTEN_FILE_CERT_KEY_NAME=${COAP_GATEWAY_FILE_CERT_KEY_NAME} LISTEN_FILE_DISABLE_VERIFY_CLIENT_CERTIFICATE=${COAP_GATEWAY_DISABLE_VERIFY_CLIENTS} coap-gateway >$LOGS_PATH/coap-gateway.log 2>&1 & +status=$? +if [ $status -ne 0 ]; then + echo "Failed to start coap-gateway: $status" + sync + cat $LOGS_PATH/coap-gateway.log + exit $status +fi +coap_gw_pid=`ps -ef | grep coap-gateway | grep -v grep | grep -v $coap_gw_unsecure_pid | awk '{print $2}'` +if [ "$coap_gw_pid" = "" ]; then + echo "Failed to get pid coap-gateway" + sync + cat $LOGS_PATH/coap-gateway.log + exit 1 +fi + +# grpc-gateway +echo "starting grpc-gateway" +ADDRESS=${GRPC_GATEWAY_ADDRESS} LISTEN_FILE_DISABLE_VERIFY_CLIENT_CERTIFICATE=${GRPC_GATEWAY_DISABLE_VERIFY_CLIENTS} grpc-gateway >$LOGS_PATH/grpc-gateway.log 2>&1 & +status=$? +if [ $status -ne 0 ]; then + echo "Failed to start grpc-gateway: $status" + sync + cat $LOGS_PATH/grpc-gateway.log + exit $status +fi + + +# Naive check runs checks once a minute to see if either of the processes exited. +# This illustrates part of the heavy lifting you need to do if you want to run +# more than one service in a container. The container exits with an error +# if it detects that either of the processes has exited. +# Otherwise it loops forever, waking up every 60 seconds +while sleep 10; do + ps aux |grep nats-server |grep -q -v grep + if [ $? -ne 0 ]; then + echo "nats-server has already exited." + sync + cat $LOGS_PATH/nats-server.log + exit 1 + fi + ps aux |grep mongod |grep -q -v grep + if [ $? -ne 0 ]; then + echo "mongod has already exited." + sync + cat $LOGS_PATH/mongod.log + exit 1 + fi + ps aux |grep authorization |grep -q -v grep + if [ $? -ne 0 ]; then + echo "authorization has already exited." + sync + cat $LOGS_PATH/authorization.log + exit 1 + fi + ps aux |grep resource-aggregate |grep -q -v grep + if [ $? -ne 0 ]; then + echo "resource-aggregate has already exited." + sync + cat $LOGS_PATH/resource-aggregate.log + exit 1 + fi + ps aux |grep resource-directory |grep -q -v grep + if [ $? -ne 0 ]; then + echo "resource-directory has already exited." + sync + cat $LOGS_PATH/resource-directory.log + exit 1 + fi + ps aux |grep $coap_gw_unsecure_pid |grep -q -v grep + if [ $? -ne 0 ]; then + echo "coap-gateway-unsecure has already exited." + sync + cat $LOGS_PATH/coap-gateway-unsecure.log + exit 1 + fi + ps aux |grep $coap_gw_pid |grep -q -v grep + if [ $? -ne 0 ]; then + echo "coap-gateway has already exited." + sync + cat $LOGS_PATH/coap-gateway.log + exit 1 + fi + ps aux |grep grpc-gateway |grep -q -v grep + if [ $? -ne 0 ]; then + echo "grpc-gateway has already exited." + sync + cat $LOGS_PATH/grpc-gateway.log + exit 1 + fi +done \ No newline at end of file diff --git a/certificate-authority/.dockerignore b/certificate-authority/.dockerignore new file mode 100644 index 000000000..5ed6efd6f --- /dev/null +++ b/certificate-authority/.dockerignore @@ -0,0 +1,6 @@ +.git +.github +.gitignore +.travis.yml +Makefile +vendor diff --git a/certificate-authority/Dockerfile b/certificate-authority/Dockerfile new file mode 100644 index 000000000..4f62854ed --- /dev/null +++ b/certificate-authority/Dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.13.5-alpine3.10 AS build +RUN apk add --no-cache curl git build-base +WORKDIR $GOPATH/src/github.com/go-ocf/cloud/certificate-authority +COPY . . +RUN go mod download +RUN go build -o /go/bin/service ./cmd/service + +FROM alpine:3.11 as service +RUN apk add --no-cache ca-certificates +COPY --from=build /go/bin/service /usr/local/bin/service +ENTRYPOINT ["/usr/local/bin/service"] \ No newline at end of file diff --git a/certificate-authority/Makefile b/certificate-authority/Makefile new file mode 100644 index 000000000..bba00e672 --- /dev/null +++ b/certificate-authority/Makefile @@ -0,0 +1,37 @@ +SHELL = /bin/bash +SERVICE_NAME = $(notdir $(CURDIR)) +LATEST_TAG = vnext +VERSION_TAG = vnext-$(shell git rev-parse --short=7 --verify HEAD) + +default: build + +define build-docker-image + docker build \ + --network=host \ + --tag ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) \ + --tag ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) \ + --target $(1) \ + --file Dockerfile \ + . +endef + +build-servicecontainer: + $(call build-docker-image,service) + +build: build-servicecontainer + +push: build-servicecontainer + docker push ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) + docker push ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) + +proto/generate: + protoc -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf --gogofaster_out=${GOPATH}/src pb/cert.proto + protoc -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf --go_out=plugins=grpc:${GOPATH}/src pb/service.proto + +.PHONY: build-servicecontainer build push proto/generate + + + + + + diff --git a/certificate-authority/README.md b/certificate-authority/README.md new file mode 100644 index 000000000..89a7cdb4d --- /dev/null +++ b/certificate-authority/README.md @@ -0,0 +1,4 @@ +[![codecov](https://codecov.io/gh/go-ocf/certificate-authority/branch/master/graph/badge.svg)](https://codecov.io/gh/go-ocf/certificate-authority) +[![Go Report](https://goreportcard.com/badge/github.com/go-ocf/cloud/certificate-authority)](https://goreportcard.com/report/github.com/go-ocf/cloud/certificate-authority) + +# certificate-authority diff --git a/certificate-authority/acme/refImpl/refImpl.go b/certificate-authority/acme/refImpl/refImpl.go new file mode 100644 index 000000000..7b60ef115 --- /dev/null +++ b/certificate-authority/acme/refImpl/refImpl.go @@ -0,0 +1,157 @@ +package refImpl + +import ( + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "encoding/json" + "encoding/pem" + "fmt" + "net" + "net/http" + "time" + + kitNet "github.com/go-ocf/kit/net" + "github.com/go-ocf/kit/security/generateCertificate" + "github.com/go-ocf/cloud/certificate-authority/acme/service" + + "github.com/go-ocf/kit/log" + "github.com/go-ocf/kit/security" + ocfSigner "github.com/go-ocf/kit/security/signer" +) + +type Config struct { + Log log.Config + Addr string `envconfig:"ADDR" default:"0.0.0.0:10443"` + FQDN string `envconfig:"FQDN" default:"acme.ca.ocf.cloud"` + Domains []string `envconfig:"DOMAINS"` + SignerCertificate string `envconfig:"SIGNER_CERTIFICATE" required:"True"` + SignerPrivateKey string `envconfig:"SIGNER_PRIVATE_KEY" required:"True"` + SignerValidDuration time.Duration `envconfig:"SIGNER_VALID_DURATION" default:"87600h"` +} + +type RefImpl struct { + listenTLS tls.Config + listener net.Listener + server *http.Server + selfSigner *selfSigner +} + +// NewRefImplFromConfig creates RegisterGrpcGatewayServer with all dependencies. +func NewRefImplFromConfig(config Config) (*RefImpl, error) { + var addr kitNet.Addr + addr, err := kitNet.ParseString("", config.Addr) + if err != nil { + return nil, err + } + + chainCerts, err := security.LoadX509(config.SignerCertificate) + if err != nil { + return nil, err + } + privateKey, err := security.LoadX509PrivateKey(config.SignerPrivateKey) + if err != nil { + return nil, err + } + + listenCert, err := getSelfCertificate(config.FQDN, config.Domains, chainCerts, privateKey) + if err != nil { + return nil, err + } + + // Create the main listener. + l, err := tls.Listen("tcp", config.Addr, &tls.Config{ + Certificates: []tls.Certificate{listenCert}, + ClientAuth: tls.NoClientCert, + }) + if err != nil { + return nil, err + } + + signer := ocfSigner.NewBasicCertificateSigner(chainCerts, privateKey, config.SignerValidDuration) + + selfSigner := &selfSigner{ + pemSigner: signer, + } + + h, err := service.NewHandler(config.FQDN, addr.GetPort(), []service.Signer{selfSigner}) + if err != nil { + return nil, err + } + + return &RefImpl{ + server: &http.Server{ + Handler: h, + }, + listener: l, + }, nil +} + +//String return string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} + +func Init(config Config) (*RefImpl, error) { + log.Setup(config.Log) + log.Info(config.String()) + + impl, err := NewRefImplFromConfig(config) + if err != nil { + return nil, err + } + + return impl, nil +} + +func getSelfCertificate(FQDN string, domains []string, chainCerts []*x509.Certificate, privateKey *ecdsa.PrivateKey) (tls.Certificate, error) { + var cfg generateCertificate.Configuration + cfg.Subject.CommonName = FQDN + cfg.SubjectAlternativeName.DNSNames = domains + cfg.ExtensionKeyUsages = []string{"server", "client"} + cfg.ValidFor = time.Hour * 86400 + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return tls.Certificate{}, err + } + + pemCert, err := generateCertificate.GenerateCert(cfg, priv, chainCerts, privateKey) + if err != nil { + return tls.Certificate{}, err + } + b, err := x509.MarshalECPrivateKey(priv) + if err != nil { + return tls.Certificate{}, err + } + pemKey := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: b}) + return tls.X509KeyPair(pemCert, pemKey) +} + +func (r *RefImpl) Serve() error { + return r.server.Serve(r.listener) +} + +func (r *RefImpl) Shutdown() { + r.listener.Close() + r.server.Shutdown(context.Background()) +} + +type signer = interface { + Sign(ctx context.Context, csr []byte) ([]byte, error) +} + +type selfSigner struct { + pemSigner signer +} + +func (s *selfSigner) ID() string { + return "proxy" +} + +func (s *selfSigner) Sign(ctx context.Context, csr []byte) ([]byte, error) { + return s.pemSigner.Sign(ctx, csr) +} diff --git a/certificate-authority/acme/refImpl/refImpl_test.go b/certificate-authority/acme/refImpl/refImpl_test.go new file mode 100644 index 000000000..dd896cd5c --- /dev/null +++ b/certificate-authority/acme/refImpl/refImpl_test.go @@ -0,0 +1,98 @@ +package refImpl + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestInit(t *testing.T) { + dir, err := ioutil.TempDir("", "gotesttmp") + require.NoError(t, err) + defer os.RemoveAll(dir) + var cfg Config + cfg = testSignerCerts(t, dir, cfg) + + type args struct { + config Config + } + tests := []struct { + name string + args args + wantServer bool + wantErr bool + }{ + { + name: "invalid", + wantErr: true, + }, + { + name: "valid", + args: args{ + config: Config{ + SignerCertificate: cfg.SignerCertificate, + SignerPrivateKey: cfg.SignerPrivateKey, + SignerValidDuration: time.Hour * 10, + Addr: ":1234", + }, + }, + wantServer: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Init(tt.args.config) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + if tt.wantServer { + assert.NotEmpty(t, got) + } else { + assert.Empty(t, got) + + } + }) + } +} + +func testSignerCerts(t *testing.T, dir string, cfg Config) Config { + crt := filepath.Join(dir, "cert.crt") + if err := ioutil.WriteFile(crt, IdentityIntermediateCA, 0600); err != nil { + assert.NoError(t, err) + } + crtKey := filepath.Join(dir, "cert.key") + if err := ioutil.WriteFile(crtKey, IdentityIntermediateCAKey, 0600); err != nil { + assert.NoError(t, err) + } + cfg.SignerCertificate = crt + cfg.SignerPrivateKey = crtKey + return cfg +} + +var ( + IdentityIntermediateCA = []byte(`-----BEGIN CERTIFICATE----- +MIIBczCCARmgAwIBAgIRANntjEpzu9krzL0EG6fcqqgwCgYIKoZIzj0EAwIwETEP +MA0GA1UEAxMGUm9vdENBMCAXDTE5MDcxOTIwMzczOVoYDzIxMTkwNjI1MjAzNzM5 +WjAZMRcwFQYDVQQDEw5JbnRlcm1lZGlhdGVDQTBZMBMGByqGSM49AgEGCCqGSM49 +AwEHA0IABKw1/6WHFcWtw67hH5DzoZvHgA0suC6IYLKms4IP/pds9wU320eDaENo +5860TOyKrGn7vW/cj/OVe2Dzr4KSFVijSDBGMA4GA1UdDwEB/wQEAwIBBjATBgNV +HSUEDDAKBggrBgEFBQcDATASBgNVHRMBAf8ECDAGAQH/AgEAMAsGA1UdEQQEMAKC +ADAKBggqhkjOPQQDAgNIADBFAiEAgPtnYpgwxmPhN0Mo8VX582RORnhcdSHMzFjh +P/li1WwCIFVVWBOrfBnTt7A6UfjP3ljAyHrJERlMauQR+tkD/aqm +-----END CERTIFICATE----- +`) + IdentityIntermediateCAKey = []byte(`-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIPF4DPvFeiRL1G0ROd6MosoUGnvIG/2YxH0CbHwnLKxqoAoGCCqGSM49 +AwEHoUQDQgAErDX/pYcVxa3DruEfkPOhm8eADSy4Lohgsqazgg/+l2z3BTfbR4No +Q2jnzrRM7Iqsafu9b9yP85V7YPOvgpIVWA== +-----END EC PRIVATE KEY----- +`) +) diff --git a/certificate-authority/acme/service/authority.go b/certificate-authority/acme/service/authority.go new file mode 100644 index 000000000..ea57004b6 --- /dev/null +++ b/certificate-authority/acme/service/authority.go @@ -0,0 +1,101 @@ +package service + +import ( + "context" + "crypto/x509" + "encoding/pem" + "fmt" + "strings" + + "github.com/smallstep/certificates/authority/provisioner" +) + +type Signer = interface { + Sign(ctx context.Context, csr []byte) (certChain []byte, err error) + ID() string +} + +type authority struct { + signers map[string]Signer +} + +func loadPEM(pemCert []byte) ([]*x509.Certificate, error) { + cert := make([]*x509.Certificate, 0, 4) + for { + if len(pemCert) == 0 { + break + } + b, rest := pem.Decode(pemCert) + if b == nil { + return nil, fmt.Errorf("cannot decode pem of cert") + } + crt, err := x509.ParseCertificate(b.Bytes) + if err != nil { + return nil, err + } + cert = append(cert, crt) + pemCert = rest + } + if len(cert) == 0 { + return nil, fmt.Errorf("cert not found") + } + return cert, nil +} + +// Sign creates a signed certificate from a certificate signing request. +func (s *authority) Sign(cr *x509.CertificateRequest, opts provisioner.Options, signOpts ...provisioner.SignOption) (*x509.Certificate, *x509.Certificate, error) { + var ID string + var ctx context.Context + for _, s := range signOpts { + switch v := s.(type) { + case ProvisionerName: + ID = string(v) + case context.Context: + ctx = v + } + } + if ctx == nil { + return nil, nil, fmt.Errorf("unknown context for sign") + } + + signer, ok := s.signers[ID] + if !ok { + return nil, nil, fmt.Errorf("unknown ID %v of provisioner", ID) + } + + pemCSR := pem.EncodeToMemory(&pem.Block{ + Type: "CERTIFICATE REQUEST", + Bytes: cr.Raw, + }) + pemCert, err := signer.Sign(ctx, pemCSR) + if err != nil { + return nil, nil, fmt.Errorf("cannot sign cert for %v: %v", ID, err) + } + + certs, err := loadPEM(pemCert) + if err != nil { + return nil, nil, fmt.Errorf("cannot load cert for %v: %v", ID, err) + } + + if len(certs) > 1 { + return certs[0], certs[1], nil + } + return certs[0], nil, nil +} + +// LoadProvisionerByID calls out to the SignAuthority interface to load a +// provisioner by ID. +func (s *authority) LoadProvisionerByID(ID string) (provisioner.Interface, error) { + fmt.Println("SignAuthority.LoadProvisionerByID", ID) + + v := strings.Split(ID, "/") + if len(v) < 2 { + return nil, fmt.Errorf("invalid ID %v of provisioner", ID) + } + signer, ok := s.signers[v[1]] + if !ok { + return nil, fmt.Errorf("unknown ID %v of provisioner", ID) + } + + return ACME{ProvisionerName: ProvisionerName(signer.ID())}, nil +} diff --git a/certificate-authority/acme/service/handler.go b/certificate-authority/acme/service/handler.go new file mode 100644 index 000000000..400b65bcc --- /dev/null +++ b/certificate-authority/acme/service/handler.go @@ -0,0 +1,47 @@ +package service + +import ( + "net/http" + "strconv" + + "github.com/go-chi/chi" + "github.com/go-chi/chi/middleware" + "github.com/smallstep/certificates/acme" + acmeAPI "github.com/smallstep/certificates/acme/api" + "github.com/smallstep/nosql" +) + +func NewHandler(FQDN string, port uint16, signers []Signer) (http.Handler, error) { + // Using chi as the main router + mux := chi.NewRouter() + handler := http.Handler(mux) + // A good base middleware stack + mux.Use(middleware.RequestID) + mux.Use(middleware.RealIP) + mux.Use(middleware.Logger) + mux.Use(middleware.Recoverer) + + sigs := make(map[string]Signer) + for _, s := range signers { + sigs[s.ID()] = s + } + + auth := authority{ + signers: sigs, + } + + db, err := nosql.New("badger", "acmeDB") + if err != nil { + return nil, err + } + + dns := FQDN + ":" + strconv.FormatUint(uint64(port), 10) + prefix := "acme" + acmeAuth := acme.NewAuthority(db, dns, prefix, &auth) + acmeRouterHandler := acmeAPI.New(acmeAuth) + mux.Route("/"+prefix, func(r chi.Router) { + acmeRouterHandler.Route(r) + }) + + return handler, nil +} diff --git a/certificate-authority/acme/service/provisioner.go b/certificate-authority/acme/service/provisioner.go new file mode 100644 index 000000000..cad0c502d --- /dev/null +++ b/certificate-authority/acme/service/provisioner.go @@ -0,0 +1,66 @@ +package service + +import ( + "context" + "crypto/x509" + "errors" + + "github.com/smallstep/certificates/authority/provisioner" +) + +// ACME is the acme provisioner type, an entity that can authorize the ACME +// provisioning flow. +type ACME struct { + ProvisionerName ProvisionerName +} + +// ProvisionerName provides name of provision +type ProvisionerName string + +// GetID returns the provisioner unique identifier. +func (p ACME) GetID() string { + return "acme/" + string(p.ProvisionerName) +} + +// GetTokenID returns the identifier of the token. +func (p ACME) GetTokenID(ott string) (string, error) { + return "", errors.New("acme provisioner does not implement GetTokenID") +} + +// GetName returns the name of the provisioner. +func (p ACME) GetName() string { + return string(p.ProvisionerName) +} + +// GetType returns the type of provisioner. +func (p ACME) GetType() provisioner.Type { + return provisioner.TypeACME +} + +// GetEncryptedKey returns the base provisioner encrypted key if it's defined. +func (p ACME) GetEncryptedKey() (string, string, bool) { + return "", "", false +} + +// Init initializes and validates the fields of a JWK type. +func (ACME) Init(config provisioner.Config) error { + return nil +} + +// AuthorizeSign validates the given token. +func (a ACME) AuthorizeSign(ctx context.Context, token string) ([]provisioner.SignOption, error) { + return []provisioner.SignOption{ + a.ProvisionerName, + ctx, + }, nil +} + +// AuthorizeRenewal is not implemented for the ACME provisioner. +func (ACME) AuthorizeRenewal(cert *x509.Certificate) error { + return nil +} + +// AuthorizeRevoke is not implemented yet for the ACME provisioner. +func (ACME) AuthorizeRevoke(token string) error { + return nil +} diff --git a/certificate-authority/cmd/acme-service/main.go b/certificate-authority/cmd/acme-service/main.go new file mode 100644 index 000000000..369c1a5f2 --- /dev/null +++ b/certificate-authority/cmd/acme-service/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "github.com/go-ocf/cloud/certificate-authority/acme/refImpl" + "github.com/go-ocf/kit/log" + "github.com/kelseyhightower/envconfig" +) + +func main() { + var config refImpl.Config + if err := envconfig.Process("", &config); err != nil { + log.Fatalf("cannot parse configuration: %v", err) + } + server, err := refImpl.Init(config) + if err != nil { + log.Fatalf("cannot init server: %v", err) + } + if err := server.Serve(); err != nil { + log.Fatalf("unexpected ends: %v", err) + } +} diff --git a/certificate-authority/cmd/service/main.go b/certificate-authority/cmd/service/main.go new file mode 100644 index 000000000..54aa11a84 --- /dev/null +++ b/certificate-authority/cmd/service/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "github.com/go-ocf/cloud/certificate-authority/refImpl" + "github.com/go-ocf/kit/log" + "github.com/kelseyhightower/envconfig" +) + +func main() { + var config refImpl.Config + if err := envconfig.Process("", &config); err != nil { + log.Fatalf("cannot parse configuration: %v", err) + } + server, err := refImpl.Init(config) + if err != nil { + log.Fatalf("cannot init server: %v", err) + } + if err := server.Serve(); err != nil { + log.Fatalf("unexpected ends: %v", err) + } +} diff --git a/certificate-authority/pb/cert.pb.go b/certificate-authority/pb/cert.pb.go new file mode 100644 index 000000000..b1db445bb --- /dev/null +++ b/certificate-authority/pb/cert.pb.go @@ -0,0 +1,500 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: pb/cert.proto + +package pb + +import ( + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type SignCertificateRequest struct { + CertificateSigningRequest []byte `protobuf:"bytes,1,opt,name=certificate_signing_request,json=certificateSigningRequest,proto3" json:"certificate_signing_request,omitempty"` +} + +func (m *SignCertificateRequest) Reset() { *m = SignCertificateRequest{} } +func (m *SignCertificateRequest) String() string { return proto.CompactTextString(m) } +func (*SignCertificateRequest) ProtoMessage() {} +func (*SignCertificateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_490d2f57cafd3ca8, []int{0} +} +func (m *SignCertificateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignCertificateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignCertificateRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SignCertificateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignCertificateRequest.Merge(m, src) +} +func (m *SignCertificateRequest) XXX_Size() int { + return m.Size() +} +func (m *SignCertificateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SignCertificateRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SignCertificateRequest proto.InternalMessageInfo + +func (m *SignCertificateRequest) GetCertificateSigningRequest() []byte { + if m != nil { + return m.CertificateSigningRequest + } + return nil +} + +type SignCertificateResponse struct { + Certificate []byte `protobuf:"bytes,1,opt,name=certificate,proto3" json:"certificate,omitempty"` +} + +func (m *SignCertificateResponse) Reset() { *m = SignCertificateResponse{} } +func (m *SignCertificateResponse) String() string { return proto.CompactTextString(m) } +func (*SignCertificateResponse) ProtoMessage() {} +func (*SignCertificateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_490d2f57cafd3ca8, []int{1} +} +func (m *SignCertificateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignCertificateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignCertificateResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SignCertificateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignCertificateResponse.Merge(m, src) +} +func (m *SignCertificateResponse) XXX_Size() int { + return m.Size() +} +func (m *SignCertificateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SignCertificateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SignCertificateResponse proto.InternalMessageInfo + +func (m *SignCertificateResponse) GetCertificate() []byte { + if m != nil { + return m.Certificate + } + return nil +} + +func init() { + proto.RegisterType((*SignCertificateRequest)(nil), "ocf.cloud.certificateauthority.pb.SignCertificateRequest") + proto.RegisterType((*SignCertificateResponse)(nil), "ocf.cloud.certificateauthority.pb.SignCertificateResponse") +} + +func init() { proto.RegisterFile("pb/cert.proto", fileDescriptor_490d2f57cafd3ca8) } + +var fileDescriptor_490d2f57cafd3ca8 = []byte{ + // 237 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2d, 0x48, 0xd2, 0x4f, + 0x4e, 0x2d, 0x2a, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x52, 0xcc, 0x4f, 0x4e, 0xd3, 0x4b, + 0xce, 0xc9, 0x2f, 0x4d, 0xd1, 0x03, 0x89, 0x66, 0xa6, 0x65, 0x26, 0x27, 0x96, 0xa4, 0x26, 0x96, + 0x96, 0x64, 0xe4, 0x17, 0x65, 0x96, 0x54, 0xea, 0x15, 0x24, 0x49, 0xe9, 0xa6, 0x67, 0x96, 0x64, + 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0xa7, 0xe7, 0xa7, 0xe7, 0xeb, 0x83, 0x75, 0x26, 0x95, + 0xa6, 0x81, 0x79, 0x60, 0x0e, 0x98, 0x05, 0x31, 0x51, 0x29, 0x82, 0x4b, 0x2c, 0x38, 0x33, 0x3d, + 0xcf, 0x19, 0x61, 0x5a, 0x50, 0x6a, 0x61, 0x69, 0x6a, 0x71, 0x89, 0x90, 0x1d, 0x97, 0x34, 0x92, + 0x1d, 0xf1, 0xc5, 0x99, 0xe9, 0x79, 0x99, 0x79, 0xe9, 0xf1, 0x45, 0x10, 0x69, 0x09, 0x46, 0x05, + 0x46, 0x0d, 0x9e, 0x20, 0x49, 0x24, 0x25, 0xc1, 0x10, 0x15, 0x50, 0xfd, 0x4a, 0xd6, 0x5c, 0xe2, + 0x18, 0x26, 0x17, 0x17, 0xe4, 0xe7, 0x15, 0xa7, 0x0a, 0x29, 0x70, 0x71, 0x23, 0xe9, 0x83, 0x1a, + 0x85, 0x2c, 0xe4, 0x14, 0x78, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, + 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0xe6, + 0x28, 0xfe, 0xd3, 0xcd, 0x4f, 0x4e, 0xd3, 0xcf, 0x4f, 0x4e, 0xd3, 0x05, 0x07, 0x8a, 0x3e, 0x92, + 0x11, 0xba, 0xf0, 0x50, 0xd1, 0x2f, 0x48, 0xb2, 0x2e, 0x48, 0x4a, 0x62, 0x03, 0x7b, 0xd8, 0x18, + 0x10, 0x00, 0x00, 0xff, 0xff, 0x6c, 0x99, 0x06, 0xfe, 0x53, 0x01, 0x00, 0x00, +} + +func (m *SignCertificateRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SignCertificateRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignCertificateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.CertificateSigningRequest) > 0 { + i -= len(m.CertificateSigningRequest) + copy(dAtA[i:], m.CertificateSigningRequest) + i = encodeVarintCert(dAtA, i, uint64(len(m.CertificateSigningRequest))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SignCertificateResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SignCertificateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignCertificateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Certificate) > 0 { + i -= len(m.Certificate) + copy(dAtA[i:], m.Certificate) + i = encodeVarintCert(dAtA, i, uint64(len(m.Certificate))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintCert(dAtA []byte, offset int, v uint64) int { + offset -= sovCert(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *SignCertificateRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.CertificateSigningRequest) + if l > 0 { + n += 1 + l + sovCert(uint64(l)) + } + return n +} + +func (m *SignCertificateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Certificate) + if l > 0 { + n += 1 + l + sovCert(uint64(l)) + } + return n +} + +func sovCert(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozCert(x uint64) (n int) { + return sovCert(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *SignCertificateRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCert + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignCertificateRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignCertificateRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CertificateSigningRequest", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCert + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCert + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCert + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CertificateSigningRequest = append(m.CertificateSigningRequest[:0], dAtA[iNdEx:postIndex]...) + if m.CertificateSigningRequest == nil { + m.CertificateSigningRequest = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCert(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCert + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthCert + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SignCertificateResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCert + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignCertificateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignCertificateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Certificate", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCert + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthCert + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthCert + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Certificate = append(m.Certificate[:0], dAtA[iNdEx:postIndex]...) + if m.Certificate == nil { + m.Certificate = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCert(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCert + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthCert + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipCert(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCert + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCert + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCert + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthCert + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupCert + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthCert + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthCert = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowCert = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupCert = fmt.Errorf("proto: unexpected end of group") +) diff --git a/certificate-authority/pb/cert.proto b/certificate-authority/pb/cert.proto new file mode 100644 index 000000000..47d49d009 --- /dev/null +++ b/certificate-authority/pb/cert.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package ocf.cloud.certificateauthority.pb; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +option go_package = "github.com/go-ocf/cloud/certificate-authority/pb;pb"; + +message SignCertificateRequest { + bytes certificate_signing_request = 1; // PEM format +} + +message SignCertificateResponse { + bytes certificate = 1; // PEM format +} diff --git a/certificate-authority/pb/service.pb.go b/certificate-authority/pb/service.pb.go new file mode 100644 index 000000000..212e60e4c --- /dev/null +++ b/certificate-authority/pb/service.pb.go @@ -0,0 +1,170 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: pb/service.proto + +package pb + +import ( + context "context" + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +func init() { proto.RegisterFile("pb/service.proto", fileDescriptor_6ff5ab49d8a5fcc4) } + +var fileDescriptor_6ff5ab49d8a5fcc4 = []byte{ + // 221 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x28, 0x48, 0xd2, 0x2f, + 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x52, 0xcc, 0x4f, + 0x4e, 0xd3, 0x4b, 0xce, 0xc9, 0x2f, 0x4d, 0xd1, 0x4b, 0x4e, 0x2d, 0x2a, 0xc9, 0x4c, 0xcb, 0x4c, + 0x4e, 0x2c, 0x49, 0x4d, 0x2c, 0x2d, 0xc9, 0xc8, 0x2f, 0xca, 0x2c, 0xa9, 0xd4, 0x2b, 0x48, 0x92, + 0xd2, 0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0x4f, 0xcf, + 0xd7, 0x07, 0xeb, 0x4c, 0x2a, 0x4d, 0x03, 0xf3, 0xc0, 0x1c, 0x30, 0x0b, 0x62, 0xa2, 0x94, 0x3d, + 0x8a, 0x72, 0xdd, 0xfc, 0xe4, 0x34, 0xfd, 0xfc, 0xe4, 0x34, 0x5d, 0xb0, 0x1d, 0xfa, 0x48, 0x76, + 0xe8, 0xc2, 0x2d, 0xd1, 0x2f, 0x48, 0x02, 0x4b, 0x40, 0x0c, 0x30, 0xda, 0xc1, 0xc4, 0x25, 0xe2, + 0x8c, 0x50, 0xe7, 0x08, 0x53, 0x26, 0x34, 0x89, 0x91, 0x4b, 0x3c, 0x38, 0x33, 0x3d, 0xcf, 0x33, + 0x25, 0x35, 0xaf, 0x24, 0xb3, 0xa4, 0x12, 0x49, 0x91, 0x90, 0xa5, 0x1e, 0x41, 0x8f, 0xe8, 0x81, + 0xf4, 0x22, 0xe9, 0x09, 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x91, 0xb2, 0x22, 0x47, 0x6b, 0x71, + 0x41, 0x7e, 0x5e, 0x71, 0xaa, 0x12, 0x83, 0x50, 0x17, 0x23, 0x17, 0x3f, 0x9a, 0xec, 0x80, 0x39, + 0xc6, 0xc9, 0x32, 0xca, 0x9c, 0x9c, 0xd0, 0xb7, 0x2e, 0x48, 0x4a, 0x62, 0x03, 0x07, 0xbe, 0x31, + 0x20, 0x00, 0x00, 0xff, 0xff, 0xd1, 0x6e, 0x00, 0x16, 0x23, 0x02, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// CertificateAuthorityClient is the client API for CertificateAuthority service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type CertificateAuthorityClient interface { + // SignIdentityCertificate sends a Identity Certificate Signing Request to the certificate authority + // and obtains a signed certificate. Both in the PEM format. It adds EKU: '1.3.6.1.4.1.44924.1.6' . + SignIdentityCertificate(ctx context.Context, in *SignCertificateRequest, opts ...grpc.CallOption) (*SignCertificateResponse, error) + // SignCertificate sends a Certificate Signing Request to the certificate authority + // and obtains a signed certificate. Both in the PEM format. + SignCertificate(ctx context.Context, in *SignCertificateRequest, opts ...grpc.CallOption) (*SignCertificateResponse, error) +} + +type certificateAuthorityClient struct { + cc *grpc.ClientConn +} + +func NewCertificateAuthorityClient(cc *grpc.ClientConn) CertificateAuthorityClient { + return &certificateAuthorityClient{cc} +} + +func (c *certificateAuthorityClient) SignIdentityCertificate(ctx context.Context, in *SignCertificateRequest, opts ...grpc.CallOption) (*SignCertificateResponse, error) { + out := new(SignCertificateResponse) + err := c.cc.Invoke(ctx, "/ocf.cloud.certificateauthority.pb.CertificateAuthority/SignIdentityCertificate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *certificateAuthorityClient) SignCertificate(ctx context.Context, in *SignCertificateRequest, opts ...grpc.CallOption) (*SignCertificateResponse, error) { + out := new(SignCertificateResponse) + err := c.cc.Invoke(ctx, "/ocf.cloud.certificateauthority.pb.CertificateAuthority/SignCertificate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// CertificateAuthorityServer is the server API for CertificateAuthority service. +type CertificateAuthorityServer interface { + // SignIdentityCertificate sends a Identity Certificate Signing Request to the certificate authority + // and obtains a signed certificate. Both in the PEM format. It adds EKU: '1.3.6.1.4.1.44924.1.6' . + SignIdentityCertificate(context.Context, *SignCertificateRequest) (*SignCertificateResponse, error) + // SignCertificate sends a Certificate Signing Request to the certificate authority + // and obtains a signed certificate. Both in the PEM format. + SignCertificate(context.Context, *SignCertificateRequest) (*SignCertificateResponse, error) +} + +// UnimplementedCertificateAuthorityServer can be embedded to have forward compatible implementations. +type UnimplementedCertificateAuthorityServer struct { +} + +func (*UnimplementedCertificateAuthorityServer) SignIdentityCertificate(ctx context.Context, req *SignCertificateRequest) (*SignCertificateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SignIdentityCertificate not implemented") +} +func (*UnimplementedCertificateAuthorityServer) SignCertificate(ctx context.Context, req *SignCertificateRequest) (*SignCertificateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SignCertificate not implemented") +} + +func RegisterCertificateAuthorityServer(s *grpc.Server, srv CertificateAuthorityServer) { + s.RegisterService(&_CertificateAuthority_serviceDesc, srv) +} + +func _CertificateAuthority_SignIdentityCertificate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SignCertificateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CertificateAuthorityServer).SignIdentityCertificate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocf.cloud.certificateauthority.pb.CertificateAuthority/SignIdentityCertificate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CertificateAuthorityServer).SignIdentityCertificate(ctx, req.(*SignCertificateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _CertificateAuthority_SignCertificate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SignCertificateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(CertificateAuthorityServer).SignCertificate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocf.cloud.certificateauthority.pb.CertificateAuthority/SignCertificate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(CertificateAuthorityServer).SignCertificate(ctx, req.(*SignCertificateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _CertificateAuthority_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ocf.cloud.certificateauthority.pb.CertificateAuthority", + HandlerType: (*CertificateAuthorityServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SignIdentityCertificate", + Handler: _CertificateAuthority_SignIdentityCertificate_Handler, + }, + { + MethodName: "SignCertificate", + Handler: _CertificateAuthority_SignCertificate_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "pb/service.proto", +} diff --git a/certificate-authority/pb/service.proto b/certificate-authority/pb/service.proto new file mode 100644 index 000000000..3c68dac8c --- /dev/null +++ b/certificate-authority/pb/service.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package ocf.cloud.certificateauthority.pb; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/go-ocf/cloud/certificate-authority/pb/cert.proto"; + +option go_package = "github.com/go-ocf/cloud/certificate-authority/pb;pb"; + +service CertificateAuthority { + // SignIdentityCertificate sends a Identity Certificate Signing Request to the certificate authority + // and obtains a signed certificate. Both in the PEM format. It adds EKU: '1.3.6.1.4.1.44924.1.6' . + rpc SignIdentityCertificate(SignCertificateRequest) returns (SignCertificateResponse) {} + + // SignCertificate sends a Certificate Signing Request to the certificate authority + // and obtains a signed certificate. Both in the PEM format. + rpc SignCertificate(SignCertificateRequest) returns (SignCertificateResponse) {} +} diff --git a/certificate-authority/refImpl/refImpl.go b/certificate-authority/refImpl/refImpl.go new file mode 100644 index 000000000..d6524c89b --- /dev/null +++ b/certificate-authority/refImpl/refImpl.go @@ -0,0 +1,91 @@ +package refImpl + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/go-ocf/kit/security/certManager" + + "github.com/go-ocf/cloud/certificate-authority/pb" + "github.com/go-ocf/cloud/certificate-authority/service" + "github.com/go-ocf/kit/log" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +type Config struct { + Log log.Config + Service service.Config + Signer service.SignerConfig + Listen certManager.Config `envconfig:"LISTEN"` + JwksURL string `envconfig:"JWKS_URL" required:"True"` +} + +type RefImpl struct { + handle *service.RequestHandler + server *kitNetGrpc.Server + listenCertManager certManager.CertManager +} + +// NewRequestHandlerFromConfig creates RegisterGrpcGatewayServer with all dependencies. +func NewRefImplFromConfig(config Config, auth kitNetGrpc.AuthInterceptors) (*RefImpl, error) { + listenCertManager, err := certManager.NewCertManager(config.Listen) + if err != nil { + return nil, fmt.Errorf("cannot create listen cert manager %v", err) + } + + serverTLSConfig := listenCertManager.GetServerTLSConfig() + svr, err := kitNetGrpc.NewServer(config.Service.Addr, grpc.Creds(credentials.NewTLS(&serverTLSConfig)), auth.Stream(), auth.Unary()) + if err != nil { + listenCertManager.Close() + return nil, err + } + + handler, err := service.NewRequestHandlerFromConfig(config.Signer) + if err != nil { + return nil, err + } + return &RefImpl{ + handle: handler, + listenCertManager: listenCertManager, + server: svr, + }, nil +} + +//String return string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} + +func Init(config Config) (*RefImpl, error) { + auth := kitNetGrpc.MakeAuthInterceptors(func(ctx context.Context, method string) (context.Context, error) { + return ctx, nil + }) + return InitWithAuth(config, auth) +} + +func InitWithAuth(config Config, auth kitNetGrpc.AuthInterceptors) (*RefImpl, error) { + log.Setup(config.Log) + log.Info(config.String()) + + impl, err := NewRefImplFromConfig(config, auth) + if err != nil { + return nil, err + } + + pb.RegisterCertificateAuthorityServer(impl.server.Server, impl.handle) + + return impl, nil +} + +func (r *RefImpl) Serve() error { + return r.server.Serve() +} + +func (r *RefImpl) Shutdown() { + r.server.Stop() + r.listenCertManager.Close() +} diff --git a/certificate-authority/refImpl/refImpl_test.go b/certificate-authority/refImpl/refImpl_test.go new file mode 100644 index 000000000..a9aed865c --- /dev/null +++ b/certificate-authority/refImpl/refImpl_test.go @@ -0,0 +1,60 @@ +package refImpl + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestInit(t *testing.T) { + var config Config + dir, err := ioutil.TempDir("", "gotesttmp") + require.NoError(t, err) + defer os.RemoveAll(dir) + testSignerCerts(t, dir) + os.Setenv("JWKS_URL", "test.com") + err = envconfig.Process("", &config) + require.NoError(t, err) + + got, err := Init(config) + require.NoError(t, err) + require.NotEmpty(t, got) +} + +func testSignerCerts(t *testing.T, dir string) { + crt := filepath.Join(dir, "cert.crt") + if err := ioutil.WriteFile(crt, IdentityIntermediateCA, 0600); err != nil { + assert.NoError(t, err) + } + crtKey := filepath.Join(dir, "cert.key") + if err := ioutil.WriteFile(crtKey, IdentityIntermediateCAKey, 0600); err != nil { + assert.NoError(t, err) + } + os.Setenv("SIGNER_CERTIFICATE", crt) + os.Setenv("SIGNER_PRIVATE_KEY", crtKey) +} + +var ( + IdentityIntermediateCA = []byte(`-----BEGIN CERTIFICATE----- +MIIBczCCARmgAwIBAgIRANntjEpzu9krzL0EG6fcqqgwCgYIKoZIzj0EAwIwETEP +MA0GA1UEAxMGUm9vdENBMCAXDTE5MDcxOTIwMzczOVoYDzIxMTkwNjI1MjAzNzM5 +WjAZMRcwFQYDVQQDEw5JbnRlcm1lZGlhdGVDQTBZMBMGByqGSM49AgEGCCqGSM49 +AwEHA0IABKw1/6WHFcWtw67hH5DzoZvHgA0suC6IYLKms4IP/pds9wU320eDaENo +5860TOyKrGn7vW/cj/OVe2Dzr4KSFVijSDBGMA4GA1UdDwEB/wQEAwIBBjATBgNV +HSUEDDAKBggrBgEFBQcDATASBgNVHRMBAf8ECDAGAQH/AgEAMAsGA1UdEQQEMAKC +ADAKBggqhkjOPQQDAgNIADBFAiEAgPtnYpgwxmPhN0Mo8VX582RORnhcdSHMzFjh +P/li1WwCIFVVWBOrfBnTt7A6UfjP3ljAyHrJERlMauQR+tkD/aqm +-----END CERTIFICATE----- +`) + IdentityIntermediateCAKey = []byte(`-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIPF4DPvFeiRL1G0ROd6MosoUGnvIG/2YxH0CbHwnLKxqoAoGCCqGSM49 +AwEHoUQDQgAErDX/pYcVxa3DruEfkPOhm8eADSy4Lohgsqazgg/+l2z3BTfbR4No +Q2jnzrRM7Iqsafu9b9yP85V7YPOvgpIVWA== +-----END EC PRIVATE KEY----- +`) +) diff --git a/certificate-authority/service/caApi.go b/certificate-authority/service/caApi.go new file mode 100644 index 000000000..310e96bd8 --- /dev/null +++ b/certificate-authority/service/caApi.go @@ -0,0 +1,73 @@ +package service + +import ( + "context" + "fmt" + "time" + + "github.com/go-ocf/cloud/certificate-authority/pb" + "github.com/go-ocf/kit/log" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/kit/security" + "github.com/go-ocf/kit/security/signer" + "google.golang.org/grpc" +) + +type CertificateSigner interface { + //csr is encoded by PEM and returns PEM + Sign(ctx context.Context, csr []byte) ([]byte, error) +} + +// RequestHandler handles incoming requests. +type RequestHandler struct { + identitySigner CertificateSigner + signer CertificateSigner +} + +type SignerConfig struct { + Certificate string `envconfig:"SIGNER_CERTIFICATE" required:"True"` + PrivateKey string `envconfig:"SIGNER_PRIVATE_KEY" required:"True"` + ValidDuration time.Duration `envconfig:"SIGNER_VALID_DURATION" default:"87600h"` +} + +func AddHandler(svr *kitNetGrpc.Server, cfg SignerConfig) error { + handler, err := NewRequestHandlerFromConfig(cfg) + if err != nil { + return fmt.Errorf("could not create go-ocf/certificate-authority: %v", err) + } + pb.RegisterCertificateAuthorityServer(svr.Server, handler) + return nil +} + +// Register registers the handler instance with a gRPC server. +func Register(server *grpc.Server, handler *RequestHandler) { + pb.RegisterCertificateAuthorityServer(server, handler) +} + +func NewRequestHandlerFromConfig(cfg SignerConfig) (*RequestHandler, error) { + chainCerts, err := security.LoadX509(cfg.Certificate) + if err != nil { + return nil, err + } + privateKey, err := security.LoadX509PrivateKey(cfg.PrivateKey) + if err != nil { + return nil, err + } + + identity := signer.NewIdentityCertificateSigner(chainCerts, privateKey, cfg.ValidDuration) + basic := signer.NewBasicCertificateSigner(chainCerts, privateKey, cfg.ValidDuration) + return NewRequestHandler(basic, identity), nil +} + +// NewRequestHandler factory for new RequestHandler. +func NewRequestHandler(signer, identitySigner CertificateSigner) *RequestHandler { + return &RequestHandler{ + signer: signer, + identitySigner: identitySigner, + } +} + +func logAndReturnError(err error) error { + log.Errorf("%v", err) + return err +} diff --git a/certificate-authority/service/config.go b/certificate-authority/service/config.go new file mode 100644 index 000000000..693ddf588 --- /dev/null +++ b/certificate-authority/service/config.go @@ -0,0 +1,21 @@ +package service + +import ( + "encoding/json" + "fmt" + + "github.com/go-ocf/kit/net/grpc" +) + +// Config represents application configuration +type Config struct { + grpc.Config + + JwksURI string `envconfig:"JWKS_URI" default:"https://127.0.0.1:7000/oauth/jwks"` +} + +//String returns string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} diff --git a/certificate-authority/service/signCertificate.go b/certificate-authority/service/signCertificate.go new file mode 100644 index 000000000..3d703e049 --- /dev/null +++ b/certificate-authority/service/signCertificate.go @@ -0,0 +1,21 @@ +package service + +import ( + "context" + + "github.com/go-ocf/cloud/certificate-authority/pb" + "github.com/go-ocf/kit/log" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func (r *RequestHandler) SignCertificate(ctx context.Context, req *pb.SignCertificateRequest) (*pb.SignCertificateResponse, error) { + log.Debugf("RequestHandler.SignCertificate: %v", string(req.CertificateSigningRequest)) + cert, err := r.signer.Sign(ctx, req.CertificateSigningRequest) + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot sign certificate: %v", err)) + } + return &pb.SignCertificateResponse{ + Certificate: cert, + }, nil +} diff --git a/certificate-authority/service/signCertificate_test.go b/certificate-authority/service/signCertificate_test.go new file mode 100644 index 000000000..f1ecd429d --- /dev/null +++ b/certificate-authority/service/signCertificate_test.go @@ -0,0 +1,68 @@ +package service + +import ( + "context" + "crypto/x509" + "encoding/pem" + "testing" + "time" + + "github.com/go-ocf/cloud/certificate-authority/pb" + ocfSigner "github.com/go-ocf/kit/security/signer" + "github.com/stretchr/testify/require" +) + +func newBasicSigner(t *testing.T) CertificateSigner { + identityIntermediateCABlock, _ := pem.Decode(IdentityIntermediateCA) + require.NotEmpty(t, identityIntermediateCABlock) + identityIntermediateCA, err := x509.ParseCertificates(identityIntermediateCABlock.Bytes) + require.NoError(t, err) + identityIntermediateCAKeyBlock, _ := pem.Decode(IdentityIntermediateCAKey) + require.NotEmpty(t, identityIntermediateCAKeyBlock) + identityIntermediateCAKey, err := x509.ParseECPrivateKey(identityIntermediateCAKeyBlock.Bytes) + require.NoError(t, err) + return ocfSigner.NewBasicCertificateSigner(identityIntermediateCA, identityIntermediateCAKey, time.Hour*86400) +} + +func TestRequestHandler_SignCertificate(t *testing.T) { + type args struct { + req *pb.SignCertificateRequest + } + tests := []struct { + name string + args args + want *pb.SignCertificateResponse + wantErr bool + }{ + { + name: "invalid auth", + args: args{ + req: &pb.SignCertificateRequest{}, + }, + wantErr: true, + }, + { + name: "valid", + args: args{ + req: &pb.SignCertificateRequest{ + CertificateSigningRequest: testCSR, + }, + }, + wantErr: false, + }, + } + + r := NewRequestHandler(newBasicSigner(t), nil) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := r.SignCertificate(context.Background(), tt.args.req) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.NotEmpty(t, got) + }) + } +} diff --git a/certificate-authority/service/signIdentityCertificate.go b/certificate-authority/service/signIdentityCertificate.go new file mode 100644 index 000000000..1baa03647 --- /dev/null +++ b/certificate-authority/service/signIdentityCertificate.go @@ -0,0 +1,21 @@ +package service + +import ( + "context" + + "github.com/go-ocf/cloud/certificate-authority/pb" + "github.com/go-ocf/kit/log" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func (r *RequestHandler) SignIdentityCertificate(ctx context.Context, req *pb.SignCertificateRequest) (*pb.SignCertificateResponse, error) { + log.Debugf("RequestHandler.SignIdentityCertificate: %v", string(req.CertificateSigningRequest)) + cert, err := r.identitySigner.Sign(ctx, req.CertificateSigningRequest) + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot sign identity certificate: %v", err)) + } + return &pb.SignCertificateResponse{ + Certificate: cert, + }, nil +} diff --git a/certificate-authority/service/signIdentityCertificate_test.go b/certificate-authority/service/signIdentityCertificate_test.go new file mode 100644 index 000000000..a5a1597d4 --- /dev/null +++ b/certificate-authority/service/signIdentityCertificate_test.go @@ -0,0 +1,98 @@ +package service + +import ( + "context" + "crypto/x509" + "encoding/pem" + "testing" + "time" + + "github.com/go-ocf/cloud/certificate-authority/pb" + ocfSigner "github.com/go-ocf/kit/security/signer" + "github.com/stretchr/testify/require" +) + +func newIdentitySigner(t *testing.T) CertificateSigner { + identityIntermediateCABlock, _ := pem.Decode(IdentityIntermediateCA) + require.NotEmpty(t, identityIntermediateCABlock) + identityIntermediateCA, err := x509.ParseCertificates(identityIntermediateCABlock.Bytes) + require.NoError(t, err) + identityIntermediateCAKeyBlock, _ := pem.Decode(IdentityIntermediateCAKey) + require.NotEmpty(t, identityIntermediateCAKeyBlock) + identityIntermediateCAKey, err := x509.ParseECPrivateKey(identityIntermediateCAKeyBlock.Bytes) + require.NoError(t, err) + return ocfSigner.NewIdentityCertificateSigner(identityIntermediateCA, identityIntermediateCAKey, time.Hour*86400) +} + +func TestRequestHandler_SignIdentityCertificate(t *testing.T) { + type args struct { + req *pb.SignCertificateRequest + } + tests := []struct { + name string + args args + want *pb.SignCertificateResponse + wantErr bool + }{ + { + name: "invalid auth", + args: args{ + req: &pb.SignCertificateRequest{}, + }, + wantErr: true, + }, + { + name: "valid", + args: args{ + req: &pb.SignCertificateRequest{ + CertificateSigningRequest: testCSR, + }, + }, + wantErr: false, + }, + } + + r := NewRequestHandler(nil, newIdentitySigner(t)) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := r.SignIdentityCertificate(context.Background(), tt.args.req) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + require.NotEmpty(t, got) + }) + } +} + +var ( + IdentityIntermediateCA = []byte(`-----BEGIN CERTIFICATE----- +MIIBczCCARmgAwIBAgIRANntjEpzu9krzL0EG6fcqqgwCgYIKoZIzj0EAwIwETEP +MA0GA1UEAxMGUm9vdENBMCAXDTE5MDcxOTIwMzczOVoYDzIxMTkwNjI1MjAzNzM5 +WjAZMRcwFQYDVQQDEw5JbnRlcm1lZGlhdGVDQTBZMBMGByqGSM49AgEGCCqGSM49 +AwEHA0IABKw1/6WHFcWtw67hH5DzoZvHgA0suC6IYLKms4IP/pds9wU320eDaENo +5860TOyKrGn7vW/cj/OVe2Dzr4KSFVijSDBGMA4GA1UdDwEB/wQEAwIBBjATBgNV +HSUEDDAKBggrBgEFBQcDATASBgNVHRMBAf8ECDAGAQH/AgEAMAsGA1UdEQQEMAKC +ADAKBggqhkjOPQQDAgNIADBFAiEAgPtnYpgwxmPhN0Mo8VX582RORnhcdSHMzFjh +P/li1WwCIFVVWBOrfBnTt7A6UfjP3ljAyHrJERlMauQR+tkD/aqm +-----END CERTIFICATE----- +`) + IdentityIntermediateCAKey = []byte(`-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIPF4DPvFeiRL1G0ROd6MosoUGnvIG/2YxH0CbHwnLKxqoAoGCCqGSM49 +AwEHoUQDQgAErDX/pYcVxa3DruEfkPOhm8eADSy4Lohgsqazgg/+l2z3BTfbR4No +Q2jnzrRM7Iqsafu9b9yP85V7YPOvgpIVWA== +-----END EC PRIVATE KEY----- +`) + testCSR = []byte(`-----BEGIN CERTIFICATE REQUEST----- +MIIBRjCB7QIBADA0MTIwMAYDVQQDEyl1dWlkOjAwMDAwMDAwLTAwMDAtMDAwMC0w +MDAwLTAwMDAwMDAwMDAwMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLiT0onX +Dw9JpJR9L1+SfyvILLZfluLTuxC7DNa0CdAhrGU2f6SCv+7VJQiQ02wlCt4iFCMx +u1XoaoEZuwcGKaSgVzBVBgkqhkiG9w0BCQ4xSDBGMAwGA1UdEwQFMAMBAQAwCwYD +VR0PBAQDAgGIMCkGA1UdJQQiMCAGCCsGAQUFBwMBBggrBgEFBQcDAgYKKwYBBAGC +3nwBBjAKBggqhkjOPQQDAgNIADBFAiAl/msC2XmurMvieTSOGt9aEgjZ197rchKL +IpK9P9vnXgIhAJ64cyN2X2uWu+x4NqpRkcneK0L3o0yOR4+DxF683pQ2 +-----END CERTIFICATE REQUEST----- +`) +) diff --git a/certificate-authority/signer/basicCertificateSigner.go b/certificate-authority/signer/basicCertificateSigner.go new file mode 100644 index 000000000..38c166084 --- /dev/null +++ b/certificate-authority/signer/basicCertificateSigner.go @@ -0,0 +1,23 @@ +package local + +import ( + "context" + + "github.com/go-ocf/cloud/certificate-authority/pb" +) + +type BasicCertificateSigner struct { + client pb.CertificateAuthorityClient +} + +// NewBasicCertificateSigner creates an instance. +func NewBasicCertificateSigner(client pb.CertificateAuthorityClient) *BasicCertificateSigner { + return &BasicCertificateSigner{client: client} +} + +// Sign a certificate. A valid access token might be required in the context. +func (s *BasicCertificateSigner) Sign(ctx context.Context, csr []byte) (signedCsr []byte, err error) { + req := pb.SignCertificateRequest{CertificateSigningRequest: csr} + resp, err := s.client.SignCertificate(ctx, &req) + return resp.GetCertificate(), err +} diff --git a/certificate-authority/signer/identityCertificateSigner.go b/certificate-authority/signer/identityCertificateSigner.go new file mode 100644 index 000000000..533814ed5 --- /dev/null +++ b/certificate-authority/signer/identityCertificateSigner.go @@ -0,0 +1,23 @@ +package local + +import ( + "context" + + "github.com/go-ocf/cloud/certificate-authority/pb" +) + +type IdentityCertificateSigner struct { + client pb.CertificateAuthorityClient +} + +// NewIdentityCertificateSigner creates an instance. +func NewIdentityCertificateSigner(client pb.CertificateAuthorityClient) *IdentityCertificateSigner { + return &IdentityCertificateSigner{client: client} +} + +// Sign a certificate. A valid access token might be required in the context. +func (s *IdentityCertificateSigner) Sign(ctx context.Context, csr []byte) (signedCsr []byte, err error) { + req := pb.SignCertificateRequest{CertificateSigningRequest: csr} + resp, err := s.client.SignIdentityCertificate(ctx, &req) + return resp.GetCertificate(), err +} diff --git a/coap-gateway/.dockerignore b/coap-gateway/.dockerignore new file mode 100644 index 000000000..f6f32bc9e --- /dev/null +++ b/coap-gateway/.dockerignore @@ -0,0 +1,5 @@ +.git +.github +.gitignore +.travis.yml +Makefile diff --git a/coap-gateway/Dockerfile b/coap-gateway/Dockerfile new file mode 100644 index 000000000..57de77c3b --- /dev/null +++ b/coap-gateway/Dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.13.5-alpine3.10 AS build +RUN apk add --no-cache curl git build-base +WORKDIR $GOPATH/src/github.com/go-ocf/cloud/coap-gateway +COPY . . +RUN go mod download +RUN go build -o /go/bin/service ./cmd/service + +FROM alpine:3.11 as service +RUN apk add --no-cache ca-certificates +COPY --from=build /go/bin/service /usr/local/bin/service +ENTRYPOINT ["/usr/local/bin/service"] \ No newline at end of file diff --git a/coap-gateway/Makefile b/coap-gateway/Makefile new file mode 100644 index 000000000..1e5c98490 --- /dev/null +++ b/coap-gateway/Makefile @@ -0,0 +1,29 @@ +SHELL = /bin/bash +SERVICE_NAME = $(notdir $(CURDIR)) +LATEST_TAG = vnext +VERSION_TAG = vnext-$(shell git rev-parse --short=7 --verify HEAD) + +default: build + +define build-docker-image + docker build \ + --network=host \ + --tag ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) \ + --tag ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) \ + --target $(1) \ + --file Dockerfile \ + . +endef + +build-servicecontainer: + $(call build-docker-image,service) + +build: build-servicecontainer + +push: build-servicecontainer + docker push ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) + docker push ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) + +proto/generate: + +.PHONY: build-servicecontainer build push proto/generate diff --git a/coap-gateway/README.md b/coap-gateway/README.md new file mode 100644 index 000000000..33a026f9a --- /dev/null +++ b/coap-gateway/README.md @@ -0,0 +1,76 @@ +[![Go Report](https://goreportcard.com/badge/github.com/go-ocf/cloud/coap-gateway)](https://goreportcard.com/report/github.com/go-ocf/cloud/coap-gateway) +[![Gitter](https://badges.gitter.im/ocfcloud/Lobby.svg)](https://gitter.im/ocfcloud/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + + +# coap-gateway + +# Overview + +OCF Servers / Clients communicate over TCP / UDP using the CoAP application protocol. Communication within the OCF Native Cloud shouldn't be restricted to the CoAP protocol, implementation should allow the use of whatever protocol might be introduced in the future. That's why the gateway is the access point for CoAP over TCP, and further communication is OCF Native Cloud specific. + +TCP connection to the OCF Native Cloud is by its nature stateful. The OCF CoAP Gateway is therefore also stateful, keeping open connections to the OCF Servers / Clients. The goal of the Gateway is to translate between the OCF Servers / Clients (CoAP) and the protocol of the OCF Native Cloud and communicate in an asynchronous way. + +# Validation +- OCF CoAP Gateway can accept requests from the OCF Client / Server only after a successful sign-in +- OCF CoAP Gateway can forward requests to the OCF Client / Server only after successful sign-in +- If sign-in was not issued within the configured amount of time or sign-in request failed, OCF Native Cloud will forcibly close the TCP connection +- OCF CoAP Gateway sends command to update device core resource with its status. + - Online when the device was successfully signed-in and communication lock released + - Offline when the device was disconnected or signed-out +- Access Token from a successful sign-in must be locally persisted in the OCF CoAP Gateway and linked with an opened TCP channel +- Access Token linked with the opened TCP channel has to be included in each command issued to other OCF Native Cloud components +- OCF CoAP Gateway processes only those commands, which are designated for a device which the Gateway has an opened TCP channel to +- OCF CoAP Gateway is observing each resource published to the resource directory and publishes an event for every change +- OCF CoAP Gateway retrieves each published resource and updates Resources +- OCF CoAP Gateway has to expose the coap ping-pong + retry count configuration, which can be configured during the deployment +- OCF CoAP Gateway has to ping the device in the configured time, if pong is not received after the configured number of retries, then the connection with the device is closed and device is set as offline +- OCF CoAP Gateway processes events from Resources, by issuing a proper CoAP request to the device and raising an event with the response +- OCF CoAP Gateway has to process a waiting request within the configured time, or set the device as offline + +# Build + +## Docker + +```sh +make build-servicecontainer +``` +## Local machine +Creates `coap-gateway` binary. +```sh +go build -o coap-gateway ./cmd/service +``` + +## Configuration +| Option | ENV variable | Type | Description | Default | +| ------ | --------- | ----------- | ------- | ------- | +| `-` | `ADDRESS` | string | tbd | `"0.0.0.0:5684"` | +| `-` | `EXTERNAL_PORT` | int | tbd | `5684` | +| `-` | `NETWORK` | string | tbd | `"tcp"` | +| `-` | `FQDN` | string | tbd | `"coap-gw.ocf.cloud"` | +| `-` | `AUTH_SERVER_ADDRESS` | string | tbd | `"127.0.0.1:9100"` | +| `-` | `RESOURCE_AGGREGATE_ADDRESS` | string | tbd | `"127.0.0.1:9100"` | +| `-` | `RESOURCE_DIRECTORY_ADDRESS` | string | tbd | `"127.0.0.1:9100"` | +| `-` | `KEEPALIVE_ENABLE` | bool | tbd | `true` | +| `-` | `KEEPALIVE_TIMEOUT_CONNECTION` | string | tbd | `"20s"` | +| `-` | `DISABLE_TCP_SIGNAL_MESSAGE_CSM` | bool | tbd | `false` | +| `-` | `DISABLE_PEER_TCP_SIGNAL_MESSAGE_CSMS` | bool | tbd | `true` | +| `-` | `ERROR_IN_RESPONSE` | bool | tbd | `true` | +| `-` | `GOROUTINE_POOL_SIZE` | int | tbd | `16` | +| `-` | `NATS_URL` | string | tbd | `"nats://localhost:4222"` | +| `-` | `MONGODB_URI` | string | tbd | `"mongodb://localhost:27017"` | +| `-` | `MONGODB_DATABASE` | string | tbd | `"eventstore"` | +| `-` | `MONGODB_BATCH_SIZE` | int | tbd | `16` | +| `-` | `MONGODB_MAX_POOL_SIZE` | int | tbd | `16` | +| `-` | `MONGODB_MAX_CONN_IDLE_TIME` | string | tbd | `"240s"` | +| `-` | `DIAL_ACME_CA_POOL` | string | tbd | `""` | +| `-` | `DIAL_ACME_DIRECTORY_URL` | string | tbd | `""` | +| `-` | `DIAL_ACME_DOMAINS` | string | tbd | `""` | +| `-` | `DIAL_ACME_REGISTRATION_EMAIL` | string | tbd | `""` | +| `-` | `DIAL_ACME_TICK_FREQUENCY` | string | tbd | `""` | +| `-` | `LISTEN_ACME_CA_POOL` | string | tbd | `""` | +| `-` | `LISTEN_ACME_DIRECTORY_URL` | string | tbd | `""` | +| `-` | `LISTEN_ACME_DOMAINS` | string | tbd | `""` | +| `-` | `LISTEN_ACME_REGISTRATION_EMAIL` | string | tbd | `""` | +| `-` | `LISTEN_ACME_TICK_FREQUENCY` | string | tbd | `""` | +| `-` | `LISTEN_ACME_DEVICE_ID` | string | tbd | `""` | +| `-` | `LOG_ENABLE_DEBUG` | bool | tbd | `false` | diff --git a/coap-gateway/cmd/service/main.go b/coap-gateway/cmd/service/main.go new file mode 100644 index 000000000..d77b85318 --- /dev/null +++ b/coap-gateway/cmd/service/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "github.com/go-ocf/cloud/coap-gateway/refImpl" + "github.com/go-ocf/kit/log" + "github.com/kelseyhightower/envconfig" +) + +func main() { + var config refImpl.Config + if err := envconfig.Process("", &config); err != nil { + log.Fatalf("cannot parse configuration: %v", err) + } + if server, err := refImpl.Init(config); err != nil { + log.Fatalf("cannot init server: %v", err) + } else { + if err = server.Serve(); err != nil { + log.Fatalf("unexpected ends: %v", err) + } + } +} diff --git a/coap-gateway/coapconv/coapconv.go b/coap-gateway/coapconv/coapconv.go new file mode 100644 index 000000000..7069ae51d --- /dev/null +++ b/coap-gateway/coapconv/coapconv.go @@ -0,0 +1,199 @@ +package coapconv + +import ( + "bytes" + "fmt" + + coap "github.com/go-ocf/go-coap" + "github.com/go-ocf/go-coap/codes" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +func StatusToCoapCode(status pbRA.Status, cmdCode codes.Code) codes.Code { + switch status { + case pbRA.Status_OK: + switch cmdCode { + case codes.POST: + return codes.Changed + case codes.GET: + return codes.Content + case codes.PUT: + return codes.Created + case codes.DELETE: + return codes.Deleted + } + case pbRA.Status_ACCEPTED: + return codes.Valid + case pbRA.Status_BAD_REQUEST: + return codes.BadRequest + case pbRA.Status_UNAUTHORIZED: + return codes.Unauthorized + case pbRA.Status_FORBIDDEN: + return codes.Forbidden + case pbRA.Status_NOT_FOUND: + return codes.NotFound + case pbRA.Status_UNAVAILABLE: + return codes.ServiceUnavailable + case pbRA.Status_NOT_IMPLEMENTED: + return codes.NotImplemented + } + return codes.BadRequest +} + +func CoapCodeToStatus(code codes.Code) pbRA.Status { + switch code { + case codes.Changed, codes.Content: + return pbRA.Status_OK + case codes.Valid: + return pbRA.Status_ACCEPTED + case codes.BadRequest: + return pbRA.Status_BAD_REQUEST + case codes.Unauthorized: + return pbRA.Status_UNAUTHORIZED + case codes.Forbidden: + return pbRA.Status_FORBIDDEN + case codes.NotFound: + return pbRA.Status_NOT_FOUND + case codes.ServiceUnavailable: + return pbRA.Status_UNAVAILABLE + case codes.NotImplemented: + return pbRA.Status_NOT_IMPLEMENTED + default: + return pbRA.Status_UNKNOWN + } +} + +func MakeMediaType(coapContentFormat int32, contentType string) (coap.MediaType, error) { + if coapContentFormat >= 0 { + return coap.MediaType(coapContentFormat), nil + } + switch contentType { + case coap.TextPlain.String(): + return coap.TextPlain, nil + case coap.AppJSON.String(): + return coap.AppJSON, nil + case coap.AppCBOR.String(): + return coap.AppCBOR, nil + case coap.AppOcfCbor.String(): + return coap.AppOcfCbor, nil + default: + return coap.TextPlain, fmt.Errorf("unknown content type coapContentFormat(%v), contentType(%v)", coapContentFormat, contentType) + } +} + +func NewCoapResourceUpdateRequest(client *coap.ClientConn, href string, reqContentUpdate *pbRA.ResourceUpdatePending) (coap.Message, error) { + if reqContentUpdate.Content == nil { + return nil, fmt.Errorf("invalid content for update content") + } + mediaType, err := MakeMediaType(reqContentUpdate.Content.CoapContentFormat, reqContentUpdate.Content.ContentType) + if err != nil { + return nil, fmt.Errorf("invalid content type for update content: %v", err) + } + req, err := client.NewPostRequest(href, mediaType, bytes.NewBuffer(reqContentUpdate.Content.Data)) + if err != nil { + return nil, fmt.Errorf("cannot create update content request: %v", err) + } + if reqContentUpdate.GetResourceInterface() != "" { + req.AddOption(coap.URIQuery, "if="+reqContentUpdate.GetResourceInterface()) + } + return req, nil +} + +func NewCoapResourceRetrieveRequest(client *coap.ClientConn, href string, resRetrieve *pbRA.ResourceRetrievePending) (coap.Message, error) { + req, err := client.NewGetRequest(href) + if err != nil { + return nil, fmt.Errorf("cannot create retrieve content request: %v", err) + } + if resRetrieve.GetResourceInterface() != "" { + req.AddOption(coap.URIQuery, "if="+resRetrieve.GetResourceInterface()) + } + return req, nil +} + +func MakeContent(resp coap.Message) pbRA.Content { + contentTypeString := "" + coapContentFormat := int32(-1) + if contentType, ok := resp.Option(coap.ContentFormat).(coap.MediaType); ok { + contentTypeString = contentType.String() + coapContentFormat = int32(contentType) + } + return pbRA.Content{ + ContentType: contentTypeString, + CoapContentFormat: coapContentFormat, + Data: resp.Payload(), + } +} + +func MakeCommandMetadata(req *coap.Request) pbCQRS.CommandMetadata { + return pbCQRS.CommandMetadata{ + Sequence: req.Sequence, + ConnectionId: req.Client.RemoteAddr().String(), + } +} + +func MakeConfirmResourceRetrieveRequest(resourceId, correlationId string, authCtx pbCQRS.AuthorizationContext, req *coap.Request) pbRA.ConfirmResourceRetrieveRequest { + content := MakeContent(req.Msg) + metadata := MakeCommandMetadata(req) + + return pbRA.ConfirmResourceRetrieveRequest{ + AuthorizationContext: &authCtx, + ResourceId: resourceId, + CorrelationId: correlationId, + Status: CoapCodeToStatus(req.Msg.Code()), + Content: &content, + CommandMetadata: &metadata, + } +} + +func MakeConfirmResourceUpdateRequest(resourceId, correlationId string, authCtx pbCQRS.AuthorizationContext, req *coap.Request) pbRA.ConfirmResourceUpdateRequest { + content := MakeContent(req.Msg) + metadata := MakeCommandMetadata(req) + + return pbRA.ConfirmResourceUpdateRequest{ + AuthorizationContext: &authCtx, + ResourceId: resourceId, + CorrelationId: correlationId, + Status: CoapCodeToStatus(req.Msg.Code()), + Content: &content, + CommandMetadata: &metadata, + } +} + +func MakeNotifyResourceChangedRequest(resourceId string, authCtx pbCQRS.AuthorizationContext, req *coap.Request) pbRA.NotifyResourceChangedRequest { + content := MakeContent(req.Msg) + metadata := MakeCommandMetadata(req) + + return pbRA.NotifyResourceChangedRequest{ + AuthorizationContext: &authCtx, + ResourceId: resourceId, + Content: &content, + CommandMetadata: &metadata, + Status: CoapCodeToStatus(req.Msg.Code()), + } +} + +func MakeUpdateResourceRequest(resourceId, correlationId string, authCtx pbCQRS.AuthorizationContext, req *coap.Request) pbRA.UpdateResourceRequest { + content := MakeContent(req.Msg) + metadata := MakeCommandMetadata(req) + + return pbRA.UpdateResourceRequest{ + AuthorizationContext: &authCtx, + ResourceId: resourceId, + CorrelationId: correlationId, + Content: &content, + CommandMetadata: &metadata, + } +} + +func MakeRetrieveResourceRequest(resourceId, resourceInterface, correlationId string, authCtx pbCQRS.AuthorizationContext, req *coap.Request) pbRA.RetrieveResourceRequest { + metadata := MakeCommandMetadata(req) + + return pbRA.RetrieveResourceRequest{ + AuthorizationContext: &authCtx, + ResourceId: resourceId, + CorrelationId: correlationId, + ResourceInterface: resourceInterface, + CommandMetadata: &metadata, + } +} diff --git a/coap-gateway/coapconv/grpcCode2CoapCode.go b/coap-gateway/coapconv/grpcCode2CoapCode.go new file mode 100644 index 000000000..f3662ac48 --- /dev/null +++ b/coap-gateway/coapconv/grpcCode2CoapCode.go @@ -0,0 +1,55 @@ +package coapconv + +import ( + coapCodes "github.com/go-ocf/go-coap/codes" + "google.golang.org/grpc/codes" +) + +func GrpcCode2CoapCode(statusCode codes.Code, method coapCodes.Code) coapCodes.Code { + switch statusCode { + case codes.OK: + switch method { + case coapCodes.POST: + return coapCodes.Changed + case coapCodes.GET: + return coapCodes.Content + case coapCodes.PUT: + return coapCodes.Created + case coapCodes.DELETE: + return coapCodes.Deleted + } + case codes.Canceled: + return coapCodes.Empty + case codes.Unknown: + return coapCodes.InternalServerError + case codes.InvalidArgument: + return coapCodes.BadRequest + case codes.DeadlineExceeded: + return coapCodes.InternalServerError + case codes.NotFound: + return coapCodes.NotFound + case codes.AlreadyExists: + return coapCodes.InternalServerError + case codes.PermissionDenied: + return coapCodes.Forbidden + case codes.ResourceExhausted: + return coapCodes.InternalServerError + case codes.FailedPrecondition: + return coapCodes.PreconditionFailed + case codes.Aborted: + return coapCodes.InternalServerError + case codes.OutOfRange: + return coapCodes.RequestEntityTooLarge + case codes.Unimplemented: + return coapCodes.NotImplemented + case codes.Internal: + return coapCodes.InternalServerError + case codes.Unavailable: + return coapCodes.ServiceUnavailable + case codes.DataLoss: + return coapCodes.InternalServerError + case codes.Unauthenticated: + return coapCodes.Unauthorized + } + return coapCodes.InternalServerError +} diff --git a/coap-gateway/coapconv/httpCode2CoapCode.go b/coap-gateway/coapconv/httpCode2CoapCode.go new file mode 100644 index 000000000..fbabc4fed --- /dev/null +++ b/coap-gateway/coapconv/httpCode2CoapCode.go @@ -0,0 +1,106 @@ +package coapconv + +import ( + "net/http" + + coapCodes "github.com/go-ocf/go-coap/codes" +) + +func HttpCode2CoapCode(statusCode int, method coapCodes.Code) coapCodes.Code { + switch statusCode { + //1xx + case http.StatusContinue: + return coapCodes.Continue + case http.StatusSwitchingProtocols: + case http.StatusProcessing: + + //2xx + case http.StatusOK: + switch method { + case coapCodes.POST: + return coapCodes.Changed + case coapCodes.GET: + return coapCodes.Content + case coapCodes.PUT: + return coapCodes.Created + case coapCodes.DELETE: + return coapCodes.Deleted + } + case http.StatusCreated: + return coapCodes.Created + case http.StatusAccepted: + case http.StatusNonAuthoritativeInfo: + case http.StatusNoContent: + case http.StatusResetContent: + case http.StatusPartialContent: + case http.StatusMultiStatus: + case http.StatusAlreadyReported: + case http.StatusIMUsed: + + //3xx + case http.StatusMultipleChoices: + case http.StatusMovedPermanently: + case http.StatusFound: + case http.StatusSeeOther: + case http.StatusNotModified: + case http.StatusUseProxy: + case http.StatusTemporaryRedirect: + case http.StatusPermanentRedirect: + + //4xx + case http.StatusBadRequest: + return coapCodes.BadRequest + case http.StatusUnauthorized: + return coapCodes.Unauthorized + case http.StatusPaymentRequired: + case http.StatusForbidden: + return coapCodes.Forbidden + case http.StatusNotFound: + return coapCodes.NotFound + case http.StatusMethodNotAllowed: + return coapCodes.MethodNotAllowed + case http.StatusNotAcceptable: + return coapCodes.NotAcceptable + case http.StatusProxyAuthRequired: + case http.StatusRequestTimeout: + case http.StatusConflict: + case http.StatusGone: + case http.StatusLengthRequired: + case http.StatusPreconditionFailed: + return coapCodes.PreconditionFailed + case http.StatusRequestEntityTooLarge: + return coapCodes.RequestEntityTooLarge + case http.StatusRequestURITooLong: + case http.StatusUnsupportedMediaType: + return coapCodes.UnsupportedMediaType + case http.StatusRequestedRangeNotSatisfiable: + case http.StatusExpectationFailed: + case http.StatusTeapot: + case http.StatusUnprocessableEntity: + case http.StatusLocked: + case http.StatusFailedDependency: + case http.StatusUpgradeRequired: + case http.StatusPreconditionRequired: + case http.StatusTooManyRequests: + case http.StatusRequestHeaderFieldsTooLarge: + case http.StatusUnavailableForLegalReasons: + + //5xx + case http.StatusInternalServerError: + case http.StatusNotImplemented: + return coapCodes.NotImplemented + case http.StatusBadGateway: + return coapCodes.BadGateway + case http.StatusServiceUnavailable: + return coapCodes.ServiceUnavailable + case http.StatusGatewayTimeout: + return coapCodes.GatewayTimeout + case http.StatusHTTPVersionNotSupported: + case http.StatusVariantAlsoNegotiates: + case http.StatusInsufficientStorage: + case http.StatusLoopDetected: + case http.StatusNotExtended: + case http.StatusNetworkAuthenticationRequired: + } + return coapCodes.InternalServerError +} diff --git a/coap-gateway/coapconv/httpCode2CoapCode_test.go b/coap-gateway/coapconv/httpCode2CoapCode_test.go new file mode 100644 index 000000000..25efb291e --- /dev/null +++ b/coap-gateway/coapconv/httpCode2CoapCode_test.go @@ -0,0 +1,98 @@ +package coapconv + +import ( + "testing" + + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/valyala/fasthttp" +) + +func TestHttpCode2CoapCode(t *testing.T) { + tbl := []struct { + name string + inStatusCode int + inCoapCode coapCodes.Code + out coapCodes.Code + }{ + {"Continue", fasthttp.StatusContinue, coapCodes.Empty, coapCodes.Continue}, + {"fasthttp.StatusSwitchingProtocols", fasthttp.StatusSwitchingProtocols, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusProcessing", fasthttp.StatusProcessing, coapCodes.Empty, coapCodes.InternalServerError}, + + //2xx + {"fasthttp.StatusOK", fasthttp.StatusOK, coapCodes.POST, coapCodes.Changed}, + {"fasthttp.StatusOK", fasthttp.StatusOK, coapCodes.GET, coapCodes.Content}, + {"fasthttp.StatusOK", fasthttp.StatusOK, coapCodes.PUT, coapCodes.Created}, + {"fasthttp.StatusOK", fasthttp.StatusOK, coapCodes.DELETE, coapCodes.Deleted}, + + {"fasthttp.StatusCreated", fasthttp.StatusCreated, coapCodes.Empty, coapCodes.Created}, + {"fasthttp.StatusAccepted", fasthttp.StatusAccepted, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusNonAuthoritativeInfo", fasthttp.StatusNonAuthoritativeInfo, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusNoContent", fasthttp.StatusNoContent, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusResetContent", fasthttp.StatusResetContent, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusPartialContent", fasthttp.StatusPartialContent, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusMultiStatus", fasthttp.StatusMultiStatus, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusAlreadyReported", fasthttp.StatusAlreadyReported, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusIMUsed", fasthttp.StatusIMUsed, coapCodes.Empty, coapCodes.InternalServerError}, + + //3xx + {"fasthttp.StatusMultipleChoices", fasthttp.StatusMultipleChoices, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusMovedPermanently", fasthttp.StatusMovedPermanently, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusFound", fasthttp.StatusFound, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusSeeOther", fasthttp.StatusSeeOther, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusNotModified", fasthttp.StatusNotModified, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusUseProxy", fasthttp.StatusUseProxy, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusTemporaryRedirect", fasthttp.StatusTemporaryRedirect, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusPermanentRedirect", fasthttp.StatusPermanentRedirect, coapCodes.Empty, coapCodes.InternalServerError}, + + //4xx + {"fasthttp.StatusBadRequest", fasthttp.StatusBadRequest, coapCodes.Empty, coapCodes.BadRequest}, + {"fasthttp.StatusUnauthorized", fasthttp.StatusUnauthorized, coapCodes.Empty, coapCodes.Unauthorized}, + {"fasthttp.StatusPaymentRequired", fasthttp.StatusPaymentRequired, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusForbidden", fasthttp.StatusForbidden, coapCodes.Empty, coapCodes.Forbidden}, + {"fasthttp.StatusNotFound", fasthttp.StatusNotFound, coapCodes.Empty, coapCodes.NotFound}, + {"fasthttp.StatusMethodNotAllowed", fasthttp.StatusMethodNotAllowed, coapCodes.Empty, coapCodes.MethodNotAllowed}, + {"fasthttp.StatusNotAcceptable", fasthttp.StatusNotAcceptable, coapCodes.Empty, coapCodes.NotAcceptable}, + {"fasthttp.StatusProxyAuthRequired", fasthttp.StatusProxyAuthRequired, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusRequestTimeout", fasthttp.StatusRequestTimeout, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusConflict", fasthttp.StatusConflict, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusGone", fasthttp.StatusGone, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusLengthRequired", fasthttp.StatusLengthRequired, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusPreconditionFailed", fasthttp.StatusPreconditionFailed, coapCodes.Empty, coapCodes.PreconditionFailed}, + {"fasthttp.StatusRequestEntityTooLarge", fasthttp.StatusRequestEntityTooLarge, coapCodes.Empty, coapCodes.RequestEntityTooLarge}, + {"fasthttp.StatusRequestURITooLong", fasthttp.StatusRequestURITooLong, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusUnsupportedMediaType", fasthttp.StatusUnsupportedMediaType, coapCodes.Empty, coapCodes.UnsupportedMediaType}, + {"fasthttp.StatusRequestedRangeNotSatisfiable", fasthttp.StatusRequestedRangeNotSatisfiable, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusExpectationFailed", fasthttp.StatusExpectationFailed, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusTeapot", fasthttp.StatusTeapot, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusUnprocessableEntity", fasthttp.StatusUnprocessableEntity, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusLocked", fasthttp.StatusLocked, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusFailedDependency", fasthttp.StatusFailedDependency, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusUpgradeRequired", fasthttp.StatusUpgradeRequired, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusPreconditionRequired", fasthttp.StatusPreconditionRequired, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusTooManyRequests", fasthttp.StatusTooManyRequests, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusRequestHeaderFieldsTooLarge", fasthttp.StatusRequestHeaderFieldsTooLarge, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusUnavailableForLegalReasons", fasthttp.StatusUnavailableForLegalReasons, coapCodes.Empty, coapCodes.InternalServerError}, + + //5xx + {"fasthttp.StatusInternalServerError", fasthttp.StatusInternalServerError, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusNotImplemented", fasthttp.StatusNotImplemented, coapCodes.Empty, coapCodes.NotImplemented}, + {"fasthttp.StatusBadGateway", fasthttp.StatusBadGateway, coapCodes.Empty, coapCodes.BadGateway}, + {"fasthttp.StatusServiceUnavailable", fasthttp.StatusServiceUnavailable, coapCodes.Empty, coapCodes.ServiceUnavailable}, + {"fasthttp.StatusGatewayTimeout", fasthttp.StatusGatewayTimeout, coapCodes.Empty, coapCodes.GatewayTimeout}, + {"fasthttp.StatusHTTPVersionNotSupported", fasthttp.StatusHTTPVersionNotSupported, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusVariantAlsoNegotiates", fasthttp.StatusVariantAlsoNegotiates, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusInsufficientStorage", fasthttp.StatusInsufficientStorage, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusLoopDetected", fasthttp.StatusLoopDetected, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusNotExtended", fasthttp.StatusNotExtended, coapCodes.Empty, coapCodes.InternalServerError}, + {"fasthttp.StatusNetworkAuthenticationRequired", fasthttp.StatusNetworkAuthenticationRequired, coapCodes.Empty, coapCodes.InternalServerError}, + } + for _, e := range tbl { + testCode := func(t *testing.T) { + code := HttpCode2CoapCode(e.inStatusCode, e.inCoapCode) + if e.out != code { + t.Errorf("Unexpected code(%v) returned, expected %v", code, e.out) + } + } + t.Run(e.name, testCode) + } +} diff --git a/coap-gateway/refImpl/refImpl.go b/coap-gateway/refImpl/refImpl.go new file mode 100644 index 000000000..715b6e4af --- /dev/null +++ b/coap-gateway/refImpl/refImpl.go @@ -0,0 +1,129 @@ +package refImpl + +import ( + "context" + "encoding/json" + "fmt" + "strings" + + "github.com/go-ocf/kit/security/certManager" + + "github.com/go-ocf/cloud/coap-gateway/service" + "github.com/go-ocf/cloud/coap-gateway/uri" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/go-ocf/kit/log" + kitNetCoap "github.com/go-ocf/kit/net/coap" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventbus/nats" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/mongodb" + "github.com/panjf2000/ants" +) + +type Config struct { + Service service.Config + GoRoutinePoolSize int `envconfig:"GOROUTINE_POOL_SIZE" default:"16"` + Nats nats.Config `envconfig:"NATS"` + MongoDB mongodb.Config `envconfig:"MONGODB"` + Dial certManager.Config `envconfig:"DIAL"` + Listen certManager.OcfConfig `envconfig:"LISTEN"` + Log log.Config `envconfig:"LOG"` +} + +type RefImpl struct { + pool *ants.Pool + eventstore *mongodb.EventStore + service *service.Server + subscriber *nats.Subscriber + dialCertManager certManager.CertManager + listenCertManager certManager.CertManager +} + +//String return string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} + +// Init creates reference implementation for coap-gateway with default authorization interceptor. +func Init(config Config) (*RefImpl, error) { + dialCertManager, err := certManager.NewCertManager(config.Dial) + if err != nil { + return nil, fmt.Errorf("cannot create dial cert manager %v", err) + } + auth := NewDefaultAuthInterceptor() + return InitWithAuthInterceptor(config, dialCertManager, auth) +} + +// InitWithAuthInterceptor creates reference implementation for coap-gateway with custom authorization interceptor. +func InitWithAuthInterceptor(config Config, dialCertManager certManager.CertManager, auth kitNetCoap.Interceptor) (*RefImpl, error) { + log.Setup(config.Log) + + log.Info(config.String()) + + dialTLSConfig := dialCertManager.GetClientTLSConfig() + + pool, err := ants.NewPool(config.GoRoutinePoolSize) + if err != nil { + return nil, fmt.Errorf("cannot create goroutine pool: %v", err) + } + + eventstore, err := mongodb.NewEventStore(config.MongoDB, pool.Submit, mongodb.WithTLS(&dialTLSConfig)) + if err != nil { + return nil, fmt.Errorf("cannot create mongodb eventstore %v", err) + } + + subscriber, err := nats.NewSubscriber(config.Nats, pool.Submit, func(err error) { log.Errorf("coap-gateway: error occurs during receiving event: %v", err) }, nats.WithTLS(&dialTLSConfig)) + if err != nil { + return nil, fmt.Errorf("cannot create nats publisher %v", err) + } + + var listenCertManager certManager.CertManager + if strings.Contains(config.Service.Net, "-tls") { + listenCertManager, err = certManager.NewOcfCertManager(config.Listen) + if err != nil { + dialCertManager.Close() + return nil, fmt.Errorf("cannot create listen cert manager %v", err) + } + } + + return &RefImpl{ + pool: pool, + eventstore: eventstore, + service: service.New(config.Service, dialCertManager, listenCertManager, auth, eventstore, subscriber, pool), + subscriber: subscriber, + dialCertManager: dialCertManager, + listenCertManager: listenCertManager, + }, nil +} + +// Serve starts handling coap requests. +func (r *RefImpl) Serve() error { + return r.service.Serve() +} + +// Shutdown shutdowns the service. +func (r *RefImpl) Shutdown() error { + err := r.service.Shutdown() + r.eventstore.Close(context.Background()) + r.subscriber.Close() + r.pool.Release() + r.dialCertManager.Close() + if r.listenCertManager != nil { + r.listenCertManager.Close() + } + return err +} + +// NewDefaultAuthInterceptor creates default authorization interceptor. +func NewDefaultAuthInterceptor() kitNetCoap.Interceptor { + return func(ctx context.Context, code coapCodes.Code, path string) (context.Context, error) { + switch path { + case uri.RefreshToken, uri.SecureRefreshToken, uri.SignUp, uri.SecureSignUp, uri.SignIn, uri.SecureSignIn, uri.ResourcePing: + return ctx, nil + } + _, err := kitNetCoap.TokenFromCtx(ctx) + if err != nil { + return ctx, err + } + return ctx, nil + } +} diff --git a/coap-gateway/refImpl/refImpl_test.go b/coap-gateway/refImpl/refImpl_test.go new file mode 100644 index 000000000..78d852216 --- /dev/null +++ b/coap-gateway/refImpl/refImpl_test.go @@ -0,0 +1,21 @@ +package refImpl + +import ( + "testing" + + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/require" +) + +func TestInit(t *testing.T) { + var config Config + err := envconfig.Process("", &config) + require.NoError(t, err) + config.GoRoutinePoolSize = 1 + config.Service.Net = "tcp-tls" + + got, err := Init(config) + require.NoError(t, err) + require.NotEmpty(t, got) +} + diff --git a/coap-gateway/service/client.go b/coap-gateway/service/client.go new file mode 100644 index 000000000..861ef14d3 --- /dev/null +++ b/coap-gateway/service/client.go @@ -0,0 +1,458 @@ +package service + +import ( + "context" + "fmt" + "sync" + "sync/atomic" + + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/go-ocf/kit/log" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/cloud/coap-gateway/coapconv" + cqrsRA "github.com/go-ocf/cloud/resource-aggregate/cqrs" + raEvents "github.com/go-ocf/cloud/resource-aggregate/cqrs/events" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + "github.com/go-ocf/sdk/schema/cloud" +) + +type observedResource struct { + res *pbRA.Resource + observation *gocoap.Observation +} + +type authCtx struct { + pbCQRS.AuthorizationContext + AccessToken string +} + +//Client a setup of connection +type Client struct { + server *Server + coapConn *gocoap.ClientConn + isClosed int32 + + observedResources map[string]map[int64]observedResource // [deviceID][instanceID] + observedResourcesLock sync.Mutex + authCtx authCtx + authContextLock sync.Mutex +} + +//newClient create and initialize client +func newClient(server *Server, client *gocoap.ClientConn) *Client { + return &Client{ + server: server, + coapConn: client, + observedResources: make(map[string]map[int64]observedResource), + } +} + +func (client *Client) remoteAddrString() string { + return client.coapConn.RemoteAddr().String() +} + +func (client *Client) observeResource(ctx context.Context, res *pbRA.Resource, allowDuplicit bool) (err error) { + log.Debugf("DeviceId: %v, ResourceId: %v: observe resource", res.DeviceId, res.Id) + + client.observedResourcesLock.Lock() + defer client.observedResourcesLock.Unlock() + if _, ok := client.observedResources[res.DeviceId]; !ok { + client.observedResources[res.DeviceId] = make(map[int64]observedResource) + } + if _, ok := client.observedResources[res.DeviceId][res.InstanceId]; ok { + if allowDuplicit { + return nil + } + return fmt.Errorf("resource is already already published") + } + return client.addObservedResourceLocked(ctx, res) +} + +func (client *Client) getResourceContent(ctx context.Context, obsRes *pbRA.Resource) { + resp, err := client.coapConn.GetWithContext(ctx, obsRes.Href) + if err != nil { + log.Errorf("DeviceId: %v, ResourceId: %v: cannot get resource content: %v", obsRes.DeviceId, obsRes.Id, err) + return + } + r := gocoap.Request{Msg: resp, Client: client.coapConn, Ctx: ctx, Sequence: client.coapConn.Sequence()} + err = client.notifyContentChanged(obsRes, &r) + if err != nil { + // cloud is unsynchronized against device. To recover cloud state, client need to reconnect to cloud. + log.Errorf("DeviceId: %v, ResourceId: %v: cannot get resource content: %v", obsRes.DeviceId, obsRes.Id, err) + client.Close() + } + if resp.Code() == coapCodes.NotFound { + client.unpublishResources([]*pbRA.Resource{obsRes}) + } +} + +func (client *Client) addObservedResourceLocked(ctx context.Context, res *pbRA.Resource) error { + var observation *gocoap.Observation + obs := isObservable(res) + log.Debugf("DeviceId: %v, ResourceId: %v: Observable: %v: Client.addObservedResourceLocked", res.DeviceId, res.Href, obs) + + if res.Id == cqrsRA.MakeResourceId(res.DeviceId, cloud.StatusHref) { + return nil + } + + obsRes := res.Clone() + if obs { + obs, err := client.coapConn.ObserveWithContext(ctx, res.Href, func(req *gocoap.Request) { + err := client.notifyContentChanged(obsRes, req) + if err != nil { + // cloud is unsynchronized against device. To recover cloud state, client need to reconnect to cloud. + log.Errorf("DeviceId: %v, ResourceId: %v: cannot get resource content: %v", obsRes.DeviceId, obsRes.Id, err) + client.Close() + } + if req.Msg.Code() == coapCodes.NotFound { + client.unpublishResources([]*pbRA.Resource{obsRes}) + } + }) + if err != nil { + log.Errorf("DeviceId: %v, ResourceId: %v: cannot observe resource: %v", obsRes.DeviceId, obsRes.Id, err) + } else { + observation = obs + } + } else { + go client.getResourceContent(ctx, obsRes) + } + client.observedResources[res.DeviceId][res.InstanceId] = observedResource{res: obsRes, observation: observation} + return nil +} + +func (client *Client) getObservedResources(deviceID string, instanceIDs []int64, matches []*pbRA.Resource) []*pbRA.Resource { + client.observedResourcesLock.Lock() + defer client.observedResourcesLock.Unlock() + + getAllDeviceIDMatches := len(instanceIDs) == 0 + + if deviceResourcesMap, ok := client.observedResources[deviceID]; ok { + if getAllDeviceIDMatches { + for _, value := range deviceResourcesMap { + matches = append(matches, value.res) + } + } else { + for _, instanceID := range instanceIDs { + if resource, ok := deviceResourcesMap[instanceID]; ok { + matches = append(matches, resource.res) + } + } + } + } + + return matches +} + +func (client *Client) removeResource(deviceID string, instanceID int64) { + if device, ok := client.observedResources[deviceID]; ok { + delete(device, instanceID) + if len(client.observedResources[deviceID]) == 0 { + delete(client.observedResources, deviceID) + } + } +} + +func (client *Client) popObservation(deviceID string, instanceID int64) *gocoap.Observation { + log.Debugf("remove published resource ocf://%v/%v", deviceID, instanceID) + + var obs *gocoap.Observation + if device, ok := client.observedResources[deviceID]; ok { + if res, ok := device[instanceID]; ok { + obs = res.observation + res.observation = nil + } + } + + return obs +} + +func (client *Client) unobserveResources(rscs []*pbRA.Resource, rscsUnpublished map[string]bool) { + observartions := client.unobserveAndRemoveResources(rscs, rscsUnpublished) + for _, obs := range observartions { + obs.Cancel() + } +} + +// Close closes coap connection +func (client *Client) Close() error { + err := client.coapConn.Close() + if err != nil { + return fmt.Errorf("cannot close client: %v", err) + } + return nil +} + +func (client *Client) unobserveAndRemoveResources(rscs []*pbRA.Resource, rscsUnpublished map[string]bool) []*gocoap.Observation { + observartions := make([]*gocoap.Observation, 0, 32) + + client.observedResourcesLock.Lock() + defer client.observedResourcesLock.Unlock() + + for _, resource := range rscs { + if del, ok := rscsUnpublished[resource.Id]; ok && del { + log.Debugf("DeviceId: %v, ResourceId: %v: delete resource", resource.DeviceId, resource.Id) + } else { + log.Debugf("DeviceId: %v, ResourceId: %v: unobserve resource", resource.DeviceId, resource.Id) + } + + obs := client.popObservation(resource.DeviceId, resource.InstanceId) + if obs != nil { + observartions = append(observartions, obs) + } + if rscsUnpublished[resource.Id] { + client.removeResource(resource.DeviceId, resource.InstanceId) + } + } + return observartions +} + +// cleanObservedResourcesOfDevices remove all pbRA observations requested from device to cloud. +func (client *Client) cleanObservedResourcesOfDevices() { + obs, _ := client.server.observeResourceContainer.PopByRemoteAddr(client.remoteAddrString()) + for _, ob := range obs { + err := client.server.projection.Unregister(ob.deviceId) + if err != nil { + log.Errorf("cannot unregister observed device %v from projection: %v", ob.deviceId, err) + } + } +} + +func (client *Client) popObservedResources() []*gocoap.Observation { + observartions := make([]*gocoap.Observation, 0, 32) + client.observedResourcesLock.Lock() + defer client.observedResourcesLock.Unlock() + for deviceId, instanceIDs := range client.observedResources { + for instanceID := range instanceIDs { + obs := client.popObservation(deviceId, instanceID) + if obs != nil { + observartions = append(observartions, obs) + } + client.removeResource(deviceId, instanceID) + } + client.server.clientContainerByDeviceId.Remove(deviceId) + err := client.server.projection.Unregister(deviceId) + if err != nil { + log.Errorf("DeviceId %v: cannot unregister device from projection: %v", deviceId, err) + } + } + return observartions +} + +// cleanObservedResources remove all device pbRA observation requested by cloud. +func (client *Client) cleanObservedResources() { + for _, obs := range client.popObservedResources() { + obs.Cancel() + } +} + +// OnClose action when coap connection was closed. +func (client *Client) OnClose() { + log.Debugf("close client %v", client.coapConn.RemoteAddr()) + + atomic.StoreInt32(&client.isClosed, 1) + client.server.oicPingCache.Delete(client.remoteAddrString()) + + client.cleanObservedResources() + + client.cleanObservedResourcesOfDevices() + + authCtx := client.loadAuthorizationContext() + + if client.authCtx.DeviceId != "" { + err := client.UpdateCloudDeviceStatus(kitNetGrpc.CtxWithToken(context.Background(), authCtx.AccessToken), authCtx.DeviceId, authCtx.AuthorizationContext, false) + if err != nil { + // Device will be still reported as online and it can fix his state by next calls online, offline commands. + log.Errorf("DeviceId %v: cannot handle sign out: cannot update cloud device status: %v", authCtx.DeviceId, err) + } + } +} + +func (client *Client) storeAuthorizationContext(authCtx authCtx) (oldDeviceId string) { + log.Debugf("Authorization context stored for client %v, device %v, user %v", client.coapConn.RemoteAddr(), authCtx.GetDeviceId(), authCtx.GetUserId()) + client.authContextLock.Lock() + defer client.authContextLock.Unlock() + oldAuthContext := client.authCtx + client.authCtx = authCtx + return oldAuthContext.DeviceId +} + +func (client *Client) loadAuthorizationContext() authCtx { + client.authContextLock.Lock() + defer client.authContextLock.Unlock() + return client.authCtx +} + +func (client *Client) notifyContentChanged(res *pbRA.Resource, notification *gocoap.Request) error { + decodeMsgToDebug(client, notification.Msg, "RECEIVED-NOTIFICATION") + authCtx := client.loadAuthorizationContext() + + request := coapconv.MakeNotifyResourceChangedRequest(res.Id, authCtx.AuthorizationContext, notification) + _, err := client.server.raClient.NotifyResourceChanged(kitNetGrpc.CtxWithToken(notification.Ctx, authCtx.AccessToken), &request) + if err != nil { + return fmt.Errorf("cannot notify resource content changed: %v", err) + } + return nil +} + +func (client *Client) updateContent(ctx context.Context, resource *pbRA.Resource, reqContentUpdate *raEvents.ResourceUpdatePending) error { + if reqContentUpdate.AuditContext == nil { + return fmt.Errorf("invalid AuditContext of resource update pending request") + } + if resource.Id == cqrsRA.MakeResourceId(resource.DeviceId, cloud.StatusHref) { + authCtx := client.loadAuthorizationContext() + msg := client.coapConn.NewMessage(gocoap.MessageParams{ + Code: coapCodes.MethodNotAllowed, + }) + notification := gocoap.Request{Msg: msg, Client: client.coapConn, Sequence: client.coapConn.Sequence()} + request := coapconv.MakeConfirmResourceUpdateRequest(resource.Id, reqContentUpdate.AuditContext.CorrelationId, authCtx.AuthorizationContext, ¬ification) + _, err := client.server.raClient.ConfirmResourceUpdate(kitNetGrpc.CtxWithToken(ctx, authCtx.AccessToken), &request) + if err != nil { + return err + } + return nil + } + + req, err := coapconv.NewCoapResourceUpdateRequest(client.coapConn, resource.Href, &reqContentUpdate.ResourceUpdatePending) + if err != nil { + return err + } + + ctx, cancel := context.WithTimeout(ctx, client.server.RequestTimeout) + defer cancel() + + resp, err := client.coapConn.ExchangeWithContext(ctx, req) + if err != nil { + return err + } + + decodeMsgToDebug(client, resp, "RESOURCE-UPDATE") + + if resp.Code() == coapCodes.NotFound { + client.unpublishResources([]*pbRA.Resource{resource}) + } + + authCtx := client.loadAuthorizationContext() + notification := gocoap.Request{Msg: resp, Client: client.coapConn, Sequence: client.coapConn.Sequence()} + + request := coapconv.MakeConfirmResourceUpdateRequest(resource.Id, reqContentUpdate.AuditContext.CorrelationId, authCtx.AuthorizationContext, ¬ification) + _, err = client.server.raClient.ConfirmResourceUpdate(kitNetGrpc.CtxWithToken(ctx, authCtx.AccessToken), &request) + if err != nil { + return err + } + + return nil +} + +func (client *Client) retrieveContent(ctx context.Context, resource *pbRA.Resource, reqContentUpdate *raEvents.ResourceRetrievePending) error { + if reqContentUpdate.AuditContext == nil { + return fmt.Errorf("invalid AuditContext of resource retrieve pending request") + } + if resource.Id == cqrsRA.MakeResourceId(resource.DeviceId, cloud.StatusHref) { + authCtx := client.loadAuthorizationContext() + msg := client.coapConn.NewMessage(gocoap.MessageParams{ + Code: coapCodes.Content, + }) + notification := gocoap.Request{Msg: msg, Client: client.coapConn, Sequence: client.coapConn.Sequence()} + request := coapconv.MakeConfirmResourceRetrieveRequest(resource.Id, reqContentUpdate.AuditContext.CorrelationId, authCtx.AuthorizationContext, ¬ification) + _, err := client.server.raClient.ConfirmResourceRetrieve(kitNetGrpc.CtxWithToken(ctx, authCtx.AccessToken), &request) + if err != nil { + return err + } + return nil + } + + req, err := coapconv.NewCoapResourceRetrieveRequest(client.coapConn, resource.Href, &reqContentUpdate.ResourceRetrievePending) + if err != nil { + return err + } + + ctx, cancel := context.WithTimeout(ctx, client.server.RequestTimeout) + defer cancel() + + resp, err := client.coapConn.ExchangeWithContext(ctx, req) + if err != nil { + return err + } + + decodeMsgToDebug(client, resp, "RESOURCE-RETRIEVE") + + if resp.Code() == coapCodes.NotFound { + client.unpublishResources([]*pbRA.Resource{resource}) + } + + authCtx := client.loadAuthorizationContext() + notification := gocoap.Request{Msg: resp, Client: client.coapConn, Sequence: client.coapConn.Sequence()} + + request := coapconv.MakeConfirmResourceRetrieveRequest(resource.Id, reqContentUpdate.AuditContext.CorrelationId, authCtx.AuthorizationContext, ¬ification) + _, err = client.server.raClient.ConfirmResourceRetrieve(kitNetGrpc.CtxWithToken(ctx, authCtx.AccessToken), &request) + if err != nil { + return err + } + + return nil +} + +func (client *Client) publishResource(ctx context.Context, resource *pbRA.Resource, ttl int32, connectionId string, sequence uint64, authCtx pbCQRS.AuthorizationContext) (*pbRA.Resource, error) { + if resource.DeviceId == "" { + return resource, fmt.Errorf("cannot send command publish resource: invalid DeviceId") + } + resource.Href = fixHref(resource.Href) + + if resource.Href == "" || resource.Href == "/" { + return resource, fmt.Errorf("cannot send command publish resource: invalid Href") + } + resource.Id = resource2UUID(resource.DeviceId, resource.Href) + + request := pbRA.PublishResourceRequest{ + AuthorizationContext: &authCtx, + ResourceId: resource.Id, + Resource: resource, + TimeToLive: ttl, + CommandMetadata: &pbCQRS.CommandMetadata{ + Sequence: sequence, + ConnectionId: connectionId, + }, + } + + response, err := client.server.raClient.PublishResource(ctx, &request) + if err != nil { + return resource, fmt.Errorf("cannot process command publish resource: %v", err) + } + + resource.InstanceId = response.InstanceId + return resource, nil +} + +func (client *Client) unpublishResource(ctx context.Context, resource *pbRA.Resource, authCtx pbCQRS.AuthorizationContext, rscsUnpublished map[string]bool) map[string]bool { + _, err := client.server.raClient.UnpublishResource(ctx, &pbRA.UnpublishResourceRequest{ + AuthorizationContext: &authCtx, + ResourceId: resource.Id, + CommandMetadata: &pbCQRS.CommandMetadata{ + ConnectionId: client.remoteAddrString(), + Sequence: client.coapConn.Sequence(), + }, + }) + if err != nil { + // unpublish resource is not critical -> resource is still accessible, + // but next update of device resource will returns 'not found; and it triggers again unpublish. + log.Errorf("DeviceId: %v, ResourceId: %v: cannot unpublish resource: %v", resource.DeviceId, resource.Id, err) + rscsUnpublished[resource.Id] = false + return rscsUnpublished + } + + rscsUnpublished[resource.Id] = true + return rscsUnpublished +} + +func (client *Client) unpublishResources(rscs []*pbRA.Resource) { + rscsUnpublished := make(map[string]bool, 32) + authCtx := client.loadAuthorizationContext() + + for _, resource := range rscs { + rscsUnpublished = client.unpublishResource(kitNetGrpc.CtxWithToken(context.Background(), authCtx.AccessToken), resource, authCtx.AuthorizationContext, rscsUnpublished) + } + + client.unobserveResources(rscs, rscsUnpublished) +} diff --git a/coap-gateway/service/clientContainer.go b/coap-gateway/service/clientContainer.go new file mode 100644 index 000000000..b3b33efd7 --- /dev/null +++ b/coap-gateway/service/clientContainer.go @@ -0,0 +1,36 @@ +package service + +import ( + "sync" +) + +//ClientContainer client <-> server connections +type ClientContainer struct { + sessions map[string]*Client + mutex sync.Mutex +} + +func (c *ClientContainer) Add(remoteAddr string, client *Client) { + c.mutex.Lock() + defer c.mutex.Unlock() + c.sessions[remoteAddr] = client +} + +func (c *ClientContainer) Find(remoteAddr string) *Client { + c.mutex.Lock() + defer c.mutex.Unlock() + if session, ok := c.sessions[remoteAddr]; ok { + return session + } + return nil +} + +func (c *ClientContainer) Pop(remoteAddr string) (*Client, bool) { + c.mutex.Lock() + defer c.mutex.Unlock() + defer delete(c.sessions, remoteAddr) + if client, ok := c.sessions[remoteAddr]; ok { + return client, true + } + return nil, false +} diff --git a/coap-gateway/service/clientContainerByDeviceId.go b/coap-gateway/service/clientContainerByDeviceId.go new file mode 100644 index 000000000..6154ce56b --- /dev/null +++ b/coap-gateway/service/clientContainerByDeviceId.go @@ -0,0 +1,36 @@ +package service + +import ( + "sync" +) + +//clientContainerByDeviceId client <-> server connections +type clientContainerByDeviceId struct { + sessions map[string]*Client + mutex sync.Mutex +} + +func NewClientContainerByDeviceId() *clientContainerByDeviceId { + return &clientContainerByDeviceId{sessions: make(map[string]*Client)} +} + +func (c *clientContainerByDeviceId) Add(deviceId string, session *Client) { + c.mutex.Lock() + defer c.mutex.Unlock() + c.sessions[deviceId] = session +} + +func (c *clientContainerByDeviceId) Find(deviceId string) *Client { + c.mutex.Lock() + defer c.mutex.Unlock() + if session, ok := c.sessions[deviceId]; ok { + return session + } + return nil +} + +func (c *clientContainerByDeviceId) Remove(deviceId string) { + c.mutex.Lock() + defer c.mutex.Unlock() + delete(c.sessions, deviceId) +} diff --git a/coap-gateway/service/clientObserveHandler.go b/coap-gateway/service/clientObserveHandler.go new file mode 100644 index 000000000..aca57cc8d --- /dev/null +++ b/coap-gateway/service/clientObserveHandler.go @@ -0,0 +1,197 @@ +package service + +import ( + "fmt" + "io" + "time" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/cloud/coap-gateway/coapconv" + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/go-ocf/kit/log" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + "google.golang.org/grpc/status" +) + +func clientObserveHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client, observe uint32) { + t := time.Now() + defer func() { + log.Debugf("clientObserveHandler takes %v", time.Since(t)) + }() + + authCtx := client.loadAuthorizationContext() + deviceId, href, err := URIToDeviceIDHref(req.Msg) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot handle observe resource: %v", authCtx.DeviceId, err), s, client, coapCodes.BadRequest) + return + } + resourceId := resource2UUID(deviceId, href) + + switch observe { + case 0: + startResourceObservation(s, req, client, authCtx, deviceId, resourceId) + case 1: + stopResourceObservation(s, req, client, authCtx, deviceId, resourceId) + default: + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot observe resource %v.%v: invalid Observe value", authCtx.DeviceId, deviceId, resourceId), s, client, coapCodes.BadRequest) + return + } + +} + +func cleanStartResourceObservation(client *Client, deviceId, resourceId string, token []byte) { + err := client.server.projection.Unregister(deviceId) + if err != nil { + log.Errorf("DeviceId: %v: cannot start resource observation - unregister device from projection %v: %v", deviceId, err) + } + err = client.server.observeResourceContainer.RemoveByResource(resourceId, client.remoteAddrString(), token) + if err != nil { + log.Errorf("DeviceId: %v: cannot start resource observation - remove resource from observation %v: %v", resourceId, err) + } +} + +func SendResourceContentToObserver(s gocoap.ResponseWriter, client *Client, contentCtx *pbRA.ResourceChanged, observe uint32, deviceId, resourceId string, token []byte) { + if contentCtx.GetStatus() != pbRA.Status_OK { + cleanStartResourceObservation(client, deviceId, resourceId, token) + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot observe resource %v, device response: %v", deviceId, resourceId, contentCtx.GetStatus()), s, client, coapconv.StatusToCoapCode(contentCtx.GetStatus(), coapCodes.GET)) + return + } + + if contentCtx.GetContent() == nil { + sendResponse(s, client, coapCodes.Content, gocoap.TextPlain, nil) + return + } + mediaType, err := coapconv.MakeMediaType(contentCtx.GetContent().GetCoapContentFormat(), contentCtx.GetContent().GetContentType()) + if err != nil { + log.Errorf("cannot set content format for observer: %v", err) + return + } + + msg := s.NewResponse(coapCodes.Content) + msg.SetOption(gocoap.ContentFormat, mediaType) + msg.SetObserve(observe) + msg.SetPayload(contentCtx.GetContent().GetData()) + err = s.WriteMsg(msg) + if err != nil { + log.Errorf("cannot send observe notification to %v: %v", client.remoteAddrString(), err) + } + decodeMsgToDebug(client, msg, "SEND-NOTIFICATION") +} + +func startResourceObservation(s gocoap.ResponseWriter, req *gocoap.Request, client *Client, authCtx authCtx, deviceId, resourceId string) { + userIdsFilter := []string(nil) + if authCtx.UserId != "" { + userIdsFilter = []string{authCtx.UserId} + } + getUserDevicesClient, err := client.server.asClient.GetUserDevices(kitNetGrpc.CtxWithToken(req.Ctx, authCtx.AccessToken), &pbAS.GetUserDevicesRequest{ + UserIdsFilter: userIdsFilter, + DeviceIdsFilter: []string{deviceId}, + }) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot start resource observation %v.%v: %v", authCtx.DeviceId, deviceId, resourceId, err), s, client, coapCodes.InternalServerError) + return + } + var found bool + defer getUserDevicesClient.CloseSend() + for { + userDev, err := getUserDevicesClient.Recv() + if err == io.EOF { + break + } + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot start resource observation %v.%v: %v", authCtx.DeviceId, deviceId, resourceId, err), s, client, coapconv.GrpcCode2CoapCode(status.Convert(err).Code(), coapCodes.GET)) + return + } + if userDev.DeviceId == deviceId { + found = true + break + } + } + if !found { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot start resource observation %v.%v: unauthorized access", authCtx.DeviceId, deviceId, resourceId), s, client, coapCodes.BadRequest) + return + } + + observeResource := observeResource{ + remoteAddr: client.remoteAddrString(), + deviceId: deviceId, + resourceId: resourceId, + token: req.Msg.Token(), + client: client, + responseWriter: s, + } + + err = client.server.observeResourceContainer.Add(observeResource) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot start resource observation %v.%v: %v", authCtx.DeviceId, deviceId, resourceId, err), s, client, coapCodes.BadRequest) + return + } + + loaded, err := client.server.projection.Register(req.Ctx, deviceId) + if err != nil { + err1 := client.server.observeResourceContainer.RemoveByResource(resourceId, client.remoteAddrString(), req.Msg.Token()) + if err1 != nil { + log.Errorf("DeviceId: %v: cannot start resource observation - remove resource from observation %v: %v", resourceId, err1) + } + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot start resource observation %v.%v: cannot register: %v", authCtx.DeviceId, deviceId, resourceId, err), s, client, coapCodes.BadRequest) + return + } + resourceCtxs := client.server.projection.Models(deviceId, resourceId) + if len(resourceCtxs) == 0 { + err := client.server.projection.ForceUpdate(req.Ctx, deviceId, resourceId) + if err != nil { + cleanStartResourceObservation(client, deviceId, resourceId, req.Msg.Token()) + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot start resource observation %v.%v: force update: %v", authCtx.DeviceId, deviceId, resourceId, err), s, client, coapCodes.BadRequest) + return + } + resourceCtxs = client.server.projection.Models(deviceId, resourceId) + if len(resourceCtxs) == 0 { + cleanStartResourceObservation(client, deviceId, resourceId, req.Msg.Token()) + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot start resource observation %v.%v: resource model: %v", authCtx.DeviceId, deviceId, resourceId, err), s, client, coapCodes.BadRequest) + return + } + } + + if !loaded { + SendResourceContentToObserver(s, client, resourceCtxs[0].(*resourceCtx).Content(), observeResource.Observe(), deviceId, resourceId, req.Msg.Token()) + return + } + // response will be send from projection +} + +func stopResourceObservation(s gocoap.ResponseWriter, req *gocoap.Request, client *Client, authCtx authCtx, deviceId, resourceId string) { + err := client.server.observeResourceContainer.RemoveByResource(resourceId, client.remoteAddrString(), req.Msg.Token()) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot stop resource observation %v.%v: %v", authCtx.DeviceId, deviceId, resourceId, err), s, client, coapCodes.BadRequest) + return + } + var content *pbRA.ResourceChanged + resourceCtxs := client.server.projection.Models(deviceId, resourceId) + if len(resourceCtxs) > 0 { + content = resourceCtxs[0].(*resourceCtx).Content() + } else { + log.Errorf("DeviceId: %v: cannot get content for stop observation %v.%v: %v", authCtx.DeviceId, deviceId, resourceId, err) + } + + err = client.server.projection.Unregister(deviceId) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot stop resource observation %v.%v: %v", authCtx.DeviceId, deviceId, resourceId, err), s, client, coapCodes.NotFound) + return + } + + SendResourceContentToObserver(s, client, content, 1, deviceId, resourceId, req.Msg.Token()) +} + +func clientResetObservationHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client, authCtx pbCQRS.AuthorizationContext) { + observer, err := client.server.observeResourceContainer.PopByRemoteAddrToken(client.remoteAddrString(), req.Msg.Token()) + if err != nil { + return + } + err = client.server.projection.Unregister(observer.deviceId) + if err != nil { + fmt.Errorf("DeviceId: %v: cannot reset resource observation %v.%v: %v", authCtx.DeviceId, observer.deviceId, observer.resourceId, err) + } +} diff --git a/coap-gateway/service/clientObserveHandler_test.go b/coap-gateway/service/clientObserveHandler_test.go new file mode 100644 index 000000000..edcaa8801 --- /dev/null +++ b/coap-gateway/service/clientObserveHandler_test.go @@ -0,0 +1,112 @@ +package service + +import ( + "context" + "testing" + "time" + + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" +) + +func Test_clientObserveHandler(t *testing.T) { + var config Config + err := envconfig.Process("", &config) + assert.NoError(t, err) + config.Addr = "127.0.0.1:5684" + config.AuthServerAddr = "localhost:12345" + config.ResourceAggregateAddr = "localhost:12348" + config.ResourceDirectoryAddr = "localhost:12349" + + resourceDB := t.Name() + "_resourceDB" + + shutdownSA := testCreateAuthServer(t, config.AuthServerAddr) + defer shutdownSA() + shutdownRA := testCreateResourceAggregate(t, resourceDB, config.ResourceAggregateAddr, config.AuthServerAddr) + defer shutdownRA() + shutdownRS := testCreateResourceDirectory(t, resourceDB, config.ResourceDirectoryAddr, config.AuthServerAddr) + defer shutdownRS() + shutdownGW := testCreateCoapGateway(t, resourceDB, config) + defer shutdownGW() + + co := testCoapDial(t, config.Addr, config.Net) + if co == nil { + return + } + defer co.Close() + + NewGetRequest := func(path string, observe uint32, token []byte) gocoap.Message { + msg, err := co.NewGetRequest(path) + msg.SetObserve(observe) + if token != nil { + msg.SetToken(token) + } + assert.NoError(t, err) + return msg + } + + type args struct { + req gocoap.Message + } + tests := []struct { + name string + args args + wantsCode coapCodes.Code + }{ + + { + name: "invalid observe", + args: args{ + req: NewGetRequest(resourceRoute+"/dev0/res0", 123, nil), + }, + wantsCode: coapCodes.BadRequest, + }, + + { + name: "observe - not exist resource", + args: args{ + req: NewGetRequest(resourceRoute+"/dev0/res0", 0, nil), + }, + wantsCode: coapCodes.BadRequest, + }, + + { + name: "unobserve - not exist resource", + args: args{ + req: NewGetRequest(resourceRoute+"/dev0/res0", 1, nil), + }, + wantsCode: coapCodes.BadRequest, + }, + + { + name: "observe", + args: args{ + req: NewGetRequest(resourceRoute+"/"+CertIdentity+TestAResourceHref, 0, []byte("observe")), + }, + wantsCode: coapCodes.Content, + }, + + { + name: "unobserve", + args: args{ + req: NewGetRequest(resourceRoute+"/"+CertIdentity+TestAResourceHref, 1, []byte("observe")), + }, + wantsCode: coapCodes.Content, + }, + } + + testPrepareDevice(t, co) + time.Sleep(time.Second) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), TestExchangeTimeout) + defer cancel() + resp, err := co.ExchangeWithContext(ctx, tt.args.req) + assert.NoError(t, err) + assert.Equal(t, tt.wantsCode, resp.Code()) + }) + } +} diff --git a/coap-gateway/service/clientResetHandler.go b/coap-gateway/service/clientResetHandler.go new file mode 100644 index 000000000..870654543 --- /dev/null +++ b/coap-gateway/service/clientResetHandler.go @@ -0,0 +1,10 @@ +package service + +import ( + gocoap "github.com/go-ocf/go-coap" +) + +func clientResetHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + authCtx := client.loadAuthorizationContext() + clientResetObservationHandler(s, req, client, authCtx.AuthorizationContext) +} diff --git a/coap-gateway/service/clientResetHandler_test.go b/coap-gateway/service/clientResetHandler_test.go new file mode 100644 index 000000000..5b2cfaccf --- /dev/null +++ b/coap-gateway/service/clientResetHandler_test.go @@ -0,0 +1,107 @@ +package service + +import ( + "context" + "testing" + "time" + + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" +) + +func Test_clientResetHandler(t *testing.T) { + var config Config + err := envconfig.Process("", &config) + assert.NoError(t, err) + config.AuthServerAddr = "localhost:12345" + config.ResourceAggregateAddr = "localhost:12348" + config.ResourceDirectoryAddr = "localhost:12349" + resourceDB := t.Name() + "_resourceDB" + + shutdownSA := testCreateAuthServer(t, config.AuthServerAddr) + defer shutdownSA() + shutdownRA := testCreateResourceAggregate(t, resourceDB, config.ResourceAggregateAddr, config.AuthServerAddr) + defer shutdownRA() + shutdownRS := testCreateResourceDirectory(t, resourceDB, config.ResourceDirectoryAddr, config.AuthServerAddr) + defer shutdownRS() + shutdownGW := testCreateCoapGateway(t, resourceDB, config) + defer shutdownGW() + + co := testCoapDial(t, config.Addr, config.Net) + if co == nil { + return + } + defer co.Close() + + NewGetRequest := func(path string, observe uint32, token []byte) gocoap.Message { + msg, err := co.NewGetRequest(path) + msg.SetObserve(observe) + if token != nil { + msg.SetToken(token) + } + assert.NoError(t, err) + return msg + } + + NewReset := func(token []byte) gocoap.Message { + msg := co.NewMessage(gocoap.MessageParams{ + Code: coapCodes.Empty, + Token: token, + }) + return msg + } + + type args struct { + req gocoap.Message + } + tests := []struct { + name string + args args + wantsCode coapCodes.Code + }{ + { + name: "observe", + args: args{ + req: NewGetRequest(resourceRoute+"/"+CertIdentity+TestAResourceHref, 0, []byte("observe")), + }, + wantsCode: coapCodes.Content, + }, + { + name: "reset", + args: args{ + req: NewReset([]byte("observe")), + }, + wantsCode: coapCodes.Empty, + }, + { + name: "unobserve", + args: args{ + req: NewGetRequest(resourceRoute+"/"+CertIdentity+TestAResourceHref, 1, []byte("observe")), + }, + wantsCode: coapCodes.BadRequest, + }, + } + + testPrepareDevice(t, co) + time.Sleep(time.Second) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.args.req.Code() == coapCodes.Empty { + ctx, cancel := context.WithTimeout(context.Background(), TestExchangeTimeout) + defer cancel() + err := co.WriteMsgWithContext(ctx, tt.args.req) + assert.NoError(t, err) + } else { + ctx, cancel := context.WithTimeout(context.Background(), TestExchangeTimeout) + defer cancel() + resp, err := co.ExchangeWithContext(ctx, tt.args.req) + assert.NoError(t, err) + assert.Equal(t, tt.wantsCode, resp.Code()) + } + time.Sleep(time.Second) // to avoid reorder test case + }) + } +} diff --git a/coap-gateway/service/clientRetrieveHandler.go b/coap-gateway/service/clientRetrieveHandler.go new file mode 100644 index 000000000..498ef7dfc --- /dev/null +++ b/coap-gateway/service/clientRetrieveHandler.go @@ -0,0 +1,149 @@ +package service + +import ( + "context" + "fmt" + "io" + "strings" + "time" + + "github.com/go-ocf/cloud/coap-gateway/coapconv" + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/go-ocf/kit/log" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRS "github.com/go-ocf/cloud/resource-directory/pb/resource-shadow" + "github.com/gofrs/uuid" + "google.golang.org/grpc/status" +) + +// URIToDeviceIDHref convert uri to deviceID and href. Expected input "/oic/route/{deviceID}/{Href}". +func URIToDeviceIDHref(msg gocoap.Message) (deviceID, href string, err error) { + path := msg.Path() + if len(path) < 4 { + return "", "", fmt.Errorf("cannot parse deviceID, href from uri") + } + return path[2], fixHref(strings.Join(path[3:], "/")), nil +} + +func getResourceInterface(msg gocoap.Message) string { + for _, queryRaw := range msg.Options(gocoap.URIQuery) { + if query, ok := queryRaw.(string); ok && strings.HasPrefix(query, "if=") { + return strings.TrimLeft(query, "if=") + } + } + return "" +} + +func clientRetrieveHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + t := time.Now() + defer func() { + log.Debugf("clientRetrieveHandler takes %v", time.Since(t)) + }() + authCtx := client.loadAuthorizationContext() + + deviceID, href, err := URIToDeviceIDHref(req.Msg) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot handle retrieve resource: %w", authCtx.DeviceId, err), s, client, coapCodes.BadRequest) + return + } + + var content *pbRA.Content + var code coapCodes.Code + resourceInterface := getResourceInterface(req.Msg) + resourceID := resource2UUID(deviceID, href) + if resourceInterface == "" { + content, code, err = clientRetrieveFromResourceShadowHandler(kitNetGrpc.CtxWithToken(req.Ctx, authCtx.AccessToken), client, resourceID) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot retrieve resource /%v%v from resource shadow: %w", authCtx.DeviceId, deviceID, href, err), s, client, code) + return + } + } else { + content, code, err = clientRetrieveFromDeviceHandler(req, client, deviceID, resourceID, resourceInterface) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot retrieve resource /%v%v from device: %w", authCtx.DeviceId, deviceID, href, err), s, client, code) + return + } + } + + if content == nil || len(content.Data) == 0 { + sendResponse(s, client, code, gocoap.TextPlain, nil) + return + } + mediaType, err := coapconv.MakeMediaType(content.CoapContentFormat, content.ContentType) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot retrieve resource /%v%v: %w", authCtx.DeviceId, deviceID, href, err), s, client, code) + return + } + sendResponse(s, client, code, mediaType, content.Data) +} + +func clientRetrieveFromResourceShadowHandler(ctx context.Context, client *Client, resourceID string) (*pbRA.Content, coapCodes.Code, error) { + authCtx := client.loadAuthorizationContext() + retrieveResourcesValuesClient, err := client.server.rsClient.RetrieveResourcesValues(kitNetGrpc.CtxWithToken(ctx, authCtx.AccessToken), &pbRS.RetrieveResourcesValuesRequest{ + ResourceIdsFilter: []string{resourceID}, + AuthorizationContext: &authCtx.AuthorizationContext, + }) + if err != nil { + return nil, coapconv.GrpcCode2CoapCode(status.Convert(err).Code(), coapCodes.GET), err + } + defer retrieveResourcesValuesClient.CloseSend() + for { + resourceValue, err := retrieveResourcesValuesClient.Recv() + if err == io.EOF { + break + } + if err != nil { + return nil, coapconv.GrpcCode2CoapCode(status.Convert(err).Code(), coapCodes.GET), err + } + if resourceValue.ResourceId == resourceID && resourceValue.Content != nil { + return resourceValue.Content, coapCodes.Content, nil + } + } + return nil, coapCodes.NotFound, fmt.Errorf("not found") +} + +func clientRetrieveFromDeviceHandler(req *gocoap.Request, client *Client, deviceID, resourceID, resourceInterface string) (*pbRA.Content, coapCodes.Code, error) { + authCtx := client.loadAuthorizationContext() + correlationIDUUID, err := uuid.NewV4() + if err != nil { + return nil, coapCodes.InternalServerError, err + } + + correlationID := correlationIDUUID.String() + + notify := client.server.retrieveNotificationContainer.Add(correlationID) + defer client.server.retrieveNotificationContainer.Remove(correlationID) + + loaded, err := client.server.projection.Register(req.Ctx, deviceID) + if err != nil { + return nil, coapCodes.NotFound, fmt.Errorf("cannot register device to projection: %w", err) + } + defer client.server.projection.Unregister(deviceID) + if !loaded { + if len(client.server.projection.Models(deviceID, resourceID)) == 0 { + err = client.server.projection.ForceUpdate(req.Ctx, deviceID, resourceID) + if err != nil { + return nil, coapCodes.NotFound, err + } + } + } + + request := coapconv.MakeRetrieveResourceRequest(resourceID, resourceInterface, correlationID, authCtx.AuthorizationContext, req) + + _, err = client.server.raClient.RetrieveResource(kitNetGrpc.CtxWithToken(req.Ctx, authCtx.AccessToken), &request) + if err != nil { + return nil, coapconv.GrpcCode2CoapCode(status.Convert(err).Code(), coapCodes.GET), err + } + + // first wait for notification + timeoutCtx, cancel := context.WithTimeout(req.Ctx, client.server.RequestTimeout) + defer cancel() + select { + case processed := <-notify: + return processed.GetContent(), coapconv.StatusToCoapCode(processed.Status, coapCodes.GET), nil + case <-timeoutCtx.Done(): + return nil, coapCodes.GatewayTimeout, fmt.Errorf("timeout") + } +} diff --git a/coap-gateway/service/clientRetrieveHandler_test.go b/coap-gateway/service/clientRetrieveHandler_test.go new file mode 100644 index 000000000..01d95c1fb --- /dev/null +++ b/coap-gateway/service/clientRetrieveHandler_test.go @@ -0,0 +1,107 @@ +package service + +import ( + "context" + "testing" + "time" + + "github.com/go-ocf/kit/log" + + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" +) + +func Test_clientRetrieveHandler(t *testing.T) { + var config Config + err := envconfig.Process("", &config) + assert.NoError(t, err) + config.AuthServerAddr = "localhost:12345" + config.ResourceAggregateAddr = "localhost:12348" + config.ResourceDirectoryAddr = "localhost:12349" + + resourceDB := t.Name() + "_resourceDB" + + shutdownSA := testCreateAuthServer(t, config.AuthServerAddr) + defer shutdownSA() + shutdownRA := testCreateResourceAggregate(t, resourceDB, config.ResourceAggregateAddr, config.AuthServerAddr) + defer shutdownRA() + shutdownRS := testCreateResourceDirectory(t, resourceDB, config.ResourceDirectoryAddr, config.AuthServerAddr) + defer shutdownRS() + shutdownGW := testCreateCoapGateway(t, resourceDB, config) + defer shutdownGW() + + co := testCoapDial(t, config.Addr, config.Net) + if co == nil { + return + } + defer co.Close() + + NewGetRequest := func(path string) gocoap.Message { + msg, err := co.NewGetRequest(path) + assert.NoError(t, err) + return msg + } + + NewGetRequestWithInterface := func(path, resourceInterface string) gocoap.Message { + msg, err := co.NewGetRequest(path) + assert.NoError(t, err) + msg.AddOption(gocoap.URIQuery, "if="+resourceInterface) + return msg + } + + type args struct { + req gocoap.Message + } + tests := []struct { + name string + args args + wantsCode coapCodes.Code + }{ + { + name: "invalid href", + args: args{ + req: NewGetRequest(resourceRoute + TestAResourceHref), + }, + wantsCode: coapCodes.BadRequest, + }, + { + name: "not found", + args: args{ + req: NewGetRequest(resourceRoute + "/dev0/res0"), + }, + wantsCode: coapCodes.NotFound, + }, + { + name: "found", + args: args{ + req: NewGetRequest(resourceRoute + "/" + CertIdentity + TestAResourceHref), + }, + wantsCode: coapCodes.Content, + }, + { + name: "found with interface", + args: args{ + req: NewGetRequestWithInterface(resourceRoute+"/"+CertIdentity+TestAResourceHref, "oic.if.baseline"), + }, + wantsCode: coapCodes.Content, + }, + } + + testPrepareDevice(t, co) + time.Sleep(time.Second) // for publish content of device resources + + log.Setup(log.Config{ + Debug: true, + }) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), TestExchangeTimeout) + defer cancel() + resp, err := co.ExchangeWithContext(ctx, tt.args.req) + assert.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 new file mode 100644 index 000000000..2b77fb349 --- /dev/null +++ b/coap-gateway/service/clientUpdateHandler.go @@ -0,0 +1,91 @@ +package service + +import ( + "context" + "fmt" + "time" + + "github.com/go-ocf/cloud/coap-gateway/coapconv" + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/go-ocf/kit/log" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + "github.com/gofrs/uuid" + "google.golang.org/grpc/status" +) + +func clientUpdateHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + t := time.Now() + defer func() { + log.Debugf("clientUpdateHandler takes %v", time.Since(t)) + }() + + authCtx := client.loadAuthorizationContext() + deviceID, href, err := URIToDeviceIDHref(req.Msg) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot handle update resource: %w", authCtx.DeviceId, err), s, client, coapCodes.BadRequest) + return + } + + resourceID := resource2UUID(deviceID, href) + content, code, err := clientUpdateDeviceHandler(req, client, deviceID, resourceID) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot handle update resource /%v%v: %w", authCtx.DeviceId, deviceID, href, err), s, client, code) + return + } + if content == nil || len(content.Data) == 0 { + sendResponse(s, client, code, gocoap.TextPlain, nil) + return + } + mediaType, err := coapconv.MakeMediaType(content.CoapContentFormat, content.ContentType) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot encode response for update resource /%v%v: %w", authCtx.DeviceId, deviceID, href, err), s, client, code) + return + } + sendResponse(s, client, code, mediaType, content.Data) +} + +func clientUpdateDeviceHandler(req *gocoap.Request, client *Client, deviceID, resourceID string) (*pbRA.Content, coapCodes.Code, error) { + authCtx := client.loadAuthorizationContext() + correlationIDUUID, err := uuid.NewV4() + if err != nil { + return nil, coapCodes.InternalServerError, err + } + + correlationID := correlationIDUUID.String() + + notify := client.server.updateNotificationContainer.Add(correlationID) + defer client.server.updateNotificationContainer.Remove(correlationID) + + loaded, err := client.server.projection.Register(req.Ctx, deviceID) + if err != nil { + return nil, coapCodes.NotFound, fmt.Errorf("cannot register device to projection: %w", err) + } + defer client.server.projection.Unregister(deviceID) + if !loaded { + if len(client.server.projection.Models(deviceID, resourceID)) == 0 { + err = client.server.projection.ForceUpdate(req.Ctx, deviceID, resourceID) + if err != nil { + return nil, coapCodes.NotFound, err + } + } + } + + request := coapconv.MakeUpdateResourceRequest(resourceID, correlationID, authCtx.AuthorizationContext, req) + + _, err = client.server.raClient.UpdateResource(kitNetGrpc.CtxWithToken(req.Ctx, authCtx.AccessToken), &request) + if err != nil { + return nil, coapconv.GrpcCode2CoapCode(status.Convert(err).Code(), coapCodes.POST), err + } + + // first wait for notification + timeoutCtx, cancel := context.WithTimeout(req.Ctx, client.server.RequestTimeout) + defer cancel() + select { + case processed := <-notify: + return processed.GetContent(), coapconv.StatusToCoapCode(processed.Status, coapCodes.POST), nil + case <-timeoutCtx.Done(): + return nil, coapCodes.GatewayTimeout, fmt.Errorf("timeout") + } +} diff --git a/coap-gateway/service/clientUpdateHandler_test.go b/coap-gateway/service/clientUpdateHandler_test.go new file mode 100644 index 000000000..431ff8420 --- /dev/null +++ b/coap-gateway/service/clientUpdateHandler_test.go @@ -0,0 +1,89 @@ +package service + +import ( + "bytes" + "context" + "testing" + "time" + + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" +) + +func Test_clientUpdateHandler(t *testing.T) { + var config Config + err := envconfig.Process("", &config) + assert.NoError(t, err) + config.AuthServerAddr = "localhost:12345" + config.ResourceAggregateAddr = "localhost:12348" + config.ResourceDirectoryAddr = "localhost:12349" + config.RequestTimeout = time.Second * 7 + config.Net = "tcp-tls" + resourceDB := t.Name() + "_resourceDB" + + shutdownSA := testCreateAuthServer(t, config.AuthServerAddr) + defer shutdownSA() + shutdownRA := testCreateResourceAggregate(t, resourceDB, config.ResourceAggregateAddr, config.AuthServerAddr) + defer shutdownRA() + shutdownGW := testCreateCoapGateway(t, resourceDB, config) + defer shutdownGW() + + co := testCoapDial(t, config.Addr, config.Net) + if co == nil { + return + } + defer co.Close() + + NewPostRequest := func(href string, contentFormat gocoap.MediaType, payload []byte) gocoap.Message { + msg, err := co.NewPostRequest(href, contentFormat, bytes.NewBuffer(payload)) + assert.NoError(t, err) + return msg + } + + type args struct { + req gocoap.Message + } + tests := []struct { + name string + args args + wantsCode coapCodes.Code + }{ + { + name: "invalid href", + args: args{ + req: NewPostRequest(resourceRoute+TestAResourceHref, gocoap.TextPlain, []byte{}), + }, + wantsCode: coapCodes.BadRequest, + }, + { + name: "not found", + args: args{ + req: NewPostRequest(resourceRoute+"/a/b", gocoap.TextPlain, []byte{}), + }, + wantsCode: coapCodes.NotFound, + }, + { + name: "valid", + args: args{ + req: NewPostRequest(resourceRoute+"/"+CertIdentity+TestAResourceHref, gocoap.TextPlain, []byte("data")), + }, + wantsCode: coapCodes.Changed, + }, + } + + testPrepareDevice(t, co) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), TestExchangeTimeout) + defer cancel() + resp, err := co.ExchangeWithContext(ctx, tt.args.req) + assert.NoError(t, err) + if resp != nil { + assert.Equal(t, tt.wantsCode, resp.Code()) + } + }) + } +} diff --git a/coap-gateway/service/config.go b/coap-gateway/service/config.go new file mode 100644 index 000000000..f31de2cf8 --- /dev/null +++ b/coap-gateway/service/config.go @@ -0,0 +1,23 @@ +package service + +import ( + "time" +) + +//config for application +type Config struct { + Addr string `envconfig:"ADDRESS" default:"0.0.0.0:5684"` + ExternalPort uint16 `envconfig:"EXTERNAL_PORT" default:"5684"` + Net string `envconfig:"NETWORK" default:"tcp"` + FQDN string `envconfig:"FQDN" default:"coapgw.ocf.cloud"` + AuthServerAddr string `envconfig:"AUTH_SERVER_ADDRESS" default:"127.0.0.1:9100"` + ResourceAggregateAddr string `envconfig:"RESOURCE_AGGREGATE_ADDRESS" default:"127.0.0.1:9100"` + ResourceDirectoryAddr string `envconfig:"RESOURCE_DIRECTORY_ADDRESS" default:"127.0.0.1:9100"` + RequestTimeout time.Duration `envconfig:"REQUEST_TIMEOUT" default:"10s"` + KeepaliveEnable bool `envconfig:"KEEPALIVE_ENABLE" default:"true"` + KeepaliveTimeoutConnection time.Duration `envconfig:"KEEPALIVE_TIMEOUT_CONNECTION" default:"20s"` + DisableTCPSignalMessageCSM bool `envconfig:"DISABLE_TCP_SIGNAL_MESSAGE_CSM" default:"false"` + DisablePeerTCPSignalMessageCSMs bool `envconfig:"DISABLE_PEER_TCP_SIGNAL_MESSAGE_CSMS" default:"true"` + SendErrorTextInResponse bool `envconfig:"ERROR_IN_RESPONSE" default:"true"` + ConnectionsHeartBeat time.Duration `envconfig:"CONNECTIONS_HEART_BEAT" default:"4s"` +} diff --git a/coap-gateway/service/decodeMsgToDebug.go b/coap-gateway/service/decodeMsgToDebug.go new file mode 100644 index 000000000..5343e33c0 --- /dev/null +++ b/coap-gateway/service/decodeMsgToDebug.go @@ -0,0 +1,47 @@ +package service + +import ( + "bytes" + "fmt" + + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/go-ocf/kit/codec/cbor" + "github.com/go-ocf/kit/log" +) + +func decodeMsgToDebug(client *Client, resp gocoap.Message, tag string) { + buf := bytes.NewBuffer(make([]byte, 0, 2048)) + fmt.Fprintf(buf, "\n-------------------%v------------------\n", tag) + fmt.Fprintf(buf, "DeviceId: %v\n", getDeviceId(client)) + fmt.Fprintf(buf, "Token: %v\n", resp.Token()) + fmt.Fprintf(buf, "Path: %v\n", resp.PathString()) + fmt.Fprintf(buf, "Code: %v\n", resp.Code()) + fmt.Fprintf(buf, "Type: %v\n", resp.Type()) + fmt.Fprintf(buf, "Query: %v\n", resp.Options(gocoap.URIQuery)) + fmt.Fprintf(buf, "ContentFormat: %v\n", resp.Options(gocoap.ContentFormat)) + if resp.Code() == coapCodes.GET || resp.Code() == coapCodes.Content { + fmt.Fprintf(buf, "Observe: %v\n", resp.Option(gocoap.Observe)) + } + if mediaType, ok := resp.Option(gocoap.ContentFormat).(gocoap.MediaType); ok { + switch mediaType { + case gocoap.AppCBOR, gocoap.AppOcfCbor: + s, err := cbor.ToJSON(resp.Payload()) + if err != nil { + log.Errorf("Cannot encode %v to JSON: %v", resp.Payload(), err) + } + fmt.Fprintf(buf, "CBOR:\n%v", s) + case gocoap.TextPlain: + fmt.Fprintf(buf, "TXT:\n%v", string(resp.Payload())) + case gocoap.AppJSON: + fmt.Fprintf(buf, "JSON:\n%v", string(resp.Payload())) + case gocoap.AppXML: + fmt.Fprintf(buf, "XML:\n%v", string(resp.Payload())) + default: + fmt.Fprintf(buf, "RAW(%v):\n%v", mediaType, resp.Payload()) + } + } else { + fmt.Fprintf(buf, "RAW:\n%v", resp.Payload()) + } + log.Debug(buf.String()) +} diff --git a/coap-gateway/service/deviceCloudStatus.go b/coap-gateway/service/deviceCloudStatus.go new file mode 100644 index 000000000..a99333fef --- /dev/null +++ b/coap-gateway/service/deviceCloudStatus.go @@ -0,0 +1,58 @@ +package service + +import ( + "context" + + "github.com/go-ocf/kit/codec/cbor" + + gocoap "github.com/go-ocf/go-coap" + cqrsRA "github.com/go-ocf/cloud/resource-aggregate/cqrs" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + "github.com/go-ocf/sdk/schema/cloud" +) + +func (client *Client) PublishCloudDeviceStatus(ctx context.Context, deviceID string, authCtx pbCQRS.AuthorizationContext) error { + devStatus := pbRA.Resource{ + Id: cqrsRA.MakeResourceId(deviceID, cloud.StatusHref), + Href: cloud.StatusHref, + ResourceTypes: cloud.StatusResourceTypes, + Interfaces: cloud.StatusInterfaces, + DeviceId: deviceID, + Policies: &pbRA.Policies{ + BitFlags: 3, + }, + Title: "Cloud device status", + } + _, err := client.publishResource(ctx, &devStatus, int32(0), client.remoteAddrString(), client.coapConn.Sequence(), authCtx) + return err +} + +func (client *Client) UpdateCloudDeviceStatus(ctx context.Context, deviceID string, authCtx pbCQRS.AuthorizationContext, online bool) error { + status := cloud.Status{ + ResourceTypes: cloud.StatusResourceTypes, + Interfaces: cloud.StatusInterfaces, + Online: online, + } + data, err := cbor.Encode(status) + if err != nil { + return err + } + + request := pbRA.NotifyResourceChangedRequest{ + ResourceId: cqrsRA.MakeResourceId(deviceID, cloud.StatusHref), + Content: &pbRA.Content{ + ContentType: gocoap.AppOcfCbor.String(), + CoapContentFormat: int32(gocoap.AppOcfCbor), + Data: data, + }, + CommandMetadata: &pbCQRS.CommandMetadata{ + Sequence: client.coapConn.Sequence(), + ConnectionId: client.remoteAddrString(), + }, + AuthorizationContext: &authCtx, + } + + _, err = client.server.raClient.NotifyResourceChanged(ctx, &request) + return err +} diff --git a/coap-gateway/service/logAndWriteErrorResponse.go b/coap-gateway/service/logAndWriteErrorResponse.go new file mode 100644 index 000000000..624856c6c --- /dev/null +++ b/coap-gateway/service/logAndWriteErrorResponse.go @@ -0,0 +1,28 @@ +package service + +import ( + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/go-ocf/kit/log" +) + +func logAndWriteErrorResponse(err error, s gocoap.ResponseWriter, client *Client, code coapCodes.Code) { + msg := s.NewResponse(code) + if err != nil { + log.Errorf("%v", err) + } + if msg != nil { + if client != nil && client.server.SendErrorTextInResponse { + msg.SetOption(gocoap.ContentFormat, gocoap.TextPlain) + msg.SetPayload([]byte(err.Error())) + } else { + msg.SetOption(gocoap.ContentFormat, gocoap.AppCBOR) + msg.SetPayload([]byte{0xA0}) // empty object + } + err = s.WriteMsg(msg) + if err != nil { + log.Errorf("cannot send error to %v: %v", getDeviceId(client), err) + } + decodeMsgToDebug(client, msg, "SEND-ERROR") + } +} diff --git a/coap-gateway/service/observeResourceContainer.go b/coap-gateway/service/observeResourceContainer.go new file mode 100644 index 000000000..0c356ea3c --- /dev/null +++ b/coap-gateway/service/observeResourceContainer.go @@ -0,0 +1,210 @@ +package service + +import ( + "encoding/base64" + "fmt" + "sync" + + gocoap "github.com/go-ocf/go-coap" +) + +type observeResource struct { + remoteAddr string + deviceId string + resourceId string + token []byte + client *Client + responseWriter gocoap.ResponseWriter + + mutex sync.Mutex + observe uint32 +} + +type observeResourceContainer struct { + observersByResource map[string]map[string]map[string]*observeResource //resourceId, remoteAddr, token + observersByRemoteAddr map[string]map[string]*observeResource //remoteAddr, token + mutex sync.Mutex +} + +func (r *observeResource) Observe() uint32 { + r.mutex.Lock() + defer r.mutex.Unlock() + + r.observe++ + if r.observe >= 1<<24 || r.observe < 2 { + r.observe = 2 + } + return r.observe +} + +func NewObserveResourceContainer() *observeResourceContainer { + return &observeResourceContainer{ + observersByResource: make(map[string]map[string]map[string]*observeResource), + observersByRemoteAddr: make(map[string]map[string]*observeResource), + } +} + +func tokenToString(token []byte) string { + return base64.StdEncoding.EncodeToString(token) +} + +func (c *observeResourceContainer) prepareObserversByResourceLocked(resourceId, remoteAddr, token string) (map[string]*observeResource, error) { + var ok bool + var clients map[string]map[string]*observeResource + var tokens map[string]*observeResource + + if clients, ok = c.observersByResource[resourceId]; !ok { + clients = make(map[string]map[string]*observeResource) + c.observersByResource[resourceId] = clients + } + if tokens, ok = clients[remoteAddr]; !ok { + tokens = make(map[string]*observeResource) + clients[remoteAddr] = tokens + } + if _, ok = tokens[token]; ok { + return nil, fmt.Errorf("token already exists") + } + return tokens, nil +} + +func (c *observeResourceContainer) prepareObserversByDeviceLocked(remoteAddr, token string) (map[string]*observeResource, error) { + var ok bool + var tokens map[string]*observeResource + + if tokens, ok = c.observersByRemoteAddr[remoteAddr]; !ok { + tokens = make(map[string]*observeResource) + c.observersByRemoteAddr[remoteAddr] = tokens + } + if _, ok = tokens[token]; ok { + return nil, fmt.Errorf("token already exists") + } + return tokens, nil +} + +func (c *observeResourceContainer) Add(observeResource observeResource) error { + tokenStr := tokenToString(observeResource.token) + + c.mutex.Lock() + defer c.mutex.Unlock() + byResource, err := c.prepareObserversByResourceLocked(observeResource.resourceId, observeResource.remoteAddr, tokenStr) + if err != nil { + return fmt.Errorf("cannot observe resource observersByResource[%v][%v][%v]: %v", observeResource.resourceId, observeResource.remoteAddr, observeResource.token, err) + } + byDevice, err := c.prepareObserversByDeviceLocked(observeResource.remoteAddr, tokenStr) + if err != nil { + c.removeByResourceLocked(observeResource.resourceId, observeResource.remoteAddr, tokenStr) + //this cannot occurs - it mean that byResource and byDevice are unsync + return fmt.Errorf("cannot observe resource observersByRemoteAddr[%v][%v]: %v", observeResource.remoteAddr, observeResource.token, err) + } + + byResource[tokenStr] = &observeResource + byDevice[tokenStr] = &observeResource + return nil +} + +func (c *observeResourceContainer) Find(resourceId string) []*observeResource { + found := make([]*observeResource, 0, 128) + c.mutex.Lock() + defer c.mutex.Unlock() + for _, clients := range c.observersByResource[resourceId] { + for _, ob := range clients { + found = append(found, ob) + } + } + return found +} + +func (c *observeResourceContainer) removeByResourceLocked(resourceId, remoteAddr, token string) error { + found := false + if clients, ok := c.observersByResource[resourceId]; ok { + if tokens, ok := clients[remoteAddr]; ok { + if _, ok := tokens[token]; ok { + delete(tokens, token) + found = true + } + if len(clients[remoteAddr]) == 0 { + delete(clients, remoteAddr) + } + } + if len(c.observersByResource[resourceId]) == 0 { + delete(c.observersByResource, resourceId) + } + } + if !found { + return fmt.Errorf("not found") + } + return nil +} + +func (c *observeResourceContainer) RemoveByResource(resourceId, remoteAddr string, token []byte) error { + tokenStr := tokenToString(token) + c.mutex.Lock() + defer c.mutex.Unlock() + err := c.removeByResourceLocked(resourceId, remoteAddr, tokenStr) + if err != nil { + return fmt.Errorf("cannot remove observer of resource: %v", err) + } + if tokens, ok := c.observersByRemoteAddr[remoteAddr]; ok { + if _, ok := tokens[tokenStr]; ok { + delete(tokens, tokenStr) + if len(tokens) == 0 { + delete(c.observersByRemoteAddr, remoteAddr) + } + return nil + } + return fmt.Errorf("unstable container - observersByRemoteAddr[%v][%v]", remoteAddr, token) + } else { + return fmt.Errorf("unstable container - observersByRemoteAddr[%v]", remoteAddr) + } +} + +func (c *observeResourceContainer) PopByRemoteAddr(remoteAddr string) ([]*observeResource, error) { + poped := make([]*observeResource, 0, 32) + var tokens map[string]*observeResource + var ok bool + + c.mutex.Lock() + defer c.mutex.Unlock() + + if tokens, ok = c.observersByRemoteAddr[remoteAddr]; !ok { + return nil, fmt.Errorf("not found") + } + delete(c.observersByRemoteAddr, remoteAddr) + var errors []error + for token, obs := range tokens { + err := c.removeByResourceLocked(obs.resourceId, remoteAddr, token) + if err != nil { + errors = append(errors, fmt.Errorf("observersByResource[%v][%v][%v]:%v", obs.resourceId, remoteAddr, token, err)) + } + poped = append(poped, obs) + } + if len(errors) > 0 { + return nil, fmt.Errorf("unstable container: %v", errors) + } + return poped, nil +} + +func (c *observeResourceContainer) PopByRemoteAddrToken(remoteAddr string, token []byte) (*observeResource, error) { + var obs *observeResource + var tokens map[string]*observeResource + var ok bool + + c.mutex.Lock() + defer c.mutex.Unlock() + + if tokens, ok = c.observersByRemoteAddr[remoteAddr]; !ok { + return obs, fmt.Errorf("remote address not found") + } + + tokenStr := tokenToString(token) + if obs, ok = tokens[tokenStr]; !ok { + return obs, fmt.Errorf("token not found") + } + delete(tokens, tokenStr) + + err := c.removeByResourceLocked(obs.resourceId, remoteAddr, tokenStr) + if err != nil { + return obs, fmt.Errorf("unstable container: observersByResource[%v][%v][%v]:%v", obs.resourceId, remoteAddr, token, err) + } + return obs, nil +} diff --git a/coap-gateway/service/observeResourceContainer_test.go b/coap-gateway/service/observeResourceContainer_test.go new file mode 100644 index 000000000..bfeb98aec --- /dev/null +++ b/coap-gateway/service/observeResourceContainer_test.go @@ -0,0 +1,388 @@ +package service + +import ( + "sort" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_observeResourceContainer_Add(t *testing.T) { + type args struct { + observeResource observeResource + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "ok", + args: args{ + observeResource: observeResource{ + resourceId: "resourceId", + remoteAddr: "remoteAddr", + token: []byte("token"), + }, + }, + }, + { + name: "ok-token", + args: args{ + observeResource: observeResource{ + resourceId: "resourceId", + remoteAddr: "remoteAddr", + token: []byte("token1"), + }, + }, + }, + { + name: "ok-remoteAddr", + args: args{ + observeResource: observeResource{ + resourceId: "resourceId", + remoteAddr: "remoteAddr1", + token: []byte("token"), + }, + }, + }, + { + name: "duplicit", + args: args{ + observeResource: observeResource{ + resourceId: "resourceId", + remoteAddr: "remoteAddr", + token: []byte("token"), + }, + }, + wantErr: true, + }, + } + + c := NewObserveResourceContainer() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := c.Add(tt.args.observeResource) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } + + assert.Equal(t, 2, len(c.observersByRemoteAddr)) + assert.Equal(t, 1, len(c.observersByResource)) +} + +type sortObserveResource []*observeResource + +func (s sortObserveResource) Len() int { + return len(s) +} +func (s sortObserveResource) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} +func (s sortObserveResource) Less(i, j int) bool { + if s[i].deviceId < s[j].deviceId { + return true + } + if s[i].resourceId < s[j].resourceId { + return true + } + if tokenToString(s[i].token) < tokenToString(s[j].token) { + return true + } + return false +} + +func Test_observeResourceContainer_Find(t *testing.T) { + obs := []*observeResource{ + &observeResource{ + resourceId: "a", + token: []byte("0"), + }, + &observeResource{ + resourceId: "a", + token: []byte("1"), + }, + &observeResource{ + resourceId: "b", + token: []byte("2"), + }, + } + + type args struct { + resourceId string + } + tests := []struct { + name string + args args + want []*observeResource + }{ + { + name: "found 1", + args: args{ + resourceId: obs[2].resourceId, + }, + want: []*observeResource{obs[2]}, + }, + { + name: "found 2", + args: args{ + resourceId: obs[0].resourceId, + }, + want: obs[:2], + }, + { + name: "not found", + args: args{ + resourceId: "not found", + }, + want: []*observeResource{}, + }, + } + + c := NewObserveResourceContainer() + for _, ob := range obs { + err := c.Add(*ob) + assert.NoError(t, err) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := c.Find(tt.args.resourceId) + sort.Sort(sortObserveResource(tt.want)) + sort.Sort(sortObserveResource(got)) + assert.Equal(t, tt.want, got) + }) + } +} + +func Test_observeResourceContainer_RemoveByResource(t *testing.T) { + obs := []observeResource{ + observeResource{ + resourceId: "a", + token: []byte("0"), + }, + observeResource{ + resourceId: "a", + token: []byte("1"), + }, + observeResource{ + resourceId: "b", + token: []byte("2"), + }, + } + type args struct { + resourceId string + remoteAddr string + token []byte + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "remove0", + args: args{ + resourceId: obs[2].resourceId, + token: obs[2].token, + }, + }, + { + name: "not found", + args: args{ + resourceId: obs[2].resourceId, + token: obs[2].token, + }, + wantErr: true, + }, + { + name: "remove1", + args: args{ + resourceId: obs[1].resourceId, + token: obs[1].token, + }, + }, + { + name: "remove2", + args: args{ + resourceId: obs[0].resourceId, + token: obs[0].token, + }, + }, + } + + c := NewObserveResourceContainer() + for _, ob := range obs { + err := c.Add(ob) + assert.NoError(t, err) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := c.RemoveByResource(tt.args.resourceId, tt.args.remoteAddr, tt.args.token) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } + + assert.Equal(t, 0, len(c.observersByRemoteAddr)) + assert.Equal(t, 0, len(c.observersByResource)) +} + +func Test_observeResourceContainer_PopByRemoteAddr(t *testing.T) { + obs := []*observeResource{ + &observeResource{ + resourceId: "a", + remoteAddr: "A", + token: []byte("0"), + }, + &observeResource{ + resourceId: "a", + remoteAddr: "A", + token: []byte("1"), + }, + &observeResource{ + resourceId: "b", + remoteAddr: "B", + token: []byte("2"), + }, + } + + type args struct { + remoteAddr string + } + tests := []struct { + name string + args args + wantErr bool + want []*observeResource + }{ + { + name: "remove 0", + args: args{ + remoteAddr: obs[2].remoteAddr, + }, + want: []*observeResource{obs[2]}, + }, + { + name: "not found 0", + args: args{ + remoteAddr: obs[2].remoteAddr, + }, + wantErr: true, + }, + { + name: "remove1", + args: args{ + remoteAddr: obs[1].remoteAddr, + }, + want: []*observeResource{obs[0], obs[1]}, + }, + { + name: "not found 1", + args: args{ + remoteAddr: obs[0].remoteAddr, + }, + wantErr: true, + }, + } + + c := NewObserveResourceContainer() + for _, ob := range obs { + err := c.Add(*ob) + assert.NoError(t, err) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := c.PopByRemoteAddr(tt.args.remoteAddr) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + sort.Sort(sortObserveResource(tt.want)) + sort.Sort(sortObserveResource(got)) + assert.Equal(t, tt.want, got) + }) + } + + assert.Equal(t, 0, len(c.observersByRemoteAddr)) + assert.Equal(t, 0, len(c.observersByResource)) +} + +func Test_observeResourceContainer_PopByRemoteAddrToken(t *testing.T) { + obs := []*observeResource{ + &observeResource{ + resourceId: "a", + remoteAddr: "A", + token: []byte("0"), + }, + &observeResource{ + resourceId: "a", + remoteAddr: "A", + token: []byte("1"), + }, + &observeResource{ + resourceId: "b", + remoteAddr: "B", + token: []byte("2"), + }, + } + type args struct { + remoteAddr string + token []byte + } + tests := []struct { + name string + args args + want *observeResource + wantErr bool + }{ + { + name: "ok", + args: args{ + remoteAddr: obs[0].remoteAddr, + token: obs[0].token, + }, + want: obs[0], + }, + { + name: "not found", + args: args{ + remoteAddr: obs[0].remoteAddr, + token: obs[0].token, + }, + wantErr: true, + }, + } + + c := NewObserveResourceContainer() + for _, ob := range obs { + err := c.Add(*ob) + assert.NoError(t, err) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := c.PopByRemoteAddrToken(tt.args.remoteAddr, tt.args.token) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.want, got) + }) + } + + assert.Equal(t, 2, len(c.observersByRemoteAddr)) + assert.Equal(t, 2, len(c.observersByResource)) +} diff --git a/coap-gateway/service/ping.go b/coap-gateway/service/ping.go new file mode 100644 index 000000000..6c6d44e92 --- /dev/null +++ b/coap-gateway/service/ping.go @@ -0,0 +1,97 @@ +package service + +import ( + "fmt" + "sync/atomic" + "time" + + "github.com/go-ocf/kit/codec/cbor" + + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/go-ocf/kit/log" + "github.com/go-ocf/kit/net/coap" +) + +type oicwkping struct { + IntervalArray []int64 `json:"inarray,omitempty"` + Interval int64 `json:"in,omitempty"` +} + +func getPingConfiguration(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + t := time.Now() + defer func() { + log.Debugf("resourcePingGetConfiguration takes %v", time.Since(t)) + }() + + ping := oicwkping{ + IntervalArray: []int64{1}, + } + + accept := coap.GetAccept(req.Msg) + encode, err := coap.GetEncoder(accept) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot send ping configuration: %v", err), s, client, coapCodes.InternalServerError) + return + } + + out, err := encode(ping) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot send ping configuration: %v", err), s, client, coapCodes.InternalServerError) + return + } + + //return not fount to disable ping from client + sendResponse(s, client, coapCodes.Content, accept, out) +} + +func ping(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + t := time.Now() + defer func() { + log.Debugf("resourcePing takes %v", time.Since(t)) + }() + deviceId := client.loadAuthorizationContext().DeviceId + if deviceId == "" { + deviceId = "unknown" + } + + var ping oicwkping + err := cbor.Decode(req.Msg.Payload(), &ping) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId %v: cannot handle ping: %v", deviceId, err), s, client, coapCodes.BadRequest) + return + } + if ping.Interval == 0 { + logAndWriteErrorResponse(fmt.Errorf("DeviceId %v: cannot handle ping: invalid interval value", deviceId), s, client, coapCodes.BadRequest) + return + } + + client.server.oicPingCache.Set(client.remoteAddrString(), client, time.Duration(float64(ping.Interval)*float64(time.Minute)*1.3)) + + //return not fount to disable ping from client + sendResponse(s, client, coapCodes.Valid, gocoap.TextPlain, nil) +} + +func pingOnEvicted(key string, v interface{}) { + if client, ok := v.(*Client); ok { + if atomic.LoadInt32(&client.isClosed) == 0 { + client.Close() + deviceId := client.loadAuthorizationContext().DeviceId + if deviceId == "" { + deviceId = "unknown" + } + log.Errorf("DeviceId %v: ping timeout", deviceId) + } + } +} + +func resourcePingHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + switch req.Msg.Code() { + case coapCodes.GET: + getPingConfiguration(s, req, client) + case coapCodes.POST: + ping(s, req, client) + default: + logAndWriteErrorResponse(fmt.Errorf("Forbidden request from %v", req.Client.RemoteAddr()), s, client, coapCodes.Forbidden) + } +} diff --git a/coap-gateway/service/ping_test.go b/coap-gateway/service/ping_test.go new file mode 100644 index 000000000..47c0a1fd1 --- /dev/null +++ b/coap-gateway/service/ping_test.go @@ -0,0 +1,93 @@ +package service + +import ( + "bytes" + "context" + "testing" + + "github.com/go-ocf/cloud/coap-gateway/uri" + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/go-ocf/kit/codec/cbor" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" +) + +func Test_resourcePingHandler(t *testing.T) { + var config Config + err := envconfig.Process("", &config) + assert.NoError(t, err) + config.AuthServerAddr = "localhost:12345" + config.ResourceAggregateAddr = "localhost:12348" + config.ResourceDirectoryAddr = "localhost:12349" + deviceDB := t.Name() + "_deviceDB" + resourceDB := t.Name() + "_resourceDB" + + shutdownSA := testCreateAuthServer(t, config.AuthServerAddr) + defer shutdownSA() + shutdownDA := testCreateResourceAggregate(t, deviceDB, config.ResourceAggregateAddr, config.AuthServerAddr) + defer shutdownDA() + shutdownGW := testCreateCoapGateway(t, resourceDB, config) + defer shutdownGW() + + co := testCoapDial(t, config.Addr, config.Net) + if co == nil { + return + } + defer co.Close() + + NewPostRequest := func(interval int64) gocoap.Message { + ping := oicwkping{ + Interval: interval, + } + out, err := cbor.Encode(ping) + msg, err := co.NewPostRequest(uri.ResourcePing, gocoap.AppCBOR, bytes.NewBuffer(out)) + assert.NoError(t, err) + return msg + } + NewGetRequest := func() gocoap.Message { + msg, err := co.NewGetRequest(uri.ResourcePing) + assert.NoError(t, err) + return msg + } + + type args struct { + req gocoap.Message + } + tests := []struct { + name string + args args + wantsCode coapCodes.Code + }{ + { + name: "invalid interval", + args: args{ + req: NewPostRequest(0), + }, + wantsCode: coapCodes.BadRequest, + }, + { + name: "get configuration", + args: args{ + req: NewGetRequest(), + }, + wantsCode: coapCodes.Content, + }, + { + name: "ping", + args: args{ + req: NewPostRequest(1), + }, + wantsCode: coapCodes.Valid, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), TestExchangeTimeout) + defer cancel() + resp, err := co.ExchangeWithContext(ctx, tt.args.req) + assert.NoError(t, err) + assert.Equal(t, tt.wantsCode, resp.Code()) + }) + } +} diff --git a/coap-gateway/service/projection.go b/coap-gateway/service/projection.go new file mode 100644 index 000000000..2659e20a5 --- /dev/null +++ b/coap-gateway/service/projection.go @@ -0,0 +1,276 @@ +package service + +import ( + "context" + "fmt" + "sync" + + "github.com/go-ocf/cqrs/event" + "github.com/go-ocf/cqrs/eventstore" + "github.com/go-ocf/kit/log" + "github.com/go-ocf/kit/net/http" + + raEvents "github.com/go-ocf/cloud/resource-aggregate/cqrs/events" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +type resourceCtx struct { + lock sync.Mutex + resource *pbRA.Resource + isPublished bool + isProcessed bool + resourceUpdatePendings []raEvents.ResourceUpdatePending + resourceRetrievePendings []raEvents.ResourceRetrievePending + contentCtx *pbRA.ResourceChanged + + server *Server +} + +func newResourceCtx(server *Server) func(context.Context) (eventstore.Model, error) { + return func(context.Context) (eventstore.Model, error) { + return &resourceCtx{ + resourceUpdatePendings: make([]raEvents.ResourceUpdatePending, 0, 8), + server: server, + }, nil + } +} + +func (m *resourceCtx) Resource() *pbRA.Resource { + m.lock.Lock() + defer m.lock.Unlock() + + return m.resource +} + +func (m *resourceCtx) onResourcePublishedLocked() { + client := m.server.clientContainerByDeviceId.Find(m.resource.DeviceId) + if client == nil { + return + } + err := client.observeResource(context.Background(), m.resource, true) + if err != nil { + log.Errorf("cannot observe resource: %v", err) + } +} + +func (m *resourceCtx) onResourceUnpublishedLocked() { + client := m.server.clientContainerByDeviceId.Find(m.resource.DeviceId) + if client == nil { + return + } + client.unobserveResources([]*pbRA.Resource{m.resource}, map[string]bool{m.resource.Id: true}) +} + +func (m *resourceCtx) onResourceChangedLocked() { + for _, obs := range m.server.observeResourceContainer.Find(m.resource.Id) { + SendResourceContentToObserver(obs.responseWriter, obs.client, m.contentCtx, obs.Observe(), obs.deviceId, obs.resourceId, obs.token) + } +} + +func (m *resourceCtx) TriggerSignIn() { + m.lock.Lock() + defer m.lock.Unlock() + + if m.isPublished { + m.onResourcePublishedLocked() + m.onUpdateResourceLocked() + } +} + +func (m *resourceCtx) onUpdateResourceLocked() { + client := m.server.clientContainerByDeviceId.Find(m.resource.DeviceId) + if client == nil { + return + } + for { + if len(m.resourceUpdatePendings) == 0 { + return + } + updatePending := m.resourceUpdatePendings[0] + err := client.updateContent(context.Background(), m.resource, &updatePending) + if err != nil { + log.Errorf("DeviceId: %v, ResourceId: %v: cannot perform update: %v", m.resource.DeviceId, m.resource.Id, err) + return + } else { + m.resourceUpdatePendings = m.resourceUpdatePendings[1:] + } + } +} + +func (m *resourceCtx) onRetrieveResourceLocked() { + client := m.server.clientContainerByDeviceId.Find(m.resource.DeviceId) + if client == nil { + return + } + for { + if len(m.resourceRetrievePendings) == 0 { + return + } + retrievePending := m.resourceRetrievePendings[0] + err := client.retrieveContent(context.Background(), m.resource, &retrievePending) + if err != nil { + log.Errorf("DeviceId: %v, ResourceId: %v: cannot perform retrieve: %v", m.resource.DeviceId, m.resource.Id, err) + return + } else { + m.resourceRetrievePendings = m.resourceRetrievePendings[1:] + } + } +} + +func (m *resourceCtx) onResourceUpdatedLocked(updateProcessed []raEvents.ResourceUpdated) { + for _, up := range updateProcessed { + notify := m.server.updateNotificationContainer.Find(up.AuditContext.CorrelationId) + if notify != nil { + select { + case notify <- up: + default: + log.Debugf("DeviceId: %v, ResourceId: %v: cannot send resource updated event", m.resource.DeviceId, m.resource.Id) + } + } + } +} + +func (m *resourceCtx) onResourceRetrievedLocked(resourceRetrieved []raEvents.ResourceRetrieved) { + for _, up := range resourceRetrieved { + notify := m.server.retrieveNotificationContainer.Find(up.AuditContext.CorrelationId) + if notify != nil { + select { + case notify <- up: + default: + log.Debugf("DeviceId: %v, ResourceId: %v: cannot send resource retrieved event", m.resource.DeviceId, m.resource.Id) + } + } + } +} + +func (m *resourceCtx) SnapshotEventType() string { + s := &raEvents.ResourceStateSnapshotTaken{} + return s.SnapshotEventType() +} +func (m *resourceCtx) Content() *pbRA.ResourceChanged { + m.lock.Lock() + defer m.lock.Unlock() + return m.contentCtx +} + +func (m *resourceCtx) Handle(ctx context.Context, iter event.Iter) error { + var eu event.EventUnmarshaler + var onResourcePublished, onResourceUnpublished, onResourceChanged bool + resourceUpdatePendings := make([]raEvents.ResourceUpdatePending, 0, 16) + resourceUpdated := make([]raEvents.ResourceUpdated, 0, 16) + resourceRetrievePendings := make([]raEvents.ResourceRetrievePending, 0, 16) + resourceRetrieved := make([]raEvents.ResourceRetrieved, 0, 16) + m.lock.Lock() + defer m.lock.Unlock() + var anyEventProcessed bool + for iter.Next(ctx, &eu) { + anyEventProcessed = true + log.Debugf("coap-gateway.resourceCtx.Handle: DeviceId: %v, ResourceId: %v, Version: %v, EventType: %v", eu.GroupId, eu.AggregateId, eu.Version, eu.EventType) + switch eu.EventType { + case http.ProtobufContentType(&pbRA.ResourceStateSnapshotTaken{}): + var s raEvents.ResourceStateSnapshotTaken + if err := eu.Unmarshal(&s); err != nil { + return err + } + if !m.isPublished { + onResourcePublished = s.IsPublished + onResourceUnpublished = !s.IsPublished + } + m.contentCtx = s.GetLatestResourceChange() + m.resource = s.Resource + m.isPublished = s.IsPublished + onResourceChanged = true + case http.ProtobufContentType(&pbRA.ResourcePublished{}): + var s raEvents.ResourcePublished + if err := eu.Unmarshal(&s); err != nil { + return err + } + if !m.isPublished { + onResourcePublished = true + onResourceUnpublished = false + } + m.isPublished = true + m.resource = s.Resource + case http.ProtobufContentType(&pbRA.ResourceUnpublished{}): + if m.isPublished { + onResourcePublished = false + onResourceUnpublished = true + } + m.isPublished = false + case http.ProtobufContentType(&pbRA.ResourceUpdatePending{}): + var s raEvents.ResourceUpdatePending + if err := eu.Unmarshal(&s); err != nil { + return err + } + resourceUpdatePendings = append(resourceUpdatePendings, s) + case http.ProtobufContentType(&pbRA.ResourceUpdated{}): + var s raEvents.ResourceUpdated + if err := eu.Unmarshal(&s); err != nil { + return err + } + tmp := resourceUpdatePendings[:0] + for _, cu := range resourceUpdatePendings { + if cu.AuditContext.CorrelationId != s.AuditContext.CorrelationId { + tmp = append(tmp, cu) + } + } + resourceUpdatePendings = tmp + resourceUpdated = append(resourceUpdated, s) + case http.ProtobufContentType(&pbRA.ResourceRetrievePending{}): + var s raEvents.ResourceRetrievePending + if err := eu.Unmarshal(&s); err != nil { + return err + } + resourceRetrievePendings = append(resourceRetrievePendings, s) + case http.ProtobufContentType(&pbRA.ResourceRetrieved{}): + var s raEvents.ResourceRetrieved + if err := eu.Unmarshal(&s); err != nil { + return err + } + tmp := resourceRetrievePendings[:0] + for _, cu := range resourceRetrievePendings { + if cu.AuditContext.CorrelationId != s.AuditContext.CorrelationId { + tmp = append(tmp, cu) + } + } + resourceRetrievePendings = tmp + resourceRetrieved = append(resourceRetrieved, s) + case http.ProtobufContentType(&pbRA.ResourceChanged{}): + var s raEvents.ResourceChanged + if err := eu.Unmarshal(&s); err != nil { + return err + } + m.contentCtx = &s.ResourceChanged + onResourceChanged = true + } + } + + if !anyEventProcessed { + // if event event not processed, it means that the projection will be reloaded. + return nil + } + + if m.resource == nil { + return fmt.Errorf("DeviceId: %v, ResourceId: %v: invalid resource is stored in eventstore: Resource attribute is not set", eu.GroupId, eu.AggregateId) + } + + if onResourcePublished { + m.onResourcePublishedLocked() + } else if onResourceUnpublished { + m.onResourceUnpublishedLocked() + } + + if onResourceChanged && m.isPublished { + m.onResourceChangedLocked() + } + + m.onResourceUpdatedLocked(resourceUpdated) + m.onResourceRetrievedLocked(resourceRetrieved) + + m.resourceUpdatePendings = append(m.resourceUpdatePendings, resourceUpdatePendings...) + m.onUpdateResourceLocked() + m.resourceRetrievePendings = append(m.resourceRetrievePendings, resourceRetrievePendings...) + m.onRetrieveResourceLocked() + + return nil +} diff --git a/coap-gateway/service/refreshToken.go b/coap-gateway/service/refreshToken.go new file mode 100644 index 000000000..e2e6002af --- /dev/null +++ b/coap-gateway/service/refreshToken.go @@ -0,0 +1,94 @@ +package service + +import ( + "errors" + "fmt" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/cloud/coap-gateway/coapconv" + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/go-ocf/kit/codec/cbor" + "github.com/go-ocf/kit/net/coap" + "google.golang.org/grpc/status" +) + +type CoapRefreshTokenReq struct { + DeviceId string `json:"di"` + UserId string `json:"uid"` + RefreshToken string `json:"refreshtoken"` +} + +type CoapRefreshTokenResp struct { + ExpiresIn int64 `json:"expiresin"` + AccessToken string `json:"accesstoken"` + RefreshToken string `json:"refreshtoken"` +} + +func validateRefreshToken(req CoapRefreshTokenReq) error { + if req.DeviceId == "" { + return errors.New("cannot refresh token: invalid deviceId") + } + if req.RefreshToken == "" { + return errors.New("cannot refresh token: invalid refreshToken") + } + if req.UserId == "" { + return errors.New("cannot refresh token: invalid userId") + } + return nil +} + +func refreshTokenPostHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + var refreshToken CoapRefreshTokenReq + err := cbor.Decode(req.Msg.Payload(), &refreshToken) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle refresh token: %v", err), s, client, coapCodes.BadRequest) + return + } + + err = validateRefreshToken(refreshToken) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle refresh token: %v", err), s, client, coapCodes.BadRequest) + return + } + + resp, err := client.server.asClient.RefreshToken(req.Ctx, &pbAS.RefreshTokenRequest{ + DeviceId: refreshToken.DeviceId, + UserId: refreshToken.UserId, + RefreshToken: refreshToken.RefreshToken, + }) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle refresh token: %v", err), s, client, coapconv.GrpcCode2CoapCode(status.Convert(err).Code(), coapCodes.POST)) + return + } + + coapResp := CoapRefreshTokenResp{ + RefreshToken: resp.RefreshToken, + AccessToken: resp.AccessToken, + ExpiresIn: resp.ExpiresIn, + } + + accept := coap.GetAccept(req.Msg) + encode, err := coap.GetEncoder(accept) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle sign in: %v", err), s, client, coapCodes.InternalServerError) + return + } + out, err := encode(coapResp) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle sign in: %v", err), s, client, coapCodes.InternalServerError) + return + } + sendResponse(s, client, coapCodes.Changed, accept, out) +} + +// RefreshToken +// https://github.com/openconnectivityfoundation/security/blob/master/swagger2.0/oic.sec.tokenrefresh.swagger.json +func refreshTokenHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + switch req.Msg.Code() { + case coapCodes.POST: + refreshTokenPostHandler(s, req, client) + default: + logAndWriteErrorResponse(fmt.Errorf("Forbidden request from %v", req.Client.RemoteAddr()), s, client, coapCodes.Forbidden) + } +} diff --git a/coap-gateway/service/refreshToken_test.go b/coap-gateway/service/refreshToken_test.go new file mode 100644 index 000000000..6aaf4706b --- /dev/null +++ b/coap-gateway/service/refreshToken_test.go @@ -0,0 +1,54 @@ +package service + +import ( + "testing" + + "github.com/go-ocf/cloud/coap-gateway/uri" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" +) + +type TestCoapRefreshTokenResponse struct { + ExpiresIn int64 `json:"-"` + AccessToken string `json:"-"` + RefreshToken string `json:"refreshtoken"` +} + +func Test_refreshTokenHandler(t *testing.T) { + tbl := []testEl{ + {"BadRequest0", input{coapCodes.POST, `{}`, nil}, output{coapCodes.BadRequest, `invalid deviceId`, nil}}, + {"BadRequest1", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "refreshtoken": 123}`, nil}, output{coapCodes.BadRequest, `cannot handle refresh token: cbor: cannot unmarshal`, nil}}, + {"BadRequest2", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "refreshtoken": "123"}`, nil}, output{coapCodes.BadRequest, `invalid userId`, nil}}, + {"BadRequest3", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "uid": "` + AuthorizationUserId + `"}`, nil}, output{coapCodes.BadRequest, `invalid refreshToken`, nil}}, + {"Changed1", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "uid":"` + AuthorizationUserId + `", "refreshtoken":"123" }`, nil}, output{coapCodes.Changed, TestCoapRefreshTokenResponse{RefreshToken: AuthorizationRefreshToken}, nil}}, + } + + var config Config + err := envconfig.Process("", &config) + assert.NoError(t, err) + config.AuthServerAddr = "localhost:12345" + config.ResourceAggregateAddr = "localhost:12348" + resourceDB := t.Name() + "_resourceDB" + + shutdownSA := testCreateAuthServer(t, config.AuthServerAddr) + defer shutdownSA() + shutdownRA := testCreateResourceAggregate(t, resourceDB, config.ResourceAggregateAddr, config.AuthServerAddr) + defer shutdownRA() + shutdownGW := testCreateCoapGateway(t, resourceDB, config) + defer shutdownGW() + + co := testCoapDial(t, config.Addr, config.Net) + if co == nil { + return + } + defer co.Close() + + for _, test := range tbl { + tf := func(t *testing.T) { + testPostHandler(t, uri.RefreshToken, test, co) + testPostHandler(t, uri.SecureRefreshToken, test, co) + } + t.Run(test.name, tf) + } +} diff --git a/coap-gateway/service/resourceDirectory.go b/coap-gateway/service/resourceDirectory.go new file mode 100644 index 000000000..1a39171b8 --- /dev/null +++ b/coap-gateway/service/resourceDirectory.go @@ -0,0 +1,233 @@ +package service + +import ( + "errors" + "fmt" + "net/url" + "regexp" + "strconv" + "strings" + + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/go-ocf/kit/codec/cbor" + "github.com/go-ocf/kit/log" + "github.com/go-ocf/kit/net/coap" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + uuid "github.com/satori/go.uuid" +) + +const observable = 2 + +type wkRd struct { + DeviceID string `json:"di"` + Links []*pbRA.Resource `json:"links"` + TimeToLive int `json:"ttl"` + TimeToLiveLegacy int `json:"lt"` +} + +func fixTTL(w wkRd) wkRd { + // set time to live properly + if w.TimeToLive <= 0 { + w.TimeToLive = w.TimeToLiveLegacy + } else { + w.TimeToLiveLegacy = w.TimeToLive + } + return w +} + +func sendResponse(s gocoap.ResponseWriter, client *Client, code coapCodes.Code, contentFormat gocoap.MediaType, payload []byte) { + msg := s.NewResponse(code) + if msg != nil { + if len(payload) > 0 { + msg.SetOption(gocoap.ContentFormat, contentFormat) + msg.SetPayload(payload) + } + err := s.WriteMsg(msg) + if err != nil { + log.Errorf("Cannot send reply to %v: %v", getDeviceId(client), err) + } + decodeMsgToDebug(client, msg, "SEND-RESPONSE") + } +} + +func isObservable(res *pbRA.Resource) bool { + return res.Policies != nil && res.Policies.BitFlags&observable == observable +} + +// fixHref always lead by "/" +func fixHref(href string) string { + backslash := regexp.MustCompile(`\/+`) + p := backslash.ReplaceAllString(href, "/") + p = strings.TrimLeft(p, "/") + p = strings.TrimRight(p, "/") + + return "/" + p +} + +func resource2UUID(deviceID, href string) string { + return uuid.NewV5(uuid.NamespaceURL, deviceID+href).String() +} + +func validatePublish(w wkRd) error { + if w.DeviceID == "" { + return errors.New("invalid DeviceId") + } + if len(w.Links) == 0 { + return errors.New("empty links") + } + if w.TimeToLive <= 0 && w.TimeToLiveLegacy <= 0 { + return errors.New("invalid TimeToLive") + } + + return nil +} + +func resourceDirectoryPublishHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + authCtx := client.loadAuthorizationContext() + + var w wkRd + err := cbor.Decode(req.Msg.Payload(), &w) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot publish resource: %v", authCtx.DeviceId, err), s, client, coapCodes.BadRequest) + return + } + + if err := validatePublish(w); err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot publish resource: %v", authCtx.DeviceId, err), s, client, coapCodes.BadRequest) + return + } + + // set time to live properly + w = fixTTL(w) + + links := make([]*pbRA.Resource, 0, len(w.Links)) + for _, resource := range w.Links { + if resource.DeviceId == "" { + resource.DeviceId = w.DeviceID + } + resource, err := client.publishResource(kitNetGrpc.CtxWithToken(req.Ctx, authCtx.AccessToken), resource.Clone(), int32(w.TimeToLive), req.Client.RemoteAddr().String(), req.Sequence, authCtx.AuthorizationContext) + if err != nil { + // publish resource is not critical, it cause unaccessible resource + log.Errorf("DeviceId %v: cannot handle coap req to publish resource: %v", authCtx.DeviceId, err) + } else { + links = append(links, resource) + } + } + + if len(links) == 0 { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot publish resource: empty links", authCtx.DeviceId), s, client, coapCodes.BadRequest) + return + } + + w.Links = links + + for _, res := range links { + err := client.observeResource(req.Ctx, res, true) + if err != nil { + log.Errorf("DeviceId: %v: ResourceId: %v cannot observe published resource", res.DeviceId, res.Id) + } + } + + accept := coap.GetAccept(req.Msg) + encode, err := coap.GetEncoder(accept) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot publish resource: %v", authCtx.DeviceId, err), s, client, coapCodes.InternalServerError) + return + } + out, err := encode(w) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot publish resource: %v", authCtx.DeviceId, err), s, client, coapCodes.InternalServerError) + return + } + sendResponse(s, client, coapCodes.Changed, accept, out) +} + +func parseUnpublishQueryString(queries []interface{}) (deviceId string, instanceIDs []int64, err error) { + for _, query := range queries { + var q string + var ok bool + if q, ok = query.(string); !ok { + continue + } + + values, err := url.ParseQuery(q) + if err != nil { + return "", nil, fmt.Errorf("cannot parse unpublish query: %v", err) + } + if di := values.Get("di"); di != "" { + deviceId = di + } + + if ins := values.Get("ins"); ins != "" { + i, err := strconv.Atoi(ins) + if err != nil { + return "", nil, fmt.Errorf("cannot convert %v to number", ins) + } + instanceIDs = append(instanceIDs, int64(i)) + } + } + + if deviceId == "" { + return "", nil, fmt.Errorf("deviceId not found") + } + + return +} + +func resourceDirectoryUnpublishHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + queries := req.Msg.Options(gocoap.URIQuery) + deviceID, inss, err := parseUnpublishQueryString(queries) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("Incorrect Unpublish query string - %v", err), s, client, coapCodes.BadRequest) + return + } + + rscs := make([]*pbRA.Resource, 0, 32) + + rscs = client.getObservedResources(deviceID, inss, rscs) + if len(rscs) == 0 { + logAndWriteErrorResponse(fmt.Errorf("cannot found resources for the DELETE request parameters - with device ID and instance IDs %v, ", queries), s, client, coapCodes.BadRequest) + return + } + + client.unpublishResources(rscs) + + sendResponse(s, client, coapCodes.Deleted, gocoap.TextPlain, nil) +} + +type resourceDirectorySelector struct { + SelectionCriteria int `json:"sel"` +} + +func resourceDirectoryGetSelector(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + var rds resourceDirectorySelector //we want to use sel:0 to prefer cloud RD + + accept := coap.GetAccept(req.Msg) + encode, err := coap.GetEncoder(accept) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot get selector: %v", err), s, client, coapCodes.InternalServerError) + return + } + out, err := encode(rds) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot get selector: %v", err), s, client, coapCodes.InternalServerError) + return + } + + sendResponse(s, client, coapCodes.Content, accept, out) +} + +func resourceDirectoryHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + switch req.Msg.Code() { + case coapCodes.POST: + resourceDirectoryPublishHandler(s, req, client) + case coapCodes.DELETE: + resourceDirectoryUnpublishHandler(s, req, client) + case coapCodes.GET: + resourceDirectoryGetSelector(s, req, client) + default: + logAndWriteErrorResponse(fmt.Errorf("Forbidden request from %v", req.Client.RemoteAddr()), s, client, coapCodes.Forbidden) + } +} diff --git a/coap-gateway/service/resourceDirectory_test.go b/coap-gateway/service/resourceDirectory_test.go new file mode 100644 index 000000000..4ed801ca0 --- /dev/null +++ b/coap-gateway/service/resourceDirectory_test.go @@ -0,0 +1,255 @@ +package service + +import ( + "context" + "testing" + + oauthTest "github.com/go-ocf/cloud/authorization/provider" + "github.com/go-ocf/cloud/coap-gateway/uri" + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" +) + +type TestResource struct { + DeviceId string `json:"di"` + //Eps interface{} `json:"eps"` + Href string `json:"href"` + Id string `json:"id"` + Interfaces []string `json:"if"` + InstanceId uint64 `json:"-"` + //P interface{} `json:"p"` + ResourceTypes []string `json:"rt"` + Type []string `json:"type"` +} + +type TestWkRD struct { + DeviceID string `json:"di"` + Links []TestResource `json:"links"` + TimeToLive int `json:"ttl"` + TimeToLiveLegacy int `json:"lt"` +} + +var tblResourceDirectory = []testEl{ + {"BadRequest0", input{coapCodes.POST, `{ "di":"` + CertIdentity + `" }`, nil}, output{coapCodes.BadRequest, `empty links`, nil}}, + {"BadRequest1", input{coapCodes.POST, `{ "di":"` + CertIdentity + `", "links":"abc" }`, nil}, output{coapCodes.BadRequest, `cannot publish resource: cbor: cannot unmarshal`, nil}}, + {"BadRequest2", input{coapCodes.POST, `{ "di":"` + CertIdentity + `", "links":[ "abc" ]}`, nil}, output{coapCodes.BadRequest, `cannot publish resource: cbor: cannot unmarshal`, nil}}, + {"BadRequest3", input{coapCodes.POST, `{ "di":"` + CertIdentity + `", "links":[ {} ]}`, nil}, output{coapCodes.BadRequest, `invalid TimeToLive`, nil}}, + {"BadRequest4", input{coapCodes.POST, `{ "di":"` + CertIdentity + `", "links":[ { "href":"" } ]}`, nil}, output{coapCodes.BadRequest, `invalid TimeToLive`, nil}}, + {"BadRequest5", input{coapCodes.POST, `{ "di":"` + CertIdentity + `", "links":[ { "di":"` + CertIdentity + `", "href":"" } ], "ttl":12345}`, nil}, output{coapCodes.BadRequest, `empty links`, nil}}, + {"BadRequest6", input{coapCodes.POST, `{ "di":"` + CertIdentity + `", "links":[ { "href":"" } ], "ttl":12345}`, nil}, output{coapCodes.BadRequest, `empty links`, nil}}, + {"Changed0", input{coapCodes.POST, `{ "di":"` + CertIdentity + `", "links":[ { "di":"` + CertIdentity + `", "href":"` + TestAResourceHref + `" } ], "ttl":12345}`, nil}, + output{coapCodes.Changed, TestWkRD{ + DeviceID: CertIdentity, + TimeToLive: 12345, + TimeToLiveLegacy: 12345, + Links: []TestResource{ + TestResource{ + DeviceId: CertIdentity, + Href: TestAResourceHref, + Id: TestAResourceId, + }, + }, + }, nil}}, + + {"Changed1", input{coapCodes.POST, `{ "di":"` + CertIdentity + `", "links":[ { "di":"` + CertIdentity + `", "href":"/b" } ], "ttl":12345}`, nil}, + output{coapCodes.Changed, TestWkRD{ + DeviceID: CertIdentity, + TimeToLive: 12345, + TimeToLiveLegacy: 12345, + Links: []TestResource{ + TestResource{ + DeviceId: CertIdentity, + Href: "/b", + Id: "1f36abb2-c5f8-556e-bf74-3b34ed66a2b4", + }, + }, + }, nil}}, + {"Changed2", input{coapCodes.POST, `{ "di":"` + CertIdentity + `", "links":[ { "di":"` + CertIdentity + `", "href":"/b" } , { "di":"` + CertIdentity + `", "href":"/c" }], "ttl":12345}`, nil}, + output{coapCodes.Changed, TestWkRD{ + DeviceID: CertIdentity, + TimeToLive: 12345, + TimeToLiveLegacy: 12345, + Links: []TestResource{ + TestResource{ + DeviceId: CertIdentity, + Href: "/b", + Id: "1f36abb2-c5f8-556e-bf74-3b34ed66a2b4", + }, + TestResource{ + DeviceId: CertIdentity, + Href: "/c", + Id: "41529a9c-b80f-5487-82da-da4a476402ae", + }, + }, + }, nil}}, +} + +func TestResourceDirectoryPostHandler(t *testing.T) { + var config Config + err := envconfig.Process("", &config) + assert.NoError(t, err) + config.AuthServerAddr = "localhost:12345" + config.ResourceAggregateAddr = "localhost:12348" + config.ResourceDirectoryAddr = "localhost:12349" + resourceDB := t.Name() + "_resourceDB" + + shutdownSA := testCreateAuthServer(t, config.AuthServerAddr) + defer shutdownSA() + shutdownRA := testCreateResourceAggregate(t, resourceDB, config.ResourceAggregateAddr, config.AuthServerAddr) + defer shutdownRA() + shutdownGW := testCreateCoapGateway(t, resourceDB, config) + defer shutdownGW() + + co := testCoapDial(t, config.Addr, config.Net) + if co == nil { + return + } + defer co.Close() + + signUpEl := testEl{"signUp", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "accesstoken": "123", "authprovider": "` + oauthTest.NewTestProvider().GetProviderName() + `"}`, nil}, output{coapCodes.Changed, TestCoapSignUpResponse{RefreshToken: "refresh-token", UserId: AuthorizationUserId}, nil}} + t.Run(signUpEl.name, func(t *testing.T) { + testPostHandler(t, uri.SignUp, signUpEl, co) + }) + signInEl := testEl{"signIn", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "uid":"` + AuthorizationUserId + `", "accesstoken":"` + oauthTest.UserToken + `", "login": true }`, nil}, output{coapCodes.Changed, TestCoapSignInResponse{}, nil}} + t.Run(signInEl.name, func(t *testing.T) { + testPostHandler(t, uri.SignIn, signInEl, co) + }) + + for _, test := range tblResourceDirectory { + tf := func(t *testing.T) { + testPostHandler(t, uri.ResourceDirectory, test, co) + } + t.Run(test.name, tf) + } +} + +func TestResourceDirectoryDeleteHandler(t *testing.T) { + //set counter 0, when other test run with this that it can be modified + deletetblResourceDirectory := []testEl{ + {"NotExist1", input{coapCodes.DELETE, ``, []string{"di=c", "ins=5"}}, output{coapCodes.BadRequest, `cannot found resources for the DELETE request parameters`, nil}}, // Non-existent device ID. + {"NotExist2", input{coapCodes.DELETE, ``, []string{"ins=4"}}, output{coapCodes.BadRequest, `Incorrect Unpublish query string - deviceId not found`, nil}}, // Device ID empty. + {"NotExist3", input{coapCodes.DELETE, ``, []string{`di=` + CertIdentity, "ins=999"}}, output{coapCodes.BadRequest, `cannot found resources for the DELETE request parameters`, nil}}, // Instance ID non-existent. + {"Exist1", input{coapCodes.DELETE, ``, []string{`di=` + CertIdentity}}, output{coapCodes.Deleted, nil, nil}}, // If instanceIDs empty, all instances for a given device ID should be unpublished. + {"NotExist4", input{coapCodes.DELETE, ``, []string{`di=` + CertIdentity}}, output{coapCodes.BadRequest, `cannot found resources for the DELETE request parameters`, nil}}, + } + + var config Config + err := envconfig.Process("", &config) + assert.NoError(t, err) + config.AuthServerAddr = "localhost:12345" + config.ResourceAggregateAddr = "localhost:12348" + config.ResourceDirectoryAddr = "localhost:12349" + resourceDB := t.Name() + "_resourceDB" + + shutdownSA := testCreateAuthServer(t, config.AuthServerAddr) + defer shutdownSA() + shutdownRA := testCreateResourceAggregate(t, resourceDB, config.ResourceAggregateAddr, config.AuthServerAddr) + defer shutdownRA() + shutdownGW := testCreateCoapGateway(t, resourceDB, config) + defer shutdownGW() + + co := testCoapDial(t, config.Addr, config.Net) + if co == nil { + return + } + defer co.Close() + + signUpEl := testEl{"signUp", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "accesstoken": "123", "authprovider": "` + oauthTest.NewTestProvider().GetProviderName() + `"}`, nil}, output{coapCodes.Changed, TestCoapSignUpResponse{RefreshToken: "refresh-token", UserId: AuthorizationUserId}, nil}} + t.Run(signUpEl.name, func(t *testing.T) { + testPostHandler(t, uri.SignUp, signUpEl, co) + }) + signInEl := testEl{"signIn", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "uid":"` + AuthorizationUserId + `", "accesstoken":"` + oauthTest.UserToken + `", "login": true }`, nil}, output{coapCodes.Changed, TestCoapSignInResponse{}, nil}} + t.Run(signInEl.name, func(t *testing.T) { + testPostHandler(t, uri.SignIn, signInEl, co) + }) + + // Publish resources first! + for _, test := range tblResourceDirectory { + tf := func(t *testing.T) { + testPostHandler(t, uri.ResourceDirectory, test, co) + } + t.Run(test.name, tf) + } + + //delete resources + for _, test := range deletetblResourceDirectory { + tf := func(t *testing.T) { + req, err := co.NewDeleteRequest(uri.ResourceDirectory) + if err != nil { + t.Fatalf("cannot create request: %v", err) + } + for _, q := range test.in.queries { + req.AddOption(gocoap.URIQuery, q) + } + ctx, cancel := context.WithTimeout(context.Background(), TestExchangeTimeout) + defer cancel() + resp, err := co.ExchangeWithContext(ctx, req) + if err != nil { + t.Fatalf("Cannot send/retrieve msg: %v", err) + } + testValidateResp(t, test, resp) + } + t.Run(test.name, tf) + } +} + +type TestGetSelector struct { + Selector uint64 `json:"sel"` +} + +func TestResourceDirectoryGetSelector(t *testing.T) { + tbl := []testEl{ + {"GetSelector", input{coapCodes.GET, ``, []string{}}, output{coapCodes.Content, TestGetSelector{}, nil}}, + } + + var config Config + err := envconfig.Process("", &config) + assert.NoError(t, err) + config.AuthServerAddr = "localhost:12345" + config.ResourceAggregateAddr = "localhost:12348" + config.ResourceDirectoryAddr = "localhost:12349" + resourceDB := t.Name() + "_resourceDB" + + shutdownSA := testCreateAuthServer(t, config.AuthServerAddr) + defer shutdownSA() + shutdownRA := testCreateResourceAggregate(t, resourceDB, config.ResourceAggregateAddr, config.AuthServerAddr) + defer shutdownRA() + shutdownGW := testCreateCoapGateway(t, resourceDB, config) + defer shutdownGW() + + co := testCoapDial(t, config.Addr, config.Net) + if co == nil { + return + } + defer co.Close() + + signUpEl := testEl{"signUp", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "accesstoken": "123", "authprovider": "` + oauthTest.NewTestProvider().GetProviderName() + `"}`, nil}, output{coapCodes.Changed, TestCoapSignUpResponse{RefreshToken: "refresh-token", UserId: AuthorizationUserId}, nil}} + t.Run(signUpEl.name, func(t *testing.T) { + testPostHandler(t, uri.SignUp, signUpEl, co) + }) + signInEl := testEl{"signIn", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "uid":"` + AuthorizationUserId + `", "accesstoken":"` + oauthTest.UserToken + `", "login": true }`, nil}, output{coapCodes.Changed, TestCoapSignInResponse{}, nil}} + t.Run(signInEl.name, func(t *testing.T) { + testPostHandler(t, uri.SignIn, signInEl, co) + }) + + for _, test := range tbl { + tf := func(t *testing.T) { + req, err := co.NewGetRequest(uri.ResourceDirectory) + if err != nil { + t.Fatalf("cannot create request: %v", err) + } + for _, q := range test.in.queries { + req.AddOption(gocoap.URIQuery, q) + } + + resp, err := co.Exchange(req) + if err != nil { + t.Fatalf("Cannot send/retrieve msg: %v", err) + } + testValidateResp(t, test, resp) + } + t.Run(test.name, tf) + } +} diff --git a/coap-gateway/service/resourceDiscovery.go b/coap-gateway/service/resourceDiscovery.go new file mode 100644 index 000000000..59e796643 --- /dev/null +++ b/coap-gateway/service/resourceDiscovery.go @@ -0,0 +1,176 @@ +package service + +import ( + "fmt" + "io" + "net/url" + "strconv" + "strings" + "time" + + "github.com/go-ocf/cloud/coap-gateway/coapconv" + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/go-ocf/kit/log" + "github.com/go-ocf/kit/net/coap" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRD "github.com/go-ocf/cloud/resource-directory/pb/resource-directory" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func makeListDevicesCommand(msg gocoap.Message, authCtx pbCQRS.AuthorizationContext) (pbRD.GetResourceLinksRequest, error) { + deviceIdsFilter := make([]string, 0, 4) + typeFilter := make([]string, 0, 4) + + for _, q := range msg.Options(gocoap.URIQuery) { + var query string + var ok bool + if query, ok = q.(string); !ok { + continue + } + + values, err := url.ParseQuery(query) + if err != nil { + return pbRD.GetResourceLinksRequest{}, fmt.Errorf("cannot parse list devices query: %v", err) + } + if di := values.Get("di"); di != "" { + deviceIdsFilter = append(deviceIdsFilter, di) + } + + if rt := values.Get("rt"); rt != "" { + typeFilter = append(typeFilter, rt) + } + } + + cmd := pbRD.GetResourceLinksRequest{ + AuthorizationContext: &authCtx, + DeviceIdsFilter: deviceIdsFilter, + TypeFilter: typeFilter, + } + + return cmd, nil +} + +func makeHref(deviceId, href string) string { + return fixHref("/" + resourceRoute + "/" + deviceId + "/" + href) +} + +func makeDiscoveryResp(serverNetwork, serverAddr string, getResourceLinksClient pbRD.ResourceDirectory_GetResourceLinksClient) ([]*wkRd, codes.Code, error) { + deviceRes := make(map[string]*wkRd) + + ep := "coap" + if strings.HasSuffix(serverNetwork, "-tls") { + ep = "coaps" + } + ep = ep + "+tcp://" + serverAddr + + for { + link, err := getResourceLinksClient.Recv() + if err == io.EOF { + break + } + if err != nil { + return nil, status.Convert(err).Code(), fmt.Errorf("cannot create discovery response: %v", err) + } + resource := link.Resource + d, ok := deviceRes[resource.DeviceId] + if !ok { + d = &wkRd{ + DeviceID: resource.DeviceId, + Links: make([]*pbRA.Resource, 0, 16), + } + deviceRes[resource.DeviceId] = d + } + + resource.Href = makeHref(resource.DeviceId, resource.Href) + //set anchor if it is not set + if resource.Anchor == "" { + resource.Anchor = "ocf://" + resource.DeviceId + } + //override EndpointInformations to cloud address + resource.EndpointInformations = []*pbRA.EndpointInformation{ + &pbRA.EndpointInformation{ + Endpoint: ep, + Priority: 1, + }, + } + d.Links = append(d.Links, resource) + } + + resp := make([]*wkRd, 0, 128) + for _, rd := range deviceRes { + resp = append(resp, rd) + } + + return resp, codes.OK, nil +} + +func resourceDirectoryFind(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + t := time.Now() + defer func() { + log.Debugf("resourceDirectoryFind takes %v", time.Since(t)) + }() + + authCtx := client.loadAuthorizationContext() + request, err := makeListDevicesCommand(req.Msg, authCtx.AuthorizationContext) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot handle resource discovery: %v", authCtx.DeviceId, err), s, client, coapCodes.BadRequest) + return + } + + getResourceLinksClient, err := client.server.rdClient.GetResourceLinks(kitNetGrpc.CtxWithToken(req.Ctx, authCtx.AccessToken), &request) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: handle resource discovery: %v", authCtx.DeviceId, err), s, client, coapconv.GrpcCode2CoapCode(status.Convert(err).Code(), coapCodes.GET)) + return + } + + discoveryResp, code, err := makeDiscoveryResp(client.server.Net, client.server.FQDN+":"+strconv.Itoa(int(client.server.ExternalPort)), getResourceLinksClient) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: handle resource discovery: %v", authCtx.DeviceId, err), s, client, coapconv.GrpcCode2CoapCode(code, coapCodes.GET)) + return + } + + coapCode := coapCodes.Content + if len(discoveryResp) == 0 { + coapCode = coapCodes.NotFound + } + + var resp interface{} + respContentFormat := coap.GetAccept(req.Msg) + switch respContentFormat { + case gocoap.AppOcfCbor: + links := make([]*pbRA.Resource, 0, 64) + for _, d := range discoveryResp { + links = append(links, d.Links...) + } + resp = links + case gocoap.AppCBOR: + resp = discoveryResp + } + + accept := coap.GetAccept(req.Msg) + encode, err := coap.GetEncoder(accept) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot marshal discovery response: %v", authCtx.DeviceId, err), s, client, coapCodes.InternalServerError) + return + } + out, err := encode(resp) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: cannot marshal discovery response: %v", authCtx.DeviceId, err), s, client, coapCodes.InternalServerError) + return + } + + sendResponse(s, client, coapCode, accept, out) +} + +func resourceDiscoveryHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + switch req.Msg.Code() { + case coapCodes.GET: + resourceDirectoryFind(s, req, client) + default: + logAndWriteErrorResponse(fmt.Errorf("Forbidden request from %v", req.Client.RemoteAddr()), s, client, coapCodes.Forbidden) + } +} diff --git a/coap-gateway/service/resourceDiscovery_test.go b/coap-gateway/service/resourceDiscovery_test.go new file mode 100644 index 000000000..ead28c5cd --- /dev/null +++ b/coap-gateway/service/resourceDiscovery_test.go @@ -0,0 +1,104 @@ +package service + +import ( + "context" + "testing" + "time" + + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" +) + +func Test_resourceDirectoryFind(t *testing.T) { + var config Config + err := envconfig.Process("", &config) + assert.NoError(t, err) + config.AuthServerAddr = "localhost:12345" + config.ResourceAggregateAddr = "localhost:12348" + config.ResourceDirectoryAddr = "localhost:12349" + config.RequestTimeout = time.Second * 2 + resourceDB := t.Name() + "_resourceDB" + + shutdownSA := testCreateAuthServer(t, config.AuthServerAddr) + defer shutdownSA() + shutdownRA := testCreateResourceAggregate(t, resourceDB, config.ResourceAggregateAddr, config.AuthServerAddr) + defer shutdownRA() + shutdownRD := testCreateResourceDirectory(t, resourceDB, config.ResourceDirectoryAddr, config.AuthServerAddr) + defer shutdownRD() + shutdownGW := testCreateCoapGateway(t, resourceDB, config) + defer shutdownGW() + + co := testCoapDial(t, config.Addr, config.Net) + if co == nil { + return + } + defer co.Close() + + NewGetRequest := func(queries ...string) gocoap.Message { + msg, err := co.NewGetRequest("/oic/res") + for _, q := range queries { + msg.AddOption(gocoap.URIQuery, q) + } + assert.NoError(t, err) + return msg + } + + type args struct { + req gocoap.Message + } + tests := []struct { + name string + args args + wantsCode coapCodes.Code + }{ + { + name: "without query", + args: args{ + req: NewGetRequest(), + }, + wantsCode: coapCodes.Content, + }, + { + name: "with di", + args: args{ + req: NewGetRequest("di=" + CertIdentity), + }, + wantsCode: coapCodes.Content, + }, + { + name: "with rt", + args: args{ + req: NewGetRequest("rt=" + TestBResourceType), + }, + wantsCode: coapCodes.Content, + }, + { + name: "with di & rt", + args: args{ + req: NewGetRequest("di="+CertIdentity, "rt="+TestAResourceType), + }, + wantsCode: coapCodes.Content, + }, + { + name: "di not exist", + args: args{ + req: NewGetRequest("di=1234"), + }, + wantsCode: coapCodes.NotFound, + }, + } + + testPrepareDevice(t, co) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), TestExchangeTimeout) + defer cancel() + resp, err := co.ExchangeWithContext(ctx, tt.args.req) + assert.NoError(t, err) + assert.Equal(t, tt.wantsCode, resp.Code()) + }) + } +} diff --git a/coap-gateway/service/resourceRouteHandler.go b/coap-gateway/service/resourceRouteHandler.go new file mode 100644 index 000000000..55c4c0f1d --- /dev/null +++ b/coap-gateway/service/resourceRouteHandler.go @@ -0,0 +1,26 @@ +package service + +import ( + "fmt" + + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" +) + +var resourceRoute = "oic/route" + +func resourceRouteHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + switch req.Msg.Code() { + case coapCodes.POST: + clientUpdateHandler(s, req, client) + case coapCodes.GET: + if observe, ok := req.Msg.Option(gocoap.Observe).(uint32); ok { + clientObserveHandler(s, req, client, observe) + return + } + clientRetrieveHandler(s, req, client) + default: + deviceId := getDeviceId(client) + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v, Href %v: unsupported method %v", deviceId, req.Msg.PathString(), req.Msg.Code()), s, client, coapCodes.MethodNotAllowed) + } +} diff --git a/coap-gateway/service/server.go b/coap-gateway/service/server.go new file mode 100644 index 000000000..7acd25379 --- /dev/null +++ b/coap-gateway/service/server.go @@ -0,0 +1,312 @@ +package service + +import ( + "context" + "crypto/tls" + "fmt" + "reflect" + "strings" + "time" + + "github.com/panjf2000/ants" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/cloud/coap-gateway/uri" + "github.com/go-ocf/cqrs/eventbus" + "github.com/go-ocf/cqrs/eventstore" + coapCodes "github.com/go-ocf/go-coap/codes" + gocoapNet "github.com/go-ocf/go-coap/net" + kitNetCoap "github.com/go-ocf/kit/net/coap" + notificationRA "github.com/go-ocf/cloud/resource-aggregate/cqrs/notification" + projectionRA "github.com/go-ocf/cloud/resource-aggregate/cqrs/projection" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRD "github.com/go-ocf/cloud/resource-directory/pb/resource-directory" + pbRS "github.com/go-ocf/cloud/resource-directory/pb/resource-shadow" + + gocoap "github.com/go-ocf/go-coap" + "github.com/go-ocf/kit/log" + cache "github.com/patrickmn/go-cache" +) + +//Server a configuration of coapgateway +type Server struct { + FQDN string // fully qualified domain name of GW + ExternalPort uint16 // used to construct oic/res response + Addr string // Address to listen on, ":COAP" if empty. + Net string // if "tcp" or "tcp-tls" (COAP over TLS) it will invoke a TCP listener, otherwise an UDP one + Keepalive gocoap.KeepAlive + DisableTCPSignalMessageCSM bool + DisablePeerTCPSignalMessageCSMs bool + SendErrorTextInResponse bool + RequestTimeout time.Duration + ConnectionsHeartBeat time.Duration + + raClient pbRA.ResourceAggregateClient + asClient pbAS.AuthorizationServiceClient + rsClient pbRS.ResourceShadowClient + rdClient pbRD.ResourceDirectoryClient + + clientContainer *ClientContainer + clientContainerByDeviceId *clientContainerByDeviceId + updateNotificationContainer *notificationRA.UpdateNotificationContainer + retrieveNotificationContainer *notificationRA.RetrieveNotificationContainer + observeResourceContainer *observeResourceContainer + goroutinesPool *ants.Pool + oicPingCache *cache.Cache + + projection *projectionRA.Projection + coapServer *gocoap.Server + listener gocoap.Listener + authInterceptor kitNetCoap.Interceptor +} + +type DialCertManager = interface { + GetClientTLSConfig() tls.Config +} + +type ListenCertManager = interface { + GetServerTLSConfig() tls.Config +} + +//NewServer setup coap gateway +func New(config Config, dialCertManager DialCertManager, listenCertManager ListenCertManager, authInterceptor kitNetCoap.Interceptor, store eventstore.EventStore, subscriber eventbus.Subscriber, pool *ants.Pool) *Server { + oicPingCache := cache.New(cache.NoExpiration, time.Minute) + oicPingCache.OnEvicted(pingOnEvicted) + + clientTLSConfig := dialCertManager.GetClientTLSConfig() + + raConn, err := grpc.Dial(config.ResourceAggregateAddr, grpc.WithTransportCredentials(credentials.NewTLS(&clientTLSConfig))) + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + raClient := pbRA.NewResourceAggregateClient(raConn) + + asConn, err := grpc.Dial(config.AuthServerAddr, grpc.WithTransportCredentials(credentials.NewTLS(&clientTLSConfig))) + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + asClient := pbAS.NewAuthorizationServiceClient(asConn) + + rdConn, err := grpc.Dial(config.ResourceDirectoryAddr, grpc.WithTransportCredentials(credentials.NewTLS(&clientTLSConfig))) + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + var listener gocoap.Listener + + if listenCertManager == nil || reflect.ValueOf(listenCertManager).IsNil() { + l, err := gocoapNet.NewTCPListener("tcp", config.Addr, time.Millisecond*100) + if err != nil { + log.Fatalf("cannot setup tcp for server: %v", err) + } + listener = l + } else { + tlsConfig := listenCertManager.GetServerTLSConfig() + l, err := gocoapNet.NewTLSListener("tcp", config.Addr, &tlsConfig, time.Millisecond*100) + if err != nil { + log.Fatalf("cannot setup tcp-tls for server: %v", err) + } + listener = l + } + rdClient := pbRD.NewResourceDirectoryClient(rdConn) + rsClient := pbRS.NewResourceShadowClient(rdConn) + + var keepalive gocoap.KeepAlive + if config.KeepaliveEnable { + keepalive, err = gocoap.MakeKeepAlive(config.KeepaliveTimeoutConnection) + if err != nil { + log.Fatalf("cannot setup keepalive for server: %v", err) + } + } + + s := Server{ + Keepalive: keepalive, + Net: config.Net, + FQDN: config.FQDN, + ExternalPort: config.ExternalPort, + Addr: config.Addr, + RequestTimeout: config.RequestTimeout, + DisableTCPSignalMessageCSM: config.DisableTCPSignalMessageCSM, + DisablePeerTCPSignalMessageCSMs: config.DisablePeerTCPSignalMessageCSMs, + SendErrorTextInResponse: config.SendErrorTextInResponse, + ConnectionsHeartBeat: config.ConnectionsHeartBeat, + + raClient: raClient, + asClient: asClient, + rsClient: rsClient, + rdClient: rdClient, + + clientContainer: &ClientContainer{sessions: make(map[string]*Client)}, + clientContainerByDeviceId: NewClientContainerByDeviceId(), + updateNotificationContainer: notificationRA.NewUpdateNotificationContainer(), + retrieveNotificationContainer: notificationRA.NewRetrieveNotificationContainer(), + observeResourceContainer: NewObserveResourceContainer(), + goroutinesPool: pool, + oicPingCache: oicPingCache, + listener: listener, + authInterceptor: authInterceptor, + } + + projection, err := projectionRA.NewProjection(context.Background(), fmt.Sprintf("%v:%v", config.FQDN, config.ExternalPort), store, subscriber, newResourceCtx(&s)) + if err != nil { + log.Fatalf("cannot create projection for server: %v", err) + } + s.projection = projection + return &s +} + +func getDeviceId(client *Client) string { + deviceId := "unknown" + if client != nil { + deviceId = client.loadAuthorizationContext().DeviceId + if deviceId == "" { + deviceId = fmt.Sprintf("unknown(%v)", client.remoteAddrString()) + } + } + return deviceId +} + +func validateCommand(s gocoap.ResponseWriter, req *gocoap.Request, server *Server, fnc func(s gocoap.ResponseWriter, req *gocoap.Request, client *Client)) { + client := server.clientContainer.Find(req.Client.RemoteAddr().String()) + + switch req.Msg.Code() { + case coapCodes.POST, coapCodes.DELETE, coapCodes.PUT, coapCodes.GET: + if client == nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle command: client not found"), s, client, coapCodes.InternalServerError) + return + } + fnc(s, req, client) + case coapCodes.Empty: + if client == nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle command: client not found"), s, client, coapCodes.InternalServerError) + return + } + clientResetHandler(s, req, client) + case coapCodes.Content: + // Unregistered observer at a peer send us a notification - inform the peer to remove it + sendResponse(s, client, coapCodes.Empty, gocoap.TextPlain, nil) + default: + deviceID := getDeviceId(client) + log.Errorf("DeviceId: %v: received invalid code: CoapCode(%v)", deviceID, req.Msg.Code()) + } +} + +func defaultHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + path := req.Msg.PathString() + + switch { + case strings.HasPrefix(path, resourceRoute): + resourceRouteHandler(s, req, client) + default: + deviceId := getDeviceId(client) + logAndWriteErrorResponse(fmt.Errorf("DeviceId: %v: unknown path %v", deviceId, path), s, client, coapCodes.NotFound) + } +} + +func (server *Server) coapConnOnNew(coapConn *gocoap.ClientConn) { + remoteAddr := coapConn.RemoteAddr().String() + server.clientContainer.Add(remoteAddr, newClient(server, coapConn)) +} + +func (server *Server) coapConnOnClose(coapConn *gocoap.ClientConn, err error) { + if err != nil { + log.Errorf("coap connection closed with error: %v", err) + } + if client, ok := server.clientContainer.Pop(coapConn.RemoteAddr().String()); ok { + client.OnClose() + } + +} + +func (server *Server) logginMiddleware(next func(gocoap.ResponseWriter, *gocoap.Request)) func(gocoap.ResponseWriter, *gocoap.Request) { + return func(w gocoap.ResponseWriter, req *gocoap.Request) { + client := server.clientContainer.Find(req.Client.RemoteAddr().String()) + decodeMsgToDebug(client, req.Msg, "RECEIVED-COMMAND") + next(w, req) + } +} + +func (server *Server) authMiddleware(next func(gocoap.ResponseWriter, *gocoap.Request)) func(gocoap.ResponseWriter, *gocoap.Request) { + return func(w gocoap.ResponseWriter, req *gocoap.Request) { + client := server.clientContainer.Find(req.Client.RemoteAddr().String()) + if client == nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle request: client not found"), w, client, coapCodes.InternalServerError) + return + } + + ctx := kitNetCoap.CtxWithToken(req.Ctx, client.loadAuthorizationContext().AccessToken) + _, err := server.authInterceptor(ctx, req.Msg.Code(), "/"+req.Msg.PathString()) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle request to path '%v': %v", req.Msg.PathString(), err), w, client, coapCodes.Unauthorized) + client.Close() + return + } + next(w, req) + } +} + +//setupCoapServer setup coap server +func (server *Server) setupCoapServer() { + mux := gocoap.NewServeMux() + mux.DefaultHandle(gocoap.HandlerFunc(func(s gocoap.ResponseWriter, req *gocoap.Request) { + validateCommand(s, req, server, defaultHandler) + })) + mux.Handle(uri.ResourceDirectory, gocoap.HandlerFunc(func(s gocoap.ResponseWriter, req *gocoap.Request) { + validateCommand(s, req, server, resourceDirectoryHandler) + })) + mux.Handle(uri.SignUp, gocoap.HandlerFunc(func(s gocoap.ResponseWriter, req *gocoap.Request) { + validateCommand(s, req, server, signUpHandler) + })) + mux.Handle(uri.SecureSignUp, gocoap.HandlerFunc(func(s gocoap.ResponseWriter, req *gocoap.Request) { + validateCommand(s, req, server, signUpHandler) + })) + mux.Handle(uri.SignIn, gocoap.HandlerFunc(func(s gocoap.ResponseWriter, req *gocoap.Request) { + validateCommand(s, req, server, signInHandler) + })) + mux.Handle(uri.SecureSignIn, gocoap.HandlerFunc(func(s gocoap.ResponseWriter, req *gocoap.Request) { + validateCommand(s, req, server, signInHandler) + })) + mux.Handle(uri.ResourceDiscovery, gocoap.HandlerFunc(func(s gocoap.ResponseWriter, req *gocoap.Request) { + validateCommand(s, req, server, resourceDiscoveryHandler) + })) + mux.Handle(uri.ResourcePing, gocoap.HandlerFunc(func(s gocoap.ResponseWriter, req *gocoap.Request) { + validateCommand(s, req, server, resourcePingHandler) + })) + mux.Handle(uri.RefreshToken, gocoap.HandlerFunc(func(s gocoap.ResponseWriter, req *gocoap.Request) { + validateCommand(s, req, server, refreshTokenHandler) + })) + mux.Handle(uri.SecureRefreshToken, gocoap.HandlerFunc(func(s gocoap.ResponseWriter, req *gocoap.Request) { + validateCommand(s, req, server, refreshTokenHandler) + })) + + server.coapServer = &gocoap.Server{ + Net: server.Net, + Addr: server.Addr, + DisableTCPSignalMessageCSM: server.DisableTCPSignalMessageCSM, + DisablePeerTCPSignalMessageCSMs: server.DisablePeerTCPSignalMessageCSMs, + KeepAlive: server.Keepalive, + Handler: gocoap.HandlerFunc(server.logginMiddleware(server.authMiddleware(mux.ServeCOAP))), + NotifySessionNewFunc: server.coapConnOnNew, + NotifySessionEndFunc: server.coapConnOnClose, + HeartBeat: server.ConnectionsHeartBeat, + } +} + +func (server *Server) tlsEnabled() bool { + return strings.HasSuffix(server.Net, "-tls") +} + +// Serve starts a coapgateway on the configured address in *Server. +func (server *Server) Serve() error { + server.setupCoapServer() + server.coapServer.Listener = server.listener + return server.coapServer.ActivateAndServe() +} + +// Shutdown turn off server. +func (server *Server) Shutdown() error { + err := server.coapServer.Shutdown() + server.listener.Close() + return err +} diff --git a/coap-gateway/service/server_test.go b/coap-gateway/service/server_test.go new file mode 100644 index 000000000..0fd05e902 --- /dev/null +++ b/coap-gateway/service/server_test.go @@ -0,0 +1,92 @@ +package service_test + +import ( + "os" + "sync" + "testing" + + authConfig "github.com/go-ocf/cloud/authorization/service" + authService "github.com/go-ocf/cloud/authorization/test/service" + "github.com/go-ocf/cloud/coap-gateway/refImpl" + "github.com/go-ocf/cloud/coap-gateway/uri" + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + refImplRA "github.com/go-ocf/cloud/resource-aggregate/refImpl" + raService "github.com/go-ocf/cloud/resource-aggregate/test/service" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestServer(t *testing.T) { + var config refImpl.Config + err := envconfig.Process("", &config) + require.NoError(t, err) + + coapGWAcmeDirectory := os.Getenv("TEST_COAP_GW_OVERWRITE_LISTEN_ACME_DIRECTORY_URL") + require.NotEmpty(t, coapGWAcmeDirectory) + config.Listen.Acme.CADirURL = coapGWAcmeDirectory + + config.Service.Addr = "localhost:12345" + config.Service.AuthServerAddr = "localhost:12346" + config.Service.ResourceAggregateAddr = "localhost:12347" + config.Service.ResourceDirectoryAddr = "localhost:12348" + // config.Log.Debug = true + + var authConfig authConfig.Config + err = envconfig.Process("", &authConfig) + require.NoError(t, err) + authConfig.Addr = config.Service.AuthServerAddr + + shutdownAS := authService.NewAuthServer(t, authConfig) + defer shutdownAS() + + var raCfg refImplRA.Config + err = envconfig.Process("", &raCfg) + require.NoError(t, err) + raCfg.Service.Addr = config.Service.ResourceAggregateAddr + raCfg.Service.AuthServerAddr = config.Service.AuthServerAddr + shutdownRA := raService.NewResourceAggregate(t, raCfg) + defer shutdownRA() + + var waitForEndServe sync.WaitGroup + waitForEndServe.Add(1) + defer waitForEndServe.Wait() + + server, err := refImpl.Init(config) + require.NoError(t, err) + defer server.Shutdown() + + go func() { + server.Serve() + waitForEndServe.Done() + }() + + co := testCoapDial(t, config.Service.Addr, config.Service.Net) + defer co.Close() + + resp, err := co.Get(uri.ResourcePing) + require.NoError(t, err) + assert.Equal(t, resp.Code(), coapCodes.Content) + resp, err = co.Get(uri.ResourceDirectory) + if err == nil { + assert.Equal(t, resp.Code(), coapCodes.Unauthorized) + } +} + +func testCoapDial(t *testing.T, host, net string) *gocoap.ClientConn { + c := &gocoap.Client{Net: net, Handler: func(w gocoap.ResponseWriter, req *gocoap.Request) { + switch req.Msg.Code() { + case coapCodes.POST, coapCodes.GET, coapCodes.PUT, coapCodes.DELETE: + w.SetContentFormat(gocoap.TextPlain) + w.Write([]byte("hello world")) + } + }} + conn, err := c.Dial(host) + require.NoError(t, err) + return conn +} + +var ( + CertIdentity = "b5a2a42e-b285-42f1-a36b-034c8fc8efd5" +) diff --git a/coap-gateway/service/signIn.go b/coap-gateway/service/signIn.go new file mode 100644 index 000000000..05cc9d748 --- /dev/null +++ b/coap-gateway/service/signIn.go @@ -0,0 +1,174 @@ +package service + +import ( + "context" + "errors" + "fmt" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/cloud/coap-gateway/coapconv" + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/go-ocf/kit/codec/cbor" + "github.com/go-ocf/kit/log" + "github.com/go-ocf/kit/net/coap" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + "google.golang.org/grpc/status" +) + +type CoapSignInReq struct { + DeviceId string `json:"di"` + UserId string `json:"uid"` + AccessToken string `json:"accesstoken"` + Login bool `json:"login"` +} + +type CoapSignInResp struct { + ExpiresIn int64 `json:"expiresin"` +} + +func validateSignIn(req CoapSignInReq) error { + if req.DeviceId == "" { + return errors.New("cannot sign in to auth server: invalid deviceId") + } + if req.AccessToken == "" { + return errors.New("cannot sign in to auth server: invalid accessToken") + } + if req.UserId == "" { + return errors.New("cannot sign in to auth server: invalid userId") + } + return nil +} + +// https://github.com/openconnectivityfoundation/security/blob/master/swagger2.0/oic.sec.session.swagger.json +func signInPostHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client, signIn CoapSignInReq) { + err := validateSignIn(signIn) + if err != nil { + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle sign in: %v", err), s, client, coapCodes.BadRequest) + return + } + } + + resp, err := client.server.asClient.SignIn(kitNetGrpc.CtxWithToken(req.Ctx, signIn.AccessToken), &pbAS.SignInRequest{ + DeviceId: signIn.DeviceId, + UserId: signIn.UserId, + }) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle sign in: %v", err), s, client, coapconv.GrpcCode2CoapCode(status.Convert(err).Code(), coapCodes.POST)) + return + } + + coapResp := CoapSignInResp{ + ExpiresIn: resp.ExpiresIn, + } + + accept := coap.GetAccept(req.Msg) + encode, err := coap.GetEncoder(accept) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle sign in: %v", err), s, client, coapCodes.InternalServerError) + return + } + out, err := encode(coapResp) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle sign in: %v", err), s, client, coapCodes.InternalServerError) + return + } + + authCtx := authCtx{ + AuthorizationContext: pbCQRS.AuthorizationContext{ + DeviceId: signIn.DeviceId, + UserId: signIn.UserId, + }, + AccessToken: signIn.AccessToken, + } + + err = client.UpdateCloudDeviceStatus(kitNetGrpc.CtxWithToken(req.Ctx, signIn.AccessToken), signIn.DeviceId, authCtx.AuthorizationContext, true) + if err != nil { + // Events from resources of device will be comes but device is offline. To recover cloud state, client need to reconnect to cloud. + logAndWriteErrorResponse(fmt.Errorf("cannot handle sign in: cannot update cloud device status: %v", err), s, client, coapCodes.InternalServerError) + client.Close() + return + } + + oldDeviceId := client.storeAuthorizationContext(authCtx) + newDevice := false + + switch { + case oldDeviceId == "": + newDevice = true + case oldDeviceId != signIn.DeviceId: + err := client.server.projection.Unregister(oldDeviceId) + client.server.clientContainerByDeviceId.Remove(oldDeviceId) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle sign in: %v", err), s, client, coapCodes.InternalServerError) + client.Close() + return + } + newDevice = true + } + + if newDevice { + client.server.clientContainerByDeviceId.Add(signIn.DeviceId, client) + loaded, err := client.server.projection.Register(context.Background(), signIn.DeviceId) + if err != nil { + client.server.projection.Unregister(signIn.DeviceId) + client.server.clientContainerByDeviceId.Remove(signIn.DeviceId) + + logAndWriteErrorResponse(fmt.Errorf("cannot handle sign in: %v", err), s, client, coapCodes.InternalServerError) + client.Close() + return + } + if !loaded { + models := client.server.projection.Models(signIn.DeviceId, "") + if len(models) == 0 { + log.Errorf("cannot load models for deviceId %v", signIn.DeviceId) + } else { + for _, r := range models { + r.(*resourceCtx).TriggerSignIn() + } + } + } + } + sendResponse(s, client, coapCodes.Changed, accept, out) +} + +func signOutPostHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + authCtxOld := client.loadAuthorizationContext() + + if authCtxOld.DeviceId != "" { + err := client.UpdateCloudDeviceStatus(kitNetGrpc.CtxWithToken(req.Ctx, authCtxOld.AccessToken), authCtxOld.DeviceId, authCtxOld.AuthorizationContext, false) + if err != nil { + // Device will be still reported as online and it can fix his state by next calls online, offline commands. + log.Errorf("DeviceId %v: cannot handle sign out: cannot update cloud device status: %v", authCtxOld.DeviceId, err) + return + } + + client.storeAuthorizationContext(authCtx{}) + } + + sendResponse(s, client, coapCodes.Changed, coap.GetAccept(req.Msg), []byte{0xA0}) // empty object +} + +// Sign-in +// https://github.com/openconnectivityfoundation/security/blob/master/swagger2.0/oic.sec.session.swagger.json +func signInHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + switch req.Msg.Code() { + case coapCodes.POST: + var signIn CoapSignInReq + err := cbor.Decode(req.Msg.Payload(), &signIn) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle sign in: %v", err), s, client, coapCodes.BadRequest) + return + } + switch signIn.Login { + case true: + signInPostHandler(s, req, client, signIn) + default: + signOutPostHandler(s, req, client) + } + default: + logAndWriteErrorResponse(fmt.Errorf("Forbidden request from %v", req.Client.RemoteAddr()), s, client, coapCodes.Forbidden) + } +} diff --git a/coap-gateway/service/signIn_test.go b/coap-gateway/service/signIn_test.go new file mode 100644 index 000000000..d85710a3d --- /dev/null +++ b/coap-gateway/service/signIn_test.go @@ -0,0 +1,104 @@ +package service + +import ( + "testing" + + oauthTest "github.com/go-ocf/cloud/authorization/provider" + "github.com/go-ocf/cloud/coap-gateway/uri" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" +) + +type TestCoapSignInResponse struct { + ExpiresIn uint64 `json:"-"` +} + +func TestSignInPostHandler(t *testing.T) { + tbl := []testEl{ + {"BadRequest0", input{coapCodes.POST, `{"login": true }`, nil}, output{coapCodes.BadRequest, `invalid deviceId`, nil}}, + {"BadRequest1", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "accesstoken": 123, "login": true}`, nil}, output{coapCodes.BadRequest, `cannot handle sign in: cbor: cannot unmarshal positive integer`, nil}}, + {"BadRequest2", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "accesstoken": "123", "login": true }`, nil}, output{coapCodes.BadRequest, `invalid userId`, nil}}, + {"BadRequest3", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "uid": "0", "login": true }`, nil}, output{coapCodes.BadRequest, `invalid accessToken`, nil}}, + {"Changed1", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "uid":"` + AuthorizationUserId + `", "accesstoken":"` + oauthTest.UserToken + `", "login": true }`, nil}, output{coapCodes.Changed, TestCoapSignInResponse{}, nil}}, + } + + var config Config + err := envconfig.Process("", &config) + assert.NoError(t, err) + config.AuthServerAddr = "localhost:12345" + config.ResourceAggregateAddr = "localhost:12348" + config.ResourceDirectoryAddr = "localhost:12349" + deviceDB := t.Name() + "_deviceDB" + resourceDB := t.Name() + "_resourceDB" + + shutdownSA := testCreateAuthServer(t, config.AuthServerAddr) + defer shutdownSA() + shutdownRA := testCreateResourceAggregate(t, deviceDB, config.ResourceAggregateAddr, config.AuthServerAddr) + defer shutdownRA() + shutdownGW := testCreateCoapGateway(t, resourceDB, config) + defer shutdownGW() + + co := testCoapDial(t, config.Addr, config.Net) + if co == nil { + return + } + defer co.Close() + + signUpEl := testEl{"Changed0", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "accesstoken": "123", "authprovider": "` + oauthTest.NewTestProvider().GetProviderName() + `"}`, nil}, output{coapCodes.Changed, TestCoapSignUpResponse{RefreshToken: "refresh-token", UserId: AuthorizationUserId}, nil}} + testPostHandler(t, uri.SignUp, signUpEl, co) + + for _, test := range tbl { + tf := func(t *testing.T) { + testPostHandler(t, uri.SignIn, test, co) + testPostHandler(t, uri.SecureSignIn, test, co) + } + t.Run(test.name, tf) + } +} + +func TestSignOutPostHandler(t *testing.T) { + tbl := []testEl{ + {"Changed0", input{coapCodes.POST, `{}`, nil}, output{coapCodes.Changed, TestCoapSignInResponse{}, nil}}, + {"Changed1", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "accesstoken":"` + oauthTest.UserToken + `", "login": false }`, nil}, output{coapCodes.Changed, TestCoapSignInResponse{}, nil}}, + } + + var config Config + err := envconfig.Process("", &config) + assert.NoError(t, err) + config.AuthServerAddr = "localhost:12345" + config.ResourceAggregateAddr = "localhost:12348" + config.ResourceDirectoryAddr = "localhost:12349" + deviceDB := t.Name() + "_deviceDB" + resourceDB := t.Name() + "_resourceDB" + + shutdownSA := testCreateAuthServer(t, config.AuthServerAddr) + defer shutdownSA() + shutdownRA := testCreateResourceAggregate(t, deviceDB, config.ResourceAggregateAddr, config.AuthServerAddr) + defer shutdownRA() + shutdownGW := testCreateCoapGateway(t, resourceDB, config) + defer shutdownGW() + + co := testCoapDial(t, config.Addr, config.Net) + if co == nil { + return + } + defer co.Close() + + signUpEl := testEl{"signUp", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "accesstoken": "123", "authprovider": "` + oauthTest.NewTestProvider().GetProviderName() + `"}`, nil}, output{coapCodes.Changed, TestCoapSignUpResponse{RefreshToken: "refresh-token", UserId: AuthorizationUserId}, nil}} + t.Run(signUpEl.name, func(t *testing.T) { + testPostHandler(t, uri.SignUp, signUpEl, co) + }) + signInEl := testEl{"signIn", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "uid":"` + AuthorizationUserId + `", "accesstoken":"` + oauthTest.UserToken + `", "login": true }`, nil}, output{coapCodes.Changed, TestCoapSignInResponse{}, nil}} + t.Run(signInEl.name, func(t *testing.T) { + testPostHandler(t, uri.SignIn, signInEl, co) + }) + + for _, test := range tbl { + tf := func(t *testing.T) { + testPostHandler(t, uri.SignIn, test, co) + testPostHandler(t, uri.SecureSignIn, test, co) + } + t.Run(test.name, tf) + } +} diff --git a/coap-gateway/service/signUp.go b/coap-gateway/service/signUp.go new file mode 100644 index 000000000..0e1e75512 --- /dev/null +++ b/coap-gateway/service/signUp.go @@ -0,0 +1,182 @@ +package service + +import ( + "fmt" + "net/url" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/cloud/coap-gateway/coapconv" + gocoap "github.com/go-ocf/go-coap" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/go-ocf/kit/codec/cbor" + "github.com/go-ocf/kit/log" + "github.com/go-ocf/kit/net/coap" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + "google.golang.org/grpc/status" +) + +var ( + queryAccessToken = "accesstoken" + queryDeviceID = "di" + queryUserID = "uid" // optional because it is not defined in a current specification => it must be determined from the access token +) + +type CoapSignUpRequest struct { + DeviceId string `json:"di"` + AuthorizationCode string `json:"accesstoken"` + AuthorizationProvider string `json:"authprovider"` + AuthorizationCodeLegacy string `json:"authcode"` +} + +type CoapSignUpResponse struct { + AccessToken string `json:"accesstoken"` + UserId string `json:"uid"` + RefreshToken string `json:"refreshtoken"` + ExpiresIn int64 `json:"expiresin"` + RedirectUri string `json:"redirecturi"` +} + +func validateSignUp(req CoapSignUpRequest) error { + if req.DeviceId == "" { + return fmt.Errorf("cannot sign up to auth server: invalid deviceId") + } + if req.AuthorizationCode == "" { + return fmt.Errorf("cannot sign up to auth server: invalid authorizationCode") + } + return nil +} + +// https://github.com/openconnectivityfoundation/security/blob/master/swagger2.0/oic.sec.account.swagger.json +func signUpPostHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + var signUp CoapSignUpRequest + err := cbor.Decode(req.Msg.Payload(), &signUp) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle sign up: %v", err), s, client, coapCodes.BadRequest) + return + } + + // set AuthorizationCode from AuthorizationCodeLegacy + if signUp.AuthorizationCode == "" { + signUp.AuthorizationCode = signUp.AuthorizationCodeLegacy + } + + err = validateSignUp(signUp) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle sign up: %v", err), s, client, coapCodes.BadRequest) + return + } + + response, err := client.server.asClient.SignUp(req.Ctx, &pbAS.SignUpRequest{ + DeviceId: signUp.DeviceId, + AuthorizationCode: signUp.AuthorizationCode, + AuthorizationProvider: signUp.AuthorizationProvider, + }) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle sign up: %v", err), s, client, coapconv.GrpcCode2CoapCode(status.Convert(err).Code(), coapCodes.POST)) + return + } + + err = client.PublishCloudDeviceStatus(kitNetGrpc.CtxWithToken(req.Ctx, response.AccessToken), signUp.DeviceId, pbCQRS.AuthorizationContext{ + UserId: response.UserId, + DeviceId: signUp.DeviceId, + }) + if err != nil { + log.Errorf("cannot publish cloud device status: %v", err) + } + + coapResponse := CoapSignUpResponse{ + AccessToken: response.AccessToken, + UserId: response.UserId, + RefreshToken: response.RefreshToken, + ExpiresIn: response.ExpiresIn, + RedirectUri: response.RedirectUri, + } + + accept := coap.GetAccept(req.Msg) + encode, err := coap.GetEncoder(accept) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle sign up: %v", err), s, client, coapCodes.InternalServerError) + return + } + out, err := encode(coapResponse) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle sign up: %v", err), s, client, coapCodes.InternalServerError) + return + } + + sendResponse(s, client, coapCodes.Changed, accept, out) +} + +// Sign-up +// https://github.com/openconnectivityfoundation/security/blob/master/swagger2.0/oic.sec.account.swagger.json +func signUpHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + switch req.Msg.Code() { + case coapCodes.POST: + signUpPostHandler(s, req, client) + case coapCodes.DELETE: + signOffHandler(s, req, client) + default: + logAndWriteErrorResponse(fmt.Errorf("Forbidden request from %v", req.Client.RemoteAddr()), s, client, coapCodes.Forbidden) + } +} + +func validateSignOff(deviceID, accessToken string) error { + if deviceID == "" { + return fmt.Errorf("invalid '%v'", queryDeviceID) + } + if accessToken == "" { + return fmt.Errorf("invalid '%v'", queryAccessToken) + } + return nil +} + +// Sign-off +// https://github.com/openconnectivityfoundation/security/blob/master/swagger2.0/oic.sec.account.swagger.json +func signOffHandler(s gocoap.ResponseWriter, req *gocoap.Request, client *Client) { + //from QUERY: di, accesstoken + var deviceID string + var accessToken string + var userID string + + for _, q := range req.Msg.Options(gocoap.URIQuery) { + var query string + var ok bool + if query, ok = q.(string); !ok { + continue + } + + values, err := url.ParseQuery(query) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle sign off: %v", err), s, client, coapCodes.BadOption) + return + } + if di := values.Get(queryDeviceID); di != "" { + deviceID = di + } + + if at := values.Get(queryAccessToken); at != "" { + accessToken = at + } + + if uid := values.Get(queryUserID); uid != "" { + userID = uid + } + } + + err := validateSignOff(deviceID, accessToken) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle sign off: %v", err), s, client, coapCodes.BadRequest) + return + } + _, err = client.server.asClient.SignOff(kitNetGrpc.CtxWithToken(req.Ctx, accessToken), &pbAS.SignOffRequest{ + DeviceId: deviceID, + UserId: userID, + }) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot handle sign off: %v", err), s, client, coapconv.GrpcCode2CoapCode(status.Convert(err).Code(), coapCodes.DELETE)) + return + } + client.storeAuthorizationContext(authCtx{}) + sendResponse(s, client, coapCodes.Deleted, gocoap.TextPlain, nil) +} diff --git a/coap-gateway/service/signUp_test.go b/coap-gateway/service/signUp_test.go new file mode 100644 index 000000000..90176e20f --- /dev/null +++ b/coap-gateway/service/signUp_test.go @@ -0,0 +1,106 @@ +package service + +import ( + "testing" + + oauthTest "github.com/go-ocf/cloud/authorization/provider" + "github.com/go-ocf/cloud/coap-gateway/uri" + coapCodes "github.com/go-ocf/go-coap/codes" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" +) + +type TestCoapSignUpResponse struct { + AccessToken string `json:"-"` + RedirectUri string `json:"redirecturi"` + ExpiresIn uint64 `json:"-"` + RefreshToken string `json:"refreshtoken"` + UserId string `json:"uid"` +} + +func TestSignUpPostHandler(t *testing.T) { + tbl := []testEl{ + {"BadRequest0", input{coapCodes.POST, `{}`, nil}, output{coapCodes.BadRequest, `invalid deviceId`, nil}}, + {"BadRequest1", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "accesstoken": 123}`, nil}, output{coapCodes.BadRequest, `cannot handle sign up: cbor: cannot unmarshal positive`, nil}}, + {"Changed0", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "accesstoken": "123", "authprovider": "` + oauthTest.NewTestProvider().GetProviderName() + `"}`, nil}, output{coapCodes.Changed, TestCoapSignUpResponse{RefreshToken: "refresh-token", UserId: "1"}, nil}}, + } + + var config Config + err := envconfig.Process("", &config) + assert.NoError(t, err) + config.AuthServerAddr = "localhost:12345" + config.ResourceAggregateAddr = "localhost:12348" + config.ResourceDirectoryAddr = "localhost:12349" + deviceDB := t.Name() + "_deviceDB" + resourceDB := t.Name() + "_resourceDB" + + shutdownSA := testCreateAuthServer(t, config.AuthServerAddr) + defer shutdownSA() + shutdownDA := testCreateResourceAggregate(t, deviceDB, config.ResourceAggregateAddr, config.AuthServerAddr) + defer shutdownDA() + shutdownGW := testCreateCoapGateway(t, resourceDB, config) + defer shutdownGW() + + co := testCoapDial(t, config.Addr, config.Net) + if co == nil { + return + } + defer co.Close() + + for _, test := range tbl { + tf := func(t *testing.T) { + testPostHandler(t, uri.SignUp, test, co) + testPostHandler(t, uri.SecureSignUp, test, co) + } + t.Run(test.name, tf) + } +} + +func TestSignOffHandler(t *testing.T) { + tbl := []testEl{ + {"BadRequest0", input{coapCodes.DELETE, `{}`, nil}, output{coapCodes.BadRequest, "invalid 'di'", nil}}, + {"BadRequest1", input{coapCodes.DELETE, `{}`, []string{"di=" + CertIdentity}}, output{coapCodes.BadRequest, "invalid 'accesstoken'", nil}}, + /* TODO: coap.URIQuery param has limit to 255 bytes, but jwt token has arround 460. Token cannot be send by coap.URIQuery + {"Deleted0", input{coapCodes.DELETE, `{}`, []string{"di=" + CertIdentity, "accesstoken=" + oauthTest.UserToken}}, output{coapCodes.Deleted, nil, nil}}, + {"Deleted1", input{coapCodes.DELETE, `{}`, []string{"di=" + CertIdentity, "accesstoken=" + oauthTest.UserToken, "uid=1"}}, output{coapCodes.Deleted, nil, nil}}, + */ + } + + var config Config + err := envconfig.Process("", &config) + assert.NoError(t, err) + config.AuthServerAddr = "localhost:12345" + config.ResourceAggregateAddr = "localhost:12348" + config.ResourceDirectoryAddr = "localhost:12349" + deviceDB := t.Name() + "_deviceDB" + resourceDB := t.Name() + "_resourceDB" + + shutdownSA := testCreateAuthServer(t, config.AuthServerAddr) + defer shutdownSA() + shutdownDA := testCreateResourceAggregate(t, deviceDB, config.ResourceAggregateAddr, config.AuthServerAddr) + defer shutdownDA() + shutdownGW := testCreateCoapGateway(t, resourceDB, config) + defer shutdownGW() + + co := testCoapDial(t, config.Addr, config.Net) + if co == nil { + return + } + defer co.Close() + + signUpEl := testEl{"Changed0", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "accesstoken": "123", "authprovider": "` + oauthTest.NewTestProvider().GetProviderName() + `"}`, nil}, output{coapCodes.Changed, TestCoapSignUpResponse{RefreshToken: "refresh-token", UserId: AuthorizationUserId}, nil}} + for _, test := range tbl { + tf := func(t *testing.T) { + // create record for signUp + testPostHandler(t, uri.SignUp, signUpEl, co) + // delete record for signUp + testPostHandler(t, uri.SignUp, test, co) + + // create record for secureSignUp + testPostHandler(t, uri.SecureSignUp, signUpEl, co) + // delete record for secureSignUp + testPostHandler(t, uri.SecureSignUp, test, co) + } + t.Run(test.name, tf) + } +} diff --git a/coap-gateway/service/utils_test.go b/coap-gateway/service/utils_test.go new file mode 100644 index 000000000..c4cbce6d3 --- /dev/null +++ b/coap-gateway/service/utils_test.go @@ -0,0 +1,422 @@ +package service + +import ( + "context" + "crypto/x509" + "fmt" + "os" + "reflect" + "strings" + "sync" + "testing" + "time" + + "github.com/go-ocf/cloud/coap-gateway/uri" + "github.com/go-ocf/kit/codec/cbor" + "github.com/go-ocf/kit/codec/json" + "github.com/go-ocf/kit/net/coap" + + "github.com/go-ocf/kit/security/certManager" + "github.com/go-ocf/kit/security/certManager/acme/ocf" + + coapCodes "github.com/go-ocf/go-coap/codes" + kitNetCoap "github.com/go-ocf/kit/net/coap" + + oauthTest "github.com/go-ocf/cloud/authorization/provider" + authConfig "github.com/go-ocf/cloud/authorization/service" + authService "github.com/go-ocf/cloud/authorization/test/service" + gocoap "github.com/go-ocf/go-coap" + "github.com/go-ocf/kit/log" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventbus/nats" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/mongodb" + refImplRA "github.com/go-ocf/cloud/resource-aggregate/refImpl" + raService "github.com/go-ocf/cloud/resource-aggregate/test/service" + refImplRD "github.com/go-ocf/cloud/resource-directory/refImpl" + rdService "github.com/go-ocf/cloud/resource-directory/test/service" + "github.com/kelseyhightower/envconfig" + "github.com/panjf2000/ants" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type input struct { + code coapCodes.Code + payload interface{} + queries []string +} + +type output input + +type testEl struct { + name string + in input + out output +} + +func testCreateResourceStoreSub(t *testing.T, resourceDBname string) (*mongodb.EventStore, *nats.Subscriber) { + var natsCfg nats.Config + err := envconfig.Process("", &natsCfg) + assert.NoError(t, err) + var mgoCfg mongodb.Config + err = envconfig.Process("", &mgoCfg) + mgoCfg.DatabaseName = resourceDBname + assert.NoError(t, err) + + var cmconfig certManager.Config + err = envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + + subscriber, err := nats.NewSubscriber(natsCfg, nil, func(err error) { log.Errorf("%v", err) }, nats.WithTLS(&tlsConfig)) + assert.NoError(t, err) + eventstore, err := mongodb.NewEventStore(mgoCfg, nil, mongodb.WithTLS(&tlsConfig)) + assert.NoError(t, err) + return eventstore, subscriber +} + +func initializeStruct(t reflect.Type, v reflect.Value) { + for i := 0; i < v.NumField(); i++ { + f := v.Field(i) + ft := t.Field(i) + switch ft.Type.Kind() { + case reflect.Map: + f.Set(reflect.MakeMap(ft.Type)) + case reflect.Slice: + f.Set(reflect.MakeSlice(ft.Type, 0, 0)) + case reflect.Chan: + f.Set(reflect.MakeChan(ft.Type, 0)) + case reflect.Struct: + initializeStruct(ft.Type, f) + case reflect.Ptr: + fv := reflect.New(ft.Type.Elem()) + initializeStruct(ft.Type.Elem(), fv.Elem()) + f.Set(fv) + default: + } + } +} + +func testValidateResp(t *testing.T, test testEl, resp gocoap.Message) { + if resp.Code() != test.out.code { + t.Fatalf("Ouput code %v is invalid, expected %v", resp.Code(), test.out.code) + } else { + if len(resp.Payload()) > 0 || test.out.payload != nil { + if contentType, ok := resp.Option(gocoap.ContentFormat).(gocoap.MediaType); ok { + switch contentType { + case gocoap.AppCBOR, gocoap.AppOcfCbor: + n := reflect.New(reflect.TypeOf(test.out.payload)).Interface() + err := cbor.Decode(resp.Payload(), n) + if err != nil { + t.Fatalf("Cannot convert cbor to type: %v %v", err, n) + } + if !assert.Equal(t, test.out.payload, reflect.ValueOf(n).Elem().Interface()) { + t.Fatal() + } + case gocoap.TextPlain: + if v, ok := test.out.payload.(string); ok { + if strings.Count(string(resp.Payload()), v) == 0 { + t.Fatalf("Ouput payload '%v' is invalid, expected '%v'", string(resp.Payload()), test.out.payload) + } + } else { + t.Fatalf("Ouput payload %v is invalid, expected %v", resp.Payload(), test.out.payload) + } + } + } else { + t.Fatalf("Ouput payload %v is invalid, expected %v", resp.Payload(), test.out.payload) + } + } + if len(test.out.queries) > 0 { + queries := resp.Options(gocoap.URIQuery) + if resp == nil { + t.Fatalf("Output doesn't contains queries, expected: %v", test.out.queries) + } + if len(queries) == len(test.out.queries) { + t.Fatalf("Invalid queries %v, expected: %v", queries, test.out.queries) + } + for idx := range queries { + if queries[idx] != test.out.queries[idx] { + t.Fatalf("Invalid query %v, expected %v", queries[idx], test.out.queries[idx]) + } + } + } + } +} + +func testPostHandler(t *testing.T, path string, test testEl, co *gocoap.ClientConn) { + var inputCbor []byte + var err error + if v, ok := test.in.payload.(string); ok && v != "" { + inputCbor, err = json2cbor(v) + } + if err != nil { + t.Fatalf("Cannot convert json to cbor: %v", err) + } + + req := co.NewMessage(gocoap.MessageParams{ + Code: test.in.code, + Token: func() []byte { + token, err := gocoap.GenerateToken() + if err != nil { + t.Fatalf("Cannot generate token: %v", err) + } + return token + }(), + MessageID: gocoap.GenerateMessageID(), + }) + req.SetPathString(path) + if len(inputCbor) > 0 { + req.AddOption(gocoap.ContentFormat, gocoap.AppOcfCbor) + req.SetPayload(inputCbor) + } + for _, q := range test.in.queries { + req.AddOption(gocoap.URIQuery, q) + } + + ctx, cancel := context.WithTimeout(context.Background(), TestExchangeTimeout) + defer cancel() + resp, err := co.ExchangeWithContext(ctx, req) + if err != nil { + t.Fatalf("Cannot send/retrieve msg: %v", err) + } + testValidateResp(t, test, resp) +} + +func json2cbor(data string) ([]byte, error) { + return json.ToCBOR(data) +} + +func cannonalizeJSON(data string) (string, error) { + if len(data) == 0 { + return "", nil + } + var m interface{} + err := json.Decode([]byte(data), &m) + if err != nil { + return "", err + } + out, err := json.Encode(m) + return string(out), err +} + +func cbor2json(data []byte) (string, error) { + return cbor.ToJSON(data) +} + +func testCreateCoapGateway(t *testing.T, resourceDBname string, config Config) func() { + eventstore, subscriber := testCreateResourceStoreSub(t, resourceDBname) + var acmeCfg certManager.Config + err := envconfig.Process("DIAL", &acmeCfg) + assert.NoError(t, err) + + clientCertManager, err := certManager.NewCertManager(acmeCfg) + require.NoError(t, err) + + var listenCertManager ListenCertManager + if strings.HasSuffix(config.Net, "-tls") { + var acmeOcfCfg ocf.Config + err = envconfig.Process("LISTEN_ACME", &acmeOcfCfg) + assert.NoError(t, err) + coapGWAcmeDirectory := os.Getenv("TEST_COAP_GW_OVERWRITE_LISTEN_ACME_DIRECTORY_URL") + require.NotEmpty(t, coapGWAcmeDirectory) + acmeOcfCfg.CADirURL = coapGWAcmeDirectory + listenCertManager, err = ocf.NewAcmeCertManagerFromConfiguration(acmeOcfCfg) + require.NoError(t, err) + } + + pool, err := ants.NewPool(16) + assert.NoError(t, err) + server := New(config, clientCertManager, listenCertManager, func(ctx context.Context, code coapCodes.Code, path string) (context.Context, error) { + switch path { + case uri.RefreshToken, uri.SecureRefreshToken, uri.SignUp, uri.SecureSignUp, uri.SignIn, uri.SecureSignIn, uri.ResourcePing: + return ctx, nil + } + _, err := kitNetCoap.TokenFromCtx(ctx) + if err != nil { + return ctx, err + } + return ctx, nil + }, eventstore, subscriber, pool) + server.setupCoapServer() + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + server.Serve() + }() + + return func() { + server.Shutdown() + wg.Wait() + } +} + +func testCreateResourceAggregate(t *testing.T, resourceDBname, addr, AuthServerAddr string) (shutdown func()) { + var config refImplRA.Config + err := envconfig.Process("", &config) + assert.NoError(t, err) + config.Service.AuthServerAddr = AuthServerAddr + config.MongoDB.DatabaseName = resourceDBname + config.Service.Addr = addr + //config.Log.Debug = TestLogDebug + config.Service.SnapshotThreshold = 1 + + return raService.NewResourceAggregate(t, config) +} + +func init() { + log.Setup(log.Config{Debug: TestLogDebug}) +} + +func testPrepareDevice(t *testing.T, co *gocoap.ClientConn) { + + signUpEl := testEl{"signUp", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "accesstoken": "123", "authprovider": "` + oauthTest.NewTestProvider().GetProviderName() + `"}`, nil}, output{coapCodes.Changed, TestCoapSignUpResponse{RefreshToken: "refresh-token", UserId: AuthorizationUserId}, nil}} + testPostHandler(t, uri.SignUp, signUpEl, co) + signInEl := testEl{"signIn", input{coapCodes.POST, `{"di": "` + CertIdentity + `", "uid":"` + AuthorizationUserId + `", "accesstoken":"` + oauthTest.UserToken + `", "login": true }`, nil}, output{coapCodes.Changed, TestCoapSignInResponse{}, nil}} + testPostHandler(t, uri.SignIn, signInEl, co) + publishResEl := []testEl{ + testEl{"publishResourceA", input{coapCodes.POST, `{ "di":"` + CertIdentity + `", "links":[ { "di":"` + CertIdentity + `", "href":"` + TestAResourceHref + `", "rt":["` + TestAResourceType + `"], "type":["` + gocoap.TextPlain.String() + `"] } ], "ttl":12345}`, nil}, + output{coapCodes.Changed, TestWkRD{ + DeviceID: CertIdentity, + TimeToLive: 12345, + TimeToLiveLegacy: 12345, + Links: []TestResource{ + TestResource{ + DeviceId: CertIdentity, + Href: TestAResourceHref, + Id: TestAResourceId, + ResourceTypes: []string{TestAResourceType}, + Type: []string{gocoap.TextPlain.String()}, + }, + }, + }, nil}}, + testEl{"publishResourceB", input{coapCodes.POST, `{ "di":"` + CertIdentity + `", "links":[ { "di":"` + CertIdentity + `", "href":"` + TestBResourceHref + `", "rt":["` + TestBResourceType + `"], "type":["` + gocoap.TextPlain.String() + `"] } ], "ttl":12345}`, nil}, + output{coapCodes.Changed, TestWkRD{ + DeviceID: CertIdentity, + TimeToLive: 12345, + TimeToLiveLegacy: 12345, + Links: []TestResource{ + TestResource{ + DeviceId: CertIdentity, + Href: TestBResourceHref, + Id: TestBResourceId, + ResourceTypes: []string{TestBResourceType}, + Type: []string{gocoap.TextPlain.String()}, + }, + }, + }, nil}}, + } + for _, tt := range publishResEl { + testPostHandler(t, uri.ResourceDirectory, tt, co) + } +} + +func testCreateResourceDirectory(t *testing.T, resourceDBname, addr, AuthServerAddr string) func() { + var config refImplRD.Config + err := envconfig.Process("", &config) + assert.NoError(t, err) + config.Service.AuthServerAddr = AuthServerAddr + config.MongoDB.DatabaseName = resourceDBname + config.Service.Addr = addr + //config.Log.Debug = TestLogDebug + + return rdService.NewResourceDirectory(t, config) +} + +func testCreateAuthServer(t *testing.T, addr string) func() { + var authConfig authConfig.Config + + envconfig.Process("", &authConfig) + var acmeCfg certManager.Config + err := envconfig.Process("DIAL", &acmeCfg) + assert.NoError(t, err) + authConfig.Listen = acmeCfg + require.NoError(t, err) + authConfig.Addr = addr + + return authService.NewAuthServer(t, authConfig) +} + +func testCoapDial(t *testing.T, host, net string) *gocoap.ClientConn { + var config certManager.OcfConfig + err := envconfig.Process("LISTEN", &config) + assert.NoError(t, err) + config.Acme.DeviceID = CertIdentity + + listenCertManager, err := certManager.NewOcfCertManager(config) + require.NoError(t, err) + + tlsConfig := listenCertManager.GetClientTLSConfig() + tlsConfig.InsecureSkipVerify = true + tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { + if len(rawCerts) == 0 { + return fmt.Errorf("empty certificates chain") + } + intermediateCAPool := x509.NewCertPool() + certs := make([]*x509.Certificate, 0, len(rawCerts)) + for _, rawCert := range rawCerts { + cert, err := x509.ParseCertificate(rawCert) + if err != nil { + return err + } + certs = append(certs, cert) + } + for _, cert := range certs[1:] { + intermediateCAPool.AddCert(cert) + } + _, err := certs[0].Verify(x509.VerifyOptions{ + Roots: tlsConfig.RootCAs, + Intermediates: intermediateCAPool, + CurrentTime: time.Now(), + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, + }) + if err != nil { + return err + } + if coap.VerifyIndetityCertificate(certs[0]) != nil { + return err + } + return nil + } + + c := &gocoap.Client{Net: net, TLSConfig: &tlsConfig, Handler: func(w gocoap.ResponseWriter, req *gocoap.Request) { + switch req.Msg.Code() { + case coapCodes.POST, coapCodes.GET, coapCodes.PUT, coapCodes.DELETE: + w.SetContentFormat(gocoap.TextPlain) + w.Write([]byte("hello world")) + } + }} + conn, err := c.Dial(host) + assert.NoError(t, err) + return conn +} + +/* +type mockResponseWriter struct { + gocoap.ResponseWriter + code coapCodes.Code +} + +func (m *mockResponseWriter) NewResponse(code coapCodes.Code) gocoap.Message { m.code = code; return nil } +func (m *mockResponseWriter) Write(p []byte) (n int, err error) { return -1, nil } +func (m *mockResponseWriter) SetCode(code coapCodes.Code) { m.code = code } +func (m *mockResponseWriter) SetContentFormat(contentFormat gocoap.MediaType) {} +func (m *mockResponseWriter) WriteMsg(gocoap.Message) error { return nil } +*/ + +var ( + AuthorizationUserId = "1" + AuthorizationRefreshToken = "refresh-token" + + CertIdentity = "b5a2a42e-b285-42f1-a36b-034c8fc8efd5" + TestAResourceHref = "/a" + TestAResourceId = resource2UUID(CertIdentity, TestAResourceHref) + TestAResourceType = "x.a" + TestBResourceHref = "/b" + TestBResourceId = resource2UUID(CertIdentity, TestBResourceHref) + TestBResourceType = "x.b" + + TestExchangeTimeout = time.Second * 15 + TestLogDebug = true +) diff --git a/coap-gateway/test/service/service.go b/coap-gateway/test/service/service.go new file mode 100644 index 000000000..f206e16ca --- /dev/null +++ b/coap-gateway/test/service/service.go @@ -0,0 +1,33 @@ +package service + +import ( + "sync" + "testing" + + "github.com/go-ocf/cloud/coap-gateway/refImpl" + "github.com/stretchr/testify/require" +) + +// NewCoapGateway creates test coap-gateway. +func NewCoapGateway(t *testing.T, cfg refImpl.Config) func() { + t.Log("newCoapGateway") + defer t.Log("newCoapGateway done") + c, err := refImpl.Init(cfg) + require.NoError(t, err) + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + defer wg.Done() + err := c.Serve() + if err != nil { + t.Logf("coap-gw ends with error %v\n", err) + } + }() + + return func() { + c.Shutdown() + wg.Wait() + } +} diff --git a/coap-gateway/uri/uri.go b/coap-gateway/uri/uri.go new file mode 100644 index 000000000..f8377fecd --- /dev/null +++ b/coap-gateway/uri/uri.go @@ -0,0 +1,17 @@ +package uri + +// Resource Service URIs. +const ( + Base = "/oic" + Secure = Base + "/sec" + + SecureSignUp = Secure + "/account" + SignUp = Base + "/account" + SecureRefreshToken = Secure + "/tokenrefresh" + RefreshToken = Base + "/tokenrefresh" + SecureSignIn = Secure + "/session" + SignIn = Base + "/account/session" + ResourceDirectory = Base + "/rd" + ResourceDiscovery = Base + "/res" + ResourcePing = Base + "/ping" +) diff --git a/device-simulator/Dockerfile b/device-simulator/Dockerfile new file mode 100644 index 000000000..b85c9103f --- /dev/null +++ b/device-simulator/Dockerfile @@ -0,0 +1,12 @@ +FROM alpine:3.8 AS build + +RUN apk add --no-cache curl git build-base gcc linux-headers +RUN git clone --recursive https://github.com/iotivity/iotivity-lite.git +COPY ./patches/devsim.diff /devsim.diff +RUN (cd /iotivity-lite && git checkout e2d38bd7718ed4914060287ab4dfe47f03b832a0 && patch -p1 < /devsim.diff) +RUN make -C /iotivity-lite/port/linux CLOUD=1 SECURE=0 DEBUG=1 cloud_server + +FROM alpine:3.8 AS service +RUN apk add --no-cache ca-certificates +COPY --from=build /iotivity-lite/port/linux/cloud_server /usr/local/bin/service +ENTRYPOINT ["/usr/local/bin/service"] diff --git a/device-simulator/patches/devsim.diff b/device-simulator/patches/devsim.diff new file mode 100644 index 000000000..96b5d444b --- /dev/null +++ b/device-simulator/patches/devsim.diff @@ -0,0 +1,143 @@ +diff --git a/api/cloud/oc_cloud_resource.c b/api/cloud/oc_cloud_resource.c +index ece67a1..7678153 100644 +--- a/api/cloud/oc_cloud_resource.c ++++ b/api/cloud/oc_cloud_resource.c +@@ -144,7 +144,7 @@ post_cloud(oc_request_t *request, oc_interface_mask_t interface, + } + OC_DBG("POST request received"); + (void)interface; +- ++/* + switch (ctx->cps) { + case OC_CPS_UNINITIALIZED: + case OC_CPS_READYTOREGISTER: +@@ -155,7 +155,7 @@ post_cloud(oc_request_t *request, oc_interface_mask_t interface, + return; + } + } +- ++*/ + char *cps; + size_t cps_len = 0; + if (oc_rep_get_string(request->request_payload, "cps", &cps, &cps_len)) { +diff --git a/apps/cloud_server.c b/apps/cloud_server.c +index c5186aa..4042524 100644 +--- a/apps/cloud_server.c ++++ b/apps/cloud_server.c +@@ -18,6 +18,8 @@ + ****************************************************************************/ + + #include "oc_api.h" ++#include "oc_pki.h" ++#include "oc_core_res.h" + #include + #include + +@@ -282,11 +284,79 @@ register_resources(void) + oc_resource_set_request_handler(res2, OC_POST, post_handler, &light2); + oc_cloud_add_resource(res2); + oc_add_resource(res2); ++ ++ // publish con resource ++ oc_resource_t *con_res = oc_core_get_resource_by_index(OCF_CON, 0); ++ oc_cloud_add_resource(con_res); ++} ++ ++void ++factory_presets_cb(size_t device, void *data) ++{ ++ oc_device_info_t* dev = oc_core_get_device_info(device); ++ oc_free_string(&dev->name); ++ oc_new_string(&dev->name, device_name, strlen(device_name)); ++ (void)data; ++#if defined(OC_SECURITY) && defined(OC_PKI) ++ PRINT("factory_presets_cb: %d\n", (int) device); ++ ++ const char* cert = "-----BEGIN CERTIFICATE-----\n" ++"MIIB9zCCAZygAwIBAgIRAOwIWPAt19w7DswoszkVIEIwCgYIKoZIzj0EAwIwEzER\n" ++"MA8GA1UEChMIVGVzdCBPUkcwHhcNMTkwNTAyMjAwNjQ4WhcNMjkwMzEwMjAwNjQ4\n" ++"WjBHMREwDwYDVQQKEwhUZXN0IE9SRzEyMDAGA1UEAxMpdXVpZDpiNWEyYTQyZS1i\n" ++"Mjg1LTQyZjEtYTM2Yi0wMzRjOGZjOGVmZDUwWTATBgcqhkjOPQIBBggqhkjOPQMB\n" ++"BwNCAAQS4eiM0HNPROaiAknAOW08mpCKDQmpMUkywdcNKoJv1qnEedBhWne7Z0jq\n" ++"zSYQbyqyIVGujnI3K7C63NRbQOXQo4GcMIGZMA4GA1UdDwEB/wQEAwIDiDAzBgNV\n" ++"HSUELDAqBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMBBgorBgEEAYLefAEG\n" ++"MAwGA1UdEwEB/wQCMAAwRAYDVR0RBD0wO4IJbG9jYWxob3N0hwQAAAAAhwR/AAAB\n" ++"hxAAAAAAAAAAAAAAAAAAAAAAhxAAAAAAAAAAAAAAAAAAAAABMAoGCCqGSM49BAMC\n" ++"A0kAMEYCIQDuhl6zj6gl2YZbBzh7Th0uu5izdISuU/ESG+vHrEp7xwIhANCA7tSt\n" ++"aBlce+W76mTIhwMFXQfyF3awWIGjOcfTV8pU\n" ++"-----END CERTIFICATE-----\n"; ++ ++ const char* key = "-----BEGIN EC PRIVATE KEY-----\n" ++"MHcCAQEEIMPeADszZajrkEy4YvACwcbR0pSdlKG+m8ALJ6lj/ykdoAoGCCqGSM49\n" ++"AwEHoUQDQgAEEuHojNBzT0TmogJJwDltPJqQig0JqTFJMsHXDSqCb9apxHnQYVp3\n" ++"u2dI6s0mEG8qsiFRro5yNyuwutzUW0Dl0A==\n" ++"-----END EC PRIVATE KEY-----\n"; ++ ++ const char* root_ca = "-----BEGIN CERTIFICATE-----\n" ++"MIIBaTCCAQ+gAwIBAgIQR33gIB75I7Vi/QnMnmiWvzAKBggqhkjOPQQDAjATMREw\n" ++"DwYDVQQKEwhUZXN0IE9SRzAeFw0xOTA1MDIyMDA1MTVaFw0yOTAzMTAyMDA1MTVa\n" ++"MBMxETAPBgNVBAoTCFRlc3QgT1JHMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\n" ++"xbwMaS8jcuibSYJkCmuVHfeV3xfYVyUq8Iroz7YlXaTayspW3K4hVdwIsy/5U+3U\n" ++"vM/vdK5wn2+NrWy45vFAJqNFMEMwDgYDVR0PAQH/BAQDAgEGMBMGA1UdJQQMMAoG\n" ++"CCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0RBAQwAoIAMAoGCCqGSM49\n" ++"BAMCA0gAMEUCIBWkxuHKgLSp6OXDJoztPP7/P5VBZiwLbfjTCVRxBvwWAiEAnzNu\n" ++"6gKPwtKmY0pBxwCo3NNmzNpA6KrEOXE56PkiQYQ=\n" ++"-----END CERTIFICATE-----\n"; ++ ++ int ee_credid = oc_pki_add_mfg_cert(0, (const unsigned char *)cert, strlen(cert), ++ (const unsigned char *)key, strlen(key)); ++ if (ee_credid < 0) { ++ PRINT("ERROR installing manufacturer EE cert\n"); ++ return; ++ } ++ ++ int rootca_credid = ++ oc_pki_add_mfg_trust_anchor(0, (const unsigned char *)root_ca, strlen(root_ca)); ++ if (rootca_credid < 0) { ++ PRINT("ERROR installing root cert\n"); ++ return; ++ } ++ ++ oc_pki_set_security_profile(0, OC_SP_BLACK, OC_SP_BLACK, ee_credid); ++#endif /* OC_SECURITY && OC_PKI */ + } + + int +-main(void) ++main(int argc, const char** argv) + { ++ if (argc > 1) { ++ device_name = argv[1]; ++ PRINT("\t\tDevice Name: %s\n", argv[1]); ++ } ++ + int ret = init(); + if (ret < 0) { + return ret; +@@ -297,6 +367,7 @@ main(void) + .register_resources = + register_resources }; + oc_storage_config("./cloud_server_creds/"); ++ oc_set_factory_presets_cb(factory_presets_cb, NULL); + + ret = oc_main_init(&handler); + if (ret < 0) +@@ -304,6 +375,18 @@ main(void) + + oc_cloud_context_t *ctx = oc_cloud_get_context(0); + if (ctx) { ++ if (argc > 2) { ++ oc_new_string(&ctx->store.ci_server, argv[2], strlen(argv[2])); ++ PRINT("\t\tCloud ci_server: %s\n", argv[2]); ++ } ++ if (argc > 3) { ++ oc_new_string(&ctx->store.auth_provider, argv[3], strlen(argv[3])); ++ PRINT("\t\tCloud auth_provider: %s\n", argv[3]); ++ } ++ if (argc > 4) { ++ oc_new_string(&ctx->store.access_token, argv[4], strlen(argv[4])); ++ PRINT("\t\tCloud access_token: %s\n", argv[4]); ++ } + oc_cloud_manager_start(ctx, cloud_status_handler, NULL); + } + diff --git a/go.mod b/go.mod new file mode 100644 index 000000000..0d232d7b3 --- /dev/null +++ b/go.mod @@ -0,0 +1,39 @@ +module github.com/go-ocf/cloud + +go 1.13 + +require ( + github.com/buaazp/fasthttprouter v0.1.1 + github.com/dgrijalva/jwt-go v3.2.0+incompatible + github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 + github.com/go-chi/chi v4.1.0+incompatible + github.com/go-ocf/cqrs v0.0.0-20200324131357-db8a7b8c83be + github.com/go-ocf/go-coap v0.0.0-20200406073902-cf923db524db + github.com/go-ocf/kit v0.0.0-20200326124037-40a7509422e3 + github.com/go-ocf/sdk v0.0.0-20200406082918-51e333156732 + github.com/gofrs/uuid v3.2.0+incompatible + github.com/gogo/protobuf v1.3.1 + github.com/golang/protobuf v1.3.5 + github.com/golang/snappy v0.0.2-0.20190904063534-ff6b7dc882cf + github.com/google/go-github v17.0.0+incompatible + github.com/gorilla/mux v1.7.4 + github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 + github.com/jessevdk/go-flags v1.4.0 + github.com/kelseyhightower/envconfig v1.4.0 + github.com/lestrrat-go/jwx v0.9.1 + github.com/mattn/go-sqlite3 v2.0.3+incompatible + github.com/nats-io/nats.go v1.9.2 + github.com/panjf2000/ants v1.3.0 + github.com/patrickmn/go-cache v2.1.0+incompatible + github.com/satori/go.uuid v1.2.0 + github.com/smallstep/certificates v0.13.4-0.20191007194430-e2858e17b094 + github.com/smallstep/nosql v0.2.0 + github.com/stretchr/testify v1.5.1 + github.com/ugorji/go/codec v1.1.7 + github.com/valyala/fasthttp v1.9.0 + go.mongodb.org/mongo-driver v1.3.1 + go.uber.org/atomic v1.6.0 + golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d + golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a + google.golang.org/grpc v1.28.1 +) diff --git a/go.sum b/go.sum new file mode 100644 index 000000000..81389dbdb --- /dev/null +++ b/go.sum @@ -0,0 +1,589 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= +github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DataDog/zstd v1.3.6-0.20190409195224-796139022798/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/DataDog/zstd v1.4.0/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/DataDog/zstd v1.4.4/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Shopify/sarama v1.23.0/go.mod h1:XLH1GYJnLVE0XCr6KdJGVJRTwY30moWNJ4sERjXX6fs= +github.com/Shopify/sarama v1.24.0/go.mod h1:fGP8eQ6PugKEI0iUETYYtnP6d1pH/bdDMTel1X5ajsU= +github.com/Shopify/sarama v1.24.1 h1:svn9vfN3R1Hz21WR2Gj0VW9ehaDGkiOS+VqlIcZOkMI= +github.com/Shopify/sarama v1.24.1/go.mod h1:fGP8eQ6PugKEI0iUETYYtnP6d1pH/bdDMTel1X5ajsU= +github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/bsm/sarama-cluster v2.1.15+incompatible h1:RkV6WiNRnqEEbp81druK8zYhmnIgdOjqSVi0+9Cnl2A= +github.com/bsm/sarama-cluster v2.1.15+incompatible/go.mod h1:r7ao+4tTNXvWm+VRpRJchr2kQhqxgmAp2iEX5W96gMM= +github.com/buaazp/fasthttprouter v0.1.1 h1:4oAnN0C3xZjylvZJdP35cxfclyn4TYkW6Y+DSvS+h8Q= +github.com/buaazp/fasthttprouter v0.1.1/go.mod h1:h/Ap5oRVLeItGKTVBb+heQPks+HdIUtGmI4H5WCYijM= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +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/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/dgraph-io/badger v1.5.3 h1:5oWIuRvwn93cie+OSt1zSnkaIQ1JFQM8bGlIv6O6Sts= +github.com/dgraph-io/badger v1.5.3/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q= +github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/frankban/quicktest v1.4.1/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ= +github.com/frankban/quicktest v1.5.0 h1:Tb4jWdSpdjKzTUicPnY61PZxKbDoGa7ABbrReT3gQVY= +github.com/frankban/quicktest v1.5.0/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fxamacker/cbor/v2 v2.2.0 h1:6eXqdDDe588rSYAi1HfZKbx6YYQO4mxQ9eC6xYpU/JQ= +github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is= +github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= +github.com/go-acme/lego v2.7.2+incompatible h1:ThhpPBgf6oa9X/vRd0kEmWOsX7+vmYdckmGZSb+FEp0= +github.com/go-acme/lego v2.7.2+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M= +github.com/go-chi/chi v4.0.3-0.20191003102842-906b567ebae8+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-chi/chi v4.1.0+incompatible h1:ETj3cggsVIY2Xao5ExCu6YhEh5MD6JTfcBzS37R260w= +github.com/go-chi/chi v4.1.0+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-ocf/authorization v0.0.0-20191029080559-c0f072d9ae27/go.mod h1:HMIKVo6NWv3GIif0rYDx6UTK+DqRj3Ha7c8qoE7beGI= +github.com/go-ocf/authorization v0.0.0-20191029114330-d7b2a94275a1/go.mod h1:HMIKVo6NWv3GIif0rYDx6UTK+DqRj3Ha7c8qoE7beGI= +github.com/go-ocf/authorization v0.0.0-20191128112604-67b1046cd475/go.mod h1:W7ESga/U+98jLyVy8p3Ibl1vm3rrBSos80zits5r2L8= +github.com/go-ocf/authorization v0.0.0-20200102120905-44194ad01b6b/go.mod h1:sCsmb265KRxPlRHG5X6zgF8z2thckD6ReOKpdCcyido= +github.com/go-ocf/authorization v0.0.0-20200127150932-76f9ee40d6c6/go.mod h1:Ku446n9HbFWHXWhaYyHGIaPFQ3oG0baxhYbl+iS3lM4= +github.com/go-ocf/authorization v0.0.0-20200206133225-884a7e831bb1/go.mod h1:m8D+8OgOfpgA5/e5UvH+oGk4EaUUSUgp4c5s8ARoMCs= +github.com/go-ocf/authorization v0.0.0-20200317083801-219f66279a5a/go.mod h1:r5zz7XS2uqc+avRsPpdv7aZS3ABNbtGdkfrJBY/AifA= +github.com/go-ocf/authorization v0.0.0-20200318102231-7b6f2c3de1f8 h1:CQTRxQh55EsZPSH/MkMfIfsjAnKWER4EgMHWBXCbYF8= +github.com/go-ocf/authorization v0.0.0-20200318102231-7b6f2c3de1f8/go.mod h1:r5zz7XS2uqc+avRsPpdv7aZS3ABNbtGdkfrJBY/AifA= +github.com/go-ocf/certificate-authority v0.0.0-20200306090447-ff501f1cf29a/go.mod h1:0z120kuRIORuPujXrjGafXXpCgX3chBrevVd+WZFF4M= +github.com/go-ocf/coap-gateway v0.0.0-20191029135134-f6d64e927f2d/go.mod h1:nATrHRzqOGUkWP99R/66FoXGIVOIAuC6NiGW/5OoIQ8= +github.com/go-ocf/coap-gateway v0.0.0-20191128113433-bf8268a9145d/go.mod h1:jgZuHpW4wW0kdrOa0jy/AgtA5otvkBMGxfraUruQ5gE= +github.com/go-ocf/coap-gateway v0.0.0-20200206135044-4d6d04bae45e/go.mod h1:v0nWSD3MofMRfuIhjj6FQdp7Etc74qhfnDMDy5iMT/0= +github.com/go-ocf/coap-gateway v0.0.0-20200317094312-c095f634133e/go.mod h1:ANFvRv1xaSory80DCyfID+f68DvwE3CUO2F84ArblgM= +github.com/go-ocf/coap-gateway v0.0.0-20200323122545-250f26f53c6c h1:wgQgGczzzIkDAZ/KLzfsrkOZMmBSduUQlj4EDGo3vOU= +github.com/go-ocf/coap-gateway v0.0.0-20200323122545-250f26f53c6c/go.mod h1:puyYbXqf0IlhVf9lChu4wIJ6/17TErZGohLSVz5Vbg8= +github.com/go-ocf/cqrs v0.0.0-20190925123934-fc3dcec96e06/go.mod h1:kV/m0T0vn3lWBZTx2u/PVmXO/uRKSHmzWfQL2Us1Vv0= +github.com/go-ocf/cqrs v0.0.0-20191107150148-d3546606da81/go.mod h1:+clS9NgOOp6PvrgH5yJ/paxggQnS4bhgIajH7Zq4mTA= +github.com/go-ocf/cqrs v0.0.0-20191122204740-b228b8585d6c/go.mod h1:u5MFHqNscEjEyB+/Ljj/jpQYIZUhs5wgqX+z53fSm/w= +github.com/go-ocf/cqrs v0.0.0-20191129082251-45543ddee4b1/go.mod h1:brdwvxMYPF+rN5ldXBck/pqhc5n5+2iSnr6Rzw3ePj0= +github.com/go-ocf/cqrs v0.0.0-20200102070319-1b87e6ddf7e5/go.mod h1:RxCy5a/mZFQnE9Vph7kWwwuohvQ27zIdE0ESTg7FrCs= +github.com/go-ocf/cqrs v0.0.0-20200110084836-11790ebffed8/go.mod h1:kyFewJTbNyzZRcmoih6LsXbKBWovNSPitOq6s9kVDpc= +github.com/go-ocf/cqrs v0.0.0-20200324131357-db8a7b8c83be h1:9EnlfFOz3rgj2serwiLnklLJ5DScP4cekt5xZQv/Epk= +github.com/go-ocf/cqrs v0.0.0-20200324131357-db8a7b8c83be/go.mod h1:kyFewJTbNyzZRcmoih6LsXbKBWovNSPitOq6s9kVDpc= +github.com/go-ocf/go-coap v0.0.0-20191015202911-fb71e4849cb6/go.mod h1:BfsrAO44kduYzuyPp+993pDv4n3TkFjVVShIIo7k3/U= +github.com/go-ocf/go-coap v0.0.0-20191113204504-6084daa7bf66/go.mod h1:1sSPqpt18lLSPjJIFmb+oHKmTZ5n794pm3Iy/t8LIVY= +github.com/go-ocf/go-coap v0.0.0-20191202082900-28b53dd37265/go.mod h1:54s0jDaba7/rZ2/EoOxWF/5OIE4XuKjtsRTr9greoA0= +github.com/go-ocf/go-coap v0.0.0-20191205091034-1fba24d18397/go.mod h1:54s0jDaba7/rZ2/EoOxWF/5OIE4XuKjtsRTr9greoA0= +github.com/go-ocf/go-coap v0.0.0-20200102085052-de56d321b864/go.mod h1:4M5psGWXKgBr5EEiHBE4goezyqfCSAqk9C1UaKi5t+Y= +github.com/go-ocf/go-coap v0.0.0-20200210123238-6ad5eb48985c/go.mod h1:4M5psGWXKgBr5EEiHBE4goezyqfCSAqk9C1UaKi5t+Y= +github.com/go-ocf/go-coap v0.0.0-20200302101636-d137010769dc/go.mod h1:4M5psGWXKgBr5EEiHBE4goezyqfCSAqk9C1UaKi5t+Y= +github.com/go-ocf/go-coap v0.0.0-20200401083107-6259c4e0dca6/go.mod h1:51jqgNxk+XXTQs/yI5V8SxMbOhRfyNY7IwNFJ4Es6mU= +github.com/go-ocf/go-coap v0.0.0-20200406073902-cf923db524db h1:zYs9RIr1Ghp2POmmwWl1nmgCSrInPRqRhM1fPFqG0UY= +github.com/go-ocf/go-coap v0.0.0-20200406073902-cf923db524db/go.mod h1:QoCnsMBbmLW/XqYJj5reW4Mm7QxIz8QTycLGqWczpZA= +github.com/go-ocf/grpc-gateway v0.0.0-20191029150757-8ed2b7d67a67/go.mod h1:GuVJZHmSz7KGATOk0tqLPkgBJq2H12hZnG74CTJXOtI= +github.com/go-ocf/grpc-gateway v0.0.0-20191128115804-9c2e8f77af08/go.mod h1:XBVeqnt2JEx0Z6SLEdjnjQ53jEpbAvcx+/CAC43eAb4= +github.com/go-ocf/grpc-gateway v0.0.0-20200206140756-f2e98d630ed6/go.mod h1:UmskFCqe00ISEBHTZJbWIeyvvHcIuVsS/5c5FFEHF8s= +github.com/go-ocf/grpc-gateway v0.0.0-20200319123843-760c721614a6/go.mod h1:MrWS8kkt/2owxXBeF1iS3Phv/d+FpzJ2Q9dw8tAlPnM= +github.com/go-ocf/grpc-gateway v0.0.0-20200319135410-983e3720b65d/go.mod h1:G4Axmfc2431jBj/gJU4RtKnHTsnPUrccuLet9HS/3H0= +github.com/go-ocf/grpc-gateway v0.0.0-20200324152726-f5d2d0c21a79 h1:bB+5V1yzRCO0a8V4lMV2hoQVwySFcNrl9kB8O8kXco0= +github.com/go-ocf/grpc-gateway v0.0.0-20200324152726-f5d2d0c21a79/go.mod h1:G2jLNVvag68GNQVuRvDIohEe7R4TDKg49faDJzWPSy0= +github.com/go-ocf/kit v0.0.0-20190627123507-31d82dceaba0/go.mod h1:cP9tDuWo0oq30mYGOSvpVJ6mDSUiOUCuisMnPN/AmvQ= +github.com/go-ocf/kit v0.0.0-20191028131320-a13f1309c964/go.mod h1:cP9tDuWo0oq30mYGOSvpVJ6mDSUiOUCuisMnPN/AmvQ= +github.com/go-ocf/kit v0.0.0-20191121115122-d51c9ca11d08/go.mod h1:PyZjU7p8U5oXiCuTYfufjEsFwePni3eMr5l5qU2nkZ8= +github.com/go-ocf/kit v0.0.0-20191122203058-565314915766/go.mod h1:PyZjU7p8U5oXiCuTYfufjEsFwePni3eMr5l5qU2nkZ8= +github.com/go-ocf/kit v0.0.0-20191122214427-cbc0fdfc712b/go.mod h1:5+xUsJdk3O6xk8I1jqcv0szOgZxOiVLDaBZoE8PQCRI= +github.com/go-ocf/kit v0.0.0-20191127134502-faa1cd5fccdd/go.mod h1:5+xUsJdk3O6xk8I1jqcv0szOgZxOiVLDaBZoE8PQCRI= +github.com/go-ocf/kit v0.0.0-20191209150955-2f87120f125d/go.mod h1:tjohpoVCgliI6UhbndAbC/QibFCGLxrUTq7JZ+jh6qw= +github.com/go-ocf/kit v0.0.0-20191216085136-f42ca1346f3f/go.mod h1:dHlj9aqxutSECzZmKC5sBazcrMTihdxTSGQrOMvMUiU= +github.com/go-ocf/kit v0.0.0-20200102091224-8f9117ed4c1a/go.mod h1:s46+yZh+k7KpY7VuTZiSjK4MaDpkPEHrbwZZCQk9NHE= +github.com/go-ocf/kit v0.0.0-20200110080107-134a1202b156/go.mod h1:s46+yZh+k7KpY7VuTZiSjK4MaDpkPEHrbwZZCQk9NHE= +github.com/go-ocf/kit v0.0.0-20200127130135-cbf12801499b/go.mod h1:s46+yZh+k7KpY7VuTZiSjK4MaDpkPEHrbwZZCQk9NHE= +github.com/go-ocf/kit v0.0.0-20200206131038-d8a52e4df383/go.mod h1:s46+yZh+k7KpY7VuTZiSjK4MaDpkPEHrbwZZCQk9NHE= +github.com/go-ocf/kit v0.0.0-20200211064654-cb54e57dd411/go.mod h1:r06JYqsz49LOt+0F9oqwc/lJnycq7tO/z1ujVaqVjxY= +github.com/go-ocf/kit v0.0.0-20200305082150-0a04c7c108e1/go.mod h1:r06JYqsz49LOt+0F9oqwc/lJnycq7tO/z1ujVaqVjxY= +github.com/go-ocf/kit v0.0.0-20200316114353-9797bba7fe3e/go.mod h1:r06JYqsz49LOt+0F9oqwc/lJnycq7tO/z1ujVaqVjxY= +github.com/go-ocf/kit v0.0.0-20200317110056-dc71b8141429/go.mod h1:r06JYqsz49LOt+0F9oqwc/lJnycq7tO/z1ujVaqVjxY= +github.com/go-ocf/kit v0.0.0-20200320133358-8740baee0a2e/go.mod h1:r06JYqsz49LOt+0F9oqwc/lJnycq7tO/z1ujVaqVjxY= +github.com/go-ocf/kit v0.0.0-20200324144609-83b53db6bbf7/go.mod h1:soXSMc9RbMEFlW2trzwSowX8ppHEQAQ0cEpYnd4q+vs= +github.com/go-ocf/kit v0.0.0-20200326124037-40a7509422e3 h1:7+zQqet/ECthJdehFb2UFrijX4IgiTpOxpK0NkoT9mA= +github.com/go-ocf/kit v0.0.0-20200326124037-40a7509422e3/go.mod h1:l+jh47kCrxjvuvKicH9cczmaCGOYRoD0bF73FlG8saQ= +github.com/go-ocf/resource-aggregate v0.0.0-20190610073816-ff5194c51d2a/go.mod h1:5G1FgzxCnQhETxlFMh2DYtGJrl82AK3MvHXW4MYpO08= +github.com/go-ocf/resource-aggregate v0.0.0-20191001194720-f5aade86d89a/go.mod h1:5G1FgzxCnQhETxlFMh2DYtGJrl82AK3MvHXW4MYpO08= +github.com/go-ocf/resource-aggregate v0.0.0-20191029083107-ed77cacca426/go.mod h1:8KjjOJsevWFUzQ5UhD/tybREQhW05HeQsuGJ449X5qM= +github.com/go-ocf/resource-aggregate v0.0.0-20191029134509-e0e86ccebf83/go.mod h1:8KjjOJsevWFUzQ5UhD/tybREQhW05HeQsuGJ449X5qM= +github.com/go-ocf/resource-aggregate v0.0.0-20191122205530-8ea16eccd08a/go.mod h1:/Wbzv35Ly+8FPAJb1CoFTOSwwvew8JeGfnsbhG2N4cg= +github.com/go-ocf/resource-aggregate v0.0.0-20191125125100-ca23376cc48b/go.mod h1:+C3dqgVn06t4m2lSbOnMXZz1sSLpS9mWWbZeokjUtVQ= +github.com/go-ocf/resource-aggregate v0.0.0-20191216085155-b66d12c15739/go.mod h1:BO3kL7NAyUHWBQdBVA62kMblkn60Fv4Un9dvpgxVp6Q= +github.com/go-ocf/resource-aggregate v0.0.0-20200102132204-a31b8e1496db/go.mod h1:ulUliWNV3f2cxZSBi5XA9f8H4ia9txdYWAVplP8z3Kg= +github.com/go-ocf/resource-aggregate v0.0.0-20200127164704-62ee234b361a/go.mod h1:150nI3mTqM6qjbvYh9BcTA1jkhWlJGmlo16t4HR9H5U= +github.com/go-ocf/resource-aggregate v0.0.0-20200206134526-98a7e94bdd4d/go.mod h1:copBrVc8C9SqyvAZ7rCOVR/cMeJ8tE3qAZUbS1Cm4mo= +github.com/go-ocf/resource-aggregate v0.0.0-20200317084442-61d838e69238/go.mod h1:ncd3pGRu9/uik/poxKhOXswVU5wE26NipVM3sVHWFNs= +github.com/go-ocf/resource-aggregate v0.0.0-20200323093600-690f73062315/go.mod h1:ncd3pGRu9/uik/poxKhOXswVU5wE26NipVM3sVHWFNs= +github.com/go-ocf/resource-aggregate v0.0.0-20200324145835-165be0b7b6ca/go.mod h1:HyXMWeWV3hiqEfTAtBQZhUZLneD2JgxCJzzQrVLCVtY= +github.com/go-ocf/resource-aggregate v0.0.0-20200326125438-8ab650abf05f h1:zMQoHaLlwDcj45cpCnsN6867OG4w9mtPdmTIl416dD4= +github.com/go-ocf/resource-aggregate v0.0.0-20200326125438-8ab650abf05f/go.mod h1:p4rWYNZ/lcTp3KdEOPY7zdvZfbuyXBlxXBKq4F8qo8M= +github.com/go-ocf/resource-directory v0.0.0-20191029084211-59afab3faf54/go.mod h1:AyZwgRfDRRg9vQMxW66NUq5/O5vwd1+gOgPAtVA7xdc= +github.com/go-ocf/resource-directory v0.0.0-20191029134203-3e2a985c36e2/go.mod h1:AyZwgRfDRRg9vQMxW66NUq5/O5vwd1+gOgPAtVA7xdc= +github.com/go-ocf/resource-directory v0.0.0-20191122211635-a96a97864227/go.mod h1:HbLJmgwxFnBg+ehdMiuiQla3MEmXxFXwjN4LXKKZVJY= +github.com/go-ocf/resource-directory v0.0.0-20200127170709-a4bef91c57a2/go.mod h1:uHaGgiMoLjvsNwMyd1F5mMxfaB/oL9K8lIADP/nR2d4= +github.com/go-ocf/resource-directory v0.0.0-20200206134042-b65dd1895c5d/go.mod h1:Fp3pOkNC1EWoa/TaYGOLsNeXEc5JakGIauufi6c+KBM= +github.com/go-ocf/resource-directory v0.0.0-20200317085054-6490221ad726 h1:8pqqiIGExabOE3YLJ5+o3fEe/yKsIUe4luwMx1LACys= +github.com/go-ocf/resource-directory v0.0.0-20200317085054-6490221ad726/go.mod h1:Nm3ZojlZ3REemNMfnHtBalCR6OsdrxpOV2SoJFzya9U= +github.com/go-ocf/sdk v0.0.0-20190926135508-aa0190036e50/go.mod h1:CK33PRv54Esvk2TLNvixJTcwwBwGnGuIAZr5LwchnMo= +github.com/go-ocf/sdk v0.0.0-20191014110911-e42b567505f4/go.mod h1:CK33PRv54Esvk2TLNvixJTcwwBwGnGuIAZr5LwchnMo= +github.com/go-ocf/sdk v0.0.0-20191105105859-e5b3f03a79c0/go.mod h1:KTuP0di3ddTq/fgzjbQxWQnGnLB/ebI5OUA2RK+c8BY= +github.com/go-ocf/sdk v0.0.0-20200102133029-8f03424b4d93/go.mod h1:+UrbVN6b6aNlMPZGdAbXiJbjsJXl8qcGA0173vuQ7Zg= +github.com/go-ocf/sdk v0.0.0-20200302102400-517867bc1075/go.mod h1:5jvB+9hK7x6VULY9qidUS00nJwJIHK6+BnomwFg9s7s= +github.com/go-ocf/sdk v0.0.0-20200319134918-2e08edf8487d/go.mod h1:A/jV/AdVxe3SW6FYe8wB65WMpcmlZPPfYrEpWj13Ayo= +github.com/go-ocf/sdk v0.0.0-20200323152137-050aa844e539/go.mod h1:GKqvP80MeIWx4/ekRA2FJzFJCnTg/8TLTTANZJx2Hhw= +github.com/go-ocf/sdk v0.0.0-20200406082918-51e333156732 h1:60WsAOgaRjKGCgzBPEkIPZZ3KCTxhMscM5NutTLB2hw= +github.com/go-ocf/sdk v0.0.0-20200406082918-51e333156732/go.mod h1:mU0HDyK3eyZXb9JtmgonizwtoLXy6fCpxVURHrfzBxQ= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= +github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +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.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.2-0.20190904063534-ff6b7dc882cf h1:gFVkHXmVAhEbxZVDln5V9GKrLaluNoFHDbrZwAWZgws= +github.com/golang/snappy v0.0.2-0.20190904063534-ff6b7dc882cf/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= +github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= +github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8= +github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= +github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= +github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.7.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/compress v1.9.5 h1:U+CaK85mrNNb4k8BNOfgJtJ/gr6kswUCFj6miSzVC6M= +github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lestrrat-go/jwx v0.9.0/go.mod h1:iEoxlYfZjvoGpuWwxUz+eR5e6KTJGsaRcy/YNA/UnBk= +github.com/lestrrat-go/jwx v0.9.1 h1:yuDzdMCxjiY2oItdWPX3wM/blQ3kJrBmp3kcec7NNEE= +github.com/lestrrat-go/jwx v0.9.1/go.mod h1:iEoxlYfZjvoGpuWwxUz+eR5e6KTJGsaRcy/YNA/UnBk= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8= +github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/manifoldco/promptui v0.3.1 h1:BxqNa7q1hVHXIXy3iupJMkXYS3aHhbubJWv2Jmg6x64= +github.com/manifoldco/promptui v0.3.1/go.mod h1:zoCNXiJnyM03LlBgTsWv8mq28s7aTC71UgKasqRJHww= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v2.0.2+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= +github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/miekg/dns v1.1.22/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.25 h1:dFwPR6SfLtrSwgDcIq2bcU/gVutB4sNApq2HBdqcakg= +github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/nats-io/gnatsd v1.4.1/go.mod h1:nqco77VO78hLCJpIcVfygDP2rPGfsEHkGTUk94uh5DQ= +github.com/nats-io/go-nats v1.7.2/go.mod h1:+t7RHT5ApZebkrQdnn6AhQJmhJJiKAvJUio1PiiCtj0= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2 h1:i2Ly0B+1+rzNZHHWtD4ZwKi+OU5l+uQo1iDHZ2PmiIc= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nats.go v1.9.2 h1:oDeERm3NcZVrPpdR/JpGdWHMv3oJ8yY30YwxKq+DU2s= +github.com/nats-io/nats.go v1.9.2/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.4 h1:aEsHIssIk6ETN5m2/MD8Y4B2X7FfXrBAUdkyRvbVYzA= +github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= +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/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34= +github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/panjf2000/ants v1.0.0/go.mod h1:AaACblRPzq35m1g3enqYcxspbbiOJJYaxU2wMpm1cXY= +github.com/panjf2000/ants v1.2.0/go.mod h1:AaACblRPzq35m1g3enqYcxspbbiOJJYaxU2wMpm1cXY= +github.com/panjf2000/ants v1.3.0 h1:8pQ+8leaLc9lys2viEEr8md0U4RN6uOSUCE9bOYjQ9M= +github.com/panjf2000/ants v1.3.0/go.mod h1:AaACblRPzq35m1g3enqYcxspbbiOJJYaxU2wMpm1cXY= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pebbe/zmq4 v1.2.0/go.mod h1:7N4y5R18zBiu3l0vajMUWQgZyjv464prE8RCyBcmnZM= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/pierrec/lz4 v0.0.0-20190327172049-315a67e90e41/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.3.0+incompatible h1:CZzRn4Ut9GbUkHlQ7jqBXeZQV41ZSKWFc302ZU6lUTk= +github.com/pierrec/lz4 v2.3.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pion/dtls v1.5.1/go.mod h1:CjlPLfQdsTg3G4AEXjJp8FY5bRweBlxHrgoFrN+fQsk= +github.com/pion/dtls v1.5.2/go.mod h1:v4ULmyyV65geAZQBBckCjgMhmngTqz7HQVsQVYnfkGo= +github.com/pion/dtls v1.5.4 h1:q8pXFMF7T+EAVO4auQU/ds+5yh5yOK6NiTN/4NQ0dB0= +github.com/pion/dtls v1.5.4/go.mod h1:eVHevf4AM8R9+Pxa29q4aiI2iIbfMWOW1WgEcSCGpHU= +github.com/pion/dtls/v2 v2.0.0-rc.3/go.mod h1:x0XH+cN5z+l/+/4nYL8r4sB8g6+0d1Zp2Pfkcoz8BKY= +github.com/pion/dtls/v2 v2.0.0-rc.5/go.mod h1:k7HAs0qpJSz+Pelkbc5ZDNtenQpUvXgjg/yq4ZC6CdU= +github.com/pion/dtls/v2 v2.0.0-rc.7/go.mod h1:U199DvHpRBN0muE9+tVN4TMy1jvEhZIZ63lk4xkvVSk= +github.com/pion/dtls/v2 v2.0.0-rc.9 h1:wPb0JKmYoleAM2o8vQSPaUM+geJq7l0AdeUlPsg19ec= +github.com/pion/dtls/v2 v2.0.0-rc.9/go.mod h1:6eFkFvpo0T+odQ+39HFEtOO7LX5cUlFqXdSo4ucZtGg= +github.com/pion/logging v0.2.1/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +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.6.0/go.mod h1:iWZ07doqOosSLMhZ+FXUTq+TamDoXSllxpbGcfkCmbE= +github.com/pion/transport v0.8.9/go.mod h1:lpeSM6KJFejVtZf8k0fgeN7zE73APQpTF83WvA1FVP8= +github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8= +github.com/pion/transport v0.10.0 h1:9M12BSneJm6ggGhJyWpDveFOstJsTiQjkLf4M44rm80= +github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20190706150252-9beb055b7962/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563 h1:dY6ETXrvDG7Sa4vE8ZQG4yqWg6UnOcbqTAahkV813vQ= +github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189 h1:CmSpbxmewNQbzqztaY0bke1qzHhyNyC29wYgh17Gxfo= +github.com/samfoo/ansi v0.0.0-20160124022901-b6bd2ded7189/go.mod h1:UUwuHEJ9zkkPDxspIHOa59PUeSkGFljESGzbxntLmIg= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smallstep/assert v0.0.0-20180720014142-de77670473b5/go.mod h1:TC9A4+RjIOS+HyTH7wG17/gSqVv95uDw2J64dQZx7RE= +github.com/smallstep/assert v0.0.0-20200103212524-b99dc1097b15 h1:kSImCuenAkXtCaBeQ1UhmzzJGRhSm8sVH7I3sHE2Qdg= +github.com/smallstep/assert v0.0.0-20200103212524-b99dc1097b15/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc= +github.com/smallstep/certificates v0.13.4-0.20191007194430-e2858e17b094 h1:v4HSfEQt7PT2pdMTrX9Qwj27D5AlJeLOZkD1k57J2Gs= +github.com/smallstep/certificates v0.13.4-0.20191007194430-e2858e17b094/go.mod h1:eZ90IBg01k2RKND3sAKXVI0atx5/lzCNX0sA0DqwcUg= +github.com/smallstep/cli v0.13.4-0.20190930184324-d68486471cbd h1:fokJyC0qVzpY1PDGNnX0ppne4vAZcZozCHSZuQwWRgs= +github.com/smallstep/cli v0.13.4-0.20190930184324-d68486471cbd/go.mod h1:zGPm8vWCqzvDqkdC1laFJNdIOjNSB8V4qDp68Ny538o= +github.com/smallstep/nosql v0.1.1-0.20191009043502-4b26d8029e61/go.mod h1:MFhYHIE/0V7OOHjYzjnWHqySJ40PVbwhjy24UBkJI2g= +github.com/smallstep/nosql v0.2.0 h1:IscXK9m9hRyl5GoYgn+Iml//5Bpad3LyIj6R0dZosKM= +github.com/smallstep/nosql v0.2.0/go.mod h1:qyxCqeyGwkuM6bfJSY3sg+aiXEiD0GbQOPzIF8/ZD8Q= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +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= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/urfave/cli v1.20.1-0.20181029213200-b67dcf995b6a h1:qbTm+Zobir+JOKt4xjwK7rwNJXWVfHtV0zGf4TVJ1tQ= +github.com/urfave/cli v1.20.1-0.20181029213200-b67dcf995b6a/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.4.1-0.20190711201041-a0248ed3a1ce/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= +github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= +github.com/valyala/fasthttp v1.7.1/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= +github.com/valyala/fasthttp v1.9.0 h1:hNpmUdy/+ZXYpGy0OBfm7K0UQTzb73W0T0U4iJIVrMw= +github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w= +github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= +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/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +github.com/xdg/stringprep v1.0.1-0.20180714160509-73f8eece6fdc h1:vIp1tjhVogU0yBy7w96P027ewvNPeH6gzuNcoc+NReU= +github.com/xdg/stringprep v1.0.1-0.20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.mongodb.org/mongo-driver v1.0.4/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.1.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.2.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.2.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= +go.mongodb.org/mongo-driver v1.3.1 h1:op56IfTQiaY2679w922KVWa3qcHdml2K/Io8ayAOUEQ= +go.mongodb.org/mongo-driver v1.3.1/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.11.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191001170739-f9e2070545dc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +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= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200320220750-118fecf932d8 h1:1+zQlQqEEhUeStBTi653GZAnAuivZq/2hz+Iz+OP7rg= +golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191122200657-5d9234df094c/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +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= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190424175732-18eb32c0e2f0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191027211539-f8518d3b3627 h1:/FZUR3d/QsXe4AcJyJFCc40TOj3y6Hs23Y3YJlvVkWo= +golang.org/x/sys v0.0.0-20191027211539-f8518d3b3627/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191127064951-724660f1afeb h1:K4JMHRJSgd1q/yXZNrKKyneQJcLm1rn7JsokEs/xE9I= +golang.org/x/tools v0.0.0-20191127064951-724660f1afeb/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20191007204434-a023cd5227bd/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03 h1:4HYDjxeNXAOTv3o1N2tjo8UUSlhQgAD52FVkwxnWgM8= +google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.1 h1:C1QC6KzgSiLyBabDi87BbjaGreoRgGUF5nOyvfrAZ1k= +google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw= +gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo= +gopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN5hSM= +gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q= +gopkg.in/jcmturner/goidentity.v3 v3.0.0 h1:1duIyWiTaYvVx3YX2CYtpJbUFd7/UuPYCfgXtQ3VTbI= +gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4= +gopkg.in/jcmturner/gokrb5.v7 v7.2.3/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= +gopkg.in/jcmturner/gokrb5.v7 v7.3.0 h1:0709Jtq/6QXEuWRfAm260XqlpcwL1vxtO1tUE2qK8Z4= +gopkg.in/jcmturner/gokrb5.v7 v7.3.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= +gopkg.in/jcmturner/rpc.v1 v1.1.0 h1:QHIUxTX1ISuAv9dD2wJ9HWQVuWDX/Zc0PfeC2tjc4rU= +gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8= +gopkg.in/square/go-jose.v2 v2.4.0 h1:0kXPskUMGAXXWJlP05ktEMOV0vmzFQUWw6d+aZJQU8A= +gopkg.in/square/go-jose.v2 v2.4.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/grpc-gateway/.dockerignore b/grpc-gateway/.dockerignore new file mode 100644 index 000000000..5ed6efd6f --- /dev/null +++ b/grpc-gateway/.dockerignore @@ -0,0 +1,6 @@ +.git +.github +.gitignore +.travis.yml +Makefile +vendor diff --git a/grpc-gateway/.gitignore b/grpc-gateway/.gitignore new file mode 100644 index 000000000..8d1cee488 --- /dev/null +++ b/grpc-gateway/.gitignore @@ -0,0 +1,24 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +debug +step-ca/ +mongo/ +authsvc.db + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out +coverage.txt +.codecov + +#goland +.idea/ + +#vendor +vendor/ diff --git a/grpc-gateway/Dockerfile b/grpc-gateway/Dockerfile new file mode 100644 index 000000000..5d36e241b --- /dev/null +++ b/grpc-gateway/Dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.13.5-alpine3.10 AS build +RUN apk add --no-cache curl git build-base +WORKDIR $GOPATH/src/github.com/go-ocf/cloud/grpc-gateway +COPY . . +RUN go mod download +RUN go build -o /go/bin/service ./cmd/service + +FROM alpine:3.11 as service +RUN apk add --no-cache ca-certificates +COPY --from=build /go/bin/service /usr/local/bin/service +ENTRYPOINT ["/usr/local/bin/service"] \ No newline at end of file diff --git a/grpc-gateway/Makefile b/grpc-gateway/Makefile new file mode 100644 index 000000000..0bd754cfc --- /dev/null +++ b/grpc-gateway/Makefile @@ -0,0 +1,34 @@ +SHELL = /bin/bash +SERVICE_NAME = $(notdir $(CURDIR)) +LATEST_TAG = vnext +VERSION_TAG = vnext-$(shell git rev-parse --short=7 --verify HEAD) + +default: build + +define build-docker-image + docker build \ + --network=host \ + --tag ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) \ + --tag ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) \ + --target $(1) \ + --file Dockerfile \ + . +endef + +build-servicecontainer: + $(call build-docker-image,service) + +build: build-servicecontainer + +push: build-servicecontainer + docker push ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) + docker push ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) + +proto/generate: + protoc -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf --gogofaster_out=${GOPATH}/src pb/devices.proto + protoc -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf --go_out=plugins=grpc:${GOPATH}/src pb/service.proto + protoc -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf --go_out=${GOPATH}/src pb/errdetails/errorDetails.proto + +.PHONY: build-servicecontainer build push proto/generate + + diff --git a/grpc-gateway/README.md b/grpc-gateway/README.md new file mode 100644 index 000000000..4db38cf7c --- /dev/null +++ b/grpc-gateway/README.md @@ -0,0 +1,5 @@ +[![Go Report](https://goreportcard.com/badge/github.com/go-ocf/cloud/grpc-gateway)](https://goreportcard.com/report/github.com/go-ocf/cloud/grpc-gateway) +[![Gitter](https://badges.gitter.im/ocfcloud/Lobby.svg)](https://gitter.im/ocfcloud/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +# grpc-gateway + diff --git a/grpc-gateway/cmd/service/main.go b/grpc-gateway/cmd/service/main.go new file mode 100644 index 000000000..df068318a --- /dev/null +++ b/grpc-gateway/cmd/service/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "github.com/go-ocf/cloud/grpc-gateway/refImpl" + "github.com/go-ocf/kit/log" + "github.com/kelseyhightower/envconfig" +) + +func main() { + var config refImpl.Config + if err := envconfig.Process("", &config); err != nil { + log.Fatalf("cannot parse configuration: %v", err) + } + if server, err := refImpl.Init(config); err != nil { + log.Fatalf("cannot init server: %v", err) + } else { + if err = server.Serve(); err != nil { + log.Fatalf("unexpected ends: %v", err) + } + } +} diff --git a/grpc-gateway/pb/codes/codes.go b/grpc-gateway/pb/codes/codes.go new file mode 100644 index 000000000..ec5657d24 --- /dev/null +++ b/grpc-gateway/pb/codes/codes.go @@ -0,0 +1,10 @@ +package pb + +import codes "google.golang.org/grpc/codes" + +const ( + // Accepted device accepts request and action will be proceed in future. + Accepted codes.Code = iota + 4096 + // InvalidCode cannot determines result from device code. + InvalidCode +) diff --git a/grpc-gateway/pb/devices.pb.go b/grpc-gateway/pb/devices.pb.go new file mode 100644 index 000000000..63b8f53dc --- /dev/null +++ b/grpc-gateway/pb/devices.pb.go @@ -0,0 +1,8197 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: pb/devices.proto + +package pb + +import ( + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type GetDevicesRequest_Status int32 + +const ( + GetDevicesRequest_ONLINE GetDevicesRequest_Status = 0 + GetDevicesRequest_OFFLINE GetDevicesRequest_Status = 1 +) + +var GetDevicesRequest_Status_name = map[int32]string{ + 0: "ONLINE", + 1: "OFFLINE", +} + +var GetDevicesRequest_Status_value = map[string]int32{ + "ONLINE": 0, + "OFFLINE": 1, +} + +func (x GetDevicesRequest_Status) String() string { + return proto.EnumName(GetDevicesRequest_Status_name, int32(x)) +} + +func (GetDevicesRequest_Status) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{0, 0} +} + +type SubscribeForEvents_DevicesEventFilter_Event int32 + +const ( + SubscribeForEvents_DevicesEventFilter_REGISTERED SubscribeForEvents_DevicesEventFilter_Event = 0 + SubscribeForEvents_DevicesEventFilter_UNREGISTERED SubscribeForEvents_DevicesEventFilter_Event = 1 + SubscribeForEvents_DevicesEventFilter_ONLINE SubscribeForEvents_DevicesEventFilter_Event = 2 + SubscribeForEvents_DevicesEventFilter_OFFLINE SubscribeForEvents_DevicesEventFilter_Event = 3 +) + +var SubscribeForEvents_DevicesEventFilter_Event_name = map[int32]string{ + 0: "REGISTERED", + 1: "UNREGISTERED", + 2: "ONLINE", + 3: "OFFLINE", +} + +var SubscribeForEvents_DevicesEventFilter_Event_value = map[string]int32{ + "REGISTERED": 0, + "UNREGISTERED": 1, + "ONLINE": 2, + "OFFLINE": 3, +} + +func (x SubscribeForEvents_DevicesEventFilter_Event) String() string { + return proto.EnumName(SubscribeForEvents_DevicesEventFilter_Event_name, int32(x)) +} + +func (SubscribeForEvents_DevicesEventFilter_Event) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{9, 0, 0} +} + +type SubscribeForEvents_DeviceEventFilter_Event int32 + +const ( + SubscribeForEvents_DeviceEventFilter_RESOURCE_PUBLISHED SubscribeForEvents_DeviceEventFilter_Event = 0 + SubscribeForEvents_DeviceEventFilter_RESOURCE_UNPUBLISHED SubscribeForEvents_DeviceEventFilter_Event = 1 +) + +var SubscribeForEvents_DeviceEventFilter_Event_name = map[int32]string{ + 0: "RESOURCE_PUBLISHED", + 1: "RESOURCE_UNPUBLISHED", +} + +var SubscribeForEvents_DeviceEventFilter_Event_value = map[string]int32{ + "RESOURCE_PUBLISHED": 0, + "RESOURCE_UNPUBLISHED": 1, +} + +func (x SubscribeForEvents_DeviceEventFilter_Event) String() string { + return proto.EnumName(SubscribeForEvents_DeviceEventFilter_Event_name, int32(x)) +} + +func (SubscribeForEvents_DeviceEventFilter_Event) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{9, 1, 0} +} + +type SubscribeForEvents_ResourceEventFilter_Event int32 + +const ( + SubscribeForEvents_ResourceEventFilter_CONTENT_CHANGED SubscribeForEvents_ResourceEventFilter_Event = 0 +) + +var SubscribeForEvents_ResourceEventFilter_Event_name = map[int32]string{ + 0: "CONTENT_CHANGED", +} + +var SubscribeForEvents_ResourceEventFilter_Event_value = map[string]int32{ + "CONTENT_CHANGED": 0, +} + +func (x SubscribeForEvents_ResourceEventFilter_Event) String() string { + return proto.EnumName(SubscribeForEvents_ResourceEventFilter_Event_name, int32(x)) +} + +func (SubscribeForEvents_ResourceEventFilter_Event) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{9, 2, 0} +} + +type Event_OperationProcessed_ErrorStatus_Code int32 + +const ( + Event_OperationProcessed_ErrorStatus_OK Event_OperationProcessed_ErrorStatus_Code = 0 + Event_OperationProcessed_ErrorStatus_ERROR Event_OperationProcessed_ErrorStatus_Code = 1 + Event_OperationProcessed_ErrorStatus_NOT_FOUND Event_OperationProcessed_ErrorStatus_Code = 2 +) + +var Event_OperationProcessed_ErrorStatus_Code_name = map[int32]string{ + 0: "OK", + 1: "ERROR", + 2: "NOT_FOUND", +} + +var Event_OperationProcessed_ErrorStatus_Code_value = map[string]int32{ + "OK": 0, + "ERROR": 1, + "NOT_FOUND": 2, +} + +func (x Event_OperationProcessed_ErrorStatus_Code) String() string { + return proto.EnumName(Event_OperationProcessed_ErrorStatus_Code_name, int32(x)) +} + +func (Event_OperationProcessed_ErrorStatus_Code) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{10, 7, 0, 0} +} + +type GetDevicesRequest struct { + TypeFilter []string `protobuf:"bytes,1,rep,name=type_filter,json=typeFilter,proto3" json:"type_filter,omitempty"` + StatusFilter []GetDevicesRequest_Status `protobuf:"varint,2,rep,packed,name=status_filter,json=statusFilter,proto3,enum=ocf.cloud.grpcgateway.pb.GetDevicesRequest_Status" json:"status_filter,omitempty"` + DeviceIdsFilter []string `protobuf:"bytes,3,rep,name=device_ids_filter,json=deviceIdsFilter,proto3" json:"device_ids_filter,omitempty"` +} + +func (m *GetDevicesRequest) Reset() { *m = GetDevicesRequest{} } +func (m *GetDevicesRequest) String() string { return proto.CompactTextString(m) } +func (*GetDevicesRequest) ProtoMessage() {} +func (*GetDevicesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{0} +} +func (m *GetDevicesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetDevicesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetDevicesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetDevicesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetDevicesRequest.Merge(m, src) +} +func (m *GetDevicesRequest) XXX_Size() int { + return m.Size() +} +func (m *GetDevicesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetDevicesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetDevicesRequest proto.InternalMessageInfo + +func (m *GetDevicesRequest) GetTypeFilter() []string { + if m != nil { + return m.TypeFilter + } + return nil +} + +func (m *GetDevicesRequest) GetStatusFilter() []GetDevicesRequest_Status { + if m != nil { + return m.StatusFilter + } + return nil +} + +func (m *GetDevicesRequest) GetDeviceIdsFilter() []string { + if m != nil { + return m.DeviceIdsFilter + } + return nil +} + +type GetResourceLinksRequest struct { + TypeFilter []string `protobuf:"bytes,1,rep,name=type_filter,json=typeFilter,proto3" json:"type_filter,omitempty"` + DeviceIdsFilter []string `protobuf:"bytes,2,rep,name=device_ids_filter,json=deviceIdsFilter,proto3" json:"device_ids_filter,omitempty"` +} + +func (m *GetResourceLinksRequest) Reset() { *m = GetResourceLinksRequest{} } +func (m *GetResourceLinksRequest) String() string { return proto.CompactTextString(m) } +func (*GetResourceLinksRequest) ProtoMessage() {} +func (*GetResourceLinksRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{1} +} +func (m *GetResourceLinksRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetResourceLinksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetResourceLinksRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetResourceLinksRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetResourceLinksRequest.Merge(m, src) +} +func (m *GetResourceLinksRequest) XXX_Size() int { + return m.Size() +} +func (m *GetResourceLinksRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetResourceLinksRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetResourceLinksRequest proto.InternalMessageInfo + +func (m *GetResourceLinksRequest) GetTypeFilter() []string { + if m != nil { + return m.TypeFilter + } + return nil +} + +func (m *GetResourceLinksRequest) GetDeviceIdsFilter() []string { + if m != nil { + return m.DeviceIdsFilter + } + return nil +} + +type ResourceId struct { + DeviceId string `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` + ResourceLinkHref string `protobuf:"bytes,2,opt,name=resource_link_href,json=resourceLinkHref,proto3" json:"resource_link_href,omitempty"` +} + +func (m *ResourceId) Reset() { *m = ResourceId{} } +func (m *ResourceId) String() string { return proto.CompactTextString(m) } +func (*ResourceId) ProtoMessage() {} +func (*ResourceId) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{2} +} +func (m *ResourceId) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResourceId) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResourceId.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResourceId) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResourceId.Merge(m, src) +} +func (m *ResourceId) XXX_Size() int { + return m.Size() +} +func (m *ResourceId) XXX_DiscardUnknown() { + xxx_messageInfo_ResourceId.DiscardUnknown(m) +} + +var xxx_messageInfo_ResourceId proto.InternalMessageInfo + +func (m *ResourceId) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +func (m *ResourceId) GetResourceLinkHref() string { + if m != nil { + return m.ResourceLinkHref + } + return "" +} + +// RetrieveResourceFromDeviceRequest retrieve value from device +type RetrieveResourceFromDeviceRequest struct { + ResourceId *ResourceId `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + ResourceInterface string `protobuf:"bytes,2,opt,name=resource_interface,json=resourceInterface,proto3" json:"resource_interface,omitempty"` +} + +func (m *RetrieveResourceFromDeviceRequest) Reset() { *m = RetrieveResourceFromDeviceRequest{} } +func (m *RetrieveResourceFromDeviceRequest) String() string { return proto.CompactTextString(m) } +func (*RetrieveResourceFromDeviceRequest) ProtoMessage() {} +func (*RetrieveResourceFromDeviceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{3} +} +func (m *RetrieveResourceFromDeviceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RetrieveResourceFromDeviceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RetrieveResourceFromDeviceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RetrieveResourceFromDeviceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RetrieveResourceFromDeviceRequest.Merge(m, src) +} +func (m *RetrieveResourceFromDeviceRequest) XXX_Size() int { + return m.Size() +} +func (m *RetrieveResourceFromDeviceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RetrieveResourceFromDeviceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RetrieveResourceFromDeviceRequest proto.InternalMessageInfo + +func (m *RetrieveResourceFromDeviceRequest) GetResourceId() *ResourceId { + if m != nil { + return m.ResourceId + } + return nil +} + +func (m *RetrieveResourceFromDeviceRequest) GetResourceInterface() string { + if m != nil { + return m.ResourceInterface + } + return "" +} + +type RetrieveResourceFromDeviceResponse struct { + Content *Content `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` +} + +func (m *RetrieveResourceFromDeviceResponse) Reset() { *m = RetrieveResourceFromDeviceResponse{} } +func (m *RetrieveResourceFromDeviceResponse) String() string { return proto.CompactTextString(m) } +func (*RetrieveResourceFromDeviceResponse) ProtoMessage() {} +func (*RetrieveResourceFromDeviceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{4} +} +func (m *RetrieveResourceFromDeviceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RetrieveResourceFromDeviceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RetrieveResourceFromDeviceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RetrieveResourceFromDeviceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RetrieveResourceFromDeviceResponse.Merge(m, src) +} +func (m *RetrieveResourceFromDeviceResponse) XXX_Size() int { + return m.Size() +} +func (m *RetrieveResourceFromDeviceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RetrieveResourceFromDeviceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_RetrieveResourceFromDeviceResponse proto.InternalMessageInfo + +func (m *RetrieveResourceFromDeviceResponse) GetContent() *Content { + if m != nil { + return m.Content + } + return nil +} + +// RetrieveResourcesValuesRequest get values from resource-shadow +type RetrieveResourcesValuesRequest struct { + ResourceIdsFilter []*ResourceId `protobuf:"bytes,1,rep,name=resource_ids_filter,json=resourceIdsFilter,proto3" json:"resource_ids_filter,omitempty"` + DeviceIdsFilter []string `protobuf:"bytes,2,rep,name=device_ids_filter,json=deviceIdsFilter,proto3" json:"device_ids_filter,omitempty"` + TypeFilter []string `protobuf:"bytes,3,rep,name=type_filter,json=typeFilter,proto3" json:"type_filter,omitempty"` +} + +func (m *RetrieveResourcesValuesRequest) Reset() { *m = RetrieveResourcesValuesRequest{} } +func (m *RetrieveResourcesValuesRequest) String() string { return proto.CompactTextString(m) } +func (*RetrieveResourcesValuesRequest) ProtoMessage() {} +func (*RetrieveResourcesValuesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{5} +} +func (m *RetrieveResourcesValuesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RetrieveResourcesValuesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RetrieveResourcesValuesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RetrieveResourcesValuesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RetrieveResourcesValuesRequest.Merge(m, src) +} +func (m *RetrieveResourcesValuesRequest) XXX_Size() int { + return m.Size() +} +func (m *RetrieveResourcesValuesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RetrieveResourcesValuesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RetrieveResourcesValuesRequest proto.InternalMessageInfo + +func (m *RetrieveResourcesValuesRequest) GetResourceIdsFilter() []*ResourceId { + if m != nil { + return m.ResourceIdsFilter + } + return nil +} + +func (m *RetrieveResourcesValuesRequest) GetDeviceIdsFilter() []string { + if m != nil { + return m.DeviceIdsFilter + } + return nil +} + +func (m *RetrieveResourcesValuesRequest) GetTypeFilter() []string { + if m != nil { + return m.TypeFilter + } + return nil +} + +type ResourceValue struct { + ResourceId *ResourceId `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + Types []string `protobuf:"bytes,2,rep,name=types,proto3" json:"types,omitempty"` + Content *Content `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` +} + +func (m *ResourceValue) Reset() { *m = ResourceValue{} } +func (m *ResourceValue) String() string { return proto.CompactTextString(m) } +func (*ResourceValue) ProtoMessage() {} +func (*ResourceValue) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{6} +} +func (m *ResourceValue) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResourceValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResourceValue.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResourceValue) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResourceValue.Merge(m, src) +} +func (m *ResourceValue) XXX_Size() int { + return m.Size() +} +func (m *ResourceValue) XXX_DiscardUnknown() { + xxx_messageInfo_ResourceValue.DiscardUnknown(m) +} + +var xxx_messageInfo_ResourceValue proto.InternalMessageInfo + +func (m *ResourceValue) GetResourceId() *ResourceId { + if m != nil { + return m.ResourceId + } + return nil +} + +func (m *ResourceValue) GetTypes() []string { + if m != nil { + return m.Types + } + return nil +} + +func (m *ResourceValue) GetContent() *Content { + if m != nil { + return m.Content + } + return nil +} + +type UpdateResourceValuesRequest struct { + ResourceId *ResourceId `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + Content *Content `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` + ResourceInterface string `protobuf:"bytes,3,opt,name=resource_interface,json=resourceInterface,proto3" json:"resource_interface,omitempty"` +} + +func (m *UpdateResourceValuesRequest) Reset() { *m = UpdateResourceValuesRequest{} } +func (m *UpdateResourceValuesRequest) String() string { return proto.CompactTextString(m) } +func (*UpdateResourceValuesRequest) ProtoMessage() {} +func (*UpdateResourceValuesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{7} +} +func (m *UpdateResourceValuesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateResourceValuesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateResourceValuesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateResourceValuesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateResourceValuesRequest.Merge(m, src) +} +func (m *UpdateResourceValuesRequest) XXX_Size() int { + return m.Size() +} +func (m *UpdateResourceValuesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateResourceValuesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdateResourceValuesRequest proto.InternalMessageInfo + +func (m *UpdateResourceValuesRequest) GetResourceId() *ResourceId { + if m != nil { + return m.ResourceId + } + return nil +} + +func (m *UpdateResourceValuesRequest) GetContent() *Content { + if m != nil { + return m.Content + } + return nil +} + +func (m *UpdateResourceValuesRequest) GetResourceInterface() string { + if m != nil { + return m.ResourceInterface + } + return "" +} + +type UpdateResourceValuesResponse struct { + Content *Content `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` +} + +func (m *UpdateResourceValuesResponse) Reset() { *m = UpdateResourceValuesResponse{} } +func (m *UpdateResourceValuesResponse) String() string { return proto.CompactTextString(m) } +func (*UpdateResourceValuesResponse) ProtoMessage() {} +func (*UpdateResourceValuesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{8} +} +func (m *UpdateResourceValuesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateResourceValuesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateResourceValuesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateResourceValuesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateResourceValuesResponse.Merge(m, src) +} +func (m *UpdateResourceValuesResponse) XXX_Size() int { + return m.Size() +} +func (m *UpdateResourceValuesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateResourceValuesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdateResourceValuesResponse proto.InternalMessageInfo + +func (m *UpdateResourceValuesResponse) GetContent() *Content { + if m != nil { + return m.Content + } + return nil +} + +type SubscribeForEvents struct { + // Types that are valid to be assigned to FilterBy: + // *SubscribeForEvents_DevicesEvent + // *SubscribeForEvents_DeviceEvent + // *SubscribeForEvents_ResourceEvent + // *SubscribeForEvents_CancelSubscription_ + FilterBy isSubscribeForEvents_FilterBy `protobuf_oneof:"filter_by"` + Token string `protobuf:"bytes,101,opt,name=token,proto3" json:"token,omitempty"` +} + +func (m *SubscribeForEvents) Reset() { *m = SubscribeForEvents{} } +func (m *SubscribeForEvents) String() string { return proto.CompactTextString(m) } +func (*SubscribeForEvents) ProtoMessage() {} +func (*SubscribeForEvents) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{9} +} +func (m *SubscribeForEvents) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SubscribeForEvents) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SubscribeForEvents.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SubscribeForEvents) XXX_Merge(src proto.Message) { + xxx_messageInfo_SubscribeForEvents.Merge(m, src) +} +func (m *SubscribeForEvents) XXX_Size() int { + return m.Size() +} +func (m *SubscribeForEvents) XXX_DiscardUnknown() { + xxx_messageInfo_SubscribeForEvents.DiscardUnknown(m) +} + +var xxx_messageInfo_SubscribeForEvents proto.InternalMessageInfo + +type isSubscribeForEvents_FilterBy interface { + isSubscribeForEvents_FilterBy() + MarshalTo([]byte) (int, error) + Size() int +} + +type SubscribeForEvents_DevicesEvent struct { + DevicesEvent *SubscribeForEvents_DevicesEventFilter `protobuf:"bytes,1,opt,name=devices_event,json=devicesEvent,proto3,oneof" json:"devices_event,omitempty"` +} +type SubscribeForEvents_DeviceEvent struct { + DeviceEvent *SubscribeForEvents_DeviceEventFilter `protobuf:"bytes,2,opt,name=device_event,json=deviceEvent,proto3,oneof" json:"device_event,omitempty"` +} +type SubscribeForEvents_ResourceEvent struct { + ResourceEvent *SubscribeForEvents_ResourceEventFilter `protobuf:"bytes,3,opt,name=resource_event,json=resourceEvent,proto3,oneof" json:"resource_event,omitempty"` +} +type SubscribeForEvents_CancelSubscription_ struct { + CancelSubscription *SubscribeForEvents_CancelSubscription `protobuf:"bytes,5,opt,name=cancel_subscription,json=cancelSubscription,proto3,oneof" json:"cancel_subscription,omitempty"` +} + +func (*SubscribeForEvents_DevicesEvent) isSubscribeForEvents_FilterBy() {} +func (*SubscribeForEvents_DeviceEvent) isSubscribeForEvents_FilterBy() {} +func (*SubscribeForEvents_ResourceEvent) isSubscribeForEvents_FilterBy() {} +func (*SubscribeForEvents_CancelSubscription_) isSubscribeForEvents_FilterBy() {} + +func (m *SubscribeForEvents) GetFilterBy() isSubscribeForEvents_FilterBy { + if m != nil { + return m.FilterBy + } + return nil +} + +func (m *SubscribeForEvents) GetDevicesEvent() *SubscribeForEvents_DevicesEventFilter { + if x, ok := m.GetFilterBy().(*SubscribeForEvents_DevicesEvent); ok { + return x.DevicesEvent + } + return nil +} + +func (m *SubscribeForEvents) GetDeviceEvent() *SubscribeForEvents_DeviceEventFilter { + if x, ok := m.GetFilterBy().(*SubscribeForEvents_DeviceEvent); ok { + return x.DeviceEvent + } + return nil +} + +func (m *SubscribeForEvents) GetResourceEvent() *SubscribeForEvents_ResourceEventFilter { + if x, ok := m.GetFilterBy().(*SubscribeForEvents_ResourceEvent); ok { + return x.ResourceEvent + } + return nil +} + +func (m *SubscribeForEvents) GetCancelSubscription() *SubscribeForEvents_CancelSubscription { + if x, ok := m.GetFilterBy().(*SubscribeForEvents_CancelSubscription_); ok { + return x.CancelSubscription + } + return nil +} + +func (m *SubscribeForEvents) GetToken() string { + if m != nil { + return m.Token + } + return "" +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*SubscribeForEvents) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*SubscribeForEvents_DevicesEvent)(nil), + (*SubscribeForEvents_DeviceEvent)(nil), + (*SubscribeForEvents_ResourceEvent)(nil), + (*SubscribeForEvents_CancelSubscription_)(nil), + } +} + +type SubscribeForEvents_DevicesEventFilter struct { + FilterEvents []SubscribeForEvents_DevicesEventFilter_Event `protobuf:"varint,1,rep,packed,name=filter_events,json=filterEvents,proto3,enum=ocf.cloud.grpcgateway.pb.SubscribeForEvents_DevicesEventFilter_Event" json:"filter_events,omitempty"` +} + +func (m *SubscribeForEvents_DevicesEventFilter) Reset() { *m = SubscribeForEvents_DevicesEventFilter{} } +func (m *SubscribeForEvents_DevicesEventFilter) String() string { return proto.CompactTextString(m) } +func (*SubscribeForEvents_DevicesEventFilter) ProtoMessage() {} +func (*SubscribeForEvents_DevicesEventFilter) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{9, 0} +} +func (m *SubscribeForEvents_DevicesEventFilter) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SubscribeForEvents_DevicesEventFilter) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SubscribeForEvents_DevicesEventFilter.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SubscribeForEvents_DevicesEventFilter) XXX_Merge(src proto.Message) { + xxx_messageInfo_SubscribeForEvents_DevicesEventFilter.Merge(m, src) +} +func (m *SubscribeForEvents_DevicesEventFilter) XXX_Size() int { + return m.Size() +} +func (m *SubscribeForEvents_DevicesEventFilter) XXX_DiscardUnknown() { + xxx_messageInfo_SubscribeForEvents_DevicesEventFilter.DiscardUnknown(m) +} + +var xxx_messageInfo_SubscribeForEvents_DevicesEventFilter proto.InternalMessageInfo + +func (m *SubscribeForEvents_DevicesEventFilter) GetFilterEvents() []SubscribeForEvents_DevicesEventFilter_Event { + if m != nil { + return m.FilterEvents + } + return nil +} + +type SubscribeForEvents_DeviceEventFilter struct { + DeviceId string `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` + FilterEvents []SubscribeForEvents_DeviceEventFilter_Event `protobuf:"varint,2,rep,packed,name=filter_events,json=filterEvents,proto3,enum=ocf.cloud.grpcgateway.pb.SubscribeForEvents_DeviceEventFilter_Event" json:"filter_events,omitempty"` +} + +func (m *SubscribeForEvents_DeviceEventFilter) Reset() { *m = SubscribeForEvents_DeviceEventFilter{} } +func (m *SubscribeForEvents_DeviceEventFilter) String() string { return proto.CompactTextString(m) } +func (*SubscribeForEvents_DeviceEventFilter) ProtoMessage() {} +func (*SubscribeForEvents_DeviceEventFilter) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{9, 1} +} +func (m *SubscribeForEvents_DeviceEventFilter) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SubscribeForEvents_DeviceEventFilter) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SubscribeForEvents_DeviceEventFilter.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SubscribeForEvents_DeviceEventFilter) XXX_Merge(src proto.Message) { + xxx_messageInfo_SubscribeForEvents_DeviceEventFilter.Merge(m, src) +} +func (m *SubscribeForEvents_DeviceEventFilter) XXX_Size() int { + return m.Size() +} +func (m *SubscribeForEvents_DeviceEventFilter) XXX_DiscardUnknown() { + xxx_messageInfo_SubscribeForEvents_DeviceEventFilter.DiscardUnknown(m) +} + +var xxx_messageInfo_SubscribeForEvents_DeviceEventFilter proto.InternalMessageInfo + +func (m *SubscribeForEvents_DeviceEventFilter) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +func (m *SubscribeForEvents_DeviceEventFilter) GetFilterEvents() []SubscribeForEvents_DeviceEventFilter_Event { + if m != nil { + return m.FilterEvents + } + return nil +} + +type SubscribeForEvents_ResourceEventFilter struct { + ResourceId *ResourceId `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + FilterEvents []SubscribeForEvents_ResourceEventFilter_Event `protobuf:"varint,2,rep,packed,name=filter_events,json=filterEvents,proto3,enum=ocf.cloud.grpcgateway.pb.SubscribeForEvents_ResourceEventFilter_Event" json:"filter_events,omitempty"` +} + +func (m *SubscribeForEvents_ResourceEventFilter) Reset() { + *m = SubscribeForEvents_ResourceEventFilter{} +} +func (m *SubscribeForEvents_ResourceEventFilter) String() string { return proto.CompactTextString(m) } +func (*SubscribeForEvents_ResourceEventFilter) ProtoMessage() {} +func (*SubscribeForEvents_ResourceEventFilter) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{9, 2} +} +func (m *SubscribeForEvents_ResourceEventFilter) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SubscribeForEvents_ResourceEventFilter) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SubscribeForEvents_ResourceEventFilter.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SubscribeForEvents_ResourceEventFilter) XXX_Merge(src proto.Message) { + xxx_messageInfo_SubscribeForEvents_ResourceEventFilter.Merge(m, src) +} +func (m *SubscribeForEvents_ResourceEventFilter) XXX_Size() int { + return m.Size() +} +func (m *SubscribeForEvents_ResourceEventFilter) XXX_DiscardUnknown() { + xxx_messageInfo_SubscribeForEvents_ResourceEventFilter.DiscardUnknown(m) +} + +var xxx_messageInfo_SubscribeForEvents_ResourceEventFilter proto.InternalMessageInfo + +func (m *SubscribeForEvents_ResourceEventFilter) GetResourceId() *ResourceId { + if m != nil { + return m.ResourceId + } + return nil +} + +func (m *SubscribeForEvents_ResourceEventFilter) GetFilterEvents() []SubscribeForEvents_ResourceEventFilter_Event { + if m != nil { + return m.FilterEvents + } + return nil +} + +type SubscribeForEvents_CancelSubscription struct { + SubscriptionId string `protobuf:"bytes,1,opt,name=subscription_id,json=subscriptionId,proto3" json:"subscription_id,omitempty"` +} + +func (m *SubscribeForEvents_CancelSubscription) Reset() { *m = SubscribeForEvents_CancelSubscription{} } +func (m *SubscribeForEvents_CancelSubscription) String() string { return proto.CompactTextString(m) } +func (*SubscribeForEvents_CancelSubscription) ProtoMessage() {} +func (*SubscribeForEvents_CancelSubscription) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{9, 3} +} +func (m *SubscribeForEvents_CancelSubscription) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SubscribeForEvents_CancelSubscription) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SubscribeForEvents_CancelSubscription.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SubscribeForEvents_CancelSubscription) XXX_Merge(src proto.Message) { + xxx_messageInfo_SubscribeForEvents_CancelSubscription.Merge(m, src) +} +func (m *SubscribeForEvents_CancelSubscription) XXX_Size() int { + return m.Size() +} +func (m *SubscribeForEvents_CancelSubscription) XXX_DiscardUnknown() { + xxx_messageInfo_SubscribeForEvents_CancelSubscription.DiscardUnknown(m) +} + +var xxx_messageInfo_SubscribeForEvents_CancelSubscription proto.InternalMessageInfo + +func (m *SubscribeForEvents_CancelSubscription) GetSubscriptionId() string { + if m != nil { + return m.SubscriptionId + } + return "" +} + +type Event struct { + SubscriptionId string `protobuf:"bytes,1,opt,name=subscription_id,json=subscriptionId,proto3" json:"subscription_id,omitempty"` + // Types that are valid to be assigned to Type: + // *Event_DeviceRegistered_ + // *Event_DeviceUnregistered_ + // *Event_DeviceOnline_ + // *Event_DeviceOffline_ + // *Event_ResourcePublished_ + // *Event_ResourceUnpublished_ + // *Event_ResourceContentChanged + // *Event_OperationProcessed_ + // *Event_SubscriptionCanceled_ + Type isEvent_Type `protobuf_oneof:"type"` +} + +func (m *Event) Reset() { *m = Event{} } +func (m *Event) String() string { return proto.CompactTextString(m) } +func (*Event) ProtoMessage() {} +func (*Event) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{10} +} +func (m *Event) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Event) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Event.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Event) XXX_Merge(src proto.Message) { + xxx_messageInfo_Event.Merge(m, src) +} +func (m *Event) XXX_Size() int { + return m.Size() +} +func (m *Event) XXX_DiscardUnknown() { + xxx_messageInfo_Event.DiscardUnknown(m) +} + +var xxx_messageInfo_Event proto.InternalMessageInfo + +type isEvent_Type interface { + isEvent_Type() + MarshalTo([]byte) (int, error) + Size() int +} + +type Event_DeviceRegistered_ struct { + DeviceRegistered *Event_DeviceRegistered `protobuf:"bytes,3,opt,name=device_registered,json=deviceRegistered,proto3,oneof" json:"device_registered,omitempty"` +} +type Event_DeviceUnregistered_ struct { + DeviceUnregistered *Event_DeviceUnregistered `protobuf:"bytes,4,opt,name=device_unregistered,json=deviceUnregistered,proto3,oneof" json:"device_unregistered,omitempty"` +} +type Event_DeviceOnline_ struct { + DeviceOnline *Event_DeviceOnline `protobuf:"bytes,5,opt,name=device_online,json=deviceOnline,proto3,oneof" json:"device_online,omitempty"` +} +type Event_DeviceOffline_ struct { + DeviceOffline *Event_DeviceOffline `protobuf:"bytes,6,opt,name=device_offline,json=deviceOffline,proto3,oneof" json:"device_offline,omitempty"` +} +type Event_ResourcePublished_ struct { + ResourcePublished *Event_ResourcePublished `protobuf:"bytes,7,opt,name=resource_published,json=resourcePublished,proto3,oneof" json:"resource_published,omitempty"` +} +type Event_ResourceUnpublished_ struct { + ResourceUnpublished *Event_ResourceUnpublished `protobuf:"bytes,8,opt,name=resource_unpublished,json=resourceUnpublished,proto3,oneof" json:"resource_unpublished,omitempty"` +} +type Event_ResourceContentChanged struct { + ResourceContentChanged *Event_ResourceChanged `protobuf:"bytes,9,opt,name=resource_content_changed,json=resourceContentChanged,proto3,oneof" json:"resource_content_changed,omitempty"` +} +type Event_OperationProcessed_ struct { + OperationProcessed *Event_OperationProcessed `protobuf:"bytes,10,opt,name=operation_processed,json=operationProcessed,proto3,oneof" json:"operation_processed,omitempty"` +} +type Event_SubscriptionCanceled_ struct { + SubscriptionCanceled *Event_SubscriptionCanceled `protobuf:"bytes,11,opt,name=subscription_canceled,json=subscriptionCanceled,proto3,oneof" json:"subscription_canceled,omitempty"` +} + +func (*Event_DeviceRegistered_) isEvent_Type() {} +func (*Event_DeviceUnregistered_) isEvent_Type() {} +func (*Event_DeviceOnline_) isEvent_Type() {} +func (*Event_DeviceOffline_) isEvent_Type() {} +func (*Event_ResourcePublished_) isEvent_Type() {} +func (*Event_ResourceUnpublished_) isEvent_Type() {} +func (*Event_ResourceContentChanged) isEvent_Type() {} +func (*Event_OperationProcessed_) isEvent_Type() {} +func (*Event_SubscriptionCanceled_) isEvent_Type() {} + +func (m *Event) GetType() isEvent_Type { + if m != nil { + return m.Type + } + return nil +} + +func (m *Event) GetSubscriptionId() string { + if m != nil { + return m.SubscriptionId + } + return "" +} + +func (m *Event) GetDeviceRegistered() *Event_DeviceRegistered { + if x, ok := m.GetType().(*Event_DeviceRegistered_); ok { + return x.DeviceRegistered + } + return nil +} + +func (m *Event) GetDeviceUnregistered() *Event_DeviceUnregistered { + if x, ok := m.GetType().(*Event_DeviceUnregistered_); ok { + return x.DeviceUnregistered + } + return nil +} + +func (m *Event) GetDeviceOnline() *Event_DeviceOnline { + if x, ok := m.GetType().(*Event_DeviceOnline_); ok { + return x.DeviceOnline + } + return nil +} + +func (m *Event) GetDeviceOffline() *Event_DeviceOffline { + if x, ok := m.GetType().(*Event_DeviceOffline_); ok { + return x.DeviceOffline + } + return nil +} + +func (m *Event) GetResourcePublished() *Event_ResourcePublished { + if x, ok := m.GetType().(*Event_ResourcePublished_); ok { + return x.ResourcePublished + } + return nil +} + +func (m *Event) GetResourceUnpublished() *Event_ResourceUnpublished { + if x, ok := m.GetType().(*Event_ResourceUnpublished_); ok { + return x.ResourceUnpublished + } + return nil +} + +func (m *Event) GetResourceContentChanged() *Event_ResourceChanged { + if x, ok := m.GetType().(*Event_ResourceContentChanged); ok { + return x.ResourceContentChanged + } + return nil +} + +func (m *Event) GetOperationProcessed() *Event_OperationProcessed { + if x, ok := m.GetType().(*Event_OperationProcessed_); ok { + return x.OperationProcessed + } + return nil +} + +func (m *Event) GetSubscriptionCanceled() *Event_SubscriptionCanceled { + if x, ok := m.GetType().(*Event_SubscriptionCanceled_); ok { + return x.SubscriptionCanceled + } + return nil +} + +// XXX_OneofWrappers is for the internal use of the proto package. +func (*Event) XXX_OneofWrappers() []interface{} { + return []interface{}{ + (*Event_DeviceRegistered_)(nil), + (*Event_DeviceUnregistered_)(nil), + (*Event_DeviceOnline_)(nil), + (*Event_DeviceOffline_)(nil), + (*Event_ResourcePublished_)(nil), + (*Event_ResourceUnpublished_)(nil), + (*Event_ResourceContentChanged)(nil), + (*Event_OperationProcessed_)(nil), + (*Event_SubscriptionCanceled_)(nil), + } +} + +type Event_DeviceRegistered struct { + DeviceId string `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` +} + +func (m *Event_DeviceRegistered) Reset() { *m = Event_DeviceRegistered{} } +func (m *Event_DeviceRegistered) String() string { return proto.CompactTextString(m) } +func (*Event_DeviceRegistered) ProtoMessage() {} +func (*Event_DeviceRegistered) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{10, 0} +} +func (m *Event_DeviceRegistered) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Event_DeviceRegistered) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Event_DeviceRegistered.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Event_DeviceRegistered) XXX_Merge(src proto.Message) { + xxx_messageInfo_Event_DeviceRegistered.Merge(m, src) +} +func (m *Event_DeviceRegistered) XXX_Size() int { + return m.Size() +} +func (m *Event_DeviceRegistered) XXX_DiscardUnknown() { + xxx_messageInfo_Event_DeviceRegistered.DiscardUnknown(m) +} + +var xxx_messageInfo_Event_DeviceRegistered proto.InternalMessageInfo + +func (m *Event_DeviceRegistered) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +type Event_DeviceUnregistered struct { + DeviceId string `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` +} + +func (m *Event_DeviceUnregistered) Reset() { *m = Event_DeviceUnregistered{} } +func (m *Event_DeviceUnregistered) String() string { return proto.CompactTextString(m) } +func (*Event_DeviceUnregistered) ProtoMessage() {} +func (*Event_DeviceUnregistered) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{10, 1} +} +func (m *Event_DeviceUnregistered) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Event_DeviceUnregistered) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Event_DeviceUnregistered.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Event_DeviceUnregistered) XXX_Merge(src proto.Message) { + xxx_messageInfo_Event_DeviceUnregistered.Merge(m, src) +} +func (m *Event_DeviceUnregistered) XXX_Size() int { + return m.Size() +} +func (m *Event_DeviceUnregistered) XXX_DiscardUnknown() { + xxx_messageInfo_Event_DeviceUnregistered.DiscardUnknown(m) +} + +var xxx_messageInfo_Event_DeviceUnregistered proto.InternalMessageInfo + +func (m *Event_DeviceUnregistered) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +type Event_DeviceOnline struct { + DeviceId string `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` +} + +func (m *Event_DeviceOnline) Reset() { *m = Event_DeviceOnline{} } +func (m *Event_DeviceOnline) String() string { return proto.CompactTextString(m) } +func (*Event_DeviceOnline) ProtoMessage() {} +func (*Event_DeviceOnline) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{10, 2} +} +func (m *Event_DeviceOnline) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Event_DeviceOnline) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Event_DeviceOnline.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Event_DeviceOnline) XXX_Merge(src proto.Message) { + xxx_messageInfo_Event_DeviceOnline.Merge(m, src) +} +func (m *Event_DeviceOnline) XXX_Size() int { + return m.Size() +} +func (m *Event_DeviceOnline) XXX_DiscardUnknown() { + xxx_messageInfo_Event_DeviceOnline.DiscardUnknown(m) +} + +var xxx_messageInfo_Event_DeviceOnline proto.InternalMessageInfo + +func (m *Event_DeviceOnline) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +type Event_DeviceOffline struct { + DeviceId string `protobuf:"bytes,1,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` +} + +func (m *Event_DeviceOffline) Reset() { *m = Event_DeviceOffline{} } +func (m *Event_DeviceOffline) String() string { return proto.CompactTextString(m) } +func (*Event_DeviceOffline) ProtoMessage() {} +func (*Event_DeviceOffline) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{10, 3} +} +func (m *Event_DeviceOffline) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Event_DeviceOffline) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Event_DeviceOffline.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Event_DeviceOffline) XXX_Merge(src proto.Message) { + xxx_messageInfo_Event_DeviceOffline.Merge(m, src) +} +func (m *Event_DeviceOffline) XXX_Size() int { + return m.Size() +} +func (m *Event_DeviceOffline) XXX_DiscardUnknown() { + xxx_messageInfo_Event_DeviceOffline.DiscardUnknown(m) +} + +var xxx_messageInfo_Event_DeviceOffline proto.InternalMessageInfo + +func (m *Event_DeviceOffline) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +type Event_ResourcePublished struct { + Link *ResourceLink `protobuf:"bytes,1,opt,name=link,proto3" json:"link,omitempty"` +} + +func (m *Event_ResourcePublished) Reset() { *m = Event_ResourcePublished{} } +func (m *Event_ResourcePublished) String() string { return proto.CompactTextString(m) } +func (*Event_ResourcePublished) ProtoMessage() {} +func (*Event_ResourcePublished) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{10, 4} +} +func (m *Event_ResourcePublished) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Event_ResourcePublished) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Event_ResourcePublished.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Event_ResourcePublished) XXX_Merge(src proto.Message) { + xxx_messageInfo_Event_ResourcePublished.Merge(m, src) +} +func (m *Event_ResourcePublished) XXX_Size() int { + return m.Size() +} +func (m *Event_ResourcePublished) XXX_DiscardUnknown() { + xxx_messageInfo_Event_ResourcePublished.DiscardUnknown(m) +} + +var xxx_messageInfo_Event_ResourcePublished proto.InternalMessageInfo + +func (m *Event_ResourcePublished) GetLink() *ResourceLink { + if m != nil { + return m.Link + } + return nil +} + +type Event_ResourceUnpublished struct { + Link *ResourceLink `protobuf:"bytes,2,opt,name=link,proto3" json:"link,omitempty"` +} + +func (m *Event_ResourceUnpublished) Reset() { *m = Event_ResourceUnpublished{} } +func (m *Event_ResourceUnpublished) String() string { return proto.CompactTextString(m) } +func (*Event_ResourceUnpublished) ProtoMessage() {} +func (*Event_ResourceUnpublished) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{10, 5} +} +func (m *Event_ResourceUnpublished) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Event_ResourceUnpublished) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Event_ResourceUnpublished.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Event_ResourceUnpublished) XXX_Merge(src proto.Message) { + xxx_messageInfo_Event_ResourceUnpublished.Merge(m, src) +} +func (m *Event_ResourceUnpublished) XXX_Size() int { + return m.Size() +} +func (m *Event_ResourceUnpublished) XXX_DiscardUnknown() { + xxx_messageInfo_Event_ResourceUnpublished.DiscardUnknown(m) +} + +var xxx_messageInfo_Event_ResourceUnpublished proto.InternalMessageInfo + +func (m *Event_ResourceUnpublished) GetLink() *ResourceLink { + if m != nil { + return m.Link + } + return nil +} + +type Event_ResourceChanged struct { + ResourceId *ResourceId `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + Content *Content `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` +} + +func (m *Event_ResourceChanged) Reset() { *m = Event_ResourceChanged{} } +func (m *Event_ResourceChanged) String() string { return proto.CompactTextString(m) } +func (*Event_ResourceChanged) ProtoMessage() {} +func (*Event_ResourceChanged) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{10, 6} +} +func (m *Event_ResourceChanged) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Event_ResourceChanged) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Event_ResourceChanged.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Event_ResourceChanged) XXX_Merge(src proto.Message) { + xxx_messageInfo_Event_ResourceChanged.Merge(m, src) +} +func (m *Event_ResourceChanged) XXX_Size() int { + return m.Size() +} +func (m *Event_ResourceChanged) XXX_DiscardUnknown() { + xxx_messageInfo_Event_ResourceChanged.DiscardUnknown(m) +} + +var xxx_messageInfo_Event_ResourceChanged proto.InternalMessageInfo + +func (m *Event_ResourceChanged) GetResourceId() *ResourceId { + if m != nil { + return m.ResourceId + } + return nil +} + +func (m *Event_ResourceChanged) GetContent() *Content { + if m != nil { + return m.Content + } + return nil +} + +type Event_OperationProcessed struct { + Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` + ErrorStatus *Event_OperationProcessed_ErrorStatus `protobuf:"bytes,2,opt,name=error_status,json=errorStatus,proto3" json:"error_status,omitempty"` +} + +func (m *Event_OperationProcessed) Reset() { *m = Event_OperationProcessed{} } +func (m *Event_OperationProcessed) String() string { return proto.CompactTextString(m) } +func (*Event_OperationProcessed) ProtoMessage() {} +func (*Event_OperationProcessed) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{10, 7} +} +func (m *Event_OperationProcessed) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Event_OperationProcessed) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Event_OperationProcessed.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Event_OperationProcessed) XXX_Merge(src proto.Message) { + xxx_messageInfo_Event_OperationProcessed.Merge(m, src) +} +func (m *Event_OperationProcessed) XXX_Size() int { + return m.Size() +} +func (m *Event_OperationProcessed) XXX_DiscardUnknown() { + xxx_messageInfo_Event_OperationProcessed.DiscardUnknown(m) +} + +var xxx_messageInfo_Event_OperationProcessed proto.InternalMessageInfo + +func (m *Event_OperationProcessed) GetToken() string { + if m != nil { + return m.Token + } + return "" +} + +func (m *Event_OperationProcessed) GetErrorStatus() *Event_OperationProcessed_ErrorStatus { + if m != nil { + return m.ErrorStatus + } + return nil +} + +type Event_OperationProcessed_ErrorStatus struct { + Code Event_OperationProcessed_ErrorStatus_Code `protobuf:"varint,1,opt,name=code,proto3,enum=ocf.cloud.grpcgateway.pb.Event_OperationProcessed_ErrorStatus_Code" json:"code,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` +} + +func (m *Event_OperationProcessed_ErrorStatus) Reset() { *m = Event_OperationProcessed_ErrorStatus{} } +func (m *Event_OperationProcessed_ErrorStatus) String() string { return proto.CompactTextString(m) } +func (*Event_OperationProcessed_ErrorStatus) ProtoMessage() {} +func (*Event_OperationProcessed_ErrorStatus) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{10, 7, 0} +} +func (m *Event_OperationProcessed_ErrorStatus) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Event_OperationProcessed_ErrorStatus) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Event_OperationProcessed_ErrorStatus.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Event_OperationProcessed_ErrorStatus) XXX_Merge(src proto.Message) { + xxx_messageInfo_Event_OperationProcessed_ErrorStatus.Merge(m, src) +} +func (m *Event_OperationProcessed_ErrorStatus) XXX_Size() int { + return m.Size() +} +func (m *Event_OperationProcessed_ErrorStatus) XXX_DiscardUnknown() { + xxx_messageInfo_Event_OperationProcessed_ErrorStatus.DiscardUnknown(m) +} + +var xxx_messageInfo_Event_OperationProcessed_ErrorStatus proto.InternalMessageInfo + +func (m *Event_OperationProcessed_ErrorStatus) GetCode() Event_OperationProcessed_ErrorStatus_Code { + if m != nil { + return m.Code + } + return Event_OperationProcessed_ErrorStatus_OK +} + +func (m *Event_OperationProcessed_ErrorStatus) GetMessage() string { + if m != nil { + return m.Message + } + return "" +} + +type Event_SubscriptionCanceled struct { + Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"` +} + +func (m *Event_SubscriptionCanceled) Reset() { *m = Event_SubscriptionCanceled{} } +func (m *Event_SubscriptionCanceled) String() string { return proto.CompactTextString(m) } +func (*Event_SubscriptionCanceled) ProtoMessage() {} +func (*Event_SubscriptionCanceled) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{10, 8} +} +func (m *Event_SubscriptionCanceled) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Event_SubscriptionCanceled) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Event_SubscriptionCanceled.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Event_SubscriptionCanceled) XXX_Merge(src proto.Message) { + xxx_messageInfo_Event_SubscriptionCanceled.Merge(m, src) +} +func (m *Event_SubscriptionCanceled) XXX_Size() int { + return m.Size() +} +func (m *Event_SubscriptionCanceled) XXX_DiscardUnknown() { + xxx_messageInfo_Event_SubscriptionCanceled.DiscardUnknown(m) +} + +var xxx_messageInfo_Event_SubscriptionCanceled proto.InternalMessageInfo + +func (m *Event_SubscriptionCanceled) GetReason() string { + if m != nil { + return m.Reason + } + return "" +} + +type LocalizedString struct { + Language string `protobuf:"bytes,1,opt,name=language,proto3" json:"language,omitempty"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (m *LocalizedString) Reset() { *m = LocalizedString{} } +func (m *LocalizedString) String() string { return proto.CompactTextString(m) } +func (*LocalizedString) ProtoMessage() {} +func (*LocalizedString) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{11} +} +func (m *LocalizedString) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LocalizedString) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LocalizedString.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *LocalizedString) XXX_Merge(src proto.Message) { + xxx_messageInfo_LocalizedString.Merge(m, src) +} +func (m *LocalizedString) XXX_Size() int { + return m.Size() +} +func (m *LocalizedString) XXX_DiscardUnknown() { + xxx_messageInfo_LocalizedString.DiscardUnknown(m) +} + +var xxx_messageInfo_LocalizedString proto.InternalMessageInfo + +func (m *LocalizedString) GetLanguage() string { + if m != nil { + return m.Language + } + return "" +} + +func (m *LocalizedString) GetValue() string { + if m != nil { + return m.Value + } + return "" +} + +type Device struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Types []string `protobuf:"bytes,2,rep,name=types,proto3" json:"types,omitempty"` + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + IsOnline bool `protobuf:"varint,4,opt,name=is_online,json=isOnline,proto3" json:"is_online,omitempty"` + ManufacturerName []*LocalizedString `protobuf:"bytes,5,rep,name=manufacturer_name,json=manufacturerName,proto3" json:"manufacturer_name,omitempty"` + ModelNumber string `protobuf:"bytes,6,opt,name=model_number,json=modelNumber,proto3" json:"model_number,omitempty"` +} + +func (m *Device) Reset() { *m = Device{} } +func (m *Device) String() string { return proto.CompactTextString(m) } +func (*Device) ProtoMessage() {} +func (*Device) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{12} +} +func (m *Device) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Device) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Device.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Device) XXX_Merge(src proto.Message) { + xxx_messageInfo_Device.Merge(m, src) +} +func (m *Device) XXX_Size() int { + return m.Size() +} +func (m *Device) XXX_DiscardUnknown() { + xxx_messageInfo_Device.DiscardUnknown(m) +} + +var xxx_messageInfo_Device proto.InternalMessageInfo + +func (m *Device) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *Device) GetTypes() []string { + if m != nil { + return m.Types + } + return nil +} + +func (m *Device) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Device) GetIsOnline() bool { + if m != nil { + return m.IsOnline + } + return false +} + +func (m *Device) GetManufacturerName() []*LocalizedString { + if m != nil { + return m.ManufacturerName + } + return nil +} + +func (m *Device) GetModelNumber() string { + if m != nil { + return m.ModelNumber + } + return "" +} + +type ResourceLink struct { + Href string `protobuf:"bytes,1,opt,name=href,proto3" json:"href,omitempty"` + Types []string `protobuf:"bytes,2,rep,name=types,proto3" json:"types,omitempty"` + Interfaces []string `protobuf:"bytes,3,rep,name=interfaces,proto3" json:"interfaces,omitempty"` + DeviceId string `protobuf:"bytes,4,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` +} + +func (m *ResourceLink) Reset() { *m = ResourceLink{} } +func (m *ResourceLink) String() string { return proto.CompactTextString(m) } +func (*ResourceLink) ProtoMessage() {} +func (*ResourceLink) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{13} +} +func (m *ResourceLink) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResourceLink) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResourceLink.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResourceLink) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResourceLink.Merge(m, src) +} +func (m *ResourceLink) XXX_Size() int { + return m.Size() +} +func (m *ResourceLink) XXX_DiscardUnknown() { + xxx_messageInfo_ResourceLink.DiscardUnknown(m) +} + +var xxx_messageInfo_ResourceLink proto.InternalMessageInfo + +func (m *ResourceLink) GetHref() string { + if m != nil { + return m.Href + } + return "" +} + +func (m *ResourceLink) GetTypes() []string { + if m != nil { + return m.Types + } + return nil +} + +func (m *ResourceLink) GetInterfaces() []string { + if m != nil { + return m.Interfaces + } + return nil +} + +func (m *ResourceLink) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +type Content struct { + ContentType string `protobuf:"bytes,1,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` +} + +func (m *Content) Reset() { *m = Content{} } +func (m *Content) String() string { return proto.CompactTextString(m) } +func (*Content) ProtoMessage() {} +func (*Content) Descriptor() ([]byte, []int) { + return fileDescriptor_f2e89dd986657715, []int{14} +} +func (m *Content) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Content) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Content.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Content) XXX_Merge(src proto.Message) { + xxx_messageInfo_Content.Merge(m, src) +} +func (m *Content) XXX_Size() int { + return m.Size() +} +func (m *Content) XXX_DiscardUnknown() { + xxx_messageInfo_Content.DiscardUnknown(m) +} + +var xxx_messageInfo_Content proto.InternalMessageInfo + +func (m *Content) GetContentType() string { + if m != nil { + return m.ContentType + } + return "" +} + +func (m *Content) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func init() { + proto.RegisterEnum("ocf.cloud.grpcgateway.pb.GetDevicesRequest_Status", GetDevicesRequest_Status_name, GetDevicesRequest_Status_value) + proto.RegisterEnum("ocf.cloud.grpcgateway.pb.SubscribeForEvents_DevicesEventFilter_Event", SubscribeForEvents_DevicesEventFilter_Event_name, SubscribeForEvents_DevicesEventFilter_Event_value) + proto.RegisterEnum("ocf.cloud.grpcgateway.pb.SubscribeForEvents_DeviceEventFilter_Event", SubscribeForEvents_DeviceEventFilter_Event_name, SubscribeForEvents_DeviceEventFilter_Event_value) + proto.RegisterEnum("ocf.cloud.grpcgateway.pb.SubscribeForEvents_ResourceEventFilter_Event", SubscribeForEvents_ResourceEventFilter_Event_name, SubscribeForEvents_ResourceEventFilter_Event_value) + proto.RegisterEnum("ocf.cloud.grpcgateway.pb.Event_OperationProcessed_ErrorStatus_Code", Event_OperationProcessed_ErrorStatus_Code_name, Event_OperationProcessed_ErrorStatus_Code_value) + proto.RegisterType((*GetDevicesRequest)(nil), "ocf.cloud.grpcgateway.pb.GetDevicesRequest") + proto.RegisterType((*GetResourceLinksRequest)(nil), "ocf.cloud.grpcgateway.pb.GetResourceLinksRequest") + proto.RegisterType((*ResourceId)(nil), "ocf.cloud.grpcgateway.pb.ResourceId") + proto.RegisterType((*RetrieveResourceFromDeviceRequest)(nil), "ocf.cloud.grpcgateway.pb.RetrieveResourceFromDeviceRequest") + proto.RegisterType((*RetrieveResourceFromDeviceResponse)(nil), "ocf.cloud.grpcgateway.pb.RetrieveResourceFromDeviceResponse") + proto.RegisterType((*RetrieveResourcesValuesRequest)(nil), "ocf.cloud.grpcgateway.pb.RetrieveResourcesValuesRequest") + proto.RegisterType((*ResourceValue)(nil), "ocf.cloud.grpcgateway.pb.ResourceValue") + proto.RegisterType((*UpdateResourceValuesRequest)(nil), "ocf.cloud.grpcgateway.pb.UpdateResourceValuesRequest") + proto.RegisterType((*UpdateResourceValuesResponse)(nil), "ocf.cloud.grpcgateway.pb.UpdateResourceValuesResponse") + proto.RegisterType((*SubscribeForEvents)(nil), "ocf.cloud.grpcgateway.pb.SubscribeForEvents") + proto.RegisterType((*SubscribeForEvents_DevicesEventFilter)(nil), "ocf.cloud.grpcgateway.pb.SubscribeForEvents.DevicesEventFilter") + proto.RegisterType((*SubscribeForEvents_DeviceEventFilter)(nil), "ocf.cloud.grpcgateway.pb.SubscribeForEvents.DeviceEventFilter") + proto.RegisterType((*SubscribeForEvents_ResourceEventFilter)(nil), "ocf.cloud.grpcgateway.pb.SubscribeForEvents.ResourceEventFilter") + proto.RegisterType((*SubscribeForEvents_CancelSubscription)(nil), "ocf.cloud.grpcgateway.pb.SubscribeForEvents.CancelSubscription") + proto.RegisterType((*Event)(nil), "ocf.cloud.grpcgateway.pb.Event") + proto.RegisterType((*Event_DeviceRegistered)(nil), "ocf.cloud.grpcgateway.pb.Event.DeviceRegistered") + proto.RegisterType((*Event_DeviceUnregistered)(nil), "ocf.cloud.grpcgateway.pb.Event.DeviceUnregistered") + proto.RegisterType((*Event_DeviceOnline)(nil), "ocf.cloud.grpcgateway.pb.Event.DeviceOnline") + proto.RegisterType((*Event_DeviceOffline)(nil), "ocf.cloud.grpcgateway.pb.Event.DeviceOffline") + proto.RegisterType((*Event_ResourcePublished)(nil), "ocf.cloud.grpcgateway.pb.Event.ResourcePublished") + proto.RegisterType((*Event_ResourceUnpublished)(nil), "ocf.cloud.grpcgateway.pb.Event.ResourceUnpublished") + proto.RegisterType((*Event_ResourceChanged)(nil), "ocf.cloud.grpcgateway.pb.Event.ResourceChanged") + proto.RegisterType((*Event_OperationProcessed)(nil), "ocf.cloud.grpcgateway.pb.Event.OperationProcessed") + proto.RegisterType((*Event_OperationProcessed_ErrorStatus)(nil), "ocf.cloud.grpcgateway.pb.Event.OperationProcessed.ErrorStatus") + proto.RegisterType((*Event_SubscriptionCanceled)(nil), "ocf.cloud.grpcgateway.pb.Event.SubscriptionCanceled") + proto.RegisterType((*LocalizedString)(nil), "ocf.cloud.grpcgateway.pb.LocalizedString") + proto.RegisterType((*Device)(nil), "ocf.cloud.grpcgateway.pb.Device") + proto.RegisterType((*ResourceLink)(nil), "ocf.cloud.grpcgateway.pb.ResourceLink") + proto.RegisterType((*Content)(nil), "ocf.cloud.grpcgateway.pb.Content") +} + +func init() { proto.RegisterFile("pb/devices.proto", fileDescriptor_f2e89dd986657715) } + +var fileDescriptor_f2e89dd986657715 = []byte{ + // 1512 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4f, 0x6f, 0xe3, 0x44, + 0x14, 0x8f, 0xd3, 0x36, 0x6d, 0x5e, 0xd2, 0x34, 0x9d, 0x96, 0x25, 0xca, 0xae, 0xc2, 0xd6, 0x42, + 0x50, 0x60, 0xeb, 0xb0, 0x5d, 0x2e, 0xb0, 0x02, 0x96, 0xa6, 0x69, 0x53, 0x51, 0x25, 0xcb, 0xa4, + 0xd9, 0x95, 0xe0, 0x60, 0x39, 0xf6, 0x24, 0x35, 0x4d, 0xed, 0x30, 0xb6, 0x8b, 0xca, 0xa7, 0xe0, + 0xc2, 0x81, 0x3b, 0x17, 0x10, 0x9f, 0x80, 0x2b, 0x1c, 0x38, 0xee, 0x09, 0x21, 0xc4, 0x01, 0xed, + 0x1e, 0xf8, 0x1a, 0xc8, 0xf3, 0x27, 0x76, 0xfe, 0xb5, 0xe9, 0xb6, 0x07, 0x6e, 0x9e, 0xe7, 0x79, + 0xbf, 0xdf, 0x7b, 0x6f, 0xde, 0x1f, 0x8f, 0x21, 0xdf, 0x6f, 0x97, 0x2d, 0x72, 0x66, 0x9b, 0xc4, + 0xd3, 0xfa, 0xd4, 0xf5, 0x5d, 0x54, 0x70, 0xcd, 0x8e, 0x66, 0xf6, 0xdc, 0xc0, 0xd2, 0xba, 0xb4, + 0x6f, 0x76, 0x0d, 0x9f, 0x7c, 0x6d, 0x9c, 0x6b, 0xfd, 0x76, 0x71, 0xab, 0x6b, 0xfb, 0xc7, 0x41, + 0x5b, 0x33, 0xdd, 0xd3, 0x72, 0xd7, 0xed, 0xba, 0x65, 0xa6, 0xd0, 0x0e, 0x3a, 0x6c, 0xc5, 0x16, + 0xec, 0x89, 0x03, 0xa9, 0x7f, 0x2b, 0xb0, 0xba, 0x4f, 0xfc, 0x5d, 0x8e, 0x8e, 0xc9, 0x57, 0x01, + 0xf1, 0x7c, 0xf4, 0x1a, 0x64, 0xfc, 0xf3, 0x3e, 0xd1, 0x3b, 0x76, 0xcf, 0x27, 0xb4, 0xa0, 0xdc, + 0x9d, 0xdb, 0x4c, 0x63, 0x08, 0x45, 0x7b, 0x4c, 0x82, 0x9e, 0xc2, 0xb2, 0xe7, 0x1b, 0x7e, 0xe0, + 0xc9, 0x2d, 0xc9, 0xbb, 0x73, 0x9b, 0xb9, 0xed, 0x6d, 0x6d, 0x9a, 0x5d, 0xda, 0x18, 0x89, 0xd6, + 0x64, 0x00, 0x38, 0xcb, 0x81, 0x04, 0xf0, 0xdb, 0xb0, 0xca, 0x3d, 0xd5, 0x6d, 0x6b, 0x00, 0x3e, + 0xc7, 0xf8, 0x57, 0xf8, 0x8b, 0x03, 0x4b, 0xec, 0x55, 0x37, 0x20, 0xc5, 0x31, 0x10, 0x40, 0xaa, + 0x51, 0x3f, 0x3c, 0xa8, 0x57, 0xf3, 0x09, 0x94, 0x81, 0xc5, 0xc6, 0xde, 0x1e, 0x5b, 0x28, 0x6a, + 0x07, 0x5e, 0xdd, 0x27, 0x3e, 0x26, 0x9e, 0x1b, 0x50, 0x93, 0x1c, 0xda, 0xce, 0xc9, 0xec, 0x3e, + 0x4e, 0x34, 0x25, 0x39, 0xd9, 0x94, 0xa7, 0x00, 0x92, 0xe4, 0xc0, 0x42, 0xb7, 0x21, 0x3d, 0xd0, + 0x2c, 0x28, 0x77, 0x95, 0xcd, 0x34, 0x5e, 0x92, 0x1a, 0xe8, 0x1e, 0x20, 0x2a, 0xb6, 0xea, 0x3d, + 0xdb, 0x39, 0xd1, 0x8f, 0x29, 0xe9, 0x14, 0x92, 0x6c, 0x57, 0x9e, 0xc6, 0x2c, 0xad, 0x51, 0xd2, + 0x51, 0xbf, 0x57, 0x60, 0x03, 0x13, 0x9f, 0xda, 0xe4, 0x8c, 0x48, 0x86, 0x3d, 0xea, 0x9e, 0xf2, + 0x58, 0x4a, 0x5f, 0xaa, 0x90, 0x19, 0x60, 0x0a, 0xca, 0xcc, 0xf6, 0xeb, 0xd3, 0x0f, 0x23, 0xb2, + 0x15, 0x03, 0x8d, 0xec, 0xde, 0x8a, 0x99, 0x66, 0x3b, 0x3e, 0xa1, 0x1d, 0xc3, 0x24, 0xc2, 0xb4, + 0xd5, 0xc1, 0x3e, 0xf9, 0x42, 0x35, 0x40, 0xbd, 0xc8, 0x34, 0xaf, 0xef, 0x3a, 0x1e, 0x41, 0x0f, + 0x61, 0xd1, 0x74, 0x1d, 0x9f, 0x38, 0xbe, 0xb0, 0x6b, 0x63, 0xba, 0x5d, 0x15, 0xbe, 0x11, 0x4b, + 0x0d, 0xf5, 0x57, 0x05, 0x4a, 0xa3, 0x1c, 0xde, 0x13, 0xa3, 0x17, 0x44, 0xb9, 0x7a, 0x04, 0x6b, + 0x31, 0xdf, 0xbd, 0xf8, 0x79, 0xce, 0x1a, 0x83, 0xc8, 0x37, 0xcb, 0xbb, 0xfa, 0xe1, 0x8f, 0x66, + 0xd2, 0xdc, 0x68, 0x26, 0xa9, 0x3f, 0x2a, 0xb0, 0x2c, 0xe9, 0x98, 0xf1, 0x37, 0x75, 0x60, 0xeb, + 0xb0, 0x10, 0xd2, 0x78, 0xc2, 0x32, 0xbe, 0x88, 0x47, 0x7c, 0xee, 0xca, 0x11, 0x7f, 0xa6, 0xc0, + 0xed, 0x56, 0xdf, 0x32, 0x7c, 0x32, 0x64, 0xb1, 0x77, 0xc3, 0xa9, 0x16, 0xb3, 0x31, 0x79, 0x55, + 0x1b, 0xa7, 0xe4, 0xe9, 0xdc, 0xb4, 0x3c, 0xfd, 0x02, 0xee, 0x4c, 0xf6, 0xe8, 0x26, 0x32, 0xf4, + 0xe7, 0x34, 0xa0, 0x66, 0xd0, 0xf6, 0x4c, 0x6a, 0xb7, 0xc9, 0x9e, 0x4b, 0xab, 0x67, 0xc4, 0xf1, + 0x3d, 0xd4, 0x81, 0x65, 0xd1, 0xb1, 0x75, 0x72, 0x16, 0x21, 0x7f, 0x3c, 0x1d, 0x79, 0x1c, 0x44, + 0x13, 0x0d, 0x93, 0xad, 0x78, 0x2a, 0xd5, 0x12, 0x38, 0x6b, 0xc5, 0xa4, 0xc8, 0x04, 0xb1, 0x16, + 0x34, 0x3c, 0x98, 0x1f, 0xbd, 0x04, 0xcd, 0x30, 0x4b, 0xc6, 0x8a, 0x84, 0xc8, 0x86, 0xdc, 0x20, + 0xde, 0x9c, 0x86, 0xe7, 0xd5, 0xa3, 0x2b, 0xd1, 0xc8, 0xe8, 0x0f, 0x13, 0x2d, 0xd3, 0xb8, 0x18, + 0x51, 0x58, 0x33, 0x0d, 0xc7, 0x24, 0x3d, 0xdd, 0xe3, 0x08, 0x7d, 0xdf, 0x76, 0x9d, 0xc2, 0xc2, + 0x4b, 0x44, 0xaf, 0xc2, 0x70, 0x9a, 0x31, 0x98, 0x5a, 0x02, 0x23, 0x73, 0x4c, 0xca, 0xaa, 0xc8, + 0x3d, 0x21, 0x4e, 0x81, 0xb0, 0x0c, 0xe2, 0x8b, 0xe2, 0x6f, 0x0a, 0xa0, 0xf1, 0x03, 0x40, 0x5f, + 0xc2, 0x32, 0xaf, 0x73, 0x1e, 0x09, 0x8f, 0x35, 0x9a, 0xdc, 0x76, 0xf5, 0x9a, 0x07, 0xab, 0xb1, + 0x67, 0x9c, 0xe5, 0xd8, 0x7c, 0x9b, 0xba, 0x03, 0x0b, 0x3c, 0x2a, 0x39, 0x00, 0x5c, 0xdd, 0x3f, + 0x68, 0x1e, 0x55, 0x71, 0x75, 0x37, 0x9f, 0x40, 0x79, 0xc8, 0xb6, 0xea, 0x31, 0x89, 0x12, 0x9b, + 0x80, 0xc9, 0xf8, 0x04, 0x9c, 0x2b, 0xfe, 0xa1, 0xc0, 0xea, 0xd8, 0x01, 0x5f, 0x3c, 0xa1, 0xec, + 0x51, 0x17, 0xf9, 0x70, 0xdf, 0xbd, 0x5e, 0x52, 0x4d, 0xf4, 0xf0, 0x7d, 0xe9, 0xe1, 0x2d, 0x40, + 0xb8, 0xda, 0x6c, 0xb4, 0x70, 0xa5, 0xaa, 0x3f, 0x6e, 0xed, 0x1c, 0x1e, 0x34, 0x6b, 0xcc, 0xd3, + 0x02, 0xac, 0x0f, 0xe4, 0xad, 0x7a, 0xf4, 0x46, 0x29, 0xfe, 0xab, 0xc0, 0xda, 0x84, 0x94, 0xba, + 0xa9, 0x06, 0x75, 0x32, 0x39, 0x08, 0x7b, 0xd7, 0x4d, 0xf9, 0x89, 0x61, 0xb8, 0x23, 0xc3, 0xb0, + 0x06, 0x2b, 0x95, 0x46, 0xfd, 0xa8, 0x5a, 0x3f, 0xd2, 0x2b, 0xb5, 0x4f, 0xea, 0xfb, 0x61, 0x0c, + 0x8a, 0x1f, 0x02, 0x1a, 0xcf, 0x65, 0xf4, 0x26, 0xac, 0xc4, 0x4b, 0x24, 0x3a, 0xc8, 0x5c, 0x5c, + 0x7c, 0x60, 0xed, 0x64, 0x20, 0x2d, 0x3c, 0x69, 0x9f, 0xab, 0x3f, 0xe4, 0x24, 0xd5, 0xac, 0xfa, + 0x48, 0x1f, 0x8c, 0x42, 0x4a, 0xba, 0xb6, 0xe7, 0x13, 0x4a, 0x2c, 0xd1, 0x00, 0xde, 0x9d, 0x1e, + 0x0d, 0x46, 0xa2, 0xc9, 0x6f, 0x01, 0xa9, 0x57, 0x4b, 0xe0, 0xbc, 0x35, 0x22, 0x43, 0x04, 0xd6, + 0x04, 0x41, 0xe0, 0xc4, 0x28, 0xe6, 0x19, 0xc5, 0xf6, 0x6c, 0x14, 0xad, 0x98, 0x66, 0x58, 0xe6, + 0xd6, 0x98, 0x14, 0x35, 0x65, 0x4b, 0xd6, 0x5d, 0xa7, 0x67, 0x3b, 0x44, 0x34, 0x95, 0x7b, 0xb3, + 0x11, 0x34, 0x98, 0x4e, 0xd4, 0x7f, 0xf9, 0x1a, 0x3d, 0x81, 0x9c, 0x04, 0xed, 0x74, 0x18, 0x6a, + 0x8a, 0xa1, 0x6e, 0xcd, 0x88, 0xca, 0x95, 0xc2, 0x3e, 0x68, 0xc5, 0x05, 0xa8, 0x1d, 0x1b, 0x71, + 0xfd, 0xa0, 0xdd, 0xb3, 0xbd, 0x63, 0x62, 0x15, 0x16, 0x19, 0xf6, 0xfd, 0xcb, 0xb0, 0x65, 0xda, + 0x3d, 0x96, 0x8a, 0xb5, 0x44, 0x34, 0x17, 0x07, 0x42, 0x74, 0x0c, 0xeb, 0x03, 0x8e, 0xc0, 0x89, + 0x58, 0x96, 0x18, 0xcb, 0x83, 0x59, 0x59, 0x5a, 0x91, 0x6a, 0x2d, 0x81, 0x07, 0x1f, 0x63, 0x31, + 0x31, 0x3a, 0x81, 0xc2, 0x80, 0x49, 0x0c, 0x4e, 0xdd, 0x3c, 0x36, 0x9c, 0x2e, 0xb1, 0x0a, 0x69, + 0xc6, 0x56, 0x9e, 0x95, 0xad, 0xc2, 0xd5, 0x6a, 0x09, 0x7c, 0x4b, 0x42, 0x8a, 0x99, 0x2c, 0xde, + 0x84, 0xe9, 0xe4, 0xf6, 0x09, 0x35, 0x58, 0x56, 0xf7, 0xa9, 0x6b, 0x12, 0xcf, 0x23, 0x56, 0x01, + 0x66, 0x4b, 0xa7, 0x86, 0x54, 0x7d, 0x2c, 0x35, 0xc3, 0x74, 0x72, 0xc7, 0xa4, 0xe8, 0x04, 0x5e, + 0x19, 0xaa, 0x1f, 0x3e, 0x58, 0x88, 0x55, 0xc8, 0x30, 0xa2, 0xf7, 0x2e, 0x23, 0x8a, 0x17, 0x73, + 0x45, 0xe8, 0xd6, 0x12, 0x78, 0xdd, 0x9b, 0x20, 0x2f, 0x96, 0x21, 0x3f, 0x5a, 0x4a, 0x17, 0xf6, + 0xf0, 0xe2, 0x7d, 0x39, 0xbc, 0x86, 0x4a, 0xe0, 0x42, 0x95, 0x77, 0x20, 0x1b, 0x4f, 0xf5, 0x8b, + 0x37, 0xdf, 0x83, 0xe5, 0xa1, 0x0c, 0xbe, 0x78, 0x77, 0x03, 0x56, 0xc7, 0x72, 0x12, 0x7d, 0x00, + 0xf3, 0xe1, 0xfd, 0x47, 0x74, 0xe8, 0x37, 0x2e, 0xef, 0xd0, 0xe1, 0xa5, 0x08, 0x33, 0x9d, 0xe2, + 0x67, 0x51, 0xef, 0x8f, 0xe7, 0x99, 0x84, 0x4c, 0xbe, 0x04, 0xe4, 0x77, 0x0a, 0xac, 0x8c, 0x24, + 0xd9, 0xff, 0xe1, 0x63, 0xb7, 0xf8, 0x4b, 0x12, 0xd0, 0x78, 0x52, 0x46, 0x1f, 0x2d, 0x4a, 0xec, + 0xa3, 0x05, 0x19, 0x90, 0x25, 0x94, 0xba, 0x54, 0xe7, 0x97, 0xea, 0xcb, 0x3f, 0x07, 0xa7, 0x25, + 0xbd, 0x56, 0x0d, 0x61, 0xc4, 0x15, 0x3d, 0x43, 0xa2, 0x45, 0xf1, 0x27, 0x05, 0x32, 0xb1, 0x97, + 0xe8, 0x29, 0xcc, 0x9b, 0xae, 0x45, 0x98, 0x1d, 0xb9, 0xed, 0xca, 0xf5, 0xa8, 0xb4, 0x8a, 0x6b, + 0x11, 0xcc, 0x00, 0x51, 0x01, 0x16, 0x4f, 0x89, 0xe7, 0x19, 0x5d, 0x79, 0x05, 0x95, 0x4b, 0x75, + 0x13, 0xe6, 0xc3, 0x7d, 0x28, 0x05, 0xc9, 0xc6, 0xa7, 0xf9, 0x04, 0x4a, 0xc3, 0x42, 0x15, 0xe3, + 0x06, 0xce, 0x2b, 0x68, 0x19, 0xd2, 0xf5, 0xc6, 0x91, 0xbe, 0xd7, 0x68, 0xd5, 0x77, 0xf3, 0xc9, + 0xa2, 0x06, 0xeb, 0x93, 0xea, 0x0c, 0xdd, 0x82, 0x14, 0x25, 0x86, 0xe7, 0x3a, 0x02, 0x5a, 0xac, + 0x76, 0x52, 0x30, 0x1f, 0xde, 0xa1, 0xd4, 0x0a, 0xac, 0x1c, 0xba, 0xa6, 0xd1, 0xb3, 0xbf, 0x21, + 0x56, 0xd3, 0xa7, 0xb6, 0xd3, 0x45, 0x45, 0x58, 0xea, 0x19, 0x4e, 0x37, 0x08, 0xed, 0x11, 0xf9, + 0x2d, 0xd7, 0xe1, 0x61, 0x9c, 0x85, 0x77, 0x0a, 0x81, 0xc6, 0x17, 0xea, 0x5f, 0x0a, 0xa4, 0x78, + 0x91, 0xa0, 0x1c, 0x24, 0x07, 0x65, 0x91, 0xb4, 0xa7, 0x5d, 0xdc, 0x10, 0xcc, 0x3b, 0xc6, 0xa9, + 0xbc, 0xc9, 0xb0, 0xe7, 0xb0, 0xae, 0x6c, 0x4f, 0x4e, 0xac, 0x70, 0x24, 0x2e, 0xe1, 0x25, 0xdb, + 0x1b, 0x4c, 0x9f, 0xd5, 0x53, 0xc3, 0x09, 0x3a, 0x86, 0xe9, 0x07, 0x94, 0x50, 0x9d, 0x69, 0x2f, + 0xb0, 0x9b, 0xef, 0x5b, 0xd3, 0x0f, 0x62, 0xc4, 0x33, 0x9c, 0x8f, 0x63, 0xd4, 0x43, 0xd2, 0x0d, + 0xc8, 0x9e, 0xba, 0x16, 0xe9, 0xe9, 0x4e, 0x70, 0xda, 0x26, 0x94, 0xcd, 0xb4, 0x34, 0xce, 0x30, + 0x59, 0x9d, 0x89, 0xd4, 0x00, 0xb2, 0xf1, 0x22, 0x0a, 0x6d, 0x67, 0x3f, 0x32, 0xb8, 0x8f, 0xec, + 0x79, 0x8a, 0x97, 0x25, 0x80, 0xc1, 0xa5, 0xcd, 0x93, 0xb7, 0xe5, 0x48, 0x32, 0xdc, 0x49, 0xe6, + 0x87, 0x3b, 0x89, 0xfa, 0x08, 0x16, 0x45, 0x85, 0x84, 0x46, 0xca, 0x59, 0x12, 0x02, 0x0b, 0xe6, + 0x8c, 0x90, 0x1d, 0x9d, 0xf7, 0x49, 0x68, 0x94, 0x65, 0xf8, 0x06, 0x3b, 0x96, 0x2c, 0x66, 0xcf, + 0x3b, 0xb5, 0xdf, 0x9f, 0x97, 0x94, 0x67, 0xcf, 0x4b, 0xca, 0x3f, 0xcf, 0x4b, 0xca, 0xb7, 0x2f, + 0x4a, 0x89, 0x67, 0x2f, 0x4a, 0x89, 0x3f, 0x5f, 0x94, 0x12, 0x9f, 0x6b, 0x43, 0xbf, 0xce, 0xb6, + 0x5c, 0xb3, 0x53, 0x76, 0xcd, 0xce, 0x16, 0x8b, 0x61, 0x39, 0x8c, 0xe1, 0x96, 0x08, 0x62, 0xb9, + 0xdf, 0x7e, 0xd8, 0x6f, 0xb7, 0x53, 0xec, 0x17, 0xda, 0x83, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, + 0xf3, 0x10, 0xa0, 0xcc, 0x9f, 0x13, 0x00, 0x00, +} + +func (m *GetDevicesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetDevicesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetDevicesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DeviceIdsFilter) > 0 { + for iNdEx := len(m.DeviceIdsFilter) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.DeviceIdsFilter[iNdEx]) + copy(dAtA[i:], m.DeviceIdsFilter[iNdEx]) + i = encodeVarintDevices(dAtA, i, uint64(len(m.DeviceIdsFilter[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.StatusFilter) > 0 { + dAtA2 := make([]byte, len(m.StatusFilter)*10) + var j1 int + for _, num := range m.StatusFilter { + for num >= 1<<7 { + dAtA2[j1] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j1++ + } + dAtA2[j1] = uint8(num) + j1++ + } + i -= j1 + copy(dAtA[i:], dAtA2[:j1]) + i = encodeVarintDevices(dAtA, i, uint64(j1)) + i-- + dAtA[i] = 0x12 + } + if len(m.TypeFilter) > 0 { + for iNdEx := len(m.TypeFilter) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.TypeFilter[iNdEx]) + copy(dAtA[i:], m.TypeFilter[iNdEx]) + i = encodeVarintDevices(dAtA, i, uint64(len(m.TypeFilter[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *GetResourceLinksRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetResourceLinksRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetResourceLinksRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DeviceIdsFilter) > 0 { + for iNdEx := len(m.DeviceIdsFilter) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.DeviceIdsFilter[iNdEx]) + copy(dAtA[i:], m.DeviceIdsFilter[iNdEx]) + i = encodeVarintDevices(dAtA, i, uint64(len(m.DeviceIdsFilter[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.TypeFilter) > 0 { + for iNdEx := len(m.TypeFilter) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.TypeFilter[iNdEx]) + copy(dAtA[i:], m.TypeFilter[iNdEx]) + i = encodeVarintDevices(dAtA, i, uint64(len(m.TypeFilter[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ResourceId) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResourceId) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResourceId) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ResourceLinkHref) > 0 { + i -= len(m.ResourceLinkHref) + copy(dAtA[i:], m.ResourceLinkHref) + i = encodeVarintDevices(dAtA, i, uint64(len(m.ResourceLinkHref))) + i-- + dAtA[i] = 0x12 + } + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintDevices(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RetrieveResourceFromDeviceRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RetrieveResourceFromDeviceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RetrieveResourceFromDeviceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ResourceInterface) > 0 { + i -= len(m.ResourceInterface) + copy(dAtA[i:], m.ResourceInterface) + i = encodeVarintDevices(dAtA, i, uint64(len(m.ResourceInterface))) + i-- + dAtA[i] = 0x12 + } + if m.ResourceId != nil { + { + size, err := m.ResourceId.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RetrieveResourceFromDeviceResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RetrieveResourceFromDeviceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RetrieveResourceFromDeviceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Content != nil { + { + size, err := m.Content.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RetrieveResourcesValuesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RetrieveResourcesValuesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RetrieveResourcesValuesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.TypeFilter) > 0 { + for iNdEx := len(m.TypeFilter) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.TypeFilter[iNdEx]) + copy(dAtA[i:], m.TypeFilter[iNdEx]) + i = encodeVarintDevices(dAtA, i, uint64(len(m.TypeFilter[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.DeviceIdsFilter) > 0 { + for iNdEx := len(m.DeviceIdsFilter) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.DeviceIdsFilter[iNdEx]) + copy(dAtA[i:], m.DeviceIdsFilter[iNdEx]) + i = encodeVarintDevices(dAtA, i, uint64(len(m.DeviceIdsFilter[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.ResourceIdsFilter) > 0 { + for iNdEx := len(m.ResourceIdsFilter) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ResourceIdsFilter[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ResourceValue) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResourceValue) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResourceValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Content != nil { + { + size, err := m.Content.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.Types) > 0 { + for iNdEx := len(m.Types) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Types[iNdEx]) + copy(dAtA[i:], m.Types[iNdEx]) + i = encodeVarintDevices(dAtA, i, uint64(len(m.Types[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if m.ResourceId != nil { + { + size, err := m.ResourceId.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UpdateResourceValuesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UpdateResourceValuesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateResourceValuesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ResourceInterface) > 0 { + i -= len(m.ResourceInterface) + copy(dAtA[i:], m.ResourceInterface) + i = encodeVarintDevices(dAtA, i, uint64(len(m.ResourceInterface))) + i-- + dAtA[i] = 0x1a + } + if m.Content != nil { + { + size, err := m.Content.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.ResourceId != nil { + { + size, err := m.ResourceId.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UpdateResourceValuesResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UpdateResourceValuesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateResourceValuesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Content != nil { + { + size, err := m.Content.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SubscribeForEvents) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SubscribeForEvents) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SubscribeForEvents) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Token) > 0 { + i -= len(m.Token) + copy(dAtA[i:], m.Token) + i = encodeVarintDevices(dAtA, i, uint64(len(m.Token))) + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0xaa + } + if m.FilterBy != nil { + { + size := m.FilterBy.Size() + i -= size + if _, err := m.FilterBy.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + return len(dAtA) - i, nil +} + +func (m *SubscribeForEvents_DevicesEvent) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SubscribeForEvents_DevicesEvent) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.DevicesEvent != nil { + { + size, err := m.DevicesEvent.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} +func (m *SubscribeForEvents_DeviceEvent) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SubscribeForEvents_DeviceEvent) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.DeviceEvent != nil { + { + size, err := m.DeviceEvent.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + return len(dAtA) - i, nil +} +func (m *SubscribeForEvents_ResourceEvent) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SubscribeForEvents_ResourceEvent) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.ResourceEvent != nil { + { + size, err := m.ResourceEvent.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + return len(dAtA) - i, nil +} +func (m *SubscribeForEvents_CancelSubscription_) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SubscribeForEvents_CancelSubscription_) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.CancelSubscription != nil { + { + size, err := m.CancelSubscription.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + return len(dAtA) - i, nil +} +func (m *SubscribeForEvents_DevicesEventFilter) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SubscribeForEvents_DevicesEventFilter) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SubscribeForEvents_DevicesEventFilter) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.FilterEvents) > 0 { + dAtA15 := make([]byte, len(m.FilterEvents)*10) + var j14 int + for _, num := range m.FilterEvents { + for num >= 1<<7 { + dAtA15[j14] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j14++ + } + dAtA15[j14] = uint8(num) + j14++ + } + i -= j14 + copy(dAtA[i:], dAtA15[:j14]) + i = encodeVarintDevices(dAtA, i, uint64(j14)) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SubscribeForEvents_DeviceEventFilter) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SubscribeForEvents_DeviceEventFilter) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SubscribeForEvents_DeviceEventFilter) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.FilterEvents) > 0 { + dAtA17 := make([]byte, len(m.FilterEvents)*10) + var j16 int + for _, num := range m.FilterEvents { + for num >= 1<<7 { + dAtA17[j16] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j16++ + } + dAtA17[j16] = uint8(num) + j16++ + } + i -= j16 + copy(dAtA[i:], dAtA17[:j16]) + i = encodeVarintDevices(dAtA, i, uint64(j16)) + i-- + dAtA[i] = 0x12 + } + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintDevices(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SubscribeForEvents_ResourceEventFilter) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SubscribeForEvents_ResourceEventFilter) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SubscribeForEvents_ResourceEventFilter) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.FilterEvents) > 0 { + dAtA19 := make([]byte, len(m.FilterEvents)*10) + var j18 int + for _, num := range m.FilterEvents { + for num >= 1<<7 { + dAtA19[j18] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j18++ + } + dAtA19[j18] = uint8(num) + j18++ + } + i -= j18 + copy(dAtA[i:], dAtA19[:j18]) + i = encodeVarintDevices(dAtA, i, uint64(j18)) + i-- + dAtA[i] = 0x12 + } + if m.ResourceId != nil { + { + size, err := m.ResourceId.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *SubscribeForEvents_CancelSubscription) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SubscribeForEvents_CancelSubscription) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SubscribeForEvents_CancelSubscription) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.SubscriptionId) > 0 { + i -= len(m.SubscriptionId) + copy(dAtA[i:], m.SubscriptionId) + i = encodeVarintDevices(dAtA, i, uint64(len(m.SubscriptionId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Event) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Event) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Type != nil { + { + size := m.Type.Size() + i -= size + if _, err := m.Type.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + } + } + if len(m.SubscriptionId) > 0 { + i -= len(m.SubscriptionId) + copy(dAtA[i:], m.SubscriptionId) + i = encodeVarintDevices(dAtA, i, uint64(len(m.SubscriptionId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Event_DeviceRegistered_) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_DeviceRegistered_) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.DeviceRegistered != nil { + { + size, err := m.DeviceRegistered.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + return len(dAtA) - i, nil +} +func (m *Event_DeviceUnregistered_) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_DeviceUnregistered_) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.DeviceUnregistered != nil { + { + size, err := m.DeviceUnregistered.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + return len(dAtA) - i, nil +} +func (m *Event_DeviceOnline_) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_DeviceOnline_) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.DeviceOnline != nil { + { + size, err := m.DeviceOnline.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + return len(dAtA) - i, nil +} +func (m *Event_DeviceOffline_) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_DeviceOffline_) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.DeviceOffline != nil { + { + size, err := m.DeviceOffline.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + return len(dAtA) - i, nil +} +func (m *Event_ResourcePublished_) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_ResourcePublished_) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.ResourcePublished != nil { + { + size, err := m.ResourcePublished.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + return len(dAtA) - i, nil +} +func (m *Event_ResourceUnpublished_) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_ResourceUnpublished_) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.ResourceUnpublished != nil { + { + size, err := m.ResourceUnpublished.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + return len(dAtA) - i, nil +} +func (m *Event_ResourceContentChanged) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_ResourceContentChanged) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.ResourceContentChanged != nil { + { + size, err := m.ResourceContentChanged.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a + } + return len(dAtA) - i, nil +} +func (m *Event_OperationProcessed_) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_OperationProcessed_) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.OperationProcessed != nil { + { + size, err := m.OperationProcessed.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x52 + } + return len(dAtA) - i, nil +} +func (m *Event_SubscriptionCanceled_) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_SubscriptionCanceled_) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.SubscriptionCanceled != nil { + { + size, err := m.SubscriptionCanceled.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a + } + return len(dAtA) - i, nil +} +func (m *Event_DeviceRegistered) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Event_DeviceRegistered) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_DeviceRegistered) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintDevices(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Event_DeviceUnregistered) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Event_DeviceUnregistered) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_DeviceUnregistered) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintDevices(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Event_DeviceOnline) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Event_DeviceOnline) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_DeviceOnline) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintDevices(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Event_DeviceOffline) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Event_DeviceOffline) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_DeviceOffline) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintDevices(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Event_ResourcePublished) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Event_ResourcePublished) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_ResourcePublished) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Link != nil { + { + size, err := m.Link.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Event_ResourceUnpublished) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Event_ResourceUnpublished) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_ResourceUnpublished) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Link != nil { + { + size, err := m.Link.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + return len(dAtA) - i, nil +} + +func (m *Event_ResourceChanged) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Event_ResourceChanged) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_ResourceChanged) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Content != nil { + { + size, err := m.Content.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.ResourceId != nil { + { + size, err := m.ResourceId.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Event_OperationProcessed) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Event_OperationProcessed) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_OperationProcessed) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ErrorStatus != nil { + { + size, err := m.ErrorStatus.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Token) > 0 { + i -= len(m.Token) + copy(dAtA[i:], m.Token) + i = encodeVarintDevices(dAtA, i, uint64(len(m.Token))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Event_OperationProcessed_ErrorStatus) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Event_OperationProcessed_ErrorStatus) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_OperationProcessed_ErrorStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = encodeVarintDevices(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0x12 + } + if m.Code != 0 { + i = encodeVarintDevices(dAtA, i, uint64(m.Code)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Event_SubscriptionCanceled) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Event_SubscriptionCanceled) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Event_SubscriptionCanceled) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Reason) > 0 { + i -= len(m.Reason) + copy(dAtA[i:], m.Reason) + i = encodeVarintDevices(dAtA, i, uint64(len(m.Reason))) + i-- + dAtA[i] = 0x12 + } + return len(dAtA) - i, nil +} + +func (m *LocalizedString) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *LocalizedString) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LocalizedString) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintDevices(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x12 + } + if len(m.Language) > 0 { + i -= len(m.Language) + copy(dAtA[i:], m.Language) + i = encodeVarintDevices(dAtA, i, uint64(len(m.Language))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Device) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Device) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Device) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ModelNumber) > 0 { + i -= len(m.ModelNumber) + copy(dAtA[i:], m.ModelNumber) + i = encodeVarintDevices(dAtA, i, uint64(len(m.ModelNumber))) + i-- + dAtA[i] = 0x32 + } + if len(m.ManufacturerName) > 0 { + for iNdEx := len(m.ManufacturerName) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ManufacturerName[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintDevices(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if m.IsOnline { + i-- + if m.IsOnline { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintDevices(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x1a + } + if len(m.Types) > 0 { + for iNdEx := len(m.Types) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Types[iNdEx]) + copy(dAtA[i:], m.Types[iNdEx]) + i = encodeVarintDevices(dAtA, i, uint64(len(m.Types[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintDevices(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ResourceLink) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResourceLink) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResourceLink) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintDevices(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0x22 + } + if len(m.Interfaces) > 0 { + for iNdEx := len(m.Interfaces) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Interfaces[iNdEx]) + copy(dAtA[i:], m.Interfaces[iNdEx]) + i = encodeVarintDevices(dAtA, i, uint64(len(m.Interfaces[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Types) > 0 { + for iNdEx := len(m.Types) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Types[iNdEx]) + copy(dAtA[i:], m.Types[iNdEx]) + i = encodeVarintDevices(dAtA, i, uint64(len(m.Types[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.Href) > 0 { + i -= len(m.Href) + copy(dAtA[i:], m.Href) + i = encodeVarintDevices(dAtA, i, uint64(len(m.Href))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Content) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Content) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Content) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintDevices(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0x12 + } + if len(m.ContentType) > 0 { + i -= len(m.ContentType) + copy(dAtA[i:], m.ContentType) + i = encodeVarintDevices(dAtA, i, uint64(len(m.ContentType))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintDevices(dAtA []byte, offset int, v uint64) int { + offset -= sovDevices(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GetDevicesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.TypeFilter) > 0 { + for _, s := range m.TypeFilter { + l = len(s) + n += 1 + l + sovDevices(uint64(l)) + } + } + if len(m.StatusFilter) > 0 { + l = 0 + for _, e := range m.StatusFilter { + l += sovDevices(uint64(e)) + } + n += 1 + sovDevices(uint64(l)) + l + } + if len(m.DeviceIdsFilter) > 0 { + for _, s := range m.DeviceIdsFilter { + l = len(s) + n += 1 + l + sovDevices(uint64(l)) + } + } + return n +} + +func (m *GetResourceLinksRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.TypeFilter) > 0 { + for _, s := range m.TypeFilter { + l = len(s) + n += 1 + l + sovDevices(uint64(l)) + } + } + if len(m.DeviceIdsFilter) > 0 { + for _, s := range m.DeviceIdsFilter { + l = len(s) + n += 1 + l + sovDevices(uint64(l)) + } + } + return n +} + +func (m *ResourceId) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + l = len(m.ResourceLinkHref) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *RetrieveResourceFromDeviceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ResourceId != nil { + l = m.ResourceId.Size() + n += 1 + l + sovDevices(uint64(l)) + } + l = len(m.ResourceInterface) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *RetrieveResourceFromDeviceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Content != nil { + l = m.Content.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *RetrieveResourcesValuesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.ResourceIdsFilter) > 0 { + for _, e := range m.ResourceIdsFilter { + l = e.Size() + n += 1 + l + sovDevices(uint64(l)) + } + } + if len(m.DeviceIdsFilter) > 0 { + for _, s := range m.DeviceIdsFilter { + l = len(s) + n += 1 + l + sovDevices(uint64(l)) + } + } + if len(m.TypeFilter) > 0 { + for _, s := range m.TypeFilter { + l = len(s) + n += 1 + l + sovDevices(uint64(l)) + } + } + return n +} + +func (m *ResourceValue) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ResourceId != nil { + l = m.ResourceId.Size() + n += 1 + l + sovDevices(uint64(l)) + } + if len(m.Types) > 0 { + for _, s := range m.Types { + l = len(s) + n += 1 + l + sovDevices(uint64(l)) + } + } + if m.Content != nil { + l = m.Content.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *UpdateResourceValuesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ResourceId != nil { + l = m.ResourceId.Size() + n += 1 + l + sovDevices(uint64(l)) + } + if m.Content != nil { + l = m.Content.Size() + n += 1 + l + sovDevices(uint64(l)) + } + l = len(m.ResourceInterface) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *UpdateResourceValuesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Content != nil { + l = m.Content.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *SubscribeForEvents) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.FilterBy != nil { + n += m.FilterBy.Size() + } + l = len(m.Token) + if l > 0 { + n += 2 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *SubscribeForEvents_DevicesEvent) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.DevicesEvent != nil { + l = m.DevicesEvent.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} +func (m *SubscribeForEvents_DeviceEvent) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.DeviceEvent != nil { + l = m.DeviceEvent.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} +func (m *SubscribeForEvents_ResourceEvent) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ResourceEvent != nil { + l = m.ResourceEvent.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} +func (m *SubscribeForEvents_CancelSubscription_) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.CancelSubscription != nil { + l = m.CancelSubscription.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} +func (m *SubscribeForEvents_DevicesEventFilter) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.FilterEvents) > 0 { + l = 0 + for _, e := range m.FilterEvents { + l += sovDevices(uint64(e)) + } + n += 1 + sovDevices(uint64(l)) + l + } + return n +} + +func (m *SubscribeForEvents_DeviceEventFilter) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + if len(m.FilterEvents) > 0 { + l = 0 + for _, e := range m.FilterEvents { + l += sovDevices(uint64(e)) + } + n += 1 + sovDevices(uint64(l)) + l + } + return n +} + +func (m *SubscribeForEvents_ResourceEventFilter) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ResourceId != nil { + l = m.ResourceId.Size() + n += 1 + l + sovDevices(uint64(l)) + } + if len(m.FilterEvents) > 0 { + l = 0 + for _, e := range m.FilterEvents { + l += sovDevices(uint64(e)) + } + n += 1 + sovDevices(uint64(l)) + l + } + return n +} + +func (m *SubscribeForEvents_CancelSubscription) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.SubscriptionId) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *Event) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.SubscriptionId) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + if m.Type != nil { + n += m.Type.Size() + } + return n +} + +func (m *Event_DeviceRegistered_) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.DeviceRegistered != nil { + l = m.DeviceRegistered.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} +func (m *Event_DeviceUnregistered_) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.DeviceUnregistered != nil { + l = m.DeviceUnregistered.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} +func (m *Event_DeviceOnline_) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.DeviceOnline != nil { + l = m.DeviceOnline.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} +func (m *Event_DeviceOffline_) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.DeviceOffline != nil { + l = m.DeviceOffline.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} +func (m *Event_ResourcePublished_) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ResourcePublished != nil { + l = m.ResourcePublished.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} +func (m *Event_ResourceUnpublished_) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ResourceUnpublished != nil { + l = m.ResourceUnpublished.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} +func (m *Event_ResourceContentChanged) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ResourceContentChanged != nil { + l = m.ResourceContentChanged.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} +func (m *Event_OperationProcessed_) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.OperationProcessed != nil { + l = m.OperationProcessed.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} +func (m *Event_SubscriptionCanceled_) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SubscriptionCanceled != nil { + l = m.SubscriptionCanceled.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} +func (m *Event_DeviceRegistered) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *Event_DeviceUnregistered) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *Event_DeviceOnline) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *Event_DeviceOffline) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *Event_ResourcePublished) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Link != nil { + l = m.Link.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *Event_ResourceUnpublished) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Link != nil { + l = m.Link.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *Event_ResourceChanged) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ResourceId != nil { + l = m.ResourceId.Size() + n += 1 + l + sovDevices(uint64(l)) + } + if m.Content != nil { + l = m.Content.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *Event_OperationProcessed) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Token) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + if m.ErrorStatus != nil { + l = m.ErrorStatus.Size() + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *Event_OperationProcessed_ErrorStatus) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Code != 0 { + n += 1 + sovDevices(uint64(m.Code)) + } + l = len(m.Message) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *Event_SubscriptionCanceled) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Reason) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *LocalizedString) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Language) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + l = len(m.Value) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *Device) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + if len(m.Types) > 0 { + for _, s := range m.Types { + l = len(s) + n += 1 + l + sovDevices(uint64(l)) + } + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + if m.IsOnline { + n += 2 + } + if len(m.ManufacturerName) > 0 { + for _, e := range m.ManufacturerName { + l = e.Size() + n += 1 + l + sovDevices(uint64(l)) + } + } + l = len(m.ModelNumber) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *ResourceLink) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Href) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + if len(m.Types) > 0 { + for _, s := range m.Types { + l = len(s) + n += 1 + l + sovDevices(uint64(l)) + } + } + if len(m.Interfaces) > 0 { + for _, s := range m.Interfaces { + l = len(s) + n += 1 + l + sovDevices(uint64(l)) + } + } + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func (m *Content) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ContentType) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + l = len(m.Data) + if l > 0 { + n += 1 + l + sovDevices(uint64(l)) + } + return n +} + +func sovDevices(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozDevices(x uint64) (n int) { + return sovDevices(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GetDevicesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetDevicesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetDevicesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TypeFilter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TypeFilter = append(m.TypeFilter, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType == 0 { + var v GetDevicesRequest_Status + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= GetDevicesRequest_Status(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.StatusFilter = append(m.StatusFilter, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + if elementCount != 0 && len(m.StatusFilter) == 0 { + m.StatusFilter = make([]GetDevicesRequest_Status, 0, elementCount) + } + for iNdEx < postIndex { + var v GetDevicesRequest_Status + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= GetDevicesRequest_Status(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.StatusFilter = append(m.StatusFilter, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field StatusFilter", wireType) + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceIdsFilter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceIdsFilter = append(m.DeviceIdsFilter, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *GetResourceLinksRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetResourceLinksRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetResourceLinksRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TypeFilter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TypeFilter = append(m.TypeFilter, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceIdsFilter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceIdsFilter = append(m.DeviceIdsFilter, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResourceId) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResourceId: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResourceId: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceLinkHref", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceLinkHref = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RetrieveResourceFromDeviceRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RetrieveResourceFromDeviceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RetrieveResourceFromDeviceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceId", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ResourceId == nil { + m.ResourceId = &ResourceId{} + } + if err := m.ResourceId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceInterface", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceInterface = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RetrieveResourceFromDeviceResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RetrieveResourceFromDeviceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RetrieveResourceFromDeviceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Content == nil { + m.Content = &Content{} + } + if err := m.Content.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RetrieveResourcesValuesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RetrieveResourcesValuesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RetrieveResourcesValuesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceIdsFilter", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceIdsFilter = append(m.ResourceIdsFilter, &ResourceId{}) + if err := m.ResourceIdsFilter[len(m.ResourceIdsFilter)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceIdsFilter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceIdsFilter = append(m.DeviceIdsFilter, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TypeFilter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TypeFilter = append(m.TypeFilter, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResourceValue) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResourceValue: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResourceValue: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceId", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ResourceId == nil { + m.ResourceId = &ResourceId{} + } + if err := m.ResourceId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Types", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Types = append(m.Types, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Content == nil { + m.Content = &Content{} + } + if err := m.Content.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UpdateResourceValuesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UpdateResourceValuesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UpdateResourceValuesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceId", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ResourceId == nil { + m.ResourceId = &ResourceId{} + } + if err := m.ResourceId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Content == nil { + m.Content = &Content{} + } + if err := m.Content.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceInterface", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceInterface = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UpdateResourceValuesResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UpdateResourceValuesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UpdateResourceValuesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Content == nil { + m.Content = &Content{} + } + if err := m.Content.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SubscribeForEvents) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SubscribeForEvents: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SubscribeForEvents: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DevicesEvent", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &SubscribeForEvents_DevicesEventFilter{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.FilterBy = &SubscribeForEvents_DevicesEvent{v} + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceEvent", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &SubscribeForEvents_DeviceEventFilter{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.FilterBy = &SubscribeForEvents_DeviceEvent{v} + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceEvent", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &SubscribeForEvents_ResourceEventFilter{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.FilterBy = &SubscribeForEvents_ResourceEvent{v} + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CancelSubscription", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &SubscribeForEvents_CancelSubscription{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.FilterBy = &SubscribeForEvents_CancelSubscription_{v} + iNdEx = postIndex + case 101: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Token", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Token = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SubscribeForEvents_DevicesEventFilter) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DevicesEventFilter: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DevicesEventFilter: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType == 0 { + var v SubscribeForEvents_DevicesEventFilter_Event + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= SubscribeForEvents_DevicesEventFilter_Event(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.FilterEvents = append(m.FilterEvents, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + if elementCount != 0 && len(m.FilterEvents) == 0 { + m.FilterEvents = make([]SubscribeForEvents_DevicesEventFilter_Event, 0, elementCount) + } + for iNdEx < postIndex { + var v SubscribeForEvents_DevicesEventFilter_Event + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= SubscribeForEvents_DevicesEventFilter_Event(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.FilterEvents = append(m.FilterEvents, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field FilterEvents", wireType) + } + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SubscribeForEvents_DeviceEventFilter) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DeviceEventFilter: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DeviceEventFilter: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType == 0 { + var v SubscribeForEvents_DeviceEventFilter_Event + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= SubscribeForEvents_DeviceEventFilter_Event(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.FilterEvents = append(m.FilterEvents, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + if elementCount != 0 && len(m.FilterEvents) == 0 { + m.FilterEvents = make([]SubscribeForEvents_DeviceEventFilter_Event, 0, elementCount) + } + for iNdEx < postIndex { + var v SubscribeForEvents_DeviceEventFilter_Event + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= SubscribeForEvents_DeviceEventFilter_Event(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.FilterEvents = append(m.FilterEvents, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field FilterEvents", wireType) + } + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SubscribeForEvents_ResourceEventFilter) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResourceEventFilter: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResourceEventFilter: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceId", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ResourceId == nil { + m.ResourceId = &ResourceId{} + } + if err := m.ResourceId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType == 0 { + var v SubscribeForEvents_ResourceEventFilter_Event + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= SubscribeForEvents_ResourceEventFilter_Event(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.FilterEvents = append(m.FilterEvents, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + if elementCount != 0 && len(m.FilterEvents) == 0 { + m.FilterEvents = make([]SubscribeForEvents_ResourceEventFilter_Event, 0, elementCount) + } + for iNdEx < postIndex { + var v SubscribeForEvents_ResourceEventFilter_Event + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= SubscribeForEvents_ResourceEventFilter_Event(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.FilterEvents = append(m.FilterEvents, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field FilterEvents", wireType) + } + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *SubscribeForEvents_CancelSubscription) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CancelSubscription: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CancelSubscription: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SubscriptionId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SubscriptionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Event) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Event: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Event: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SubscriptionId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SubscriptionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceRegistered", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &Event_DeviceRegistered{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Type = &Event_DeviceRegistered_{v} + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceUnregistered", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &Event_DeviceUnregistered{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Type = &Event_DeviceUnregistered_{v} + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceOnline", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &Event_DeviceOnline{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Type = &Event_DeviceOnline_{v} + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceOffline", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &Event_DeviceOffline{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Type = &Event_DeviceOffline_{v} + iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourcePublished", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &Event_ResourcePublished{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Type = &Event_ResourcePublished_{v} + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceUnpublished", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &Event_ResourceUnpublished{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Type = &Event_ResourceUnpublished_{v} + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceContentChanged", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &Event_ResourceChanged{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Type = &Event_ResourceContentChanged{v} + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field OperationProcessed", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &Event_OperationProcessed{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Type = &Event_OperationProcessed_{v} + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SubscriptionCanceled", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &Event_SubscriptionCanceled{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Type = &Event_SubscriptionCanceled_{v} + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Event_DeviceRegistered) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DeviceRegistered: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DeviceRegistered: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Event_DeviceUnregistered) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DeviceUnregistered: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DeviceUnregistered: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Event_DeviceOnline) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DeviceOnline: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DeviceOnline: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Event_DeviceOffline) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DeviceOffline: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DeviceOffline: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Event_ResourcePublished) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResourcePublished: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResourcePublished: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Link", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Link == nil { + m.Link = &ResourceLink{} + } + if err := m.Link.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Event_ResourceUnpublished) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResourceUnpublished: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResourceUnpublished: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Link", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Link == nil { + m.Link = &ResourceLink{} + } + if err := m.Link.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Event_ResourceChanged) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResourceChanged: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResourceChanged: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceId", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ResourceId == nil { + m.ResourceId = &ResourceId{} + } + if err := m.ResourceId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Content == nil { + m.Content = &Content{} + } + if err := m.Content.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Event_OperationProcessed) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: OperationProcessed: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: OperationProcessed: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Token", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Token = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ErrorStatus", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ErrorStatus == nil { + m.ErrorStatus = &Event_OperationProcessed_ErrorStatus{} + } + if err := m.ErrorStatus.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Event_OperationProcessed_ErrorStatus) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ErrorStatus: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ErrorStatus: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Code", wireType) + } + m.Code = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Code |= Event_OperationProcessed_ErrorStatus_Code(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Message = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Event_SubscriptionCanceled) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SubscriptionCanceled: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SubscriptionCanceled: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Reason", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Reason = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LocalizedString) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LocalizedString: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LocalizedString: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Language", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Language = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Device) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Device: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Device: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Types", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Types = append(m.Types, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsOnline", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsOnline = bool(v != 0) + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ManufacturerName", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ManufacturerName = append(m.ManufacturerName, &LocalizedString{}) + if err := m.ManufacturerName[len(m.ManufacturerName)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ModelNumber", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ModelNumber = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResourceLink) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResourceLink: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResourceLink: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Href", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Href = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Types", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Types = append(m.Types, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Interfaces", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Interfaces = append(m.Interfaces, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Content) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Content: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Content: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContentType", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContentType = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDevices + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthDevices + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthDevices + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDevices(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDevices + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipDevices(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDevices + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDevices + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDevices + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthDevices + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupDevices + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthDevices + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthDevices = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowDevices = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupDevices = fmt.Errorf("proto: unexpected end of group") +) diff --git a/grpc-gateway/pb/devices.proto b/grpc-gateway/pb/devices.proto new file mode 100644 index 000000000..d316b3c30 --- /dev/null +++ b/grpc-gateway/pb/devices.proto @@ -0,0 +1,179 @@ +syntax = "proto3"; + +package ocf.cloud.grpcgateway.pb; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +option go_package = "github.com/go-ocf/cloud/grpc-gateway/pb;pb"; + +message GetDevicesRequest { + enum Status { + ONLINE = 0; + OFFLINE = 1; + } + + repeated string type_filter = 1; + repeated Status status_filter = 2; + repeated string device_ids_filter = 3; +} + +message GetResourceLinksRequest { + repeated string type_filter = 1; + repeated string device_ids_filter = 2; +} + +message ResourceId { + string device_id = 1; + string resource_link_href = 2; +} + +// RetrieveResourceFromDeviceRequest retrieve value from device +message RetrieveResourceFromDeviceRequest { + ResourceId resource_id = 1; + string resource_interface = 2; +} + +message RetrieveResourceFromDeviceResponse { + Content content = 1; +} + +// RetrieveResourcesValuesRequest get values from resource-shadow +message RetrieveResourcesValuesRequest { + repeated ResourceId resource_ids_filter = 1; + repeated string device_ids_filter = 2; + repeated string type_filter = 3; +} + +message ResourceValue { + ResourceId resource_id = 1; + repeated string types = 2; + Content content = 3; +} + +message UpdateResourceValuesRequest { + ResourceId resource_id = 1; + Content content = 2; + string resource_interface = 3; +} + +message UpdateResourceValuesResponse { + Content content = 1; +} + +message SubscribeForEvents { + message DevicesEventFilter { + enum Event { + REGISTERED = 0; + UNREGISTERED = 1; + ONLINE = 2; + OFFLINE = 3; + } + repeated Event filter_events = 1; + } + message DeviceEventFilter { + string device_id = 1; + enum Event { + RESOURCE_PUBLISHED = 0; + RESOURCE_UNPUBLISHED = 1; + } + repeated Event filter_events = 2; + } + message ResourceEventFilter { + ResourceId resource_id = 1; + enum Event { + CONTENT_CHANGED = 0; + } + repeated Event filter_events = 2; + } + message CancelSubscription { + string subscription_id = 1; + } + oneof filter_by { + DevicesEventFilter devices_event = 1; + DeviceEventFilter device_event = 2; + ResourceEventFilter resource_event = 3; + CancelSubscription cancel_subscription = 5; + } + string token = 101; // for pairing request SubscribeForEvents with Event.OperationProcessed +} + +message Event { + message DeviceRegistered { + string device_id = 1; + } + message DeviceUnregistered { + string device_id = 1; + } + message DeviceOnline { + string device_id = 1; + } + message DeviceOffline { + string device_id = 1; + } + message ResourcePublished { + ResourceLink link = 1; + } + message ResourceUnpublished { + ResourceLink link = 2; + } + message ResourceChanged { + ResourceId resource_id = 1; + Content content = 2; + } + message OperationProcessed { + message ErrorStatus { + enum Code { + OK = 0; + ERROR = 1; + NOT_FOUND = 2; + } + Code code = 1; + string message = 2; + } + string token = 1; + ErrorStatus error_status = 2; + } + message SubscriptionCanceled { + string reason = 2; + } + + string subscription_id = 1; // subscription id provided by grpc + oneof type { + DeviceRegistered device_registered = 3; + DeviceUnregistered device_unregistered = 4; + DeviceOnline device_online = 5; + DeviceOffline device_offline = 6; + ResourcePublished resource_published = 7; + ResourceUnpublished resource_unpublished = 8; + ResourceChanged resource_content_changed = 9; + OperationProcessed operation_processed = 10; + SubscriptionCanceled subscription_canceled = 11; + } + +} + +message LocalizedString { + string language = 1; + string value = 2; +} + +message Device { + string id = 1; + repeated string types = 2; + string name = 3; + bool is_online = 4; + repeated LocalizedString manufacturer_name = 5; + string model_number = 6; +} + +message ResourceLink { + string href = 1; + repeated string types = 2; + repeated string interfaces = 3; + string device_id = 4; +} + +message Content { + string content_type = 1; + bytes data = 2; +} \ No newline at end of file diff --git a/grpc-gateway/pb/errdetails/errorDetails.pb.go b/grpc-gateway/pb/errdetails/errorDetails.pb.go new file mode 100644 index 000000000..30941c601 --- /dev/null +++ b/grpc-gateway/pb/errdetails/errorDetails.pb.go @@ -0,0 +1,132 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: pb/errdetails/errorDetails.proto + +package errdetails + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type Content struct { + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + ContentType string `protobuf:"bytes,2,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Content) Reset() { *m = Content{} } +func (m *Content) String() string { return proto.CompactTextString(m) } +func (*Content) ProtoMessage() {} +func (*Content) Descriptor() ([]byte, []int) { + return fileDescriptor_dce8ee66439e2f64, []int{0} +} + +func (m *Content) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Content.Unmarshal(m, b) +} +func (m *Content) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Content.Marshal(b, m, deterministic) +} +func (m *Content) XXX_Merge(src proto.Message) { + xxx_messageInfo_Content.Merge(m, src) +} +func (m *Content) XXX_Size() int { + return xxx_messageInfo_Content.Size(m) +} +func (m *Content) XXX_DiscardUnknown() { + xxx_messageInfo_Content.DiscardUnknown(m) +} + +var xxx_messageInfo_Content proto.InternalMessageInfo + +func (m *Content) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func (m *Content) GetContentType() string { + if m != nil { + return m.ContentType + } + return "" +} + +type DeviceError struct { + Content *Content `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DeviceError) Reset() { *m = DeviceError{} } +func (m *DeviceError) String() string { return proto.CompactTextString(m) } +func (*DeviceError) ProtoMessage() {} +func (*DeviceError) Descriptor() ([]byte, []int) { + return fileDescriptor_dce8ee66439e2f64, []int{1} +} + +func (m *DeviceError) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DeviceError.Unmarshal(m, b) +} +func (m *DeviceError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DeviceError.Marshal(b, m, deterministic) +} +func (m *DeviceError) XXX_Merge(src proto.Message) { + xxx_messageInfo_DeviceError.Merge(m, src) +} +func (m *DeviceError) XXX_Size() int { + return xxx_messageInfo_DeviceError.Size(m) +} +func (m *DeviceError) XXX_DiscardUnknown() { + xxx_messageInfo_DeviceError.DiscardUnknown(m) +} + +var xxx_messageInfo_DeviceError proto.InternalMessageInfo + +func (m *DeviceError) GetContent() *Content { + if m != nil { + return m.Content + } + return nil +} + +func init() { + proto.RegisterType((*Content)(nil), "ocf.cloud.grpcgateway.pb.errdetails.Content") + proto.RegisterType((*DeviceError)(nil), "ocf.cloud.grpcgateway.pb.errdetails.DeviceError") +} + +func init() { proto.RegisterFile("pb/errdetails/errorDetails.proto", fileDescriptor_dce8ee66439e2f64) } + +var fileDescriptor_dce8ee66439e2f64 = []byte{ + // 214 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x28, 0x48, 0xd2, 0x4f, + 0x2d, 0x2a, 0x4a, 0x49, 0x2d, 0x49, 0xcc, 0xcc, 0x29, 0x06, 0x31, 0xf3, 0x8b, 0x5c, 0x20, 0x1c, + 0xbd, 0x82, 0xa2, 0xfc, 0x92, 0x7c, 0x21, 0xe5, 0xfc, 0xe4, 0x34, 0xbd, 0xe4, 0x9c, 0xfc, 0xd2, + 0x14, 0xbd, 0xf4, 0xa2, 0x82, 0xe4, 0xf4, 0xc4, 0x92, 0xd4, 0xf2, 0xc4, 0x4a, 0xbd, 0x82, 0x24, + 0x3d, 0x84, 0x3e, 0x25, 0x07, 0x2e, 0x76, 0xe7, 0xfc, 0xbc, 0x92, 0xd4, 0xbc, 0x12, 0x21, 0x21, + 0x2e, 0x96, 0x94, 0xc4, 0x92, 0x44, 0x09, 0x46, 0x05, 0x46, 0x0d, 0x9e, 0x20, 0x30, 0x5b, 0x48, + 0x91, 0x8b, 0x27, 0x19, 0x22, 0x1d, 0x5f, 0x52, 0x59, 0x90, 0x2a, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, + 0x19, 0xc4, 0x0d, 0x15, 0x0b, 0xa9, 0x2c, 0x48, 0x55, 0x0a, 0xe5, 0xe2, 0x76, 0x49, 0x2d, 0xcb, + 0x4c, 0x4e, 0x75, 0x05, 0x39, 0x41, 0xc8, 0x8d, 0x8b, 0x1d, 0x2a, 0x0b, 0x56, 0xcc, 0x6d, 0xa4, + 0xa3, 0x47, 0x84, 0x3b, 0xf4, 0xa0, 0x8e, 0x08, 0x82, 0x69, 0x76, 0x72, 0x8e, 0x72, 0x4c, 0xcf, + 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0xd7, 0xcd, 0x4f, 0x4e, 0xd3, + 0xcf, 0x4f, 0x4e, 0xd3, 0x05, 0x9b, 0xa4, 0x0f, 0x32, 0x49, 0x17, 0x6a, 0x94, 0x3e, 0x4a, 0x50, + 0x58, 0x23, 0x98, 0x49, 0x6c, 0xe0, 0x90, 0x30, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0x48, 0x5f, + 0x96, 0x84, 0x2d, 0x01, 0x00, 0x00, +} diff --git a/grpc-gateway/pb/errdetails/errorDetails.proto b/grpc-gateway/pb/errdetails/errorDetails.proto new file mode 100644 index 000000000..25b446c5d --- /dev/null +++ b/grpc-gateway/pb/errdetails/errorDetails.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package ocf.cloud.grpcgateway.pb.errdetails; +option go_package = "github.com/go-ocf/cloud/grpc-gateway/pb/errdetails;errdetails"; + +// must be generated with without gogo protobuf because it is used in grpc status details. + +message Content { + bytes data = 1; + string content_type = 2; +} + +message DeviceError { + Content content = 2; +} \ No newline at end of file diff --git a/grpc-gateway/pb/service.pb.go b/grpc-gateway/pb/service.pb.go new file mode 100644 index 000000000..f533099e9 --- /dev/null +++ b/grpc-gateway/pb/service.pb.go @@ -0,0 +1,438 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: pb/service.proto + +package pb + +import ( + context "context" + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +func init() { proto.RegisterFile("pb/service.proto", fileDescriptor_6ff5ab49d8a5fcc4) } + +var fileDescriptor_6ff5ab49d8a5fcc4 = []byte{ + // 348 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x93, 0x3d, 0x4f, 0xc3, 0x30, + 0x10, 0x86, 0x9b, 0x01, 0x06, 0xb3, 0x54, 0x96, 0x10, 0x28, 0x0b, 0xa8, 0x03, 0x20, 0x41, 0xec, + 0x02, 0x02, 0x81, 0xca, 0x84, 0xa0, 0x59, 0x98, 0x8a, 0x60, 0x60, 0x41, 0xb1, 0x7b, 0x09, 0x11, + 0x6d, 0xed, 0xfa, 0x23, 0x88, 0x8d, 0x91, 0xbf, 0xc0, 0x1f, 0x65, 0x46, 0x75, 0x1a, 0x20, 0x54, + 0x2e, 0x94, 0x2d, 0x56, 0x9e, 0xe7, 0xbd, 0x57, 0x27, 0x1d, 0x6a, 0x4a, 0x46, 0x35, 0xa8, 0x22, + 0xe7, 0x40, 0xa4, 0x12, 0x46, 0xe0, 0x75, 0xc1, 0x53, 0xc2, 0x07, 0xc2, 0xf6, 0x49, 0xa6, 0x24, + 0xcf, 0x12, 0x03, 0x4f, 0xc9, 0x33, 0x91, 0x2c, 0x8c, 0xb2, 0xdc, 0x3c, 0x58, 0x46, 0xb8, 0x18, + 0xd2, 0x4c, 0x64, 0x82, 0x3a, 0x81, 0xd9, 0xd4, 0xbd, 0xdc, 0xc3, 0x7d, 0x95, 0x41, 0xe1, 0x69, + 0x0d, 0x8f, 0x04, 0x4f, 0xa9, 0xe0, 0x69, 0xe4, 0xa2, 0xe9, 0x24, 0x3a, 0x9a, 0x66, 0x53, 0xc9, + 0x68, 0x1f, 0x26, 0x15, 0x74, 0xa9, 0x1e, 0xbc, 0x2f, 0xa1, 0x95, 0x58, 0x49, 0x1e, 0x97, 0x00, + 0xbe, 0x47, 0x28, 0x06, 0x73, 0x51, 0x32, 0x78, 0x97, 0xf8, 0x2a, 0x92, 0x2f, 0xaa, 0x07, 0x63, + 0x0b, 0xda, 0x84, 0x9b, 0x7e, 0xb8, 0x24, 0x5b, 0x8d, 0x76, 0x80, 0xc7, 0xa8, 0x19, 0x83, 0xe9, + 0x81, 0x16, 0x56, 0x71, 0xb8, 0xca, 0x47, 0x8f, 0x1a, 0xef, 0xcf, 0x1d, 0x53, 0x63, 0xab, 0x61, + 0x5b, 0x7e, 0xe5, 0x3b, 0xef, 0x46, 0xbe, 0x05, 0x28, 0xec, 0x81, 0x51, 0x39, 0x14, 0x50, 0xfd, + 0xec, 0x2a, 0x31, 0x2c, 0x5b, 0xe1, 0xce, 0xbc, 0x28, 0x9f, 0x55, 0xf5, 0x38, 0xfb, 0x9f, 0xac, + 0xa5, 0x18, 0x69, 0x68, 0x35, 0xf0, 0x4b, 0x80, 0xd6, 0x7e, 0x82, 0xfa, 0x36, 0x19, 0x58, 0xd0, + 0xf8, 0xe4, 0xef, 0xd9, 0x53, 0xa5, 0x6a, 0xb5, 0xfd, 0xfb, 0x76, 0x9c, 0xe0, 0xd6, 0xf3, 0x1a, + 0xa0, 0xd5, 0x1b, 0xd9, 0x4f, 0xcc, 0x4c, 0x81, 0x23, 0x7f, 0x4c, 0x5d, 0xa8, 0x4f, 0x3f, 0x5e, + 0x54, 0xfb, 0xdc, 0x46, 0x8e, 0xf0, 0xb5, 0x65, 0x9a, 0xab, 0x9c, 0x41, 0x57, 0xa8, 0xcb, 0x02, + 0x46, 0x46, 0xe3, 0x3d, 0x7f, 0xde, 0x2c, 0x1d, 0x6e, 0xf8, 0x69, 0x47, 0xb4, 0x1a, 0x3b, 0x41, + 0x3b, 0x38, 0x6f, 0xdf, 0x91, 0x05, 0xae, 0xa6, 0x23, 0x19, 0x5b, 0x76, 0x17, 0x73, 0xf8, 0x11, + 0x00, 0x00, 0xff, 0xff, 0x87, 0x6e, 0xa2, 0x9b, 0xc9, 0x03, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// GrpcGatewayClient is the client API for GrpcGateway service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type GrpcGatewayClient interface { + // Get all devices + GetDevices(ctx context.Context, in *GetDevicesRequest, opts ...grpc.CallOption) (GrpcGateway_GetDevicesClient, error) + // Get resource links of devices. + GetResourceLinks(ctx context.Context, in *GetResourceLinksRequest, opts ...grpc.CallOption) (GrpcGateway_GetResourceLinksClient, error) + RetrieveResourceFromDevice(ctx context.Context, in *RetrieveResourceFromDeviceRequest, opts ...grpc.CallOption) (*RetrieveResourceFromDeviceResponse, error) + // Retrieve resources values from resource shadow + RetrieveResourcesValues(ctx context.Context, in *RetrieveResourcesValuesRequest, opts ...grpc.CallOption) (GrpcGateway_RetrieveResourcesValuesClient, error) + // Update resource values + UpdateResourcesValues(ctx context.Context, in *UpdateResourceValuesRequest, opts ...grpc.CallOption) (*UpdateResourceValuesResponse, error) + // Subscribe to events + SubscribeForEvents(ctx context.Context, opts ...grpc.CallOption) (GrpcGateway_SubscribeForEventsClient, error) +} + +type grpcGatewayClient struct { + cc *grpc.ClientConn +} + +func NewGrpcGatewayClient(cc *grpc.ClientConn) GrpcGatewayClient { + return &grpcGatewayClient{cc} +} + +func (c *grpcGatewayClient) GetDevices(ctx context.Context, in *GetDevicesRequest, opts ...grpc.CallOption) (GrpcGateway_GetDevicesClient, error) { + stream, err := c.cc.NewStream(ctx, &_GrpcGateway_serviceDesc.Streams[0], "/ocf.cloud.grpcgateway.pb.GrpcGateway/GetDevices", opts...) + if err != nil { + return nil, err + } + x := &grpcGatewayGetDevicesClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type GrpcGateway_GetDevicesClient interface { + Recv() (*Device, error) + grpc.ClientStream +} + +type grpcGatewayGetDevicesClient struct { + grpc.ClientStream +} + +func (x *grpcGatewayGetDevicesClient) Recv() (*Device, error) { + m := new(Device) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *grpcGatewayClient) GetResourceLinks(ctx context.Context, in *GetResourceLinksRequest, opts ...grpc.CallOption) (GrpcGateway_GetResourceLinksClient, error) { + stream, err := c.cc.NewStream(ctx, &_GrpcGateway_serviceDesc.Streams[1], "/ocf.cloud.grpcgateway.pb.GrpcGateway/GetResourceLinks", opts...) + if err != nil { + return nil, err + } + x := &grpcGatewayGetResourceLinksClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type GrpcGateway_GetResourceLinksClient interface { + Recv() (*ResourceLink, error) + grpc.ClientStream +} + +type grpcGatewayGetResourceLinksClient struct { + grpc.ClientStream +} + +func (x *grpcGatewayGetResourceLinksClient) Recv() (*ResourceLink, error) { + m := new(ResourceLink) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *grpcGatewayClient) RetrieveResourceFromDevice(ctx context.Context, in *RetrieveResourceFromDeviceRequest, opts ...grpc.CallOption) (*RetrieveResourceFromDeviceResponse, error) { + out := new(RetrieveResourceFromDeviceResponse) + err := c.cc.Invoke(ctx, "/ocf.cloud.grpcgateway.pb.GrpcGateway/RetrieveResourceFromDevice", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *grpcGatewayClient) RetrieveResourcesValues(ctx context.Context, in *RetrieveResourcesValuesRequest, opts ...grpc.CallOption) (GrpcGateway_RetrieveResourcesValuesClient, error) { + stream, err := c.cc.NewStream(ctx, &_GrpcGateway_serviceDesc.Streams[2], "/ocf.cloud.grpcgateway.pb.GrpcGateway/RetrieveResourcesValues", opts...) + if err != nil { + return nil, err + } + x := &grpcGatewayRetrieveResourcesValuesClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type GrpcGateway_RetrieveResourcesValuesClient interface { + Recv() (*ResourceValue, error) + grpc.ClientStream +} + +type grpcGatewayRetrieveResourcesValuesClient struct { + grpc.ClientStream +} + +func (x *grpcGatewayRetrieveResourcesValuesClient) Recv() (*ResourceValue, error) { + m := new(ResourceValue) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +func (c *grpcGatewayClient) UpdateResourcesValues(ctx context.Context, in *UpdateResourceValuesRequest, opts ...grpc.CallOption) (*UpdateResourceValuesResponse, error) { + out := new(UpdateResourceValuesResponse) + err := c.cc.Invoke(ctx, "/ocf.cloud.grpcgateway.pb.GrpcGateway/UpdateResourcesValues", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *grpcGatewayClient) SubscribeForEvents(ctx context.Context, opts ...grpc.CallOption) (GrpcGateway_SubscribeForEventsClient, error) { + stream, err := c.cc.NewStream(ctx, &_GrpcGateway_serviceDesc.Streams[3], "/ocf.cloud.grpcgateway.pb.GrpcGateway/SubscribeForEvents", opts...) + if err != nil { + return nil, err + } + x := &grpcGatewaySubscribeForEventsClient{stream} + return x, nil +} + +type GrpcGateway_SubscribeForEventsClient interface { + Send(*SubscribeForEvents) error + Recv() (*Event, error) + grpc.ClientStream +} + +type grpcGatewaySubscribeForEventsClient struct { + grpc.ClientStream +} + +func (x *grpcGatewaySubscribeForEventsClient) Send(m *SubscribeForEvents) error { + return x.ClientStream.SendMsg(m) +} + +func (x *grpcGatewaySubscribeForEventsClient) Recv() (*Event, error) { + m := new(Event) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// GrpcGatewayServer is the server API for GrpcGateway service. +type GrpcGatewayServer interface { + // Get all devices + GetDevices(*GetDevicesRequest, GrpcGateway_GetDevicesServer) error + // Get resource links of devices. + GetResourceLinks(*GetResourceLinksRequest, GrpcGateway_GetResourceLinksServer) error + RetrieveResourceFromDevice(context.Context, *RetrieveResourceFromDeviceRequest) (*RetrieveResourceFromDeviceResponse, error) + // Retrieve resources values from resource shadow + RetrieveResourcesValues(*RetrieveResourcesValuesRequest, GrpcGateway_RetrieveResourcesValuesServer) error + // Update resource values + UpdateResourcesValues(context.Context, *UpdateResourceValuesRequest) (*UpdateResourceValuesResponse, error) + // Subscribe to events + SubscribeForEvents(GrpcGateway_SubscribeForEventsServer) error +} + +// UnimplementedGrpcGatewayServer can be embedded to have forward compatible implementations. +type UnimplementedGrpcGatewayServer struct { +} + +func (*UnimplementedGrpcGatewayServer) GetDevices(req *GetDevicesRequest, srv GrpcGateway_GetDevicesServer) error { + return status.Errorf(codes.Unimplemented, "method GetDevices not implemented") +} +func (*UnimplementedGrpcGatewayServer) GetResourceLinks(req *GetResourceLinksRequest, srv GrpcGateway_GetResourceLinksServer) error { + return status.Errorf(codes.Unimplemented, "method GetResourceLinks not implemented") +} +func (*UnimplementedGrpcGatewayServer) RetrieveResourceFromDevice(ctx context.Context, req *RetrieveResourceFromDeviceRequest) (*RetrieveResourceFromDeviceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RetrieveResourceFromDevice not implemented") +} +func (*UnimplementedGrpcGatewayServer) RetrieveResourcesValues(req *RetrieveResourcesValuesRequest, srv GrpcGateway_RetrieveResourcesValuesServer) error { + return status.Errorf(codes.Unimplemented, "method RetrieveResourcesValues not implemented") +} +func (*UnimplementedGrpcGatewayServer) UpdateResourcesValues(ctx context.Context, req *UpdateResourceValuesRequest) (*UpdateResourceValuesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateResourcesValues not implemented") +} +func (*UnimplementedGrpcGatewayServer) SubscribeForEvents(srv GrpcGateway_SubscribeForEventsServer) error { + return status.Errorf(codes.Unimplemented, "method SubscribeForEvents not implemented") +} + +func RegisterGrpcGatewayServer(s *grpc.Server, srv GrpcGatewayServer) { + s.RegisterService(&_GrpcGateway_serviceDesc, srv) +} + +func _GrpcGateway_GetDevices_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(GetDevicesRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GrpcGatewayServer).GetDevices(m, &grpcGatewayGetDevicesServer{stream}) +} + +type GrpcGateway_GetDevicesServer interface { + Send(*Device) error + grpc.ServerStream +} + +type grpcGatewayGetDevicesServer struct { + grpc.ServerStream +} + +func (x *grpcGatewayGetDevicesServer) Send(m *Device) error { + return x.ServerStream.SendMsg(m) +} + +func _GrpcGateway_GetResourceLinks_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(GetResourceLinksRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GrpcGatewayServer).GetResourceLinks(m, &grpcGatewayGetResourceLinksServer{stream}) +} + +type GrpcGateway_GetResourceLinksServer interface { + Send(*ResourceLink) error + grpc.ServerStream +} + +type grpcGatewayGetResourceLinksServer struct { + grpc.ServerStream +} + +func (x *grpcGatewayGetResourceLinksServer) Send(m *ResourceLink) error { + return x.ServerStream.SendMsg(m) +} + +func _GrpcGateway_RetrieveResourceFromDevice_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RetrieveResourceFromDeviceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GrpcGatewayServer).RetrieveResourceFromDevice(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocf.cloud.grpcgateway.pb.GrpcGateway/RetrieveResourceFromDevice", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GrpcGatewayServer).RetrieveResourceFromDevice(ctx, req.(*RetrieveResourceFromDeviceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GrpcGateway_RetrieveResourcesValues_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(RetrieveResourcesValuesRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GrpcGatewayServer).RetrieveResourcesValues(m, &grpcGatewayRetrieveResourcesValuesServer{stream}) +} + +type GrpcGateway_RetrieveResourcesValuesServer interface { + Send(*ResourceValue) error + grpc.ServerStream +} + +type grpcGatewayRetrieveResourcesValuesServer struct { + grpc.ServerStream +} + +func (x *grpcGatewayRetrieveResourcesValuesServer) Send(m *ResourceValue) error { + return x.ServerStream.SendMsg(m) +} + +func _GrpcGateway_UpdateResourcesValues_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateResourceValuesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GrpcGatewayServer).UpdateResourcesValues(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocf.cloud.grpcgateway.pb.GrpcGateway/UpdateResourcesValues", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GrpcGatewayServer).UpdateResourcesValues(ctx, req.(*UpdateResourceValuesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _GrpcGateway_SubscribeForEvents_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(GrpcGatewayServer).SubscribeForEvents(&grpcGatewaySubscribeForEventsServer{stream}) +} + +type GrpcGateway_SubscribeForEventsServer interface { + Send(*Event) error + Recv() (*SubscribeForEvents, error) + grpc.ServerStream +} + +type grpcGatewaySubscribeForEventsServer struct { + grpc.ServerStream +} + +func (x *grpcGatewaySubscribeForEventsServer) Send(m *Event) error { + return x.ServerStream.SendMsg(m) +} + +func (x *grpcGatewaySubscribeForEventsServer) Recv() (*SubscribeForEvents, error) { + m := new(SubscribeForEvents) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +var _GrpcGateway_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ocf.cloud.grpcgateway.pb.GrpcGateway", + HandlerType: (*GrpcGatewayServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "RetrieveResourceFromDevice", + Handler: _GrpcGateway_RetrieveResourceFromDevice_Handler, + }, + { + MethodName: "UpdateResourcesValues", + Handler: _GrpcGateway_UpdateResourcesValues_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "GetDevices", + Handler: _GrpcGateway_GetDevices_Handler, + ServerStreams: true, + }, + { + StreamName: "GetResourceLinks", + Handler: _GrpcGateway_GetResourceLinks_Handler, + ServerStreams: true, + }, + { + StreamName: "RetrieveResourcesValues", + Handler: _GrpcGateway_RetrieveResourcesValues_Handler, + ServerStreams: true, + }, + { + StreamName: "SubscribeForEvents", + Handler: _GrpcGateway_SubscribeForEvents_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "pb/service.proto", +} diff --git a/grpc-gateway/pb/service.proto b/grpc-gateway/pb/service.proto new file mode 100644 index 000000000..9a7ae28c5 --- /dev/null +++ b/grpc-gateway/pb/service.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; + +package ocf.cloud.grpcgateway.pb; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/go-ocf/cloud/grpc-gateway/pb/devices.proto"; + +option go_package = "github.com/go-ocf/cloud/grpc-gateway/pb;pb"; + +service GrpcGateway { + // Get all devices + rpc GetDevices (GetDevicesRequest) returns (stream Device) {} + // Get resource links of devices. + rpc GetResourceLinks(GetResourceLinksRequest) returns (stream ResourceLink) {} + + rpc RetrieveResourceFromDevice(RetrieveResourceFromDeviceRequest) returns (RetrieveResourceFromDeviceResponse) {} + + // Retrieve resources values from resource shadow + rpc RetrieveResourcesValues(RetrieveResourcesValuesRequest) returns (stream ResourceValue) {} + // Update resource values + rpc UpdateResourcesValues(UpdateResourceValuesRequest) returns (UpdateResourceValuesResponse) {} + + // Subscribe to events + rpc SubscribeForEvents(stream ocf.cloud.grpcgateway.pb.SubscribeForEvents) returns (stream Event) {} +} \ No newline at end of file diff --git a/grpc-gateway/refImpl/refImpl.go b/grpc-gateway/refImpl/refImpl.go new file mode 100644 index 000000000..831a8a180 --- /dev/null +++ b/grpc-gateway/refImpl/refImpl.go @@ -0,0 +1,78 @@ +package refImpl + +import ( + "context" + "crypto/tls" + "encoding/json" + "fmt" + + "github.com/go-ocf/kit/security/certManager" + + "google.golang.org/grpc" + + "github.com/go-ocf/kit/log" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/kit/security/jwt" + "github.com/go-ocf/cloud/grpc-gateway/service" + "google.golang.org/grpc/credentials" +) + +type Config struct { + Log log.Config + JwksURL string `envconfig:"JWKS_URL"` + Listen certManager.Config `envconfig:"LISTEN"` + Dial certManager.Config `envconfig:"DIAL"` + service.HandlerConfig +} + +//String return string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} + +func Init(config Config) (*kitNetGrpc.Server, error) { + log.Setup(config.Log) + log.Info(config.String()) + + listenCertManager, err := certManager.NewCertManager(config.Listen) + if err != nil { + return nil, fmt.Errorf("cannot create server cert manager %w", err) + } + dialCertManager, err := certManager.NewCertManager(config.Dial) + if err != nil { + return nil, fmt.Errorf("cannot create client cert manager %w", err) + } + + auth := NewAuth(config.JwksURL, dialCertManager.GetClientTLSConfig(), "openid") + + serverTLSConfig := listenCertManager.GetServerTLSConfig() + server, err := kitNetGrpc.NewServer(config.Service.Addr, grpc.Creds(credentials.NewTLS(&serverTLSConfig)), auth.Stream(), auth.Unary()) + if err != nil { + return nil, err + } + server.AddCloseFunc(func() { + listenCertManager.Close() + dialCertManager.Close() + }) + + if err := service.AddHandler(server, config.HandlerConfig, dialCertManager.GetClientTLSConfig()); err != nil { + return nil, err + } + + return server, nil +} + +func NewAuth(jwksUrl string, tls tls.Config, scope string) kitNetGrpc.AuthInterceptors { + return kitNetGrpc.MakeAuthInterceptors(func(ctx context.Context, method string) (context.Context, error) { + interceptor := kitNetGrpc.ValidateJWT(jwksUrl, tls, func(ctx context.Context, method string) kitNetGrpc.Claims { + return jwt.NewScopeClaims(scope) + }) + ctx, err := interceptor(ctx, method) + if err != nil { + log.Errorf("auth interceptor: %v", err) + } + return ctx, err + }) + +} diff --git a/grpc-gateway/refImpl/refImpl_test.go b/grpc-gateway/refImpl/refImpl_test.go new file mode 100644 index 000000000..afd545d87 --- /dev/null +++ b/grpc-gateway/refImpl/refImpl_test.go @@ -0,0 +1,32 @@ +package refImpl_test + +import ( + "testing" + + authConfig "github.com/go-ocf/cloud/authorization/service" + authService "github.com/go-ocf/cloud/authorization/test/service" + "github.com/go-ocf/cloud/grpc-gateway/refImpl" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/require" +) + +func TestInit(t *testing.T) { + var authCfg authConfig.Config + err := envconfig.Process("", &authCfg) + require.NoError(t, err) + authCfg.Addr = "localhost:12345" + authCfg.HTTPAddr = "localhost:12346" + authCfg.Device.Provider = "test" + authShutdown := authService.NewAuthServer(t, authCfg) + defer authShutdown() + + var config refImpl.Config + err = envconfig.Process("", &config) + require.NoError(t, err) + config.Service.OAuth.Endpoint.TokenURL = "https://" + authCfg.HTTPAddr + "/api/authz/token" + + got, err := refImpl.Init(config) + require.NoError(t, err) + require.NotEmpty(t, got) + defer got.Close() +} diff --git a/grpc-gateway/service/config.go b/grpc-gateway/service/config.go new file mode 100644 index 000000000..b725a8d78 --- /dev/null +++ b/grpc-gateway/service/config.go @@ -0,0 +1,27 @@ +package service + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/kit/security/oauth/manager" +) + +// Config represent application configuration +type Config struct { + grpc.Config + OAuth manager.Config `envconfig:"OAUTH"` + AuthServerAddr string `envconfig:"AUTH_SERVER_ADDRESS" default:"127.0.0.1:9100"` + ResourceAggregateAddr string `envconfig:"RESOURCE_AGGREGATE_ADDRESS" default:"127.0.0.1:9100"` + ResourceDirectoryAddr string `envconfig:"RESOURCE_DIRECTORY_ADDRESS" default:"127.0.0.1:9100"` + FQDN string `envconfig:"FQDN" default:"grpcgw.ocf.cloud"` + TimeoutForRequests time.Duration `envconfig:"TIMEOUT_FOR_REQUESTS" default:"10s"` +} + +//String return string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} diff --git a/grpc-gateway/service/deviceSubscription.go b/grpc-gateway/service/deviceSubscription.go new file mode 100644 index 000000000..4810b4998 --- /dev/null +++ b/grpc-gateway/service/deviceSubscription.go @@ -0,0 +1,125 @@ +package service + +import ( + "context" + "fmt" + + "github.com/go-ocf/cloud/grpc-gateway/pb" + "github.com/go-ocf/kit/log" + projectionRA "github.com/go-ocf/cloud/resource-aggregate/cqrs/projection" +) + +type deviceSubscription struct { + *subscription + deviceEvent *pb.SubscribeForEvents_DeviceEventFilter +} + +func NewDeviceSubscription(id, userID string, send SendEventFunc, resourceProjection *projectionRA.Projection, deviceEvent *pb.SubscribeForEvents_DeviceEventFilter) *deviceSubscription { + log.Debugf("subscription.NewDeviceSubscription %v", id) + defer log.Debugf("subscription.NewDeviceSubscription %v done", id) + return &deviceSubscription{ + subscription: NewSubscription(userID, id, send, resourceProjection), + deviceEvent: deviceEvent, + } +} + +func (s *deviceSubscription) DeviceID() string { + return s.deviceEvent.GetDeviceId() +} + +func (s *deviceSubscription) NotifyOfPublishedResource(ctx context.Context, link pb.ResourceLink, version uint64) error { + if s.FilterByVersion(link.GetDeviceId(), link.GetHref(), "res", version) { + return nil + } + var found bool + for _, f := range s.deviceEvent.GetFilterEvents() { + if f == pb.SubscribeForEvents_DeviceEventFilter_RESOURCE_PUBLISHED { + found = true + } + } + if !found { + return nil + } + return s.Send(ctx, pb.Event{ + SubscriptionId: s.ID(), + Type: &pb.Event_ResourcePublished_{ + ResourcePublished: &pb.Event_ResourcePublished{ + Link: &link, + }, + }, + }) +} + +func (s *deviceSubscription) NotifyOfUnpublishedResource(ctx context.Context, link pb.ResourceLink, version uint64) error { + if s.FilterByVersion(link.GetDeviceId(), link.GetHref(), "res", version) { + return nil + } + var found bool + for _, f := range s.deviceEvent.GetFilterEvents() { + if f == pb.SubscribeForEvents_DeviceEventFilter_RESOURCE_UNPUBLISHED { + found = true + } + } + if !found { + return nil + } + return s.Send(ctx, pb.Event{ + SubscriptionId: s.ID(), + Type: &pb.Event_ResourceUnpublished_{ + ResourceUnpublished: &pb.Event_ResourceUnpublished{ + Link: &link, + }, + }, + }) +} + +func (s *deviceSubscription) initSendResourcesPublished(ctx context.Context) error { + models := s.resourceProjection.Models(s.DeviceID(), "") + for _, model := range models { + link, version, ok := makeLinkRepresentation(pb.SubscribeForEvents_DeviceEventFilter_RESOURCE_PUBLISHED, model) + if !ok { + continue + } + err := s.NotifyOfPublishedResource(ctx, link, version) + if err != nil { + return fmt.Errorf("cannot send resource published: %w", err) + } + } + return nil +} + +func (s *deviceSubscription) initSendResourcesUnpublished(ctx context.Context) error { + models := s.resourceProjection.Models(s.DeviceID(), "") + for _, model := range models { + link, version, ok := makeLinkRepresentation(pb.SubscribeForEvents_DeviceEventFilter_RESOURCE_UNPUBLISHED, model) + if !ok { + continue + } + err := s.NotifyOfUnpublishedResource(ctx, link, version) + if err != nil { + return fmt.Errorf("cannot send resource published: %w", err) + } + } + return nil +} + +func (s *deviceSubscription) Init(ctx context.Context, currentDevices map[string]bool) error { + if !currentDevices[s.DeviceID()] { + return fmt.Errorf("device %v not found", s.DeviceID()) + } + _, err := s.RegisterToProjection(ctx, s.DeviceID()) + if err != nil { + return fmt.Errorf("cannot register to resource projection: %w", err) + } + + for _, f := range s.deviceEvent.GetFilterEvents() { + switch f { + case pb.SubscribeForEvents_DeviceEventFilter_RESOURCE_PUBLISHED: + err = s.initSendResourcesPublished(ctx) + case pb.SubscribeForEvents_DeviceEventFilter_RESOURCE_UNPUBLISHED: + err = s.initSendResourcesUnpublished(ctx) + } + return err + } + return nil +} diff --git a/grpc-gateway/service/devicesSubscription.go b/grpc-gateway/service/devicesSubscription.go new file mode 100644 index 000000000..a4da67790 --- /dev/null +++ b/grpc-gateway/service/devicesSubscription.go @@ -0,0 +1,198 @@ +package service + +import ( + "context" + "fmt" + + "github.com/go-ocf/cloud/grpc-gateway/pb" + "github.com/go-ocf/kit/log" + cqrsRA "github.com/go-ocf/cloud/resource-aggregate/cqrs" + projectionRA "github.com/go-ocf/cloud/resource-aggregate/cqrs/projection" + "github.com/go-ocf/sdk/schema/cloud" +) + +type devicesSubscription struct { + *subscription + devicesEvent *pb.SubscribeForEvents_DevicesEventFilter +} + +func NewDevicesSubscription(id, userID string, send SendEventFunc, resourceProjection *projectionRA.Projection, devicesEvent *pb.SubscribeForEvents_DevicesEventFilter) *devicesSubscription { + log.Debugf("subscription.NewDevicesSubscription %v", id) + defer log.Debugf("subscription.NewDevicesSubscription %v done", id) + return &devicesSubscription{ + subscription: NewSubscription(userID, id, send, resourceProjection), + devicesEvent: devicesEvent, + } +} + +func (s *devicesSubscription) Init(ctx context.Context, currentDevices map[string]bool) error { + for deviceID := range currentDevices { + var notifyRegistered, notifyOnline, notifyOffline bool + for _, f := range s.devicesEvent.GetFilterEvents() { + switch f { + case pb.SubscribeForEvents_DevicesEventFilter_REGISTERED: + notifyRegistered = true + case pb.SubscribeForEvents_DevicesEventFilter_ONLINE: + notifyOnline = true + case pb.SubscribeForEvents_DevicesEventFilter_OFFLINE: + notifyOffline = true + } + } + if notifyRegistered { + err := s.NotifyOfRegisteredDevice(ctx, deviceID) + if err != nil { + return err + } + } + _, err := s.RegisterToProjection(ctx, deviceID) + if err != nil { + log.Errorf("cannot register to resource projection for %v: %v", deviceID, err) + continue + } + if notifyOnline { + err = s.initNotifyOfOnlineDevice(ctx, deviceID) + if err != nil { + return err + } + } + if notifyOffline { + err = s.initNotifyOfOfflineDevice(ctx, deviceID) + if err != nil { + return err + } + } + } + + return nil +} + +func (s *devicesSubscription) Update(ctx context.Context, addedDevices, removedDevices map[string]bool) error { + for deviceID := range removedDevices { + err := s.UnregisterFromProjection(ctx, deviceID) + if err != nil { + log.Errorf("cannot unregister resource from projection for %v: %v", deviceID, err) + } + for _, f := range s.devicesEvent.GetFilterEvents() { + switch f { + case pb.SubscribeForEvents_DevicesEventFilter_UNREGISTERED: + err = s.NotifyOfUnregisteredDevice(ctx, deviceID) + if err != nil { + return fmt.Errorf("cannot send device unregistered: %w", err) + } + } + } + } + return s.Init(ctx, addedDevices) +} + +func (s *devicesSubscription) NotifyOfRegisteredDevice(ctx context.Context, deviceID string) error { + return s.Send(ctx, pb.Event{ + SubscriptionId: s.ID(), + Type: &pb.Event_DeviceRegistered_{ + DeviceRegistered: &pb.Event_DeviceRegistered{ + DeviceId: deviceID, + }, + }, + }) +} + +func (s *devicesSubscription) NotifyOfUnregisteredDevice(ctx context.Context, deviceID string) error { + return s.Send(ctx, pb.Event{ + SubscriptionId: s.ID(), + Type: &pb.Event_DeviceUnregistered_{ + DeviceUnregistered: &pb.Event_DeviceUnregistered{ + DeviceId: deviceID, + }, + }, + }) +} + +func (s *devicesSubscription) NotifyOfOnlineDevice(ctx context.Context, deviceID string, version uint64) error { + if s.FilterByVersion(deviceID, cloud.StatusHref, "devStatus", version) { + return nil + } + var found bool + for _, f := range s.devicesEvent.GetFilterEvents() { + if f == pb.SubscribeForEvents_DevicesEventFilter_ONLINE { + found = true + } + } + if !found { + return nil + } + return s.Send(ctx, pb.Event{ + SubscriptionId: s.ID(), + Type: &pb.Event_DeviceOnline_{ + DeviceOnline: &pb.Event_DeviceOnline{ + DeviceId: deviceID, + }, + }, + }) +} + +func (s *devicesSubscription) NotifyOfOfflineDevice(ctx context.Context, deviceID string, version uint64) error { + if s.FilterByVersion(deviceID, cloud.StatusHref, "devStatus", version) { + return nil + } + var found bool + for _, f := range s.devicesEvent.GetFilterEvents() { + if f == pb.SubscribeForEvents_DevicesEventFilter_OFFLINE { + found = true + } + } + if !found { + return nil + } + return s.Send(ctx, pb.Event{ + SubscriptionId: s.ID(), + Type: &pb.Event_DeviceOffline_{ + DeviceOffline: &pb.Event_DeviceOffline{ + DeviceId: deviceID, + }, + }, + }) +} + +func (s *devicesSubscription) initNotifyOfOnlineDevice(ctx context.Context, deviceID string) error { + cloudResourceID := cqrsRA.MakeResourceId(deviceID, cloud.StatusHref) + models := s.resourceProjection.Models(deviceID, cloudResourceID) + if len(models) == 0 { + return nil + } + res := models[0].(*resourceCtx).Clone() + online, err := isDeviceOnline(res.content.GetContent()) + if err != nil { + return fmt.Errorf("cannot determine device cloud status: %w", err) + + } + if !online { + return nil + } + err = s.NotifyOfOnlineDevice(ctx, deviceID, res.onResourceChangedVersion) + if err != nil { + return fmt.Errorf("cannot send device online: %w", err) + } + return nil +} + +func (s *devicesSubscription) initNotifyOfOfflineDevice(ctx context.Context, deviceID string) error { + cloudResourceID := cqrsRA.MakeResourceId(deviceID, cloud.StatusHref) + models := s.resourceProjection.Models(deviceID, cloudResourceID) + if len(models) == 0 { + return nil + } + res := models[0].(*resourceCtx).Clone() + online, err := isDeviceOnline(res.content.GetContent()) + if err != nil { + return fmt.Errorf("cannot determine device cloud status: %w", err) + + } + if online { + return nil + } + err = s.NotifyOfOfflineDevice(ctx, deviceID, res.onResourceChangedVersion) + if err != nil { + return fmt.Errorf("cannot send device offline: %w", err) + } + return nil +} diff --git a/grpc-gateway/service/grpcApi.go b/grpc-gateway/service/grpcApi.go new file mode 100644 index 000000000..bd5937931 --- /dev/null +++ b/grpc-gateway/service/grpcApi.go @@ -0,0 +1,364 @@ +package service + +import ( + "context" + "crypto/tls" + "fmt" + "io" + "time" + + clientAS "github.com/go-ocf/cloud/authorization/client" + pbAS "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/cloud/grpc-gateway/pb" + "github.com/go-ocf/kit/log" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + cqrsRA "github.com/go-ocf/cloud/resource-aggregate/cqrs" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventbus/nats" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/mongodb" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/notification" + projectionRA "github.com/go-ocf/cloud/resource-aggregate/cqrs/projection" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + pbDD "github.com/go-ocf/cloud/resource-directory/pb/device-directory" + pbRD "github.com/go-ocf/cloud/resource-directory/pb/resource-directory" + pbRS "github.com/go-ocf/cloud/resource-directory/pb/resource-shadow" + "github.com/gofrs/uuid" + grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth" + + "github.com/go-ocf/kit/security/oauth/manager" + "github.com/panjf2000/ants" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/status" +) + +// RequestHandler handles incoming requests. +type RequestHandler struct { + deviceDirectoryClient pbDD.DeviceDirectoryClient + resourceDirectoryClient pbRD.ResourceDirectoryClient + resourceShadowClient pbRS.ResourceShadowClient + authServiceClient pbAS.AuthorizationServiceClient + resourceAggregateClient pbRA.ResourceAggregateClient + + resourceProjection *projectionRA.Projection + subscriptions *subscriptions + seqNum uint64 + clientTLS tls.Config + updateNotificationContainer *notification.UpdateNotificationContainer + retrieveNotificationContainer *notification.RetrieveNotificationContainer + timeoutForRequests time.Duration + closeFunc func() +} + +type HandlerConfig struct { + Mongo mongodb.Config + Nats nats.Config + Service Config + + GoRoutinePoolSize int `envconfig:"GOROUTINE_POOL_SIZE" default:"16"` + UserDevicesManagerTickFrequency time.Duration `envconfig:"USER_MGMT_TICK_FREQUENCY" default:"15s"` + UserDevicesManagerExpiration time.Duration `envconfig:"USER_MGMT_EXPIRATION" default:"1m"` +} + +func AddHandler(svr *kitNetGrpc.Server, config HandlerConfig, clientTLS tls.Config) error { + handler, err := NewRequestHandlerFromConfig(config, clientTLS) + if err != nil { + return err + } + svr.AddCloseFunc(handler.Close) + pb.RegisterGrpcGatewayServer(svr.Server, handler) + return nil +} + +// Register registers the handler instance with a gRPC server. +func Register(server *grpc.Server, handler *RequestHandler) { + pb.RegisterGrpcGatewayServer(server, handler) +} + +func NewRequestHandlerFromConfig(config HandlerConfig, clientTLS tls.Config) (*RequestHandler, error) { + svc := config.Service + + rdConn, err := grpc.Dial(svc.ResourceDirectoryAddr, grpc.WithTransportCredentials(credentials.NewTLS(&clientTLS))) + if err != nil { + return nil, fmt.Errorf("cannot connect to resource directory: %w", err) + } + deviceDirectoryClient := pbDD.NewDeviceDirectoryClient(rdConn) + resourceDirectoryClient := pbRD.NewResourceDirectoryClient(rdConn) + resourceShadowClient := pbRS.NewResourceShadowClient(rdConn) + + oauthMgr, err := manager.NewManagerFromConfiguration(svc.OAuth, &clientTLS) + if err != nil { + return nil, fmt.Errorf("cannot create oauth manager: %w", err) + } + + asConn, err := grpc.Dial(svc.AuthServerAddr, grpc.WithTransportCredentials(credentials.NewTLS(&clientTLS)), grpc.WithPerRPCCredentials(kitNetGrpc.NewOAuthAccess(oauthMgr.GetToken))) + if err != nil { + return nil, fmt.Errorf("cannot connect to authorization server: %w", err) + } + authServiceClient := pbAS.NewAuthorizationServiceClient(asConn) + + raConn, err := grpc.Dial(svc.ResourceAggregateAddr, grpc.WithTransportCredentials(credentials.NewTLS(&clientTLS))) + if err != nil { + return nil, fmt.Errorf("cannot connect to resource aggregate: %w", err) + } + resourceAggregateClient := pbRA.NewResourceAggregateClient(raConn) + + pool, err := ants.NewPool(config.GoRoutinePoolSize) + if err != nil { + return nil, fmt.Errorf("cannot create goroutine pool: %w", err) + } + + resourceEventStore, err := mongodb.NewEventStore(config.Mongo, pool.Submit, mongodb.WithTLS(&clientTLS)) + if err != nil { + return nil, fmt.Errorf("cannot create resource mongodb eventstore %w", err) + } + + resourceSubscriber, err := nats.NewSubscriber(config.Nats, pool.Submit, func(err error) { log.Errorf("grpc-gateway: error occurs during receiving event: %v", err) }, nats.WithTLS(&clientTLS)) + if err != nil { + return nil, fmt.Errorf("cannot create resource nats subscriber %w", err) + } + + ctx := context.Background() + + subscriptions := NewSubscriptions() + userDevicesManager := clientAS.NewUserDevicesManager(subscriptions.UserDevicesChanged, authServiceClient, config.UserDevicesManagerTickFrequency, config.UserDevicesManagerExpiration, func(err error) { log.Errorf("grpc-gateway: error occurs during receiving devices: %v", err) }) + subscriptions.userDevicesManager = userDevicesManager + + updateNotificationContainer := notification.NewUpdateNotificationContainer() + retrieveNotificationContainer := notification.NewRetrieveNotificationContainer() + projUUID, err := uuid.NewV4() + if err != nil { + return nil, fmt.Errorf("cannot create uuid for projection %w", err) + } + resourceProjection, err := projectionRA.NewProjection(ctx, projUUID.String()+"."+svc.FQDN, resourceEventStore, resourceSubscriber, NewResourceCtx(subscriptions, updateNotificationContainer, retrieveNotificationContainer)) + if err != nil { + return nil, fmt.Errorf("cannot create projection over resource aggregate events: %w", err) + } + + closeFunc := func() { + resourceSubscriber.Close() + resourceEventStore.Close(context.Background()) + userDevicesManager.Close() + pool.Release() + raConn.Close() + rdConn.Close() + asConn.Close() + oauthMgr.Close() + } + + h := NewRequestHandler( + authServiceClient, + deviceDirectoryClient, + resourceDirectoryClient, + resourceShadowClient, + resourceAggregateClient, + resourceProjection, + subscriptions, + updateNotificationContainer, + retrieveNotificationContainer, + svc.TimeoutForRequests, + closeFunc) + h.clientTLS = clientTLS + return h, nil +} + +// NewRequestHandler factory for new RequestHandler. +func NewRequestHandler( + authServiceClient pbAS.AuthorizationServiceClient, + deviceDirectoryClient pbDD.DeviceDirectoryClient, + resourceDirectoryClient pbRD.ResourceDirectoryClient, + resourceShadowClient pbRS.ResourceShadowClient, + resourceAggregateClient pbRA.ResourceAggregateClient, + resourceProjection *projectionRA.Projection, + subscriptions *subscriptions, + updateNotificationContainer *notification.UpdateNotificationContainer, + retrieveNotificationContainer *notification.RetrieveNotificationContainer, + timeoutForRequests time.Duration, + closeFunc func(), +) *RequestHandler { + return &RequestHandler{ + authServiceClient: authServiceClient, + deviceDirectoryClient: deviceDirectoryClient, + resourceDirectoryClient: resourceDirectoryClient, + resourceShadowClient: resourceShadowClient, + resourceProjection: resourceProjection, + subscriptions: subscriptions, + resourceAggregateClient: resourceAggregateClient, + updateNotificationContainer: updateNotificationContainer, + retrieveNotificationContainer: retrieveNotificationContainer, + timeoutForRequests: timeoutForRequests, + closeFunc: closeFunc, + } +} + +func grpcStatus2ddStatus(in pb.GetDevicesRequest_Status) pbDD.Status { + if in == pb.GetDevicesRequest_ONLINE { + return pbDD.Status_ONLINE + } + return pbDD.Status_OFFLINE +} + +func ddLocalizedString2grpcLocalizedString(in *pbDD.LocalizedString) *pb.LocalizedString { + return &pb.LocalizedString{ + Language: in.Language, + Value: in.Value, + } +} + +func logAndReturnError(err error) error { + log.Errorf("%v", err) + return err +} + +func (r *RequestHandler) GetDevices(req *pb.GetDevicesRequest, srv pb.GrpcGateway_GetDevicesServer) error { + accessToken, err := grpc_auth.AuthFromMD(srv.Context(), "bearer") + if err != nil { + return logAndReturnError(status.Errorf(codes.Unauthenticated, "cannot get devices: %v", err)) + } + statusFilter := make([]pbDD.Status, 0, 2) + for _, s := range req.StatusFilter { + statusFilter = append(statusFilter, grpcStatus2ddStatus(s)) + } + ddReq := pbDD.GetDevicesRequest{ + TypeFilter: req.TypeFilter, + StatusFilter: statusFilter, + DeviceIdsFilter: req.DeviceIdsFilter, + } + getDevicesClient, err := r.deviceDirectoryClient.GetDevices(kitNetGrpc.CtxWithToken(srv.Context(), accessToken), &ddReq) + if err != nil { + return logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Unauthenticated, "cannot get devices: %v", err)) + } + defer getDevicesClient.CloseSend() + + for { + ddDev, err := getDevicesClient.Recv() + if err == io.EOF { + break + } + if err != nil { + return logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, "cannot get devices: %v", err)) + } + + manufacturerName := make([]*pb.LocalizedString, 0, 16) + for _, l := range ddDev.GetResource().GetManufacturerName() { + manufacturerName = append(manufacturerName, ddLocalizedString2grpcLocalizedString(l)) + } + + devResp := pb.Device{ + Id: ddDev.GetId(), + Types: ddDev.GetResource().GetResourceTypes(), + Name: ddDev.GetResource().GetName(), + IsOnline: ddDev.GetIsOnline(), + ManufacturerName: manufacturerName, + ModelNumber: ddDev.GetResource().GetModelNumber(), + } + err = srv.Send(&devResp) + if err != nil { + return logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, "cannot get devices: %v", err)) + } + } + + return nil +} + +func (r *RequestHandler) GetResourceLinks(req *pb.GetResourceLinksRequest, srv pb.GrpcGateway_GetResourceLinksServer) error { + accessToken, err := grpc_auth.AuthFromMD(srv.Context(), "bearer") + if err != nil { + return logAndReturnError(kitNetGrpc.ForwardErrorf(codes.NotFound, "cannot get resource links: %v", err)) + } + + rdReq := pbRD.GetResourceLinksRequest{ + TypeFilter: req.TypeFilter, + DeviceIdsFilter: req.DeviceIdsFilter, + } + getResourceLinksClient, err := r.resourceDirectoryClient.GetResourceLinks(kitNetGrpc.CtxWithToken(srv.Context(), accessToken), &rdReq) + if err != nil { + return logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, "cannot get resource links: %v", err)) + } + defer getResourceLinksClient.CloseSend() + for { + rdRes, err := getResourceLinksClient.Recv() + if err == io.EOF { + break + } + if err != nil { + return logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, "cannot get resource links: %v", err)) + } + resResp := makeResourceLink(rdRes.Resource) + err = srv.Send(&resResp) + if err != nil { + return logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, "cannot get resource links: %v", err)) + } + } + + return nil +} + +func GrpcResourceID2ResourceID(resourceId *pb.ResourceId) string { + return cqrsRA.MakeResourceId(resourceId.DeviceId, resourceId.ResourceLinkHref) +} + +func (r *RequestHandler) RetrieveResourcesValues(req *pb.RetrieveResourcesValuesRequest, srv pb.GrpcGateway_RetrieveResourcesValuesServer) error { + accessToken, err := grpc_auth.AuthFromMD(srv.Context(), "bearer") + if err != nil { + return logAndReturnError(status.Errorf(codes.Unauthenticated, "cannot retrieve resources values: %v", err)) + } + resourceIds := make([]string, 0, 16) + for _, r := range req.ResourceIdsFilter { + resourceIds = append(resourceIds, GrpcResourceID2ResourceID(r)) + } + rdReq := pbRS.RetrieveResourcesValuesRequest{ + TypeFilter: req.TypeFilter, + DeviceIdsFilter: req.DeviceIdsFilter, + ResourceIdsFilter: resourceIds, + } + retrieveResourcesValuesClient, err := r.resourceShadowClient.RetrieveResourcesValues(kitNetGrpc.CtxWithToken(srv.Context(), accessToken), &rdReq) + if err != nil { + return logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, "cannot retrieve resources values: %v", err)) + } + defer retrieveResourcesValuesClient.CloseSend() + for { + rdRes, err := retrieveResourcesValuesClient.Recv() + if err == io.EOF { + break + } + if err != nil { + return logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, "cannot retrieve resources values: %v", err)) + } + if rdRes.Content == nil { + continue + } + resResp := pb.ResourceValue{ + ResourceId: &pb.ResourceId{ + DeviceId: rdRes.DeviceId, + ResourceLinkHref: rdRes.Href, + }, + Content: &pb.Content{ + Data: rdRes.Content.Data, + ContentType: rdRes.Content.ContentType, + }, + Types: rdRes.Types, + } + err = srv.Send(&resResp) + if err != nil { + return logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, "cannot retrieve resources values: %v", err)) + } + } + return nil +} + +func (r *RequestHandler) SubscribeForEvents(srv pb.GrpcGateway_SubscribeForEventsServer) error { + err := r.subscriptions.SubscribeForEvents(r.resourceProjection, srv) + if err != nil { + return logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, "cannot subscribe for events: %v", err)) + } + return nil +} + +func (r *RequestHandler) Close() { + r.closeFunc() +} + +func (r *RequestHandler) GetClientTLSConfig() tls.Config { + return r.clientTLS +} diff --git a/grpc-gateway/service/grpcApi_test.go b/grpc-gateway/service/grpcApi_test.go new file mode 100644 index 000000000..38192e72f --- /dev/null +++ b/grpc-gateway/service/grpcApi_test.go @@ -0,0 +1,874 @@ +package service_test + +import ( + "context" + "crypto/tls" + "io" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + _ "github.com/mattn/go-sqlite3" // sql driver + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + "github.com/go-ocf/cloud/authorization/provider" + coap "github.com/go-ocf/go-coap" + "github.com/go-ocf/cloud/grpc-gateway/pb" + grpcTest "github.com/go-ocf/cloud/grpc-gateway/test" + "github.com/go-ocf/kit/codec/cbor" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/sdk/schema/cloud" +) + +const TEST_TIMEOUT = time.Second * 20 + +func TestRequestHandler_GetDevices(t *testing.T) { + deviceID := grpcTest.MustFindDeviceByName(grpcTest.TestDeviceName) + type args struct { + req *pb.GetDevicesRequest + } + tests := []struct { + name string + args args + wantErr bool + want []*pb.Device + }{ + { + name: "valid", + args: args{ + req: &pb.GetDevicesRequest{}, + }, + want: []*pb.Device{ + { + Id: deviceID, + Name: grpcTest.TestDeviceName, + IsOnline: true, + }, + }, + }, + } + + ctx, cancel := context.WithTimeout(context.Background(), TEST_TIMEOUT) + defer cancel() + ctx = kitNetGrpc.CtxWithToken(ctx, provider.UserToken) + + tearDown := grpcTest.SetUp(ctx, t) + defer tearDown() + + conn, err := grpc.Dial(grpcTest.GRPC_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: grpcTest.GetRootCertificatePool(t), + }))) + require.NoError(t, err) + c := pb.NewGrpcGatewayClient(conn) + + shutdownDevSim := grpcTest.OnboardDevSim(ctx, t, c, deviceID, grpcTest.GW_HOST, grpcTest.GetAllBackendResourceLinks()) + defer shutdownDevSim() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + client, err := c.GetDevices(ctx, tt.args.req) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + devices := make([]*pb.Device, 0, 1) + for { + dev, err := client.Recv() + if err == io.EOF { + break + } + require.NoError(t, err) + devices = append(devices, dev) + } + require.Equal(t, tt.want, devices) + } + }) + } +} + +func TestRequestHandler_GetResourceLinks(t *testing.T) { + deviceID := grpcTest.MustFindDeviceByName(grpcTest.TestDeviceName) + type args struct { + req *pb.GetResourceLinksRequest + } + tests := []struct { + name string + args args + wantErr bool + want []pb.ResourceLink + }{ + { + name: "valid", + args: args{ + req: &pb.GetResourceLinksRequest{}, + }, + wantErr: false, + want: grpcTest.SortResources(grpcTest.ConvertSchemaToPb(deviceID, grpcTest.GetAllBackendResourceLinks())), + }, + } + + ctx, cancel := context.WithTimeout(context.Background(), TEST_TIMEOUT) + defer cancel() + ctx = kitNetGrpc.CtxWithToken(ctx, provider.UserToken) + + tearDown := grpcTest.SetUp(ctx, t) + defer tearDown() + + conn, err := grpc.Dial(grpcTest.GRPC_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: grpcTest.GetRootCertificatePool(t), + }))) + require.NoError(t, err) + c := pb.NewGrpcGatewayClient(conn) + + shutdownDevSim := grpcTest.OnboardDevSim(ctx, t, c, deviceID, grpcTest.GW_HOST, grpcTest.GetAllBackendResourceLinks()) + defer shutdownDevSim() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + client, err := c.GetResourceLinks(ctx, tt.args.req) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + links := make([]pb.ResourceLink, 0, 1) + for { + link, err := client.Recv() + if err == io.EOF { + break + } + require.NoError(t, err) + links = append(links, *link) + } + require.Equal(t, tt.want, grpcTest.SortResources(links)) + } + }) + } +} + +func cmpResourceValues(t *testing.T, want []*pb.ResourceValue, got []*pb.ResourceValue) { + require.Len(t, got, len(want)) + for idx := range want { + dataWant := want[idx].GetContent().GetData() + datagot := got[idx].GetContent().GetData() + want[idx].Content.Data = nil + got[idx].Content.Data = nil + require.Equal(t, want[idx], got[idx]) + w := grpcTest.DecodeCbor(t, dataWant) + g := grpcTest.DecodeCbor(t, datagot) + require.Equal(t, w, g) + } +} + +func TestRequestHandler_RetrieveResourcesValues(t *testing.T) { + deviceID := grpcTest.MustFindDeviceByName(grpcTest.TestDeviceName) + type args struct { + req *pb.RetrieveResourcesValuesRequest + } + tests := []struct { + name string + args args + wantErr bool + want []*pb.ResourceValue + }{ + { + name: "valid", + args: args{ + req: &pb.RetrieveResourcesValuesRequest{ + ResourceIdsFilter: []*pb.ResourceId{ + &pb.ResourceId{ + DeviceId: deviceID, + ResourceLinkHref: cloud.StatusHref, + }, + }, + }, + }, + want: []*pb.ResourceValue{ + &pb.ResourceValue{ + ResourceId: &pb.ResourceId{ + DeviceId: deviceID, + ResourceLinkHref: cloud.StatusHref, + }, + Types: cloud.StatusResourceTypes, + Content: &pb.Content{ + ContentType: coap.AppOcfCbor.String(), + Data: grpcTest.EncodeToCbor(t, map[string]interface{}{ + "if": cloud.StatusInterfaces, + "rt": cloud.StatusResourceTypes, + "online": true, + }), + }, + }, + }, + }, + } + + ctx, cancel := context.WithTimeout(context.Background(), TEST_TIMEOUT) + defer cancel() + ctx = kitNetGrpc.CtxWithToken(ctx, provider.UserToken) + + tearDown := grpcTest.SetUp(ctx, t) + defer tearDown() + + conn, err := grpc.Dial(grpcTest.GRPC_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: grpcTest.GetRootCertificatePool(t), + }))) + require.NoError(t, err) + c := pb.NewGrpcGatewayClient(conn) + + shutdownDevSim := grpcTest.OnboardDevSim(ctx, t, c, deviceID, grpcTest.GW_HOST, grpcTest.GetAllBackendResourceLinks()) + defer shutdownDevSim() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + client, err := c.RetrieveResourcesValues(ctx, tt.args.req) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + values := make([]*pb.ResourceValue, 0, 1) + for { + value, err := client.Recv() + if err == io.EOF { + break + } + require.NoError(t, err) + values = append(values, value) + } + cmpResourceValues(t, tt.want, values) + } + }) + } +} + +func TestRequestHandler_UpdateResourcesValues(t *testing.T) { + deviceID := grpcTest.MustFindDeviceByName(grpcTest.TestDeviceName) + type args struct { + req pb.UpdateResourceValuesRequest + } + tests := []struct { + name string + args args + want *pb.UpdateResourceValuesResponse + wantErr bool + }{ + { + name: "valid", + args: args{ + req: pb.UpdateResourceValuesRequest{ + ResourceId: &pb.ResourceId{ + DeviceId: deviceID, + ResourceLinkHref: "/light/1", + }, + Content: &pb.Content{ + ContentType: coap.AppOcfCbor.String(), + Data: grpcTest.EncodeToCbor(t, map[string]interface{}{ + "power": 1, + }), + }, + }, + }, + want: &pb.UpdateResourceValuesResponse{ + Content: &pb.Content{}, + }, + }, + { + name: "valid with interface", + args: args{ + req: pb.UpdateResourceValuesRequest{ + ResourceInterface: "oic.if.baseline", + ResourceId: &pb.ResourceId{ + DeviceId: deviceID, + ResourceLinkHref: "/light/1", + }, + Content: &pb.Content{ + ContentType: coap.AppOcfCbor.String(), + Data: grpcTest.EncodeToCbor(t, map[string]interface{}{ + "power": 2, + }), + }, + }, + }, + want: &pb.UpdateResourceValuesResponse{ + Content: &pb.Content{}, + }, + }, + { + name: "revert update", + args: args{ + req: pb.UpdateResourceValuesRequest{ + ResourceInterface: "oic.if.baseline", + ResourceId: &pb.ResourceId{ + DeviceId: deviceID, + ResourceLinkHref: "/light/1", + }, + Content: &pb.Content{ + ContentType: coap.AppOcfCbor.String(), + Data: grpcTest.EncodeToCbor(t, map[string]interface{}{ + "power": 0, + }), + }, + }, + }, + want: &pb.UpdateResourceValuesResponse{ + Content: &pb.Content{}, + }, + }, + { + name: "update RO-resource", + args: args{ + req: pb.UpdateResourceValuesRequest{ + ResourceId: &pb.ResourceId{ + DeviceId: deviceID, + ResourceLinkHref: "/oic/d", + }, + Content: &pb.Content{ + ContentType: coap.AppOcfCbor.String(), + Data: grpcTest.EncodeToCbor(t, map[string]interface{}{ + "di": "abc", + }), + }, + }, + }, + wantErr: true, + }, + { + name: "invalid ResourceLinkHref", + args: args{ + req: pb.UpdateResourceValuesRequest{ + ResourceId: &pb.ResourceId{ + DeviceId: deviceID, + ResourceLinkHref: "/unknown", + }, + }, + }, + wantErr: true, + }, + } + + ctx, cancel := context.WithTimeout(context.Background(), TEST_TIMEOUT) + defer cancel() + ctx = kitNetGrpc.CtxWithToken(ctx, provider.UserToken) + + tearDown := grpcTest.SetUp(ctx, t) + defer tearDown() + + conn, err := grpc.Dial(grpcTest.GRPC_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: grpcTest.GetRootCertificatePool(t), + }))) + require.NoError(t, err) + c := pb.NewGrpcGatewayClient(conn) + + shutdownDevSim := grpcTest.OnboardDevSim(ctx, t, c, deviceID, grpcTest.GW_HOST, grpcTest.GetAllBackendResourceLinks()) + defer shutdownDevSim() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := c.UpdateResourcesValues(ctx, &tt.args.req) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + require.Equal(t, tt.want, got) + }) + } +} + +func TestRequestHandler_RetrieveResourceFromDevice(t *testing.T) { + deviceID := grpcTest.MustFindDeviceByName(grpcTest.TestDeviceName) + type args struct { + req pb.RetrieveResourceFromDeviceRequest + } + tests := []struct { + name string + args args + want map[string]interface{} + wantContentType string + wantErr bool + }{ + { + name: "valid /light/2", + args: args{ + req: pb.RetrieveResourceFromDeviceRequest{ + ResourceId: &pb.ResourceId{ + DeviceId: deviceID, + ResourceLinkHref: "/light/2", + }, + }, + }, + wantContentType: "application/vnd.ocf+cbor", + want: map[string]interface{}{"name": "Light", "power": uint64(0), "state": false}, + }, + { + name: "valid /oic/d", + args: args{ + req: pb.RetrieveResourceFromDeviceRequest{ + ResourceId: &pb.ResourceId{ + DeviceId: deviceID, + ResourceLinkHref: "/oic/d", + }, + }, + }, + wantContentType: "application/vnd.ocf+cbor", + want: map[string]interface{}{"di": deviceID, "dmv": "ocf.res.1.0.0", "icv": "ocf.1.0.0", "n": grpcTest.TestDeviceName}, + }, + { + name: "invalid ResourceLinkHref", + args: args{ + req: pb.RetrieveResourceFromDeviceRequest{ + ResourceId: &pb.ResourceId{ + DeviceId: deviceID, + ResourceLinkHref: "/unknown", + }, + }, + }, + wantErr: true, + }, + } + + ctx, cancel := context.WithTimeout(context.Background(), TEST_TIMEOUT) + defer cancel() + ctx = kitNetGrpc.CtxWithToken(ctx, provider.UserToken) + + tearDown := grpcTest.SetUp(ctx, t) + defer tearDown() + + conn, err := grpc.Dial(grpcTest.GRPC_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: grpcTest.GetRootCertificatePool(t), + }))) + require.NoError(t, err) + c := pb.NewGrpcGatewayClient(conn) + + shutdownDevSim := grpcTest.OnboardDevSim(ctx, t, c, deviceID, grpcTest.GW_HOST, grpcTest.GetAllBackendResourceLinks()) + defer shutdownDevSim() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := c.RetrieveResourceFromDevice(ctx, &tt.args.req) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + assert.Equal(t, tt.wantContentType, got.GetContent().GetContentType()) + var d map[string]interface{} + err := cbor.Decode(got.GetContent().GetData(), &d) + require.NoError(t, err) + delete(d, "piid") + assert.Equal(t, tt.want, d) + } + }) + } +} + +func TestRequestHandler_SubscribeForEvents(t *testing.T) { + deviceID := grpcTest.MustFindDeviceByName(grpcTest.TestDeviceName) + type args struct { + sub pb.SubscribeForEvents + } + tests := []struct { + name string + args args + want []*pb.Event + }{ + { + name: "invalid - invalid type subscription", + args: args{ + sub: pb.SubscribeForEvents{ + Token: "testToken", + }, + }, + + want: []*pb.Event{ + &pb.Event{ + Type: &pb.Event_OperationProcessed_{ + OperationProcessed: &pb.Event_OperationProcessed{ + Token: "testToken", + ErrorStatus: &pb.Event_OperationProcessed_ErrorStatus{ + Code: pb.Event_OperationProcessed_ErrorStatus_OK, + }, + }, + }, + }, + &pb.Event{ + Type: &pb.Event_SubscriptionCanceled_{ + SubscriptionCanceled: &pb.Event_SubscriptionCanceled{ + Reason: "not supported", + }, + }, + }, + }, + }, + { + name: "devices subscription - registered", + args: args{ + sub: pb.SubscribeForEvents{ + Token: "testToken", + FilterBy: &pb.SubscribeForEvents_DevicesEvent{ + DevicesEvent: &pb.SubscribeForEvents_DevicesEventFilter{ + FilterEvents: []pb.SubscribeForEvents_DevicesEventFilter_Event{ + pb.SubscribeForEvents_DevicesEventFilter_REGISTERED, pb.SubscribeForEvents_DevicesEventFilter_UNREGISTERED, + }, + }, + }, + }, + }, + want: []*pb.Event{ + &pb.Event{ + Type: &pb.Event_OperationProcessed_{ + OperationProcessed: &pb.Event_OperationProcessed{ + Token: "testToken", + ErrorStatus: &pb.Event_OperationProcessed_ErrorStatus{ + Code: pb.Event_OperationProcessed_ErrorStatus_OK, + }, + }, + }, + }, + &pb.Event{ + Type: &pb.Event_DeviceRegistered_{ + DeviceRegistered: &pb.Event_DeviceRegistered{ + DeviceId: deviceID, + }, + }, + }, + }, + }, + { + name: "devices subscription - online", + args: args{ + sub: pb.SubscribeForEvents{ + Token: "testToken", + FilterBy: &pb.SubscribeForEvents_DevicesEvent{ + DevicesEvent: &pb.SubscribeForEvents_DevicesEventFilter{ + FilterEvents: []pb.SubscribeForEvents_DevicesEventFilter_Event{ + pb.SubscribeForEvents_DevicesEventFilter_ONLINE, pb.SubscribeForEvents_DevicesEventFilter_OFFLINE, + }, + }, + }, + }, + }, + want: []*pb.Event{ + &pb.Event{ + Type: &pb.Event_OperationProcessed_{ + OperationProcessed: &pb.Event_OperationProcessed{ + Token: "testToken", + ErrorStatus: &pb.Event_OperationProcessed_ErrorStatus{ + Code: pb.Event_OperationProcessed_ErrorStatus_OK, + }, + }, + }, + }, + &pb.Event{ + Type: &pb.Event_DeviceOnline_{ + DeviceOnline: &pb.Event_DeviceOnline{ + DeviceId: deviceID, + }, + }, + }, + }, + }, + { + name: "device subscription - published", + args: args{ + sub: pb.SubscribeForEvents{ + Token: "testToken", + FilterBy: &pb.SubscribeForEvents_DeviceEvent{ + DeviceEvent: &pb.SubscribeForEvents_DeviceEventFilter{ + DeviceId: deviceID, + FilterEvents: []pb.SubscribeForEvents_DeviceEventFilter_Event{ + pb.SubscribeForEvents_DeviceEventFilter_RESOURCE_PUBLISHED, pb.SubscribeForEvents_DeviceEventFilter_RESOURCE_UNPUBLISHED, + }, + }, + }, + }, + }, + want: []*pb.Event{ + &pb.Event{ + Type: &pb.Event_OperationProcessed_{ + OperationProcessed: &pb.Event_OperationProcessed{ + Token: "testToken", + ErrorStatus: &pb.Event_OperationProcessed_ErrorStatus{ + Code: pb.Event_OperationProcessed_ErrorStatus_OK, + }, + }, + }, + }, + &pb.Event{ + Type: &pb.Event_ResourcePublished_{ + ResourcePublished: &pb.Event_ResourcePublished{ + Link: &pb.ResourceLink{ + Href: "/light/1", + Types: []string{"core.light"}, + Interfaces: []string{"oic.if.rw", "oic.if.baseline"}, + DeviceId: deviceID, + }, + }, + }, + }, + &pb.Event{ + Type: &pb.Event_ResourcePublished_{ + ResourcePublished: &pb.Event_ResourcePublished{ + Link: &pb.ResourceLink{ + Href: "/light/2", + Types: []string{"core.light"}, + Interfaces: []string{"oic.if.rw", "oic.if.baseline"}, + DeviceId: deviceID, + }, + }, + }, + }, + &pb.Event{ + Type: &pb.Event_ResourcePublished_{ + ResourcePublished: &pb.Event_ResourcePublished{ + Link: &pb.ResourceLink{ + Href: "/oic/p", + Types: []string{"oic.wk.p"}, + Interfaces: []string{"oic.if.r", "oic.if.baseline"}, + DeviceId: deviceID, + }, + }, + }, + }, + &pb.Event{ + Type: &pb.Event_ResourcePublished_{ + ResourcePublished: &pb.Event_ResourcePublished{ + Link: &pb.ResourceLink{ + Href: "/oic/d", + Types: []string{"oic.d.cloudDevice", "oic.wk.d"}, + Interfaces: []string{"oic.if.r", "oic.if.baseline"}, + DeviceId: deviceID, + }, + }, + }, + }, + &pb.Event{ + Type: &pb.Event_ResourcePublished_{ + ResourcePublished: &pb.Event_ResourcePublished{ + Link: &pb.ResourceLink{ + Href: cloud.StatusHref, + Types: cloud.StatusResourceTypes, + Interfaces: cloud.StatusInterfaces, + DeviceId: deviceID, + }, + }, + }, + }, + &pb.Event{ + Type: &pb.Event_ResourcePublished_{ + ResourcePublished: &pb.Event_ResourcePublished{ + Link: &pb.ResourceLink{ + Href: "/oc/con", + Types: []string{"oic.wk.con"}, + Interfaces: []string{"oic.if.rw", "oic.if.baseline"}, + DeviceId: deviceID, + }, + }, + }, + }, + }, + }, + } + + ctx, cancel := context.WithTimeout(context.Background(), TEST_TIMEOUT) + defer cancel() + ctx = kitNetGrpc.CtxWithToken(ctx, provider.UserToken) + + tearDown := grpcTest.SetUp(ctx, t) + defer tearDown() + + conn, err := grpc.Dial(grpcTest.GRPC_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: grpcTest.GetRootCertificatePool(t), + }))) + require.NoError(t, err) + c := pb.NewGrpcGatewayClient(conn) + + shutdownDevSim := grpcTest.OnboardDevSim(ctx, t, c, deviceID, grpcTest.GW_HOST, grpcTest.GetAllBackendResourceLinks()) + defer shutdownDevSim() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + client, err := c.SubscribeForEvents(ctx) + require.NoError(t, err) + defer client.CloseSend() + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + for _, w := range tt.want { + ev, err := client.Recv() + require.NoError(t, err) + ev.SubscriptionId = w.SubscriptionId + require.Contains(t, tt.want, ev) + } + }() + err = client.Send(&tt.args.sub) + require.NoError(t, err) + wg.Wait() + }) + } +} + +func TestRequestHandler_ValidateEventsFlow(t *testing.T) { + deviceID := grpcTest.MustFindDeviceByName(grpcTest.TestDeviceName) + ctx, cancel := context.WithTimeout(context.Background(), TEST_TIMEOUT) + defer cancel() + ctx = kitNetGrpc.CtxWithToken(ctx, provider.UserToken) + tearDown := grpcTest.SetUp(ctx, t) + defer tearDown() + + conn, err := grpc.Dial(grpcTest.GRPC_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ + RootCAs: grpcTest.GetRootCertificatePool(t), + }))) + require.NoError(t, err) + c := pb.NewGrpcGatewayClient(conn) + + shutdownDevSim := grpcTest.OnboardDevSim(ctx, t, c, deviceID, grpcTest.GW_HOST, grpcTest.GetAllBackendResourceLinks()) + + client, err := c.SubscribeForEvents(ctx) + require.NoError(t, err) + + err = client.Send(&pb.SubscribeForEvents{ + Token: "testToken", + FilterBy: &pb.SubscribeForEvents_DevicesEvent{ + DevicesEvent: &pb.SubscribeForEvents_DevicesEventFilter{ + FilterEvents: []pb.SubscribeForEvents_DevicesEventFilter_Event{ + pb.SubscribeForEvents_DevicesEventFilter_ONLINE, pb.SubscribeForEvents_DevicesEventFilter_OFFLINE, + }, + }, + }, + }) + require.NoError(t, err) + + ev, err := client.Recv() + require.NoError(t, err) + expectedEvent := &pb.Event{ + SubscriptionId: ev.SubscriptionId, + Type: &pb.Event_OperationProcessed_{ + OperationProcessed: &pb.Event_OperationProcessed{ + Token: "testToken", + ErrorStatus: &pb.Event_OperationProcessed_ErrorStatus{ + Code: pb.Event_OperationProcessed_ErrorStatus_OK, + }, + }, + }, + } + require.Equal(t, expectedEvent, ev) + + ev, err = client.Recv() + require.NoError(t, err) + expectedEvent = &pb.Event{ + SubscriptionId: ev.SubscriptionId, + Type: &pb.Event_DeviceOnline_{ + DeviceOnline: &pb.Event_DeviceOnline{ + DeviceId: deviceID, + }, + }, + } + require.Equal(t, expectedEvent, ev) + + err = client.Send(&pb.SubscribeForEvents{ + Token: "testToken", + FilterBy: &pb.SubscribeForEvents_ResourceEvent{ + ResourceEvent: &pb.SubscribeForEvents_ResourceEventFilter{ + ResourceId: &pb.ResourceId{ + DeviceId: deviceID, + ResourceLinkHref: "/light/2", + }, + FilterEvents: []pb.SubscribeForEvents_ResourceEventFilter_Event{ + pb.SubscribeForEvents_ResourceEventFilter_CONTENT_CHANGED, + }, + }, + }, + }) + require.NoError(t, err) + + ev, err = client.Recv() + require.NoError(t, err) + expectedEvent = &pb.Event{ + SubscriptionId: ev.SubscriptionId, + Type: &pb.Event_OperationProcessed_{ + OperationProcessed: &pb.Event_OperationProcessed{ + Token: "testToken", + ErrorStatus: &pb.Event_OperationProcessed_ErrorStatus{ + Code: pb.Event_OperationProcessed_ErrorStatus_OK, + }, + }, + }, + } + require.Equal(t, expectedEvent, ev) + subContentChangedID := ev.SubscriptionId + + ev, err = client.Recv() + require.NoError(t, err) + expectedEvent = &pb.Event{ + SubscriptionId: subContentChangedID, + Type: &pb.Event_ResourceContentChanged{ + ResourceContentChanged: &pb.Event_ResourceChanged{ + ResourceId: &pb.ResourceId{ + DeviceId: deviceID, + ResourceLinkHref: "/light/2", + }, + Content: &pb.Content{ + ContentType: coap.AppOcfCbor.String(), + Data: []byte("\277estate\364epower\000dnameeLight\377"), + }, + }, + }, + } + require.Equal(t, expectedEvent, ev) + + _, err = c.UpdateResourcesValues(ctx, &pb.UpdateResourceValuesRequest{ + ResourceId: &pb.ResourceId{ + DeviceId: deviceID, + ResourceLinkHref: "/light/2", + }, + Content: &pb.Content{ + ContentType: coap.AppOcfCbor.String(), + Data: func() []byte { + v := map[string]interface{}{ + "power": 99, + } + d, err := cbor.Encode(v) + require.NoError(t, err) + return d + }(), + }, + }) + require.NoError(t, err) + + ev, err = client.Recv() + require.NoError(t, err) + expectedEvent = &pb.Event{ + SubscriptionId: subContentChangedID, + Type: &pb.Event_ResourceContentChanged{ + ResourceContentChanged: &pb.Event_ResourceChanged{ + ResourceId: &pb.ResourceId{ + DeviceId: deviceID, + ResourceLinkHref: "/light/2", + }, + Content: &pb.Content{ + ContentType: coap.AppOcfCbor.String(), + Data: []byte("\277estate\364epower\030cdnameeLight\377"), + }, + }, + }, + } + require.Equal(t, expectedEvent, ev) + + shutdownDevSim() + + ev, err = client.Recv() + require.NoError(t, err) + expectedEvent = &pb.Event{ + SubscriptionId: ev.SubscriptionId, + Type: &pb.Event_DeviceOffline_{ + DeviceOffline: &pb.Event_DeviceOffline{ + DeviceId: deviceID, + }, + }, + } + require.Equal(t, expectedEvent, ev) +} diff --git a/grpc-gateway/service/jwt.go b/grpc-gateway/service/jwt.go new file mode 100644 index 000000000..f0a3eedf3 --- /dev/null +++ b/grpc-gateway/service/jwt.go @@ -0,0 +1,33 @@ +package service + +import ( + "fmt" + + "github.com/dgrijalva/jwt-go" +) + +type claims struct { + Subject string `json:"sub,omitempty"` +} + +func (c *claims) Valid() error { + return nil +} + +func parseSubFromJwtToken(rawJwtToken string) (string, error) { + parser := &jwt.Parser{ + SkipClaimsValidation: true, + } + + var claims claims + _, _, err := parser.ParseUnverified(rawJwtToken, &claims) + if err != nil { + return "", fmt.Errorf("cannot get subject from jwt token: %w", err) + } + + if claims.Subject != "" { + return claims.Subject, nil + } + + return "", fmt.Errorf("cannot get subject from jwt token: not found") +} diff --git a/grpc-gateway/service/resourceProjection.go b/grpc-gateway/service/resourceProjection.go new file mode 100644 index 000000000..81baf8596 --- /dev/null +++ b/grpc-gateway/service/resourceProjection.go @@ -0,0 +1,235 @@ +package service + +import ( + "context" + "fmt" + "sync" + + "github.com/go-ocf/cqrs/event" + "github.com/go-ocf/cqrs/eventstore" + "github.com/go-ocf/kit/log" + "github.com/go-ocf/kit/net/http" + "github.com/go-ocf/sdk/schema/cloud" + + cqrsRA "github.com/go-ocf/cloud/resource-aggregate/cqrs" + raEvents "github.com/go-ocf/cloud/resource-aggregate/cqrs/events" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/notification" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +type resourceCtx struct { + lock sync.Mutex + resource *pbRA.Resource + isPublished bool + content *pbRA.ResourceChanged + version uint64 + onResourcePublishedVersion uint64 + onResourceUnpublishedVersion uint64 + onResourceChangedVersion uint64 + + subscriptions *subscriptions + updateNotificationContainer *notification.UpdateNotificationContainer + retrieveNotificationContainer *notification.RetrieveNotificationContainer +} + +func NewResourceCtx(subscriptions *subscriptions, updateNotificationContainer *notification.UpdateNotificationContainer, retrieveNotificationContainer *notification.RetrieveNotificationContainer) func(context.Context) (eventstore.Model, error) { + return func(context.Context) (eventstore.Model, error) { + return &resourceCtx{ + subscriptions: subscriptions, + updateNotificationContainer: updateNotificationContainer, + retrieveNotificationContainer: retrieveNotificationContainer, + }, nil + } +} + +func (m *resourceCtx) cloneLocked() *resourceCtx { + return &resourceCtx{ + resource: m.resource, + isPublished: m.isPublished, + content: m.content, + version: m.version, + } +} + +func (m *resourceCtx) Clone() *resourceCtx { + m.lock.Lock() + defer m.lock.Unlock() + + return m.cloneLocked() +} + +func (m *resourceCtx) onResourcePublishedLocked(ctx context.Context) error { + log.Debugf("onResourcePublishedLocked %v%v", m.resource.GetDeviceId(), m.resource.GetHref()) + link := makeResourceLink(m.resource) + return m.subscriptions.OnResourcePublished(ctx, link, m.onResourcePublishedVersion) +} + +func (m *resourceCtx) onResourceUnpublishedLocked(ctx context.Context) error { + log.Debugf("onResourceUnpublishedLocked %v%v", m.resource.GetDeviceId(), m.resource.GetHref()) + link := makeResourceLink(m.resource) + return m.subscriptions.OnResourceUnpublished(ctx, link, m.onResourceUnpublishedVersion) +} + +func (m *resourceCtx) onResourceChangedLocked(ctx context.Context) error { + log.Debugf("onResourceChangedLocked %v%v", m.resource.GetDeviceId(), m.resource.GetHref()) + if m.content.GetStatus() != pbRA.Status_OK { + err := fmt.Errorf("unable to subscribe to resource %v%v: device response: %v", m.resource.GetDeviceId(), m.resource.GetHref(), m.content.GetStatus()) + m.subscriptions.CancelResourceSubscriptions(ctx, m.resource.GetDeviceId(), m.resource.GetHref(), err) + return err + } + content := makeContent(m.content.GetContent()) + return m.subscriptions.OnResourceContentChanged(ctx, m.resource.GetDeviceId(), m.resource.GetHref(), content, m.onResourceChangedVersion) +} + +func (m *resourceCtx) onCloudStatusChangedLocked(ctx context.Context) error { + log.Debugf("onCloudStatusChangedLocked %v%v", m.resource.GetDeviceId(), m.resource.GetHref()) + online, err := isDeviceOnline(m.content.GetContent()) + if err != nil { + return err + } + if online { + return m.subscriptions.OnDeviceOnline(ctx, m.resource.GetDeviceId(), m.onResourceChangedVersion) + } + return m.subscriptions.OnDeviceOffline(ctx, m.resource.GetDeviceId(), m.onResourceChangedVersion) +} + +func (m *resourceCtx) onResourceUpdatedLocked(updateProcessed []raEvents.ResourceUpdated) { + log.Debugf("onResourceUpdatedLocked %v%v", m.resource.GetDeviceId(), m.resource.GetHref()) + for _, up := range updateProcessed { + notify := m.updateNotificationContainer.Find(up.AuditContext.CorrelationId) + if notify != nil { + select { + case notify <- up: + default: + log.Debugf("DeviceId: %v, ResourceId: %v: cannot send resource updated event", m.resource.DeviceId, m.resource.Id) + } + } + } +} + +func (m *resourceCtx) onResourceRetrievedLocked(resourceRetrieved []raEvents.ResourceRetrieved) { + log.Debugf("onResourceRetrievedLocked %v%v", m.resource.GetDeviceId(), m.resource.GetHref()) + for _, up := range resourceRetrieved { + notify := m.retrieveNotificationContainer.Find(up.AuditContext.CorrelationId) + if notify != nil { + select { + case notify <- up: + default: + log.Debugf("DeviceId: %v, ResourceId: %v: cannot send resource retrieved event", m.resource.DeviceId, m.resource.Id) + } + } + } +} + +func (m *resourceCtx) SnapshotEventType() string { + s := &raEvents.ResourceStateSnapshotTaken{} + return s.SnapshotEventType() +} + +func (m *resourceCtx) Handle(ctx context.Context, iter event.Iter) error { + var eu event.EventUnmarshaler + var onResourcePublished, onResourceUnpublished, onResourceContentChanged bool + resourceUpdated := make([]raEvents.ResourceUpdated, 0, 16) + resourceRetrieved := make([]raEvents.ResourceRetrieved, 0, 16) + m.lock.Lock() + defer m.lock.Unlock() + var anyEventProcessed bool + for iter.Next(ctx, &eu) { + anyEventProcessed = true + log.Debugf("grpc-gateway.resourceCtx.Handle: DeviceId: %v, ResourceId: %v, Version: %v, EventType: %v", eu.GroupId, eu.AggregateId, eu.Version, eu.EventType) + m.version = eu.Version + switch eu.EventType { + case http.ProtobufContentType(&pbRA.ResourceStateSnapshotTaken{}): + var s raEvents.ResourceStateSnapshotTaken + if err := eu.Unmarshal(&s); err != nil { + return err + } + if !m.isPublished { + onResourcePublished = s.IsPublished + onResourceUnpublished = !s.IsPublished + } + m.content = s.LatestResourceChange + m.resource = s.Resource + m.isPublished = s.IsPublished + m.onResourcePublishedVersion = eu.Version + m.onResourceUnpublishedVersion = eu.Version + m.onResourceChangedVersion = eu.Version + onResourceContentChanged = true + case http.ProtobufContentType(&pbRA.ResourcePublished{}): + var s raEvents.ResourcePublished + if err := eu.Unmarshal(&s); err != nil { + return err + } + if !m.isPublished { + onResourcePublished = true + onResourceUnpublished = false + } + m.onResourcePublishedVersion = eu.Version + m.isPublished = true + m.resource = s.Resource + case http.ProtobufContentType(&pbRA.ResourceUnpublished{}): + if m.isPublished { + onResourcePublished = false + onResourceUnpublished = true + } + m.onResourceUnpublishedVersion = eu.Version + m.isPublished = false + case http.ProtobufContentType(&pbRA.ResourceChanged{}): + var s raEvents.ResourceChanged + if err := eu.Unmarshal(&s); err != nil { + return err + } + m.content = &s.ResourceChanged + m.onResourceChangedVersion = eu.Version + onResourceContentChanged = true + case http.ProtobufContentType(&pbRA.ResourceUpdated{}): + var s raEvents.ResourceUpdated + if err := eu.Unmarshal(&s); err != nil { + return err + } + resourceUpdated = append(resourceUpdated, s) + case http.ProtobufContentType(&pbRA.ResourceRetrieved{}): + var s raEvents.ResourceRetrieved + if err := eu.Unmarshal(&s); err != nil { + return err + } + resourceRetrieved = append(resourceRetrieved, s) + } + } + + if !anyEventProcessed { + // if event event not processed, it means that the projection will be reloaded. + return nil + } + + if m.resource == nil { + return fmt.Errorf("DeviceId: %v, ResourceId: %v: invalid resource is stored in eventstore: Resource attribute is not set", eu.GroupId, eu.AggregateId) + } + + if onResourcePublished { + if err := m.onResourcePublishedLocked(ctx); err != nil { + log.Errorf("%v", err) + } + } else if onResourceUnpublished { + if err := m.onResourceUnpublishedLocked(ctx); err != nil { + log.Errorf("%v", err) + } + } + + if onResourceContentChanged && m.isPublished { + if cqrsRA.MakeResourceId(m.resource.GetDeviceId(), cloud.StatusHref) == m.resource.Id { + if err := m.onCloudStatusChangedLocked(ctx); err != nil { + log.Errorf("cannot make action on cloud status changed: %v", err) + } + } + + if err := m.onResourceChangedLocked(ctx); err != nil { + log.Errorf("%v", err) + } + } + + m.onResourceUpdatedLocked(resourceUpdated) + m.onResourceRetrievedLocked(resourceRetrieved) + + return nil +} diff --git a/grpc-gateway/service/resourceSubscription.go b/grpc-gateway/service/resourceSubscription.go new file mode 100644 index 000000000..8b1277979 --- /dev/null +++ b/grpc-gateway/service/resourceSubscription.go @@ -0,0 +1,109 @@ +package service + +import ( + "context" + "fmt" + + "github.com/go-ocf/cloud/grpc-gateway/pb" + "github.com/go-ocf/kit/log" + cqrsRA "github.com/go-ocf/cloud/resource-aggregate/cqrs" + projectionRA "github.com/go-ocf/cloud/resource-aggregate/cqrs/projection" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +type resourceSubscription struct { + *subscription + resourceEvent *pb.SubscribeForEvents_ResourceEventFilter +} + +func NewResourceSubscription(id, userID string, send SendEventFunc, resourceProjection *projectionRA.Projection, resourceEvent *pb.SubscribeForEvents_ResourceEventFilter) *resourceSubscription { + log.Debugf("subscription.NewResourceSubscription %v", id) + defer log.Debugf("subscription.NewResourceSubscription %v done", id) + return &resourceSubscription{ + subscription: NewSubscription(userID, id, send, resourceProjection), + resourceEvent: resourceEvent, + } +} + +func (s *resourceSubscription) Init(ctx context.Context, currentDevices map[string]bool) error { + log.Debugf("subscriptions.SubscribeForResourceEvent.resourceProjection.Register") + if !currentDevices[s.DeviceID()] { + return fmt.Errorf("device %v not found", s.DeviceID()) + } + + created, err := s.RegisterToProjection(ctx, s.DeviceID()) + if err != nil { + return fmt.Errorf("cannot register to resource projection: %w", err) + } + log.Debugf("subscriptions.SubscribeForResourceEvent.resourceProjection.Register, created=%v", created) + + resourceID := cqrsRA.MakeResourceId(s.DeviceID(), s.Href()) + models := s.resourceProjection.Models(s.DeviceID(), resourceID) + if len(models) == 0 { + err = s.resourceProjection.ForceUpdate(ctx, s.DeviceID(), resourceID) + if err != nil { + return fmt.Errorf("cannot load resources for device: %w", err) + } + models = s.resourceProjection.Models(s.DeviceID(), resourceID) + } + + if len(models) == 0 { + return fmt.Errorf("cannot load resource models %v%v: %w", s.DeviceID(), s.Href(), err) + } + res := models[0].(*resourceCtx).Clone() + if res.content == nil { + return nil + } + + for _, f := range s.resourceEvent.FilterEvents { + switch f { + case pb.SubscribeForEvents_ResourceEventFilter_CONTENT_CHANGED: + if res.content.GetStatus() != pbRA.Status_OK { + return fmt.Errorf("unable to subscribe to resource %v%v: device response: %v", res.resource.GetDeviceId(), res.resource.GetHref(), res.content.GetStatus()) + } + content := makeContent(res.content.GetContent()) + err := s.NotifyOfContentChangedResource(ctx, content, res.onResourceChangedVersion) + if err != nil { + return fmt.Errorf("cannot send resource content changed: %w", err) + } + } + } + return nil +} + +func (s *resourceSubscription) DeviceID() string { + return s.resourceEvent.GetResourceId().GetDeviceId() +} + +func (s *resourceSubscription) Href() string { + return s.resourceEvent.GetResourceId().GetResourceLinkHref() +} + +func (s *resourceSubscription) NotifyOfContentChangedResource(ctx context.Context, content pb.Content, version uint64) error { + deviceID := s.resourceEvent.GetResourceId().GetDeviceId() + href := s.resourceEvent.GetResourceId().GetResourceLinkHref() + if s.FilterByVersion(deviceID, href, "res", version) { + return nil + } + var found bool + for _, f := range s.resourceEvent.GetFilterEvents() { + if f == pb.SubscribeForEvents_ResourceEventFilter_CONTENT_CHANGED { + found = true + } + } + if !found { + return nil + } + return s.Send(ctx, pb.Event{ + SubscriptionId: s.ID(), + Type: &pb.Event_ResourceContentChanged{ + ResourceContentChanged: &pb.Event_ResourceChanged{ + ResourceId: &pb.ResourceId{ + DeviceId: deviceID, + ResourceLinkHref: href, + }, + Content: &content, + }, + }, + }) +} diff --git a/grpc-gateway/service/retrieveResourceFromDevice.go b/grpc-gateway/service/retrieveResourceFromDevice.go new file mode 100644 index 000000000..f04ff529a --- /dev/null +++ b/grpc-gateway/service/retrieveResourceFromDevice.go @@ -0,0 +1,92 @@ +package service + +import ( + "context" + "fmt" + "sync/atomic" + + "github.com/go-ocf/cloud/grpc-gateway/pb" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + "github.com/gofrs/uuid" + grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/peer" + "google.golang.org/grpc/status" +) + +func (r *RequestHandler) RetrieveResourceFromDevice(ctx context.Context, req *pb.RetrieveResourceFromDeviceRequest) (*pb.RetrieveResourceFromDeviceResponse, error) { + accessToken, err := grpc_auth.AuthFromMD(ctx, "bearer") + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.Unauthenticated, "cannot retrieve resource from device: %v", err)) + } + if req.ResourceId == nil { + return nil, logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot retrieve resource from device: invalid ResourceId")) + } + deviceID := req.GetResourceId().GetDeviceId() + href := req.GetResourceId().GetResourceLinkHref() + errorMsg := fmt.Sprintf("cannot retrieve resource from device /%v%v", deviceID, href) + ": %v" + + correlationIDUUID, err := uuid.NewV4() + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.Internal, errorMsg, err)) + } + + correlationID := correlationIDUUID.String() + resourceID := GrpcResourceID2ResourceID(req.ResourceId) + notify := r.retrieveNotificationContainer.Add(correlationID) + defer r.retrieveNotificationContainer.Remove(correlationID) + + loaded, err := r.resourceProjection.Register(ctx, deviceID) + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.NotFound, errorMsg, fmt.Errorf("cannot register device to projection: %w", err))) + } + defer r.resourceProjection.Unregister(deviceID) + + if !loaded { + if len(r.resourceProjection.Models(deviceID, resourceID)) == 0 { + err = r.resourceProjection.ForceUpdate(ctx, deviceID, resourceID) + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.NotFound, errorMsg, err)) + } + } + } + + connectionID := "grpc-gateway" + peer, ok := peer.FromContext(ctx) + if ok { + connectionID = peer.Addr.String() + } + seq := atomic.AddUint64(&r.seqNum, 1) + raReq := pbRA.RetrieveResourceRequest{ + ResourceId: resourceID, + ResourceInterface: req.GetResourceInterface(), + CorrelationId: correlationID, + CommandMetadata: &pbCQRS.CommandMetadata{ + ConnectionId: connectionID, + Sequence: seq, + }, + } + + _, err = r.resourceAggregateClient.RetrieveResource(kitNetGrpc.CtxWithToken(ctx, accessToken), &raReq) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, errorMsg, err)) + } + + timeoutCtx, cancel := context.WithTimeout(ctx, r.timeoutForRequests) + defer cancel() + select { + case processed := <-notify: + content, err := eventContentToContent(processed.GetStatus(), processed.GetContent()) + if err != nil { + return nil, err + } + return &pb.RetrieveResourceFromDeviceResponse{ + Content: content, + }, nil + case <-timeoutCtx.Done(): + } + + return nil, logAndReturnError(status.Errorf(codes.DeadlineExceeded, errorMsg, fmt.Errorf("timeout"))) +} diff --git a/grpc-gateway/service/subscription.go b/grpc-gateway/service/subscription.go new file mode 100644 index 000000000..6356be2a3 --- /dev/null +++ b/grpc-gateway/service/subscription.go @@ -0,0 +1,145 @@ +package service + +import ( + "context" + "fmt" + "sync" + + "github.com/go-ocf/cloud/grpc-gateway/pb" + "github.com/go-ocf/kit/log" + cqrsRA "github.com/go-ocf/cloud/resource-aggregate/cqrs" + projectionRA "github.com/go-ocf/cloud/resource-aggregate/cqrs/projection" +) + +type subscription struct { + id string + userID string + + resourceProjection *projectionRA.Projection + send SendEventFunc + + lock sync.Mutex + registeredDevicesInProjection map[string]bool + + eventVersionsLock sync.Mutex + eventVersions map[string]uint64 +} + +func NewSubscription(userID, id string, send SendEventFunc, resourceProjection *projectionRA.Projection) *subscription { + return &subscription{ + userID: userID, + id: id, + send: send, + resourceProjection: resourceProjection, + eventVersions: make(map[string]uint64), + registeredDevicesInProjection: make(map[string]bool), + } +} + +func (s *subscription) UserID() string { + return s.userID +} + +func (s *subscription) ID() string { + return s.id +} + +func (s *subscription) FilterByVersion(deviceID, href, typeEvent string, version uint64) bool { + log.Debugf("subscription.filterByVersion(%v, %v, %v)", deviceID, href, version) + + resourceID := cqrsRA.MakeResourceId(deviceID, href+"."+typeEvent) + + s.eventVersionsLock.Lock() + defer s.eventVersionsLock.Unlock() + v, ok := s.eventVersions[resourceID] + if !ok { + s.eventVersions[resourceID] = version + log.Debugf("subscription.filterByVersion(%v, %v, %v) false", deviceID, href, version) + return false + } + if v >= version { + log.Debugf("subscription.filterByVersion(%v, %v, %v) true", deviceID, href, version) + return true + } + s.eventVersions[resourceID] = version + log.Debugf("subscription.filterByVersion(%v, %v, %v) false", deviceID, href, version) + return false +} + +func (s *subscription) RegisterToProjection(ctx context.Context, deviceID string) (loaded bool, err error) { + s.lock.Lock() + defer s.lock.Unlock() + loaded, err = s.resourceProjection.Register(ctx, deviceID) + if err != nil { + return loaded, err + } + s.registeredDevicesInProjection[deviceID] = true + return +} + +func (s *subscription) UnregisterFromProjection(ctx context.Context, deviceID string) error { + s.lock.Lock() + defer s.lock.Unlock() + _, ok := s.registeredDevicesInProjection[deviceID] + if !ok { + return nil + } + delete(s.registeredDevicesInProjection, deviceID) + return s.resourceProjection.Unregister(deviceID) +} + +func (s *subscription) Send(ctx context.Context, event pb.Event) error { + return s.send(ctx, event) +} + +func (s *subscription) Close(reason error) error { + r := "" + if reason != nil { + r = reason.Error() + } + + var errors []error + + err := s.unregisterProjections() + if err != nil { + errors = append(errors, err) + } + + err = s.Send(context.Background(), pb.Event{ + SubscriptionId: s.ID(), + Type: &pb.Event_SubscriptionCanceled_{ + SubscriptionCanceled: &pb.Event_SubscriptionCanceled{ + Reason: r, + }, + }, + }) + if err != nil { + errors = append(errors, err) + } + if len(errors) > 0 { + return fmt.Errorf("cannot close subscription %v: %v", s.ID(), errors) + } + + return nil +} + +func (s *subscription) unregisterProjections() error { + log.Debugf("subscription.unregisterProjections %v", s.ID()) + defer log.Debugf("subscription.unregisterProjections %v done", s.ID()) + + s.lock.Lock() + defer s.lock.Unlock() + + var errors []error + for deviceID := range s.registeredDevicesInProjection { + err := s.resourceProjection.Unregister(deviceID) + if err != nil { + errors = append(errors, err) + } + delete(s.registeredDevicesInProjection, deviceID) + } + if len(errors) > 0 { + return fmt.Errorf("cannot unregister projections for %v: %v", s.ID(), errors) + } + return nil +} diff --git a/grpc-gateway/service/subscriptions.go b/grpc-gateway/service/subscriptions.go new file mode 100644 index 000000000..fd22ef1bf --- /dev/null +++ b/grpc-gateway/service/subscriptions.go @@ -0,0 +1,694 @@ +package service + +import ( + "context" + "fmt" + "io" + "sync" + + "google.golang.org/grpc/codes" + + clientAS "github.com/go-ocf/cloud/authorization/client" + "github.com/go-ocf/go-coap" + "github.com/go-ocf/cloud/grpc-gateway/pb" + "github.com/go-ocf/kit/codec/cbor" + "github.com/go-ocf/kit/codec/json" + "github.com/go-ocf/kit/log" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + projectionRA "github.com/go-ocf/cloud/resource-aggregate/cqrs/projection" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + "github.com/go-ocf/sdk/schema/cloud" + "github.com/gofrs/uuid" + grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth" + + "github.com/go-ocf/cqrs/eventstore" +) + +type Subscriber interface { + UserID() string + ID() string + Init(ctx context.Context, currentDevices map[string]bool) error + Close(reason error) error +} + +type subscriptions struct { + userDevicesManager *clientAS.UserDevicesManager + + rwlock sync.RWMutex + allSubscriptions map[string]Subscriber // map[subscriptionID] + resourceSubscriptions map[string]map[string]map[string]map[string]*resourceSubscription // map[userId]map[req.DeviceId]map[href]map[subscriptionID] + deviceSubscriptions map[string]map[string]map[string]*deviceSubscription // map[userId]map[req.DeviceId]map[subscriptionID] + devicesSubscriptions map[string]map[string]*devicesSubscription // map[userId]map[subscriptionID] + + initSubscriptionsLock sync.Mutex + initSubscriptions map[string]map[string]Subscriber // map[userId]map[subscriptionID] +} + +type SendEventFunc func(senderCtx context.Context, e pb.Event) error + +func NewSubscriptions() *subscriptions { + return &subscriptions{ + allSubscriptions: make(map[string]Subscriber), + resourceSubscriptions: make(map[string]map[string]map[string]map[string]*resourceSubscription), + deviceSubscriptions: make(map[string]map[string]map[string]*deviceSubscription), + devicesSubscriptions: make(map[string]map[string]*devicesSubscription), + initSubscriptions: make(map[string]map[string]Subscriber), + } +} + +func (s *subscriptions) popInitSubscriptions(userID string) map[string]Subscriber { + s.initSubscriptionsLock.Lock() + defer s.initSubscriptionsLock.Unlock() + v := s.initSubscriptions[userID] + delete(s.initSubscriptions, userID) + return v +} + +func (s *subscriptions) insertToInitSubscriptions(sub Subscriber) { + s.initSubscriptionsLock.Lock() + defer s.initSubscriptionsLock.Unlock() + + v, ok := s.initSubscriptions[sub.UserID()] + if !ok { + v = make(map[string]Subscriber) + s.initSubscriptions[sub.UserID()] = v + } + v[sub.ID()] = sub +} + +func (s *subscriptions) removeFromInitSubscriptions(id, userID string) { + s.initSubscriptionsLock.Lock() + defer s.initSubscriptionsLock.Unlock() + + delete(s.initSubscriptions[userID], id) + if len(s.initSubscriptions[userID]) == 0 { + delete(s.initSubscriptions, userID) + } +} + +func (s *subscriptions) getRemovedSubscriptions(userID string, removedDevices map[string]bool) []Subscriber { + s.rwlock.RLock() + defer s.rwlock.RUnlock() + remove := make([]Subscriber, 0, 32) + for deviceID := range removedDevices { + for _, sub := range s.deviceSubscriptions[userID][deviceID] { + remove = append(remove, sub) + } + for _, resSubs := range s.resourceSubscriptions[userID][deviceID] { + for _, sub := range resSubs { + remove = append(remove, sub) + } + } + } + + return remove +} + +func (s *subscriptions) getSubscriptionsToUpdate(userID string, init map[string]Subscriber) []*devicesSubscription { + s.rwlock.RLock() + defer s.rwlock.RUnlock() + updated := make([]*devicesSubscription, 0, 32) + for _, sub := range s.devicesSubscriptions[userID] { + if _, ok := init[sub.ID()]; !ok { + updated = append(updated, sub) + } + } + + return updated +} + +func (s *subscriptions) UserDevicesChanged(ctx context.Context, userID string, addedDevices, removedDevices, currentDevices map[string]bool) { + log.Debugf("subscriptions.UserDevicesChanged %v: added: %+v removed: %+v current: %+v\n", userID, addedDevices, removedDevices, currentDevices) + + init := s.popInitSubscriptions(userID) + for _, sub := range init { + err := sub.Init(ctx, currentDevices) + if err != nil { + log.Errorf("cannot init sub ID %v: %v", sub.ID(), err) + s.Close(sub.ID(), err) + } + } + remove := s.getRemovedSubscriptions(userID, removedDevices) + for _, sub := range remove { + log.Infof("remove sub ID %v", sub.ID()) + sub.Close(fmt.Errorf("device was removed from user")) + } + + if len(addedDevices) > 0 || len(removedDevices) > 0 { + update := s.getSubscriptionsToUpdate(userID, init) + for _, sub := range update { + err := sub.Update(ctx, addedDevices, removedDevices) + if err != nil { + log.Errorf("cannot update sub ID %v: %v", sub.ID(), err) + s.Close(sub.ID(), err) + } + } + } +} + +func (s *subscriptions) Get(id string) Subscriber { + s.rwlock.RLock() + defer s.rwlock.RUnlock() + if sub, ok := s.allSubscriptions[id]; ok { + return sub + } + return nil +} + +func (s *subscriptions) Pop(id string) Subscriber { + s.rwlock.Lock() + defer s.rwlock.Unlock() + if sub, ok := s.allSubscriptions[id]; ok { + userID := sub.UserID() + switch v := sub.(type) { + case *deviceSubscription: + delete(s.deviceSubscriptions[userID][v.DeviceID()], id) + if len(s.deviceSubscriptions[userID][v.DeviceID()]) == 0 { + delete(s.deviceSubscriptions, v.DeviceID()) + if len(s.deviceSubscriptions[userID]) == 0 { + delete(s.deviceSubscriptions, userID) + } + } + case *resourceSubscription: + deviceID := v.DeviceID() + href := v.Href() + delete(s.resourceSubscriptions[userID][deviceID][href], id) + if len(s.resourceSubscriptions[userID][deviceID][href]) == 0 { + delete(s.resourceSubscriptions[userID][deviceID], href) + if len(s.resourceSubscriptions[userID][deviceID]) == 0 { + delete(s.resourceSubscriptions[userID], deviceID) + if len(s.resourceSubscriptions[userID]) == 0 { + delete(s.resourceSubscriptions, userID) + } + } + } + case *devicesSubscription: + delete(s.devicesSubscriptions[userID], id) + if len(s.devicesSubscriptions[userID]) == 0 { + delete(s.devicesSubscriptions, userID) + } + } + delete(s.initSubscriptions[userID], id) + if len(s.initSubscriptions[userID]) == 0 { + delete(s.initSubscriptions, userID) + } + delete(s.allSubscriptions, id) + return sub + } + return nil +} + +func (s *subscriptions) closeWithReleaseUserDevicesMfg(id string, reason error, releaseFromUserDevManager bool) error { + sub := s.Pop(id) + if sub == nil { + return fmt.Errorf("cannot close subscription %v: not found", id) + } + s.removeFromInitSubscriptions(id, sub.UserID()) + if releaseFromUserDevManager { + s.userDevicesManager.Release(sub.UserID()) + } + + err := sub.Close(reason) + if err != nil { + return fmt.Errorf("cannot close subscription %v: %w", id, err) + } + return nil +} + +func (s *subscriptions) Close(id string, reason error) error { + return s.closeWithReleaseUserDevicesMfg(id, reason, true) +} + +func (s *subscriptions) InsertDevicesSubscription(ctx context.Context, sub *devicesSubscription) error { + s.rwlock.Lock() + defer s.rwlock.Unlock() + _, ok := s.allSubscriptions[sub.ID()] + if ok { + return fmt.Errorf("subscription already exist") + } + if sub == nil { + return nil + } + userID := sub.UserID() + userSubs, ok := s.devicesSubscriptions[userID] + if !ok { + userSubs = make(map[string]*devicesSubscription) + s.devicesSubscriptions[userID] = userSubs + } + userSubs[sub.ID()] = sub + + initSubs, ok := s.initSubscriptions[userID] + if !ok { + initSubs = make(map[string]Subscriber) + s.initSubscriptions[userID] = initSubs + } + initSubs[sub.ID()] = sub + s.allSubscriptions[sub.ID()] = sub + return nil +} + +func (s *subscriptions) InsertDeviceSubscription(ctx context.Context, sub *deviceSubscription) error { + s.rwlock.Lock() + defer s.rwlock.Unlock() + _, ok := s.allSubscriptions[sub.ID()] + if ok { + return fmt.Errorf("subscription already exist") + } + if sub == nil { + return nil + } + userID := sub.UserID() + deviceID := sub.DeviceID() + userSubs, ok := s.deviceSubscriptions[userID] + if !ok { + userSubs = make(map[string]map[string]*deviceSubscription) + s.deviceSubscriptions[userID] = userSubs + } + devSubs, ok := userSubs[deviceID] + if !ok { + devSubs = make(map[string]*deviceSubscription) + userSubs[deviceID] = devSubs + } + devSubs[sub.ID()] = sub + + initSubs, ok := s.initSubscriptions[userID] + if !ok { + initSubs = make(map[string]Subscriber) + s.initSubscriptions[userID] = initSubs + } + initSubs[sub.ID()] = sub + s.allSubscriptions[sub.ID()] = sub + return nil +} + +func (s *subscriptions) InsertResourceSubscription(ctx context.Context, sub *resourceSubscription) error { + s.rwlock.Lock() + defer s.rwlock.Unlock() + _, ok := s.allSubscriptions[sub.ID()] + if ok { + return fmt.Errorf("subscription already exist") + } + if sub == nil { + return nil + } + userID := sub.UserID() + deviceID := sub.DeviceID() + href := sub.Href() + userSubs, ok := s.resourceSubscriptions[userID] + if !ok { + userSubs = make(map[string]map[string]map[string]*resourceSubscription) + s.resourceSubscriptions[userID] = userSubs + } + devSubs, ok := userSubs[deviceID] + if !ok { + devSubs = make(map[string]map[string]*resourceSubscription) + userSubs[deviceID] = devSubs + } + resSubs, ok := devSubs[href] + if !ok { + resSubs = make(map[string]*resourceSubscription) + devSubs[href] = resSubs + } + resSubs[sub.ID()] = sub + + initSubs, ok := s.initSubscriptions[userID] + if !ok { + initSubs = make(map[string]Subscriber) + s.initSubscriptions[userID] = initSubs + } + initSubs[sub.ID()] = sub + s.allSubscriptions[sub.ID()] = sub + return nil +} + +func makeResourceLink(resource *pbRA.Resource) pb.ResourceLink { + return pb.ResourceLink{ + Href: resource.Href, + DeviceId: resource.DeviceId, + Interfaces: resource.Interfaces, + Types: resource.ResourceTypes, + } +} + +func makeLinkRepresentation(eventType pb.SubscribeForEvents_DeviceEventFilter_Event, m eventstore.Model) (pb.ResourceLink, uint64, bool) { + c := m.(*resourceCtx).Clone() + switch eventType { + case pb.SubscribeForEvents_DeviceEventFilter_RESOURCE_PUBLISHED: + if c.isPublished { + return makeResourceLink(c.resource), c.onResourcePublishedVersion, true + } + case pb.SubscribeForEvents_DeviceEventFilter_RESOURCE_UNPUBLISHED: + if !c.isPublished { + return makeResourceLink(c.resource), c.onResourceUnpublishedVersion, true + } + } + return pb.ResourceLink{}, 0, false +} + +func makeContent(content *pbRA.Content) pb.Content { + return pb.Content{ + ContentType: content.ContentType, + Data: content.Data, + } +} + +func (s *subscriptions) OnResourcePublished(ctx context.Context, link pb.ResourceLink, version uint64) error { + s.rwlock.RLock() + defer s.rwlock.RUnlock() + + var errors []error + for userID, userSubs := range s.deviceSubscriptions { + if !s.userDevicesManager.IsUserDevice(userID, link.DeviceId) { + continue + } + for _, sub := range userSubs[link.DeviceId] { + if err := sub.NotifyOfPublishedResource(ctx, link, version); err != nil { + errors = append(errors, err) + } + } + } + if len(errors) > 0 { + return fmt.Errorf("cannot send resource published event: %v", errors) + } + return nil +} + +func (s *subscriptions) OnResourceUnpublished(ctx context.Context, link pb.ResourceLink, version uint64) error { + s.rwlock.RLock() + defer s.rwlock.RUnlock() + + var errors []error + for userID, userSubs := range s.deviceSubscriptions { + if !s.userDevicesManager.IsUserDevice(userID, link.DeviceId) { + continue + } + for _, sub := range userSubs[link.DeviceId] { + if err := sub.NotifyOfUnpublishedResource(ctx, link, version); err != nil { + errors = append(errors, err) + } + } + } + if len(errors) > 0 { + return fmt.Errorf("cannot send resource unpublished event: %v", errors) + } + return nil +} + +func (s *subscriptions) OnDeviceOnline(ctx context.Context, deviceID string, version uint64) error { + s.rwlock.RLock() + defer s.rwlock.RUnlock() + + var errors []error + for userID, userSubs := range s.devicesSubscriptions { + if !s.userDevicesManager.IsUserDevice(userID, deviceID) { + continue + } + for _, sub := range userSubs { + if err := sub.NotifyOfOnlineDevice(ctx, deviceID, version); err != nil { + errors = append(errors, err) + } + } + } + if len(errors) > 0 { + return fmt.Errorf("cannot send device online event: %v", errors) + } + + return nil +} + +func (s *subscriptions) OnDeviceOffline(ctx context.Context, deviceID string, version uint64) error { + s.rwlock.RLock() + defer s.rwlock.RUnlock() + + var errors []error + for userID, userSubs := range s.devicesSubscriptions { + if !s.userDevicesManager.IsUserDevice(userID, deviceID) { + continue + } + for _, sub := range userSubs { + if err := sub.NotifyOfOfflineDevice(ctx, deviceID, version); err != nil { + errors = append(errors, err) + } + } + } + if len(errors) > 0 { + return fmt.Errorf("cannot send device offline event: %v", errors) + } + return nil +} + +func (s *subscriptions) OnResourceContentChanged(ctx context.Context, deviceID, href string, content pb.Content, version uint64) error { + s.rwlock.RLock() + defer s.rwlock.RUnlock() + + var errors []error + for userID, userSubs := range s.resourceSubscriptions { + if !s.userDevicesManager.IsUserDevice(userID, deviceID) { + continue + } + res, ok := userSubs[deviceID] + if !ok { + return nil + } + subs := res[href] + for _, sub := range subs { + if err := sub.NotifyOfContentChangedResource(ctx, content, version); err != nil { + errors = append(errors, err) + } + } + } + if len(errors) > 0 { + return fmt.Errorf("cannot send resource content changed: %v", errors) + } + return nil +} + +func (s *subscriptions) CancelResourceSubscriptions(ctx context.Context, deviceID, href string, err error) { + subsIDs := make([]string, 0, 4) + func() { + s.rwlock.RLock() + defer s.rwlock.RUnlock() + for _, userSubs := range s.resourceSubscriptions { + subs := userSubs[deviceID][href] + for subID := range subs { + subsIDs = append(subsIDs, subID) + } + } + }() + + for _, id := range subsIDs { + s.Close(id, err) + } +} + +func isDeviceOnline(content *pbRA.Content) (bool, error) { + if content == nil { + return false, nil + } + var decoder func(data []byte, v interface{}) error + switch content.ContentType { + case coap.AppCBOR.String(), coap.AppOcfCbor.String(): + decoder = cbor.Decode + case coap.AppJSON.String(): + decoder = json.Decode + } + if decoder == nil { + return false, fmt.Errorf("decoder not found") + } + var cloudStatus cloud.Status + err := decoder(content.Data, &cloudStatus) + if err != nil { + return false, err + } + return cloudStatus.Online, nil +} + +func (s *subscriptions) SubscribeForDevicesEvent(ctx context.Context, userID string, resourceProjection *projectionRA.Projection, subscriptionID string, send SendEventFunc, req *pb.SubscribeForEvents_DevicesEventFilter) error { + sub := NewDevicesSubscription(subscriptionID, userID, send, resourceProjection, req) + err := s.InsertDevicesSubscription(ctx, sub) + if err != nil { + sub.Close(err) + return err + } + err = s.userDevicesManager.Acquire(ctx, userID) + if err != nil { + s.closeWithReleaseUserDevicesMfg(subscriptionID, err, false) + return err + } + return nil +} + +func (s *subscriptions) SubscribeForDeviceEvent(ctx context.Context, userID string, resourceProjection *projectionRA.Projection, subscriptionID string, send SendEventFunc, req *pb.SubscribeForEvents_DeviceEventFilter) error { + sub := NewDeviceSubscription(subscriptionID, userID, send, resourceProjection, req) + err := s.InsertDeviceSubscription(ctx, sub) + if err != nil { + sub.Close(err) + return err + } + err = s.userDevicesManager.Acquire(ctx, userID) + if err != nil { + s.closeWithReleaseUserDevicesMfg(subscriptionID, err, false) + return err + } + return nil +} + +func (s *subscriptions) SubscribeForResourceEvent(ctx context.Context, userID string, resourceProjection *projectionRA.Projection, subscriptionID string, send SendEventFunc, req *pb.SubscribeForEvents_ResourceEventFilter) error { + sub := NewResourceSubscription(subscriptionID, userID, send, resourceProjection, req) + err := s.InsertResourceSubscription(ctx, sub) + if err != nil { + sub.Close(err) + return err + } + err = s.userDevicesManager.Acquire(ctx, userID) + if err != nil { + s.closeWithReleaseUserDevicesMfg(subscriptionID, err, false) + return err + } + return nil +} + +func (s *subscriptions) cancelSubscription(localSubscriptions *sync.Map, subscriptionID string) error { + _, ok := localSubscriptions.Load(subscriptionID) + if !ok { + return fmt.Errorf("cannot cancel subscription %v: not found", subscriptionID) + } + localSubscriptions.Delete(subscriptionID) + return s.Close(subscriptionID, nil) +} + +func (s *subscriptions) SubscribeForEvents(resourceProjection *projectionRA.Projection, srv pb.GrpcGateway_SubscribeForEventsServer) error { + accessToken, err := grpc_auth.AuthFromMD(srv.Context(), "bearer") + if err != nil { + return kitNetGrpc.ForwardFromError(codes.Unauthenticated, err) + } + + userID, err := parseSubFromJwtToken(accessToken) + if err != nil { + return kitNetGrpc.ForwardFromError(codes.InvalidArgument, err) + } + + var wg sync.WaitGroup + var localSubscriptions sync.Map + + defer wg.Wait() + wg.Add(1) + ctx, cancel := context.WithCancel(srv.Context()) + defer cancel() + + defer func() { + subs := make([]string, 0, 32) + localSubscriptions.Range(func(k interface{}, _ interface{}) bool { + subs = append(subs, k.(string)) + return true + }) + + for _, sub := range subs { + err := s.Close(sub, nil) + if err != nil { + log.Errorf("cannot close subscription for events: %v", err) + } + } + }() + + sendChan := make(chan pb.Event, 16) + go func() { + defer wg.Done() + for { + select { + case e := <-sendChan: + err := srv.Send(&e) + if err != nil { + log.Errorf("cannot send event %+v: %v", e, err) + return + } + case <-ctx.Done(): + return + } + } + }() + send := func(senderCtx context.Context, e pb.Event) error { + log.Debugf("subscriptions.SubscribeForEvents.send: %v %+v", e.GetSubscriptionId(), e.GetType()) + + select { + case sendChan <- e: + return nil + case <-ctx.Done(): + return fmt.Errorf("cannot send event: stream context returns error: %v", ctx.Err()) + case <-senderCtx.Done(): + return fmt.Errorf("cannot send event: sender context returns error: %v", ctx.Err()) + } + } + + for { + subReq, err := srv.Recv() + if err == io.EOF { + log.Debugf("subscriptions.SubscribeForEvents: cannot receive events for userID %v: %v", userID, err) + break + } + if err != nil { + return kitNetGrpc.ForwardErrorf(codes.Internal, "cannot receive events: %v", err) + } + + subRes := pb.Event{ + Type: &pb.Event_OperationProcessed_{ + OperationProcessed: &pb.Event_OperationProcessed{ + Token: subReq.Token, + ErrorStatus: &pb.Event_OperationProcessed_ErrorStatus{ + Code: pb.Event_OperationProcessed_ErrorStatus_OK, + }, + }, + }, + } + + if r := subReq.GetCancelSubscription(); r != nil { + err := s.cancelSubscription(&localSubscriptions, r.GetSubscriptionId()) + if err != nil { + subRes.GetOperationProcessed().ErrorStatus.Code = pb.Event_OperationProcessed_ErrorStatus_ERROR + subRes.GetOperationProcessed().ErrorStatus.Message = err.Error() + } + subRes.SubscriptionId = r.GetSubscriptionId() + send(ctx, subRes) + continue + } + + subID, err := uuid.NewV4() + if err != nil { + subRes.GetOperationProcessed().ErrorStatus.Code = pb.Event_OperationProcessed_ErrorStatus_ERROR + subRes.GetOperationProcessed().ErrorStatus.Message = fmt.Sprintf("cannot generate subscription ID: %v", err) + send(ctx, subRes) + continue + } + + subRes.SubscriptionId = subID.String() + localSubscriptions.Store(subRes.SubscriptionId, true) + send(ctx, subRes) + + switch r := subReq.GetFilterBy().(type) { + case *pb.SubscribeForEvents_DevicesEvent: + err = s.SubscribeForDevicesEvent(ctx, userID, resourceProjection, subRes.SubscriptionId, send, r.DevicesEvent) + case *pb.SubscribeForEvents_DeviceEvent: + err = s.SubscribeForDeviceEvent(ctx, userID, resourceProjection, subRes.SubscriptionId, send, r.DeviceEvent) + case *pb.SubscribeForEvents_ResourceEvent: + err = s.SubscribeForResourceEvent(ctx, userID, resourceProjection, subRes.SubscriptionId, send, r.ResourceEvent) + case *pb.SubscribeForEvents_CancelSubscription_: + //handled by cancelation + err = nil + default: + err = fmt.Errorf("not supported") + send(ctx, pb.Event{ + SubscriptionId: subRes.SubscriptionId, + Type: &pb.Event_SubscriptionCanceled_{ + SubscriptionCanceled: &pb.Event_SubscriptionCanceled{ + Reason: err.Error(), + }, + }}) + } + + if err != nil { + localSubscriptions.Delete(subRes.SubscriptionId) + log.Errorf("errors occurs during %T: %v", subReq.GetFilterBy(), err) + } + } + return nil +} diff --git a/grpc-gateway/service/updateResourceValues.go b/grpc-gateway/service/updateResourceValues.go new file mode 100644 index 000000000..52fc8946c --- /dev/null +++ b/grpc-gateway/service/updateResourceValues.go @@ -0,0 +1,174 @@ +package service + +import ( + "context" + "fmt" + "sync/atomic" + + "github.com/go-ocf/go-coap" + "github.com/go-ocf/cloud/grpc-gateway/pb" + extCodes "github.com/go-ocf/cloud/grpc-gateway/pb/codes" + "github.com/go-ocf/cloud/grpc-gateway/pb/errdetails" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + raEvents "github.com/go-ocf/cloud/resource-aggregate/cqrs/events" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + "github.com/gofrs/uuid" + grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/peer" + "google.golang.org/grpc/status" +) + +func statusToGrpcStatus(status pbRA.Status) codes.Code { + switch status { + case pbRA.Status_UNKNOWN: + return codes.Unknown + case pbRA.Status_OK: + return codes.OK + case pbRA.Status_BAD_REQUEST: + return codes.InvalidArgument + case pbRA.Status_UNAUTHORIZED: + return codes.Unauthenticated + case pbRA.Status_FORBIDDEN: + return codes.PermissionDenied + case pbRA.Status_NOT_FOUND: + return codes.NotFound + case pbRA.Status_UNAVAILABLE: + return codes.Unavailable + case pbRA.Status_NOT_IMPLEMENTED: + return codes.Unimplemented + case pbRA.Status_ACCEPTED: + return extCodes.Accepted + } + return extCodes.InvalidCode +} + +func eventContentToContent(s pbRA.Status, c *pbRA.Content) (*pb.Content, error) { + var content *pb.Content + if c != nil { + contentType := c.GetContentType() + if contentType == "" && c.GetCoapContentFormat() >= 0 { + contentType = coap.MediaType(c.GetCoapContentFormat()).String() + } + content = &pb.Content{ + Data: c.GetData(), + ContentType: contentType, + } + } + statusCode := statusToGrpcStatus(s) + switch statusCode { + case codes.OK: + case extCodes.Accepted: + default: + s := status.New(statusCode, "response from device") + if content != nil { + newS, err := s.WithDetails(&errdetails.DeviceError{ + Content: &errdetails.Content{ + Data: content.GetData(), + ContentType: content.GetContentType(), + }, + }) + if err == nil { + s = newS + } + } + return nil, s.Err() + } + return content, nil +} + +func toResponse(processed raEvents.ResourceUpdated) (*pb.UpdateResourceValuesResponse, error) { + content, err := eventContentToContent(processed.GetStatus(), processed.GetContent()) + if err != nil { + return nil, err + } + return &pb.UpdateResourceValuesResponse{ + Content: content, + }, nil +} + +func (r *RequestHandler) waitForUpdateContentResponse(ctx context.Context, deviceID, resourceID string, notify <-chan raEvents.ResourceUpdated, onTimeout func(ctx context.Context, destDeviceId, resourceID string, notify <-chan raEvents.ResourceUpdated) (*pb.UpdateResourceValuesResponse, error)) (*pb.UpdateResourceValuesResponse, error) { + timeoutCtx, cancel := context.WithTimeout(ctx, r.timeoutForRequests) + defer cancel() + select { + case processed := <-notify: + return toResponse(processed) + case <-timeoutCtx.Done(): + return onTimeout(ctx, deviceID, resourceID, notify) + } +} + +func (r *RequestHandler) UpdateResourcesValues(ctx context.Context, req *pb.UpdateResourceValuesRequest) (*pb.UpdateResourceValuesResponse, error) { + accessToken, err := grpc_auth.AuthFromMD(ctx, "bearer") + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.Unauthenticated, "cannot update resource: %v", err)) + } + if req.ResourceId == nil { + return nil, logAndReturnError(status.Errorf(codes.InvalidArgument, "cannot update resource: invalid ResourceId")) + } + deviceID := req.GetResourceId().GetDeviceId() + href := req.GetResourceId().GetResourceLinkHref() + errorMsg := fmt.Sprintf("cannot update resource /%v%v", deviceID, href) + ": %v" + + correlationIDUUID, err := uuid.NewV4() + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.Internal, errorMsg, err)) + } + + correlationID := correlationIDUUID.String() + resourceID := GrpcResourceID2ResourceID(req.ResourceId) + notify := r.updateNotificationContainer.Add(correlationID) + defer r.updateNotificationContainer.Remove(correlationID) + + loaded, err := r.resourceProjection.Register(ctx, deviceID) + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.NotFound, errorMsg, fmt.Errorf("cannot register device to projection: %w", err))) + } + defer r.resourceProjection.Unregister(deviceID) + + if !loaded { + if len(r.resourceProjection.Models(deviceID, resourceID)) == 0 { + err = r.resourceProjection.ForceUpdate(ctx, deviceID, resourceID) + if err != nil { + return nil, logAndReturnError(status.Errorf(codes.NotFound, errorMsg, err)) + } + } + } + + connectionID := "grpc-gateway" + peer, ok := peer.FromContext(ctx) + if ok { + connectionID = peer.Addr.String() + } + seq := atomic.AddUint64(&r.seqNum, 1) + raReq := pbRA.UpdateResourceRequest{ + ResourceId: resourceID, + CorrelationId: correlationID, + ResourceInterface: req.GetResourceInterface(), + Content: &pbRA.Content{ + Data: req.GetContent().GetData(), + ContentType: req.GetContent().GetContentType(), + CoapContentFormat: -1, + }, + CommandMetadata: &pbCQRS.CommandMetadata{ + ConnectionId: connectionID, + Sequence: seq, + }, + } + + _, err = r.resourceAggregateClient.UpdateResource(kitNetGrpc.CtxWithToken(ctx, accessToken), &raReq) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, errorMsg, err)) + } + + timeoutCtx, cancel := context.WithTimeout(ctx, r.timeoutForRequests) + defer cancel() + select { + case processed := <-notify: + return toResponse(processed) + case <-timeoutCtx.Done(): + } + + return nil, logAndReturnError(status.Errorf(codes.DeadlineExceeded, errorMsg, fmt.Errorf("timeout"))) +} diff --git a/grpc-gateway/test/config.go b/grpc-gateway/test/config.go new file mode 100644 index 000000000..294172287 --- /dev/null +++ b/grpc-gateway/test/config.go @@ -0,0 +1,56 @@ +package test + +import ( + "github.com/go-ocf/sdk/schema" + "github.com/go-ocf/sdk/schema/cloud" +) + +var ( + TestDeviceName string + + TestDevsimResources []schema.ResourceLink + TestDevsimBackendResources []schema.ResourceLink +) + +func init() { + TestDeviceName = "devsim-" + MustGetHostname() + TestDevsimResources = []schema.ResourceLink{ + { + Href: "/oic/p", + ResourceTypes: []string{"oic.wk.p"}, + Interfaces: []string{"oic.if.r", "oic.if.baseline"}, + }, + + { + Href: "/oic/d", + ResourceTypes: []string{"oic.d.cloudDevice", "oic.wk.d"}, + Interfaces: []string{"oic.if.r", "oic.if.baseline"}, + }, + + { + Href: "/oc/con", + ResourceTypes: []string{"oic.wk.con"}, + Interfaces: []string{"oic.if.rw", "oic.if.baseline"}, + }, + + { + Href: "/light/1", + ResourceTypes: []string{"core.light"}, + Interfaces: []string{"oic.if.rw", "oic.if.baseline"}, + }, + + { + Href: "/light/2", + ResourceTypes: []string{"core.light"}, + Interfaces: []string{"oic.if.rw", "oic.if.baseline"}, + }, + } + + TestDevsimBackendResources = []schema.ResourceLink{ + schema.ResourceLink{ + Href: cloud.StatusHref, + ResourceTypes: cloud.StatusResourceTypes, + Interfaces: cloud.StatusInterfaces, + }, + } +} diff --git a/grpc-gateway/test/service/service.go b/grpc-gateway/test/service/service.go new file mode 100644 index 000000000..fbf8301e3 --- /dev/null +++ b/grpc-gateway/test/service/service.go @@ -0,0 +1,29 @@ +package service + +import ( + "sync" + "testing" + + "github.com/go-ocf/cloud/grpc-gateway/refImpl" + "github.com/stretchr/testify/require" +) + +func NewGrpcGateway(t *testing.T, cfg refImpl.Config) func() { + t.Log("NewGrpcGateway") + defer t.Log("NewGrpcGateway done") + s, err := refImpl.Init(cfg) + require.NoError(t, err) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + err := s.Serve() + require.NoError(t, err) + }() + + return func() { + s.Close() + wg.Wait() + } +} diff --git a/grpc-gateway/test/test.go b/grpc-gateway/test/test.go new file mode 100644 index 000000000..5bc8100c9 --- /dev/null +++ b/grpc-gateway/test/test.go @@ -0,0 +1,519 @@ +package test + +import ( + "context" + "crypto/tls" + "crypto/x509" + "encoding/pem" + "fmt" + "io/ioutil" + "net" + "os" + "sort" + "sync" + "testing" + "time" + + "github.com/go-ocf/kit/codec/cbor" + "github.com/go-ocf/kit/security/certManager" + "go.uber.org/atomic" + + "github.com/go-ocf/kit/log" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/kit/security" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + "go.mongodb.org/mongo-driver/mongo/options" + + "github.com/globalsign/mgo/bson" + "go.mongodb.org/mongo-driver/mongo" + + "github.com/go-ocf/sdk/schema" + "github.com/go-ocf/sdk/schema/cloud" + + coapgwService "github.com/go-ocf/cloud/coap-gateway/test/service" + "github.com/go-ocf/cloud/grpc-gateway/pb" + "github.com/go-ocf/cloud/grpc-gateway/refImpl" + "github.com/go-ocf/cloud/grpc-gateway/service" + ra "github.com/go-ocf/cloud/resource-aggregate/refImpl" + raService "github.com/go-ocf/cloud/resource-aggregate/test/service" + rd "github.com/go-ocf/cloud/resource-directory/refImpl" + rdService "github.com/go-ocf/cloud/resource-directory/test/service" + "github.com/go-ocf/sdk/local/core" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + authConfig "github.com/go-ocf/cloud/authorization/service" + authService "github.com/go-ocf/cloud/authorization/test/service" + + coapGW "github.com/go-ocf/cloud/coap-gateway/refImpl" + + "github.com/go-ocf/cloud/authorization/provider" +) + +const AUTH_HOST = "localhost:7005" +const AUTH_HTTP_HOST = "localhost:7006" +const GW_HOST = "localhost:55684" +const RESOURCE_AGGREGATE_HOST = "localhost:9083" +const RESOURCE_DIRECTORY_HOST = "localhost:9082" +const GRPC_HOST = "localhost:9086" +const TEST_TIMEOUT = time.Second * 15 + +func ClearDB(ctx context.Context, t *testing.T) { + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + + client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017").SetTLSConfig(&tlsConfig)) + require.NoError(t, err) + dbs, err := client.ListDatabaseNames(ctx, bson.M{}) + if mongo.ErrNilDocument == err { + return + } + require.NoError(t, err) + for _, db := range dbs { + if db == "admin" { + continue + } + err = client.Database(db).Drop(ctx) + require.NoError(t, err) + } + err = client.Disconnect(ctx) + require.NoError(t, err) +} + +func SetUp(ctx context.Context, t *testing.T) (TearDown func()) { + ClearDB(ctx, t) + + var authCfg authConfig.Config + err := envconfig.Process("", &authCfg) + require.NoError(t, err) + authCfg.Addr = AUTH_HOST + authCfg.HTTPAddr = AUTH_HTTP_HOST + authCfg.Device.Provider = "test" + authShutdown := authService.NewAuthServer(t, authCfg) + + var rdCfg rd.Config + err = envconfig.Process("", &rdCfg) + require.NoError(t, err) + rdCfg.Service.Addr = RESOURCE_DIRECTORY_HOST + rdCfg.Service.AuthServerAddr = AUTH_HOST + rdCfg.Service.FQDN = "resource-directory-" + t.Name() + rdShutdown := rdService.NewResourceDirectory(t, rdCfg) + + var raCfg ra.Config + err = envconfig.Process("", &raCfg) + require.NoError(t, err) + //raCfg.Log.Debug = true + raCfg.Service.Addr = RESOURCE_AGGREGATE_HOST + raCfg.Service.AuthServerAddr = AUTH_HOST + raShutdown := raService.NewResourceAggregate(t, raCfg) + + var gwCfg coapGW.Config + err = envconfig.Process("", &gwCfg) + require.NoError(t, err) + //gwCfg.Log.Debug = true + gwCfg.Service.Addr = GW_HOST + gwCfg.Service.AuthServerAddr = AUTH_HOST + gwCfg.Service.ResourceAggregateAddr = RESOURCE_AGGREGATE_HOST + gwCfg.Service.ResourceDirectoryAddr = RESOURCE_DIRECTORY_HOST + gwCfg.Service.FQDN = "coap-gateway-" + t.Name() + gwShutdown := coapgwService.NewCoapGateway(t, gwCfg) + + var grpcCfg refImpl.Config + err = envconfig.Process("", &grpcCfg) + require.NoError(t, err) + //grpcCfg.Log.Debug = true + grpcCfg.Service.Addr = GRPC_HOST + grpcCfg.Service.AuthServerAddr = AUTH_HOST + grpcCfg.Service.ResourceAggregateAddr = RESOURCE_AGGREGATE_HOST + grpcCfg.Service.ResourceDirectoryAddr = RESOURCE_DIRECTORY_HOST + grpcCfg.Service.FQDN = "grpc-gateway-" + t.Name() + grpcCfg.UserDevicesManagerExpiration = time.Second * 1 + grpcCfg.UserDevicesManagerTickFrequency = time.Millisecond * 500 + grpcCfg.Service.OAuth.Endpoint.TokenURL = "https://" + AUTH_HTTP_HOST + "/api/authz/token" + grpcShutdown := NewGrpcGateway(t, grpcCfg) + + return func() { + grpcShutdown() + gwShutdown() + raShutdown() + rdShutdown() + authShutdown() + } +} + +type serviceRunner interface { + Serve(net.Listener) error + Shutdown() +} + +func startService(t *testing.T, addr string, s serviceRunner) func() { + lis, err := net.Listen("tcp", addr) + require.NoError(t, err) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + s.Serve(lis) + }() + + return func() { + s.Shutdown() + wg.Wait() + } +} + +func NewGrpcGateway(t *testing.T, config refImpl.Config) func() { + log.Setup(config.Log) + log.Info(config.String()) + listenCertManager, err := certManager.NewCertManager(config.Listen) + require.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(config.Dial) + require.NoError(t, err) + auth := kitNetGrpc.MakeAuthInterceptors(func(ctx context.Context, method string) (context.Context, error) { + return ctx, nil + }) + serverTLSConfig := listenCertManager.GetServerTLSConfig() + serverTLSConfig.ClientAuth = tls.NoClientCert + server, err := kitNetGrpc.NewServer(config.Service.Addr, grpc.Creds(credentials.NewTLS(&serverTLSConfig)), auth.Stream(), auth.Unary()) + require.NoError(t, err) + server.AddCloseFunc(func() { + listenCertManager.Close() + dialCertManager.Close() + }) + err = service.AddHandler(server, config.HandlerConfig, dialCertManager.GetClientTLSConfig()) + require.NoError(t, err) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + server.Serve() + }() + + return func() { + server.Close() + wg.Wait() + } +} + +func OnboardDevSim(ctx context.Context, t *testing.T, c pb.GrpcGatewayClient, deviceID string, gwHost string, expectedResources []schema.ResourceLink) func() { + log.Setup(log.Config{Debug: false}) + client := core.NewClient() + dev, links, err := client.GetDevice(ctx, deviceID) + require.NoError(t, err) + devLink, ok := links.GetResourceLink("/oic/d") + require.True(t, ok) + patchedLinks := make(schema.ResourceLinks, 0, len(links)) + for _, l := range links { + if len(l.Endpoints) == 0 { + l.Endpoints = devLink.Endpoints + } + patchedLinks = append(patchedLinks, l) + } + link, ok := patchedLinks.GetResourceLink("/CoapCloudConfResURI") + require.True(t, ok) + + err = dev.UpdateResource(ctx, link, cloud.ConfigurationUpdateRequest{ + AuthorizationProvider: "test", + URL: "coap+tcp://" + gwHost, + AuthorizationCode: "authCode", + CloudID: "sid", + }, nil) + require.NoError(t, err) + + waitForDevice(ctx, t, c, deviceID, expectedResources) + + return func() { + err = dev.UpdateResource(ctx, link, cloud.ConfigurationUpdateRequest{ + AuthorizationProvider: "", + URL: "", + AuthorizationCode: "", + }, nil) + require.NoError(t, err) + dev.Close(ctx) + } +} + +func waitForDevice(ctx context.Context, t *testing.T, c pb.GrpcGatewayClient, deviceID string, expectedResources []schema.ResourceLink) { + ctx = kitNetGrpc.CtxWithToken(ctx, provider.UserToken) + client, err := c.SubscribeForEvents(ctx) + require.NoError(t, err) + + err = client.Send(&pb.SubscribeForEvents{ + Token: "testToken", + FilterBy: &pb.SubscribeForEvents_DevicesEvent{ + DevicesEvent: &pb.SubscribeForEvents_DevicesEventFilter{ + FilterEvents: []pb.SubscribeForEvents_DevicesEventFilter_Event{ + pb.SubscribeForEvents_DevicesEventFilter_ONLINE, + }, + }, + }, + }) + require.NoError(t, err) + ev, err := client.Recv() + require.NoError(t, err) + expectedEvent := &pb.Event{ + SubscriptionId: ev.SubscriptionId, + Type: &pb.Event_OperationProcessed_{ + OperationProcessed: &pb.Event_OperationProcessed{ + Token: "testToken", + ErrorStatus: &pb.Event_OperationProcessed_ErrorStatus{ + Code: pb.Event_OperationProcessed_ErrorStatus_OK, + }, + }, + }, + } + require.Equal(t, expectedEvent, ev) + + ev, err = client.Recv() + require.NoError(t, err) + expectedEvent = &pb.Event{ + SubscriptionId: ev.SubscriptionId, + Type: &pb.Event_DeviceOnline_{ + DeviceOnline: &pb.Event_DeviceOnline{ + DeviceId: deviceID, + }, + }, + } + require.Equal(t, expectedEvent, ev) + + err = client.Send(&pb.SubscribeForEvents{ + Token: "testToken", + FilterBy: &pb.SubscribeForEvents_DeviceEvent{ + DeviceEvent: &pb.SubscribeForEvents_DeviceEventFilter{ + DeviceId: deviceID, + FilterEvents: []pb.SubscribeForEvents_DeviceEventFilter_Event{ + pb.SubscribeForEvents_DeviceEventFilter_RESOURCE_PUBLISHED, + }, + }, + }, + }) + require.NoError(t, err) + ev, err = client.Recv() + require.NoError(t, err) + expectedEvent = &pb.Event{ + Type: &pb.Event_OperationProcessed_{ + OperationProcessed: &pb.Event_OperationProcessed{ + Token: "testToken", + ErrorStatus: &pb.Event_OperationProcessed_ErrorStatus{ + Code: pb.Event_OperationProcessed_ErrorStatus_OK, + }, + }, + }, + } + expectedEvent.SubscriptionId = ev.SubscriptionId + require.Equal(t, expectedEvent, ev) + subOnPublishedId := ev.SubscriptionId + + expectedEvents := ResourceLinksToExpectedPublishEvents(deviceID, expectedResources) + + for len(expectedEvents) > 0 { + ev, err = client.Recv() + require.NoError(t, err) + ev.SubscriptionId = "" + key := ev.GetResourcePublished().GetLink().GetDeviceId() + ev.GetResourcePublished().GetLink().GetHref() + require.Equal(t, expectedEvents[key], ev) + delete(expectedEvents, key) + } + + err = client.Send(&pb.SubscribeForEvents{ + Token: "testToken", + FilterBy: &pb.SubscribeForEvents_CancelSubscription_{ + CancelSubscription: &pb.SubscribeForEvents_CancelSubscription{ + SubscriptionId: subOnPublishedId, + }, + }, + }) + require.NoError(t, err) + + ev, err = client.Recv() + require.NoError(t, err) + expectedEvent = &pb.Event{ + SubscriptionId: ev.SubscriptionId, + Type: &pb.Event_SubscriptionCanceled_{ + SubscriptionCanceled: &pb.Event_SubscriptionCanceled{}, + }, + } + require.Equal(t, expectedEvent, ev) + + ev, err = client.Recv() + require.NoError(t, err) + expectedEvent = &pb.Event{ + SubscriptionId: ev.SubscriptionId, + Type: &pb.Event_OperationProcessed_{ + OperationProcessed: &pb.Event_OperationProcessed{ + Token: "testToken", + ErrorStatus: &pb.Event_OperationProcessed_ErrorStatus{ + Code: pb.Event_OperationProcessed_ErrorStatus_OK, + }, + }, + }, + } + require.Equal(t, expectedEvent, ev) + + err = client.CloseSend() + require.NoError(t, err) +} + +func GetRootCertificatePool(t *testing.T) *x509.CertPool { + pool := security.NewDefaultCertPool(nil) + dat, err := ioutil.ReadFile(os.Getenv("LISTEN_ACME_CA_POOL")) + require.NoError(t, err) + ok := pool.AppendCertsFromPEM(dat) + require.True(t, ok) + return pool +} + +func GetRootCertificateAuthorities(t *testing.T) []*x509.Certificate { + dat, err := ioutil.ReadFile(os.Getenv("LISTEN_ACME_CA_POOL")) + require.NoError(t, err) + r := make([]*x509.Certificate, 0, 4) + for { + block, rest := pem.Decode(dat) + require.NotNil(t, block) + certs, err := x509.ParseCertificates(block.Bytes) + require.NoError(t, err) + r = append(r, certs...) + if len(rest) == 0 { + break + } + } + + return r +} + +func MustGetHostname() string { + n, err := os.Hostname() + if err != nil { + panic(err) + } + return n +} + +func MustFindDeviceByName(name string) (deviceID string) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + deviceID, err := FindDeviceByName(ctx, name) + if err != nil { + panic(err) + } + return deviceID +} + +type findDeviceIDByNameHandler struct { + id atomic.Value + name string + cancel context.CancelFunc +} + +func (h *findDeviceIDByNameHandler) Handle(ctx context.Context, device *core.Device, deviceLinks schema.ResourceLinks) { + defer device.Close(ctx) + l, ok := deviceLinks.GetResourceLink("/oic/d") + if !ok { + return + } + var d schema.Device + err := device.GetResource(ctx, l, &d) + if err != nil { + return + } + if d.Name == h.name { + h.id.Store(d.ID) + h.cancel() + } +} + +func (h *findDeviceIDByNameHandler) Error(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, + } + + err := client.GetDevices(ctx, &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 +} + +func DecodeCbor(t *testing.T, data []byte) interface{} { + var v interface{} + err := cbor.Decode(data, &v) + require.NoError(t, err) + return v +} + +func EncodeToCbor(t *testing.T, v interface{}) []byte { + d, err := cbor.Encode(v) + require.NoError(t, err) + return d +} + +func ResourceLinksToExpectedPublishEvents(deviceID string, links []schema.ResourceLink) map[string]*pb.Event { + e := make(map[string]*pb.Event) + for _, l := range links { + e[deviceID+l.Href] = &pb.Event{ + Type: &pb.Event_ResourcePublished_{ + ResourcePublished: &pb.Event_ResourcePublished{ + Link: &pb.ResourceLink{ + Href: l.Href, + Types: l.ResourceTypes, + Interfaces: l.Interfaces, + DeviceId: deviceID, + }, + }, + }, + } + } + return e +} + +func GetAllBackendResourceLinks() []schema.ResourceLink { + return append(TestDevsimResources, TestDevsimBackendResources...) +} + +func ConvertSchemaToPb(deviceID string, s []schema.ResourceLink) []pb.ResourceLink { + r := make([]pb.ResourceLink, 0, len(s)) + for _, l := range s { + r = append(r, pb.ResourceLink{ + Href: l.Href, + DeviceId: deviceID, + Types: l.ResourceTypes, + Interfaces: l.Interfaces, + }) + } + return r +} + +type SortResourcesByHref []pb.ResourceLink + +func (a SortResourcesByHref) Len() int { return len(a) } +func (a SortResourcesByHref) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a SortResourcesByHref) Less(i, j int) bool { + return a[i].Href < a[j].Href +} + +func SortResources(s []pb.ResourceLink) []pb.ResourceLink { + v := SortResourcesByHref(s) + sort.Sort(v) + return v +} diff --git a/openapi-connector/.dockerignore b/openapi-connector/.dockerignore new file mode 100644 index 000000000..f6f32bc9e --- /dev/null +++ b/openapi-connector/.dockerignore @@ -0,0 +1,5 @@ +.git +.github +.gitignore +.travis.yml +Makefile diff --git a/openapi-connector/Dockerfile b/openapi-connector/Dockerfile new file mode 100644 index 000000000..749984282 --- /dev/null +++ b/openapi-connector/Dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.13.5-alpine3.10 AS build +RUN apk add --no-cache curl git build-base +WORKDIR $GOPATH/src/github.com/go-ocf/cloud/openapi-connector +COPY . . +RUN go mod download +RUN go build -o /go/bin/service ./cmd/service + +FROM alpine:3.11 as service +RUN apk add --no-cache ca-certificates +COPY --from=build /go/bin/service /usr/local/bin/service +ENTRYPOINT ["/usr/local/bin/service"] \ No newline at end of file diff --git a/openapi-connector/Makefile b/openapi-connector/Makefile new file mode 100644 index 000000000..bf54f2a84 --- /dev/null +++ b/openapi-connector/Makefile @@ -0,0 +1,32 @@ +SHELL = /bin/bash +SERVICE_NAME = $(notdir $(CURDIR)) +LATEST_TAG = vnext +VERSION_TAG = vnext-$(shell git rev-parse --short=7 --verify HEAD) + +default: build + +define build-docker-image + docker build \ + --network=host \ + --tag ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) \ + --tag ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) \ + --target $(1) \ + --file Dockerfile \ + . +endef + +build-servicecontainer: + $(call build-docker-image,service) + +build: build-servicecontainer + +push: build-servicecontainer + docker push ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) + docker push ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) + +proto/generate: + +.PHONY: build-servicecontainer build push proto/generate + + + diff --git a/openapi-connector/README.md b/openapi-connector/README.md new file mode 100644 index 000000000..be8a4a848 --- /dev/null +++ b/openapi-connector/README.md @@ -0,0 +1,18 @@ +[![Go Report](https://goreportcard.com/badge/github.com/go-ocf/cloud/openapi-connector)](https://goreportcard.com/report/github.com/go-ocf/cloud/openapi-connector) +[![Gitter](https://badges.gitter.im/ocfcloud/Lobby.svg)](https://gitter.im/ocfcloud/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +# openapi-connector + +# Build + +## Docker + +```sh +make build-servicecontainer +``` +## Local machine + +```sh +dep ensure -v --vendor-only +go build ./cmd/coap-gateway-service/ +``` \ No newline at end of file diff --git a/openapi-connector/cmd/service/main.go b/openapi-connector/cmd/service/main.go new file mode 100644 index 000000000..e75b45019 --- /dev/null +++ b/openapi-connector/cmd/service/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "github.com/go-ocf/kit/log" + "github.com/go-ocf/cloud/openapi-connector/refImpl" + "github.com/kelseyhightower/envconfig" +) + +func main() { + var config refImpl.Config + if err := envconfig.Process("", &config); err != nil { + log.Fatalf("cannot parse configuration: %v", err) + } + if server, err := refImpl.Init(config); err != nil { + log.Fatalf("cannot init server: %v", err) + } else { + if err = server.Serve(); err != nil { + log.Fatalf("unexpected ends: %v", err) + } + } +} diff --git a/openapi-connector/events/device.go b/openapi-connector/events/device.go new file mode 100644 index 000000000..116f63a59 --- /dev/null +++ b/openapi-connector/events/device.go @@ -0,0 +1,8 @@ +package events + +import ( + "github.com/go-ocf/sdk/schema" +) + +type ResourcesPublished []schema.ResourceLink +type ResourcesUnpublished []schema.ResourceLink diff --git a/openapi-connector/events/devices.go b/openapi-connector/events/devices.go new file mode 100644 index 000000000..d1d92b8c1 --- /dev/null +++ b/openapi-connector/events/devices.go @@ -0,0 +1,10 @@ +package events + +type Device struct { + ID string `json:"di"` +} + +type DevicesOnline []Device +type DevicesOffline []Device +type DevicesRegistered []Device +type DevicesUnregistered []Device diff --git a/openapi-connector/events/event.go b/openapi-connector/events/event.go new file mode 100644 index 000000000..ffa6ccd31 --- /dev/null +++ b/openapi-connector/events/event.go @@ -0,0 +1,172 @@ +package events + +import ( + "compress/gzip" + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "fmt" + "io" + "net/http" + "strconv" + "strings" + "time" + + "github.com/go-ocf/go-coap" + + "github.com/go-ocf/kit/codec/cbor" + "github.com/go-ocf/kit/codec/json" +) + +const CorrelationIDKey = "Correlation-ID" +const SubscriptionIDKey = "Subscription-ID" +const ContentTypeKey = "Content-Type" +const EventTypeKey = "Event-Type" +const SequenceNumberKey = "Sequence-Number" +const EventTimestampKey = "Event-Timestamp" +const EventSignatureKey = "Event-Signature" +const AcceptEncodingKey = "Accept-Encoding" +const ContentEncodingKey = "Content-Encoding" + +var ContentType_JSON = coap.AppJSON.String() +var ContentType_CBOR = coap.AppCBOR.String() +var ContentType_VNDOCFCBOR = coap.AppOcfCbor.String() + +type EventHeader struct { + CorrelationID string + SubscriptionID string + ContentType string + EventType EventType + SequenceNumber uint64 + EventTimestamp time.Time + EventSignature string + AcceptEncoding []string + ContentEncoding string +} + +func ParseEventHeader(r *http.Request) (h EventHeader, _ error) { + correlationID := r.Header.Get(CorrelationIDKey) + if correlationID == "" { + return h, fmt.Errorf("invalid " + CorrelationIDKey) + } + subscriptionID := r.Header.Get(SubscriptionIDKey) + if subscriptionID == "" { + return h, fmt.Errorf("invalid " + SubscriptionIDKey) + } + eventType := EventType(r.Header.Get(EventTypeKey)) + switch eventType { + case EventType_ResourceChanged, + EventType_ResourcesPublished, EventType_ResourcesUnpublished, + EventType_DevicesOnline, EventType_DevicesOffline, EventType_DevicesRegistered, EventType_DevicesUnregistered, + EventType_SubscriptionCanceled: + default: + return h, fmt.Errorf("invalid "+EventTypeKey+"(%v)", eventType) + } + + contentType := r.Header.Get(ContentTypeKey) + switch contentType { + case "": + switch eventType { + case EventType_SubscriptionCanceled: + default: + return h, fmt.Errorf("invalid " + ContentTypeKey) + } + case ContentType_JSON: + case ContentType_CBOR: + default: + return h, fmt.Errorf("invalid "+ContentTypeKey+"(%v)", contentType) + } + + seqNum := r.Header.Get(SequenceNumberKey) + if seqNum == "" { + return h, fmt.Errorf("invalid " + SequenceNumberKey) + } + sequenceNumber, err := strconv.ParseUint(seqNum, 10, 64) + if err != nil { + return h, fmt.Errorf("invalid "+SequenceNumberKey+"(%v): %v", seqNum, err) + } + + evTimestamp := r.Header.Get(EventTimestampKey) + if evTimestamp == "" { + return h, fmt.Errorf("invalid " + EventTimestampKey) + } + eventTimestamp, err := strconv.ParseInt(evTimestamp, 10, 64) + if err != nil { + return h, fmt.Errorf("invalid "+EventTimestampKey+"(%v): %v", evTimestamp, err) + } + eventSignature := r.Header.Get(EventSignatureKey) + if eventSignature == "" { + return h, fmt.Errorf("invalid " + EventSignatureKey) + } + + contentEncoding := r.Header.Get(ContentEncodingKey) + + var acceptEncoding []string + v := r.Header.Get(AcceptEncodingKey) + if r.Method == "POST" && v != "" { + acceptEncoding = strings.Split(v, ",") + if len(acceptEncoding) != 1 { + return h, fmt.Errorf("invalid "+AcceptEncodingKey+"(%+v): more than 1", acceptEncoding) + } + } + + return EventHeader{ + CorrelationID: correlationID, + SubscriptionID: subscriptionID, + ContentType: contentType, + EventType: eventType, + SequenceNumber: sequenceNumber, + EventTimestamp: time.Unix(eventTimestamp, 0), + EventSignature: eventSignature, + ContentEncoding: contentEncoding, + AcceptEncoding: acceptEncoding, + }, nil +} + +func getContentEncoder(ct string, decoder func(w io.Reader, v interface{}) error) (func(w io.Reader, v interface{}) error, error) { + switch ct { + case "gzip": + return func(w io.Reader, v interface{}) error { + reader, err := gzip.NewReader(w) + if err != nil { + return fmt.Errorf("cannot create gzip reader: %v", err) + } + return decoder(reader, v) + }, nil + case "": + return decoder, nil + default: + return nil, fmt.Errorf("content encoder %v not found", ct) + } +} + +func (h EventHeader) GetContentDecoder() (func(w []byte, v interface{}) error, error) { + var decoder func(w []byte, v interface{}) error + switch h.ContentType { + case ContentType_JSON: + decoder = json.Decode + case ContentType_CBOR, ContentType_VNDOCFCBOR: + decoder = cbor.Decode + } + if decoder == nil { + return nil, fmt.Errorf("%v decoder not found", h.ContentType) + } + + return decoder, nil +} + +func CalculateEventSignature(secret, contentType string, eventType EventType, subscriptionID string, seqNum uint64, timeStamp time.Time, body []byte) string { + hash := hmac.New(sha256.New, []byte(secret)) + hash.Write([]byte(contentType)) + hash.Write([]byte(":")) + hash.Write([]byte(eventType)) + hash.Write([]byte(":")) + hash.Write([]byte(subscriptionID)) + hash.Write([]byte(":")) + hash.Write([]byte(strconv.FormatUint(seqNum, 10))) + hash.Write([]byte(":")) + hash.Write([]byte(strconv.FormatInt(timeStamp.Unix(), 10))) + hash.Write([]byte(":")) + hash.Write([]byte(body)) + return hex.EncodeToString(hash.Sum(nil)) +} diff --git a/openapi-connector/events/event_test.go b/openapi-connector/events/event_test.go new file mode 100644 index 000000000..1c012b5ef --- /dev/null +++ b/openapi-connector/events/event_test.go @@ -0,0 +1,44 @@ +package events + +import ( + "testing" + "time" +) + +func TestCalculateEventSignature(t *testing.T) { + type args struct { + secret string + contentType string + eventType EventType + subscriptionID string + seqNum uint64 + timeStamp time.Time + body []byte + } + tests := []struct { + name string + args args + want string + }{ + { + name: "nil-body", + args: args{ + secret: "a", + contentType: "b", + eventType: EventType_DevicesOnline, + subscriptionID: "c", + seqNum: 0, + timeStamp: time.Unix(1, -1), + body: nil, + }, + want: "72150f5f9795e728fa594ece9fa6aa2f0e8877e8d36be89246782cfed00216c3", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := CalculateEventSignature(tt.args.secret, tt.args.contentType, tt.args.eventType, tt.args.subscriptionID, tt.args.seqNum, tt.args.timeStamp, tt.args.body); got != tt.want { + t.Errorf("CalculateEventSignature() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/openapi-connector/events/resource.go b/openapi-connector/events/resource.go new file mode 100644 index 000000000..2aa6c68db --- /dev/null +++ b/openapi-connector/events/resource.go @@ -0,0 +1,3 @@ +package events + +type ResourceChanged []byte diff --git a/openapi-connector/events/subscription.go b/openapi-connector/events/subscription.go new file mode 100644 index 000000000..19f5cadfc --- /dev/null +++ b/openapi-connector/events/subscription.go @@ -0,0 +1,24 @@ +package events + +type EventType string + +const ( + EventType_ResourceChanged EventType = "resource_contentchanged" + EventType_ResourcesPublished EventType = "resources_published" + EventType_ResourcesUnpublished EventType = "resources_unpublished" + EventType_DevicesOnline EventType = "devices_online" + EventType_DevicesOffline EventType = "devices_offline" + EventType_DevicesRegistered EventType = "devices_registered" + EventType_DevicesUnregistered EventType = "devices_unregistered" + EventType_SubscriptionCanceled EventType = "subscription_canceled" +) + +type SubscriptionRequest struct { + URL string `json:"eventsUrl"` + EventTypes []EventType `json:"eventTypes"` + SigningSecret string `json:"signingSecret"` +} + +type SubscriptionResponse struct { + SubscriptionId string `json:"subscriptionId"` +} diff --git a/openapi-connector/refImpl/refImpl.go b/openapi-connector/refImpl/refImpl.go new file mode 100644 index 000000000..debb9fcc3 --- /dev/null +++ b/openapi-connector/refImpl/refImpl.go @@ -0,0 +1,69 @@ +package refImpl + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/go-ocf/kit/log" + "github.com/go-ocf/kit/security/certManager" + "github.com/go-ocf/cloud/openapi-connector/service" + storeMongodb "github.com/go-ocf/cloud/openapi-connector/store/mongodb" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventbus/nats" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/mongodb" + "github.com/panjf2000/ants" +) + +type Config struct { + Log log.Config `envconfig:"LOG"` + MongoDB mongodb.Config `envconfig:"MONGODB"` + Nats nats.Config `envconfig:"NATS"` + Service service.Config + GoRoutinePoolSize int `envconfig:"GOROUTINE_POOL_SIZE" default:"16"` + Dial certManager.Config `envconfig:"DIAL"` + Listen certManager.Config `envconfig:"LISTEN"` + StoreMongoDB storeMongodb.Config +} + +//String return string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} + +func Init(config Config) (*service.Server, error) { + log.Setup(config.Log) + log.Info(config.String()) + dialCertManager, err := certManager.NewCertManager(config.Dial) + if err != nil { + return nil, fmt.Errorf("cannot create dial cert manager %v", err) + } + dialTLSConfig := dialCertManager.GetClientTLSConfig() + + pool, err := ants.NewPool(config.GoRoutinePoolSize) + if err != nil { + return nil, fmt.Errorf("cannot create goroutine pool: %v", err) + } + + resourceEventstore, err := mongodb.NewEventStore(config.MongoDB, pool.Submit, mongodb.WithTLS(&dialTLSConfig)) + if err != nil { + return nil, fmt.Errorf("cannot create resource mongodb eventstore %v", err) + } + + resourceSubscriber, err := nats.NewSubscriber(config.Nats, pool.Submit, func(err error) { log.Errorf("error occurs during receiving event: %v", err) }, nats.WithTLS(&dialTLSConfig)) + if err != nil { + return nil, fmt.Errorf("cannot create resource nats subscriber %v", err) + } + + store, err := storeMongodb.NewStore(context.Background(), config.StoreMongoDB, storeMongodb.WithTLS(&dialTLSConfig)) + if err != nil { + return nil, fmt.Errorf("cannot create mongodb store %v", err) + } + + listenCertManager, err := certManager.NewCertManager(config.Listen) + if err != nil { + return nil, fmt.Errorf("cannot create listen cert manager %v", err) + } + + return service.New(config.Service, dialCertManager, listenCertManager, resourceEventstore, resourceSubscriber, store), nil +} diff --git a/openapi-connector/refImpl/refImpl_test.go b/openapi-connector/refImpl/refImpl_test.go new file mode 100644 index 000000000..7e6b305e4 --- /dev/null +++ b/openapi-connector/refImpl/refImpl_test.go @@ -0,0 +1,27 @@ +package refImpl + +import ( + "os" + "testing" + + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/require" +) + +func TestInit(t *testing.T) { + var config Config + os.Setenv("OAUTH_CALLBACK", "OAUTH_CALLBACK") + os.Setenv("EVENTS_URL", "EVENTS_URL") + os.Setenv("NAME", "NAME") + os.Setenv("CLIENT_ID", "CLIENT_ID") + os.Setenv("CLIENT_SECRET", "CLIENT_SECRET") + os.Setenv("SCOPES", "SCOPES") + os.Setenv("AUTH_URL", "AUTH_URL") + os.Setenv("TOKEN_URL", "TOKEN_URL") + err := envconfig.Process("", &config) + require.NoError(t, err) + + got, err := Init(config) + require.NoError(t, err) + require.NotEmpty(t, got) +} diff --git a/openapi-connector/service/addLinkedAccount.go b/openapi-connector/service/addLinkedAccount.go new file mode 100644 index 000000000..f10348c69 --- /dev/null +++ b/openapi-connector/service/addLinkedAccount.go @@ -0,0 +1,100 @@ +package service + +import ( + "context" + "crypto/rand" + "encoding/base64" + "fmt" + "net/http" + + "github.com/go-ocf/cloud/openapi-connector/store" + "github.com/patrickmn/go-cache" + "golang.org/x/oauth2" +) + +type LinkedCloudHandler struct { + linkedCloud store.LinkedCloud + set bool +} + +func (h *LinkedCloudHandler) Handle(ctx context.Context, iter store.LinkedCloudIter) (err error) { + var s store.LinkedCloud + if iter.Next(ctx, &s) { + h.set = true + h.linkedCloud = s + return iter.Err() + } + return fmt.Errorf("not found") +} + +func generateRandomString(n int) (string, error) { + b := make([]byte, n) + if _, err := rand.Read(b); err != nil { + return "", err + } + return base64.URLEncoding.EncodeToString(b), nil +} + +func (rh *RequestHandler) GetLinkedCloud(ctx context.Context, data LinkedAccountData) (store.LinkedCloud, error) { + switch data.State { + case LinkedAccountState_START: + return rh.originCloud, nil + case LinkedAccountState_PROVISIONED_ORIGIN_CLOUD: + var h LinkedCloudHandler + err := rh.store.LoadLinkedClouds(ctx, store.Query{ID: data.LinkedAccount.TargetCloud.LinkedCloudID}, &h) + if err != nil { + return store.LinkedCloud{}, fmt.Errorf("cannot find linked cloud with ID %v: %v", data.LinkedAccount.TargetCloud.LinkedCloudID, err) + } + return h.linkedCloud, nil + } + return store.LinkedCloud{}, fmt.Errorf("state %v cannot provide linked cloud", data.State) +} + +func (rh *RequestHandler) HandleOAuth(w http.ResponseWriter, r *http.Request, data LinkedAccountData) (int, error) { + linkedCloud, err := rh.GetLinkedCloud(r.Context(), data) + if err != nil { + return http.StatusInternalServerError, err + } + oauth := linkedCloud.ToOAuth2Config() + oauth.RedirectURL = rh.oauthCallback + t, err := generateRandomString(32) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("cannot generate random token") + } + err = rh.provisionCache.Add(t, data, cache.DefaultExpiration) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("cannot store key - collision") + } + url := oauth.AuthCodeURL(t, oauth2.AccessTypeOffline) + if linkedCloud.Audience != "" { + //"https://portal.shared.pluggedin.cloud" + url = oauth.AuthCodeURL(t, oauth2.AccessTypeOffline, oauth2.SetAuthURLParam("audience", linkedCloud.Audience)) + } + http.Redirect(w, r, url, http.StatusTemporaryRedirect) + return http.StatusOK, nil +} + +func (rh *RequestHandler) addLinkedAccount(w http.ResponseWriter, r *http.Request) (int, error) { + l := store.LinkedAccount{ + TargetURL: r.FormValue("target_url"), + TargetCloud: store.OAuth{ + LinkedCloudID: r.FormValue("target_linked_cloud_id"), + }, + } + if l.TargetURL == "" { + return http.StatusBadRequest, fmt.Errorf("invalid target_url") + } + if l.TargetCloud.LinkedCloudID == "" { + return http.StatusBadRequest, fmt.Errorf("invalid target_linked_cloud_id") + } + + data := LinkedAccountData{LinkedAccount: l} + return rh.HandleOAuth(w, r, data) +} + +func (rh *RequestHandler) AddLinkedAccount(w http.ResponseWriter, r *http.Request) { + statusCode, err := rh.addLinkedAccount(w, r) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot add linked account: %v", err), statusCode, w) + } +} diff --git a/openapi-connector/service/addLinkedCloud.go b/openapi-connector/service/addLinkedCloud.go new file mode 100644 index 000000000..5178b4e5b --- /dev/null +++ b/openapi-connector/service/addLinkedCloud.go @@ -0,0 +1,64 @@ +package service + +import ( + "bytes" + "fmt" + "net/http" + + "github.com/go-ocf/cloud/openapi-connector/events" + "github.com/go-ocf/cloud/openapi-connector/store" + "github.com/gofrs/uuid" + + "github.com/go-ocf/kit/codec/json" +) + +func writeJson(w http.ResponseWriter, v interface{}) error { + data, err := json.Encode(v) + if err != nil { + return err + } + w.Header().Set(events.ContentTypeKey, "application/json") + _, err = w.Write(data) + if err != nil { + return err + } + return nil +} + +func (rh *RequestHandler) addLinkedCloud(w http.ResponseWriter, r *http.Request) (int, error) { + buffer := bytes.NewBuffer(make([]byte, 0, 1024)) + _, err := buffer.ReadFrom(r.Body) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot read body: %v", err) + } + var l store.LinkedCloud + err = json.Decode(buffer.Bytes(), &l) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot decode body: %v", err) + } + if l.ID == "" { + uuid, err := uuid.NewV4() + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot generate uuid %v", err) + } + l.ID = uuid.String() + err = rh.store.InsertLinkedCloud(r.Context(), l) + } else { + err = rh.store.UpdateLinkedCloud(r.Context(), l) + } + if err != nil { + return http.StatusBadRequest, err + } + err = writeJson(w, l) + if err != nil { + return http.StatusInternalServerError, err + } + return http.StatusOK, nil +} + +func (rh *RequestHandler) AddLinkedCloud(w http.ResponseWriter, r *http.Request) { + statusCode, err := rh.addLinkedCloud(w, r) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot add linked cloud: %v", err), statusCode, w) + } +} diff --git a/openapi-connector/service/config.go b/openapi-connector/service/config.go new file mode 100644 index 000000000..082189cc9 --- /dev/null +++ b/openapi-connector/service/config.go @@ -0,0 +1,27 @@ +package service + +import ( + "encoding/json" + "fmt" + + "github.com/go-ocf/cloud/openapi-connector/store" + + "github.com/go-ocf/kit/net/grpc" +) + +//Config represent application configuration +type Config struct { + grpc.Config + AuthServerAddr string `envconfig:"AUTH_SERVER_ADDRESS" default:"127.0.0.1:9100"` + ResourceAggregateAddr string `envconfig:"RESOURCE_AGGREGATE_ADDRESS" default:"127.0.0.1:9100"` + FQDN string `envconfig:"FQDN" default:"openapi.pluggedin.cloud"` + OAuthCallback string `envconfig:"OAUTH_CALLBACK" required:"true"` + EventsURL string `envconfig:"EVENTS_URL" required:"true"` + OriginCloud store.LinkedCloud +} + +//String return string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} diff --git a/openapi-connector/service/deleteLinkedAccount.go b/openapi-connector/service/deleteLinkedAccount.go new file mode 100644 index 000000000..919906f82 --- /dev/null +++ b/openapi-connector/service/deleteLinkedAccount.go @@ -0,0 +1,46 @@ +package service + +import ( + "fmt" + "net/http" + + "github.com/go-ocf/cloud/openapi-connector/store" + + "github.com/gorilla/mux" +) + +func (rh *RequestHandler) deleteLinkedAccount(w http.ResponseWriter, r *http.Request) (int, error) { + linkedAccountId, _ := mux.Vars(r)[linkedAccountIdKey] + + var h LinkedAccountHandler + err := rh.store.LoadLinkedAccounts(r.Context(), store.Query{ID: linkedAccountId}, &h) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot load linked account: %v", err) + } + if !h.ok { + return http.StatusBadRequest, fmt.Errorf("cannot load linked account: not found") + } + + var errors []error + + err = rh.subManager.StopSubscriptions(r.Context(), h.linkedAccount) + if err != nil { + errors = append(errors, err) + } + + err = rh.store.RemoveLinkedAccount(r.Context(), linkedAccountId) + if err != nil { + errors = append(errors, err) + } + if len(errors) > 0 { + return http.StatusInternalServerError, fmt.Errorf("%v", errors) + } + return http.StatusOK, nil +} + +func (rh *RequestHandler) DeleteLinkedAccount(w http.ResponseWriter, r *http.Request) { + statusCode, err := rh.deleteLinkedAccount(w, r) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot delete linked accounts: %v", err), statusCode, w) + } +} diff --git a/openapi-connector/service/deleteLinkedCloud.go b/openapi-connector/service/deleteLinkedCloud.go new file mode 100644 index 000000000..c3cccb4a7 --- /dev/null +++ b/openapi-connector/service/deleteLinkedCloud.go @@ -0,0 +1,24 @@ +package service + +import ( + "fmt" + "net/http" + + "github.com/gorilla/mux" +) + +func (rh *RequestHandler) deleteLinkedCloud(w http.ResponseWriter, r *http.Request) (int, error) { + linkedCloudID, _ := mux.Vars(r)[linkedCloudIdKey] + err := rh.store.RemoveLinkedCloud(r.Context(), linkedCloudID) + if err != nil { + return http.StatusBadRequest, err + } + return http.StatusOK, nil +} + +func (rh *RequestHandler) DeleteLinkedCloud(w http.ResponseWriter, r *http.Request) { + statusCode, err := rh.deleteLinkedCloud(w, r) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot delete linked cloud: %v", err), statusCode, w) + } +} diff --git a/openapi-connector/service/deviceSubscription.go b/openapi-connector/service/deviceSubscription.go new file mode 100644 index 000000000..274dd064d --- /dev/null +++ b/openapi-connector/service/deviceSubscription.go @@ -0,0 +1,234 @@ +package service + +import ( + "context" + "fmt" + "strings" + + gocoap "github.com/go-ocf/go-coap" + "github.com/go-ocf/kit/codec/cbor" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + kitHttp "github.com/go-ocf/kit/net/http" + "github.com/go-ocf/cloud/openapi-connector/events" + "github.com/go-ocf/cloud/openapi-connector/store" + raCqrs "github.com/go-ocf/cloud/resource-aggregate/cqrs" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + "github.com/go-ocf/sdk/schema/cloud" + "github.com/gofrs/uuid" + cache "github.com/patrickmn/go-cache" +) + +func (s *SubscribeManager) subscribeToDevice(ctx context.Context, l store.LinkedAccount, correlationID, signingSecret, deviceID string) (string, error) { + resp, err := subscribe(ctx, "/devices/"+deviceID+"/subscriptions", correlationID, events.SubscriptionRequest{ + URL: s.eventsURL, + EventTypes: []events.EventType{ + events.EventType_ResourcesPublished, + events.EventType_ResourcesUnpublished, + }, + SigningSecret: signingSecret, + }, l) + if err != nil { + return "", fmt.Errorf("cannot subscribe to device %v for %v: %v", deviceID, l.ID, err) + } + return resp.SubscriptionId, nil +} + +func cancelDeviceSubscription(ctx context.Context, l store.LinkedAccount, deviceID, subscriptionID string) error { + err := cancelSubscription(ctx, "/devices/"+deviceID+"/subscriptions/"+subscriptionID, l) + if err != nil { + return fmt.Errorf("cannot cancel device subscription for %v: %v", l.ID, err) + } + return nil +} + +func (s *SubscribeManager) updateCloudStatus(ctx context.Context, deviceID string, online bool, authContext pbCQRS.AuthorizationContext, sequence uint64) error { + status := cloud.Status{ + ResourceTypes: cloud.StatusResourceTypes, + Interfaces: cloud.StatusInterfaces, + Online: online, + } + data, err := cbor.Encode(status) + if err != nil { + return err + } + + request := pbRA.NotifyResourceChangedRequest{ + ResourceId: raCqrs.MakeResourceId(deviceID, cloud.StatusHref), + Content: &pbRA.Content{ + ContentType: gocoap.AppOcfCbor.String(), + CoapContentFormat: int32(gocoap.AppOcfCbor), + Data: data, + }, + CommandMetadata: &pbCQRS.CommandMetadata{ + ConnectionId: OpenapiConnectorConnectionId, + Sequence: sequence, + }, + AuthorizationContext: &authContext, + } + + _, err = s.raClient.NotifyResourceChanged(ctx, &request) + return err +} + +func trimDeviceIDFromHref(deviceID, href string) string { + if strings.HasPrefix(href, "/"+deviceID+"/") { + href = strings.TrimPrefix(href, "/"+deviceID) + } + return href +} + +// HandleResourcesPublished publish resources to resource aggregate and subscribes to resources. +func (s *SubscribeManager) HandleResourcesPublished(ctx context.Context, d subscriptionData, header events.EventHeader, links events.ResourcesPublished) error { + userID, err := d.linkedAccount.OriginCloud.AccessToken.GetSubject() + if err != nil { + return fmt.Errorf("cannot get userID: %v", err) + } + var errors []error + for _, link := range links { + endpoints := make([]*pbRA.EndpointInformation, 0, 4) + for _, endpoint := range link.GetEndpoints() { + endpoints = append(endpoints, &pbRA.EndpointInformation{ + Endpoint: endpoint.URI, + Priority: int64(endpoint.Priority), + }) + } + href := trimDeviceIDFromHref(link.DeviceID, link.Href) + resourceId := raCqrs.MakeResourceId(link.DeviceID, kitHttp.CanonicalHref(href)) + _, err := s.raClient.PublishResource(kitNetGrpc.CtxWithToken(ctx, d.linkedAccount.OriginCloud.AccessToken.String()), &pbRA.PublishResourceRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{ + UserId: userID, + DeviceId: link.DeviceID, + }, + ResourceId: resourceId, + Resource: &pbRA.Resource{ + Id: resourceId, + Href: href, + ResourceTypes: link.ResourceTypes, + Interfaces: link.Interfaces, + DeviceId: link.DeviceID, + InstanceId: link.InstanceID, + Anchor: link.Anchor, + Policies: &pbRA.Policies{BitFlags: int32(link.Policy.BitMask)}, + Title: link.Title, + SupportedContentTypes: link.SupportedContentTypes, + EndpointInformations: endpoints, + }, + CommandMetadata: &pbCQRS.CommandMetadata{ + ConnectionId: OpenapiConnectorConnectionId, + Sequence: header.SequenceNumber, + }, + }) + if err != nil { + errors = append(errors, fmt.Errorf("cannot publish resource: %v", err)) + continue + } + + signingSecret, err := generateRandomString(32) + if err != nil { + return fmt.Errorf("cannot generate signingSecret for device subscription: %v", err) + } + correlationID, err := uuid.NewV4() + if err != nil { + return fmt.Errorf("cannot generate correlationID for device subscription: %v", err) + } + + sub := store.Subscription{ + Type: store.Type_Resource, + LinkedAccountID: d.linkedAccount.ID, + DeviceID: link.DeviceID, + Href: href, + SigningSecret: signingSecret, + } + err = s.cache.Add(correlationID.String(), subscriptionData{ + linkedAccount: d.linkedAccount, + subscription: sub, + }, cache.DefaultExpiration) + if err != nil { + errors = append(errors, fmt.Errorf("cannot cache subscription for device subscriptions: %v", err)) + continue + } + sub.SubscriptionID, err = s.subscribeToResource(ctx, d.linkedAccount, correlationID.String(), signingSecret, link.DeviceID, href) + if err != nil { + s.cache.Delete(correlationID.String()) + errors = append(errors, fmt.Errorf("cannot subscribe to device %v resource %v: %v", link.DeviceID, href, err)) + continue + } + _, err = s.store.FindOrCreateSubscription(ctx, sub) + if err != nil { + cancelResourceSubscription(ctx, d.linkedAccount, sub.DeviceID, sub.Href, sub.SubscriptionID) + errors = append(errors, fmt.Errorf("cannot store resource subscription to DB: %v", err)) + continue + } + } + if len(errors) > 0 { + return fmt.Errorf("%v", errors) + } + return nil +} + +// HandleResourcesUnpublished unpublish resources from resource aggregate and cancel resources subscriptions. +func (s *SubscribeManager) HandleResourcesUnpublished(ctx context.Context, d subscriptionData, header events.EventHeader, links events.ResourcesUnpublished) error { + userID, err := d.linkedAccount.OriginCloud.AccessToken.GetSubject() + if err != nil { + return fmt.Errorf("cannot get userID: %v", err) + } + var errors []error + for _, link := range links { + href := trimDeviceIDFromHref(link.DeviceID, link.Href) + _, err := s.raClient.UnpublishResource(kitNetGrpc.CtxWithToken(ctx, d.linkedAccount.OriginCloud.AccessToken.String()), &pbRA.UnpublishResourceRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{ + UserId: userID, + DeviceId: link.DeviceID, + }, + ResourceId: raCqrs.MakeResourceId(link.GetDeviceID(), kitHttp.CanonicalHref(href)), + CommandMetadata: &pbCQRS.CommandMetadata{ + ConnectionId: OpenapiConnectorConnectionId, + Sequence: header.SequenceNumber, + }, + }) + if err != nil { + errors = append(errors, fmt.Errorf("cannot unpublish resource: %v", err)) + } + err = cancelResourceSubscription(ctx, d.linkedAccount, link.DeviceID, href, header.SubscriptionID) + if err != nil { + errors = append(errors, fmt.Errorf("cannot unsubscribe to resource: %v", err)) + } + + err = s.store.RemoveSubscriptions(ctx, store.SubscriptionQuery{SubscriptionID: header.SubscriptionID}) + if err != nil { + errors = append(errors, fmt.Errorf("cannot remove device %v resource %v: %v", link.DeviceID, href, err)) + } + s.cache.Delete(header.CorrelationID) + } + if len(errors) > 0 { + return fmt.Errorf("%v", errors) + } + return nil +} + +// HandleDeviceEvent handles device events. +func (s *SubscribeManager) HandleDeviceEvent(ctx context.Context, header events.EventHeader, body []byte, subscriptionData subscriptionData) error { + contentReader, err := header.GetContentDecoder() + if err != nil { + return fmt.Errorf("cannot get content reader: %v", err) + } + switch header.EventType { + case events.EventType_ResourcesPublished: + var links events.ResourcesPublished + err := contentReader(body, &links) + if err != nil { + return fmt.Errorf("cannot decode device event %v: %v", header.EventType, err) + } + return s.HandleResourcesPublished(ctx, subscriptionData, header, links) + case events.EventType_ResourcesUnpublished: + var links events.ResourcesUnpublished + err := contentReader(body, &links) + if err != nil { + return fmt.Errorf("cannot decode device event %v: %v", header.EventType, err) + } + return s.HandleResourcesUnpublished(ctx, subscriptionData, header, links) + } + + return fmt.Errorf("cannot handle device: unsupported Event-Type %v", header.EventType) +} diff --git a/openapi-connector/service/devicesSubscription.go b/openapi-connector/service/devicesSubscription.go new file mode 100644 index 000000000..bb246068e --- /dev/null +++ b/openapi-connector/service/devicesSubscription.go @@ -0,0 +1,278 @@ +package service + +import ( + "context" + "fmt" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/cloud/openapi-connector/events" + "github.com/go-ocf/cloud/openapi-connector/store" + raCqrs "github.com/go-ocf/cloud/resource-aggregate/cqrs" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + "github.com/go-ocf/sdk/schema/cloud" + "github.com/gofrs/uuid" + "github.com/patrickmn/go-cache" +) + +func (s *SubscribeManager) subscribeToDevices(ctx context.Context, l store.LinkedAccount, correlationID, signingSecret string) (string, error) { + + resp, err := subscribe(ctx, "/devices/subscriptions", correlationID, events.SubscriptionRequest{ + URL: s.eventsURL, + EventTypes: []events.EventType{ + events.EventType_DevicesRegistered, events.EventType_DevicesUnregistered, + events.EventType_DevicesOnline, events.EventType_DevicesOffline, + }, + SigningSecret: signingSecret, + }, l) + if err != nil { + return "", err + } + return resp.SubscriptionId, nil +} + +func cancelDevicesSubscription(ctx context.Context, l store.LinkedAccount, subscriptionID string) error { + err := cancelSubscription(ctx, "/devices/subscriptions/"+subscriptionID, l) + if err != nil { + return fmt.Errorf("cannot cancel devices subscription for %v: %v", l.ID, err) + } + return nil +} + +func (s *SubscribeManager) publishCloudDeviceStatus(ctx context.Context, deviceID string, authCtx pbCQRS.AuthorizationContext, sequence uint64) error { + resource := pbRA.Resource{ + Id: raCqrs.MakeResourceId(deviceID, cloud.StatusHref), + Href: cloud.StatusHref, + ResourceTypes: cloud.StatusResourceTypes, + Interfaces: cloud.StatusInterfaces, + DeviceId: deviceID, + Policies: &pbRA.Policies{ + BitFlags: 3, + }, + Title: "Cloud device status", + } + request := pbRA.PublishResourceRequest{ + AuthorizationContext: &authCtx, + ResourceId: resource.Id, + Resource: &resource, + TimeToLive: 0, + CommandMetadata: &pbCQRS.CommandMetadata{ + Sequence: sequence, + ConnectionId: OpenapiConnectorConnectionId, + }, + } + + _, err := s.raClient.PublishResource(ctx, &request) + if err != nil { + return fmt.Errorf("cannot process command publish resource: %v", err) + } + return nil +} + +func (s *SubscribeManager) HandleDevicesRegistered(ctx context.Context, d subscriptionData, devices events.DevicesRegistered, header events.EventHeader) error { + var errors []error + userID, err := d.linkedAccount.OriginCloud.AccessToken.GetSubject() + if err != nil { + return fmt.Errorf("cannot get userID: %v", err) + } + for _, device := range devices { + ctx := kitNetGrpc.CtxWithToken(ctx, d.linkedAccount.OriginCloud.AccessToken.String()) + _, err := s.asClient.AddDevice(ctx, &pbAS.AddDeviceRequest{ + DeviceId: device.ID, + UserId: userID, + }) + if err != nil { + errors = append(errors, err) + continue + } + authCtx := pbCQRS.AuthorizationContext{ + UserId: userID, + DeviceId: device.ID, + } + + err = s.publishCloudDeviceStatus(ctx, device.ID, authCtx, header.SequenceNumber) + if err != nil { + errors = append(errors, err) + continue + } + + signingSecret, err := generateRandomString(32) + if err != nil { + return fmt.Errorf("cannot generate signingSecret for device subscription: %v", err) + } + corID, err := uuid.NewV4() + if err != nil { + return fmt.Errorf("cannot generate correlationID for devices subscription: %v", err) + } + correlationID := corID.String() + sub := store.Subscription{ + Type: store.Type_Device, + LinkedAccountID: d.linkedAccount.ID, + DeviceID: device.ID, + SigningSecret: signingSecret, + } + err = s.cache.Add(correlationID, subscriptionData{ + linkedAccount: d.linkedAccount, + subscription: sub, + }, cache.DefaultExpiration) + if err != nil { + return fmt.Errorf("cannot cache subscription for device subscriptions: %v", err) + } + sub.SubscriptionID, err = s.subscribeToDevice(ctx, d.linkedAccount, correlationID, signingSecret, device.ID) + if err != nil { + s.cache.Delete(correlationID) + errors = append(errors, fmt.Errorf("cannot subscribe to device %v: %v", device.ID, err)) + continue + } + _, err = s.store.FindOrCreateSubscription(ctx, sub) + if err != nil { + cancelDevicesSubscription(ctx, d.linkedAccount, sub.SubscriptionID) + errors = append(errors, fmt.Errorf("cannot store subscription to DB: %v", err)) + continue + } + loaded, err := s.resourceProjection.Register(ctx, device.ID) + if err != nil { + errors = append(errors, fmt.Errorf("cannot register device %v to resource projection: %v", device.ID, err)) + continue + } + if !loaded { + // we want to be only once registered in projection. + s.resourceProjection.Unregister(device.ID) + } + } + if len(errors) > 0 { + return fmt.Errorf("%v", errors) + } + return nil +} + +func (s *SubscribeManager) HandleDevicesUnregistered(ctx context.Context, subscriptionData subscriptionData, correlationID string, devices events.DevicesUnregistered) error { + userID, err := subscriptionData.linkedAccount.OriginCloud.AccessToken.GetSubject() + if err != nil { + return fmt.Errorf("cannot get userID: %v", err) + } + var errors []error + for _, device := range devices { + err := cancelDeviceSubscription(ctx, subscriptionData.linkedAccount, device.ID, subscriptionData.subscription.SubscriptionID) + if err != nil { + errors = append(errors, fmt.Errorf("cannot cancel subscription to device %v: %v", device.ID, err)) + } + err = s.store.RemoveSubscriptions(ctx, store.SubscriptionQuery{SubscriptionID: subscriptionData.subscription.SubscriptionID}) + if err != nil { + errors = append(errors, fmt.Errorf("cannot remove device %v subscription: %v", device.ID, err)) + } + s.cache.Delete(correlationID) + _, err = s.asClient.RemoveDevice(kitNetGrpc.CtxWithToken(ctx, subscriptionData.linkedAccount.OriginCloud.AccessToken.String()), &pbAS.RemoveDeviceRequest{ + DeviceId: device.ID, + UserId: userID, + }) + if err != nil { + errors = append(errors, fmt.Errorf("cannot remove device %v from user: %v", device.ID, err)) + } + + err = s.resourceProjection.Unregister(device.ID) + if err != nil { + errors = append(errors, fmt.Errorf("cannot unregister device %v from resource projection: %v", device.ID, err)) + } + + } + if len(errors) > 0 { + return fmt.Errorf("%v", errors) + } + return nil +} + +// HandleDevicesOnline sets device online to resource aggregate and register device to projection. +func (s *SubscribeManager) HandleDevicesOnline(ctx context.Context, subscriptionData subscriptionData, header events.EventHeader, devices events.DevicesOnline) error { + var errors []error + for _, device := range devices { + userID, err := subscriptionData.linkedAccount.OriginCloud.AccessToken.GetSubject() + if err != nil { + errors = append(errors, fmt.Errorf("cannot get userID for set device(%v) online: %v", device.ID, err)) + continue + } + authCtx := pbCQRS.AuthorizationContext{ + UserId: userID, + DeviceId: device.ID, + } + + err = s.updateCloudStatus(kitNetGrpc.CtxWithToken(ctx, subscriptionData.linkedAccount.OriginCloud.AccessToken.String()), device.ID, true, authCtx, header.SequenceNumber) + + if err != nil { + errors = append(errors, fmt.Errorf("cannot set device %v to online: %v", device.ID, err)) + } + } + if len(errors) > 0 { + return fmt.Errorf("%v", errors) + } + + return nil +} + +// HandleDevicesOffline sets device off to resource aggregate and unregister device to projection. +func (s *SubscribeManager) HandleDevicesOffline(ctx context.Context, subscriptionData subscriptionData, header events.EventHeader, devices events.DevicesOffline) error { + var errors []error + for _, device := range devices { + userID, err := subscriptionData.linkedAccount.OriginCloud.AccessToken.GetSubject() + if err != nil { + errors = append(errors, fmt.Errorf("cannot get userID for set device(%v) offline: %v", device.ID, err)) + continue + } + authCtx := pbCQRS.AuthorizationContext{ + UserId: userID, + DeviceId: device.ID, + } + + err = s.updateCloudStatus(kitNetGrpc.CtxWithToken(ctx, subscriptionData.linkedAccount.OriginCloud.AccessToken.String()), device.ID, false, authCtx, header.SequenceNumber) + + if err != nil { + errors = append(errors, fmt.Errorf("cannot set device %v to offline: %v", device.ID, err)) + } + } + if len(errors) > 0 { + return fmt.Errorf("%v", errors) + } + + return nil +} + +func (s *SubscribeManager) HandleDevicesEvent(ctx context.Context, header events.EventHeader, body []byte, subscriptionData subscriptionData) error { + contentReader, err := header.GetContentDecoder() + if err != nil { + return fmt.Errorf("cannot handle device event: %v", err) + } + + switch header.EventType { + case events.EventType_DevicesRegistered: + var devices events.DevicesRegistered + err = contentReader(body, &devices) + if err != nil { + return fmt.Errorf("cannot decode devices event: %v", err) + } + return s.HandleDevicesRegistered(ctx, subscriptionData, devices, header) + case events.EventType_DevicesUnregistered: + var devices events.DevicesUnregistered + err = contentReader(body, &devices) + if err != nil { + return fmt.Errorf("cannot decode devices event: %v", err) + } + return s.HandleDevicesUnregistered(ctx, subscriptionData, header.CorrelationID, devices) + case events.EventType_DevicesOnline: + var devices events.DevicesOnline + err = contentReader(body, &devices) + if err != nil { + return fmt.Errorf("cannot decode devices event: %v", err) + } + return s.HandleDevicesOnline(ctx, subscriptionData, header, devices) + case events.EventType_DevicesOffline: + var devices events.DevicesOffline + err = contentReader(body, &devices) + if err != nil { + return fmt.Errorf("cannot decode devices event: %v", err) + } + return s.HandleDevicesOffline(ctx, subscriptionData, header, devices) + } + + return fmt.Errorf("cannot decode devices: unsupported Event-Type %v", header.EventType) +} diff --git a/openapi-connector/service/linkedAccountData.go b/openapi-connector/service/linkedAccountData.go new file mode 100644 index 000000000..e2f68d3e4 --- /dev/null +++ b/openapi-connector/service/linkedAccountData.go @@ -0,0 +1,19 @@ +package service + +import "github.com/go-ocf/cloud/openapi-connector/store" + +type LinkedAccountData struct { + originCloud store.LinkedCloud + LinkedAccount store.LinkedAccount + State LinkedAccountState +} + +type LinkedAccountState uint8 + +const ( + LinkedAccountState_START LinkedAccountState = iota + // OAuth Access Token of the origin cloud has been obtained + LinkedAccountState_PROVISIONED_ORIGIN_CLOUD + // OAuth Access Token of the target cloud has been obtained + LinkedAccountState_PROVISIONED_TARGET_CLOUD +) diff --git a/openapi-connector/service/notifyLinkedAccount.go b/openapi-connector/service/notifyLinkedAccount.go new file mode 100644 index 000000000..b8feb839b --- /dev/null +++ b/openapi-connector/service/notifyLinkedAccount.go @@ -0,0 +1,31 @@ +package service + +import ( + "bytes" + "fmt" + "net/http" + + "github.com/go-ocf/cloud/openapi-connector/events" +) + +func (rh *RequestHandler) notifyLinkedAccount(w http.ResponseWriter, r *http.Request) (int, error) { + header, err := events.ParseEventHeader(r) + if err != nil { + return http.StatusGone, err + } + + b := bytes.NewBuffer(make([]byte, 0, 1024)) + _, err = b.ReadFrom(r.Body) + if err != nil { + return http.StatusGone, err + } + + return rh.subManager.HandleEvent(r.Context(), header, b.Bytes()) +} + +func (rh *RequestHandler) NotifyLinkedAccount(w http.ResponseWriter, r *http.Request) { + statusCode, err := rh.notifyLinkedAccount(w, r) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot notify linked accounts: %v", err), statusCode, w) + } +} diff --git a/openapi-connector/service/oauthCallback.go b/openapi-connector/service/oauthCallback.go new file mode 100644 index 000000000..6f3d349a4 --- /dev/null +++ b/openapi-connector/service/oauthCallback.go @@ -0,0 +1,98 @@ +package service + +import ( + "context" + "fmt" + "net/http" + + "github.com/gofrs/uuid" + + "github.com/go-ocf/cloud/openapi-connector/store" + "golang.org/x/oauth2" +) + +func (rh *RequestHandler) HandleLinkedAccount(ctx context.Context, data LinkedAccountData, authCode string) (LinkedAccountData, error) { + var oauth oauth2.Config + switch data.State { + case LinkedAccountState_START: + oauth = rh.originCloud.ToOAuth2Config() + oauth.RedirectURL = rh.oauthCallback + ctx = context.WithValue(ctx, oauth2.HTTPClient, http.DefaultClient) + token, err := oauth.Exchange(ctx, authCode) + if err != nil { + return data, fmt.Errorf("cannot exchange origin cloud authorization code for access token: %v", err) + } + data.LinkedAccount.OriginCloud.AccessToken = store.AccessToken(token.AccessToken) + data.LinkedAccount.OriginCloud.Expiry = token.Expiry + data.LinkedAccount.OriginCloud.RefreshToken = token.RefreshToken + data.State = LinkedAccountState_PROVISIONED_ORIGIN_CLOUD + return data, nil + case LinkedAccountState_PROVISIONED_ORIGIN_CLOUD: + var h LinkedCloudHandler + err := rh.store.LoadLinkedClouds(ctx, store.Query{ID: data.LinkedAccount.TargetCloud.LinkedCloudID}, &h) + if err != nil { + return data, fmt.Errorf("cannot find linked cloud with ID %v: %v", data.LinkedAccount.TargetCloud.LinkedCloudID, err) + } + oauth = h.linkedCloud.ToOAuth2Config() + oauth.RedirectURL = rh.oauthCallback + ctx = context.WithValue(ctx, oauth2.HTTPClient, http.DefaultClient) + token, err := oauth.Exchange(ctx, authCode) + if err != nil { + return data, fmt.Errorf("cannot exchange target cloud authorization code for access token: %v", err) + } + data.LinkedAccount.TargetCloud.AccessToken = store.AccessToken(token.AccessToken) + data.LinkedAccount.TargetCloud.Expiry = token.Expiry + data.LinkedAccount.TargetCloud.RefreshToken = token.RefreshToken + data.State = LinkedAccountState_PROVISIONED_TARGET_CLOUD + return data, nil + case LinkedAccountState_PROVISIONED_TARGET_CLOUD: + return data, nil + } + return data, fmt.Errorf("unknown state %v", data.State) +} + +func (rh *RequestHandler) oAuthCallback(w http.ResponseWriter, r *http.Request) (int, error) { + authCode := r.FormValue("code") + state := r.FormValue("state") + + linkedAccountData, ok := rh.provisionCache.Get(string(state)) + if !ok { + return http.StatusBadRequest, fmt.Errorf("invalid/expired OAuth state") + } + rh.provisionCache.Delete(string(state)) + + data := linkedAccountData.(LinkedAccountData) + newData, err := rh.HandleLinkedAccount(r.Context(), data, authCode) + if err != nil { + return http.StatusBadRequest, err + } + + switch newData.State { + case LinkedAccountState_PROVISIONED_ORIGIN_CLOUD: + return rh.HandleOAuth(w, r, newData) + case LinkedAccountState_PROVISIONED_TARGET_CLOUD: + id, err := uuid.NewV4() + if err != nil { + return http.StatusInternalServerError, err + } + newData.LinkedAccount.ID = id.String() + err = rh.store.InsertLinkedAccount(r.Context(), newData.LinkedAccount) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot store linked account for url %v: %v", data.LinkedAccount.TargetURL, err) + } + err = rh.subManager.StartSubscriptions(r.Context(), newData.LinkedAccount) + if err != nil { + rh.store.RemoveLinkedAccount(r.Context(), newData.LinkedAccount.ID) + return http.StatusBadRequest, fmt.Errorf("cannot start subscriptions %v: %v", data.LinkedAccount.TargetURL, err) + } + return http.StatusOK, nil + } + return http.StatusInternalServerError, fmt.Errorf("invalid linked account state - %v", newData.State) +} + +func (rh *RequestHandler) OAuthCallback(w http.ResponseWriter, r *http.Request) { + statusCode, err := rh.oAuthCallback(w, r) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot process oauth callback: %v", err), statusCode, w) + } +} diff --git a/openapi-connector/service/requestHandler.go b/openapi-connector/service/requestHandler.go new file mode 100644 index 000000000..aace44a5f --- /dev/null +++ b/openapi-connector/service/requestHandler.go @@ -0,0 +1,113 @@ +package service + +import ( + "net/http" + "net/url" + "time" + + "github.com/patrickmn/go-cache" + + "github.com/go-ocf/kit/log" + "github.com/go-ocf/cloud/openapi-connector/events" + "github.com/go-ocf/cloud/openapi-connector/store" + "github.com/go-ocf/cloud/openapi-connector/uri" + + projectionRA "github.com/go-ocf/cloud/resource-aggregate/cqrs/projection" + router "github.com/gorilla/mux" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +const linkedCloudIdKey = "linkedCloudId" +const linkedAccountIdKey = "linkedCloudId" + +//RequestHandler for handling incoming request +type RequestHandler struct { + originCloud store.LinkedCloud + oauthCallback string + resourceProjection *projectionRA.Projection + store store.Store + + asClient pbAS.AuthorizationServiceClient + raClient pbRA.ResourceAggregateClient + + provisionCache *cache.Cache + subManager *SubscribeManager +} + +func logAndWriteErrorResponse(err error, statusCode int, w http.ResponseWriter) { + log.Errorf("%v", err) + w.Header().Set(events.ContentTypeKey, "text/plain") + w.WriteHeader(statusCode) + w.Write([]byte(err.Error())) +} + +//NewRequestHandler factory for new RequestHandler +func NewRequestHandler( + originCloud store.LinkedCloud, + oauthCallback string, + subManager *SubscribeManager, + asClient pbAS.AuthorizationServiceClient, + raClient pbRA.ResourceAggregateClient, + resourceProjection *projectionRA.Projection, + store store.Store, +) *RequestHandler { + return &RequestHandler{ + originCloud: originCloud, + oauthCallback: oauthCallback, + subManager: subManager, + asClient: asClient, + raClient: raClient, + resourceProjection: resourceProjection, + store: store, + provisionCache: cache.New(5*time.Minute, 10*time.Minute), + } +} + +func loggingMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + log.Debugf("%v %v", r.Method, r.RequestURI) + // Call the next handler, which can be another middleware in the chain, or the final handler. + next.ServeHTTP(w, r) + }) +} + +func healthCheck(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} + +// NewHTTP returns HTTP server +func NewHTTP(requestHandler *RequestHandler) *http.Server { + r := router.NewRouter() + r.Use(loggingMiddleware) + + // health check + r.HandleFunc("/", healthCheck).Methods("GET") + + s := r.PathPrefix(uri.LinkedClouds).Subrouter() + + // retrieve all linked clouds + s.HandleFunc("", requestHandler.RetrieveLinkedClouds).Methods("GET") + // add linked cloud + s.HandleFunc("", requestHandler.AddLinkedCloud).Methods("POST") + // delete linked cloud + s.HandleFunc("/{"+linkedCloudIdKey+"}", requestHandler.DeleteLinkedCloud).Methods("DELETE") + + s = r.PathPrefix(uri.LinkedAccounts).Subrouter() + // add linked account + s.HandleFunc("", requestHandler.AddLinkedAccount).Methods("GET") + // retrieve all linked accounts + s.HandleFunc("/retrieve", requestHandler.RetrieveLinkedAccounts).Methods("GET") + // delete linked cloud + s.HandleFunc("/{"+linkedAccountIdKey+"}", requestHandler.DeleteLinkedAccount).Methods("DELETE") + + // notify linked cloud + r.HandleFunc(uri.NotifyLinkedAccount, requestHandler.NotifyLinkedAccount).Methods("POST") + + // OAuthCallback + oauthURL, _ := url.Parse(requestHandler.oauthCallback) + r.HandleFunc(oauthURL.Path, requestHandler.OAuthCallback).Methods("GET") + + return &http.Server{Handler: r} +} diff --git a/openapi-connector/service/resourceProjection.go b/openapi-connector/service/resourceProjection.go new file mode 100644 index 000000000..661035e61 --- /dev/null +++ b/openapi-connector/service/resourceProjection.go @@ -0,0 +1,319 @@ +package service + +import ( + "bytes" + "context" + "fmt" + "io" + "net/http" + "sync" + + "github.com/go-ocf/cqrs/event" + "github.com/go-ocf/cqrs/eventstore" + coap "github.com/go-ocf/go-coap" + + "github.com/go-ocf/kit/log" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + kitHttp "github.com/go-ocf/kit/net/http" + "github.com/go-ocf/cloud/openapi-connector/events" + "github.com/go-ocf/cloud/openapi-connector/store" + + raEvents "github.com/go-ocf/cloud/resource-aggregate/cqrs/events" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +type resourceCtx struct { + lock sync.Mutex + resource *pbRA.Resource + isPublished bool + content *pbRA.ResourceChanged + pendingContentUpdate []raEvents.ResourceUpdatePending + store store.Store + raClient pbRA.ResourceAggregateClient +} + +func newResourceCtx(store store.Store, raClient pbRA.ResourceAggregateClient) func(context.Context) (eventstore.Model, error) { + return func(context.Context) (eventstore.Model, error) { + return &resourceCtx{ + store: store, + raClient: raClient, + pendingContentUpdate: make([]raEvents.ResourceUpdatePending, 0, 8), + }, nil + } +} + +func (m *resourceCtx) cloneLocked() *resourceCtx { + return &resourceCtx{ + resource: m.resource, + isPublished: m.isPublished, + content: m.content, + } +} + +func (m *resourceCtx) Clone() *resourceCtx { + m.lock.Lock() + defer m.lock.Unlock() + + return m.cloneLocked() +} + +func (m *resourceCtx) onResourcePublishedLocked(ctx context.Context) error { + return nil +} + +func (m *resourceCtx) onResourceUnpublishedLocked(ctx context.Context) error { + return nil +} + +func (m *resourceCtx) onResourceChangedLocked(ctx context.Context) error { + return nil +} + +func makeUpdateHref(url, deviceID, href string) string { + return url + kitHttp.CanonicalHref("devices/"+deviceID+"/"+href) +} + +func updateDeviceResource(deviceID, href, contentType string, content []byte, l store.LinkedAccount) (string, []byte, pbRA.Status, error) { + client := http.Client{} + + r, w := io.Pipe() + + req, err := http.NewRequest("POST", makeUpdateHref(l.TargetURL, deviceID, href), r) + if err != nil { + return "", nil, pbRA.Status_BAD_REQUEST, fmt.Errorf("cannot create post request: %v", err) + } + req.Header.Set(AcceptHeader, events.ContentType_JSON+","+events.ContentType_CBOR+","+events.ContentType_VNDOCFCBOR) + req.Header.Set(events.ContentTypeKey, contentType) + req.Header.Set(AuthorizationHeader, "Bearer "+string(l.TargetCloud.AccessToken)) + + go func() { + defer w.Close() + _, err := w.Write(content) + if err != nil { + log.Errorf("cannot update content of device %v resource %v: %v", deviceID, href, err) + } + }() + httpResp, err := client.Do(req) + if err != nil { + return "", nil, pbRA.Status_UNAVAILABLE, fmt.Errorf("cannot post: %v", err) + } + defer httpResp.Body.Close() + if httpResp.StatusCode != http.StatusOK { + status := pbRA.Status_UNKNOWN + switch status { + case http.StatusAccepted: + status = pbRA.Status_ACCEPTED + case http.StatusOK: + status = pbRA.Status_OK + case http.StatusBadRequest: + status = pbRA.Status_BAD_REQUEST + case http.StatusNotFound: + status = pbRA.Status_NOT_FOUND + case http.StatusNotImplemented: + status = pbRA.Status_NOT_IMPLEMENTED + case http.StatusForbidden: + status = pbRA.Status_FORBIDDEN + case http.StatusUnauthorized: + status = pbRA.Status_UNAUTHORIZED + } + return "", nil, status, fmt.Errorf("unexpected statusCode %v", httpResp.StatusCode) + } + respContentType := httpResp.Header.Get(events.ContentTypeKey) + respContent := bytes.NewBuffer(make([]byte, 0, 1024)) + _, err = respContent.ReadFrom(httpResp.Body) + if err != nil { + return "", nil, pbRA.Status_UNAVAILABLE, fmt.Errorf("cannot read update response: %v", err) + } + + return respContentType, respContent.Bytes(), pbRA.Status_OK, nil +} + +func (m *resourceCtx) onPendingContentUpdate(ctx context.Context) error { + var h SubscriptionHandler + err := m.store.LoadSubscriptions(ctx, []store.SubscriptionQuery{store.SubscriptionQuery{Type: store.Type_Resource, DeviceID: m.resource.DeviceId, Href: m.resource.Href}}, &h) + if err != nil { + return err + } + if !h.ok { + return fmt.Errorf("subscription not found") + } + + var lah LinkedAccountHandler + err = m.store.LoadLinkedAccounts(ctx, store.Query{ID: h.subscription.LinkedAccountID}, &lah) + if err != nil { + return err + } + if !h.ok { + return fmt.Errorf("linked account not found") + } + + linkedAccount, err := lah.linkedAccount.RefreshTokens(ctx, m.store) + if err != nil { + return err + } + + userID, err := linkedAccount.OriginCloud.AccessToken.GetSubject() + if err != nil { + return fmt.Errorf("cannot get userID: %v", err) + } + + for { + if len(m.pendingContentUpdate) == 0 { + break + } + contentType, content, status, err := updateDeviceResource(m.resource.DeviceId, m.resource.Href, m.pendingContentUpdate[0].Content.ContentType, m.pendingContentUpdate[0].Content.Data, linkedAccount) + if err != nil { + log.Errorf("cannot update content of device %v resource %v: %v", m.resource.DeviceId, m.resource.Href, err) + } + coapContentFormat := int32(-1) + + switch contentType { + case coap.AppCBOR.String(): + coapContentFormat = int32(coap.AppCBOR) + case coap.AppOcfCbor.String(): + coapContentFormat = int32(coap.AppOcfCbor) + case coap.AppJSON.String(): + coapContentFormat = int32(coap.AppJSON) + } + + _, err = m.raClient.ConfirmResourceUpdate(kitNetGrpc.CtxWithToken(ctx, linkedAccount.OriginCloud.AccessToken.String()), &pbRA.ConfirmResourceUpdateRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{ + UserId: userID, + }, + ResourceId: m.resource.Id, + CorrelationId: m.pendingContentUpdate[0].AuditContext.CorrelationId, + CommandMetadata: &pbCQRS.CommandMetadata{ + ConnectionId: OpenapiConnectorConnectionId, + //Sequence: header.SequenceNumber, + }, + Content: &pbRA.Content{ + Data: content, + ContentType: contentType, + CoapContentFormat: coapContentFormat, + }, + Status: status, + }) + if err != nil { + log.Errorf("cannot update content of device %v resource %v: %v", m.resource.DeviceId, m.resource.Href, err) + } + m.pendingContentUpdate = m.pendingContentUpdate[1:] + } + return nil +} + +func (m *resourceCtx) SnapshotEventType() string { + s := &raEvents.ResourceStateSnapshotTaken{} + return s.SnapshotEventType() +} + +func (m *resourceCtx) Handle(ctx context.Context, iter event.Iter) error { + var eu event.EventUnmarshaler + var onResourcePublished, onResourceUnpublished, onResourceChanged bool + m.lock.Lock() + defer m.lock.Unlock() + + var anyEventProcessed bool + for iter.Next(ctx, &eu) { + anyEventProcessed = true + log.Debugf("resourceCtx.Handle: DeviceId: %v, ResourceId: %v, Version: %v, EventType: %v", eu.GroupId, eu.AggregateId, eu.Version, eu.EventType) + switch eu.EventType { + case kitHttp.ProtobufContentType(&pbRA.ResourceStateSnapshotTaken{}): + var s raEvents.ResourceStateSnapshotTaken + if err := eu.Unmarshal(&s); err != nil { + return err + } + if !m.isPublished { + onResourcePublished = s.IsPublished + onResourceUnpublished = !s.IsPublished + } + if m.content == nil { + onResourceChanged = true + } else { + onResourceChanged = s.GetLatestResourceChange().GetEventMetadata().GetVersion() > m.content.GetEventMetadata().GetVersion() + } + m.content = s.LatestResourceChange + m.resource = s.Resource + m.isPublished = s.IsPublished + case kitHttp.ProtobufContentType(&pbRA.ResourcePublished{}): + var s raEvents.ResourcePublished + if err := eu.Unmarshal(&s); err != nil { + return err + } + if !m.isPublished { + onResourcePublished = true + onResourceUnpublished = false + } + m.isPublished = true + m.resource = s.Resource + case kitHttp.ProtobufContentType(&pbRA.ResourceUnpublished{}): + if m.isPublished { + onResourcePublished = false + onResourceUnpublished = true + } + m.isPublished = false + case kitHttp.ProtobufContentType(&pbRA.ResourceChanged{}): + var s raEvents.ResourceChanged + if err := eu.Unmarshal(&s); err != nil { + return err + } + if m.content == nil { + onResourceChanged = true + } else { + onResourceChanged = s.GetEventMetadata().GetVersion() > m.content.GetEventMetadata().GetVersion() + } + m.content = &s.ResourceChanged + case kitHttp.ProtobufContentType(&pbRA.ResourceUpdatePending{}): + var s raEvents.ResourceUpdatePending + if err := eu.Unmarshal(&s); err != nil { + return err + } + m.pendingContentUpdate = append(m.pendingContentUpdate, s) + case kitHttp.ProtobufContentType(&pbRA.ResourceUpdated{}): + var s raEvents.ResourceUpdated + if err := eu.Unmarshal(&s); err != nil { + return err + } + tmp := make([]raEvents.ResourceUpdatePending, 0, len(m.pendingContentUpdate)) + for _, cu := range m.pendingContentUpdate { + if cu.AuditContext.CorrelationId != s.AuditContext.CorrelationId { + tmp = append(tmp, cu) + } + } + m.pendingContentUpdate = tmp + } + } + + if !anyEventProcessed { + // if event event not processed, it means that the projection will be reloaded. + return nil + } + + if m.resource == nil { + return fmt.Errorf("DeviceId: %v, ResourceId: %v: invalid resource is stored in eventstore: Resource attribute is not set", eu.GroupId, eu.AggregateId) + } + + if onResourcePublished { + if err := m.onResourcePublishedLocked(ctx); err != nil { + log.Errorf("%v", err) + } + } else if onResourceUnpublished { + if err := m.onResourceUnpublishedLocked(ctx); err != nil { + log.Errorf("%v", err) + } + } + + if onResourceChanged && m.isPublished { + if err := m.onResourceChangedLocked(ctx); err != nil { + log.Errorf("%v", err) + } + } + + if len(m.pendingContentUpdate) > 0 && m.isPublished { + if err := m.onPendingContentUpdate(ctx); err != nil { + log.Errorf("cannot update device %v resource %v: %v", m.resource.DeviceId, m.resource.Href, err) + } + } + + return nil +} diff --git a/openapi-connector/service/resourceSubscription.go b/openapi-connector/service/resourceSubscription.go new file mode 100644 index 000000000..101566e00 --- /dev/null +++ b/openapi-connector/service/resourceSubscription.go @@ -0,0 +1,84 @@ +package service + +import ( + "context" + "fmt" + + "github.com/go-ocf/go-coap" + + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + kitHttp "github.com/go-ocf/kit/net/http" + "github.com/go-ocf/cloud/openapi-connector/events" + "github.com/go-ocf/cloud/openapi-connector/store" + raCqrs "github.com/go-ocf/cloud/resource-aggregate/cqrs" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +func (s *SubscribeManager) subscribeToResource(ctx context.Context, l store.LinkedAccount, correlationID, signingSecret, deviceID, resourceHrefLink string) (string, error) { + resp, err := subscribe(ctx, "/devices/"+deviceID+"/"+resourceHrefLink+"/subscriptions", correlationID, events.SubscriptionRequest{ + URL: s.eventsURL, + EventTypes: []events.EventType{events.EventType_ResourceChanged}, + SigningSecret: signingSecret, + }, l) + if err != nil { + return "", fmt.Errorf("cannot subscribe to device %v for %v: %v", deviceID, l.ID, err) + } + return resp.SubscriptionId, nil +} + +func cancelResourceSubscription(ctx context.Context, l store.LinkedAccount, deviceID, resourceID, subscriptionID string) error { + err := cancelSubscription(ctx, "/devices/"+deviceID+"/"+resourceID+"/subscriptions/"+subscriptionID, l) + if err != nil { + return fmt.Errorf("cannot cancel resource subscription for %v: %v", l.ID, err) + } + return nil +} + +func (s *SubscribeManager) HandleResourceChangedEvent(ctx context.Context, subscriptionData subscriptionData, header events.EventHeader, body []byte) error { + userID, err := subscriptionData.linkedAccount.OriginCloud.AccessToken.GetSubject() + if err != nil { + return fmt.Errorf("cannot get userID for device (%v) resource (%v) content changed: %v", subscriptionData.subscription.DeviceID, subscriptionData.subscription.Href, err) + } + + coapContentFormat := int32(-1) + switch header.ContentType { + case coap.AppCBOR.String(): + coapContentFormat = int32(coap.AppCBOR) + case coap.AppOcfCbor.String(): + coapContentFormat = int32(coap.AppOcfCbor) + case coap.AppJSON.String(): + coapContentFormat = int32(coap.AppJSON) + } + + _, err = s.raClient.NotifyResourceChanged(kitNetGrpc.CtxWithToken(ctx, subscriptionData.linkedAccount.OriginCloud.AccessToken.String()), &pbRA.NotifyResourceChangedRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{ + UserId: userID, + DeviceId: subscriptionData.subscription.DeviceID, + }, + ResourceId: raCqrs.MakeResourceId(subscriptionData.subscription.DeviceID, kitHttp.CanonicalHref(subscriptionData.subscription.Href)), + CommandMetadata: &pbCQRS.CommandMetadata{ + ConnectionId: OpenapiConnectorConnectionId, + Sequence: header.SequenceNumber, + }, + Content: &pbRA.Content{ + Data: body, + ContentType: header.ContentType, + CoapContentFormat: coapContentFormat, + }, + }) + if err != nil { + return fmt.Errorf("cannot update resource aggregate (%v) resource (%v) content changed: %v", subscriptionData.subscription.DeviceID, subscriptionData.subscription.Href, err) + } + + return nil + +} + +func (s *SubscribeManager) HandleResourceEvent(ctx context.Context, header events.EventHeader, body []byte, subscriptionData subscriptionData) error { + switch header.EventType { + case events.EventType_ResourceChanged: + return s.HandleResourceChangedEvent(ctx, subscriptionData, header, body) + } + return fmt.Errorf("cannot handle resource event: unsupported Event-Type %v", header.EventType) +} diff --git a/openapi-connector/service/retrieveLinkedAccounts.go b/openapi-connector/service/retrieveLinkedAccounts.go new file mode 100644 index 000000000..65cb4ebc8 --- /dev/null +++ b/openapi-connector/service/retrieveLinkedAccounts.go @@ -0,0 +1,41 @@ +package service + +import ( + "context" + "fmt" + "net/http" + + "github.com/go-ocf/cloud/openapi-connector/store" +) + +type LinkedAccountsHandler struct { + linkedAccounts []store.LinkedAccount +} + +func (h *LinkedAccountsHandler) Handle(ctx context.Context, iter store.LinkedAccountIter) (err error) { + var s store.LinkedAccount + for iter.Next(ctx, &s) { + h.linkedAccounts = append(h.linkedAccounts, s) + } + return iter.Err() +} + +func (rh *RequestHandler) retrieveLinkedAccounts(w http.ResponseWriter, r *http.Request) (int, error) { + var h LinkedAccountsHandler + err := rh.store.LoadLinkedAccounts(r.Context(), store.Query{}, &h) + if err != nil { + return http.StatusInternalServerError, err + } + err = writeJson(w, h.linkedAccounts) + if err != nil { + return http.StatusInternalServerError, err + } + return http.StatusOK, nil +} + +func (rh *RequestHandler) RetrieveLinkedAccounts(w http.ResponseWriter, r *http.Request) { + statusCode, err := rh.retrieveLinkedAccounts(w, r) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot retrieve linked accounts: %v", err), statusCode, w) + } +} diff --git a/openapi-connector/service/retrieveLinkedClouds.go b/openapi-connector/service/retrieveLinkedClouds.go new file mode 100644 index 000000000..371469992 --- /dev/null +++ b/openapi-connector/service/retrieveLinkedClouds.go @@ -0,0 +1,41 @@ +package service + +import ( + "context" + "fmt" + "net/http" + + "github.com/go-ocf/cloud/openapi-connector/store" +) + +type LinkedCloudsHandler struct { + linkedClouds []store.LinkedCloud +} + +func (h *LinkedCloudsHandler) Handle(ctx context.Context, iter store.LinkedCloudIter) (err error) { + var s store.LinkedCloud + for iter.Next(ctx, &s) { + h.linkedClouds = append(h.linkedClouds, s) + } + return iter.Err() +} + +func (rh *RequestHandler) retrieveLinkedClouds(w http.ResponseWriter, r *http.Request) (int, error) { + var h LinkedCloudsHandler + err := rh.store.LoadLinkedClouds(r.Context(), store.Query{}, &h) + if err != nil { + return http.StatusInternalServerError, err + } + err = writeJson(w, h.linkedClouds) + if err != nil { + return http.StatusInternalServerError, err + } + return http.StatusOK, nil +} + +func (rh *RequestHandler) RetrieveLinkedClouds(w http.ResponseWriter, r *http.Request) { + statusCode, err := rh.retrieveLinkedClouds(w, r) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot retrieve linked clouds: %v", err), statusCode, w) + } +} diff --git a/openapi-connector/service/service.go b/openapi-connector/service/service.go new file mode 100644 index 000000000..ff45d8ec8 --- /dev/null +++ b/openapi-connector/service/service.go @@ -0,0 +1,122 @@ +package service + +import ( + "context" + "crypto/tls" + "net" + "net/http" + "net/url" + + "google.golang.org/grpc" + + "github.com/go-ocf/cqrs/eventbus" + cqrsEventStore "github.com/go-ocf/cqrs/eventstore" + "github.com/go-ocf/kit/log" + connectorStore "github.com/go-ocf/cloud/openapi-connector/store" + "google.golang.org/grpc/credentials" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + projectionRA "github.com/go-ocf/cloud/resource-aggregate/cqrs/projection" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +//Server handle HTTP request +type Server struct { + server *http.Server + cfg Config + handler *RequestHandler + ln net.Listener +} + +type loadDeviceSubscriptionsHandler struct { + resourceProjection *projectionRA.Projection +} + +func (h *loadDeviceSubscriptionsHandler) Handle(ctx context.Context, iter connectorStore.SubscriptionIter) error { + var sub connectorStore.Subscription + for iter.Next(ctx, &sub) { + _, err := h.resourceProjection.Register(ctx, sub.DeviceID) + if err != nil { + log.Errorf("cannot register device %v subscription to resource projection: %v", sub.DeviceID, err) + } + } + return iter.Err() +} + +type DialCertManager = interface { + GetClientTLSConfig() tls.Config +} + +type ListenCertManager = interface { + GetServerTLSConfig() tls.Config +} + +//New create new Server with provided store and bus +func New(config Config, dialCertManager DialCertManager, listenCertManager ListenCertManager, resourceEventStore cqrsEventStore.EventStore, resourceSubscriber eventbus.Subscriber, store connectorStore.Store) *Server { + dialTLSConfig := dialCertManager.GetClientTLSConfig() + listenTLSConfig := listenCertManager.GetServerTLSConfig() + listenTLSConfig.ClientAuth = tls.NoClientCert + + ln, err := tls.Listen("tcp", config.Addr, &listenTLSConfig) + if err != nil { + log.Fatalf("cannot listen and serve: %v", err) + } + + raConn, err := grpc.Dial(config.ResourceAggregateAddr, grpc.WithTransportCredentials(credentials.NewTLS(&dialTLSConfig))) + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + raClient := pbRA.NewResourceAggregateClient(raConn) + + authConn, err := grpc.Dial(config.AuthServerAddr, grpc.WithTransportCredentials(credentials.NewTLS(&dialTLSConfig))) + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + authClient := pbAS.NewAuthorizationServiceClient(authConn) + + ctx := context.Background() + + resourceProjection, err := projectionRA.NewProjection(ctx, config.FQDN, resourceEventStore, resourceSubscriber, newResourceCtx(store, raClient)) + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + + // load resource subscritpion + h := loadDeviceSubscriptionsHandler{ + resourceProjection: resourceProjection, + } + err = store.LoadSubscriptions(ctx, []connectorStore.SubscriptionQuery{ + connectorStore.SubscriptionQuery{ + Type: connectorStore.Type_Device, + }, + }, &h) + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + + _, err = url.Parse(config.OAuthCallback) + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + + requestHandler := NewRequestHandler(config.OriginCloud, config.OAuthCallback, NewSubscriptionManager(config.EventsURL, authClient, raClient, store, resourceProjection), authClient, raClient, resourceProjection, store) + + server := Server{ + server: NewHTTP(requestHandler), + cfg: config, + handler: requestHandler, + ln: ln, + } + + return &server +} + +// Serve starts the service's HTTP server and blocks. +func (s *Server) Serve() error { + return s.server.Serve(s.ln) +} + +// Shutdown ends serving +func (s *Server) Shutdown() error { + return s.server.Shutdown(context.Background()) +} diff --git a/openapi-connector/service/subscriptions.go b/openapi-connector/service/subscriptions.go new file mode 100644 index 000000000..20c8e90e6 --- /dev/null +++ b/openapi-connector/service/subscriptions.go @@ -0,0 +1,316 @@ +package service + +import ( + "context" + "fmt" + "io" + "net/http" + "time" + + "github.com/gofrs/uuid" + "github.com/patrickmn/go-cache" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/kit/codec/json" + "github.com/go-ocf/kit/log" + kitHttp "github.com/go-ocf/kit/net/http" + "github.com/go-ocf/cloud/openapi-connector/events" + "github.com/go-ocf/cloud/openapi-connector/store" + projectionRA "github.com/go-ocf/cloud/resource-aggregate/cqrs/projection" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +const AuthorizationHeader string = "Authorization" +const AcceptHeader string = "Accept" +const OpenapiConnectorConnectionId string = "openapi-connector" + +type SubscribeManager struct { + eventsURL string + store store.Store + raClient pbRA.ResourceAggregateClient + asClient pbAS.AuthorizationServiceClient + resourceProjection *projectionRA.Projection + cache *cache.Cache +} + +func NewSubscriptionManager(EventsURL string, asClient pbAS.AuthorizationServiceClient, raClient pbRA.ResourceAggregateClient, + store store.Store, resourceProjection *projectionRA.Projection) *SubscribeManager { + return &SubscribeManager{ + eventsURL: EventsURL, + store: store, + raClient: raClient, + asClient: asClient, + cache: cache.New(time.Minute*10, time.Minute*5), + resourceProjection: resourceProjection, + } +} + +func subscribe(ctx context.Context, href, correlationID string, reqBody events.SubscriptionRequest, l store.LinkedAccount) (resp events.SubscriptionResponse, err error) { + client := http.Client{} + + r, w := io.Pipe() + + req, err := http.NewRequest("POST", l.TargetURL+kitHttp.CanonicalHref(href), r) + if err != nil { + return resp, fmt.Errorf("cannot create post request: %v", err) + } + req.Header.Set(events.CorrelationIDKey, correlationID) + req.Header.Set("Accept", events.ContentType_JSON+","+events.ContentType_CBOR+","+events.ContentType_VNDOCFCBOR) + req.Header.Set(events.ContentTypeKey, events.ContentType_JSON) + req.Header.Set(AuthorizationHeader, "Bearer "+string(l.TargetCloud.AccessToken)) + + go func() { + defer w.Close() + err := json.WriteTo(w, reqBody) + if err != nil { + log.Errorf("cannot encode to json: %v", err) + } + }() + httpResp, err := client.Do(req) + if err != nil { + return resp, fmt.Errorf("cannot post: %v", err) + } + defer httpResp.Body.Close() + if httpResp.StatusCode != http.StatusOK { + return resp, fmt.Errorf("unexpected statusCode %v", httpResp.StatusCode) + } + err = json.ReadFrom(httpResp.Body, &resp) + if err != nil { + return resp, fmt.Errorf("cannot device response: %v", err) + } + return resp, nil +} + +func cancelSubscription(ctx context.Context, href string, l store.LinkedAccount) error { + client := http.Client{} + req, err := http.NewRequest("DELETE", l.TargetURL+kitHttp.CanonicalHref(href), nil) + if err != nil { + return fmt.Errorf("cannot create delete request: %v", err) + } + req.Header.Set("Token", l.ID) + req.Header.Set("Accept", events.ContentType_JSON+","+events.ContentType_CBOR+","+events.ContentType_VNDOCFCBOR) + req.Header.Set(AuthorizationHeader, "Bearer "+string(l.TargetCloud.AccessToken)) + + httpResp, err := client.Do(req) + if err != nil { + return fmt.Errorf("cannot delete: %v", err) + } + defer httpResp.Body.Close() + if httpResp.StatusCode != http.StatusOK { + return fmt.Errorf("unexpected statusCode %v", httpResp.StatusCode) + } + return nil +} + +type SubscriptionHandler struct { + subscription store.Subscription + ok bool +} + +func (h *SubscriptionHandler) Handle(ctx context.Context, iter store.SubscriptionIter) (err error) { + var s store.Subscription + if iter.Next(ctx, &s) { + h.ok = true + h.subscription = s + return iter.Err() + } + return iter.Err() +} + +func (s *SubscribeManager) HandleEvent(ctx context.Context, header events.EventHeader, body []byte) (int, error) { + var subData subscriptionData + var err error + data, ok := s.cache.Get(header.CorrelationID) + if ok { + subData = data.(subscriptionData) + subData.subscription.SubscriptionID = header.SubscriptionID + newSubscription, err := s.store.FindOrCreateSubscription(ctx, subData.subscription) + if err != nil { + cancelDevicesSubscription(ctx, subData.linkedAccount, subData.subscription.SubscriptionID) + return http.StatusGone, fmt.Errorf("cannot store subscription to DB: %v", err) + } + subData.subscription = newSubscription + } else { + var h SubscriptionHandler + err := s.store.LoadSubscriptions(ctx, []store.SubscriptionQuery{store.SubscriptionQuery{SubscriptionID: header.SubscriptionID}}, &h) + if err != nil { + return http.StatusGone, fmt.Errorf("cannot load subscription from DB: %v", err) + } + if !h.ok { + return http.StatusGone, fmt.Errorf("unknown subscription %v, eventType %v", header.SubscriptionID, header.EventType) + } + subData.subscription = h.subscription + var lh LinkedAccountHandler + err = s.store.LoadLinkedAccounts(ctx, store.Query{ID: subData.subscription.LinkedAccountID}, &lh) + if err != nil { + return http.StatusGone, fmt.Errorf("cannot load linked account for subscription %v: %v", header.SubscriptionID, err) + } + if !h.ok { + return http.StatusGone, fmt.Errorf("unknown linked account %v subscription %v", subData.subscription.LinkedAccountID, subData.subscription.SubscriptionID) + } + subData.linkedAccount = lh.linkedAccount + } + + // verify event signature + if header.EventSignature != events.CalculateEventSignature(subData.subscription.SigningSecret, + header.ContentType, + header.EventType, + header.SubscriptionID, + header.SequenceNumber, + header.EventTimestamp, body) { + return http.StatusBadRequest, fmt.Errorf("invalid event signature %v: %v", header.SubscriptionID, err) + } + + s.cache.Set(header.CorrelationID, subData, cache.DefaultExpiration) + + subData.linkedAccount, err = subData.linkedAccount.RefreshTokens(ctx, s.store) + if err != nil { + return http.StatusGone, fmt.Errorf("cannot refresh access token for linked account %v: %v", subData.linkedAccount.ID, err) + } + + if header.EventType == events.EventType_SubscriptionCanceled { + err := s.HandleCancelEvent(ctx, header, subData.linkedAccount) + if err != nil { + return http.StatusGone, fmt.Errorf("cannot cancel subscription: %v", err) + } + return http.StatusOK, nil + } + + switch subData.subscription.Type { + case store.Type_Devices: + err = s.HandleDevicesEvent(ctx, header, body, subData) + + case store.Type_Device: + err = s.HandleDeviceEvent(ctx, header, body, subData) + case store.Type_Resource: + err = s.HandleResourceEvent(ctx, header, body, subData) + default: + return http.StatusGone, fmt.Errorf("cannot handle event %v: handler not found", header.EventType) + } + if err != nil { + return http.StatusGone, err + } + return http.StatusOK, nil +} + +type LinkedAccountHandler struct { + linkedAccount store.LinkedAccount + ok bool +} + +func (h *LinkedAccountHandler) Handle(ctx context.Context, iter store.LinkedAccountIter) (err error) { + var s store.LinkedAccount + if iter.Next(ctx, &s) { + h.ok = true + h.linkedAccount = s + return iter.Err() + } + return fmt.Errorf("not found") +} + +func (s *SubscribeManager) HandleCancelEvent(ctx context.Context, header events.EventHeader, linkedAccount store.LinkedAccount) error { + var h SubscriptionHandler + err := s.store.LoadSubscriptions(ctx, []store.SubscriptionQuery{store.SubscriptionQuery{SubscriptionID: header.SubscriptionID}}, &h) + if err != nil { + return fmt.Errorf("cannot load subscription from DB: %v", err) + } + if !h.ok { + return fmt.Errorf("unknown subscription %v, eventType %v", header.SubscriptionID, header.EventType) + } + return s.store.RemoveSubscriptions(ctx, store.SubscriptionQuery{SubscriptionID: header.SubscriptionID}) +} + +type subscriptionData struct { + linkedAccount store.LinkedAccount + subscription store.Subscription +} + +func (s *SubscribeManager) StartSubscriptions(ctx context.Context, l store.LinkedAccount) error { + signingSecret, err := generateRandomString(32) + if err != nil { + return fmt.Errorf("cannot generate signingSecret for start subscriptions: %v", err) + } + corID, err := uuid.NewV4() + if err != nil { + return fmt.Errorf("cannot generate correlationID for start subscriptions: %v", err) + } + correlationID := corID.String() + + sub := store.Subscription{ + Type: store.Type_Devices, + LinkedAccountID: l.ID, + SigningSecret: signingSecret, + } + err = s.cache.Add(correlationID, subscriptionData{ + linkedAccount: l, + subscription: sub, + }, cache.DefaultExpiration) + if err != nil { + return fmt.Errorf("cannot cache subscription for start subscriptions: %v", err) + } + sub.SubscriptionID, err = s.subscribeToDevices(ctx, l, correlationID, signingSecret) + if err != nil { + s.cache.Delete(correlationID) + return fmt.Errorf("cannot subscribe to devices for %v: %v", l.ID, err) + } + _, err = s.store.FindOrCreateSubscription(ctx, sub) + if err != nil { + cancelDevicesSubscription(ctx, l, sub.SubscriptionID) + return fmt.Errorf("cannot store subscription to DB: %v", err) + } + return nil +} + +type SubscriptionsHandler struct { + subscriptions []store.Subscription +} + +func (h *SubscriptionsHandler) Handle(ctx context.Context, iter store.SubscriptionIter) (err error) { + var s store.Subscription + for iter.Next(ctx, &s) { + h.subscriptions = append(h.subscriptions, s) + } + return iter.Err() +} + +func (s *SubscribeManager) StopSubscriptions(ctx context.Context, l store.LinkedAccount) error { + var h SubscriptionsHandler + err := s.store.LoadSubscriptions(ctx, []store.SubscriptionQuery{store.SubscriptionQuery{LinkedAccountID: l.ID}}, &h) + if err != nil { + return fmt.Errorf("cannot load subscriptions: %v", err) + } + if len(h.subscriptions) == 0 { + return nil + } + linkedAccount, err := l.RefreshTokens(ctx, s.store) + + var errors []error + for _, sub := range h.subscriptions { + switch sub.Type { + case store.Type_Devices: + err = cancelDevicesSubscription(ctx, linkedAccount, sub.SubscriptionID) + if err != nil { + errors = append(errors, err) + } + case store.Type_Device: + err = cancelDeviceSubscription(ctx, linkedAccount, sub.DeviceID, sub.SubscriptionID) + if err != nil { + errors = append(errors, err) + } + case store.Type_Resource: + err = cancelResourceSubscription(ctx, linkedAccount, sub.DeviceID, sub.Href, sub.SubscriptionID) + if err != nil { + errors = append(errors, err) + } + } + } + err = s.store.RemoveSubscriptions(ctx, store.SubscriptionQuery{LinkedAccountID: l.ID}) + if err != nil { + errors = append(errors, err) + } + if len(errors) > 0 { + return fmt.Errorf("%v", errors) + } + + return nil +} diff --git a/openapi-connector/store/linkedAccount.go b/openapi-connector/store/linkedAccount.go new file mode 100644 index 000000000..660a252ca --- /dev/null +++ b/openapi-connector/store/linkedAccount.go @@ -0,0 +1,147 @@ +package store + +import ( + "context" + "fmt" + "net/http" + "time" + + jwt "github.com/dgrijalva/jwt-go" + "golang.org/x/oauth2" +) + +type AccessToken string + +func (t AccessToken) String() string { + return string(t) +} + +type OAuth struct { + LinkedCloudID string + AccessToken AccessToken + RefreshToken string + Expiry time.Time +} + +type LinkedCloudsHandler struct { + LinkedClouds []LinkedCloud +} + +func (h *LinkedCloudsHandler) Handle(ctx context.Context, iter LinkedCloudIter) (err error) { + var s LinkedCloud + for iter.Next(ctx, &s) { + h.LinkedClouds = append(h.LinkedClouds, s) + } + return iter.Err() +} + +func (o OAuth) Refresh(ctx context.Context, s Store) (OAuth, error) { + if o.Expiry.IsZero() { + return o, nil + } + var h LinkedCloudsHandler + err := s.LoadLinkedClouds(ctx, Query{ID: o.LinkedCloudID}, &h) + if err != nil { + return o, err + } + if len(h.LinkedClouds) != 1 { + return o, fmt.Errorf("linked cloud %v not found", o.LinkedCloudID) + } + l := h.LinkedClouds[0] + c := l.ToOAuth2Config() + restoredToken := oauth2.Token{ + RefreshToken: o.RefreshToken, + } + ctx = context.WithValue(ctx, oauth2.HTTPClient, http.DefaultClient) + tokenSource := c.TokenSource(ctx, &restoredToken) + token, err := tokenSource.Token() + if err != nil { + return o, err + } + return OAuth{ + LinkedCloudID: o.LinkedCloudID, + AccessToken: AccessToken(token.AccessToken), + Expiry: token.Expiry, + RefreshToken: token.RefreshToken, + }, nil +} + +func (o OAuth) IsValidAccessToken() bool { + if o.Expiry.IsZero() || o.Expiry.UnixNano() > time.Now().UnixNano() { + return true + } + return false +} + +func (o OAuth) GetAccessToken() (AccessToken, error) { + if o.IsValidAccessToken() { + return o.AccessToken, nil + } + return AccessToken(""), fmt.Errorf("cannot get accesstoken: token is invalid") +} + +type claims struct { + Subject string `json:"sub,omitempty"` +} + +func (c *claims) Valid() error { + return nil +} + +func parseSubFromJwtToken(rawJwtToken string) (string, error) { + parser := &jwt.Parser{ + SkipClaimsValidation: true, + } + + var claims claims + _, _, err := parser.ParseUnverified(rawJwtToken, &claims) + if err != nil { + return "", fmt.Errorf("cannot get subject from jwt token: %v", err) + } + + if claims.Subject != "" { + return claims.Subject, nil + } + + return "", fmt.Errorf("cannot get subject from jwt token: not found") +} + +func (t AccessToken) GetSubject() (string, error) { + return parseSubFromJwtToken(string(t)) +} + +type LinkedAccount struct { + ID string + TargetURL string + TargetCloud OAuth + OriginCloud OAuth +} + +func (l LinkedAccount) RefreshTokens(ctx context.Context, s Store) (LinkedAccount, error) { + if l.TargetCloud.IsValidAccessToken() && l.OriginCloud.IsValidAccessToken() { + return l, nil + } + t := l.TargetCloud + o := l.OriginCloud + var err error + if !t.IsValidAccessToken() { + t, err = t.Refresh(ctx, s) + if err != nil { + return l, fmt.Errorf("cannot refreash target cloud access token: %v", err) + } + } + if !o.IsValidAccessToken() { + o, err = o.Refresh(ctx, s) + if err != nil { + return l, fmt.Errorf("cannot refresh target cloud access token: %v", err) + } + } + l.TargetCloud = t + l.OriginCloud = o + + err = s.UpdateLinkedAccount(ctx, l) + if err != nil { + return l, fmt.Errorf("cannot store updated linked account: %v", err) + } + return l, nil +} diff --git a/openapi-connector/store/linkedCloud.go b/openapi-connector/store/linkedCloud.go new file mode 100644 index 000000000..4ee3975f1 --- /dev/null +++ b/openapi-connector/store/linkedCloud.go @@ -0,0 +1,30 @@ +package store + +import "golang.org/x/oauth2" + +type Endpoint struct { + AuthUrl string `json:"AuthUrl" envconfig:"AUTH_URL" required:"true"` + TokenUrl string `json:"TokenUrl" envconfig:"TOKEN_URL" required:"true"` +} + +type LinkedCloud struct { + ID string `json:"ID"` + Name string `json:"Name" envconfig:"NAME" required:"true"` + ClientID string `json:"ClientId" envconfig:"CLIENT_ID" required:"true"` + ClientSecret string `json:"ClientSecret" envconfig:"CLIENT_SECRET" required:"true"` + Scopes []string `json:"Scopes" envconfig:"SCOPES" required:"true"` + Endpoint Endpoint `json:"Endpoint"` + Audience string `json:"Audience" envconfig:"AUDIENCE"` +} + +func (l LinkedCloud) ToOAuth2Config() oauth2.Config { + return oauth2.Config{ + ClientID: l.ClientID, + ClientSecret: l.ClientSecret, + Scopes: l.Scopes, + Endpoint: oauth2.Endpoint{ + AuthURL: l.Endpoint.AuthUrl, + TokenURL: l.Endpoint.TokenUrl, + }, + } +} diff --git a/openapi-connector/store/mongodb/linkeClouds.go b/openapi-connector/store/mongodb/linkeClouds.go new file mode 100644 index 000000000..e3ff6ac10 --- /dev/null +++ b/openapi-connector/store/mongodb/linkeClouds.go @@ -0,0 +1,173 @@ +package mongodb + +import ( + "context" + "fmt" + + "github.com/go-ocf/cloud/openapi-connector/store" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" +) + +const resLinkedCloudCName = "LinkedCloud" + +type dbEndpoint struct { + AuthUrl string + TokenUrl string +} + +type dbLinkedCloud struct { + Id string `bson:"_id"` + Name string + ClientId string + ClientSecret string + Scopes []string + Endpoint dbEndpoint + Audience string +} + +func makeDBLinkedCloud(sub store.LinkedCloud) dbLinkedCloud { + return dbLinkedCloud{ + Id: sub.ID, + Name: sub.Name, + ClientId: sub.ClientID, + ClientSecret: sub.ClientSecret, + Scopes: sub.Scopes, + Audience: sub.Audience, + Endpoint: dbEndpoint{ + AuthUrl: sub.Endpoint.AuthUrl, + TokenUrl: sub.Endpoint.TokenUrl, + }, + } + +} + +func validateLinkedCloud(sub store.LinkedCloud) error { + if sub.ID == "" { + return fmt.Errorf("cannot save linked cloud: invalid Id") + } + if sub.ClientID == "" { + return fmt.Errorf("cannot save linked cloud: invalid ClientId") + } + if sub.ClientSecret == "" { + return fmt.Errorf("cannot save linked cloud: invalid ClientSecret") + } + if len(sub.Scopes) == 0 { + return fmt.Errorf("cannot save linked cloud: invalid Scopes") + } + if sub.Endpoint.AuthUrl == "" { + return fmt.Errorf("cannot save linked cloud: invalid AuthUrl") + } + if sub.Endpoint.TokenUrl == "" { + return fmt.Errorf("cannot save linked cloud: invalid TokenUrl") + } + return nil +} + +func (s *Store) UpdateLinkedCloud(ctx context.Context, sub store.LinkedCloud) error { + err := validateLinkedCloud(sub) + if err != nil { + return err + } + + dbSub := makeDBLinkedCloud(sub) + col := s.client.Database(s.DBName()).Collection(resLinkedCloudCName) + + res, err := col.UpdateOne(ctx, bson.M{"_id": sub.ID}, bson.M{"$set": dbSub}) + if err != nil { + return fmt.Errorf("cannot save linked cloud: %v", err) + } + if res.MatchedCount == 0 { + return fmt.Errorf("cannot update linked cloud: not found") + } + return nil +} + +func (s *Store) InsertLinkedCloud(ctx context.Context, sub store.LinkedCloud) error { + err := validateLinkedCloud(sub) + if err != nil { + return err + } + + dbSub := makeDBLinkedCloud(sub) + col := s.client.Database(s.DBName()).Collection(resLinkedCloudCName) + + if _, err := col.InsertOne(ctx, dbSub); err != nil { + return fmt.Errorf("cannot save linked cloud: %v", err) + } + return nil +} + +func (s *Store) RemoveLinkedCloud(ctx context.Context, LinkedCloudId string) error { + if LinkedCloudId == "" { + return fmt.Errorf("cannot remove linked cloud: invalid LinkedCloudId") + } + + res, err := s.client.Database(s.DBName()).Collection(resLinkedCloudCName).DeleteOne(ctx, bson.M{"_id": LinkedCloudId}) + if err != nil { + return fmt.Errorf("cannot remove linked cloud: %v", err) + } + if res.DeletedCount == 0 { + return fmt.Errorf("cannot remove linked cloud: not found") + } + return nil +} + +func (s *Store) LoadLinkedClouds(ctx context.Context, query store.Query, h store.LinkedCloudHandler) error { + var iter *mongo.Cursor + var err error + col := s.client.Database(s.DBName()).Collection(resLinkedCloudCName) + switch { + case query.ID != "": + iter, err = col.Find(ctx, bson.M{"_id": query.ID}) + default: + iter, err = col.Find(ctx, bson.M{}) + } + if err == mongo.ErrNilDocument { + return nil + } + + i := linkedCloudIterator{ + iter: iter, + } + err = h.Handle(ctx, &i) + + errClose := iter.Close(ctx) + if err == nil { + return errClose + } + return err +} + +type linkedCloudIterator struct { + iter *mongo.Cursor +} + +func (i *linkedCloudIterator) Next(ctx context.Context, s *store.LinkedCloud) bool { + var sub dbLinkedCloud + + if !i.iter.Next(ctx) { + return false + } + + err := i.iter.Decode(&sub) + if err != nil { + return false + } + s.ID = sub.Id + s.Name = sub.Name + s.ClientID = sub.ClientId + s.ClientSecret = sub.ClientSecret + s.Scopes = sub.Scopes + s.Audience = sub.Audience + s.Endpoint = store.Endpoint{ + AuthUrl: sub.Endpoint.AuthUrl, + TokenUrl: sub.Endpoint.TokenUrl, + } + + return true +} + +func (i *linkedCloudIterator) Err() error { + return i.iter.Err() +} diff --git a/openapi-connector/store/mongodb/linkeClouds_test.go b/openapi-connector/store/mongodb/linkeClouds_test.go new file mode 100644 index 000000000..7acf123f1 --- /dev/null +++ b/openapi-connector/store/mongodb/linkeClouds_test.go @@ -0,0 +1,266 @@ +package mongodb + +import ( + "context" + "testing" + + "github.com/go-ocf/kit/security/certManager" + + "github.com/go-ocf/cloud/openapi-connector/store" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func newStore(ctx context.Context, t *testing.T, cfg Config) *Store { + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + defer dialCertManager.Close() + tlsConfig := dialCertManager.GetClientTLSConfig() + s, err := NewStore(ctx, cfg, WithTLS(&tlsConfig)) + require.NoError(t, err) + return s +} + +func TestStore_UpdateLinkedCloud(t *testing.T) { + type args struct { + sub store.LinkedCloud + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "not found", + args: args{ + sub: store.LinkedCloud{ + ID: "testIDnotFound", + Name: "testName", + ClientID: "testClientID", + ClientSecret: "testClientSecret", + Scopes: []string{"testScopes"}, + Endpoint: store.Endpoint{ + AuthUrl: "testAuthUrl", + TokenUrl: "testTokenUrl", + }, + }, + }, + wantErr: true, + }, + { + name: "valid", + args: args{ + sub: store.LinkedCloud{ + ID: "testID", + Name: "testNameUpdated", + ClientID: "testClientID", + ClientSecret: "testClientSecret", + Scopes: []string{"testScopes"}, + Audience: "testAudience", + Endpoint: store.Endpoint{ + AuthUrl: "testAuthUrl", + TokenUrl: "testTokenUrl", + }, + }, + }, + }, + } + + require := require.New(t) + var config Config + err := envconfig.Process("", &config) + require.NoError(err) + ctx := context.Background() + s := newStore(ctx, t, config) + require.NoError(err) + defer s.Clear(ctx) + + assert := assert.New(t) + + err = s.InsertLinkedCloud(ctx, store.LinkedCloud{ + ID: "testID", + Name: "testName", + ClientID: "testClientID", + ClientSecret: "testClientSecret", + Scopes: []string{"testScopes"}, + Endpoint: store.Endpoint{ + AuthUrl: "testAuthUrl", + TokenUrl: "testTokenUrl", + }, + }) + require.NoError(err) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := s.UpdateLinkedCloud(ctx, tt.args.sub) + if tt.wantErr { + assert.Error(err) + } else { + assert.NoError(err) + } + }) + } +} + +func TestStore_RemoveLinkedCloud(t *testing.T) { + type args struct { + LinkedCloudId string + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "not found", + args: args{ + LinkedCloudId: "notFound", + }, + wantErr: true, + }, + + { + name: "valid", + args: args{ + LinkedCloudId: "testID", + }, + }, + } + + require := require.New(t) + var config Config + err := envconfig.Process("", &config) + require.NoError(err) + ctx := context.Background() + s := newStore(ctx, t, config) + defer s.Clear(ctx) + + assert := assert.New(t) + + err = s.InsertLinkedCloud(ctx, store.LinkedCloud{ + ID: "testID", + Name: "testName", + ClientID: "testClientID", + ClientSecret: "testClientSecret", + Scopes: []string{"testScopes"}, + Endpoint: store.Endpoint{ + AuthUrl: "testAuthUrl", + TokenUrl: "testTokenUrl", + }, + }) + require.NoError(err) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := s.RemoveLinkedCloud(ctx, tt.args.LinkedCloudId) + if tt.wantErr { + assert.Error(err) + } else { + assert.NoError(err) + } + }) + } +} + +type testLinkedCloudHandler struct { + lcs []store.LinkedCloud +} + +func (h *testLinkedCloudHandler) Handle(ctx context.Context, iter store.LinkedCloudIter) (err error) { + var sub store.LinkedCloud + for iter.Next(ctx, &sub) { + h.lcs = append(h.lcs, sub) + } + return iter.Err() +} + +func TestStore_LoadLinkedClouds(t *testing.T) { + lcs := []store.LinkedCloud{store.LinkedCloud{ + ID: "testID", + Name: "testName", + ClientID: "testClientID", + ClientSecret: "testClientSecret", + Scopes: []string{"testScopes"}, + Audience: "testAudience", + Endpoint: store.Endpoint{ + AuthUrl: "testAuthUrl", + TokenUrl: "testTokenUrl", + }, + }, + { + ID: "testID2", + Name: "testName", + ClientID: "testClientID", + ClientSecret: "testClientSecret", + Scopes: []string{"testScopes"}, + Endpoint: store.Endpoint{ + AuthUrl: "testAuthUrl", + TokenUrl: "testTokenUrl", + }, + }, + } + + type args struct { + query store.Query + } + tests := []struct { + name string + args args + wantErr bool + want []store.LinkedCloud + }{ + { + name: "all", + args: args{ + query: store.Query{}, + }, + want: lcs, + }, + { + name: "id", + args: args{ + query: store.Query{ID: lcs[0].ID}, + }, + want: []store.LinkedCloud{lcs[0]}, + }, + { + name: "not found", + args: args{ + query: store.Query{ID: "not found"}, + }, + }, + } + + require := require.New(t) + var config Config + err := envconfig.Process("", &config) + require.NoError(err) + ctx := context.Background() + s := newStore(ctx, t, config) + require.NoError(err) + defer s.Clear(ctx) + + assert := assert.New(t) + + for _, l := range lcs { + err = s.InsertLinkedCloud(ctx, l) + require.NoError(err) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var h testLinkedCloudHandler + err := s.LoadLinkedClouds(ctx, tt.args.query, &h) + if tt.wantErr { + assert.Error(err) + } else { + assert.NoError(err) + assert.Equal(tt.want, h.lcs) + } + }) + } +} diff --git a/openapi-connector/store/mongodb/linkedAccounts.go b/openapi-connector/store/mongodb/linkedAccounts.go new file mode 100644 index 000000000..f7381b68b --- /dev/null +++ b/openapi-connector/store/mongodb/linkedAccounts.go @@ -0,0 +1,184 @@ +package mongodb + +import ( + "context" + "fmt" + "time" + + "github.com/go-ocf/cloud/openapi-connector/store" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" +) + +const resLinkedAccountCName = "linkedAccounts" + +type dbOAuth struct { + LinkedCloudID string + AccessToken string + RefreshToken string + Expiry int64 +} + +type dbLinkedAccount struct { + ID string `bson:"_id"` + TargetURL string + TargetCloud dbOAuth + OriginCloud dbOAuth +} + +func makeDBLinkedAccount(sub store.LinkedAccount) dbLinkedAccount { + targetExpiry := int64(0) + if !sub.TargetCloud.Expiry.IsZero() { + targetExpiry = sub.TargetCloud.Expiry.UnixNano() + } + originExpiry := int64(0) + if !sub.TargetCloud.Expiry.IsZero() { + originExpiry = sub.OriginCloud.Expiry.UnixNano() + } + + return dbLinkedAccount{ + ID: sub.ID, + TargetURL: sub.TargetURL, + TargetCloud: dbOAuth{ + LinkedCloudID: sub.TargetCloud.LinkedCloudID, + AccessToken: string(sub.TargetCloud.AccessToken), + RefreshToken: sub.TargetCloud.RefreshToken, + Expiry: targetExpiry, + }, + OriginCloud: dbOAuth{ + LinkedCloudID: sub.OriginCloud.LinkedCloudID, + AccessToken: string(sub.OriginCloud.AccessToken), + RefreshToken: sub.OriginCloud.RefreshToken, + Expiry: originExpiry, + }, + } + +} + +func validateLinkedAccount(sub store.LinkedAccount) error { + if sub.ID == "" { + return fmt.Errorf("cannot save linked account: invalid ID") + } + if sub.TargetCloud.LinkedCloudID == "" { + return fmt.Errorf("cannot save linked account: invalid ConfigId") + } + if sub.TargetCloud.AccessToken == "" && sub.TargetCloud.RefreshToken == "" { + return fmt.Errorf("cannot save linked account: invalid AccessToken and RefreshToken") + } + if sub.TargetURL == "" { + return fmt.Errorf("cannot save linked account: invalid TargetURL") + } + return nil +} + +func (s *Store) InsertLinkedAccount(ctx context.Context, sub store.LinkedAccount) error { + err := validateLinkedAccount(sub) + if err != nil { + return err + } + + dbSub := makeDBLinkedAccount(sub) + col := s.client.Database(s.DBName()).Collection(resLinkedAccountCName) + + if _, err := col.InsertOne(ctx, dbSub); err != nil { + return fmt.Errorf("cannot insert linked account: %v", err) + } + return nil +} + +func (s *Store) UpdateLinkedAccount(ctx context.Context, sub store.LinkedAccount) error { + err := validateLinkedAccount(sub) + if err != nil { + return err + } + dbSub := makeDBLinkedAccount(sub) + col := s.client.Database(s.DBName()).Collection(resLinkedAccountCName) + + if res, err := col.UpdateOne(ctx, bson.M{"_id": sub.ID}, bson.M{"$set": dbSub}); err != nil { + return fmt.Errorf("cannot update linked account: %v", err) + } else { + if res.MatchedCount == 0 { + return fmt.Errorf("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") + } + res, err := s.client.Database(s.DBName()).Collection(resLinkedAccountCName).DeleteOne(ctx, bson.M{"_id": linkedAccountId}) + if err != nil { + return fmt.Errorf("cannot remove linked account: %v", err) + } + if res.DeletedCount == 0 { + return fmt.Errorf("cannot remove linked account: not found") + } + return nil +} + +func (s *Store) LoadLinkedAccounts(ctx context.Context, query store.Query, h store.LinkedAccountHandler) error { + var iter *mongo.Cursor + var err error + + col := s.client.Database(s.DBName()).Collection(resLinkedAccountCName) + switch { + case query.ID != "": + iter, err = col.Find(ctx, bson.M{"_id": query.ID}) + default: + iter, err = col.Find(ctx, bson.M{}) + } + if err == mongo.ErrNilDocument { + return nil + } + + i := iterator{ + iter: iter, + } + err = h.Handle(ctx, &i) + + errClose := iter.Close(ctx) + if err == nil { + return errClose + } + return err +} + +type iterator struct { + iter *mongo.Cursor +} + +func (i *iterator) Next(ctx context.Context, s *store.LinkedAccount) bool { + var sub dbLinkedAccount + + if !i.iter.Next(ctx) { + return false + } + + err := i.iter.Decode(&sub) + if err != nil { + return false + } + + s.ID = sub.ID + s.TargetURL = sub.TargetURL + s.TargetCloud.LinkedCloudID = sub.TargetCloud.LinkedCloudID + s.TargetCloud.AccessToken = store.AccessToken(sub.TargetCloud.AccessToken) + s.TargetCloud.RefreshToken = sub.TargetCloud.RefreshToken + if sub.TargetCloud.Expiry != 0 { + s.TargetCloud.Expiry = time.Unix(-1, sub.TargetCloud.Expiry) + } + s.OriginCloud.LinkedCloudID = sub.OriginCloud.LinkedCloudID + s.OriginCloud.AccessToken = store.AccessToken(sub.OriginCloud.AccessToken) + s.OriginCloud.RefreshToken = sub.OriginCloud.RefreshToken + if sub.OriginCloud.Expiry != 0 { + s.OriginCloud.Expiry = time.Unix(-1, sub.OriginCloud.Expiry) + } + + return true +} + +func (i *iterator) Err() error { + return i.iter.Err() +} diff --git a/openapi-connector/store/mongodb/linkedAccounts_test.go b/openapi-connector/store/mongodb/linkedAccounts_test.go new file mode 100644 index 000000000..2d9083653 --- /dev/null +++ b/openapi-connector/store/mongodb/linkedAccounts_test.go @@ -0,0 +1,307 @@ +package mongodb + +import ( + "context" + "testing" + + "github.com/go-ocf/cloud/openapi-connector/store" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestStore_InsertLinkedAccount(t *testing.T) { + type args struct { + sub store.LinkedAccount + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "valid", + args: args{ + sub: store.LinkedAccount{ + ID: "testID", + TargetURL: "testTargetURL", + TargetCloud: store.OAuth{ + LinkedCloudID: "testLinkedCloudID", + AccessToken: "testAccessToken", + RefreshToken: "testRefreshToken", + }, + OriginCloud: store.OAuth{ + LinkedCloudID: "testLinkedCloudID", + AccessToken: "testAccessToken", + RefreshToken: "testRefreshToken", + }, + }, + }, + }, + } + + require := require.New(t) + var config Config + err := envconfig.Process("", &config) + require.NoError(err) + ctx := context.Background() + s := newStore(ctx, t, config) + require.NoError(err) + defer s.Clear(ctx) + + assert := assert.New(t) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := s.InsertLinkedAccount(ctx, tt.args.sub) + if tt.wantErr { + assert.Error(err) + } else { + assert.NoError(err) + } + }) + } +} + +func TestStore_UpdateLinkedAccount(t *testing.T) { + type args struct { + sub store.LinkedAccount + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "not found", + args: args{ + sub: store.LinkedAccount{ + ID: "testID1", + TargetURL: "testTargetURL", + TargetCloud: store.OAuth{ + LinkedCloudID: "testLinkedCloudID", + AccessToken: "testAccessToken", + RefreshToken: "testRefreshToken", + }, + OriginCloud: store.OAuth{ + LinkedCloudID: "testLinkedCloudID", + AccessToken: "testAccessToken", + RefreshToken: "testRefreshToken", + }, + }, + }, + wantErr: true, + }, + { + name: "valid", + args: args{ + sub: store.LinkedAccount{ + ID: "testID", + TargetURL: "testTargetURL", + TargetCloud: store.OAuth{ + LinkedCloudID: "testLinkedCloudID", + AccessToken: "testAccessToken", + RefreshToken: "testRefreshToken", + }, + OriginCloud: store.OAuth{ + LinkedCloudID: "testLinkedCloudID", + AccessToken: "testAccessToken", + RefreshToken: "testRefreshToken", + }, + }, + }, + }, + } + + require := require.New(t) + var config Config + err := envconfig.Process("", &config) + require.NoError(err) + ctx := context.Background() + s := newStore(ctx, t, config) + require.NoError(err) + defer s.Clear(ctx) + + assert := assert.New(t) + + err = s.InsertLinkedAccount(ctx, store.LinkedAccount{ + ID: "testID", + TargetURL: "testTargetURL", + TargetCloud: store.OAuth{ + LinkedCloudID: "testLinkedCloudID", + AccessToken: "testAccessToken", + RefreshToken: "testRefreshToken", + }, + OriginCloud: store.OAuth{ + LinkedCloudID: "testLinkedCloudID", + AccessToken: "testAccessToken", + RefreshToken: "testRefreshToken", + }, + }) + require.NoError(err) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := s.UpdateLinkedAccount(ctx, tt.args.sub) + if tt.wantErr { + assert.Error(err) + } else { + assert.NoError(err) + } + }) + } +} + +func TestStore_RemoveLinkedAccount(t *testing.T) { + type args struct { + linkedAccountId string + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "not found", + args: args{ + linkedAccountId: "testNotFound", + }, + wantErr: true, + }, + { + name: "valid", + args: args{ + linkedAccountId: "testID", + }, + }, + } + + require := require.New(t) + var config Config + err := envconfig.Process("", &config) + require.NoError(err) + ctx := context.Background() + s := newStore(ctx, t, config) + require.NoError(err) + defer s.Clear(ctx) + + assert := assert.New(t) + + err = s.InsertLinkedAccount(ctx, store.LinkedAccount{ + ID: "testID", + TargetURL: "testTargetURL", + TargetCloud: store.OAuth{ + LinkedCloudID: "testLinkedCloudID", + AccessToken: "testAccessToken", + RefreshToken: "testRefreshToken", + }, + OriginCloud: store.OAuth{ + LinkedCloudID: "testLinkedCloudID", + AccessToken: "testAccessToken", + RefreshToken: "testRefreshToken", + }, + }) + require.NoError(err) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := s.RemoveLinkedAccount(ctx, tt.args.linkedAccountId) + if tt.wantErr { + assert.Error(err) + } else { + assert.NoError(err) + } + }) + } +} + +type testLinkedAccountHandler struct { + accs []store.LinkedAccount +} + +func (h *testLinkedAccountHandler) Handle(ctx context.Context, iter store.LinkedAccountIter) (err error) { + var sub store.LinkedAccount + for iter.Next(ctx, &sub) { + h.accs = append(h.accs, sub) + } + return iter.Err() +} + +func TestStore_LoadLinkedAccounts(t *testing.T) { + linkedAccounts := []store.LinkedAccount{ + store.LinkedAccount{ + ID: "testID", + TargetURL: "testTargetURL", + TargetCloud: store.OAuth{ + LinkedCloudID: "testLinkedCloudID", + AccessToken: "testAccessToken", + RefreshToken: "testRefreshToken", + }, + OriginCloud: store.OAuth{ + LinkedCloudID: "testLinkedCloudID", + AccessToken: "testAccessToken", + RefreshToken: "testRefreshToken", + }, + }, + } + + type args struct { + query store.Query + } + tests := []struct { + name string + args args + wantErr bool + want []store.LinkedAccount + }{ + { + name: "all", + args: args{ + query: store.Query{}, + }, + want: linkedAccounts, + }, + { + name: "id", + args: args{ + query: store.Query{ID: linkedAccounts[0].ID}, + }, + want: []store.LinkedAccount{linkedAccounts[0]}, + }, + { + name: "not found", + args: args{ + query: store.Query{ID: "not found"}, + }, + }, + } + + require := require.New(t) + var config Config + err := envconfig.Process("", &config) + require.NoError(err) + ctx := context.Background() + s := newStore(ctx, t, config) + require.NoError(err) + defer s.Clear(ctx) + + assert := assert.New(t) + + for _, a := range linkedAccounts { + err = s.InsertLinkedAccount(ctx, a) + require.NoError(err) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var h testLinkedAccountHandler + err := s.LoadLinkedAccounts(ctx, tt.args.query, &h) + if tt.wantErr { + assert.Error(err) + } else { + assert.NoError(err) + assert.Equal(tt.want, h.accs) + } + }) + } +} diff --git a/openapi-connector/store/mongodb/store.go b/openapi-connector/store/mongodb/store.go new file mode 100644 index 000000000..259ae2821 --- /dev/null +++ b/openapi-connector/store/mongodb/store.go @@ -0,0 +1,133 @@ +package mongodb + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "strings" + "time" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/mongo/readpref" +) + +// Store implements an Store for MongoDB. +type Store struct { + client *mongo.Client + dbPrefix string +} + +type Config struct { + Host string `envconfig:"LINKED_STORE_MONGO_HOST" default:"localhost:27017"` + DatabaseName string `envconfig:"LINKED_STORE_MONGO_DATABASE" default:"openapiConnector"` + tlsCfg *tls.Config +} + +// Option provides the means to use function call chaining +type Option func(Config) Config + +// WithTLS configures connection to use TLS +func WithTLS(cfg *tls.Config) Option { + return func(c Config) Config { + c.tlsCfg = cfg + return c + } +} + +// NewStore creates a new Store. +func NewStore(ctx context.Context, cfg Config, opts ...Option) (*Store, error) { + for _, o := range opts { + cfg = o(cfg) + } + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://"+cfg.Host).SetTLSConfig(cfg.tlsCfg)) + if err != nil { + return nil, fmt.Errorf("could not dial database: %v", err) + } + err = client.Ping(ctx, readpref.Primary()) + if err != nil { + return nil, fmt.Errorf("could not dial database: %v", err) + } + + return NewStoreWithSession(ctx, client, cfg.DatabaseName) +} + +// NewStoreWithSession creates a new Store with a session. +func NewStoreWithSession(ctx context.Context, client *mongo.Client, dbPrefix string) (*Store, error) { + if client == nil { + return nil, errors.New("no database session") + } + + if dbPrefix == "" { + dbPrefix = "default" + } + + s := &Store{ + client: client, + dbPrefix: dbPrefix, + } + + col := s.client.Database(s.DBName()).Collection(subscriptionCName) + + err := ensureIndex(ctx, col, typeQueryIndex, subscriptionLinkAccountQueryIndex, subscriptionDeviceQueryIndex, subscriptionDeviceHrefQueryIndex) + if err != nil { + client.Disconnect(ctx) + return nil, fmt.Errorf("cannot ensure index for device subscription: %v", err) + } + + return s, nil +} + +func ensureIndex(ctx context.Context, col *mongo.Collection, indexes ...bson.D) error { + for _, keys := range indexes { + opts := &options.IndexOptions{} + opts.SetBackground(false) + index := mongo.IndexModel{ + Keys: keys, + Options: opts, + } + _, err := col.Indexes().CreateOne(ctx, index) + if err != nil { + if strings.HasPrefix(err.Error(), "(IndexKeySpecsConflict)") { + //index already exist, just skip error and continue + continue + } + return fmt.Errorf("cannot ensure indexes for eventstore: %v", err) + } + } + return nil +} + +// DBName returns db name +func (s *Store) DBName() string { + ns := "db" + return s.dbPrefix + "_" + ns +} + +// Clear clears the event storage. +func (s *Store) Clear(ctx context.Context) error { + var errors []error + if err := s.client.Database(s.DBName()).Collection(resLinkedAccountCName).Drop(ctx); err != nil { + errors = append(errors, err) + } + if err := s.client.Database(s.DBName()).Collection(resLinkedCloudCName).Drop(ctx); err != nil { + errors = append(errors, err) + } + if err := s.client.Database(s.DBName()).Collection(subscriptionCName).Drop(ctx); err != nil { + errors = append(errors, err) + } + if len(errors) > 0 { + return fmt.Errorf("cannot clear: %v", errors) + } + + return nil +} + +// Close closes the database session. +func (s *Store) Close(ctx context.Context) error { + return s.client.Disconnect(ctx) +} diff --git a/openapi-connector/store/mongodb/subscriptions.go b/openapi-connector/store/mongodb/subscriptions.go new file mode 100644 index 000000000..97cd87b92 --- /dev/null +++ b/openapi-connector/store/mongodb/subscriptions.go @@ -0,0 +1,229 @@ +package mongodb + +import ( + "context" + "fmt" + + "github.com/go-ocf/cloud/openapi-connector/store" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +const subscriptionCName = "Subscription" +const hrefKey = "resourcehref" +const linkedAccountIDKey = "linkedAccountID" +const deviceIDKey = "deviceid" +const signingSecretKey = "signingsecret" +const typeKey = "type" + +var typeQueryIndex = bson.D{ + {typeKey, 1}, +} + +var subscriptionLinkAccountQueryIndex = bson.D{ + {linkedAccountIDKey, 1}, +} + +var subscriptionDeviceQueryIndex = bson.D{ + {deviceIDKey, 1}, + {typeKey, 1}, +} + +var subscriptionDeviceHrefQueryIndex = bson.D{ + {hrefKey, 1}, + {deviceIDKey, 1}, + {typeKey, 1}, +} + +type dbSubscription struct { + SubscriptionID string `bson:"_id"` + LinkedAccountID string `bson:linkedAccountIDKey` + DeviceID string `bson:deviceIDKey` + Href string `bson:hrefKey` + Type string `bson:typeKey` + SigningSecret string `bson:signingSecretKey` +} + +func makeDBSubscription(sub store.Subscription) dbSubscription { + return dbSubscription{ + SubscriptionID: sub.SubscriptionID, + LinkedAccountID: sub.LinkedAccountID, + DeviceID: sub.DeviceID, + Href: sub.Href, + Type: string(sub.Type), + SigningSecret: sub.SigningSecret, + } +} + +func (s *Store) LoadSubscriptions(ctx context.Context, queries []store.SubscriptionQuery, h store.SubscriptionHandler) error { + col := s.client.Database(s.DBName()).Collection(subscriptionCName) + opts := options.FindOptions{} + q := bson.M{} + bsonQueries := make([]bson.M, 0, 32) + for _, query := range queries { + tmp := bson.M{} + if query.SubscriptionID != "" { + tmp["_id"] = query.SubscriptionID + } + if query.LinkedAccountID != "" { + tmp[linkedAccountIDKey] = query.LinkedAccountID + opts.SetHint(subscriptionLinkAccountQueryIndex) + } + if query.Type != "" { + tmp[typeKey] = query.Type + opts.SetHint(typeQueryIndex) + } + if query.DeviceID != "" { + if query.Type == "" { + return fmt.Errorf("cannot load device subscription: invalid Type") + } + tmp[deviceIDKey] = query.DeviceID + opts.SetHint(subscriptionDeviceQueryIndex) + } + if query.Href != "" { + if query.DeviceID == "" { + return fmt.Errorf("cannot load resource subscription: invalid DeviceID") + } + if query.Type == "" { + return fmt.Errorf("cannot load resource subscription: invalid Type") + } + tmp[hrefKey] = query.Href + opts.SetHint(subscriptionDeviceHrefQueryIndex) + } + bsonQueries = append(bsonQueries, tmp) + } + if len(bsonQueries) > 0 { + q["$or"] = bsonQueries + } + + iter, err := col.Find(ctx, q, &opts) + if err == mongo.ErrNilDocument { + return nil + } + if err != nil { + return err + } + i := subscriptionIterator{ + iter: iter, + } + err = h.Handle(ctx, &i) + + errClose := iter.Close(ctx) + if err == nil { + return errClose + } + return err +} + +func (s *Store) FindOrCreateSubscription(ctx context.Context, sub store.Subscription) (store.Subscription, error) { + if sub.SubscriptionID == "" { + return store.Subscription{}, fmt.Errorf("invalid SubscriptionID") + } + if sub.LinkedAccountID == "" { + return store.Subscription{}, fmt.Errorf("invalid LinkedAccountID") + } + q := bson.M{ + //"_id": sub.SubscriptionID, + "$and": []bson.M{ + {linkedAccountIDKey: sub.LinkedAccountID}, + {typeKey: sub.Type}, + }, + } + switch sub.Type { + case "": + return sub, fmt.Errorf("invalid Type") + case store.Type_Device: + if sub.DeviceID == "" { + return store.Subscription{}, fmt.Errorf("invalid DeviceID") + } + q[deviceIDKey] = sub.DeviceID + case store.Type_Resource: + if sub.DeviceID == "" { + return store.Subscription{}, fmt.Errorf("invalid DeviceID") + } + if sub.Href == "" { + return store.Subscription{}, fmt.Errorf("invalid Href") + } + q[deviceIDKey] = sub.DeviceID + q[hrefKey] = sub.Href + } + + dbSub := makeDBSubscription(sub) + col := s.client.Database(s.DBName()).Collection(subscriptionCName) + + opts := options.FindOneAndUpdateOptions{} + opts.SetUpsert(true) + opts.SetReturnDocument(options.ReturnDocument(options.After)) + res := col.FindOneAndUpdate(ctx, q, bson.M{"$setOnInsert": dbSub}, &opts) + if res.Err() != nil { + return store.Subscription{}, fmt.Errorf("cannot find and create for device subscription: %v", res.Err()) + } + + var storedSub dbSubscription + err := res.Decode(&storedSub) + if err != nil { + return store.Subscription{}, fmt.Errorf("cannot devcode all device subscription: %v", err) + } + if storedSub.SubscriptionID != dbSub.SubscriptionID { + return store.Subscription{}, fmt.Errorf("cannet create duplicit subscription of type %v:%v:%v", dbSub.Type, dbSub.DeviceID, dbSub.Href) + } + + return sub, nil +} + +func (s *Store) RemoveSubscriptions(ctx context.Context, query store.SubscriptionQuery) error { + if query.DeviceID != "" { + return fmt.Errorf("remove by DeviceID is not supported") + } + if query.Href != "" { + return fmt.Errorf("remove by Href is not supported") + } + if query.Type != "" { + return fmt.Errorf("remove by Type is not supported") + } + q := bson.M{} + if query.SubscriptionID != "" { + q["_id"] = query.SubscriptionID + } else if query.LinkedAccountID == "" { + q[linkedAccountIDKey] = query.LinkedAccountID + } + _, err := s.client.Database(s.DBName()).Collection(subscriptionCName).DeleteMany(ctx, q) + if err != nil { + return fmt.Errorf("cannot remove subscriptions: %v", err) + } + return nil +} + +type subscriptionIterator struct { + iter *mongo.Cursor +} + +func (i *subscriptionIterator) Next(ctx context.Context, s *store.Subscription) bool { + var sub dbSubscription + + if i.iter == nil { + return false + } + if !i.iter.Next(ctx) { + return false + } + + err := i.iter.Decode(&sub) + if err != nil { + return false + } + + s.LinkedAccountID = sub.LinkedAccountID + s.DeviceID = sub.DeviceID + s.SubscriptionID = sub.SubscriptionID + s.Href = sub.Href + s.Type = store.Type(sub.Type) + s.SigningSecret = sub.SigningSecret + + return true +} + +func (i *subscriptionIterator) Err() error { + return i.iter.Err() +} diff --git a/openapi-connector/store/mongodb/subscriptions_test.go b/openapi-connector/store/mongodb/subscriptions_test.go new file mode 100644 index 000000000..19a403b76 --- /dev/null +++ b/openapi-connector/store/mongodb/subscriptions_test.go @@ -0,0 +1,391 @@ +package mongodb + +import ( + "context" + "testing" + + "github.com/go-ocf/cloud/openapi-connector/store" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestStore_FindOrCreateSubscription(t *testing.T) { + type args struct { + sub store.Subscription + } + tests := []struct { + name string + args args + want store.Subscription + wantErr bool + }{ + { + name: "Type_Devices - valid", + args: args{ + sub: store.Subscription{ + SubscriptionID: "0", + Type: store.Type_Devices, + LinkedAccountID: "testLinkedAccountID", + }, + }, + want: store.Subscription{ + SubscriptionID: "0", + Type: store.Type_Devices, + LinkedAccountID: "testLinkedAccountID", + }, + }, + { + name: "Type_Devices - valid - duplicit with same LinkedAccountID", + args: args{ + sub: store.Subscription{ + SubscriptionID: "0", + Type: store.Type_Devices, + LinkedAccountID: "testLinkedAccountID", + }, + }, + want: store.Subscription{ + SubscriptionID: "0", + Type: store.Type_Devices, + LinkedAccountID: "testLinkedAccountID", + }, + }, + { + name: "Type_Devices - error - duplicit with different LinkedAccountID", + args: args{ + sub: store.Subscription{ + SubscriptionID: "1", + Type: store.Type_Devices, + LinkedAccountID: "testLinkedAccountID", + }, + }, + wantErr: true, + }, + { + name: "Type_Device - valid", + args: args{ + sub: store.Subscription{ + SubscriptionID: "1", + Type: store.Type_Device, + LinkedAccountID: "testLinkedAccountID", + DeviceID: "testDeviceID", + }, + }, + want: store.Subscription{ + SubscriptionID: "1", + Type: store.Type_Device, + LinkedAccountID: "testLinkedAccountID", + DeviceID: "testDeviceID", + }, + }, + + { + name: "Type_Device - valid - duplicit with same LinkedAccountID", + args: args{ + sub: store.Subscription{ + SubscriptionID: "1", + Type: store.Type_Device, + LinkedAccountID: "testLinkedAccountID", + DeviceID: "testDeviceID", + }, + }, + want: store.Subscription{ + SubscriptionID: "1", + Type: store.Type_Device, + LinkedAccountID: "testLinkedAccountID", + DeviceID: "testDeviceID", + }, + }, + { + name: "Type_Device - error - duplicit with different LinkedAccountID", + args: args{ + sub: store.Subscription{ + SubscriptionID: "2", + Type: store.Type_Device, + LinkedAccountID: "testLinkedAccountID", + DeviceID: "testDeviceID", + }, + }, + wantErr: true, + }, + { + name: "Type_Resource - valid", + args: args{ + sub: store.Subscription{ + SubscriptionID: "2", + Type: store.Type_Resource, + LinkedAccountID: "testLinkedAccountID", + DeviceID: "testDeviceID", + Href: "testHref", + }, + }, + want: store.Subscription{ + SubscriptionID: "2", + Type: store.Type_Resource, + LinkedAccountID: "testLinkedAccountID", + DeviceID: "testDeviceID", + Href: "testHref", + }, + }, + + { + name: "Type_Resource - valid - duplicit with same LinkedAccountID", + args: args{ + sub: store.Subscription{ + SubscriptionID: "2", + Type: store.Type_Resource, + LinkedAccountID: "testLinkedAccountID", + DeviceID: "testDeviceID", + Href: "testHref", + }, + }, + want: store.Subscription{ + SubscriptionID: "2", + Type: store.Type_Resource, + LinkedAccountID: "testLinkedAccountID", + DeviceID: "testDeviceID", + Href: "testHref", + }, + }, + { + name: "Type_Resource - error - duplicit with different LinkedAccountID", + args: args{ + sub: store.Subscription{ + SubscriptionID: "3", + Type: store.Type_Resource, + LinkedAccountID: "testLinkedAccountID", + DeviceID: "testDeviceID", + Href: "testHref", + }, + }, + wantErr: true, + }, + } + + require := require.New(t) + var config Config + err := envconfig.Process("", &config) + require.NoError(err) + ctx := context.Background() + s := newStore(ctx, t, config) + require.NoError(err) + defer s.Clear(ctx) + + assert := assert.New(t) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := s.FindOrCreateSubscription(ctx, tt.args.sub) + if tt.wantErr { + assert.Error(err) + } else { + assert.NoError(err) + assert.Equal(tt.want, got) + } + }) + } +} + +type testSubscriptionHandler struct { + subs []store.Subscription +} + +func (h *testSubscriptionHandler) Handle(ctx context.Context, iter store.SubscriptionIter) (err error) { + var sub store.Subscription + for iter.Next(ctx, &sub) { + h.subs = append(h.subs, sub) + } + return iter.Err() +} + +func TestStore_LoadSubscriptions(t *testing.T) { + + subs := []store.Subscription{ + store.Subscription{ + SubscriptionID: "0", + Type: store.Type_Devices, + LinkedAccountID: "testLinkedAccountID", + }, + store.Subscription{ + SubscriptionID: "1", + Type: store.Type_Device, + LinkedAccountID: "testLinkedAccountID", + DeviceID: "testDeviceID", + }, + store.Subscription{ + SubscriptionID: "2", + Type: store.Type_Resource, + LinkedAccountID: "testLinkedAccountID", + DeviceID: "testDeviceID", + Href: "testHref", + }, + } + + type args struct { + queries []store.SubscriptionQuery + } + tests := []struct { + name string + args args + wantErr bool + want []store.Subscription + }{ + { + name: "bySubscriptionID", + args: args{ + queries: []store.SubscriptionQuery{store.SubscriptionQuery{SubscriptionID: "2"}}, + }, + want: []store.Subscription{subs[2]}, + }, + { + name: "byLinkedAccountID", + args: args{ + queries: []store.SubscriptionQuery{store.SubscriptionQuery{LinkedAccountID: "testLinkedAccountID"}}, + }, + want: subs, + }, + { + name: "byResource", + args: args{ + queries: []store.SubscriptionQuery{store.SubscriptionQuery{Type: store.Type_Resource, DeviceID: "testDeviceID", Href: "testHref"}}, + }, + want: []store.Subscription{subs[2]}, + }, + { + name: "byDevice", + args: args{ + queries: []store.SubscriptionQuery{store.SubscriptionQuery{Type: store.Type_Device, DeviceID: "testDeviceID"}}, + }, + want: []store.Subscription{subs[1]}, + }, + { + name: "allDeviceSubscriptions", + args: args{ + queries: []store.SubscriptionQuery{store.SubscriptionQuery{Type: store.Type_Device}}, + }, + want: []store.Subscription{subs[1]}, + }, + } + + require := require.New(t) + var config Config + err := envconfig.Process("", &config) + require.NoError(err) + ctx := context.Background() + s := newStore(ctx, t, config) + require.NoError(err) + defer s.Clear(ctx) + + assert := assert.New(t) + + for _, sub := range subs { + _, err := s.FindOrCreateSubscription(ctx, sub) + require.NoError(err) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var h testSubscriptionHandler + err := s.LoadSubscriptions(ctx, tt.args.queries, &h) + if tt.wantErr { + assert.Error(err) + } else { + assert.NoError(err) + assert.Equal(tt.want, h.subs) + } + }) + } +} + +func TestStore_RemoveSubscriptions(t *testing.T) { + subs := []store.Subscription{ + store.Subscription{ + SubscriptionID: "0", + Type: store.Type_Devices, + LinkedAccountID: "testLinkedAccountID", + }, + store.Subscription{ + SubscriptionID: "1", + Type: store.Type_Device, + LinkedAccountID: "testLinkedAccountID", + DeviceID: "testDeviceID", + }, + store.Subscription{ + SubscriptionID: "2", + Type: store.Type_Resource, + LinkedAccountID: "testLinkedAccountID", + DeviceID: "testDeviceID", + Href: "testHref", + }, + } + type args struct { + query store.SubscriptionQuery + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "byLinkedAccountID", + args: args{ + query: store.SubscriptionQuery{ + LinkedAccountID: "testLinkedAccountID", + }, + }, + }, + { + name: "byDeviceID", + args: args{ + query: store.SubscriptionQuery{ + DeviceID: "testDeviceID", + }, + }, + wantErr: true, + }, + { + name: "byHref", + args: args{ + query: store.SubscriptionQuery{ + Href: "testHref", + }, + }, + wantErr: true, + }, + { + name: "byType", + args: args{ + query: store.SubscriptionQuery{ + Type: store.Type_Devices, + }, + }, + wantErr: true, + }, + } + + require := require.New(t) + var config Config + err := envconfig.Process("", &config) + require.NoError(err) + ctx := context.Background() + s := newStore(ctx, t, config) + require.NoError(err) + defer s.Clear(ctx) + assert := assert.New(t) + + for _, sub := range subs { + _, err := s.FindOrCreateSubscription(ctx, sub) + require.NoError(err) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := s.RemoveSubscriptions(ctx, tt.args.query) + if tt.wantErr { + assert.Error(err) + } else { + assert.NoError(err) + } + }) + } +} diff --git a/openapi-connector/store/store.go b/openapi-connector/store/store.go new file mode 100644 index 000000000..fa25b5b5b --- /dev/null +++ b/openapi-connector/store/store.go @@ -0,0 +1,60 @@ +package store + +import ( + "context" +) + +type Query struct { + ID string +} + +type LinkedAccountIter interface { + Next(ctx context.Context, sub *LinkedAccount) bool + Err() error +} + +type LinkedAccountHandler interface { + Handle(ctx context.Context, iter LinkedAccountIter) (err error) +} + +type LinkedCloudIter interface { + Next(ctx context.Context, sub *LinkedCloud) bool + Err() error +} + +type LinkedCloudHandler interface { + Handle(ctx context.Context, iter LinkedCloudIter) (err error) +} + +type SubscriptionQuery struct { + SubscriptionID string + LinkedAccountID string + DeviceID string + Href string + Type Type +} + +type SubscriptionIter interface { + Next(ctx context.Context, sub *Subscription) bool + Err() error +} + +type SubscriptionHandler interface { + Handle(ctx context.Context, iter SubscriptionIter) (err error) +} + +type Store interface { + UpdateLinkedCloud(ctx context.Context, sub LinkedCloud) error + InsertLinkedCloud(ctx context.Context, sub LinkedCloud) error + RemoveLinkedCloud(ctx context.Context, ConfigId string) error + LoadLinkedClouds(ctx context.Context, query Query, h LinkedCloudHandler) error + + UpdateLinkedAccount(ctx context.Context, sub LinkedAccount) error + InsertLinkedAccount(ctx context.Context, sub LinkedAccount) error + RemoveLinkedAccount(ctx context.Context, LinkedAccountId string) error + LoadLinkedAccounts(ctx context.Context, query Query, h LinkedAccountHandler) error + + LoadSubscriptions(ctx context.Context, query []SubscriptionQuery, h SubscriptionHandler) error + FindOrCreateSubscription(ctx context.Context, sub Subscription) (Subscription, error) + RemoveSubscriptions(ctx context.Context, query SubscriptionQuery) error +} diff --git a/openapi-connector/store/subscription.go b/openapi-connector/store/subscription.go new file mode 100644 index 000000000..9c7043e3f --- /dev/null +++ b/openapi-connector/store/subscription.go @@ -0,0 +1,18 @@ +package store + +type Type string + +const ( + Type_Devices Type = "devices" + Type_Device Type = "device" + Type_Resource Type = "resource" +) + +type Subscription struct { + SubscriptionID string + Type Type + LinkedAccountID string + DeviceID string + Href string + SigningSecret string +} diff --git a/openapi-connector/uri/uri.go b/openapi-connector/uri/uri.go new file mode 100644 index 000000000..71d5674d7 --- /dev/null +++ b/openapi-connector/uri/uri.go @@ -0,0 +1,28 @@ +package uri + +// Resource Service URIs. +const ( + API string = "/api" + Version string = API + "/v1" + + // GET - retrieve all linked clouds + // POST - add linked cloud + LinkedClouds string = Version + "/linkedclouds" + + // DELETE - delete linked cloud + LinkedCloud string = LinkedClouds + "/{{ .LinkedCloudId }}" + + // GET - add linked account - params: target_url, target_linked_cloud_id + LinkedAccounts string = Version + "/linkedaccounts" + // GET - retrieve all linked accounts + RetrieveLinkedAccounts string = Version + "/linkedaccounts/retrieve" + + // DELETE - delete linked account + LinkedAccount string = LinkedAccounts + "/{{ .LinkedAccountId }}" + + // POST - new events from target cloud subscriptions + NotifyLinkedAccount string = Version + "/linkedaccountsevents" + + // GET + //OAuthCallback = is loaded from ENV var +) diff --git a/openapi-gateway/.dockerignore b/openapi-gateway/.dockerignore new file mode 100644 index 000000000..5ed6efd6f --- /dev/null +++ b/openapi-gateway/.dockerignore @@ -0,0 +1,6 @@ +.git +.github +.gitignore +.travis.yml +Makefile +vendor diff --git a/openapi-gateway/Dockerfile b/openapi-gateway/Dockerfile new file mode 100644 index 000000000..025a61e0e --- /dev/null +++ b/openapi-gateway/Dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.13.5-alpine3.10 AS build +RUN apk add --no-cache curl git build-base +WORKDIR $GOPATH/src/github.com/go-ocf/cloud/openapi-gateway +COPY . . +RUN go mod download +RUN go build -o /go/bin/service ./cmd/service + +FROM alpine:3.11 as service +RUN apk add --no-cache ca-certificates +COPY --from=build /go/bin/service /usr/local/bin/service +ENTRYPOINT ["/usr/local/bin/service"] \ No newline at end of file diff --git a/openapi-gateway/Makefile b/openapi-gateway/Makefile new file mode 100644 index 000000000..bf54f2a84 --- /dev/null +++ b/openapi-gateway/Makefile @@ -0,0 +1,32 @@ +SHELL = /bin/bash +SERVICE_NAME = $(notdir $(CURDIR)) +LATEST_TAG = vnext +VERSION_TAG = vnext-$(shell git rev-parse --short=7 --verify HEAD) + +default: build + +define build-docker-image + docker build \ + --network=host \ + --tag ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) \ + --tag ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) \ + --target $(1) \ + --file Dockerfile \ + . +endef + +build-servicecontainer: + $(call build-docker-image,service) + +build: build-servicecontainer + +push: build-servicecontainer + docker push ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) + docker push ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) + +proto/generate: + +.PHONY: build-servicecontainer build push proto/generate + + + diff --git a/openapi-gateway/README.md b/openapi-gateway/README.md new file mode 100644 index 000000000..49f84b7b1 --- /dev/null +++ b/openapi-gateway/README.md @@ -0,0 +1,122 @@ +[![Go Report](https://goreportcard.com/badge/github.com/go-ocf/cloud/openapi-gateway)](https://goreportcard.com/report/github.com/go-ocf/cloud/openapi-gateway) +[![Gitter](https://badges.gitter.im/ocfcloud/Lobby.svg)](https://gitter.im/ocfcloud/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +# Cloud API for Cloud Services +## How to try? +### Steps +1. Authorize the user: Request the user's authorization and redirect back to your application with an authorization code. +2. Request tokens: Exchange your authorization code for tokens. +3. Call your API: Use the retrieved Access Token to call your API. +4. Refresh Tokens: Use a Refresh Token to request new tokens when the existing ones expire. + +### Authorize the User +- Authenticating the user; +- Redirecting the user to an Identity Provider to handle authentication; +- Checking for active Single Sign-on (SSO) sessions; +- Obtaining user consent for the requested permission level, unless consent has been previously given. + +To authorize the user, your app must send the user to the authorization URL. +#### Try pluggedin.cloud +```bash +https://auth.plgd.cloud/authorize? + response_type=code& + client_id=9XjK2mCf2J0or4Ko0ow7wCmZeDTjC1mW& + redirect_uri=http://localhost:8080/callback& + scope=r:deviceinformation:* r:resources:* w:resources:* w:subscriptions:* offline_access& + audience=https://openapi.try.plgd.cloud/& + state=STATE +``` + +#### Response +If all goes well, you'll receive an HTTP 302 response. The authorization code is included at the end of the URL: +``` +http://localhost:8080/callback?code=s65bpdt-ry7QEh6O&state=STATE +``` + +### Request Tokens +Now that you have an Authorization Code, you must exchange it for tokens. Using the extracted Authorization Code (code) from the previous step, you will need to POST to the token URL. + +#### Try pluggedin.cloud +```bash +curl --request POST \ + --url 'https://auth.plgd.cloud/oauth/token' \ + --header 'content-type: application/x-www-form-urlencoded' \ + --data grant_type=authorization_code \ + --data 'client_id=9XjK2mCf2J0or4Ko0ow7wCmZeDTjC1mW' \ + --data client_secret=UTeeIsSugTuDNbn4QMdBaNLDnMiBQzQaa6elm4SDuWOdZUou-aH00EPSbBhgppFD \ + --data code={YOUR_AUTHORIZATION_CODE} \ + --data 'redirect_uri=http://localhost:8080/callback' +``` + +#### Response +If all goes well, you'll receive an HTTP 200 response with a payload containing access_token, refresh_token, scope, expires_in and token_type values: +```json +{ + "access_token":"ey...ojg", + "refresh_token":"pL...btL", + "scope":"r:deviceinformation:* r:resources:* w:resources:* w:subscriptions:* offline_access", + "expires_in":86400, + "token_type":"Bearer" +} +``` + +### Call the C2C API +To call the C2C API as an authorized user, the application must pass the retrieved Access Token as a Bearer token in the Authorization header of your HTTP request. +```bash +curl --request GET \ + --url https://openapi.try.plgd.cloud/api/v1/devices \ + --header 'authorization: Bearer eyJ...lojg' \ + --header 'content-type: application/json' \ + --header 'accept: application/json' +``` + +### Refresh the token +You can use the Refresh Token to get a new Access Token. The application communicating with the C2C Endpoint needs a new Access Token only after the previous one expires. It's bad practice to call the endpoint to get a new Access Token every time you call an API, and pluggedin.cloud maintains rate limits that will throttle the amount of requests to the endpoint that can be executed using the same token from the same IP. + +To refresh your token, make a POST request to the token endpoint, using grant_type=refresh_token. +```bash +curl --request POST \ + --url 'https://auth.plgd.cloud/oauth/token' \ + --header 'content-type: application/x-www-form-urlencoded' \ + --data grant_type=refresh_token \ + --data 'client_id=9XjK2mCf2J0or4Ko0ow7wCmZeDTjC1mW' \ + --data refresh_token={YOUR_REFRESH_TOKEN} +``` + +> Now you're able to authorize the user, request the token, communicate with the C2C API and refresh the token before it expires. + +### Device Onboarding +To be able to see the devices through the `pluggedin.cloud` C2C API, first you need to onboard the device. When you have your device ready, go to the `https://pluggedin.cloud` and click `TRY`. This redirects you to the `pluggedin.cloud Portal`. + +First thing you need is an authorization code. In the `pluggedin.cloud Portal` go to `Devices` and click `Onboard Device`. This displays you the code needed to onboard the device. Values which should be set to the [coapcloudconf](https://github.com/openconnectivityfoundation/cloud-services/blob/c2c/swagger2.0/oic.r.coapcloudconf.swagger.json) device resource are: + +#### Unsecured device +- `apn` : `auth0` +- `cis` : `coap+tcp://try.plgd.cloud:5683` +- `sid` : `adebc667-1f2b-41e3-bf5c-6d6eabc68cc6` +- `at` : `CODE_FROM_PORTAL` + +#### Secured device +- `apn` : `auth0` +- `cis` : `coaps+tcp://try.plgd.cloud:5684` +- `sid` : `adebc667-1f2b-41e3-bf5c-6d6eabc68cc6` +- `at` : `CODE_FROM_PORTAL` + +Conditions: +- `Device must be owned.` +- `Cloud CA must be set as TRUST CA with subject adebc667-1f2b-41e3-bf5c-6d6eabc68cc6 in device.` +- `Cloud CA in PEM:` +``` +-----BEGIN CERTIFICATE----- +MIIBhDCCASmgAwIBAgIQdAMxveYP9Nb48xe9kRm3ajAKBggqhkjOPQQDAjAxMS8w +LQYDVQQDEyZPQ0YgQ2xvdWQgUHJpdmF0ZSBDZXJ0aWZpY2F0ZXMgUm9vdCBDQTAe +Fw0xOTExMDYxMjAzNTJaFw0yOTExMDMxMjAzNTJaMDExLzAtBgNVBAMTJk9DRiBD +bG91ZCBQcml2YXRlIENlcnRpZmljYXRlcyBSb290IENBMFkwEwYHKoZIzj0CAQYI +KoZIzj0DAQcDQgAEaNJi86t5QlZiLcJ7uRMNlcwIpmFiJf9MOqyz2GGnGVBypU6H +lwZHY2/l5juO/O4EH2s9h3HfcR+nUG2/tFzFEaMjMCEwDgYDVR0PAQH/BAQDAgEG +MA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhAM7gFe39UJPIjIDE +KrtyPSIGAk0OAO8txhow1BAGV486AiEAqszg1fTfOHdE/pfs8/9ZP5gEVVkexRHZ +JCYVaa2Spbg= +-----END CERTIFICATE----- +``` +- `ACL for Cloud (Subject: adebc667-1f2b-41e3-bf5c-6d6eabc68cc6) must be set with full access to all published resources in device.` diff --git a/openapi-gateway/cmd/service/main.go b/openapi-gateway/cmd/service/main.go new file mode 100644 index 000000000..747e13be9 --- /dev/null +++ b/openapi-gateway/cmd/service/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "github.com/go-ocf/kit/log" + "github.com/go-ocf/cloud/openapi-gateway/refImpl" + "github.com/kelseyhightower/envconfig" +) + +func main() { + var config refImpl.Config + if err := envconfig.Process("", &config); err != nil { + log.Fatalf("cannot parse configuration: %v", err) + } + if server, err := refImpl.Init(config); err != nil { + log.Fatalf("cannot init server: %v", err) + } else { + if err = server.Serve(); err != nil { + log.Fatalf("unexpected ends: %v", err) + } + } +} diff --git a/openapi-gateway/refImpl/refImpl.go b/openapi-gateway/refImpl/refImpl.go new file mode 100644 index 000000000..40234aead --- /dev/null +++ b/openapi-gateway/refImpl/refImpl.go @@ -0,0 +1,200 @@ +package refImpl + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "regexp" + + "github.com/panjf2000/ants" + + "github.com/go-ocf/kit/log" + kitNetHttp "github.com/go-ocf/kit/net/http" + "github.com/go-ocf/kit/security/certManager" + "github.com/go-ocf/cloud/openapi-gateway/service" + storeMongodb "github.com/go-ocf/cloud/openapi-gateway/store/mongodb" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventbus/nats" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/mongodb" +) + +type Config struct { + Log log.Config `envconfig:"LOG"` + ResourceMongoDB mongodb.Config `envconfig:"MONGODB"` + ResourceNats nats.Config `envconfig:"NATS"` + Service service.Config + GoRoutinePoolSize int `envconfig:"GOROUTINE_POOL_SIZE" default:"16"` + StoreMongoDB storeMongodb.Config + Dial certManager.Config `envconfig:"DIAL"` + Listen certManager.Config `envconfig:"LISTEN"` + JwksURL string `envconfig:"JWKS_URL"` +} + +//String return string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} + +type RefImpl struct { + server *service.Server + dialCertManager certManager.CertManager + listenCertManager certManager.CertManager +} + +func Init(config Config) (*RefImpl, error) { + log.Setup(config.Log) + + dialCertManager, err := certManager.NewCertManager(config.Dial) + if err != nil { + return nil, fmt.Errorf("cannot create dial cert manager %w", err) + } + dialTLSConfig := dialCertManager.GetClientTLSConfig() + + pool, err := ants.NewPool(config.GoRoutinePoolSize) + if err != nil { + return nil, fmt.Errorf("cannot create goroutine pool: %w", err) + } + + resourceEventstore, err := mongodb.NewEventStore(config.ResourceMongoDB, pool.Submit, mongodb.WithTLS(&dialTLSConfig)) + if err != nil { + return nil, fmt.Errorf("cannot create resource mongodb eventstore %w", err) + } + + resourceSubscriber, err := nats.NewSubscriber(config.ResourceNats, pool.Submit, func(err error) { log.Errorf("error occurs during receiving event: %v", err) }, nats.WithTLS(&dialTLSConfig)) + if err != nil { + return nil, fmt.Errorf("cannot create resource kafka subscriber %w", err) + } + + substore, err := storeMongodb.NewStore(context.Background(), config.StoreMongoDB, storeMongodb.WithTLS(&dialTLSConfig)) + if err != nil { + return nil, fmt.Errorf("cannot create mongodb substore %w", err) + } + + listenCertManager, err := certManager.NewCertManager(config.Listen) + if err != nil { + return nil, fmt.Errorf("cannot create listen cert manager %w", err) + } + + log.Info(config.String()) + + auth := kitNetHttp.NewInterceptor(config.JwksURL, dialCertManager.GetClientTLSConfig(), authRules) + + return &RefImpl{ + server: service.New(config.Service, dialCertManager, listenCertManager, auth, resourceEventstore, resourceSubscriber, substore, pool.Submit), + dialCertManager: dialCertManager, + listenCertManager: listenCertManager, + }, nil +} + +func (r *RefImpl) Serve() error { + return r.server.Serve() +} + +func (r *RefImpl) Shutdown() { + r.server.Shutdown() + r.dialCertManager.Close() + r.listenCertManager.Close() +} + +// https://openconnectivity.org/draftspecs/Gaborone/OCF_Cloud_API_for_Cloud_Services.pdf +var authRules = map[string][]kitNetHttp.AuthArgs{ + http.MethodGet: []kitNetHttp.AuthArgs{ + kitNetHttp.AuthArgs{ + URI: regexp.MustCompile(`\/api\/v1\/devices$`), + Scopes: []*regexp.Regexp{ + regexp.MustCompile(`r:deviceinformation:.*`), + }, + }, + kitNetHttp.AuthArgs{ + URI: regexp.MustCompile(`\/api\/v1\/devices\?` + service.ContentQuery + `=` + service.ContentQueryBaseValue + `$`), + Scopes: []*regexp.Regexp{ + regexp.MustCompile(`r:deviceinformation:.*`), + }, + }, + kitNetHttp.AuthArgs{ + URI: regexp.MustCompile(`\/api\/v1\/devices\?` + service.ContentQuery + `=` + service.ContentQueryAllValue + `$`), + Scopes: []*regexp.Regexp{ + regexp.MustCompile(`r:deviceinformation:.*`), + regexp.MustCompile(`r:resources:.*`), + }, + }, + kitNetHttp.AuthArgs{ + URI: regexp.MustCompile(`\/api\/v1\/devices\/[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}$`), + Scopes: []*regexp.Regexp{ + regexp.MustCompile(`r:deviceinformation:.*`), + }, + }, + kitNetHttp.AuthArgs{ + URI: regexp.MustCompile(`\/api\/v1\/devices\/[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\?` + service.ContentQuery + `=` + service.ContentQueryBaseValue + `$`), + Scopes: []*regexp.Regexp{ + regexp.MustCompile(`r:deviceinformation:.*`), + }, + }, + kitNetHttp.AuthArgs{ + URI: regexp.MustCompile(`\/api\/v1\/devices\/[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\?` + service.ContentQuery + `=` + service.ContentQueryAllValue + `$`), + Scopes: []*regexp.Regexp{ + regexp.MustCompile(`r:deviceinformation:.*`), + regexp.MustCompile(`r:resources:.*`), + }, + }, + kitNetHttp.AuthArgs{ + URI: regexp.MustCompile(`\/api\/v1\/devices\/[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\/.*`), + Scopes: []*regexp.Regexp{ + regexp.MustCompile(`r:resources:.*`), + }, + }, + }, + http.MethodPost: []kitNetHttp.AuthArgs{ + kitNetHttp.AuthArgs{ + URI: regexp.MustCompile(`\/api\/v1\/devices\/subscriptions$`), + Scopes: []*regexp.Regexp{ + regexp.MustCompile(`r:deviceinformation:.*`), + regexp.MustCompile(`w:subscriptions:.*`), + }, + }, + kitNetHttp.AuthArgs{ + URI: regexp.MustCompile(`\/api\/v1\/devices\/[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\/subscriptions$`), + Scopes: []*regexp.Regexp{ + regexp.MustCompile(`r:deviceinformation:.*`), + regexp.MustCompile(`w:subscriptions:.*`), + }, + }, + kitNetHttp.AuthArgs{ + URI: regexp.MustCompile(`\/api\/v1\/devices\/[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\/.*\/subscriptions$`), + Scopes: []*regexp.Regexp{ + regexp.MustCompile(`r:resources:.*`), + regexp.MustCompile(`w:subscriptions:.*`), + }, + }, + kitNetHttp.AuthArgs{ + URI: regexp.MustCompile(`\/api\/v1\/devices\/[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\/.*`), + Scopes: []*regexp.Regexp{ + regexp.MustCompile(`w:resources:.*`), + }, + }, + }, + http.MethodDelete: []kitNetHttp.AuthArgs{ + kitNetHttp.AuthArgs{ + URI: regexp.MustCompile(`\/api\/v1\/devices\/subscriptions\/[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}$`), + Scopes: []*regexp.Regexp{ + regexp.MustCompile(`r:deviceinformation:.*`), + regexp.MustCompile(`w:subscriptions:.*`), + }, + }, + kitNetHttp.AuthArgs{ + URI: regexp.MustCompile(`\/api\/v1\/devices\/[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\/subscriptions\/[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}$`), + Scopes: []*regexp.Regexp{ + regexp.MustCompile(`r:deviceinformation:.*`), + regexp.MustCompile(`w:subscriptions:.*`), + }, + }, + kitNetHttp.AuthArgs{ + URI: regexp.MustCompile(`\/api\/v1\/devices\/[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\/.*\/subscriptions\/[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}$`), + Scopes: []*regexp.Regexp{ + regexp.MustCompile(`r:resources:.*`), + regexp.MustCompile(`w:subscriptions:.*`), + }, + }, + }, +} diff --git a/openapi-gateway/refImpl/refImpl_test.go b/openapi-gateway/refImpl/refImpl_test.go new file mode 100644 index 000000000..c669889b3 --- /dev/null +++ b/openapi-gateway/refImpl/refImpl_test.go @@ -0,0 +1,28 @@ +package refImpl + +import ( + "os" + "testing" + + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/require" +) + +func TestInit(t *testing.T) { + var config Config + os.Setenv("OAUTH_CALLBACK", "OAUTH_CALLBACK") + os.Setenv("EVENTS_URL", "EVENTS_URL") + os.Setenv("NAME", "NAME") + os.Setenv("CLIENT_ID", "CLIENT_ID") + os.Setenv("CLIENT_SECRET", "CLIENT_SECRET") + os.Setenv("SCOPES", "SCOPES") + os.Setenv("AUTH_URL", "AUTH_URL") + os.Setenv("TOKEN_URL", "TOKEN_URL") + err := envconfig.Process("", &config) + require.NoError(t, err) + config.GoRoutinePoolSize = 1 + + got, err := Init(config) + require.NoError(t, err) + require.NotEmpty(t, got) +} diff --git a/openapi-gateway/renovate.json b/openapi-gateway/renovate.json new file mode 100644 index 000000000..f844c2ed8 --- /dev/null +++ b/openapi-gateway/renovate.json @@ -0,0 +1,15 @@ +{ + "extends": [ + "config:base" + ], + "postUpdateOptions": [ + "gomodTidy" + ], + "commitBody": "Generated by renovateBot", + "packageRules": [ + { + "packagePatterns": [".+"], + "schedule": ["on the first day of the month"] + } + ] +} diff --git a/openapi-gateway/service/auth.go b/openapi-gateway/service/auth.go new file mode 100644 index 000000000..f566530d4 --- /dev/null +++ b/openapi-gateway/service/auth.go @@ -0,0 +1,48 @@ +package service + +import ( + "fmt" + "strings" + + jwt "github.com/dgrijalva/jwt-go" +) + +func parseAuth(auth string) (token, sub string, err error) { + if strings.HasPrefix(auth, "Bearer ") { + rawToken := auth[7:] + sub, err = parseSubFromJwtToken(rawToken) + if err != nil { + err = fmt.Errorf("cannot parse bearer: %w", err) + return + } + token = rawToken + return + } + return "", "", fmt.Errorf("cannot parse bearer: prefix 'Bearer ' not found") +} + +type claims struct { + Subject string `json:"sub,omitempty"` +} + +func (c *claims) Valid() error { + return nil +} + +func parseSubFromJwtToken(rawJwtToken string) (string, error) { + parser := &jwt.Parser{ + SkipClaimsValidation: true, + } + + var claims claims + _, _, err := parser.ParseUnverified(rawJwtToken, &claims) + if err != nil { + return "", fmt.Errorf("cannot get sub from jwt token: %w", err) + } + + if claims.Subject != "" { + return claims.Subject, nil + } + + return "", fmt.Errorf("cannot get sub from jwt token: not found") +} diff --git a/openapi-gateway/service/cborEncoder.go b/openapi-gateway/service/cborEncoder.go new file mode 100644 index 000000000..3022d4f6d --- /dev/null +++ b/openapi-gateway/service/cborEncoder.go @@ -0,0 +1,19 @@ +package service + +import ( + "net/http" + + "github.com/go-ocf/cloud/openapi-connector/events" + + "github.com/go-ocf/kit/codec/cbor" +) + +func newCBORResponseWriterEncoder(contentType string) func(w http.ResponseWriter, v interface{}) error { + return func(w http.ResponseWriter, v interface{}) error { + if v == nil { + return nil + } + w.Header().Set(events.ContentTypeKey, contentType) + return cbor.WriteTo(w, v) + } +} diff --git a/openapi-gateway/service/config.go b/openapi-gateway/service/config.go new file mode 100644 index 000000000..bfc0db0c8 --- /dev/null +++ b/openapi-gateway/service/config.go @@ -0,0 +1,26 @@ +package service + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/go-ocf/kit/net/grpc" +) + +//Config represent application configuration +type Config struct { + grpc.Config + AuthServerAddr string `envconfig:"AUTH_SERVER_ADDRESS" default:"127.0.0.1:9100"` + ResourceAggregateAddr string `envconfig:"RESOURCE_AGGREGATE_ADDRESS" default:"127.0.0.1:9100"` + ResourceDirectoryAddr string `envconfig:"RESOURCE_DIRECTORY_ADDRESS" default:"127.0.0.1:9100"` + FQDN string `envconfig:"FQDN" default:"openapi.pluggedin.cloud"` + DevicesCheckInterval time.Duration `envconfig:"ALL_DEVICES_CHECK_INTERVAL" default:"3s"` + TimeoutForRequests time.Duration `envconfig:"TIMEOUT_FOR_REQUESTS" default:"10s"` +} + +//String return string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} diff --git a/openapi-gateway/service/devicesSubscription.go b/openapi-gateway/service/devicesSubscription.go new file mode 100644 index 000000000..ef2cc170e --- /dev/null +++ b/openapi-gateway/service/devicesSubscription.go @@ -0,0 +1,201 @@ +package service + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/go-ocf/kit/log" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/cloud/openapi-connector/events" + "github.com/go-ocf/cloud/openapi-gateway/store" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +type devicesSubscription struct { + rh *RequestHandler + goroutinePoolGo GoroutinePoolGoFunc +} + +func newDevicesSubscription(rh *RequestHandler, goroutinePoolGo GoroutinePoolGoFunc) *devicesSubscription { + return &devicesSubscription{ + rh: rh, + goroutinePoolGo: goroutinePoolGo, + } +} + +func handleSubscription(ctx context.Context, rh *RequestHandler, sub store.DevicesSubscription) error { + devicesRegistered := make(map[string]events.Device) + devicesOnline := make(map[string]events.Device) + devicesOffline := make(map[string]events.Device) + authorizationContext := pbCQRS.AuthorizationContext{ + UserId: sub.UserID, + } + devices, err := rh.GetDevices(kitNetGrpc.CtxWithToken(ctx, sub.AccessToken), nil, authorizationContext) + if err != nil { + sub, errPop := rh.store.PopSubscription(ctx, sub.ID) + if errPop != nil { + return err + } + _, _ = emitEvent(ctx, events.EventType_SubscriptionCanceled, sub, func(ctx context.Context, subscriptionID string) (uint64, error) { + return sub.SequenceNumber, nil + }, nil) + return err + } + lastDevicesRegistered := make(events.DevicesRegistered, 0, len(devices)) + lastDevicesOnline := make(events.DevicesOnline, 0, len(devices)) + lastDevicesOffline := make(events.DevicesOffline, 0, len(devices)) + + for _, dev := range devices { + devicesRegistered[dev.Device.ID] = events.Device{ + ID: dev.Device.ID, + } + lastDevicesRegistered = append(lastDevicesRegistered, events.Device{ + ID: dev.Device.ID, + }) + if dev.Status == Status_ONLINE { + devicesOnline[dev.Device.ID] = events.Device{ + ID: dev.Device.ID, + } + lastDevicesOnline = append(lastDevicesOnline, events.Device{ + ID: dev.Device.ID, + }) + } else { + devicesOffline[dev.Device.ID] = events.Device{ + ID: dev.Device.ID, + } + lastDevicesOffline = append(lastDevicesOffline, events.Device{ + ID: dev.Device.ID, + }) + } + } + + devicesUnregistered := make(map[string]events.Device) + for _, dev := range sub.LastDevicesRegistered { + dev, ok := devicesRegistered[dev.ID] + if ok { + delete(devicesRegistered, dev.ID) + } else { + devicesUnregistered[dev.ID] = dev + } + } + + for _, dev := range sub.LastDevicesOnline { + delete(devicesOnline, dev.ID) + } + for _, dev := range sub.LastDevicesOffline { + delete(devicesOffline, dev.ID) + } + + if len(devicesRegistered) == 0 && len(devicesUnregistered) == 0 && len(devicesOnline) == 0 && len(devicesOffline) == 0 { + return nil + } + + log.Debugf("emit events for subscription %+v", sub) + + for _, eventType := range sub.EventTypes { + switch eventType { + case events.EventType_DevicesRegistered: + if len(devicesRegistered) > 0 { + devs := make(events.DevicesRegistered, 0, len(devicesRegistered)) + for _, dev := range devicesRegistered { + devs = append(devs, dev) + } + remove, err := emitEvent(ctx, eventType, sub.Subscription, rh.store.IncrementSubscriptionSequenceNumber, devs) + if remove { + rh.store.PopSubscription(ctx, sub.ID) + } + if err != nil { + return fmt.Errorf("cannot emit event: %w", err) + } + } + case events.EventType_DevicesUnregistered: + if len(devicesUnregistered) > 0 { + devs := make(events.DevicesUnregistered, 0, len(devicesUnregistered)) + for _, dev := range devicesUnregistered { + devs = append(devs, dev) + } + remove, err := emitEvent(ctx, eventType, sub.Subscription, rh.store.IncrementSubscriptionSequenceNumber, devs) + if remove { + rh.store.PopSubscription(ctx, sub.ID) + } + if err != nil { + return fmt.Errorf("cannot emit event: %w", err) + } + } + + case events.EventType_DevicesOnline: + if len(devicesOnline) > 0 { + devs := make(events.DevicesOnline, 0, len(devicesOnline)) + for _, dev := range devicesOnline { + devs = append(devs, dev) + } + remove, err := emitEvent(ctx, eventType, sub.Subscription, rh.store.IncrementSubscriptionSequenceNumber, devs) + if remove { + rh.store.PopSubscription(ctx, sub.ID) + } + if err != nil { + return fmt.Errorf("cannot emit event: %w", err) + } + } + case events.EventType_DevicesOffline: + if len(devicesOffline) > 0 { + devs := make(events.DevicesOffline, 0, len(devicesOffline)) + for _, dev := range devicesOffline { + devs = append(devs, dev) + } + remove, err := emitEvent(ctx, eventType, sub.Subscription, rh.store.IncrementSubscriptionSequenceNumber, devs) + if remove { + rh.store.PopSubscription(ctx, sub.ID) + } + if err != nil { + return fmt.Errorf("cannot emit event: %w", err) + } + } + } + } + + err = rh.store.UpdateDevicesSubscription(ctx, sub.ID, lastDevicesRegistered, lastDevicesOnline, lastDevicesOffline) + if err != nil { + return err + } + return nil +} + +func (s *devicesSubscription) Handle(ctx context.Context, iter store.DevicesSubscriptionIter) error { + var sub store.DevicesSubscription + + var wg sync.WaitGroup + for iter.Next(ctx, &sub) { + wg.Add(1) + err := s.goroutinePoolGo(func() { + defer wg.Done() + err := handleSubscription(ctx, s.rh, sub) + if err != nil { + log.Error(fmt.Errorf("cannot handle subscription %v: %w", sub.ID, err)) + } + }) + if err != nil { + wg.Done() + } + } + wg.Wait() + return iter.Err() +} + +func (s *devicesSubscription) Serve(ctx context.Context, checkInterval time.Duration) { + for { + err := s.rh.store.LoadDevicesSubscriptions(ctx, store.DevicesSubscriptionQuery{ + LastCheck: time.Now().Add(-checkInterval), + }, s) + if err != nil { + log.Errorf("cannot server devicesscriptionSubscription: %v", err) + } + select { + case <-time.After(checkInterval): + case <-ctx.Done(): + return + } + } +} diff --git a/openapi-gateway/service/eventHandler.go b/openapi-gateway/service/eventHandler.go new file mode 100644 index 000000000..27009ab3a --- /dev/null +++ b/openapi-gateway/service/eventHandler.go @@ -0,0 +1,267 @@ +package service + +import ( + "context" + "fmt" + "io" + "io/ioutil" + netHttp "net/http" + "strconv" + "sync" + "time" + + "github.com/go-ocf/cloud/openapi-connector/events" + oapiStore "github.com/go-ocf/cloud/openapi-connector/store" + + "github.com/go-ocf/cqrs/eventstore" + "github.com/go-ocf/kit/log" + "github.com/go-ocf/cloud/openapi-gateway/store" + "github.com/go-ocf/sdk/schema" +) + +type EventHandler struct { + store store.Store + goroutinePoolGo GoroutinePoolGoFunc +} + +func newEventHandler( + store store.Store, + goroutinePoolGo GoroutinePoolGoFunc, +) *EventHandler { + return &EventHandler{ + store: store, + goroutinePoolGo: goroutinePoolGo, + } +} + +type incrementSubscriptionSequenceNumberFunc func(ctx context.Context, subscriptionID string) (uint64, error) + +func emitEvent(ctx context.Context, eventType events.EventType, s store.Subscription, incrementSubscriptionSequenceNumber incrementSubscriptionSequenceNumberFunc, rep interface{}) (remove bool, err error) { + log.Debugf("emitEvent: %v: %+v", eventType, s) + defer log.Debugf("emitEvent done: %v: %+v", eventType, s) + client := netHttp.Client{} + encoder, err := getEncoder(s.ContentType) + if err != nil { + return false, fmt.Errorf("cannot get encoder: %w", err) + } + seqNum, err := incrementSubscriptionSequenceNumber(ctx, s.ID) + if err != nil { + return false, fmt.Errorf("cannot increment sequence number: %w", err) + } + + r, w := io.Pipe() + + req, err := netHttp.NewRequest("POST", s.URL, r) + if err != nil { + return false, fmt.Errorf("cannot create post request: %w", err) + } + timestamp := time.Now() + req.Header.Set(events.EventTypeKey, string(eventType)) + req.Header.Set(events.SubscriptionIDKey, s.ID) + req.Header.Set(events.SequenceNumberKey, strconv.FormatUint(seqNum, 10)) + req.Header.Set(events.CorrelationIDKey, s.CorrelationID) + req.Header.Set(events.EventTimestampKey, strconv.FormatInt(timestamp.Unix(), 10)) + var body []byte + if rep != nil { + body, err = encoder(rep) + if err != nil { + return false, fmt.Errorf("cannot encode data to body: %w", err) + } + req.Header.Set(events.ContentTypeKey, s.ContentType) + go func() { + defer w.Close() + if len(body) > 0 { + _, err := w.Write(body) + if err != nil { + log.Errorf("cannot write data to client: %v", err) + } + } + }() + } else { + w.Close() + } + req.Header.Set(events.EventSignatureKey, events.CalculateEventSignature( + s.SigningSecret, + req.Header.Get(events.ContentTypeKey), + eventType, + req.Header.Get(events.SubscriptionIDKey), + seqNum, + timestamp, + body, + )) + + resp, err := client.Do(req) + if err != nil { + return false, fmt.Errorf("cannot post: %w", err) + } + defer resp.Body.Close() + if resp.StatusCode != netHttp.StatusOK { + errBody, _ := ioutil.ReadAll(resp.Body) + return resp.StatusCode == netHttp.StatusGone, fmt.Errorf("%v: unexpected statusCode %v: body: '%v'", s.URL, resp.StatusCode, string(errBody)) + } + return eventType == events.EventType_SubscriptionCanceled, nil +} + +type resourceSubscriptionHandler struct { + store store.Store + goroutinePoolGo GoroutinePoolGoFunc + event Event +} + +func newResourceSubscriptionHandler( + store store.Store, + goroutinePoolGo GoroutinePoolGoFunc, + event Event, +) *resourceSubscriptionHandler { + return &resourceSubscriptionHandler{ + store: store, + goroutinePoolGo: goroutinePoolGo, + event: event, + } +} + +func (c *resourceSubscriptionHandler) Handle(ctx context.Context, iter store.SubscriptionIter) error { + var s store.Subscription + var wg sync.WaitGroup + for iter.Next(ctx, &s) { + for _, e := range s.EventTypes { + if e == c.event.EventType { + wg.Add(1) + c.goroutinePoolGo(func() { + defer wg.Done() + rs := s + remove, err := emitEvent(ctx, c.event.EventType, rs, c.store.IncrementSubscriptionSequenceNumber, c.event.Representation) + if remove { + c.store.PopSubscription(ctx, rs.ID) + } + if err != nil { + log.Errorf("cannot emit event: %v", err) + } + }) + break + } + } + } + wg.Wait() + return iter.Err() +} + +func (h *EventHandler) processResourceEvent(e Event) error { + err := h.store.LoadSubscriptions( + context.Background(), + store.SubscriptionQuery{ + Type: oapiStore.Type_Resource, + DeviceID: e.DeviceID, + Href: e.Href, + }, + newResourceSubscriptionHandler(h.store, h.goroutinePoolGo, e), + ) + if err != nil { + return fmt.Errorf("cannot process resource event (DeviceID: %v, Href: %v): %w", e.DeviceID, e.Href, err) + } + return nil +} + +type deviceSubscriptionHandlerEvent struct { + store store.Store + goroutinePoolGo GoroutinePoolGoFunc + event Event +} + +func newDeviceSubscriptionHandler( + store store.Store, + goroutinePoolGo GoroutinePoolGoFunc, + event Event, +) *deviceSubscriptionHandlerEvent { + return &deviceSubscriptionHandlerEvent{ + store: store, + goroutinePoolGo: goroutinePoolGo, + event: event, + } +} + +func makeLinksRepresentation(eventType events.EventType, models []eventstore.Model) []schema.ResourceLink { + result := make([]schema.ResourceLink, 0, len(models)) + for _, m := range models { + c := m.(*resourceCtx).Clone() + switch eventType { + case events.EventType_ResourcesPublished: + if c.isPublished { + result = append(result, makeResourceLink(c.resource)) + } + case events.EventType_ResourcesUnpublished: + if !c.isPublished { + result = append(result, makeResourceLink(c.resource)) + } + } + } + return result +} + +func (c *deviceSubscriptionHandlerEvent) Handle(ctx context.Context, iter store.SubscriptionIter) error { + var s store.Subscription + var wg sync.WaitGroup + for iter.Next(ctx, &s) { + for _, e := range s.EventTypes { + if e == c.event.EventType { + wg.Add(1) + err := c.goroutinePoolGo(func() { + defer wg.Done() + rs := s + remove, err := emitEvent(ctx, c.event.EventType, rs, c.store.IncrementSubscriptionSequenceNumber, c.event.Representation) + if remove { + c.store.PopSubscription(ctx, rs.ID) + } + if err != nil { + log.Errorf("cannot emit event: %v", err) + } + }) + if err != nil { + wg.Done() + } + break + } + } + } + wg.Wait() + return iter.Err() +} + +func (h *EventHandler) processDeviceEvent(e Event) error { + err := h.store.LoadSubscriptions( + context.Background(), + store.SubscriptionQuery{ + Type: oapiStore.Type_Device, + DeviceID: e.DeviceID, + }, + newDeviceSubscriptionHandler(h.store, h.goroutinePoolGo, e), + ) + if err != nil { + return fmt.Errorf("cannot process resource event (DeviceID: %v, Href: %v): %w", e.DeviceID, e.Href, err) + } + return nil +} + +func (h *EventHandler) processEvent(e Event) error { + switch e.EventType { + case events.EventType_ResourceChanged: + return h.processResourceEvent(e) + case events.EventType_ResourcesPublished, events.EventType_ResourcesUnpublished: + return h.processDeviceEvent(e) + } + return fmt.Errorf("cannot process event: unknown event-type %v", e.EventType) +} + +func (h *EventHandler) Handle(ctx context.Context, iter Iter) (err error) { + var e Event + + for iter.Next(ctx, &e) { + err := h.processEvent(e) + if err != nil { + log.Errorf("%v", err) + } + } + + return iter.Err() + +} diff --git a/openapi-gateway/service/goroutinePoolHandler.go b/openapi-gateway/service/goroutinePoolHandler.go new file mode 100644 index 000000000..08128ac45 --- /dev/null +++ b/openapi-gateway/service/goroutinePoolHandler.go @@ -0,0 +1,188 @@ +package service + +import ( + "context" + "fmt" + "sync" + + "github.com/go-ocf/cloud/openapi-connector/events" +) + +// ErrFunc used by handler to report error from observation +type ErrFunc func(err error) + +// GoroutinePoolGoFunc processes actions via provided function +type GoroutinePoolGoFunc func(func()) error + +type Event struct { + Id string + EventType events.EventType + DeviceID string + Href string + Representation interface{} +} + +//Iter provides iterator over events from eventstore or eventbus. +type Iter interface { + Next(ctx context.Context, event *Event) bool + Err() error +} + +type Handler interface { + Handle(ctx context.Context, iter Iter) (err error) +} + +// GoroutinePoolHandler submit events to goroutine pool for process them. +type GoroutinePoolHandler struct { + lock sync.Mutex + aggregateEventsContainer map[string]*eventsProcessor + + goroutinePoolGo GoroutinePoolGoFunc + eventsHandler Handler + errFunc ErrFunc +} + +// NewGoroutinePoolHandler creates new event processor. +func NewGoroutinePoolHandler( + goroutinePoolGo GoroutinePoolGoFunc, + eventsHandler Handler, + errFunc ErrFunc) *GoroutinePoolHandler { + + return &GoroutinePoolHandler{ + goroutinePoolGo: goroutinePoolGo, + eventsHandler: eventsHandler, + errFunc: errFunc, + aggregateEventsContainer: make(map[string]*eventsProcessor), + } +} + +func (ep *GoroutinePoolHandler) run(ctx context.Context, p *eventsProcessor) error { + if ep.goroutinePoolGo == nil { + err := p.process(ctx, ep.eventsHandler) + ep.tryToDelete(p.name) + return err + } + err := ep.goroutinePoolGo(func() { + err := p.process(ctx, ep.eventsHandler) + if err != nil { + ep.errFunc(err) + } + ep.tryToDelete(p.name) + }) + if err != nil { + return fmt.Errorf("cannot execute goroutine pool go function: %w", err) + } + return nil +} + +// Handle pushes event to queue and process the queue by goroutine pool. +func (ep *GoroutinePoolHandler) Handle(ctx context.Context, event Event) (err error) { + ed := ep.getEventsData(event.Id) + spawnGo := ed.push([]Event{event}) + if spawnGo { + err := ep.run(ctx, ed) + if err != nil { + return fmt.Errorf("cannot handle events: %w", err) + } + } + return nil +} + +func (ep *GoroutinePoolHandler) getEventsData(name string) *eventsProcessor { + ep.lock.Lock() + defer ep.lock.Unlock() + ed, ok := ep.aggregateEventsContainer[name] + if !ok { + ed = newEventsProcessor(name) + ep.aggregateEventsContainer[name] = ed + } + return ed +} + +func (ep *GoroutinePoolHandler) tryToDelete(name string) { + ep.lock.Lock() + defer ep.lock.Unlock() + ed, ok := ep.aggregateEventsContainer[name] + if ok { + ed.lock.Lock() + defer ed.lock.Unlock() + if !ed.isProcessed { + delete(ep.aggregateEventsContainer, name) + } + } +} + +type eventsProcessor struct { + name string + queue []Event + isProcessed bool + lock sync.Mutex +} + +func newEventsProcessor(name string) *eventsProcessor { + return &eventsProcessor{ + name: name, + queue: make([]Event, 0, 16), + } +} + +func (ed *eventsProcessor) push(events []Event) bool { + ed.lock.Lock() + defer ed.lock.Unlock() + ed.queue = append(ed.queue, events...) + if ed.isProcessed == false { + ed.isProcessed = true + return true + } + return false +} + +func (ed *eventsProcessor) pop() []Event { + ed.lock.Lock() + defer ed.lock.Unlock() + if len(ed.queue) > 0 { + res := ed.queue + ed.queue = make([]Event, 0, 16) + return res + } + ed.isProcessed = false + return nil +} + +func (ed *eventsProcessor) process(ctx context.Context, eh Handler) error { + for { + events := ed.pop() + if len(events) == 0 { + return nil + } + + i := iter{ + events: events, + } + + if err := eh.Handle(ctx, &i); err != nil { + ed.lock.Lock() + defer ed.lock.Unlock() + ed.isProcessed = false + return fmt.Errorf("cannot process event: %w", err) + } + } +} + +type iter struct { + events []Event + idx int +} + +func (i *iter) Next(ctx context.Context, e *Event) bool { + if i.idx >= len(i.events) { + return false + } + *e = i.events[i.idx] + i.idx++ + return true +} + +func (i *iter) Err() error { + return nil +} diff --git a/openapi-gateway/service/httpApi.go b/openapi-gateway/service/httpApi.go new file mode 100644 index 000000000..67a77c622 --- /dev/null +++ b/openapi-gateway/service/httpApi.go @@ -0,0 +1,246 @@ +package service + +import ( + "context" + "fmt" + "net/http" + "net/url" + "strings" + "time" + + "github.com/go-ocf/kit/codec/cbor" + "github.com/go-ocf/kit/codec/json" + + "github.com/go-ocf/go-coap" + "github.com/go-ocf/kit/log" + kitNetHttp "github.com/go-ocf/kit/net/http" + "github.com/go-ocf/cloud/openapi-connector/events" + "github.com/go-ocf/cloud/openapi-gateway/store" + "github.com/go-ocf/cloud/openapi-gateway/uri" + + raCqrs "github.com/go-ocf/cloud/resource-aggregate/cqrs/notification" + projectionRA "github.com/go-ocf/cloud/resource-aggregate/cqrs/projection" + router "github.com/gorilla/mux" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + pbDD "github.com/go-ocf/cloud/resource-directory/pb/device-directory" + pbRD "github.com/go-ocf/cloud/resource-directory/pb/resource-directory" + pbRS "github.com/go-ocf/cloud/resource-directory/pb/resource-shadow" +) + +const resourceLinkHrefKey = "resourceLinkHref" +const subscriptionIDKey = "subscriptionID" +const deviceIDKey = "deviceID" + +const ContentQuery = "content" +const ContentQueryBaseValue = "base" +const ContentQueryAllValue = "all" +const ContentQueryDefault = ContentQueryBaseValue + +type ListDevicesOfUserFunc func(ctx context.Context, correlationID, userID, accessToken string) (deviceIds []string, statusCode int, err error) + +//RequestHandler for handling incoming request +type RequestHandler struct { + resourceProjection *projectionRA.Projection + store store.Store + updateNotificationContainer *raCqrs.UpdateNotificationContainer + timeoutForRequests time.Duration + + asClient pbAS.AuthorizationServiceClient + raClient pbRA.ResourceAggregateClient + rsClient pbRS.ResourceShadowClient + rdClient pbRD.ResourceDirectoryClient + ddClient pbDD.DeviceDirectoryClient +} + +func logAndWriteErrorResponse(err error, statusCode int, w http.ResponseWriter) { + log.Errorf("%v", err) + w.Header().Set(events.ContentTypeKey, "text/plain") + w.WriteHeader(kitNetHttp.ErrToStatusWithDef(err, statusCode)) + w.Write([]byte(err.Error())) +} + +//NewRequestHandler factory for new RequestHandler +func NewRequestHandler( + asClient pbAS.AuthorizationServiceClient, + raClient pbRA.ResourceAggregateClient, + rsClient pbRS.ResourceShadowClient, + rdClient pbRD.ResourceDirectoryClient, + ddClient pbDD.DeviceDirectoryClient, + resourceProjection *projectionRA.Projection, + store store.Store, + updateNotificationContainer *raCqrs.UpdateNotificationContainer, + timeoutForRequests time.Duration, +) *RequestHandler { + return &RequestHandler{ + asClient: asClient, + raClient: raClient, + rsClient: rsClient, + rdClient: rdClient, + ddClient: ddClient, + resourceProjection: resourceProjection, + store: store, + updateNotificationContainer: updateNotificationContainer, + timeoutForRequests: timeoutForRequests, + } +} + +func getContentQueryValue(u *url.URL) string { + m, err := url.ParseQuery(u.RawQuery) + if err != nil { + return ContentQueryDefault + } + + c, _ := m[ContentQuery] + if len(c) != 1 { + return ContentQueryDefault + } + switch c[0] { + case ContentQueryAllValue: + return ContentQueryAllValue + case ContentQueryBaseValue: + return ContentQueryBaseValue + } + + return ContentQueryDefault +} + +const applicationMimeType = "application" + +func getResponseWriterEncoder(accept []string) (responseWriterEncoderFunc, error) { + if len(accept) == 0 { + return newCBORResponseWriterEncoder(coap.AppOcfCbor.String()), nil + } + var encode responseWriterEncoderFunc + for _, v := range accept { + switch v { + case coap.AppJSON.String(): + encode = jsonResponseWriterEncoder + case coap.AppCBOR.String(): + return newCBORResponseWriterEncoder(v), nil + case coap.AppOcfCbor.String(): + return newCBORResponseWriterEncoder(v), nil + case applicationMimeType + "/*": + return newCBORResponseWriterEncoder(coap.AppOcfCbor.String()), nil + } + } + if encode != nil { + return encode, nil + } + return nil, fmt.Errorf("invalid accept header(%v)", accept) +} + +func getEncoder(contentType string) (func(v interface{}) ([]byte, error), error) { + switch contentType { + case events.ContentType_JSON: + return json.Encode, nil + case events.ContentType_VNDOCFCBOR: + return cbor.Encode, nil + } + + return nil, fmt.Errorf("invalid %v header(%v)", events.ContentTypeKey, contentType) +} + +func makeResourceLinkHref(path []string) string { + return "/" + strings.Join(path, "/") +} + +func splitDevicePath(requestURI string) []string { + p := kitNetHttp.CanonicalHref(requestURI) + p = strings.TrimPrefix(p, uri.Devices) // remove core prefix + p = strings.TrimLeft(p, "/") + return strings.Split(p, "/") +} + +func loggingMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + log.Debugf("%v %v", r.Method, r.RequestURI) + // Call the next handler, which can be another middleware in the chain, or the final handler. + next.ServeHTTP(w, r) + }) +} + +func healthCheck(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) +} + +func resourceSubscriptionMatcher(r *http.Request, rm *router.RouteMatch) bool { + paths := splitDevicePath(r.RequestURI) + if len(paths) > 3 && paths[len(paths)-2] == "subscriptions" { + if rm.Vars == nil { + rm.Vars = make(map[string]string) + } + rm.Vars[deviceIDKey] = paths[0] + rm.Vars[resourceLinkHrefKey] = makeResourceLinkHref(paths[1 : len(paths)-2]) + rm.Vars[subscriptionIDKey] = paths[len(paths)-1] + return true + } + return false +} + +func resourceMatcher(r *http.Request, rm *router.RouteMatch) bool { + paths := splitDevicePath(r.RequestURI) + if len(paths) >= 2 && paths[len(paths)-1] != "subscriptions" { + if rm.Vars == nil { + rm.Vars = make(map[string]string) + } + rm.Vars[deviceIDKey] = paths[0] + rm.Vars[resourceLinkHrefKey] = makeResourceLinkHref(paths[1:]) + return true + } + return false +} + +// NewHTTP returns HTTP server +func NewHTTP(requestHandler *RequestHandler, authInterceptor kitNetHttp.Interceptor) *http.Server { + r := router.NewRouter() + r.StrictSlash(true) + r.Use(loggingMiddleware) + r.Use(kitNetHttp.CreateAuthMiddleware(authInterceptor, func(ctx 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) + })) + + // health check + r.HandleFunc("/", healthCheck).Methods("GET") + + s := r.PathPrefix(uri.Devices).Subrouter() + + // retrieve all devices + s.HandleFunc("", requestHandler.RetrieveDevices).Methods("GET") + + // devices subscription + s.HandleFunc("/subscriptions", requestHandler.SubscribeToDevices).Methods("POST") + s.HandleFunc("/subscriptions/{"+subscriptionIDKey+"}", requestHandler.RetrieveDevicesSubscription).Methods("GET") + s.HandleFunc("/subscriptions/{"+subscriptionIDKey+"}", requestHandler.UnsubscribeFromDevices).Methods("DELETE") + + // retrieve device + s1 := s.PathPrefix("/").Subrouter() + s1.HandleFunc("/{"+deviceIDKey+"}", requestHandler.RetrieveDevice).Methods("GET") + + // device subscription + s1.HandleFunc("/{"+deviceIDKey+"}/subscriptions", requestHandler.SubscribeToDevice).Methods("POST") + s1.HandleFunc("/{"+deviceIDKey+"}/subscriptions/{"+subscriptionIDKey+"}", requestHandler.RetrieveDeviceSubscription).Methods("GET") + s1.HandleFunc("/{"+deviceIDKey+"}/subscriptions/{"+subscriptionIDKey+"}", requestHandler.UnsubscribeFromDevice).Methods("DELETE") + + // resource subscription + s1.MatcherFunc(func(r *http.Request, rm *router.RouteMatch) bool { + paths := splitDevicePath(r.RequestURI) + if len(paths) > 2 && paths[len(paths)-1] == "subscriptions" { + if rm.Vars == nil { + rm.Vars = make(map[string]string) + } + rm.Vars[deviceIDKey] = paths[0] + rm.Vars[resourceLinkHrefKey] = makeResourceLinkHref(paths[1 : len(paths)-1]) + return true + } + return false + }).Methods("POST").HandlerFunc(requestHandler.SubscribeToResource) + s1.MatcherFunc(resourceSubscriptionMatcher).Methods("GET").HandlerFunc(requestHandler.RetrieveResourceSubscription) + s1.MatcherFunc(resourceSubscriptionMatcher).Methods("DELETE").HandlerFunc(requestHandler.UnsubscribeFromResource) + + // resource + s1.MatcherFunc(resourceMatcher).Methods("POST").HandlerFunc(requestHandler.UpdateResource) + s1.MatcherFunc(resourceMatcher).Methods("GET").HandlerFunc(requestHandler.RetrieveResource) + return &http.Server{Handler: r} +} diff --git a/openapi-gateway/service/jsonEncoder.go b/openapi-gateway/service/jsonEncoder.go new file mode 100644 index 000000000..ba7029d51 --- /dev/null +++ b/openapi-gateway/service/jsonEncoder.go @@ -0,0 +1,17 @@ +package service + +import ( + "net/http" + + "github.com/go-ocf/go-coap" + "github.com/go-ocf/kit/codec/json" + "github.com/go-ocf/cloud/openapi-connector/events" +) + +func jsonResponseWriterEncoder(w http.ResponseWriter, v interface{}) error { + if v == nil { + return nil + } + w.Header().Set(events.ContentTypeKey, coap.AppJSON.String()) + return json.WriteTo(w, v) +} diff --git a/openapi-gateway/service/resourceProjection.go b/openapi-gateway/service/resourceProjection.go new file mode 100644 index 000000000..60ab7ff4a --- /dev/null +++ b/openapi-gateway/service/resourceProjection.go @@ -0,0 +1,260 @@ +package service + +import ( + "context" + "fmt" + "sync" + + "github.com/go-ocf/kit/codec/cbor" + "github.com/go-ocf/kit/codec/json" + "github.com/go-ocf/cloud/openapi-connector/events" + + coap "github.com/go-ocf/go-coap" + "github.com/go-ocf/sdk/schema/cloud" + + "github.com/go-ocf/cqrs/event" + "github.com/go-ocf/cqrs/eventstore" + "github.com/go-ocf/kit/log" + "github.com/go-ocf/kit/net/http" + + raCqrs "github.com/go-ocf/cloud/resource-aggregate/cqrs" + raEvents "github.com/go-ocf/cloud/resource-aggregate/cqrs/events" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/notification" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +type resourceCtx struct { + lock sync.Mutex + resource *pbRA.Resource + isPublished bool + content *pbRA.ResourceChanged + + syncPoolHandler *GoroutinePoolHandler + updateNotificationContainer *notification.UpdateNotificationContainer +} + +func newResourceCtx(syncPoolHandler *GoroutinePoolHandler, updateNotificationContainer *notification.UpdateNotificationContainer) func(context.Context) (eventstore.Model, error) { + return func(context.Context) (eventstore.Model, error) { + return &resourceCtx{ + syncPoolHandler: syncPoolHandler, + updateNotificationContainer: updateNotificationContainer, + }, nil + } +} + +func (m *resourceCtx) cloneLocked() *resourceCtx { + return &resourceCtx{ + resource: m.resource, + isPublished: m.isPublished, + content: m.content, + } +} + +func (m *resourceCtx) Clone() *resourceCtx { + m.lock.Lock() + defer m.lock.Unlock() + + return m.cloneLocked() +} + +func (m *resourceCtx) onResourcePublishedLocked(ctx context.Context) error { + err := m.syncPoolHandler.Handle(ctx, Event{ + Id: m.resource.GetDeviceId(), + EventType: events.EventType_ResourcesPublished, + DeviceID: m.resource.GetDeviceId(), + Href: m.resource.GetHref(), + Representation: makeLinksRepresentation(events.EventType_ResourcesPublished, []eventstore.Model{m.cloneLocked()}), + }) + if err != nil { + err = fmt.Errorf("cannot make action on resource published: %w", err) + } + return err +} + +func (m *resourceCtx) onCloudStatusChangedLocked(ctx context.Context) error { + var decoder func(data []byte, v interface{}) error + switch m.content.GetContent().GetContentType() { + case coap.AppCBOR.String(), coap.AppOcfCbor.String(): + decoder = cbor.Decode + case coap.AppJSON.String(): + decoder = json.Decode + } + if decoder == nil { + return fmt.Errorf("decoder not found") + } + var cloudStatus cloud.Status + err := decoder(m.content.GetContent().GetData(), &cloudStatus) + if err != nil { + return err + } + eventType := events.EventType_DevicesOffline + if cloudStatus.Online { + eventType = events.EventType_DevicesOnline + } + + return m.syncPoolHandler.Handle(ctx, Event{ + Id: m.resource.GetDeviceId(), + EventType: eventType, + DeviceID: m.resource.GetDeviceId(), + Representation: makeOnlineOfflineRepresentation(m.resource.GetDeviceId()), + }) +} + +func (m *resourceCtx) onResourceUnpublishedLocked(ctx context.Context) error { + err := m.syncPoolHandler.Handle(ctx, Event{ + Id: m.resource.GetDeviceId(), + EventType: events.EventType_ResourcesUnpublished, + DeviceID: m.resource.GetDeviceId(), + Href: m.resource.GetHref(), + Representation: makeLinksRepresentation(events.EventType_ResourcesUnpublished, []eventstore.Model{m.cloneLocked()}), + }) + if err != nil { + err = fmt.Errorf("cannot make action on resource unpublished: %w", err) + } + return err +} + +func (m *resourceCtx) onResourceChangedLocked(ctx context.Context) error { + var rep interface{} + var err error + eventType := events.EventType_ResourceChanged + if m.content.GetStatus() != pbRA.Status_OK { + eventType = events.EventType_SubscriptionCanceled + } else { + rep, err = unmarshalContent(m.content.GetContent()) + if err != nil { + return fmt.Errorf("cannot make action on resource content changed: %w", err) + } + } + + err = m.syncPoolHandler.Handle(ctx, Event{ + Id: m.resource.GetDeviceId(), + EventType: eventType, + DeviceID: m.resource.GetDeviceId(), + Href: m.resource.GetHref(), + Representation: rep, + }) + if err != nil { + err = fmt.Errorf("cannot make action on resource content changed: %w", err) + } + return err +} + +func (m *resourceCtx) onProcessedContentUpdatesLocked(updateProcessed []raEvents.ResourceUpdated) { + for _, up := range updateProcessed { + notify := m.updateNotificationContainer.Find(up.AuditContext.CorrelationId) + if notify != nil { + select { + case notify <- up: + default: + log.Debugf("DeviceId: %v, ResourceId: %v: cannot send notification", m.resource.DeviceId, m.resource.Id) + } + } + } +} + +func (m *resourceCtx) SnapshotEventType() string { + s := &raEvents.ResourceStateSnapshotTaken{} + return s.SnapshotEventType() +} + +func (m *resourceCtx) Handle(ctx context.Context, iter event.Iter) error { + var eu event.EventUnmarshaler + var onResourcePublished, onResourceUnpublished, onResourceChanged bool + processedContentUpdates := make([]raEvents.ResourceUpdated, 0, 128) + m.lock.Lock() + defer m.lock.Unlock() + var anyEventProcessed bool + for iter.Next(ctx, &eu) { + anyEventProcessed = true + log.Debugf("resourceCtx.Handle: DeviceID: %v, ResourceId: %v, Version: %v, EventType: %v", eu.GroupId, eu.AggregateId, eu.Version, eu.EventType) + switch eu.EventType { + case http.ProtobufContentType(&pbRA.ResourceStateSnapshotTaken{}): + var s raEvents.ResourceStateSnapshotTaken + if err := eu.Unmarshal(&s); err != nil { + return err + } + if !m.isPublished { + onResourcePublished = s.IsPublished + onResourceUnpublished = !s.IsPublished + } + if m.content == nil { + onResourceChanged = true + } else { + onResourceChanged = s.GetLatestResourceChange().GetEventMetadata().GetVersion() > m.content.GetEventMetadata().GetVersion() + } + m.content = s.LatestResourceChange + m.resource = s.Resource + m.isPublished = s.IsPublished + case http.ProtobufContentType(&pbRA.ResourcePublished{}): + var s raEvents.ResourcePublished + if err := eu.Unmarshal(&s); err != nil { + return err + } + if !m.isPublished { + onResourcePublished = true + onResourceUnpublished = false + } + m.isPublished = true + m.resource = s.Resource + case http.ProtobufContentType(&pbRA.ResourceUnpublished{}): + if m.isPublished { + onResourcePublished = false + onResourceUnpublished = true + } + m.isPublished = false + case http.ProtobufContentType(&pbRA.ResourceChanged{}): + var s raEvents.ResourceChanged + if err := eu.Unmarshal(&s); err != nil { + return err + } + if m.content == nil { + onResourceChanged = true + } else { + onResourceChanged = s.GetEventMetadata().GetVersion() > m.content.GetEventMetadata().GetVersion() + } + m.content = &s.ResourceChanged + case http.ProtobufContentType(&pbRA.ResourceUpdated{}): + var s raEvents.ResourceUpdated + if err := eu.Unmarshal(&s); err != nil { + return err + } + processedContentUpdates = append(processedContentUpdates, s) + } + } + + if !anyEventProcessed { + // if event event not processed, it means that the projection will be reloaded. + return nil + } + + if m.resource == nil { + return fmt.Errorf("DeviceID: %v, ResourceId: %v: invalid resource is stored in eventstore: Resource attribute is not set", eu.GroupId, eu.AggregateId) + } + + if onResourcePublished { + if err := m.onResourcePublishedLocked(ctx); err != nil { + log.Errorf("%v", err) + } + } else if onResourceUnpublished { + if err := m.onResourceUnpublishedLocked(ctx); err != nil { + log.Errorf("%v", err) + } + } + + if onResourceChanged && m.isPublished { + if raCqrs.MakeResourceId(m.resource.GetDeviceId(), cloud.StatusHref) == m.resource.Id { + if err := m.onCloudStatusChangedLocked(ctx); err != nil { + log.Errorf("cannot make action on cloud status changed: %v", err) + } + } + + if err := m.onResourceChangedLocked(ctx); err != nil { + log.Errorf("%v", err) + } + } + + m.onProcessedContentUpdatesLocked(processedContentUpdates) + + return nil +} diff --git a/openapi-gateway/service/retrieveAllDevicesSubscription.go b/openapi-gateway/service/retrieveAllDevicesSubscription.go new file mode 100644 index 000000000..a5dc346c8 --- /dev/null +++ b/openapi-gateway/service/retrieveAllDevicesSubscription.go @@ -0,0 +1,56 @@ +package service + +import ( + "context" + "fmt" + "net/http" + + "github.com/go-ocf/cloud/openapi-gateway/store" + + "github.com/gorilla/mux" +) + +type retrieveDevicesSubscriptionHandler struct { + s store.DevicesSubscription +} + +func (c *retrieveDevicesSubscriptionHandler) Handle(ctx context.Context, iter store.DevicesSubscriptionIter) error { + for iter.Next(ctx, &c.s) { + return nil + } + return fmt.Errorf("not found") +} + +func (rh *RequestHandler) retrieveDevicesSubscription(w http.ResponseWriter, r *http.Request) (int, error) { + routeVars := mux.Vars(r) + subscriptionID := routeVars[subscriptionIDKey] + userDevices, err := rh.GetUsersDevices(r.Context(), r) + if err != nil { + return http.StatusUnauthorized, err + } + if len(userDevices) == 0 { + return http.StatusForbidden, fmt.Errorf("cannot get user devices: empty") + } + + res := retrieveDevicesSubscriptionHandler{} + err = rh.store.LoadDevicesSubscriptions(r.Context(), store.DevicesSubscriptionQuery{SubscriptionID: subscriptionID}, &res) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot load subscription %v: %w", subscriptionID, err) + } + + err = jsonResponseWriterEncoder(w, SubscriptionResponse{ + SubscriptionID: subscriptionID, + }) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot write response: %w", err) + } + + return http.StatusOK, nil +} + +func (rh *RequestHandler) RetrieveDevicesSubscription(w http.ResponseWriter, r *http.Request) { + statusCode, err := rh.retrieveDevicesSubscription(w, r) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot retrieve all devices subscription: %w", err), statusCode, w) + } +} diff --git a/openapi-gateway/service/retrieveDevice.go b/openapi-gateway/service/retrieveDevice.go new file mode 100644 index 000000000..48cecd16a --- /dev/null +++ b/openapi-gateway/service/retrieveDevice.go @@ -0,0 +1,259 @@ +package service + +import ( + "context" + "fmt" + "io" + "net/http" + + "github.com/go-ocf/kit/codec/cbor" + "github.com/go-ocf/kit/codec/json" + "github.com/go-ocf/kit/log" + kitNetHttp "github.com/go-ocf/kit/net/http" + "github.com/go-ocf/sdk/schema" + + coap "github.com/go-ocf/go-coap" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRD "github.com/go-ocf/cloud/resource-directory/pb/resource-directory" + pbRS "github.com/go-ocf/cloud/resource-directory/pb/resource-shadow" +) + +func toEndpoint(s *pbRA.EndpointInformation) schema.Endpoint { + return schema.Endpoint{ + URI: s.GetEndpoint(), + Priority: uint64(s.GetPriority()), + } +} + +func toEndpoints(s []*pbRA.EndpointInformation) []schema.Endpoint { + r := make([]schema.Endpoint, 0, 16) + for _, v := range s { + r = append(r, toEndpoint(v)) + } + return r +} + +func toPolicy(s *pbRA.Policies) schema.Policy { + return schema.Policy{ + BitMask: schema.BitMask(s.GetBitFlags()), + } +} + +type RetrieveDeviceWithLinksResponse struct { + Device + Links []schema.ResourceLink `json:"links"` +} + +func getHref(deviceID, href string) string { + return "/" + deviceID + href +} + +func makeResourceLink(resource *pbRA.Resource) schema.ResourceLink { + return schema.ResourceLink{ + Href: getHref(resource.GetDeviceId(), resource.GetHref()), + ResourceTypes: resource.GetResourceTypes(), + Interfaces: resource.GetInterfaces(), + DeviceID: resource.GetDeviceId(), + InstanceID: resource.GetInstanceId(), + Anchor: resource.GetAnchor(), + Policy: toPolicy(resource.GetPolicies()), + Title: resource.GetTitle(), + SupportedContentTypes: resource.GetSupportedContentTypes(), + Endpoints: toEndpoints(resource.GetEndpointInformations()), + } +} + +func (rh *RequestHandler) GetResourceLinks(ctx context.Context, deviceIdsFilter []string, authorizationContext pbCQRS.AuthorizationContext) (map[string]schema.ResourceLinks, error) { + client, err := rh.rdClient.GetResourceLinks(ctx, &pbRD.GetResourceLinksRequest{ + DeviceIdsFilter: deviceIdsFilter, + AuthorizationContext: &authorizationContext, + }) + + if err != nil { + return nil, fmt.Errorf("cannot get resource links: %w", err) + } + defer client.CloseSend() + + resourceLinks := make(map[string]schema.ResourceLinks) + for { + resourceLink, err := client.Recv() + if err == io.EOF { + break + } + if err != nil { + return nil, fmt.Errorf("cannot get resource links: %w", err) + } + _, ok := resourceLinks[resourceLink.GetResource().GetDeviceId()] + if !ok { + resourceLinks[resourceLink.GetResource().GetDeviceId()] = make(schema.ResourceLinks, 0, 32) + } + resourceLinks[resourceLink.GetResource().GetDeviceId()] = append(resourceLinks[resourceLink.GetResource().GetDeviceId()], makeResourceLink(resourceLink.GetResource())) + } + if len(resourceLinks) == 0 { + return nil, fmt.Errorf("cannot get resource links: not found") + } + return resourceLinks, nil +} + +type Representation struct { + Href string `json:"href"` + Representation interface{} `json:"rep"` + Status pbRA.Status `json:"-"` +} + +type RetrieveDeviceAllResponse struct { + Device + Links []Representation `json:"links"` +} + +func normalizeContentType(c *pbRA.Content) string { + if c.GetContentType() != "" { + return c.GetContentType() + } + switch coap.MediaType(c.GetCoapContentFormat()) { + case coap.AppCBOR: + return coap.AppCBOR.String() + case coap.AppOcfCbor: + return coap.AppOcfCbor.String() + case coap.AppJSON: + return coap.AppJSON.String() + case coap.TextPlain: + return coap.TextPlain.String() + } + return "" +} + +func unmarshalContent(c *pbRA.Content) (interface{}, error) { + var m interface{} + switch normalizeContentType(c) { + case coap.AppCBOR.String(), coap.AppOcfCbor.String(): + err := cbor.Decode(c.GetData(), &m) + if err != nil { + return nil, fmt.Errorf("cannot unmarshal resource content: %w", err) + } + case coap.AppJSON.String(): + err := json.Decode(c.GetData(), &m) + if err != nil { + return nil, fmt.Errorf("cannot unmarshal resource content: %w", err) + } + case coap.TextPlain.String(): + m = string(c.Data) + default: + if c.CoapContentFormat == -1 { + return c.Data, nil + } + return nil, fmt.Errorf("cannot unmarshal resource content: unknown content type (%v/%v)", c.ContentType, c.CoapContentFormat) + } + return m, nil +} + +func (rh *RequestHandler) RetrieveResourcesValues(ctx context.Context, resourceIdsFilter []string, deviceIdsFilter []string, authorizationContext pbCQRS.AuthorizationContext) (map[string][]Representation, error) { + client, err := rh.rsClient.RetrieveResourcesValues(ctx, &pbRS.RetrieveResourcesValuesRequest{ + DeviceIdsFilter: deviceIdsFilter, + ResourceIdsFilter: resourceIdsFilter, + AuthorizationContext: &authorizationContext, + }) + + if err != nil { + return nil, fmt.Errorf("cannot retrieve resources values: %w", err) + } + defer client.CloseSend() + + allResources := make(map[string][]Representation) + for { + content, err := client.Recv() + if err == io.EOF { + break + } + if err != nil { + return nil, fmt.Errorf("cannot retrieve resources values: %w", err) + } + rep, err := unmarshalContent(content.GetContent()) + if err != nil { + log.Errorf("cannot retrieve resources values: %v", err) + continue + } + + _, ok := allResources[content.GetDeviceId()] + if !ok { + allResources[content.GetDeviceId()] = make([]Representation, 0, 32) + } + allResources[content.GetDeviceId()] = append(allResources[content.GetDeviceId()], Representation{ + Href: getHref(content.GetDeviceId(), content.GetHref()), + Representation: rep, + Status: content.Status, + }) + + } + if len(allResources) == 0 { + return nil, fmt.Errorf("cannot retrieve resources values: not found") + } + return allResources, nil +} + +func (rh *RequestHandler) RetrieveDeviceWithLinks(ctx context.Context, w http.ResponseWriter, deviceID string, encoder responseWriterEncoderFunc) (int, error) { + devices, err := rh.GetDevices(ctx, []string{deviceID}, pbCQRS.AuthorizationContext{}) + if err != nil { + return kitNetHttp.ErrToStatusWithDef(err, http.StatusForbidden), fmt.Errorf("cannot retrieve device(%v) [base]: %w", deviceID, err) + } + resourceLink, err := rh.GetResourceLinks(ctx, []string{deviceID}, pbCQRS.AuthorizationContext{}) + if err != nil { + return kitNetHttp.ErrToStatusWithDef(err, http.StatusForbidden), fmt.Errorf("cannot retrieve device(%v) [base]: %w", deviceID, err) + } + + resp := RetrieveDeviceWithLinksResponse{ + Device: devices[0], + Links: resourceLink[deviceID], + } + + err = encoder(w, resp) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot retrieve devices(%v) [base]: %w", deviceID, err) + } + return http.StatusOK, nil +} + +type RetrieveDeviceContentAllResponse struct { + Device + Links []Representation `json:"links"` +} + +func (rh *RequestHandler) RetrieveDeviceWithRepresentations(ctx context.Context, w http.ResponseWriter, deviceID string, encoder responseWriterEncoderFunc) (int, error) { + devices, err := rh.GetDevices(ctx, []string{deviceID}, pbCQRS.AuthorizationContext{}) + if err != nil { + return kitNetHttp.ErrToStatusWithDef(err, http.StatusForbidden), fmt.Errorf("cannot retrieve device(%v) [base]: %w", deviceID, err) + } + allResources, err := rh.RetrieveResourcesValues(ctx, nil, []string{deviceID}, pbCQRS.AuthorizationContext{}) + if err != nil { + return kitNetHttp.ErrToStatusWithDef(err, http.StatusForbidden), fmt.Errorf("cannot retrieve device(%v) [all]: %w", deviceID, err) + } + + resp := RetrieveDeviceContentAllResponse{ + Device: devices[0], + Links: allResources[deviceID], + } + + err = encoder(w, resp) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot retrieve devices(%v) [all]: %w", deviceID, err) + } + return http.StatusOK, nil +} + +func (rh *RequestHandler) RetrieveDeviceWithContentQuery(ctx context.Context, w http.ResponseWriter, routeVars map[string]string, contentQuery string, encoder responseWriterEncoderFunc) (int, error) { + switch contentQuery { + case ContentQueryBaseValue: + return rh.RetrieveDeviceWithLinks(ctx, w, routeVars[deviceIDKey], encoder) + case ContentQueryAllValue: + return rh.RetrieveDeviceWithRepresentations(ctx, w, routeVars[deviceIDKey], encoder) + } + return http.StatusBadRequest, fmt.Errorf("invalid content query parameter") +} + +func (rh *RequestHandler) RetrieveDevice(w http.ResponseWriter, r *http.Request) { + statusCode, err := retrieveWithCallback(w, r, rh.RetrieveDeviceWithContentQuery) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot retrieve device: %w", err), statusCode, w) + } +} diff --git a/openapi-gateway/service/retrieveDeviceSubscription.go b/openapi-gateway/service/retrieveDeviceSubscription.go new file mode 100644 index 000000000..11e1ade2a --- /dev/null +++ b/openapi-gateway/service/retrieveDeviceSubscription.go @@ -0,0 +1,54 @@ +package service + +import ( + "context" + "fmt" + "net/http" + + "github.com/go-ocf/cloud/openapi-gateway/store" + + "github.com/gorilla/mux" +) + +type retrieveDeviceSubscriptionHandler struct { + s store.Subscription +} + +func (c *retrieveDeviceSubscriptionHandler) Handle(ctx context.Context, iter store.SubscriptionIter) error { + for iter.Next(ctx, &c.s) { + return nil + } + return fmt.Errorf("not found") +} + +func (rh *RequestHandler) retrieveDeviceSubscription(w http.ResponseWriter, r *http.Request) (int, error) { + routeVars := mux.Vars(r) + deviceID := routeVars[deviceIDKey] + subscriptionID := routeVars[subscriptionIDKey] + err := rh.IsAuthorized(r.Context(), r, deviceID) + if err != nil { + return http.StatusUnauthorized, err + } + + res := retrieveDeviceSubscriptionHandler{} + err = rh.store.LoadSubscriptions(r.Context(), store.SubscriptionQuery{SubscriptionID: subscriptionID}, &res) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot load subscription %v: %w", subscriptionID, err) + } + + err = jsonResponseWriterEncoder(w, SubscriptionResponse{ + SubscriptionID: subscriptionID, + }) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot write response: %w", err) + } + + return http.StatusOK, nil +} + +func (rh *RequestHandler) RetrieveDeviceSubscription(w http.ResponseWriter, r *http.Request) { + statusCode, err := rh.retrieveDeviceSubscription(w, r) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot retrieve device subscription: %w", err), statusCode, w) + } +} diff --git a/openapi-gateway/service/retrieveDevices.go b/openapi-gateway/service/retrieveDevices.go new file mode 100644 index 000000000..906a8ca85 --- /dev/null +++ b/openapi-gateway/service/retrieveDevices.go @@ -0,0 +1,199 @@ +package service + +import ( + "context" + "fmt" + "io" + "net/http" + "strings" + + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + kitNetHttp "github.com/go-ocf/kit/net/http" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbDD "github.com/go-ocf/cloud/resource-directory/pb/device-directory" + "github.com/go-ocf/sdk/schema" + "github.com/gorilla/mux" +) + +type Status string + +const Status_ONLINE Status = "online" +const Status_OFFLINE Status = "offline" + +func toStatus(isOnline bool) Status { + if isOnline { + return "online" + } + return "offline" +} + +func toLocalizedString(s *pbDD.LocalizedString) schema.LocalizedString { + return schema.LocalizedString{ + Value: s.Value, + Language: s.Language, + } +} + +func toLocalizedStrings(s []*pbDD.LocalizedString) []schema.LocalizedString { + r := make([]schema.LocalizedString, 0, 16) + for _, v := range s { + r = append(r, toLocalizedString(v)) + } + return r +} + +type responseWriterEncoderFunc func(w http.ResponseWriter, v interface{}) error + +type Device struct { + Device schema.Device `codec:"device"` + Status Status `codec:"status"` +} + +func (rh *RequestHandler) GetDevices(ctx context.Context, deviceIdsFilter []string, authorizationContext pbCQRS.AuthorizationContext) ([]Device, error) { + getDevicesClient, err := rh.ddClient.GetDevices(ctx, &pbDD.GetDevicesRequest{ + DeviceIdsFilter: deviceIdsFilter, + AuthorizationContext: &authorizationContext, + }) + + if err != nil { + return nil, fmt.Errorf("cannot get devices: %w", err) + } + defer getDevicesClient.CloseSend() + + devices := make([]Device, 0, 32) + for { + device, err := getDevicesClient.Recv() + if err == io.EOF { + break + } + if err != nil { + return nil, fmt.Errorf("cannot get devices: %w", err) + } + + devices = append(devices, Device{ + Device: schema.Device{ + ID: device.GetId(), + ResourceTypes: device.GetResource().GetResourceTypes(), + Name: device.GetResource().GetName(), + ModelNumber: device.GetResource().GetModelNumber(), + ManufacturerName: toLocalizedStrings(device.GetResource().GetManufacturerName()), + }, + Status: toStatus(device.IsOnline), + }) + } + if len(devices) == 0 { + return nil, fmt.Errorf("cannot get devices: not found") + } + return devices, nil +} + +func (rh *RequestHandler) RetrieveDevicesBase(ctx context.Context, w http.ResponseWriter, encoder responseWriterEncoderFunc) (int, error) { + devices, err := rh.GetDevices(ctx, nil, pbCQRS.AuthorizationContext{}) + if err != nil { + return kitNetHttp.ErrToStatusWithDef(err, http.StatusForbidden), fmt.Errorf("cannot retrieve all devices[base]: %w", err) + } + resourceLink, err := rh.GetResourceLinks(ctx, nil, pbCQRS.AuthorizationContext{}) + if err != nil { + return kitNetHttp.ErrToStatusWithDef(err, http.StatusForbidden), fmt.Errorf("cannot retrieve all devices[base]: %w", err) + } + + resp := make([]RetrieveDeviceWithLinksResponse, 0, 32) + for _, dev := range devices { + links, ok := resourceLink[dev.Device.ID] + if ok { + resp = append(resp, RetrieveDeviceWithLinksResponse{ + Device: dev, + Links: links, + }) + } + } + + err = encoder(w, resp) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot retrieve all devices[base]: %w", err) + } + return http.StatusOK, nil +} + +func (rh *RequestHandler) RetrieveDevicesAll(ctx context.Context, w http.ResponseWriter, encoder responseWriterEncoderFunc) (int, error) { + devices, err := rh.GetDevices(ctx, nil, pbCQRS.AuthorizationContext{}) + if err != nil { + return kitNetHttp.ErrToStatusWithDef(err, http.StatusForbidden), fmt.Errorf("cannot retrieve all devices[base]: %w", err) + } + reps, err := rh.RetrieveResourcesValues(ctx, nil, nil, pbCQRS.AuthorizationContext{}) + if err != nil { + return kitNetHttp.ErrToStatusWithDef(err, http.StatusForbidden), fmt.Errorf("cannot retrieve all devices[base]: %w", err) + } + + resp := make([]RetrieveDeviceContentAllResponse, 0, 32) + for _, dev := range devices { + devReps, ok := reps[dev.Device.ID] + if ok { + resp = append(resp, RetrieveDeviceContentAllResponse{ + Device: dev, + Links: devReps, + }) + } + } + + err = encoder(w, resp) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot retrieve all devices[base]: %w", err) + } + return http.StatusOK, nil +} + +func (rh *RequestHandler) RetrieveDevicesWithContentQuery(ctx context.Context, w http.ResponseWriter, routeVars map[string]string, contentQuery string, encoder responseWriterEncoderFunc) (statusCode int, err error) { + switch contentQuery { + case ContentQueryAllValue: + statusCode, err = rh.RetrieveDevicesAll(ctx, w, encoder) + case ContentQueryBaseValue: + statusCode, err = rh.RetrieveDevicesBase(ctx, w, encoder) + default: + return http.StatusBadRequest, fmt.Errorf("invalid value '%v' of '%v' query parameter", contentQuery, ContentQuery) + } + if err != nil { + statusCode = kitNetHttp.ErrToStatusWithDef(err, statusCode) + if statusCode == http.StatusNotFound { + // return's empty array + errEnc := encoder(w, []interface{}{}) + if errEnc == nil { + return http.StatusOK, nil + } + } + } + return statusCode, err + +} + +type callbackFunc func(ctx context.Context, w http.ResponseWriter, routeVars map[string]string, contentQuery string, encoder responseWriterEncoderFunc) (int, error) + +func getAccessToken(r *http.Request) (string, error) { + token, _, err := parseAuth(r.Header.Get("Authorization")) + if err != nil { + return "", fmt.Errorf("cannot retrieve: %w", err) + } + + return token, nil +} + +func retrieveWithCallback(w http.ResponseWriter, r *http.Request, callback callbackFunc) (int, error) { + accessToken, err := getAccessToken(r) + if err != nil { + return http.StatusUnauthorized, err + } + + encoder, err := getResponseWriterEncoder(strings.Split(r.Header.Get("Accept"), ",")) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot retrieve: %w", err) + } + + return callback(kitNetGrpc.CtxWithToken(r.Context(), accessToken), w, mux.Vars(r), getContentQueryValue(r.URL), encoder) +} + +func (rh *RequestHandler) RetrieveDevices(w http.ResponseWriter, r *http.Request) { + statusCode, err := retrieveWithCallback(w, r, rh.RetrieveDevicesWithContentQuery) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot retrieve all devices: %w", err), statusCode, w) + } +} diff --git a/openapi-gateway/service/retrieveResource.go b/openapi-gateway/service/retrieveResource.go new file mode 100644 index 000000000..24d23d902 --- /dev/null +++ b/openapi-gateway/service/retrieveResource.go @@ -0,0 +1,53 @@ +package service + +import ( + "context" + "fmt" + "net/http" + + kitNetHttp "github.com/go-ocf/kit/net/http" + cqrsRA "github.com/go-ocf/cloud/resource-aggregate/cqrs" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +func (rh *RequestHandler) RetrieveResourceBase(ctx context.Context, w http.ResponseWriter, resourceID string, encoder responseWriterEncoderFunc) (int, error) { + allResources, err := rh.RetrieveResourcesValues(ctx, []string{resourceID}, nil, pbCQRS.AuthorizationContext{}) + if err != nil { + return kitNetHttp.ErrToStatusWithDef(err, http.StatusForbidden), fmt.Errorf("cannot retrieve resource(%v) [all]: %w", resourceID, err) + } + + for _, v := range allResources { + if v[0].Status != pbCQRS.Status_OK { + return statusToHttpStatus(v[0].Status), fmt.Errorf("cannot retrieve resource(%v) [all]: device returns code %v", resourceID, v[0].Status) + } + + err = encoder(w, v[0].Representation) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot retrieve resource(%v) [all]: %w", resourceID, err) + } + return http.StatusOK, nil + } + return http.StatusNotFound, fmt.Errorf("cannot retrieve resource(%v) [all]: %w", resourceID, err) +} + +func (rh *RequestHandler) RetrieveResourceWithContentQuery(ctx context.Context, w http.ResponseWriter, routeVars map[string]string, contentQuery string, encoder responseWriterEncoderFunc) (int, error) { + switch contentQuery { + case ContentQueryBaseValue: + deviceID := routeVars[deviceIDKey] + resourceID := routeVars[resourceLinkHrefKey] + code, err := rh.RetrieveResourceBase(ctx, w, cqrsRA.MakeResourceId(deviceID, resourceID), encoder) + if err != nil { + err = fmt.Errorf("cannot retrieve resource(deviceID: %v, Href: %v): %w", deviceID, resourceID, err) + } + return code, err + + } + return http.StatusBadRequest, fmt.Errorf("invalid content query parameter") +} + +func (rh *RequestHandler) RetrieveResource(w http.ResponseWriter, r *http.Request) { + statusCode, err := retrieveWithCallback(w, r, rh.RetrieveResourceWithContentQuery) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot retrieve resource: %w", err), statusCode, w) + } +} diff --git a/openapi-gateway/service/retrieveResourceSubscription.go b/openapi-gateway/service/retrieveResourceSubscription.go new file mode 100644 index 000000000..c4f79e14b --- /dev/null +++ b/openapi-gateway/service/retrieveResourceSubscription.go @@ -0,0 +1,54 @@ +package service + +import ( + "context" + "fmt" + "net/http" + + "github.com/go-ocf/cloud/openapi-gateway/store" + + "github.com/gorilla/mux" +) + +type retrieveResourceSubscriptionHandler struct { + s store.Subscription +} + +func (c *retrieveResourceSubscriptionHandler) Handle(ctx context.Context, iter store.SubscriptionIter) error { + for iter.Next(ctx, &c.s) { + return nil + } + return fmt.Errorf("not found") +} + +func (rh *RequestHandler) retrieveResourceSubscription(w http.ResponseWriter, r *http.Request) (int, error) { + routeVars := mux.Vars(r) + deviceID := routeVars[deviceIDKey] + subscriptionID := routeVars[subscriptionIDKey] + err := rh.IsAuthorized(r.Context(), r, deviceID) + if err != nil { + return http.StatusUnauthorized, err + } + + res := retrieveResourceSubscriptionHandler{} + err = rh.store.LoadSubscriptions(r.Context(), store.SubscriptionQuery{SubscriptionID: subscriptionID}, &res) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot load subscription %v: %w", subscriptionID, err) + } + + err = jsonResponseWriterEncoder(w, SubscriptionResponse{ + SubscriptionID: subscriptionID, + }) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot write response: %w", err) + } + + return http.StatusOK, nil +} + +func (rh *RequestHandler) RetrieveResourceSubscription(w http.ResponseWriter, r *http.Request) { + statusCode, err := rh.retrieveResourceSubscription(w, r) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot retrieve resource subscription: %w", err), statusCode, w) + } +} diff --git a/openapi-gateway/service/service.go b/openapi-gateway/service/service.go new file mode 100644 index 000000000..4159399bb --- /dev/null +++ b/openapi-gateway/service/service.go @@ -0,0 +1,177 @@ +package service + +import ( + "context" + "crypto/tls" + "net" + "net/http" + "time" + + "github.com/go-ocf/cqrs/eventbus" + cqrsEventStore "github.com/go-ocf/cqrs/eventstore" + "github.com/go-ocf/kit/log" + + oapiStore "github.com/go-ocf/cloud/openapi-connector/store" + "github.com/go-ocf/cloud/openapi-gateway/store" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + kitNetHttp "github.com/go-ocf/kit/net/http" + raCqrs "github.com/go-ocf/cloud/resource-aggregate/cqrs/notification" + projectionRA "github.com/go-ocf/cloud/resource-aggregate/cqrs/projection" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + pbDD "github.com/go-ocf/cloud/resource-directory/pb/device-directory" + pbRD "github.com/go-ocf/cloud/resource-directory/pb/resource-directory" + pbRS "github.com/go-ocf/cloud/resource-directory/pb/resource-shadow" +) + +//Server handle HTTP request +type Server struct { + server *http.Server + cfg Config + handler *RequestHandler + ln net.Listener + devicesSubscription *devicesSubscription +} + +type ResourceSubscriptionLoader struct { + projection *projectionRA.Projection +} + +func newResourceSubscriptionLoader(projection *projectionRA.Projection) *ResourceSubscriptionLoader { + return &ResourceSubscriptionLoader{projection: projection} +} + +func (l *ResourceSubscriptionLoader) Handle(ctx context.Context, iter store.SubscriptionIter) error { + var s store.Subscription + for iter.Next(ctx, &s) { + _, err := l.projection.Register(ctx, s.DeviceID) + if err != nil { + log.Errorf("cannot register to resource projection for resource subscription %v: %v", s.ID, err) + } + } + return iter.Err() +} + +type DeviceSubscriptionLoader struct { + resourceProjection *projectionRA.Projection +} + +func newDeviceSubscriptionLoader(resourceProjection *projectionRA.Projection) *DeviceSubscriptionLoader { + return &DeviceSubscriptionLoader{ + resourceProjection: resourceProjection, + } +} + +func (l *DeviceSubscriptionLoader) Handle(ctx context.Context, iter store.SubscriptionIter) error { + var s store.Subscription + for iter.Next(ctx, &s) { + _, err := l.resourceProjection.Register(ctx, s.DeviceID) + if err != nil { + log.Errorf("cannot register to resource projection for device subscription %v: %v", s.ID, err) + } + } + return iter.Err() +} + +type DialCertManager = interface { + GetClientTLSConfig() tls.Config +} + +type ListenCertManager = interface { + GetServerTLSConfig() tls.Config +} + +//New create new Server with provided store and bus +func New( + config Config, + dialCertManager DialCertManager, + listenCertManager ListenCertManager, + authInterceptor kitNetHttp.Interceptor, + resourceEventStore cqrsEventStore.EventStore, + resourceSubscriber eventbus.Subscriber, + subscriptionStore store.Store, + goroutinePoolGo GoroutinePoolGoFunc, +) *Server { + dialTLSConfig := dialCertManager.GetClientTLSConfig() + listenTLSConfig := listenCertManager.GetServerTLSConfig() + listenTLSConfig.ClientAuth = tls.NoClientCert + + ln, err := tls.Listen("tcp", config.Addr, &listenTLSConfig) + if err != nil { + log.Fatalf("cannot listen and serve: %v", err) + } + + raConn, err := grpc.Dial(config.ResourceAggregateAddr, grpc.WithTransportCredentials(credentials.NewTLS(&dialTLSConfig))) + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + raClient := pbRA.NewResourceAggregateClient(raConn) + + rdConn, err := grpc.Dial(config.ResourceDirectoryAddr, grpc.WithTransportCredentials(credentials.NewTLS(&dialTLSConfig))) + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + rsClient := pbRS.NewResourceShadowClient(rdConn) + rdClient := pbRD.NewResourceDirectoryClient(rdConn) + ddClient := pbDD.NewDeviceDirectoryClient(rdConn) + + asConn, err := grpc.Dial(config.AuthServerAddr, grpc.WithTransportCredentials(credentials.NewTLS(&dialTLSConfig))) + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + asClient := pbAS.NewAuthorizationServiceClient(asConn) + + if config.DevicesCheckInterval < time.Millisecond*200 { + log.Fatalf("cannot create server: invalid config.DevicesCheckInterval %v", config.DevicesCheckInterval) + } + + ctx := context.Background() + + syncPoolHandler := NewGoroutinePoolHandler(goroutinePoolGo, newEventHandler(subscriptionStore, goroutinePoolGo), func(err error) { log.Errorf("%v", err) }) + updateNotificationContainer := raCqrs.NewUpdateNotificationContainer() + + resourceProjection, err := projectionRA.NewProjection(ctx, config.FQDN, resourceEventStore, resourceSubscriber, newResourceCtx(syncPoolHandler, updateNotificationContainer)) + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + + // load subscriptions to projection + err = subscriptionStore.LoadSubscriptions(ctx, store.SubscriptionQuery{Type: oapiStore.Type_Resource}, newResourceSubscriptionLoader(resourceProjection)) + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + err = subscriptionStore.LoadSubscriptions(ctx, store.SubscriptionQuery{Type: oapiStore.Type_Device}, newDeviceSubscriptionLoader(resourceProjection)) + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + + requestHandler := NewRequestHandler(asClient, raClient, rsClient, rdClient, ddClient, resourceProjection, subscriptionStore, updateNotificationContainer, config.TimeoutForRequests) + + devicesSubscription := newDevicesSubscription(requestHandler, goroutinePoolGo) + + server := Server{ + server: NewHTTP(requestHandler, authInterceptor), + cfg: config, + handler: requestHandler, + ln: ln, + devicesSubscription: devicesSubscription, + } + + return &server +} + +// Serve starts the service's HTTP server and blocks. +func (s *Server) Serve() error { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go s.devicesSubscription.Serve(ctx, s.cfg.DevicesCheckInterval) + + return s.server.Serve(s.ln) +} + +// Shutdown ends serving +func (s *Server) Shutdown() error { + return s.server.Shutdown(context.Background()) +} diff --git a/openapi-gateway/service/subscribeToDevice.go b/openapi-gateway/service/subscribeToDevice.go new file mode 100644 index 000000000..4755d9dde --- /dev/null +++ b/openapi-gateway/service/subscribeToDevice.go @@ -0,0 +1,97 @@ +package service + +import ( + "fmt" + "net/http" + + "github.com/go-ocf/cloud/openapi-connector/events" + oapiStore "github.com/go-ocf/cloud/openapi-connector/store" + + "github.com/gorilla/mux" +) + +func makeOnlineOfflineRepresentation(deviceID string) interface{} { + return []map[string]string{map[string]string{"di": deviceID}} +} + +func (rh *RequestHandler) subscribeToDevice(w http.ResponseWriter, r *http.Request) (int, error) { + routeVars := mux.Vars(r) + deviceID := routeVars[deviceIDKey] + + err := rh.IsAuthorized(r.Context(), r, deviceID) + if err != nil { + return http.StatusUnauthorized, err + } + + _, userID, err := parseAuth(r.Header.Get("Authorization")) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot parse authorization header: %w", err) + } + + s, code, err := rh.makeSubscription(w, r, oapiStore.Type_Device, userID, []events.EventType{ + events.EventType_ResourcesPublished, + events.EventType_ResourcesUnpublished, + }) + if err != nil { + return code, err + } + s.DeviceID = deviceID + + _, err = rh.resourceProjection.Register(r.Context(), deviceID) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot register to resource projection: %w", err) + } + models := rh.resourceProjection.Models(deviceID, "") + if len(models) == 0 { + err = rh.resourceProjection.ForceUpdate(r.Context(), deviceID, "") + if err != nil { + rh.resourceProjection.Unregister(deviceID) + return http.StatusBadRequest, fmt.Errorf("cannot load resources for device: %w", err) + } + } + + err = rh.store.SaveSubscription(r.Context(), s) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot save subscription: %w", err) + } + + models = rh.resourceProjection.Models(deviceID, "") + if len(models) == 0 { + rh.resourceProjection.Unregister(deviceID) + rh.store.PopSubscription(r.Context(), s.ID) + return http.StatusBadRequest, fmt.Errorf("cannot load resources for device and device: %w", err) + } + + for _, eventType := range s.EventTypes { + var rep interface{} + switch eventType { + case events.EventType_ResourcesPublished, events.EventType_ResourcesUnpublished: + rep = makeLinksRepresentation(eventType, models) + } + + _, err = emitEvent(r.Context(), eventType, s, rh.store.IncrementSubscriptionSequenceNumber, rep) + if err != nil { + rh.resourceProjection.Unregister(deviceID) + rh.store.PopSubscription(r.Context(), s.ID) + return http.StatusBadRequest, fmt.Errorf("cannot emit event: %w", err) + } + } + + err = jsonResponseWriterEncoder(w, SubscriptionResponse{ + SubscriptionID: s.ID, + }) + if err != nil { + rh.resourceProjection.Unregister(deviceID) + rh.store.PopSubscription(r.Context(), s.ID) + return http.StatusBadRequest, fmt.Errorf("cannot write response: %w", err) + } + + return http.StatusOK, nil +} + +func (rh *RequestHandler) SubscribeToDevice(w http.ResponseWriter, r *http.Request) { + statusCode, err := rh.subscribeToDevice(w, r) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot subscribe to device: %w", err), statusCode, w) + } +} diff --git a/openapi-gateway/service/subscribeToDevices.go b/openapi-gateway/service/subscribeToDevices.go new file mode 100644 index 000000000..737309150 --- /dev/null +++ b/openapi-gateway/service/subscribeToDevices.go @@ -0,0 +1,92 @@ +package service + +import ( + "context" + "fmt" + "io" + "net/http" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/cloud/openapi-connector/events" + oapiStore "github.com/go-ocf/cloud/openapi-connector/store" + "github.com/go-ocf/cloud/openapi-gateway/store" + "google.golang.org/grpc/status" +) + +func (rh *RequestHandler) GetUsersDevices(ctx context.Context, r *http.Request) ([]string, error) { + token, err := getAccessToken(r) + if err != nil { + return nil, fmt.Errorf("cannot get users devices: %w", err) + } + + client, err := rh.asClient.GetUserDevices(kitNetGrpc.CtxWithToken(ctx, token), &pbAS.GetUserDevicesRequest{}) + if err != nil { + return nil, status.Errorf(status.Convert(err).Code(), "cannot get users devices: %v", err) + } + defer client.CloseSend() + userDevices := make([]string, 0, 32) + for { + userDevice, err := client.Recv() + if err == io.EOF { + break + } + if err != nil { + return nil, status.Errorf(status.Convert(err).Code(), "cannot get users devices: %v", err) + } + userDevices = append(userDevices, userDevice.GetDeviceId()) + } + return userDevices, nil +} + +func (rh *RequestHandler) subscribeToDevices(w http.ResponseWriter, r *http.Request) (int, error) { + userDevices, err := rh.GetUsersDevices(r.Context(), r) + if err != nil { + return http.StatusUnauthorized, err + } + if len(userDevices) == 0 { + return http.StatusForbidden, fmt.Errorf("cannot get user devices: empty") + } + + token, userID, err := parseAuth(r.Header.Get("Authorization")) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot parse authorization header: %w", err) + } + + s, code, err := rh.makeSubscription(w, r, oapiStore.Type_Devices, userID, []events.EventType{ + events.EventType_DevicesRegistered, + events.EventType_DevicesUnregistered, + events.EventType_DevicesOnline, + events.EventType_DevicesOffline, + }) + if err != nil { + return code, err + } + + subscription := store.DevicesSubscription{ + Subscription: s, + AccessToken: token, + } + + err = rh.store.SaveDevicesSubscription(r.Context(), subscription) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot save subscription: %w", err) + } + + err = jsonResponseWriterEncoder(w, SubscriptionResponse{ + SubscriptionID: subscription.ID, + }) + if err != nil { + rh.store.PopSubscription(r.Context(), subscription.ID) + return http.StatusBadRequest, fmt.Errorf("cannot write response: %w", err) + } + + return http.StatusOK, nil +} + +func (rh *RequestHandler) SubscribeToDevices(w http.ResponseWriter, r *http.Request) { + statusCode, err := rh.subscribeToDevices(w, r) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot subscribe to all devices: %w", err), statusCode, w) + } +} diff --git a/openapi-gateway/service/subscribeToResouce.go b/openapi-gateway/service/subscribeToResouce.go new file mode 100644 index 000000000..6f302ae45 --- /dev/null +++ b/openapi-gateway/service/subscribeToResouce.go @@ -0,0 +1,174 @@ +package service + +import ( + "context" + "fmt" + "io" + "net/http" + "net/url" + + "github.com/go-ocf/kit/codec/json" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/cloud/openapi-connector/events" + oapiStore "github.com/go-ocf/cloud/openapi-connector/store" + "github.com/go-ocf/cloud/openapi-gateway/store" + "github.com/gofrs/uuid" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + cqrsRA "github.com/go-ocf/cloud/resource-aggregate/cqrs" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + "github.com/gorilla/mux" +) + +func (rh *RequestHandler) IsAuthorized(ctx context.Context, r *http.Request, deviceID string) error { + token, err := getAccessToken(r) + if err != nil { + return fmt.Errorf("cannot authorized: cannot get users devices: %w", err) + } + + getUserDevicesClient, err := rh.asClient.GetUserDevices(kitNetGrpc.CtxWithToken(ctx, token), &pbAS.GetUserDevicesRequest{ + DeviceIdsFilter: []string{deviceID}, + }) + if err != nil { + return fmt.Errorf("cannot authorized: cannot get users devices: %w", err) + } + defer getUserDevicesClient.CloseSend() + for { + userDevice, err := getUserDevicesClient.Recv() + if err == io.EOF { + break + } + if err != nil { + return fmt.Errorf("cannot authorized: cannot get users devices: %w", err) + } + if userDevice.DeviceId == deviceID { + return nil + } + } + return fmt.Errorf("cannot authorized: access denied") +} + +type SubscriptionResponse struct { + SubscriptionID string `json:"subscriptionId"` +} + +func (rh *RequestHandler) makeSubscription(w http.ResponseWriter, r *http.Request, typ oapiStore.Type, userID string, validEventTypes []events.EventType) (store.Subscription, int, error) { + var res store.Subscription + var req events.SubscriptionRequest + err := json.ReadFrom(r.Body, &req) + if err != nil { + return res, http.StatusBadRequest, fmt.Errorf("cannot decode request body: %w", err) + } + + _, err = url.Parse(req.URL) + if err != nil { + return res, http.StatusBadRequest, fmt.Errorf("invalid eventsurl(%v)", err) + } + + eventTypes := make([]events.EventType, 0, 10) + for _, r := range req.EventTypes { + ev := events.EventType(r) + for _, v := range validEventTypes { + if ev == v { + eventTypes = append(eventTypes, ev) + } + } + } + if len(eventTypes) == 0 { + return res, http.StatusBadRequest, fmt.Errorf("invalid eventtypes(%v)", err) + } + res.ID = uuid.Must(uuid.NewV4()).String() + res.EventTypes = eventTypes + res.URL = req.URL + res.CorrelationID = r.Header.Get(events.CorrelationIDKey) + res.ContentType = r.Header.Get(events.ContentTypeKey) + res.UserID = userID + res.SigningSecret = req.SigningSecret + res.Type = typ + + return res, http.StatusOK, nil + +} + +func (rh *RequestHandler) subscribeToResource(w http.ResponseWriter, r *http.Request) (int, error) { + routeVars := mux.Vars(r) + deviceID := routeVars[deviceIDKey] + href := routeVars[resourceLinkHrefKey] + + err := rh.IsAuthorized(r.Context(), r, deviceID) + if err != nil { + return http.StatusUnauthorized, err + } + + _, userID, err := parseAuth(r.Header.Get("Authorization")) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot parse authorization header: %w", err) + } + + s, code, err := rh.makeSubscription(w, r, oapiStore.Type_Resource, userID, []events.EventType{events.EventType_ResourceChanged}) + if err != nil { + return code, err + } + + _, err = rh.resourceProjection.Register(r.Context(), deviceID) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot register to resource projection: %w", err) + } + resourceID := cqrsRA.MakeResourceId(deviceID, href) + models := rh.resourceProjection.Models(deviceID, resourceID) + if len(models) == 0 { + err = rh.resourceProjection.ForceUpdate(r.Context(), deviceID, href) + if err != nil { + rh.resourceProjection.Unregister(deviceID) + return http.StatusBadRequest, fmt.Errorf("cannot load resource: %w", err) + } + } + + s.DeviceID = deviceID + s.Href = href + + err = rh.store.SaveSubscription(r.Context(), s) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot save subscription: %w", err) + } + + models = rh.resourceProjection.Models(deviceID, resourceID) + for _, m := range models { + resourceCtx := m.(*resourceCtx).Clone() + if resourceCtx.content.GetStatus() != pbRA.Status_OK { + rh.store.PopSubscription(r.Context(), s.ID) + rh.resourceProjection.Unregister(deviceID) + return statusToHttpStatus(resourceCtx.content.GetStatus()), fmt.Errorf("cannot prepare content to emit first event: %w", err) + } + rep, err := unmarshalContent(resourceCtx.content.GetContent()) + if err != nil { + rh.store.PopSubscription(r.Context(), s.ID) + rh.resourceProjection.Unregister(deviceID) + return http.StatusBadRequest, fmt.Errorf("cannot prepare content to emit first event: %w", err) + } + _, err = emitEvent(r.Context(), events.EventType_ResourceChanged, s, rh.store.IncrementSubscriptionSequenceNumber, rep) + if err != nil { + rh.store.PopSubscription(r.Context(), s.ID) + rh.resourceProjection.Unregister(deviceID) + return http.StatusBadRequest, fmt.Errorf("cannot emit event: %w", err) + } + } + + err = jsonResponseWriterEncoder(w, SubscriptionResponse{ + SubscriptionID: s.ID, + }) + if err != nil { + rh.store.PopSubscription(r.Context(), s.ID) + rh.resourceProjection.Unregister(deviceID) + return http.StatusBadRequest, fmt.Errorf("cannot write response: %w", err) + } + + return http.StatusOK, nil +} + +func (rh *RequestHandler) SubscribeToResource(w http.ResponseWriter, r *http.Request) { + statusCode, err := rh.subscribeToResource(w, r) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot subscribe to resource: %w", err), statusCode, w) + } +} diff --git a/openapi-gateway/service/unsubscribeFromDevice.go b/openapi-gateway/service/unsubscribeFromDevice.go new file mode 100644 index 000000000..87e804c37 --- /dev/null +++ b/openapi-gateway/service/unsubscribeFromDevice.go @@ -0,0 +1,51 @@ +package service + +import ( + "context" + "fmt" + "net/http" + + "github.com/go-ocf/kit/log" + "github.com/go-ocf/cloud/openapi-connector/events" + "github.com/gorilla/mux" +) + +func (rh *RequestHandler) unsubscribeFromDevice(w http.ResponseWriter, r *http.Request) (int, error) { + routeVars := mux.Vars(r) + deviceID := routeVars[deviceIDKey] + subscriptionID := routeVars[subscriptionIDKey] + err := rh.IsAuthorized(r.Context(), r, deviceID) + if err != nil { + return http.StatusUnauthorized, err + } + + sub, err := rh.store.PopSubscription(r.Context(), subscriptionID) + if err != nil { + return http.StatusBadRequest, err + } + + err = rh.resourceProjection.Unregister(deviceID) + if err != nil { + log.Errorf("cannot unregister resource projection for %v: %v", deviceID, err) + } + + _, err = emitEvent(r.Context(), events.EventType_SubscriptionCanceled, sub, func(ctx context.Context, subscriptionID string) (uint64, error) { + return sub.SequenceNumber, nil + }, nil) + if err != nil { + log.Errorf("cannot emit event: %v", err) + } + + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot write response: %w", err) + } + + return http.StatusOK, nil +} + +func (rh *RequestHandler) UnsubscribeFromDevice(w http.ResponseWriter, r *http.Request) { + statusCode, err := rh.unsubscribeFromDevice(w, r) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot unsubscribe from device: %w", err), statusCode, w) + } +} diff --git a/openapi-gateway/service/unsubscribeFromDevices.go b/openapi-gateway/service/unsubscribeFromDevices.go new file mode 100644 index 000000000..e4efd1e1f --- /dev/null +++ b/openapi-gateway/service/unsubscribeFromDevices.go @@ -0,0 +1,48 @@ +package service + +import ( + "context" + "fmt" + "net/http" + + "github.com/go-ocf/kit/log" + "github.com/go-ocf/cloud/openapi-connector/events" + "github.com/gorilla/mux" +) + +func (rh *RequestHandler) unsubscribeFromDevices(w http.ResponseWriter, r *http.Request) (int, error) { + routeVars := mux.Vars(r) + subscriptionID := routeVars[subscriptionIDKey] + userDevices, err := rh.GetUsersDevices(r.Context(), r) + if err != nil { + return http.StatusUnauthorized, err + } + if len(userDevices) == 0 { + return http.StatusForbidden, fmt.Errorf("cannot get user devices: empty") + } + + sub, err := rh.store.PopDevicesSubscription(r.Context(), subscriptionID) + if err != nil { + return http.StatusBadRequest, err + } + + _, err = emitEvent(r.Context(), events.EventType_SubscriptionCanceled, sub.Subscription, func(ctx context.Context, subscriptionID string) (uint64, error) { + return sub.SequenceNumber, nil + }, nil) + if err != nil { + log.Errorf("cannot emit event: %v", err) + } + + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot write response: %w", err) + } + + return http.StatusOK, nil +} + +func (rh *RequestHandler) UnsubscribeFromDevices(w http.ResponseWriter, r *http.Request) { + statusCode, err := rh.unsubscribeFromDevices(w, r) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot unsubscribe from all devices: %w", err), statusCode, w) + } +} diff --git a/openapi-gateway/service/unsubscribeFromResource.go b/openapi-gateway/service/unsubscribeFromResource.go new file mode 100644 index 000000000..2083e3c9c --- /dev/null +++ b/openapi-gateway/service/unsubscribeFromResource.go @@ -0,0 +1,51 @@ +package service + +import ( + "context" + "fmt" + "net/http" + + "github.com/go-ocf/kit/log" + "github.com/go-ocf/cloud/openapi-connector/events" + "github.com/gorilla/mux" +) + +func (rh *RequestHandler) unsubscribeFromResource(w http.ResponseWriter, r *http.Request) (int, error) { + routeVars := mux.Vars(r) + deviceID := routeVars[deviceIDKey] + subscriptionID := routeVars[subscriptionIDKey] + err := rh.IsAuthorized(r.Context(), r, deviceID) + if err != nil { + return http.StatusUnauthorized, err + } + + sub, err := rh.store.PopSubscription(r.Context(), subscriptionID) + if err != nil { + return http.StatusBadRequest, err + } + + err = rh.resourceProjection.Unregister(deviceID) + if err != nil { + log.Errorf("cannot unregister resource projection for %v: %v", deviceID, err) + } + + _, err = emitEvent(r.Context(), events.EventType_SubscriptionCanceled, sub, func(ctx context.Context, subscriptionID string) (uint64, error) { + return sub.SequenceNumber, nil + }, nil) + if err != nil { + log.Errorf("cannot emit event: %v", err) + } + + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot write response: %w", err) + } + + return http.StatusOK, nil +} + +func (rh *RequestHandler) UnsubscribeFromResource(w http.ResponseWriter, r *http.Request) { + statusCode, err := rh.unsubscribeFromResource(w, r) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot unsubscribe from resource: %w", err), statusCode, w) + } +} diff --git a/openapi-gateway/service/updateResource.go b/openapi-gateway/service/updateResource.go new file mode 100644 index 000000000..45bf8e9e0 --- /dev/null +++ b/openapi-gateway/service/updateResource.go @@ -0,0 +1,182 @@ +package service + +import ( + "bytes" + "context" + "fmt" + "net/http" + "sync/atomic" + + "github.com/go-ocf/go-coap" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/cloud/openapi-connector/events" + "github.com/gofrs/uuid" + "github.com/gorilla/mux" + + cqrsRA "github.com/go-ocf/cloud/resource-aggregate/cqrs" + raEvents "github.com/go-ocf/cloud/resource-aggregate/cqrs/events" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +const checkAgain = http.StatusPreconditionRequired + +func getCoapContentFormat(contentType string) int32 { + switch contentType { + case coap.AppJSON.String(): + return int32(coap.AppJSON) + case coap.AppCBOR.String(): + return int32(coap.AppCBOR) + case coap.AppOcfCbor.String(): + return int32(coap.AppOcfCbor) + } + + return -1 +} + +var seqNumber uint64 + +func (rh *RequestHandler) onFirstTimeout(ctx context.Context, w http.ResponseWriter, deviceID, resourceID string) (int, error) { + if err := rh.resourceProjection.ForceUpdate(ctx, deviceID, resourceID); err != nil { + return http.StatusInternalServerError, fmt.Errorf("cannot get response for update resource %v.%v: %w", deviceID, resourceID, err) + } + return checkAgain, nil +} + +func (rh *RequestHandler) onSecondTimeout(ctx context.Context, w http.ResponseWriter, deviceID, resourceID string) (int, error) { + // timeout again it means update will be processed in future + w.WriteHeader(http.StatusAccepted) + return http.StatusAccepted, nil +} + +func statusToHttpStatus(status pbRA.Status) int { + switch status { + case pbRA.Status_UNKNOWN: + return http.StatusBadRequest + case pbRA.Status_OK: + return http.StatusOK + case pbRA.Status_BAD_REQUEST: + return http.StatusBadRequest + case pbRA.Status_UNAUTHORIZED: + return http.StatusUnauthorized + case pbRA.Status_FORBIDDEN: + return http.StatusForbidden + case pbRA.Status_NOT_FOUND: + return http.StatusNotFound + case pbRA.Status_UNAVAILABLE: + return http.StatusServiceUnavailable + case pbRA.Status_NOT_IMPLEMENTED: + return http.StatusNotImplemented + case pbRA.Status_ACCEPTED: + return http.StatusAccepted + } + return http.StatusInternalServerError +} + +func clientUpdateSendResponse(w http.ResponseWriter, deviceID, resourceID string, processed raEvents.ResourceUpdated) (int, error) { + statusCode := statusToHttpStatus(processed.Status) + + if processed.Content != nil { + content, err := unmarshalContent(processed.Content) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot make action on resource content changed: %w", err), statusCode, w) + return statusCode, nil + } + switch v := content.(type) { + case string: + w.WriteHeader(statusCode) + w.Write([]byte(v)) + return statusCode, nil + case []byte: + w.WriteHeader(statusCode) + w.Write(v) + return statusCode, nil + default: + w.WriteHeader(statusCode) + err = jsonResponseWriterEncoder(w, content) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot make action on resource content changed: %w", err), statusCode, w) + return statusCode, nil + } + return statusCode, nil + } + } + return statusCode, nil +} + +func (rh *RequestHandler) waitForUpdateContentResponse(ctx context.Context, w http.ResponseWriter, deviceID, resourceID string, notify <-chan raEvents.ResourceUpdated, onTimeout func(ctx context.Context, w http.ResponseWriter, destDeviceId, resourceId string) (int, error)) (int, error) { + timeoutCtx, cancel := context.WithTimeout(ctx, rh.timeoutForRequests) + defer cancel() + select { + case processed := <-notify: + return clientUpdateSendResponse(w, deviceID, resourceID, processed) + case <-timeoutCtx.Done(): + return onTimeout(ctx, w, deviceID, resourceID) + } +} + +func (rh *RequestHandler) updateResourceContent(w http.ResponseWriter, r *http.Request) (int, error) { + token, err := getAccessToken(r) + if err != nil { + return http.StatusUnauthorized, fmt.Errorf("cannot get access token: %w", err) + } + + contentType := r.Header.Get(events.ContentTypeKey) + coapContentFormat := getCoapContentFormat(contentType) + + routeVars := mux.Vars(r) + deviceID := routeVars[deviceIDKey] + resourceID := cqrsRA.MakeResourceId(deviceID, routeVars[resourceLinkHrefKey]) + correlationIdUUID, err := uuid.NewV4() + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("cannot create correlationId for update resource %v.%v: %w", deviceID, resourceID, err) + } + correlationId := correlationIdUUID.String() + + buffer := bytes.NewBuffer(make([]byte, 0, 1024)) + _, err = buffer.ReadFrom(r.Body) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot read body: %w", err) + } + + notify := rh.updateNotificationContainer.Add(correlationId) + defer rh.updateNotificationContainer.Remove(correlationId) + + ctx := context.Background() + + _, err = rh.resourceProjection.Register(ctx, deviceID) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("DeviceId %v: cannot regiter device to projection for update resource: %w", deviceID, err) + } + defer rh.resourceProjection.Unregister(deviceID) + + _, err = rh.raClient.UpdateResource(kitNetGrpc.CtxWithToken(r.Context(), token), &pbRA.UpdateResourceRequest{ + ResourceId: resourceID, + Content: &pbRA.Content{ + CoapContentFormat: coapContentFormat, + ContentType: contentType, + Data: buffer.Bytes(), + }, + CommandMetadata: &pbCQRS.CommandMetadata{ + ConnectionId: r.RemoteAddr, + Sequence: atomic.AddUint64(&seqNumber, 1), + }, + CorrelationId: correlationId, + }) + if err != nil { + return http.StatusBadRequest, fmt.Errorf("cannot update resource content: %w", err) + } + + statusCode, err := rh.waitForUpdateContentResponse(ctx, w, deviceID, resourceID, notify, rh.onFirstTimeout) + if statusCode == checkAgain && err == nil { + statusCode, err = rh.waitForUpdateContentResponse(ctx, w, deviceID, resourceID, notify, rh.onSecondTimeout) + } + return statusCode, err +} + +func (rh *RequestHandler) UpdateResource(w http.ResponseWriter, r *http.Request) { + statusCode, err := rh.updateResourceContent(w, r) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot update resource: %w", err), statusCode, w) + } +} diff --git a/openapi-gateway/store/mongodb/devicesSubscription.go b/openapi-gateway/store/mongodb/devicesSubscription.go new file mode 100644 index 000000000..a18bae463 --- /dev/null +++ b/openapi-gateway/store/mongodb/devicesSubscription.go @@ -0,0 +1,234 @@ +package mongodb + +import ( + "context" + "fmt" + "time" + + "github.com/go-ocf/cloud/openapi-connector/events" + + oapiConStore "github.com/go-ocf/cloud/openapi-connector/store" + + "github.com/go-ocf/cloud/openapi-gateway/store" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +const lastCheckKey = "lastcheck" +const lastDevicesRegisteredKey = "lastdevicesregistered" +const lastDevicesOnlineKey = "lastdevicesonline" +const lastDevicesOfflineKey = "lastdevicesoffline" + +var devicesLastCheckKeySubscriptionQueryIndex = bson.D{ + {typeKey, 1}, + {lastCheckKey, 1}, +} + +type dbDevicesSub struct { + ID string `bson:"_id"` + URL string + CorrelationID string // uuid + Type oapiConStore.Type + ContentType string + EventTypes []events.EventType + DeviceID string `bson:deviceIDKey` + Href string `bson:hrefKey` + SequenceNumber uint64 `bson:sequenceNumberKey` + UserID string `bson:userIDKey` + SigningSecret string + AccessToken string + LastDevicesRegistered []string `bson:lastDevicesRegisteredKey` + LastDevicesOnline []string `bson:lastDevicesOnlineKey` + LastDevicesOffline []string `bson:lastDevicesOfflineKey` + LastCheck int64 `bson:lastCheckKey` +} + +func toStringArray(devices []events.Device) []string { + d := make([]string, 0, len(devices)) + for _, v := range devices { + d = append(d, v.ID) + } + return d +} + +func toDeviceArray(devices []string) []events.Device { + d := make([]events.Device, 0, len(devices)) + for _, v := range devices { + d = append(d, events.Device{ + ID: v, + }) + } + return d +} + +func makeDBDevicesSub(sub store.DevicesSubscription) dbDevicesSub { + return dbDevicesSub{ + ID: sub.ID, + URL: sub.URL, + CorrelationID: sub.CorrelationID, + Type: sub.Type, + ContentType: sub.ContentType, + EventTypes: sub.EventTypes, + DeviceID: sub.DeviceID, + Href: sub.Href, + SequenceNumber: sub.SequenceNumber, + UserID: sub.UserID, + SigningSecret: sub.SigningSecret, + AccessToken: sub.AccessToken, + LastDevicesRegistered: toStringArray([]events.Device(sub.LastDevicesRegistered)), + LastDevicesOnline: toStringArray([]events.Device(sub.LastDevicesOnline)), + LastDevicesOffline: toStringArray([]events.Device(sub.LastDevicesOffline)), + LastCheck: sub.LastCheck.Unix(), + } +} + +func (s *Store) SaveDevicesSubscription(ctx context.Context, sub store.DevicesSubscription) error { + if sub.ID == "" { + return fmt.Errorf("invalid ID") + } + if len(sub.EventTypes) == 0 { + return fmt.Errorf("invalid EventTypes") + } + if sub.URL == "" { + return fmt.Errorf("invalid URL") + } + if sub.SigningSecret == "" { + return fmt.Errorf("invalid SigningSecret") + } + if sub.UserID == "" { + return fmt.Errorf("invalid UserID") + } + + DBSub := makeDBDevicesSub(sub) + col := s.client.Database(s.DBName()).Collection(subscriptionsCName) + + if _, err := col.InsertOne(ctx, DBSub); err != nil { + return fmt.Errorf("cannot save devices subscription: %w", err) + } + return nil +} + +func (s *Store) LoadDevicesSubscriptions(ctx context.Context, query store.DevicesSubscriptionQuery, h store.DevicesSubscriptionHandler) error { + var iter *mongo.Cursor + var err error + col := s.client.Database(s.DBName()).Collection(subscriptionsCName) + + switch { + case query.SubscriptionID != "": + iter, err = col.Find(ctx, bson.M{"_id": query.SubscriptionID}) + case !query.LastCheck.IsZero(): + q := bson.M{ + typeKey: oapiConStore.Type_Devices, + lastCheckKey: bson.M{ + "$lt": query.LastCheck.Unix(), + }, + } + iter, err = col.Find(ctx, q, &options.FindOptions{ + Hint: devicesLastCheckKeySubscriptionQueryIndex, + }) + if err == mongo.ErrNilDocument { + return nil + } + _, errUpd := col.UpdateMany(ctx, q, bson.M{ + "$set": bson.M{ + lastCheckKey: time.Now().Unix(), + }, + }) + if errUpd != nil { + iter.Close(ctx) + return fmt.Errorf("cannot load all devices subscription - update last check: %w", errUpd) + } + default: + iter, err = col.Find(ctx, bson.M{}) + } + if err == mongo.ErrNilDocument { + return nil + } + if err != nil { + return fmt.Errorf("cannot load all devices subscription: %w", err) + } + + i := devicesSubscriptionIterator{ + iter: iter, + } + err = h.Handle(ctx, &i) + + errClose := iter.Close(ctx) + if err == nil { + return errClose + } + return err +} + +func (s *Store) PopDevicesSubscription(ctx context.Context, subscriptionID string) (sub store.DevicesSubscription, err error) { + var DBSub dbDevicesSub + res := s.client.Database(s.DBName()).Collection(subscriptionsCName).FindOneAndDelete(ctx, bson.M{"_id": subscriptionID}) + if res.Err() != nil { + return sub, res.Err() + } + err = res.Decode(&DBSub) + if err != nil { + return sub, err + } + return convertToDevicesSubscription(DBSub), nil +} + +func (s *Store) UpdateDevicesSubscription(ctx context.Context, subscriptionID string, lastDevicesRegistered events.DevicesRegistered, lastDevicesOnline events.DevicesOnline, lastDevicesOffline events.DevicesOffline) error { + col := s.client.Database(s.DBName()).Collection(subscriptionsCName) + + _, err := col.UpdateOne(ctx, bson.M{"_id": subscriptionID}, bson.M{ + "$set": bson.M{ + lastDevicesRegisteredKey: toStringArray([]events.Device(lastDevicesRegistered)), + lastDevicesOnlineKey: toStringArray([]events.Device(lastDevicesOnline)), + lastDevicesOfflineKey: toStringArray([]events.Device(lastDevicesOffline)), + }, + }) + if err != nil { + return fmt.Errorf("cannot update last devices hash of %v: %w", subscriptionID, err) + } + return err +} + +type devicesSubscriptionIterator struct { + iter *mongo.Cursor +} + +func convertToDevicesSubscription(sub dbDevicesSub) (s store.DevicesSubscription) { + s.ID = sub.ID + s.URL = sub.URL + s.CorrelationID = sub.CorrelationID + s.Type = sub.Type + s.ContentType = sub.ContentType + s.EventTypes = sub.EventTypes + s.DeviceID = sub.DeviceID + s.Href = sub.Href + s.SequenceNumber = sub.SequenceNumber + s.UserID = sub.UserID + s.SigningSecret = sub.SigningSecret + s.AccessToken = sub.AccessToken + s.LastDevicesRegistered = toDeviceArray(sub.LastDevicesRegistered) + s.LastDevicesOnline = toDeviceArray(sub.LastDevicesOnline) + s.LastDevicesOffline = toDeviceArray(sub.LastDevicesOffline) + s.LastCheck = time.Unix(sub.LastCheck, 0) + return +} + +func (i *devicesSubscriptionIterator) Next(ctx context.Context, s *store.DevicesSubscription) bool { + var sub dbDevicesSub + + if !i.iter.Next(ctx) { + return false + } + + err := i.iter.Decode(&sub) + if err != nil { + return false + } + *s = convertToDevicesSubscription(sub) + return true +} + +func (i *devicesSubscriptionIterator) Err() error { + return i.iter.Err() +} diff --git a/openapi-gateway/store/mongodb/store.go b/openapi-gateway/store/mongodb/store.go new file mode 100644 index 000000000..6f7fa4c16 --- /dev/null +++ b/openapi-gateway/store/mongodb/store.go @@ -0,0 +1,151 @@ +package mongodb + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "strings" + "time" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/mongo/readpref" +) + +// Store implements an Store for MongoDB. +type Store struct { + client *mongo.Client + dbPrefix string +} + +type Config struct { + Host string `envconfig:"SUBSTORE_MONGO_HOST" default:"localhost:27017"` + DatabaseName string `envconfig:"SUBSTORE_MONGO_DATABASE" default:"openapiGateway"` + tlsCfg *tls.Config +} + +// Option provides the means to use function call chaining +type Option func(Config) Config + +// WithTLS configures connection to use TLS +func WithTLS(cfg *tls.Config) Option { + return func(c Config) Config { + c.tlsCfg = cfg + return c + } +} + +// NewStore creates a new Store. +func NewStore(ctx context.Context, cfg Config, opts ...Option) (*Store, error) { + for _, o := range opts { + cfg = o(cfg) + } + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://"+cfg.Host).SetTLSConfig(cfg.tlsCfg)) + if err != nil { + return nil, fmt.Errorf("could not dial database: %w", err) + } + err = client.Ping(ctx, readpref.Primary()) + if err != nil { + return nil, fmt.Errorf("could not dial database: %w", err) + } + + return NewStoreWithSession(ctx, client, cfg.DatabaseName) +} + +// NewStoreWithSession creates a new Store with a session. +func NewStoreWithSession(ctx context.Context, client *mongo.Client, dbPrefix string) (*Store, error) { + if client == nil { + return nil, errors.New("no database session") + } + + if dbPrefix == "" { + dbPrefix = "default" + } + + s := &Store{ + client: client, + dbPrefix: dbPrefix, + } + + subCol := s.client.Database(s.DBName()).Collection(subscriptionsCName) + err := ensureIndex(ctx, subCol, typeQueryIndex, typeDeviceIDQueryIndex, typeResourceIDQueryIndex, devicesLastCheckKeySubscriptionQueryIndex) + if err != nil { + client.Disconnect(ctx) + return nil, fmt.Errorf("cannot ensure index for device subscription: %w", err) + } + + return s, nil +} + +func ensureIndex(ctx context.Context, col *mongo.Collection, indexes ...bson.D) error { + for _, keys := range indexes { + opts := &options.IndexOptions{} + opts.SetBackground(false) + index := mongo.IndexModel{ + Keys: keys, + Options: opts, + } + _, err := col.Indexes().CreateOne(ctx, index) + if err != nil { + if strings.HasPrefix(err.Error(), "(IndexKeySpecsConflict)") { + //index already exist, just skip error and continue + continue + } + return fmt.Errorf("cannot ensure indexes for eventstore: %w", err) + } + } + return nil +} + +// DBName returns db name +func (s *Store) DBName() string { + ns := "db" + return s.dbPrefix + "_" + ns +} + +// Clear clears the event storage. +func (s *Store) Clear(ctx context.Context) error { + var errors []error + if err := s.client.Database(s.DBName()).Collection(subscriptionsCName).Drop(ctx); err != nil { + errors = append(errors, err) + } + if len(errors) > 0 { + return fmt.Errorf("cannot clear: %w", errors) + } + + return 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") + } + + var res bson.M + opts := &options.FindOneAndUpdateOptions{} + result := col.FindOneAndUpdate(ctx, bson.M{"_id": subscriptionId}, bson.M{"$inc": bson.M{sequenceNumberKey: 1}}, opts.SetReturnDocument(options.After)) + if result.Err() != nil { + return 0, fmt.Errorf("cannot increment sequence number for %v: %w", subscriptionId, result.Err()) + } + + err := result.Decode(&res) + if err != nil { + return 0, fmt.Errorf("cannot increment sequence number for %v: %w", subscriptionId, err) + } + + value, ok := res[sequenceNumberKey] + if !ok { + return 0, fmt.Errorf("cannot increment sequence number for %v: '%v' not found", subscriptionId, sequenceNumberKey) + } + + return uint64(value.(int64)) - 1, nil +} + +// Close closes the database session. +func (s *Store) Close(ctx context.Context) error { + return s.client.Disconnect(ctx) +} diff --git a/openapi-gateway/store/mongodb/subscription.go b/openapi-gateway/store/mongodb/subscription.go new file mode 100644 index 000000000..a7686b3db --- /dev/null +++ b/openapi-gateway/store/mongodb/subscription.go @@ -0,0 +1,225 @@ +package mongodb + +import ( + "context" + "fmt" + + "github.com/go-ocf/cloud/openapi-connector/events" + oapiConStore "github.com/go-ocf/cloud/openapi-connector/store" + "github.com/go-ocf/cloud/openapi-gateway/store" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + + "go.mongodb.org/mongo-driver/bson" +) + +const subscriptionsCName = "subscriptions" +const typeKey = "type" +const hrefKey = "href" +const sequenceNumberKey = "sequencenumber" +const deviceIDKey = "deviceid" +const userIDKey = "userid" + +var typeQueryIndex = bson.D{ + {typeKey, 1}, +} + +var typeDeviceIDQueryIndex = bson.D{ + {typeKey, 1}, + {deviceIDKey, 1}, +} + +var typeResourceIDQueryIndex = bson.D{ + {typeKey, 1}, + {deviceIDKey, 1}, + {hrefKey, 1}, +} + +type DBSub struct { + ID string `bson:"_id"` + URL string + CorrelationID string // uuid + Type oapiConStore.Type + ContentType string + EventTypes []events.EventType + DeviceID string `bson:deviceIDKey` + Href string `bson:hrefKey` + SequenceNumber uint64 `bson:sequenceNumberKey` + UserID string `bson:userIDKey` + SigningSecret string +} + +func makeDBSub(sub store.Subscription) DBSub { + return DBSub{ + ID: sub.ID, + URL: sub.URL, + CorrelationID: sub.CorrelationID, + Type: sub.Type, + ContentType: sub.ContentType, + EventTypes: sub.EventTypes, + DeviceID: sub.DeviceID, + Href: sub.Href, + SequenceNumber: sub.SequenceNumber, + UserID: sub.UserID, + SigningSecret: sub.SigningSecret, + } +} + +func (s *Store) SaveSubscription(ctx context.Context, sub store.Subscription) error { + if sub.ID == "" { + return fmt.Errorf("invalid ID") + } + if len(sub.EventTypes) == 0 { + return fmt.Errorf("invalid EventTypes") + } + if sub.URL == "" { + return fmt.Errorf("invalid URL") + } + if sub.SigningSecret == "" { + return fmt.Errorf("invalid SigningSecret") + } + if sub.UserID == "" { + return fmt.Errorf("invalid UserID") + } + switch sub.Type { + case oapiConStore.Type_Device: + if sub.DeviceID == "" { + return fmt.Errorf("invalid DeviceID for device subscription type") + } + if sub.Href != "" { + return fmt.Errorf("invalid Href for device subscription type") + } + case oapiConStore.Type_Resource: + if sub.DeviceID == "" { + return fmt.Errorf("invalid DeviceID for resource subscription type") + } + if sub.Href == "" { + return fmt.Errorf("invalid Href for resource subscription type") + } + default: + return fmt.Errorf("not supported Type %v", sub.Type) + } + DBSub := makeDBSub(sub) + col := s.client.Database(s.DBName()).Collection(subscriptionsCName) + if _, err := col.InsertOne(ctx, DBSub); err != nil { + return fmt.Errorf("cannot save resource subscription: %w", err) + } + return nil +} + +func (s *Store) IncrementSubscriptionSequenceNumber(ctx context.Context, subscriptionID string) (uint64, error) { + col := s.client.Database(s.DBName()).Collection(subscriptionsCName) + + res, err := incrementSubscriptionSequenceNumber(ctx, col, subscriptionID) + if err != nil { + return 0, fmt.Errorf("cannot increment sequence number for subscription: %w", err) + } + return res, err +} + +func (s *Store) PopSubscription(ctx context.Context, subscriptionID string) (sub store.Subscription, err error) { + var DBSub DBSub + res := s.client.Database(s.DBName()).Collection(subscriptionsCName).FindOneAndDelete(ctx, bson.M{"_id": subscriptionID}) + if res.Err() != nil { + return sub, res.Err() + } + err = res.Decode(&DBSub) + if err != nil { + return sub, err + } + return convertToSubscription(DBSub), nil +} + +func (s *Store) LoadSubscriptions(ctx context.Context, query store.SubscriptionQuery, h store.SubscriptionHandler) error { + var iter *mongo.Cursor + var err error + + col := s.client.Database(s.DBName()).Collection(subscriptionsCName) + switch { + case query.SubscriptionID != "": + iter, err = col.Find(ctx, bson.M{"_id": query.SubscriptionID}) + case query.Type == "" && query.DeviceID == "" && query.Href == "": + iter, err = col.Find(ctx, bson.M{}) + case query.Type == "": + return fmt.Errorf("invalid Type") + case query.DeviceID != "" && query.Href != "": + q := bson.M{ + typeKey: query.Type, + deviceIDKey: query.DeviceID, + hrefKey: query.Href, + } + iter, err = col.Find(ctx, q, &options.FindOptions{ + Hint: typeResourceIDQueryIndex, + }) + case query.DeviceID != "": + q := bson.M{ + typeKey: query.Type, + deviceIDKey: query.DeviceID, + } + iter, err = col.Find(ctx, q, &options.FindOptions{ + Hint: typeDeviceIDQueryIndex, + }) + default: + q := bson.M{ + typeKey: query.Type, + } + iter, err = col.Find(ctx, q, &options.FindOptions{ + Hint: typeQueryIndex, + }) + } + if err == mongo.ErrNilDocument { + return nil + } + if err != nil { + return err + } + + i := subscriptionIterator{ + iter: iter, + } + err = h.Handle(ctx, &i) + + errClose := iter.Close(ctx) + if err == nil { + return errClose + } + return err +} + +type subscriptionIterator struct { + iter *mongo.Cursor +} + +func convertToSubscription(sub DBSub) (s store.Subscription) { + s.ID = sub.ID + s.URL = sub.URL + s.CorrelationID = sub.CorrelationID + s.Type = sub.Type + s.ContentType = sub.ContentType + s.EventTypes = sub.EventTypes + s.DeviceID = sub.DeviceID + s.Href = sub.Href + s.SequenceNumber = sub.SequenceNumber + s.UserID = sub.UserID + s.SigningSecret = sub.SigningSecret + return +} + +func (i *subscriptionIterator) Next(ctx context.Context, s *store.Subscription) bool { + var sub DBSub + + if !i.iter.Next(ctx) { + return false + } + + err := i.iter.Decode(&sub) + if err != nil { + return false + } + *s = convertToSubscription(sub) + return true +} + +func (i *subscriptionIterator) Err() error { + return i.iter.Err() +} diff --git a/openapi-gateway/store/mongodb/subscription_test.go b/openapi-gateway/store/mongodb/subscription_test.go new file mode 100644 index 000000000..9ff2f40b9 --- /dev/null +++ b/openapi-gateway/store/mongodb/subscription_test.go @@ -0,0 +1,369 @@ +package mongodb + +import ( + "context" + "testing" + + "github.com/go-ocf/kit/security/certManager" + oapiConStore "github.com/go-ocf/cloud/openapi-connector/store" + "github.com/stretchr/testify/assert" + + "github.com/go-ocf/cloud/openapi-connector/events" + "github.com/go-ocf/cloud/openapi-gateway/store" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/require" +) + +func newStore(ctx context.Context, t *testing.T, cfg Config) *Store { + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + defer dialCertManager.Close() + tlsConfig := dialCertManager.GetClientTLSConfig() + s, err := NewStore(ctx, cfg, WithTLS(&tlsConfig)) + require.NoError(t, err) + return s +} + +func TestStore_SaveSubscription(t *testing.T) { + type args struct { + sub store.Subscription + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "valid1", + args: args{ + sub: store.Subscription{ + ID: "id1", + URL: "url", + CorrelationID: "correlationID", + ContentType: "contentType", + EventTypes: []events.EventType{"eventtypes"}, + SequenceNumber: 0, + DeviceID: "deviceid", + Href: "resourcehref", + SigningSecret: "signingSecret", + UserID: "userID", + Type: oapiConStore.Type_Resource, + }, + }, + }, + { + name: "valid2", + args: args{ + sub: store.Subscription{ + ID: "id2", + URL: "url", + CorrelationID: "correlationID", + ContentType: "contentType", + EventTypes: []events.EventType{"eventtypes"}, + SequenceNumber: 0, + DeviceID: "deviceid", + Href: "resourcehref", + SigningSecret: "signingSecret", + UserID: "userID", + Type: oapiConStore.Type_Resource, + }, + }, + }, + { + name: "valid3", + args: args{ + sub: store.Subscription{ + ID: "id3", + URL: "url", + CorrelationID: "correlationID", + ContentType: "contentType", + EventTypes: []events.EventType{"eventtypes"}, + SequenceNumber: 0, + DeviceID: "deviceid", + Href: "resourcehref", + UserID: "userID", + SigningSecret: "signingSecret", + Type: oapiConStore.Type_Resource, + }, + }, + }, + { + name: "err", + args: args{ + sub: store.Subscription{ + ID: "id3", + URL: "url", + CorrelationID: "correlationID", + ContentType: "contentType", + EventTypes: []events.EventType{"eventtypes"}, + SequenceNumber: 0, + DeviceID: "deviceid", + Href: "resourcehref", + SigningSecret: "signingSecret", + UserID: "userID", + Type: oapiConStore.Type_Resource, + }, + }, + wantErr: true, + }, + } + + var cfg Config + err := envconfig.Process("", &cfg) + require.NoError(t, err) + ctx := context.Background() + s := newStore(ctx, t, cfg) + defer s.Close(ctx) + defer s.Clear(ctx) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := s.SaveSubscription(context.Background(), tt.args.sub) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + + }) + } +} + +func TestStore_IncrementSubscriptionSequenceNumber(t *testing.T) { + sub := store.Subscription{ + ID: "id", + URL: "url", + CorrelationID: "correlationID", + ContentType: "contentType", + EventTypes: []events.EventType{"eventtypes"}, + SequenceNumber: 0, + DeviceID: "deviceid", + Href: "resourcehref", + SigningSecret: "signingSecret", + UserID: "userID", + Type: oapiConStore.Type_Resource, + } + + type args struct { + subscriptionID string + } + tests := []struct { + name string + args args + want uint64 + wantErr bool + }{ + { + name: "valid1", + args: args{ + subscriptionID: sub.ID, + }, + want: 0, + }, + { + name: "valid2", + args: args{ + subscriptionID: sub.ID, + }, + want: 1, + }, + { + name: "err", + args: args{ + subscriptionID: "id1", + }, + wantErr: true, + }, + } + + var cfg Config + err := envconfig.Process("", &cfg) + require.NoError(t, err) + ctx := context.Background() + s := newStore(ctx, t, cfg) + defer s.Close(ctx) + defer s.Clear(ctx) + + err = s.SaveSubscription(context.Background(), sub) + require.NoError(t, err) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := s.IncrementSubscriptionSequenceNumber(ctx, tt.args.subscriptionID) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tt.want, got) + } + }) + } +} + +func TestStore_PopSubscription(t *testing.T) { + sub := store.Subscription{ + ID: "id1", + URL: "url", + CorrelationID: "correlationID", + ContentType: "contentType", + EventTypes: []events.EventType{"eventtypes"}, + SequenceNumber: 0, + DeviceID: "deviceid", + Href: "resourcehref", + SigningSecret: "signingSecret", + UserID: "userID", + Type: oapiConStore.Type_Resource, + } + + type args struct { + subscriptionID string + } + tests := []struct { + name string + args args + wantSub store.Subscription + wantErr bool + }{ + { + name: "valid1", + args: args{ + subscriptionID: sub.ID, + }, + wantSub: sub, + }, + { + name: "valid1", + args: args{ + subscriptionID: sub.ID, + }, + wantErr: true, + }, + } + + var cfg Config + err := envconfig.Process("", &cfg) + require.NoError(t, err) + ctx := context.Background() + s := newStore(ctx, t, cfg) + defer s.Close(ctx) + defer s.Clear(ctx) + + err = s.SaveSubscription(context.Background(), sub) + require.NoError(t, err) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotSub, err := s.PopSubscription(ctx, tt.args.subscriptionID) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tt.wantSub, gotSub) + } + }) + } +} + +type testResourceHandler struct { + data []store.Subscription +} + +func (h *testResourceHandler) Handle(ctx context.Context, iter store.SubscriptionIter) (err error) { + var sub store.Subscription + for iter.Next(ctx, &sub) { + h.data = append(h.data, sub) + } + return iter.Err() +} + +func TestStore_LoadSubscriptions(t *testing.T) { + sub := store.Subscription{ + ID: "id", + URL: "url", + CorrelationID: "correlationID", + ContentType: "contentType", + EventTypes: []events.EventType{"eventtypes"}, + SequenceNumber: 0, + DeviceID: "deviceid", + Href: "resourcehref", + SigningSecret: "signingSecret", + UserID: "userID", + Type: oapiConStore.Type_Resource, + } + + type args struct { + query store.SubscriptionQuery + } + tests := []struct { + name string + args args + wantErr bool + want []store.Subscription + }{ + { + name: "valid1", + args: args{ + query: store.SubscriptionQuery{ + SubscriptionID: sub.ID, + }, + }, + want: []store.Subscription{sub}, + }, + { + name: "valid1", + args: args{ + query: store.SubscriptionQuery{ + Type: oapiConStore.Type_Resource, + DeviceID: sub.DeviceID, + }, + }, + want: []store.Subscription{sub}, + }, + { + name: "valid2", + args: args{ + query: store.SubscriptionQuery{ + Type: oapiConStore.Type_Resource, + DeviceID: sub.DeviceID, + Href: sub.Href, + }, + }, + want: []store.Subscription{sub}, + }, + { + name: "empty", + args: args{ + query: store.SubscriptionQuery{ + SubscriptionID: "empty", + }, + }, + }, + } + + var cfg Config + err := envconfig.Process("", &cfg) + require.NoError(t, err) + ctx := context.Background() + s := newStore(ctx, t, cfg) + defer s.Close(ctx) + defer s.Clear(ctx) + + err = s.SaveSubscription(context.Background(), sub) + require.NoError(t, err) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var h testResourceHandler + err = s.LoadSubscriptions(ctx, tt.args.query, &h) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tt.want, h.data) + } + }) + } +} diff --git a/openapi-gateway/store/store.go b/openapi-gateway/store/store.go new file mode 100644 index 000000000..bd68a5b52 --- /dev/null +++ b/openapi-gateway/store/store.go @@ -0,0 +1,52 @@ +package store + +import ( + "context" + "time" + + "github.com/go-ocf/cloud/openapi-connector/events" + + "github.com/go-ocf/cloud/openapi-connector/store" +) + +type SubscriptionQuery struct { + SubscriptionID string + DeviceID string + Href string + Type store.Type +} + +type DevicesSubscriptionQuery struct { + SubscriptionID string + LastCheck time.Time +} + +type SubscriptionIter interface { + Next(ctx context.Context, sub *Subscription) bool + Err() error +} + +type DevicesSubscriptionIter interface { + Next(ctx context.Context, sub *DevicesSubscription) bool + Err() error +} + +type SubscriptionHandler interface { + Handle(ctx context.Context, iter SubscriptionIter) (err error) +} + +type DevicesSubscriptionHandler interface { + Handle(ctx context.Context, iter DevicesSubscriptionIter) (err error) +} + +type Store interface { + SaveDevicesSubscription(ctx context.Context, sub DevicesSubscription) error + LoadDevicesSubscriptions(ctx context.Context, query DevicesSubscriptionQuery, h DevicesSubscriptionHandler) error + UpdateDevicesSubscription(ctx context.Context, subscriptionID string, lastDevicesRegistered events.DevicesRegistered, lastDevicesOnline events.DevicesOnline, lastDevicesOffline events.DevicesOffline) error + PopDevicesSubscription(ctx context.Context, subscriptionID string) (DevicesSubscription, error) + + SaveSubscription(ctx context.Context, sub Subscription) error + PopSubscription(ctx context.Context, subscriptionID string) (Subscription, error) + LoadSubscriptions(ctx context.Context, query SubscriptionQuery, h SubscriptionHandler) error + IncrementSubscriptionSequenceNumber(ctx context.Context, subscriptionID string) (uint64, error) +} diff --git a/openapi-gateway/store/subscription.go b/openapi-gateway/store/subscription.go new file mode 100644 index 000000000..d6d0aa3a8 --- /dev/null +++ b/openapi-gateway/store/subscription.go @@ -0,0 +1,32 @@ +package store + +import ( + "time" + + "github.com/go-ocf/cloud/openapi-connector/events" + "github.com/go-ocf/cloud/openapi-connector/store" +) + +type Subscription struct { + ID string // Id + URL string // href + CorrelationID string // uuid + Type store.Type + ContentType string // application/json or application/vnd.ocf+cbor + EventTypes []events.EventType + DeviceID string // filled for device and resource events + Href string // filled for resource events + SequenceNumber uint64 + UserID string + SigningSecret string +} + +type DevicesSubscription struct { + // EventTypes = [devices_registered, devices_unregistered, devices_online, devices_offline] + Subscription + AccessToken string + LastDevicesRegistered events.DevicesRegistered + LastDevicesOnline events.DevicesOnline + LastDevicesOffline events.DevicesOffline + LastCheck time.Time +} diff --git a/openapi-gateway/uri/uri.go b/openapi-gateway/uri/uri.go new file mode 100644 index 000000000..316f0276e --- /dev/null +++ b/openapi-gateway/uri/uri.go @@ -0,0 +1,20 @@ +package uri + +// Resource Service URIs. +const ( + API string = "/api" + Version string = API + "/v1" + Devices string = Version + "/devices" + Device string = Devices + "/{{ .DeviceId }}" + + ResourceValues string = Devices + "/{{ .DeviceId }}/{{ .ResourceLinkHref }}" + + DevicesSubscriptions string = Devices + "/subscriptions" + DevicesSubscription string = Devices + "/subscriptions/{{ .SubscriptionID }}" + + DeviceSubscriptions string = Devices + "​/{{ .DeviceId }}/subscriptions" + DeviceSubscription string = Devices + "/{{ .DeviceId }}/subscriptions/{{ .SubscriptionID }}" + + ResourceSubscriptions string = Devices + "/{{ .DeviceId }}/{{ .ResourceLinkHref }}/subscriptions" + ResourceSubscription string = Devices + "/{{ .DeviceId }}/{{ .ResourceLinkHref }}/subscriptions/{{ .SubscriptionID }}" +) diff --git a/portal-webapi/.dockerignore b/portal-webapi/.dockerignore new file mode 100644 index 000000000..16d990037 --- /dev/null +++ b/portal-webapi/.dockerignore @@ -0,0 +1,7 @@ +.git +.github +.gitignore +.travis.yml +.gitlab-ci.yml +Makefile +vendor diff --git a/portal-webapi/.gitignore b/portal-webapi/.gitignore new file mode 100644 index 000000000..0836d6706 --- /dev/null +++ b/portal-webapi/.gitignore @@ -0,0 +1,22 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +debug +step-ca + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out +coverage.txt +.codecov + +#goland +.idea/ + +#vendor +vendor/ diff --git a/portal-webapi/Dockerfile b/portal-webapi/Dockerfile new file mode 100644 index 000000000..60ab3cee9 --- /dev/null +++ b/portal-webapi/Dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.13.5-alpine3.10 AS build +RUN apk add --no-cache curl git build-base +WORKDIR $GOPATH/src/github.com/go-ocf/cloud/portal-webapi +COPY . . +RUN go mod download +RUN go build -o /go/bin/service ./cmd/service + +FROM alpine:3.11 as service +RUN apk add --no-cache ca-certificates +COPY --from=build /go/bin/service /usr/local/bin/service +ENTRYPOINT ["/usr/local/bin/service"] \ No newline at end of file diff --git a/portal-webapi/Makefile b/portal-webapi/Makefile new file mode 100644 index 000000000..6ab4bcb29 --- /dev/null +++ b/portal-webapi/Makefile @@ -0,0 +1,31 @@ +SHELL = /bin/bash +SERVICE_NAME = $(notdir $(CURDIR)) +LATEST_TAG = vnext +VERSION_TAG = vnext-$(shell git rev-parse --short=7 --verify HEAD) + +default: build + +define build-docker-image + docker build \ + --network=host \ + --tag ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) \ + --tag ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) \ + --target $(1) \ + --file Dockerfile \ + . +endef + +build-servicecontainer: + $(call build-docker-image,service) + +build: build-servicecontainer + +push: build-servicecontainer + docker push ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) + docker push ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) + +proto/generate: + +.PHONY: build-servicecontainer build push proto/generate + + diff --git a/portal-webapi/README.md b/portal-webapi/README.md new file mode 100644 index 000000000..9f1a9d58b --- /dev/null +++ b/portal-webapi/README.md @@ -0,0 +1,5 @@ +[![Go Report](https://goreportcard.com/badge/github.com/go-ocf/cloud/portal-webapi)](https://goreportcard.com/report/github.com/go-ocf/cloud/portal-webapi) +[![Gitter](https://badges.gitter.im/ocfcloud/Lobby.svg)](https://gitter.im/ocfcloud/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +# portal-webapi + diff --git a/portal-webapi/cmd/service/main.go b/portal-webapi/cmd/service/main.go new file mode 100644 index 000000000..ccf11e15a --- /dev/null +++ b/portal-webapi/cmd/service/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "github.com/go-ocf/kit/log" + "github.com/kelseyhightower/envconfig" + "github.com/go-ocf/cloud/portal-webapi/refImpl" +) + +func main() { + var config refImpl.Config + if err := envconfig.Process("", &config); err != nil { + log.Fatalf("cannot parse configuration: %v", err) + } + if server, err := refImpl.Init(config); err != nil { + log.Fatalf("cannot init server: %v", err) + } else { + if err = server.Serve(); err != nil { + log.Fatalf("unexpected ends: %v", err) + } + } +} diff --git a/portal-webapi/refImpl/refImpl.go b/portal-webapi/refImpl/refImpl.go new file mode 100644 index 000000000..0d64b34c5 --- /dev/null +++ b/portal-webapi/refImpl/refImpl.go @@ -0,0 +1,59 @@ +package refImpl + +import ( + "encoding/json" + "fmt" + + "github.com/go-ocf/kit/log" + "github.com/go-ocf/kit/security/certManager" + "github.com/go-ocf/cloud/portal-webapi/service" +) + +type Config struct { + Log log.Config `envconfig:"LOG"` + Service service.Config + Dial certManager.Config `envconfig:"DIAL"` + Listen certManager.Config `envconfig:"LISTEN"` +} + +type RefImpl struct { + server *service.Server + dialCertManager certManager.CertManager + listenCertManager certManager.CertManager +} + +//String return string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} + +func Init(config Config) (*RefImpl, error) { + log.Setup(config.Log) + + dialCertManager, err := certManager.NewCertManager(config.Dial) + if err != nil { + return nil, fmt.Errorf("cannot create dial cert manager %v", err) + } + listenCertManager, err := certManager.NewCertManager(config.Listen) + if err != nil { + return nil, fmt.Errorf("cannot create listen cert manager %v", err) + } + log.Info(config.String()) + + return &RefImpl{ + server: service.New(config.Service, dialCertManager, listenCertManager), + dialCertManager: dialCertManager, + listenCertManager: listenCertManager, + }, nil +} + +func (r *RefImpl) Serve() error { + return r.server.Serve() +} + +func (r *RefImpl) Shutdown() { + r.server.Shutdown() + r.dialCertManager.Close() + r.listenCertManager.Close() +} diff --git a/portal-webapi/refImpl/refImpl_test.go b/portal-webapi/refImpl/refImpl_test.go new file mode 100644 index 000000000..ca2af2f8e --- /dev/null +++ b/portal-webapi/refImpl/refImpl_test.go @@ -0,0 +1,18 @@ +package refImpl + +import ( + "testing" + + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/require" +) + +func TestInit(t *testing.T) { + var config Config + err := envconfig.Process("", &config) + require.NoError(t, err) + + got, err := Init(config) + require.NoError(t, err) + require.NotEmpty(t, got) +} diff --git a/portal-webapi/service/auth.go b/portal-webapi/service/auth.go new file mode 100644 index 000000000..9c719eb77 --- /dev/null +++ b/portal-webapi/service/auth.go @@ -0,0 +1,57 @@ +package service + +import ( + "fmt" + "strings" + + jwt "github.com/dgrijalva/jwt-go" + "github.com/valyala/fasthttp" +) + +func parseAuth(ctx *fasthttp.RequestCtx) (token, sub string, err error) { + token, sub, err = parseBearer(string(ctx.Request.Header.Peek("Authorization"))) + if err != nil { + err = fmt.Errorf("cannot parse authentication header: %v", err) + } + return +} + +func parseBearer(auth string) (token, sub string, err error) { + if strings.HasPrefix(auth, "Bearer ") { + rawToken := auth[7:] + sub, err = parseSubFromJwtToken(rawToken) + if err != nil { + err = fmt.Errorf("cannot parse bearer: %v", err) + return + } + token = rawToken + return + } + return "", "", fmt.Errorf("cannot parse bearer: prefix 'Bearer ' not found") +} + +type claims struct { + Subject string `json:"sub,omitempty"` +} + +func (c *claims) Valid() error { + return nil +} + +func parseSubFromJwtToken(rawJwtToken string) (string, error) { + parser := &jwt.Parser{ + SkipClaimsValidation: true, + } + + var claims claims + _, _, err := parser.ParseUnverified(rawJwtToken, &claims) + if err != nil { + return "", fmt.Errorf("cannot get sub from jwt token: %v", err) + } + + if claims.Subject != "" { + return claims.Subject, nil + } + + return "", fmt.Errorf("cannot get sub from jwt token: not found") +} diff --git a/portal-webapi/service/auth_test.go b/portal-webapi/service/auth_test.go new file mode 100644 index 000000000..daa8e789d --- /dev/null +++ b/portal-webapi/service/auth_test.go @@ -0,0 +1,42 @@ +package service + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_parseBearer(t *testing.T) { + type args struct { + auth string + } + tests := []struct { + name string + args args + wantToken string + wantSub string + wantErr bool + }{ + { + name: "subNotFound", + args: args{ + auth: "Bearer " + `eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlFrWTRNekZHTVRkRk16TXlOME5HUWpFeU9VRkZNekU1UTBaRU1VWXpRVVF4TmtORU5UbEVNZyJ9.eyJpc3MiOiJodHRwczovL29jZmNsb3VkLmV1LmF1dGgwLmNvbS8iLCJzdWIiOiJRZkhoaE5LWEoxRjlTMXNRZHY3OTFNSm1FVndLc0J2cUBjbGllbnRzIiwiYXVkIjoiaHR0cHM6Ly9wb3J0YWwub2NmY2xvdWQuY29tLyIsImlhdCI6MTU1MDg0ODQ4NSwiZXhwIjoxNTUwOTM0ODg1LCJhenAiOiJRZkhoaE5LWEoxRjlTMXNRZHY3OTFNSm1FVndLc0J2cSIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.Wv93hqC0branpOzm3_-T_DG2tQNYX8CvJntU7ZWK6J9BLzXqVx0Up-oH5lS_hShAVV3JjIoJ7CtACY_knWSkxvMjkeYd5Kcltm__XK9vK153RJyMHnc1EZFRR36ifH_Z6ewxYeUJoAcitNNJEQcaTkhavTBDe5-oXk-KT8Mtui0uzE18uO7Mdl0d2NN6mnd-2sJsC8LC5-rCOCPv3WRbEm76G_oBAllGhHf21bx4wP6iexZbhO1vofOSq4JfK_fdye4e86cmitCoQBUuOIV-Qrr8i_MiRVKVGdDxw1_wGsrjJcAr3NH4EiekGhQU4pbqHO5BJ8eJOXCs0OhGjXCQsw`, + }, + wantErr: false, + wantToken: `eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlFrWTRNekZHTVRkRk16TXlOME5HUWpFeU9VRkZNekU1UTBaRU1VWXpRVVF4TmtORU5UbEVNZyJ9.eyJpc3MiOiJodHRwczovL29jZmNsb3VkLmV1LmF1dGgwLmNvbS8iLCJzdWIiOiJRZkhoaE5LWEoxRjlTMXNRZHY3OTFNSm1FVndLc0J2cUBjbGllbnRzIiwiYXVkIjoiaHR0cHM6Ly9wb3J0YWwub2NmY2xvdWQuY29tLyIsImlhdCI6MTU1MDg0ODQ4NSwiZXhwIjoxNTUwOTM0ODg1LCJhenAiOiJRZkhoaE5LWEoxRjlTMXNRZHY3OTFNSm1FVndLc0J2cSIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.Wv93hqC0branpOzm3_-T_DG2tQNYX8CvJntU7ZWK6J9BLzXqVx0Up-oH5lS_hShAVV3JjIoJ7CtACY_knWSkxvMjkeYd5Kcltm__XK9vK153RJyMHnc1EZFRR36ifH_Z6ewxYeUJoAcitNNJEQcaTkhavTBDe5-oXk-KT8Mtui0uzE18uO7Mdl0d2NN6mnd-2sJsC8LC5-rCOCPv3WRbEm76G_oBAllGhHf21bx4wP6iexZbhO1vofOSq4JfK_fdye4e86cmitCoQBUuOIV-Qrr8i_MiRVKVGdDxw1_wGsrjJcAr3NH4EiekGhQU4pbqHO5BJ8eJOXCs0OhGjXCQsw`, + wantSub: `QfHhhNKXJ1F9S1sQdv791MJmEVwKsBvq@clients`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotToken, gotSub, err := parseBearer(tt.args.auth) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.wantToken, gotToken) + assert.Equal(t, tt.wantSub, gotSub) + }) + } +} diff --git a/portal-webapi/service/config.go b/portal-webapi/service/config.go new file mode 100644 index 000000000..a0274eed7 --- /dev/null +++ b/portal-webapi/service/config.go @@ -0,0 +1,33 @@ +package service + +import ( + "encoding/json" + "fmt" + + "github.com/go-ocf/kit/net/grpc" +) + +type httpProto string + +func (a *httpProto) Decode(value string) error { + switch value { + case "http", "https": + *a = httpProto(value) + return nil + default: + return fmt.Errorf("unsupported protocol type %v", value) + } +} + +//Config represent application configuration +type Config struct { + grpc.Config + ResourceDirectoryAddr string `envconfig:"RESOURCE_DIRECTORY_ADDRESS" default:"127.0.0.1:9100"` + ResourceAggregateAddr string `envconfig:"RESOURCE_AGGREGATE_ADDRESS" default:"127.0.0.1:9100"` +} + +//String return string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} diff --git a/portal-webapi/service/getResourceContent.go b/portal-webapi/service/getResourceContent.go new file mode 100644 index 000000000..551b84220 --- /dev/null +++ b/portal-webapi/service/getResourceContent.go @@ -0,0 +1,87 @@ +package service + +import ( + "context" + "fmt" + "io" + "net/http" + "time" + + coap "github.com/go-ocf/go-coap" + "github.com/ugorji/go/codec" + + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + "github.com/go-ocf/kit/log" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + pbRS "github.com/go-ocf/cloud/resource-directory/pb/resource-shadow" + "github.com/valyala/fasthttp" +) + +func (r *RequestHandler) getResourceContent(ctx *fasthttp.RequestCtx, token, sub string) { + log.Debugf("RequestHandler.listResourceDirectory start") + t := time.Now() + defer func() { + log.Debugf("RequestHandler.listResourceDirectory takes %v", time.Since(t)) + }() + var resourceId string + var ok bool + + if resourceId, ok = ctx.UserValue("resourceId").(string); !ok { + logAndWriteErrorResponse(fmt.Errorf("cannot retrieve resource content: resourceId from uri"), http.StatusBadRequest, ctx) + return + } + + retrieveResourcesValuesClient, err := r.rsClient.RetrieveResourcesValues(kitNetGrpc.CtxWithToken(context.Background(), token), &pbRS.RetrieveResourcesValuesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{ + UserId: sub, + }, + ResourceIdsFilter: []string{resourceId}, + }) + + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot retrieve resource content: %v", err), http.StatusBadRequest, ctx) + return + } + defer retrieveResourcesValuesClient.CloseSend() + + var m interface{} + + for { + resourceValue, err := retrieveResourcesValuesClient.Recv() + if err == io.EOF { + break + } + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot retrieve resource content: %v", err), http.StatusBadRequest, ctx) + return + } + if resourceValue.ResourceId == resourceId && resourceValue.Content != nil { + switch resourceValue.Content.ContentType { + case coap.AppCBOR.String(), coap.AppOcfCbor.String(): + err := codec.NewDecoderBytes(resourceValue.Content.Data, new(codec.CborHandle)).Decode(&m) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot retrieve resource content: %v", err), http.StatusInternalServerError, ctx) + return + } + case coap.AppJSON.String(): + err := codec.NewDecoderBytes(resourceValue.Content.Data, new(codec.JsonHandle)).Decode(&m) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot retrieve resource content: %v", err), http.StatusInternalServerError, ctx) + return + } + case coap.TextPlain.String(): + m = string(resourceValue.Content.Data) + default: + logAndWriteErrorResponse(fmt.Errorf("cannot retrieve resource content: cannot convert content-type '%v' to json", resourceValue.Content.ContentType), http.StatusInternalServerError, ctx) + return + } + break + } + } + if m == nil { + logAndWriteErrorResponse(fmt.Errorf("cannot retrieve resource content: not found"), http.StatusNotFound, ctx) + return + } + + writeJson(m, fasthttp.StatusOK, ctx) +} diff --git a/portal-webapi/service/healthcheck.go b/portal-webapi/service/healthcheck.go new file mode 100644 index 000000000..6f59edbd7 --- /dev/null +++ b/portal-webapi/service/healthcheck.go @@ -0,0 +1,7 @@ +package service + +import "github.com/valyala/fasthttp" + +func (r *RequestHandler) healthcheck(ctx *fasthttp.RequestCtx) { + ctx.SetStatusCode(fasthttp.StatusOK) +} diff --git a/portal-webapi/service/httpApi.go b/portal-webapi/service/httpApi.go new file mode 100644 index 000000000..da374585c --- /dev/null +++ b/portal-webapi/service/httpApi.go @@ -0,0 +1,99 @@ +package service + +import ( + "bytes" + "fmt" + + "github.com/go-ocf/kit/log" + "github.com/go-ocf/cloud/portal-webapi/uri" + "github.com/ugorji/go/codec" + "github.com/valyala/fasthttp" + + router "github.com/buaazp/fasthttprouter" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + pbDD "github.com/go-ocf/cloud/resource-directory/pb/device-directory" + pbRD "github.com/go-ocf/cloud/resource-directory/pb/resource-directory" + pbRS "github.com/go-ocf/cloud/resource-directory/pb/resource-shadow" +) + +//RequestHandler for handling incoming request +type RequestHandler struct { + config Config + server *Server + raClient pbRA.ResourceAggregateClient + rsClient pbRS.ResourceShadowClient + rdClient pbRD.ResourceDirectoryClient + ddClient pbDD.DeviceDirectoryClient +} + +//NewRequestHandler factory for new RequestHandler +func NewRequestHandler(server *Server, raClient pbRA.ResourceAggregateClient, rsClient pbRS.ResourceShadowClient, rdClient pbRD.ResourceDirectoryClient, ddClient pbDD.DeviceDirectoryClient) *RequestHandler { + return &RequestHandler{ + server: server, + raClient: raClient, + rsClient: rsClient, + rdClient: rdClient, + ddClient: ddClient, + } +} + +func logAndWriteErrorResponse(err error, statusCode int, ctx *fasthttp.RequestCtx) { + log.Errorf("%v", err) + ctx.Response.SetBody([]byte(err.Error())) + ctx.SetStatusCode(statusCode) +} + +func toJson(v interface{}) ([]byte, error) { + bw := bytes.NewBuffer(make([]byte, 0, 1024)) + h := &codec.JsonHandle{} + h.BasicHandle.Canonical = true + err := codec.NewEncoder(bw, h).Encode(v) + if err != nil { + return nil, fmt.Errorf("cannot convert to json: %v", err) + } + return bw.Bytes(), nil +} + +func writeJson(v interface{}, statusCode int, ctx *fasthttp.RequestCtx) { + body, err := toJson(v) + if err != nil { + err = fmt.Errorf("cannot write body: %v", err) + logAndWriteErrorResponse(err, fasthttp.StatusInternalServerError, ctx) + return + } + ctx.Response.Header.SetContentType("application/json") + ctx.Response.SetBody(body) + ctx.SetStatusCode(statusCode) +} + +func validateRequest(ctx *fasthttp.RequestCtx, cbk func(ctx *fasthttp.RequestCtx, token, sub string)) { + token, sub, err := parseAuth(ctx) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("invalid request: %v", err), fasthttp.StatusUnauthorized, ctx) + return + } + cbk(ctx, token, sub) +} + +//NewHTTP return router handle HTTP request +func NewHTTP(requestHandler *RequestHandler) *router.Router { + router := router.New() + router.GET(uri.Devices, func(ctx *fasthttp.RequestCtx) { + validateRequest(ctx, requestHandler.listDevices) + }) + router.DELETE(uri.Devices+"/:deviceId", func(ctx *fasthttp.RequestCtx) { + validateRequest(ctx, requestHandler.offboardDevice) + }) + router.GET(uri.Resources, func(ctx *fasthttp.RequestCtx) { + validateRequest(ctx, requestHandler.listResources) + }) + router.GET(uri.Resources+"/:resourceId", func(ctx *fasthttp.RequestCtx) { + validateRequest(ctx, requestHandler.getResourceContent) + }) + router.PUT(uri.Resources+"/:resourceId", func(ctx *fasthttp.RequestCtx) { + validateRequest(ctx, requestHandler.updateResourceContent) + }) + router.GET(uri.Healthcheck, requestHandler.healthcheck) + + return router +} diff --git a/portal-webapi/service/listDevices.go b/portal-webapi/service/listDevices.go new file mode 100644 index 000000000..42c91c9dc --- /dev/null +++ b/portal-webapi/service/listDevices.go @@ -0,0 +1,79 @@ +package service + +import ( + "context" + "fmt" + "io" + "net/http" + "time" + + "github.com/go-ocf/kit/log" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbDD "github.com/go-ocf/cloud/resource-directory/pb/device-directory" + "github.com/valyala/fasthttp" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" +) + +type GetDevicesResponse struct { + Devices map[string]*Device `json:"devices"` +} + +type State struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + IsOnline bool `protobuf:"varint,2,opt,name=is_online,json=isOnline,proto3" json:"is_online,omitempty"` + IsConnected bool `protobuf:"varint,3,opt,name=is_connected,json=isConnected,proto3" json:"is_connected,omitempty"` +} + +type Device struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Resource *pbDD.Resource `protobuf:"bytes,2,opt,name=resource" json:"resource,omitempty"` + State *State `protobuf:"bytes,3,opt,name=state" json:"state,omitempty"` +} + +func (r *RequestHandler) listDevices(ctx *fasthttp.RequestCtx, token, sub string) { + log.Debugf("RequestHandler.listDevices start") + t := time.Now() + defer func() { + log.Debugf("RequestHandler.listDevices takes %v", time.Since(t)) + }() + + getDevicesClient, err := r.ddClient.GetDevices(kitNetGrpc.CtxWithToken(context.Background(), token), &pbDD.GetDevicesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{ + UserId: sub, + }, + }) + + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot list device directory: %v", err), http.StatusBadRequest, ctx) + return + } + defer getDevicesClient.CloseSend() + + devices := make(map[string]*Device) + for { + device, err := getDevicesClient.Recv() + if err == io.EOF { + break + } + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot list device directory: %v", err), http.StatusBadRequest, ctx) + return + } + devices[device.Id] = &Device{ + Id: device.Id, + Resource: device.Resource, + State: &State{ + Id: device.Id, + IsOnline: device.IsOnline, + }, + } + } + if len(devices) == 0 { + logAndWriteErrorResponse(fmt.Errorf("cannot list device directory: not found"), http.StatusNotFound, ctx) + return + } + + writeJson(GetDevicesResponse{ + Devices: devices, + }, fasthttp.StatusOK, ctx) +} diff --git a/portal-webapi/service/listResources.go b/portal-webapi/service/listResources.go new file mode 100644 index 000000000..a56613893 --- /dev/null +++ b/portal-webapi/service/listResources.go @@ -0,0 +1,69 @@ +package service + +import ( + "context" + "fmt" + "io" + "net/http" + "time" + + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/kit/log" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRD "github.com/go-ocf/cloud/resource-directory/pb/resource-directory" + "github.com/valyala/fasthttp" +) + +type DeviceResources struct { + Resources map[string]*pbRA.Resource `json:"resources"` +} + +type GetResourceLinksResponse struct { + Devices map[string]*DeviceResources `json:"devices"` +} + +func (r *RequestHandler) listResources(ctx *fasthttp.RequestCtx, token, sub string) { + log.Debugf("RequestHandler.listResources start") + t := time.Now() + defer func() { + log.Debugf("RequestHandler.listResources takes %v", time.Since(t)) + }() + + getResourceLinksClient, err := r.rdClient.GetResourceLinks(kitNetGrpc.CtxWithToken(context.Background(), token), &pbRD.GetResourceLinksRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{ + UserId: sub, + }, + }) + + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot list resource directory: %v", err), http.StatusBadRequest, ctx) + return + } + defer getResourceLinksClient.CloseSend() + + response := GetResourceLinksResponse{ + Devices: make(map[string]*DeviceResources), + } + for { + resLink, err := getResourceLinksClient.Recv() + if err == io.EOF { + break + } + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot list device directory: %v", err), http.StatusBadRequest, ctx) + return + } + + device, ok := response.Devices[resLink.Resource.DeviceId] + if !ok { + device = &DeviceResources{ + Resources: make(map[string]*pbRA.Resource), + } + response.Devices[resLink.Resource.DeviceId] = device + } + device.Resources[resLink.Resource.Id] = resLink.Resource + } + + writeJson(response, fasthttp.StatusOK, ctx) +} diff --git a/portal-webapi/service/offboardDevice.go b/portal-webapi/service/offboardDevice.go new file mode 100644 index 000000000..ba53ccd24 --- /dev/null +++ b/portal-webapi/service/offboardDevice.go @@ -0,0 +1,57 @@ +package service + +import ( + "fmt" + "net/http" + "time" + + "github.com/go-ocf/kit/log" + "github.com/valyala/fasthttp" +) + +func (r *RequestHandler) offboardDevice(ctx *fasthttp.RequestCtx, token, sub string) { + log.Debugf("RequestHandler.offboardDevice start") + t := time.Now() + defer func() { + log.Debugf("RequestHandler.offboardDevice takes %v", time.Since(t)) + }() + + logAndWriteErrorResponse(fmt.Errorf("cannot offboard device: not supported"), http.StatusNotImplemented, ctx) + return + + /* + + var deviceId string + var ok bool + + if deviceId, ok = ctx.UserValue("deviceId").(string); !ok { + logAndWriteErrorResponse(fmt.Errorf("cannot offboard device: deviceId from uri"), http.StatusBadRequest, ctx) + return + } + + httpRequestCtx := httputil.AcquireRequestCtx() + defer httputil.ReleaseRequestCtx(httpRequestCtx) + + request := commands.UnpublishResourceRequest{ + AuthorizationContext: &kitCqrsProtobuf.AuthorizationContext{ + UserId: sub, + AccessToken: token, + }, + //ResourceId: resourceId, + } + var response commands.UnpublishResourceResponse + httpCode, err := httpRequestCtx.PostProto(r.server.client, getContentResourceShadowURI(r.server), &request, &response) + + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot offboard device: %v", err), http.StatusBadRequest, ctx) + return + } + + if httpCode != fasthttp.StatusOK { + logAndWriteErrorResponse(fmt.Errorf("ccannot offboard device: StatusCode(%v)", httpCode), httpCode, ctx) + return + } + + writeJson(response, ctx) + */ +} diff --git a/portal-webapi/service/service.go b/portal-webapi/service/service.go new file mode 100644 index 000000000..8fa27e330 --- /dev/null +++ b/portal-webapi/service/service.go @@ -0,0 +1,83 @@ +package service + +import ( + "crypto/tls" + "net" + + "github.com/go-ocf/kit/log" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + pbDD "github.com/go-ocf/cloud/resource-directory/pb/device-directory" + pbRD "github.com/go-ocf/cloud/resource-directory/pb/resource-directory" + pbRS "github.com/go-ocf/cloud/resource-directory/pb/resource-shadow" + "github.com/valyala/fasthttp" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +//Server handle HTTP request +type Server struct { + server *fasthttp.Server + config Config + ln net.Listener +} + +type DialCertManager = interface { + GetClientTLSConfig() tls.Config +} + +type ListenCertManager = interface { + GetServerTLSConfig() tls.Config +} + +//New create new Server with provided stores +func New(config Config, dialCertManager DialCertManager, listenCertManager ListenCertManager) *Server { + dialTLSConfig := dialCertManager.GetClientTLSConfig() + listenTLSConfig := listenCertManager.GetServerTLSConfig() + listenTLSConfig.ClientAuth = tls.NoClientCert + + ln, err := tls.Listen("tcp", config.Addr, &listenTLSConfig) + if err != nil { + log.Fatalf("cannot listen and serve: %v", err) + } + + raConn, err := grpc.Dial(config.ResourceAggregateAddr, grpc.WithTransportCredentials(credentials.NewTLS(&dialTLSConfig))) + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + raClient := pbRA.NewResourceAggregateClient(raConn) + rdConn, err := grpc.Dial(config.ResourceDirectoryAddr, grpc.WithTransportCredentials(credentials.NewTLS(&dialTLSConfig))) + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + + rdClient := pbRD.NewResourceDirectoryClient(rdConn) + rsClient := pbRS.NewResourceShadowClient(rdConn) + ddClient := pbDD.NewDeviceDirectoryClient(rdConn) + + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + + server := Server{ + config: config, + ln: ln, + } + + requestHandler := NewRequestHandler(&server, raClient, rsClient, rdClient, ddClient) + router := NewHTTP(requestHandler) + + server.server = &fasthttp.Server{ + Handler: router.Handler, + } + return &server +} + +// Shutdown ends serving +func (s *Server) Shutdown() error { + return s.ln.Close() +} + +// Serve starts server and handle OS signals. +func (s *Server) Serve() error { + return s.server.Serve(s.ln) +} diff --git a/portal-webapi/service/updateResourceContent.go b/portal-webapi/service/updateResourceContent.go new file mode 100644 index 000000000..9e27c379a --- /dev/null +++ b/portal-webapi/service/updateResourceContent.go @@ -0,0 +1,87 @@ +package service + +import ( + "bytes" + "context" + "fmt" + "net/http" + "time" + + coap "github.com/go-ocf/go-coap" + "github.com/gofrs/uuid" + + "github.com/ugorji/go/codec" + + "github.com/go-ocf/kit/log" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + "github.com/valyala/fasthttp" +) + +func (r *RequestHandler) updateResourceContent(ctx *fasthttp.RequestCtx, token, sub string) { + log.Debugf("RequestHandler.updateResourceContent start") + t := time.Now() + defer func() { + log.Debugf("RequestHandler.updateResourceContent takes %v", time.Since(t)) + }() + var resourceId string + var ok bool + + if resourceId, ok = ctx.UserValue("resourceId").(string); !ok { + logAndWriteErrorResponse(fmt.Errorf("cannot update resource content: resourceId from uri"), http.StatusBadRequest, ctx) + return + } + + if len(ctx.Request.Body()) == 0 { + logAndWriteErrorResponse(fmt.Errorf("cannot update resource content: body is empty"), http.StatusBadRequest, ctx) + return + } + + var m interface{} + + err := codec.NewDecoderBytes(ctx.Request.Body(), new(codec.JsonHandle)).Decode(&m) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot update resource content - decode json: %v", err), http.StatusBadRequest, ctx) + return + } + + bw := bytes.NewBuffer(make([]byte, 0, 1024)) + err = codec.NewEncoder(bw, new(codec.CborHandle)).Encode(m) + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot update resource content - encode cbor: %v", err), http.StatusInternalServerError, ctx) + return + } + + correlationIdUUID, err := uuid.NewV4() + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot update resource content - generate uuid: %v", err), http.StatusBadRequest, ctx) + return + } + + correlationId := correlationIdUUID.String() + + response, err := r.raClient.UpdateResource(kitNetGrpc.CtxWithToken(context.Background(), token), &pbRA.UpdateResourceRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{ + UserId: sub, + }, + ResourceId: resourceId, + Content: &pbRA.Content{ + CoapContentFormat: int32(coap.AppOcfCbor), + ContentType: coap.AppOcfCbor.String(), + Data: bw.Bytes(), + }, + CommandMetadata: &pbCQRS.CommandMetadata{ + ConnectionId: ctx.RemoteAddr().String(), + Sequence: ctx.ConnRequestNum(), + }, + CorrelationId: correlationId, + }) + + if err != nil { + logAndWriteErrorResponse(fmt.Errorf("cannot update resource content: %v", err), http.StatusBadRequest, ctx) + return + } + + writeJson(response, fasthttp.StatusAccepted, ctx) +} diff --git a/portal-webapi/uri/uri.go b/portal-webapi/uri/uri.go new file mode 100644 index 000000000..6cbdda7c2 --- /dev/null +++ b/portal-webapi/uri/uri.go @@ -0,0 +1,10 @@ +package uri + +// Resource Service URIs. +const ( + Base string = "/api" + V1 string = Base + "/v1" + Devices string = V1 + "/devices" + Resources string = V1 + "/resources" + Healthcheck string = "/" +) diff --git a/renovate.json b/renovate.json new file mode 100644 index 000000000..f844c2ed8 --- /dev/null +++ b/renovate.json @@ -0,0 +1,15 @@ +{ + "extends": [ + "config:base" + ], + "postUpdateOptions": [ + "gomodTidy" + ], + "commitBody": "Generated by renovateBot", + "packageRules": [ + { + "packagePatterns": [".+"], + "schedule": ["on the first day of the month"] + } + ] +} diff --git a/resource-aggregate/.dockerignore b/resource-aggregate/.dockerignore new file mode 100644 index 000000000..f6f32bc9e --- /dev/null +++ b/resource-aggregate/.dockerignore @@ -0,0 +1,5 @@ +.git +.github +.gitignore +.travis.yml +Makefile diff --git a/resource-aggregate/.plantuml b/resource-aggregate/.plantuml new file mode 100644 index 000000000..9db55505c --- /dev/null +++ b/resource-aggregate/.plantuml @@ -0,0 +1,116 @@ +@startuml + +title "Publish / Unpublish Resources" + +entity "OCF Server" as Server +participant "Gateway" as Gateway +participant "Resource Aggregate" as ResourceAggregate +control "Event Bus" as Bus + +Server -> Gateway : [UPDATE] '/oic/rd' +activate Server +activate Gateway +Gateway -> ResourceAggregate : PublishResourceRequest +activate ResourceAggregate +ResourceAggregate -> Gateway : PublishResourceResponse +deactivate Gateway +ResourceAggregate -> Bus : ResourcePublished +deactivate ResourceAggregate +Gateway <- Bus: ResourcePublished +activate Gateway +Gateway -> Server : OK +deactivate Gateway +deactivate Server + +@enduml + +@startuml + +title "Resource content changed (e.g. temperature changed)" + +entity "OCF Server" as Server +participant "Gateway" as Gateway +participant "Resource Aggregate" as ResourceAggregate +control "Event Bus" as Bus + +Server -> Gateway : [NOTIFY] 'oic.r.temperature' changed +activate Gateway +Gateway -> ResourceAggregate : NotifyResourceChangedRequest +activate ResourceAggregate +ResourceAggregate -> Gateway : NotifyResourceChangedResponse +deactivate Gateway +ResourceAggregate --> Bus : ResourceChanged +deactivate ResourceAggregate + +@enduml + +@startuml + +title "Update Resource" + +entity "OCF Server" as Server +participant "Gateway" as Gateway +participant "Resource Aggregate" as ResourceAggregate +control "Event Bus" as Bus +entity "Client" as Client + +Client -> ResourceAggregate : UpdateResourceRequest +activate Client +activate ResourceAggregate +ResourceAggregate -> Client : UpdateResourceResponse +ResourceAggregate --> Bus : ResourceUpdatePending +deactivate ResourceAggregate +Bus --> Gateway : ResourceUpdatePending +activate Gateway +Gateway -> Server: [UPDATE] 'oic.r.temperature' +activate Server +Server -> Gateway : OK +deactivate Server +Gateway -> ResourceAggregate : ConfirmResourceUpdateRequest +activate ResourceAggregate +ResourceAggregate -> Gateway : ConfirmResourceUpdateResponse +deactivate Gateway +ResourceAggregate --> Bus : ResourceUpdated +deactivate ResourceAggregate +Bus --> Client : ResourceUpdated +deactivate Client + +== Resource content changed == + +Server -> Gateway : [NOTIFY] 'oic.r.temperature' changed + + +@enduml + +@startuml + +title "Retrieve Resource" + +entity "OCF Server" as Server +participant "Gateway" as Gateway +participant "Resource Aggregate" as ResourceAggregate +control "Event Bus" as Bus +entity "Client" as Client + +Client -> ResourceAggregate : RetrieveResourceRequest +activate Client +activate ResourceAggregate +ResourceAggregate -> Client : RetrieveResourceResponse +ResourceAggregate --> Bus : ResourceRetrievePending +deactivate ResourceAggregate +Bus --> Gateway : ResourceRetrievePending +activate Gateway +Gateway -> Server: [RETRIEVE] '/oic/d' +activate Server +Server -> Gateway : OK +deactivate Server +Gateway -> ResourceAggregate : ConfirmResourceRetrieveRequest +activate ResourceAggregate +ResourceAggregate -> Gateway : ConfirmResourceRetrieveResponse +deactivate Gateway +ResourceAggregate --> Bus : ResourceRetrieved +deactivate ResourceAggregate +Bus --> Client : ResourceRetrieved +deactivate Client + +@enduml \ No newline at end of file diff --git a/resource-aggregate/Dockerfile b/resource-aggregate/Dockerfile new file mode 100644 index 000000000..a6bf15787 --- /dev/null +++ b/resource-aggregate/Dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.13.5-alpine3.10 AS build +RUN apk add --no-cache curl git build-base +WORKDIR $GOPATH/src/github.com/go-ocf/cloud/resource-aggregate +COPY . . +RUN go mod download +RUN go build -o /go/bin/service ./cmd/service + +FROM alpine:3.11 as service +RUN apk add --no-cache ca-certificates +COPY --from=build /go/bin/service /usr/local/bin/service +ENTRYPOINT ["/usr/local/bin/service"] \ No newline at end of file diff --git a/resource-aggregate/Makefile b/resource-aggregate/Makefile new file mode 100644 index 000000000..93d89ea0b --- /dev/null +++ b/resource-aggregate/Makefile @@ -0,0 +1,36 @@ +SHELL = /bin/bash +SERVICE_NAME = $(notdir $(CURDIR)) +LATEST_TAG = vnext +VERSION_TAG = vnext-$(shell git rev-parse --short=7 --verify HEAD) + +default: build + +define build-docker-image + docker build \ + --network=host \ + --tag ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) \ + --tag ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) \ + --target $(1) \ + --file Dockerfile \ + .. +endef + +build-servicecontainer: + $(call build-docker-image,service) + +build: build-servicecontainer + +push: build-servicecontainer + docker push ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) + docker push ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) + +proto/generate: + protoc -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf --gogofaster_out=${GOPATH}/src pb/commands.proto + protoc -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf --gogofaster_out=${GOPATH}/src pb/events.proto + protoc -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf --gogofaster_out=${GOPATH}/src pb/resources.proto + + protoc -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf --go_out=plugins=grpc:${GOPATH}/src pb/service.proto + goderive ./... + +.PHONY: build-servicecontainer build push clean proto/generate + diff --git a/resource-aggregate/README.md b/resource-aggregate/README.md new file mode 100644 index 000000000..ee6e47310 --- /dev/null +++ b/resource-aggregate/README.md @@ -0,0 +1,56 @@ +[![Go Report](https://goreportcard.com/badge/github.com/go-ocf/cloud/resource-aggregate)](https://goreportcard.com/report/github.com/go-ocf/cloud/resource-aggregate) +[![Gitter](https://badges.gitter.im/ocfcloud/Lobby.svg)](https://gitter.im/ocfcloud/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +# resource - aggregate + +## service initialization +Service can be initialized with different Databases(EventStore) or Message systems(Publisher) via function `New`. For example `cmd/service` it uses mongodb/kafka. To initialize package with other eventstore/publisher it must satisfy interfaces: + +# Build + +## Docker + +```sh +make build-servicecontainer +``` +## Local machine + +```sh +dep ensure -v --vendor-only +go build ./cmd/coap-gateway-service/ +``` + +## Configuration +| Option | ENV variable | Type | Description | Default | +| ------ | --------- | ----------- | ------- | ------- | +| `-` | `ADDRESS` | string | tbd | `"0.0.0.0:9100"` | +| `-` | `AUTH_SERVER_ADDRESS` | string | tbd | `"127.0.0.1:9100"` | +| `-` | `SNAPSHOT_THRESHOLD` | int | tbd | `128` | +| `-` | `OCC_MAX_RETRY` | int | tbd | `8` | +| `-` | `NATS_URL` | string | tbd | `"nats://localhost:4222"` | +| `-` | `MONGODB_URI` | string | tbd | `"mongodb://localhost:27017"` | +| `-` | `MONGODB_DATABASE` | string | tbd | `"eventstore"` | +| `-` | `MONGODB_BATCH_SIZE` | int | tbd | `16` | +| `-` | `MONGODB_MAX_POOL_SIZE` | int | tbd | `16` | +| `-` | `MONGODB_MAX_CONN_IDLE_TIME` | string | tbd | `"240s"` | +| `-` | `DIAL_TYPE` | string | tbd | `"acme"` | +| `-` | `DIAL_ACME_CA_POOL` | string | tbd | `""` | +| `-` | `DIAL_ACME_DIRECTORY_URL` | string | tbd | `""` | +| `-` | `DIAL_ACME_DOMAINS` | string | tbd | `""` | +| `-` | `DIAL_ACME_REGISTRATION_EMAIL` | string | tbd | `""` | +| `-` | `DIAL_ACME_TICK_FREQUENCY` | string | tbd | `""` | +| `-` | `DIAL_FILE_CA_POOL` | string | tbd | `""` | +| `-` | `DIAL_FILE_CERT_KEY_NAME` | string | tbd | `""` | +| `-` | `DIAL_FILE_CERT_DIR_PATH` | string | tbd | `""` | +| `-` | `DIAL_FILE_CERT_NAME` | string | tbd | `""` | +| `-` | `LISTEN_TYPE` | string | tbd | `"acme"` | +| `-` | `LISTEN_ACME_CA_POOL` | string | tbd | `""` | +| `-` | `LISTEN_ACME_DIRECTORY_URL` | string | tbd | `""` | +| `-` | `LISTEN_ACME_DOMAINS` | string | tbd | `""` | +| `-` | `LISTEN_ACME_REGISTRATION_EMAIL` | string | tbd | `""` | +| `-` | `LISTEN_ACME_TICK_FREQUENCY` | string | tbd | `""` | +| `-` | `LISTEN_FILE_CA_POOL` | string | tbd | `""` | +| `-` | `LISTEN_FILE_CERT_KEY_NAME` | string | tbd | `""` | +| `-` | `LISTEN_FILE_CERT_DIR_PATH` | string | tbd | `""` | +| `-` | `LISTEN_FILE_CERT_NAME` | string | tbd | `""` | +| `-` | `LOG_ENABLE_DEBUG` | bool | tbd | `false` | \ No newline at end of file diff --git a/resource-aggregate/cmd/maintenance/maintenance.go b/resource-aggregate/cmd/maintenance/maintenance.go new file mode 100644 index 000000000..05b236bec --- /dev/null +++ b/resource-aggregate/cmd/maintenance/maintenance.go @@ -0,0 +1,15 @@ +package main + +import ( + "os" + + "github.com/go-ocf/kit/log" + "github.com/go-ocf/cloud/resource-aggregate/refImpl/maintenance" +) + +func main() { + if err := maintenance.PerformMaintenance(); err != nil { + log.Error(err) + os.Exit(2) + } +} diff --git a/resource-aggregate/cmd/service/main.go b/resource-aggregate/cmd/service/main.go new file mode 100644 index 000000000..b36cfed49 --- /dev/null +++ b/resource-aggregate/cmd/service/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "github.com/go-ocf/kit/log" + "github.com/go-ocf/cloud/resource-aggregate/refImpl" + "github.com/kelseyhightower/envconfig" +) + +func main() { + var config refImpl.Config + if err := envconfig.Process("", &config); err != nil { + log.Fatalf("cannot parse configuration: %v", err) + } + if server, err := refImpl.Init(config); err != nil { + log.Fatalf("cannot init server: %v", err) + } else { + if err = server.Serve(); err != nil { + log.Fatalf("unexpected ends: %v", err) + } + server.Shutdown() + } +} diff --git a/resource-aggregate/cqrs/eventbus/nats/config.go b/resource-aggregate/cqrs/eventbus/nats/config.go new file mode 100644 index 000000000..449950f99 --- /dev/null +++ b/resource-aggregate/cqrs/eventbus/nats/config.go @@ -0,0 +1,31 @@ +package nats + +import ( + "crypto/tls" + "encoding/json" + "fmt" + + nats "github.com/nats-io/nats.go" +) + +// Option provides the means to use function call chaining +type Option func(Config) Config + +type Config struct { + URL string `envconfig:"URL" default:"nats://localhost:4222"` + Options []nats.Option +} + +// String returns string representation of Config. +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} + +// WithTLS configures connection to use TLS +func WithTLS(cfg *tls.Config) Option { + return func(c Config) Config { + c.Options = append(c.Options, nats.Secure(cfg)) + return c + } +} diff --git a/resource-aggregate/cqrs/eventbus/nats/publisher.go b/resource-aggregate/cqrs/eventbus/nats/publisher.go new file mode 100644 index 000000000..20af17583 --- /dev/null +++ b/resource-aggregate/cqrs/eventbus/nats/publisher.go @@ -0,0 +1,30 @@ +package nats + +import ( + cqrsNats "github.com/go-ocf/cqrs/eventbus/nats" + cqrsUtils "github.com/go-ocf/cloud/resource-aggregate/cqrs" +) + +type Publisher struct { + *cqrsNats.Publisher +} + +// NewPublisher creates new publisher with proto marshaller. +func NewPublisher(config Config, opts ...Option) (*Publisher, error) { + for _, o := range opts { + config = o(config) + } + + p, err := cqrsNats.NewPublisher(config.URL, cqrsUtils.Marshal, config.Options...) + if err != nil { + return nil, err + } + return &Publisher{ + p, + }, nil +} + +// Close closes the publisher. +func (p *Publisher) Close() { + p.Publisher.Close() +} diff --git a/resource-aggregate/cqrs/eventbus/nats/publisher_test.go b/resource-aggregate/cqrs/eventbus/nats/publisher_test.go new file mode 100644 index 000000000..8f2fb3dc6 --- /dev/null +++ b/resource-aggregate/cqrs/eventbus/nats/publisher_test.go @@ -0,0 +1,28 @@ +package nats + +import ( + "testing" + + "github.com/go-ocf/kit/security/certManager" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewPublisher(t *testing.T) { + var config certManager.Config + err := envconfig.Process("DIAL", &config) + assert.NoError(t, err) + + dialCertManager, err := certManager.NewCertManager(config) + require.NoError(t, err) + + tlsConfig := dialCertManager.GetClientTLSConfig() + + bus, err := NewPublisher(Config{ + URL: "nats://localhost:4222", + }, WithTLS(&tlsConfig)) + require.NoError(t, err) + assert.NotNil(t, bus) + defer bus.Close() +} diff --git a/resource-aggregate/cqrs/eventbus/nats/subscriber.go b/resource-aggregate/cqrs/eventbus/nats/subscriber.go new file mode 100644 index 000000000..4bc22ecd2 --- /dev/null +++ b/resource-aggregate/cqrs/eventbus/nats/subscriber.go @@ -0,0 +1,31 @@ +package nats + +import ( + cqrsEventBus "github.com/go-ocf/cqrs/eventbus" + cqrsNats "github.com/go-ocf/cqrs/eventbus/nats" + cqrsUtils "github.com/go-ocf/cloud/resource-aggregate/cqrs" +) + +type Subscriber struct { + *cqrsNats.Subscriber +} + +// NewSubscriber create new subscriber with proto unmarshaller. +func NewSubscriber(config Config, goroutinePoolGo cqrsEventBus.GoroutinePoolGoFunc, errFunc cqrsEventBus.ErrFunc, opts ...Option) (*Subscriber, error) { + for _, o := range opts { + config = o(config) + } + + s, err := cqrsNats.NewSubscriber(config.URL, cqrsUtils.Unmarshal, goroutinePoolGo, errFunc, config.Options...) + if err != nil { + return nil, err + } + return &Subscriber{ + s, + }, nil +} + +// Close closes the publisher. +func (p *Subscriber) Close() { + p.Subscriber.Close() +} diff --git a/resource-aggregate/cqrs/events/resourceChanged.go b/resource-aggregate/cqrs/events/resourceChanged.go new file mode 100644 index 000000000..893c3ba73 --- /dev/null +++ b/resource-aggregate/cqrs/events/resourceChanged.go @@ -0,0 +1,30 @@ +package events + +import ( + "github.com/go-ocf/kit/net/http" + "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +type ResourceChanged struct { + pb.ResourceChanged +} + +func (e ResourceChanged) Version() uint64 { + return e.EventMetadata.Version +} + +func (e ResourceChanged) Marshal() ([]byte, error) { + return e.ResourceChanged.Marshal() +} + +func (e *ResourceChanged) Unmarshal(b []byte) error { + return e.ResourceChanged.Unmarshal(b) +} + +func (e ResourceChanged) EventType() string { + return http.ProtobufContentType(&pb.ResourceChanged{}) +} + +func (e ResourceChanged) AggregateId() string { + return e.Id +} diff --git a/resource-aggregate/cqrs/events/resourcePublished.go b/resource-aggregate/cqrs/events/resourcePublished.go new file mode 100644 index 000000000..1384a593d --- /dev/null +++ b/resource-aggregate/cqrs/events/resourcePublished.go @@ -0,0 +1,30 @@ +package events + +import ( + "github.com/go-ocf/kit/net/http" + "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +type ResourcePublished struct { + pb.ResourcePublished +} + +func (e ResourcePublished) Version() uint64 { + return e.EventMetadata.Version +} + +func (e ResourcePublished) Marshal() ([]byte, error) { + return e.ResourcePublished.Marshal() +} + +func (e *ResourcePublished) Unmarshal(b []byte) error { + return e.ResourcePublished.Unmarshal(b) +} + +func (e ResourcePublished) EventType() string { + return http.ProtobufContentType(&pb.ResourcePublished{}) +} + +func (e ResourcePublished) AggregateId() string { + return e.Id +} diff --git a/resource-aggregate/cqrs/events/resourceRetrievePending.go b/resource-aggregate/cqrs/events/resourceRetrievePending.go new file mode 100644 index 000000000..0fdd57841 --- /dev/null +++ b/resource-aggregate/cqrs/events/resourceRetrievePending.go @@ -0,0 +1,30 @@ +package events + +import ( + "github.com/go-ocf/kit/net/http" + "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +type ResourceRetrievePending struct { + pb.ResourceRetrievePending +} + +func (e ResourceRetrievePending) Version() uint64 { + return e.EventMetadata.Version +} + +func (e ResourceRetrievePending) Marshal() ([]byte, error) { + return e.ResourceRetrievePending.Marshal() +} + +func (e *ResourceRetrievePending) Unmarshal(b []byte) error { + return e.ResourceRetrievePending.Unmarshal(b) +} + +func (e ResourceRetrievePending) EventType() string { + return http.ProtobufContentType(&pb.ResourceRetrievePending{}) +} + +func (e ResourceRetrievePending) AggregateId() string { + return e.Id +} diff --git a/resource-aggregate/cqrs/events/resourceRetrieved.go b/resource-aggregate/cqrs/events/resourceRetrieved.go new file mode 100644 index 000000000..fa53d4664 --- /dev/null +++ b/resource-aggregate/cqrs/events/resourceRetrieved.go @@ -0,0 +1,30 @@ +package events + +import ( + "github.com/go-ocf/kit/net/http" + "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +type ResourceRetrieved struct { + pb.ResourceRetrieved +} + +func (e ResourceRetrieved) Version() uint64 { + return e.EventMetadata.Version +} + +func (e ResourceRetrieved) Marshal() ([]byte, error) { + return e.ResourceRetrieved.Marshal() +} + +func (e *ResourceRetrieved) Unmarshal(b []byte) error { + return e.ResourceRetrieved.Unmarshal(b) +} + +func (e ResourceRetrieved) EventType() string { + return http.ProtobufContentType(&pb.ResourceRetrieved{}) +} + +func (e ResourceRetrieved) AggregateId() string { + return e.Id +} diff --git a/resource-aggregate/cqrs/events/resourceStateSnapshotTaken.go b/resource-aggregate/cqrs/events/resourceStateSnapshotTaken.go new file mode 100644 index 000000000..d8a1d7e0f --- /dev/null +++ b/resource-aggregate/cqrs/events/resourceStateSnapshotTaken.go @@ -0,0 +1,476 @@ +package events + +import ( + "context" + "fmt" + + "github.com/go-ocf/kit/net/grpc" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/go-ocf/go-coap" + + "github.com/go-ocf/cqrs" + "github.com/go-ocf/cqrs/event" + "github.com/go-ocf/kit/codec/cbor" + "github.com/go-ocf/kit/codec/json" + "github.com/go-ocf/kit/net/http" + cqrsUtils "github.com/go-ocf/cloud/resource-aggregate/cqrs" + "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +type VerifyAccessFunc func(deviceId, resourceId string) error + +type ResourceStateSnapshotTaken struct { + pb.ResourceStateSnapshotTaken + mapForCalculatePendingRequestsCount map[string]bool + verifyAccess VerifyAccessFunc +} + +func (rs *ResourceStateSnapshotTaken) AggregateId() string { + return rs.Id +} + +func (rs *ResourceStateSnapshotTaken) GroupId() string { + return rs.Resource.DeviceId +} + +func (rs *ResourceStateSnapshotTaken) Version() uint64 { + return rs.ResourceStateSnapshotTaken.EventMetadata.Version +} + +func (rs *ResourceStateSnapshotTaken) Marshal() ([]byte, error) { + return rs.ResourceStateSnapshotTaken.Marshal() +} + +func (rs *ResourceStateSnapshotTaken) Unmarshal(b []byte) error { + return rs.ResourceStateSnapshotTaken.Unmarshal(b) +} + +func (rs *ResourceStateSnapshotTaken) EventType() string { + return http.ProtobufContentType(&pb.ResourceStateSnapshotTaken{}) +} + +func (rs *ResourceStateSnapshotTaken) HandleEventResourcePublished(ctx context.Context, pub ResourcePublished) error { + rs.Id = pub.Resource.Id + if rs.IsPublished { + pub.Resource.InstanceId = rs.Resource.InstanceId + } + rs.Resource = pub.Resource + rs.TimeToLive = pub.TimeToLive + rs.IsPublished = true + return nil +} + +func (rs *ResourceStateSnapshotTaken) HandleEventResourceUnpublished(ctx context.Context, pub ResourceUnpublished) error { + if !rs.IsPublished { + return status.Errorf(codes.FailedPrecondition, "resource is already unpublished") + } + rs.IsPublished = false + return nil +} + +func (rs *ResourceStateSnapshotTaken) HandleEventResourceUpdatePending(ctx context.Context, contentUpdatePending ResourceUpdatePending) error { + rs.mapForCalculatePendingRequestsCount[contentUpdatePending.AuditContext.CorrelationId] = true + return nil +} + +func (rs *ResourceStateSnapshotTaken) HandleEventResourceRetrievePending(ctx context.Context, contentRetrievePending ResourceRetrievePending) error { + if !rs.IsPublished { + return status.Errorf(codes.FailedPrecondition, "resource is unpublished") + } + return nil +} + +func (rs *ResourceStateSnapshotTaken) HandleEventResourceUpdated(ctx context.Context, contentUpdateProcessed ResourceUpdated) error { + delete(rs.mapForCalculatePendingRequestsCount, contentUpdateProcessed.AuditContext.CorrelationId) + rs.PendingRequestsCount = uint32(len(rs.mapForCalculatePendingRequestsCount)) + return nil +} + +func (rs *ResourceStateSnapshotTaken) HandleEventResourceRetrieved(ctx context.Context, contentUpdateProcessed ResourceRetrieved) error { + return nil +} + +func (rs *ResourceStateSnapshotTaken) ValidateSequence(eventMetadata *pb.EventMetadata) bool { + if rs.LatestResourceChange == nil { + return true + } + if rs.GetLatestResourceChange().GetEventMetadata().GetConnectionId() != eventMetadata.GetConnectionId() { + return true + } + if rs.GetLatestResourceChange().GetEventMetadata().GetSequence() < eventMetadata.GetSequence() { + return true + } + return false +} + +func (rs *ResourceStateSnapshotTaken) HandleEventResourceChanged(ctx context.Context, contentChanged ResourceChanged) (bool, error) { + if rs.ValidateSequence(contentChanged.EventMetadata) { + rs.LatestResourceChange = &contentChanged.ResourceChanged + return true, nil + } + return false, nil +} + +func (rs *ResourceStateSnapshotTaken) HandleEventResourceStateSnapshotTaken(ctx context.Context, s ResourceStateSnapshotTaken) error { + if s.PendingRequestsCount != 0 { + return status.Errorf(codes.FailedPrecondition, "invalid pending requests") + } + rs.Id = s.Resource.Id + rs.Resource = s.Resource + rs.LatestResourceChange = s.LatestResourceChange + rs.TimeToLive = s.TimeToLive + rs.IsPublished = s.IsPublished + rs.EventMetadata = s.EventMetadata + + return nil +} + +func (rs *ResourceStateSnapshotTaken) Handle(ctx context.Context, iter event.Iter) error { + var eu event.EventUnmarshaler + for iter.Next(ctx, &eu) { + if eu.EventType == "" { + return status.Errorf(codes.Internal, "cannot determine type of event") + } + err := rs.verifyAccess(eu.GroupId, eu.AggregateId) + if err != nil { + return grpc.ForwardErrorf(codes.Unauthenticated, "unauthorized access to resource: %v", err) + } + switch eu.EventType { + case http.ProtobufContentType(&pb.ResourceStateSnapshotTaken{}): + var s ResourceStateSnapshotTaken + if err := eu.Unmarshal(&s); err != nil { + return status.Errorf(codes.Internal, "%v", err) + } + if err := rs.HandleEventResourceStateSnapshotTaken(ctx, s); err != nil { + return err + } + case http.ProtobufContentType(&pb.ResourcePublished{}): + var s ResourcePublished + if err := eu.Unmarshal(&s); err != nil { + return status.Errorf(codes.Internal, "%v", err) + } + if err := rs.HandleEventResourcePublished(ctx, s); err != nil { + return err + } + case http.ProtobufContentType(&pb.ResourceUnpublished{}): + var s ResourceUnpublished + if err := eu.Unmarshal(&s); err != nil { + return status.Errorf(codes.Internal, "%v", err) + } + if err := rs.HandleEventResourceUnpublished(ctx, s); err != nil { + return err + } + case http.ProtobufContentType(&pb.ResourceUpdatePending{}): + var s ResourceUpdatePending + if err := eu.Unmarshal(&s); err != nil { + return status.Errorf(codes.Internal, "%v", err) + } + if err := rs.HandleEventResourceUpdatePending(ctx, s); err != nil { + return err + } + case http.ProtobufContentType(&pb.ResourceUpdated{}): + var s ResourceUpdated + if err := eu.Unmarshal(&s); err != nil { + return status.Errorf(codes.Internal, "%v", err) + } + if err := rs.HandleEventResourceUpdated(ctx, s); err != nil { + return err + } + case http.ProtobufContentType(&pb.ResourceChanged{}): + var s ResourceChanged + if err := eu.Unmarshal(&s); err != nil { + return status.Errorf(codes.Internal, "%v", err) + } + if _, err := rs.HandleEventResourceChanged(ctx, s); err != nil { + return err + } + case http.ProtobufContentType(&pb.ResourceRetrieved{}): + case http.ProtobufContentType(&pb.ResourceRetrievePending{}): + } + } + return iter.Err() +} + +func convertContent(content *pb.Content, supportedContentTypes []string) (newContent *pb.Content, err error) { + contentType := content.ContentType + coapContentFormat := int32(-1) + if len(supportedContentTypes) == 0 { + supportedContentTypes = []string{coap.AppOcfCbor.String()} + } + if content.CoapContentFormat >= 0 && contentType == "" { + contentType = coap.MediaType(content.CoapContentFormat).String() + } + var encode func(v interface{}) ([]byte, error) + for _, supportedContentType := range supportedContentTypes { + switch supportedContentType { + case contentType: + return content, nil + case coap.AppCBOR.String(): + encode = cbor.Encode + coapContentFormat = int32(coap.AppCBOR) + case coap.AppOcfCbor.String(): + if encode == nil { + encode = cbor.Encode + coapContentFormat = int32(coap.AppOcfCbor) + } + case coap.AppJSON.String(): + if encode == nil { + encode = json.Encode + coapContentFormat = int32(coap.AppJSON) + } + } + + } + + if encode == nil { + return nil, status.Errorf(codes.InvalidArgument, "cannot convert content-type from %v: unknown target", contentType) + } + + var decode func(in []byte, v interface{}) error + switch contentType { + case coap.AppCBOR.String(), coap.AppOcfCbor.String(): + decode = cbor.Decode + case coap.AppJSON.String(): + decode = json.Decode + default: + return nil, status.Errorf(codes.InvalidArgument, "cannot convert content-type from %v: unsupported source", contentType) + } + + var m interface{} + err = decode(content.Data, &m) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "cannot decode content data from %v: %v", contentType, err) + } + + data, err := encode(m) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "cannot encode content data to %v: %v", coap.MediaType(coapContentFormat).String(), err) + } + return &pb.Content{ + CoapContentFormat: coapContentFormat, + ContentType: coap.MediaType(coapContentFormat).String(), + Data: data, + }, nil +} + +func (rs *ResourceStateSnapshotTaken) HandleCommand(ctx context.Context, cmd cqrs.Command, newVersion uint64) ([]event.Event, error) { + switch req := cmd.(type) { + case *pb.PublishResourceRequest: + if rs.Id != req.ResourceId && rs.Id != "" { + return nil, status.Errorf(codes.Internal, "cannot handle resource publish: invalid resource id") + } + if req.CommandMetadata == nil { + return nil, status.Errorf(codes.InvalidArgument, "cannot handle resource publish: invalid command metadata") + } + + em := cqrsUtils.MakeEventMeta(req.CommandMetadata.ConnectionId, req.CommandMetadata.Sequence, newVersion) + ac := cqrsUtils.MakeAuditContext(req.AuthorizationContext, "") + + rp := ResourcePublished{pb.ResourcePublished{ + Id: req.ResourceId, + Resource: req.Resource, + TimeToLive: req.TimeToLive, + AuditContext: &ac, + EventMetadata: &em, + }, + } + err := rs.HandleEventResourcePublished(ctx, rp) + if err != nil { + return nil, fmt.Errorf("cannot handle resource publish: %w", err) + } + return []event.Event{rp}, nil + case *pb.UnpublishResourceRequest: + if newVersion == 0 { + return nil, status.Errorf(codes.NotFound, "cannot handle resource unpublish: invalid version for events") + } + if rs.Id != req.ResourceId { + return nil, status.Errorf(codes.Internal, "cannot handle resource unpublish: invalid resource id") + } + if req.CommandMetadata == nil { + return nil, status.Errorf(codes.InvalidArgument, "cannot handle resource unpublish: invalid command metadata") + } + + em := cqrsUtils.MakeEventMeta(req.CommandMetadata.ConnectionId, req.CommandMetadata.Sequence, newVersion) + ac := cqrsUtils.MakeAuditContext(req.AuthorizationContext, "") + ru := ResourceUnpublished{pb.ResourceUnpublished{ + Id: req.ResourceId, + AuditContext: &ac, + EventMetadata: &em, + }} + err := rs.HandleEventResourceUnpublished(ctx, ru) + if err != nil { + return nil, fmt.Errorf("cannot handle resource unpublish: %w", err) + } + return []event.Event{ru}, nil + case *pb.NotifyResourceChangedRequest: + if newVersion == 0 { + return nil, status.Errorf(codes.NotFound, "cannot handle notify resource changed: invalid version for events") + } + if rs.Id != req.ResourceId { + return nil, status.Errorf(codes.Internal, "cannot handle notify resource changed: invalid resource id") + } + if req.CommandMetadata == nil { + return nil, status.Errorf(codes.InvalidArgument, "cannot handle notify resource changed: invalid command metadata") + } + + em := cqrsUtils.MakeEventMeta(req.CommandMetadata.ConnectionId, req.CommandMetadata.Sequence, newVersion) + ac := cqrsUtils.MakeAuditContext(req.AuthorizationContext, "") + + rc := ResourceChanged{ + pb.ResourceChanged{ + Id: req.ResourceId, + AuditContext: &ac, + EventMetadata: &em, + Content: req.Content, + Status: req.Status, + }, + } + var ok bool + var err error + if ok, err = rs.HandleEventResourceChanged(ctx, rc); err != nil { + return nil, fmt.Errorf("cannot handle notify resource changed: %w", err) + } + if ok { + return []event.Event{rc}, nil + } + return nil, nil + case *pb.UpdateResourceRequest: + if newVersion == 0 { + return nil, status.Errorf(codes.NotFound, "cannot handle update resource content invalid version for events") + } + if rs.Id != req.GetResourceId() { + return nil, status.Errorf(codes.Internal, "cannot handle update resource content: invalid resource id") + } + if req.CommandMetadata == nil { + return nil, status.Errorf(codes.InvalidArgument, "cannot handle update resource content: invalid command metadata") + } + + em := cqrsUtils.MakeEventMeta(req.CommandMetadata.ConnectionId, req.CommandMetadata.Sequence, newVersion) + ac := cqrsUtils.MakeAuditContext(req.AuthorizationContext, req.CorrelationId) + content, err := convertContent(req.Content, rs.Resource.SupportedContentTypes) + if err != nil { + return nil, fmt.Errorf("cannot handle notify resource changed: %w", err) + } + + rc := ResourceUpdatePending{ + pb.ResourceUpdatePending{ + Id: req.GetResourceId(), + ResourceInterface: req.GetResourceInterface(), + AuditContext: &ac, + EventMetadata: &em, + Content: content, + }, + } + + if err = rs.HandleEventResourceUpdatePending(ctx, rc); err != nil { + return nil, fmt.Errorf("cannot handle update resource content: %w", err) + } + return []event.Event{rc}, nil + case *pb.ConfirmResourceUpdateRequest: + if newVersion == 0 { + return nil, status.Errorf(codes.NotFound, "cannot handle notify resource content update processed: invalid version for events") + } + if rs.Id != req.ResourceId { + return nil, status.Errorf(codes.Internal, "cannot handle notify resource content update processed: invalid resource id") + } + if req.CommandMetadata == nil { + return nil, status.Errorf(codes.InvalidArgument, "cannot handle notify resource content update processed: invalid command metadata") + } + + em := cqrsUtils.MakeEventMeta(req.CommandMetadata.ConnectionId, req.CommandMetadata.Sequence, newVersion) + ac := cqrsUtils.MakeAuditContext(req.AuthorizationContext, req.CorrelationId) + rc := ResourceUpdated{ + pb.ResourceUpdated{ + Id: req.ResourceId, + AuditContext: &ac, + EventMetadata: &em, + Content: req.Content, + Status: req.Status, + }, + } + if err := rs.HandleEventResourceUpdated(ctx, rc); err != nil { + return nil, fmt.Errorf("cannot handle notify resource content update processed: %w", err) + } + return []event.Event{rc}, nil + case *pb.RetrieveResourceRequest: + if newVersion == 0 { + return nil, status.Errorf(codes.NotFound, "cannot handle retrieve resource content invalid version for events") + } + if rs.Id != req.GetResourceId() { + return nil, status.Errorf(codes.Internal, "cannot handle retrieve resource content: invalid resource id") + } + if req.GetCommandMetadata() == nil { + return nil, status.Errorf(codes.InvalidArgument, "cannot handle retrieve resource content: invalid command metadata") + } + + em := cqrsUtils.MakeEventMeta(req.GetCommandMetadata().GetConnectionId(), req.GetCommandMetadata().GetSequence(), newVersion) + ac := cqrsUtils.MakeAuditContext(req.GetAuthorizationContext(), req.CorrelationId) + + rc := ResourceRetrievePending{ + pb.ResourceRetrievePending{ + Id: req.GetResourceId(), + ResourceInterface: req.GetResourceInterface(), + AuditContext: &ac, + EventMetadata: &em, + }, + } + + if err := rs.HandleEventResourceRetrievePending(ctx, rc); err != nil { + return nil, fmt.Errorf("cannot handle retrieve resource content: %w", err) + } + return []event.Event{rc}, nil + case *pb.ConfirmResourceRetrieveRequest: + if newVersion == 0 { + return nil, status.Errorf(codes.NotFound, "cannot handle notify resource content retrieve processed: invalid version for events") + } + if rs.Id != req.GetResourceId() { + return nil, status.Errorf(codes.Internal, "cannot handle notify resource content retrieve processed: invalid resource id") + } + if req.GetCommandMetadata() == nil { + return nil, status.Errorf(codes.InvalidArgument, "cannot handle notify resource content retrieve processed: invalid command metadata") + } + + em := cqrsUtils.MakeEventMeta(req.GetCommandMetadata().GetConnectionId(), req.GetCommandMetadata().GetSequence(), newVersion) + ac := cqrsUtils.MakeAuditContext(req.GetAuthorizationContext(), req.GetCorrelationId()) + rc := ResourceRetrieved{ + pb.ResourceRetrieved{ + Id: req.GetResourceId(), + AuditContext: &ac, + EventMetadata: &em, + Content: req.GetContent(), + Status: req.GetStatus(), + }, + } + if err := rs.HandleEventResourceRetrieved(ctx, rc); err != nil { + return nil, fmt.Errorf("cannot handle notify resource content retrieve processed: %w", err) + } + return []event.Event{rc}, nil + } + + return nil, fmt.Errorf("unknown command") +} + +func (rs *ResourceStateSnapshotTaken) SnapshotEventType() string { return rs.EventType() } + +func (rs *ResourceStateSnapshotTaken) TakeSnapshot(version uint64) (event.Event, bool) { + if rs.PendingRequestsCount > 0 { + return nil, false + } + rs.EventMetadata.Version = version + return rs, true +} + +func NewResourceStateSnapshotTaken(verifyAccess VerifyAccessFunc) *ResourceStateSnapshotTaken { + + return &ResourceStateSnapshotTaken{ + ResourceStateSnapshotTaken: pb.ResourceStateSnapshotTaken{ + Resource: &pb.Resource{}, + EventMetadata: &pb.EventMetadata{}, + }, + mapForCalculatePendingRequestsCount: make(map[string]bool), + verifyAccess: verifyAccess, + } +} diff --git a/resource-aggregate/cqrs/events/resourceUnpublished.go b/resource-aggregate/cqrs/events/resourceUnpublished.go new file mode 100644 index 000000000..dc4591c95 --- /dev/null +++ b/resource-aggregate/cqrs/events/resourceUnpublished.go @@ -0,0 +1,30 @@ +package events + +import ( + "github.com/go-ocf/kit/net/http" + "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +type ResourceUnpublished struct { + pb.ResourceUnpublished +} + +func (e ResourceUnpublished) Version() uint64 { + return e.EventMetadata.Version +} + +func (e ResourceUnpublished) Marshal() ([]byte, error) { + return e.ResourceUnpublished.Marshal() +} + +func (e *ResourceUnpublished) Unmarshal(b []byte) error { + return e.ResourceUnpublished.Unmarshal(b) +} + +func (e ResourceUnpublished) EventType() string { + return http.ProtobufContentType(&pb.ResourceUnpublished{}) +} + +func (e ResourceUnpublished) AggregateId() string { + return e.Id +} diff --git a/resource-aggregate/cqrs/events/resourceUpdatePending.go b/resource-aggregate/cqrs/events/resourceUpdatePending.go new file mode 100644 index 000000000..f97f62336 --- /dev/null +++ b/resource-aggregate/cqrs/events/resourceUpdatePending.go @@ -0,0 +1,30 @@ +package events + +import ( + "github.com/go-ocf/kit/net/http" + "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +type ResourceUpdatePending struct { + pb.ResourceUpdatePending +} + +func (e ResourceUpdatePending) Version() uint64 { + return e.EventMetadata.Version +} + +func (e ResourceUpdatePending) Marshal() ([]byte, error) { + return e.ResourceUpdatePending.Marshal() +} + +func (e *ResourceUpdatePending) Unmarshal(b []byte) error { + return e.ResourceUpdatePending.Unmarshal(b) +} + +func (e ResourceUpdatePending) EventType() string { + return http.ProtobufContentType(&pb.ResourceUpdatePending{}) +} + +func (e ResourceUpdatePending) AggregateId() string { + return e.Id +} diff --git a/resource-aggregate/cqrs/events/resourceUpdated.go b/resource-aggregate/cqrs/events/resourceUpdated.go new file mode 100644 index 000000000..2be3271b4 --- /dev/null +++ b/resource-aggregate/cqrs/events/resourceUpdated.go @@ -0,0 +1,30 @@ +package events + +import ( + "github.com/go-ocf/kit/net/http" + "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +type ResourceUpdated struct { + pb.ResourceUpdated +} + +func (e ResourceUpdated) Version() uint64 { + return e.EventMetadata.Version +} + +func (e ResourceUpdated) Marshal() ([]byte, error) { + return e.ResourceUpdated.Marshal() +} + +func (e *ResourceUpdated) Unmarshal(b []byte) error { + return e.ResourceUpdated.Unmarshal(b) +} + +func (e ResourceUpdated) EventType() string { + return http.ProtobufContentType(&pb.ResourceUpdated{}) +} + +func (e ResourceUpdated) AggregateId() string { + return e.Id +} diff --git a/resource-aggregate/cqrs/eventstore/mongodb/config.go b/resource-aggregate/cqrs/eventstore/mongodb/config.go new file mode 100644 index 000000000..f2e991a22 --- /dev/null +++ b/resource-aggregate/cqrs/eventstore/mongodb/config.go @@ -0,0 +1,55 @@ +package mongodb + +import ( + "crypto/tls" + "encoding/json" + "fmt" + "time" + + "github.com/go-ocf/cqrs/event" +) + +// Option provides the means to use function call chaining +type Option func(Config) Config + +// WithMarshaler provides the possibility to set an marshaling function for the config +func WithMarshaler(f event.MarshalerFunc) Option { + return func(cfg Config) Config { + cfg.marshalerFunc = f + return cfg + } +} + +// WithUnmarshaler provides the possibility to set an unmarshaling function for the config +func WithUnmarshaler(f event.UnmarshalerFunc) Option { + return func(cfg Config) Config { + cfg.unmarshalerFunc = f + return cfg + } +} + +// WithTLS configures connection to use TLS +func WithTLS(cfg *tls.Config) Option { + return func(c Config) Config { + c.tlsCfg = cfg + return c + } +} + +// Config provides Mongo DB configuration options +type Config struct { + URI string `long:"uri" env:"URI" envconfig:"URI" default:"mongodb://localhost:27017"` + DatabaseName string `long:"dbName" env:"DATABASE" envconfig:"DATABASE" default:"eventStore"` + BatchSize int `long:"batchSize" env:"BATCH_SIZE" envconfig:"BATCH_SIZE" default:"16"` + MaxPoolSize uint64 `long:"maxPoolSize" env:"MAX_POOL_SIZE" envconfig:"MAX_POOL_SIZE" default:"16"` + MaxConnIdleTime time.Duration `long:"maxConnIdleTime" env:"MAX_CONN_IDLE_TIME" envconfig:"MAX_CONN_IDLE_TIME" default:"240s"` + tlsCfg *tls.Config + marshalerFunc event.MarshalerFunc + unmarshalerFunc event.UnmarshalerFunc +} + +//String return string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} diff --git a/resource-aggregate/cqrs/eventstore/mongodb/eventstore.go b/resource-aggregate/cqrs/eventstore/mongodb/eventstore.go new file mode 100644 index 000000000..1f63bd8a2 --- /dev/null +++ b/resource-aggregate/cqrs/eventstore/mongodb/eventstore.go @@ -0,0 +1,181 @@ +// Copyright (c) 2015 - The Event Horizon authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mongodb + +import ( + "context" + "fmt" + "math/rand" + "time" + + "github.com/go-ocf/cqrs/eventstore/maintenance" + + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/mongo/readpref" + + "github.com/go-ocf/cqrs/event" + "github.com/go-ocf/cqrs/eventstore" + cqrsMongodb "github.com/go-ocf/cqrs/eventstore/mongodb" + "github.com/go-ocf/kit/log" + cqrsUtils "github.com/go-ocf/cloud/resource-aggregate/cqrs" +) + +const instanceIdsCollection = "instanceIds" + +func init() { + rand.Seed(time.Now().UTC().UnixNano()) +} + +// EventStore implements an EventStore for MongoDB. +type EventStore struct { + es *cqrsMongodb.EventStore + client *mongo.Client + config Config + + uniqueIdIsInitialized uint64 +} + +//NewEventStore create a event store from configuration +func NewEventStore(config Config, goroutinePoolGo eventstore.GoroutinePoolGoFunc, opts ...Option) (*EventStore, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + + config.marshalerFunc = cqrsUtils.Marshal + config.unmarshalerFunc = cqrsUtils.Unmarshal + for _, o := range opts { + config = o(config) + } + + client, err := mongo.Connect(ctx, options.Client().ApplyURI(config.URI).SetMaxPoolSize(config.MaxPoolSize).SetMaxConnIdleTime(config.MaxConnIdleTime).SetTLSConfig(config.tlsCfg)) + if err != nil { + return nil, fmt.Errorf("could not dial database: %w", err) + } + err = client.Ping(ctx, readpref.Primary()) + if err != nil { + return nil, fmt.Errorf("could not dial database: %w", err) + } + + es, err := cqrsMongodb.NewEventStoreWithClient(ctx, client, config.DatabaseName, "events", config.BatchSize, goroutinePoolGo, config.marshalerFunc, config.unmarshalerFunc, log.Debugf) + if err != nil { + return nil, err + } + return &EventStore{ + es: es, + client: client, + config: config, + }, nil +} + +// Save saves events to a path. +func (s *EventStore) Save(ctx context.Context, groupId, aggregateId string, events []event.Event) (concurrencyException bool, err error) { + return s.es.Save(ctx, groupId, aggregateId, events) +} + +// SaveSnapshot saves snapshots to a path. +func (s *EventStore) SaveSnapshot(ctx context.Context, groupId, aggregateId string, event event.Event) (concurrencyException bool, err error) { + return s.es.SaveSnapshot(ctx, groupId, aggregateId, event) +} + +// LoadFromVersion loads aggragate events from a specific version. +func (s *EventStore) LoadFromVersion(ctx context.Context, queries []eventstore.VersionQuery, eventHandler event.Handler) error { + return s.es.LoadFromVersion(ctx, queries, eventHandler) +} + +// LoadUpToVersion loads aggragate events up to a specific version. +func (s *EventStore) LoadUpToVersion(ctx context.Context, queries []eventstore.VersionQuery, eventHandler event.Handler) error { + return s.es.LoadUpToVersion(ctx, queries, eventHandler) +} + +// LoadFromSnapshot loads events from begining. +func (s *EventStore) LoadFromSnapshot(ctx context.Context, queries []eventstore.SnapshotQuery, eventHandler event.Handler) error { + return s.es.LoadFromSnapshot(ctx, queries, eventHandler) +} + +// RemoveUpToVersion deletes the aggragates events up to a specific version. +func (s *EventStore) RemoveUpToVersion(ctx context.Context, queries []eventstore.VersionQuery) error { + return s.es.RemoveUpToVersion(ctx, queries) +} + +// Insert stores (or updates) the information about the latest snapshot version per aggregate into the DB +func (s *EventStore) Insert(ctx context.Context, task maintenance.Task) error { + return s.es.Insert(ctx, task) +} + +// Query retrieves the latest snapshot version per aggregate for thw number of aggregates specified by 'limit' +func (s *EventStore) Query(ctx context.Context, limit int, taskHandler maintenance.TaskHandler) error { + return s.es.Query(ctx, limit, taskHandler) +} + +// Remove deletes (the latest snapshot version) database record for a given aggregate ID +func (s *EventStore) Remove(ctx context.Context, task maintenance.Task) error { + return s.es.Remove(ctx, task) +} + +// Clear clears the event storage. +func (s *EventStore) Clear(ctx context.Context) error { + err1 := s.es.Clear(ctx) + err2 := s.client.Database(s.es.DBName()).Collection(instanceIdsCollection).Drop(ctx) + if err1 != nil { + return fmt.Errorf("cannot clear events: %v", err1) + } + if err2 != nil && err2 != mongo.ErrNoDocuments { + return fmt.Errorf("cannot clear sequence number: %v", err2) + } + return nil +} + +type seqRecord struct { + AggregateId string `bson:"aggregateid"` + InstanceId int64 `bson:"_id"` +} + +// GetInstanceId returns int64 that is unique +func (s *EventStore) GetInstanceId(ctx context.Context, aggregateId string) (int64, error) { + var newInstanceId uint32 + for { + newInstanceId = rand.Uint32() + + r := seqRecord{ + AggregateId: aggregateId, + InstanceId: int64(newInstanceId), + } + + if _, err := s.client.Database(s.es.DBName()).Collection(instanceIdsCollection).InsertOne(ctx, r); err != nil { + if cqrsMongodb.IsDup(err) { + rand.Seed(time.Now().UTC().UnixNano()) + } else { + return -1, fmt.Errorf("cannot generate instance id: %w", err) + } + } else { + break + } + } + + return int64(newInstanceId), nil +} + +func (s *EventStore) RemoveInstanceId(ctx context.Context, instanceId int64) error { + if _, err := s.client.Database(s.es.DBName()).Collection(instanceIdsCollection).DeleteOne(ctx, bson.M{"_id": instanceId}); err != nil { + return fmt.Errorf("cannot remove instance id: %w", err) + } + return nil +} + +// Close closes the database session. +func (s *EventStore) Close(ctx context.Context) error { + return s.es.Close(ctx) +} diff --git a/resource-aggregate/cqrs/eventstore/mongodb/eventstore_test.go b/resource-aggregate/cqrs/eventstore/mongodb/eventstore_test.go new file mode 100644 index 000000000..bd4c0cc1d --- /dev/null +++ b/resource-aggregate/cqrs/eventstore/mongodb/eventstore_test.go @@ -0,0 +1,71 @@ +// Copyright (c) 2015 - The Event Horizon authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mongodb + +import ( + "context" + "testing" + + "github.com/go-ocf/kit/security/certManager" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewEventStore(t *testing.T) { + var config certManager.Config + err := envconfig.Process("DIAL", &config) + assert.NoError(t, err) + + dialCertManager, err := certManager.NewCertManager(config) + require.NoError(t, err) + + tlsConfig := dialCertManager.GetClientTLSConfig() + + bus, err := NewEventStore(Config{ + URI: "mongodb://localhost:27017", + }, nil, WithTLS(&tlsConfig)) + assert.NoError(t, err) + assert.NotNil(t, bus) +} + +func TestInstanceId(t *testing.T) { + var config certManager.Config + err := envconfig.Process("DIAL", &config) + assert.NoError(t, err) + + dialCertManager, err := certManager.NewCertManager(config) + require.NoError(t, err) + + tlsConfig := dialCertManager.GetClientTLSConfig() + + ctx := context.Background() + store, err := NewEventStore(Config{ + URI: "mongodb://localhost:27017", + DatabaseName: "test", + }, nil, WithTLS(&tlsConfig)) + defer func() { + store.Clear(ctx) + store.Close(ctx) + }() + assert.NoError(t, err) + + for i := int64(1); i < 10; i++ { + instanceId, err := store.GetInstanceId(ctx, "b") + assert.NoError(t, err) + err = store.RemoveInstanceId(ctx, instanceId) + assert.NoError(t, err) + } +} diff --git a/resource-aggregate/cqrs/eventstore/test/events.go b/resource-aggregate/cqrs/eventstore/test/events.go new file mode 100644 index 000000000..bc1402c20 --- /dev/null +++ b/resource-aggregate/cqrs/eventstore/test/events.go @@ -0,0 +1,223 @@ +package test + +import ( + "fmt" + + "github.com/go-ocf/cqrs/event" + httpUtils "github.com/go-ocf/kit/net/http" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/events" + "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +func MakeResourcePublishedEvent(resource pb.Resource, eventMetadata pb.EventMetadata) event.EventUnmarshaler { + rp := events.ResourcePublished{ + ResourcePublished: pb.ResourcePublished{ + Id: resource.Id, + Resource: &resource, + AuditContext: &pb.AuditContext{ + UserId: "userId", + DeviceId: resource.DeviceId, + }, + EventMetadata: &eventMetadata, + }, + } + return event.EventUnmarshaler{ + Version: rp.EventMetadata.Version, + EventType: httpUtils.ProtobufContentType(&pb.ResourcePublished{}), + AggregateId: rp.Id, + GroupId: rp.Resource.DeviceId, + Unmarshal: func(v interface{}) error { + if x, ok := v.(*events.ResourcePublished); ok { + *x = rp + return nil + } + return fmt.Errorf("cannot unmarshal event") + }, + } +} + +func MakeResourceUnpublishedEvent(id, deviceID string, eventMetadata pb.EventMetadata) event.EventUnmarshaler { + ru := events.ResourceUnpublished{ + ResourceUnpublished: pb.ResourceUnpublished{ + Id: id, + AuditContext: &pb.AuditContext{ + UserId: "userId", + DeviceId: deviceID, + }, + EventMetadata: &eventMetadata, + }, + } + return event.EventUnmarshaler{ + Version: ru.EventMetadata.Version, + EventType: httpUtils.ProtobufContentType(&pb.ResourceUnpublished{}), + AggregateId: ru.Id, + GroupId: deviceID, + Unmarshal: func(v interface{}) error { + if x, ok := v.(*events.ResourceUnpublished); ok { + *x = ru + return nil + } + return fmt.Errorf("cannot unmarshal event") + }, + } +} + +func MakeResourceStateSnapshotTaken(isPublished bool, resource pb.Resource, latestResourceChange pb.ResourceChanged, eventMetadata pb.EventMetadata) event.EventUnmarshaler { + rs := events.NewResourceStateSnapshotTaken(func(string, string) error { return nil }) + rs.Id = resource.Id + rs.Resource = &resource + rs.IsPublished = isPublished + rs.LatestResourceChange = &latestResourceChange + rs.EventMetadata = &eventMetadata + + return event.EventUnmarshaler{ + Version: rs.EventMetadata.Version, + EventType: httpUtils.ProtobufContentType(&pb.ResourceStateSnapshotTaken{}), + AggregateId: rs.Id, + GroupId: rs.Resource.DeviceId, + Unmarshal: func(v interface{}) error { + if x, ok := v.(*events.ResourceStateSnapshotTaken); ok { + *x = *rs + return nil + } + return fmt.Errorf("cannot unmarshal event") + }, + } +} + +func MakeResourceUpdatePending(deviceId, resourceId string, content pb.Content, eventMetadata pb.EventMetadata) event.EventUnmarshaler { + rc := events.ResourceUpdatePending{ + ResourceUpdatePending: pb.ResourceUpdatePending{ + Id: resourceId, + Content: &content, + AuditContext: &pb.AuditContext{ + UserId: "userId", + DeviceId: deviceId, + }, + EventMetadata: &eventMetadata, + }, + } + return event.EventUnmarshaler{ + Version: rc.EventMetadata.Version, + EventType: httpUtils.ProtobufContentType(&pb.ResourceUpdatePending{}), + AggregateId: rc.Id, + GroupId: deviceId, + Unmarshal: func(v interface{}) error { + if x, ok := v.(*events.ResourceUpdatePending); ok { + *x = rc + return nil + } + return fmt.Errorf("cannot unmarshal event") + }, + } +} + +func MakeResourceUpdated(deviceId, resourceId string, status pb.Status, content pb.Content, eventMetadata pb.EventMetadata) event.EventUnmarshaler { + rc := events.ResourceUpdated{ + ResourceUpdated: pb.ResourceUpdated{ + Id: resourceId, + Content: &content, + Status: status, + AuditContext: &pb.AuditContext{ + UserId: "userId", + DeviceId: deviceId, + }, + EventMetadata: &eventMetadata, + }, + } + return event.EventUnmarshaler{ + Version: rc.EventMetadata.Version, + EventType: httpUtils.ProtobufContentType(&pb.ResourceUpdated{}), + AggregateId: rc.Id, + GroupId: deviceId, + Unmarshal: func(v interface{}) error { + if x, ok := v.(*events.ResourceUpdated); ok { + *x = rc + return nil + } + return fmt.Errorf("cannot unmarshal event") + }, + } +} + +func MakeResourceChangedEvent(id, deviceID string, content pb.Content, eventMetadata pb.EventMetadata) event.EventUnmarshaler { + ru := events.ResourceChanged{ + ResourceChanged: pb.ResourceChanged{ + Id: id, + AuditContext: &pb.AuditContext{ + UserId: "userId", + DeviceId: deviceID, + }, + Content: &content, + EventMetadata: &eventMetadata, + }, + } + return event.EventUnmarshaler{ + Version: ru.EventMetadata.Version, + EventType: httpUtils.ProtobufContentType(&pb.ResourceChanged{}), + AggregateId: ru.Id, + GroupId: deviceID, + Unmarshal: func(v interface{}) error { + if x, ok := v.(*events.ResourceChanged); ok { + *x = ru + return nil + } + return fmt.Errorf("cannot unmarshal event") + }, + } +} + +func MakeResourceRetrievePending(deviceId, resourceId string, resourceInterface string, eventMetadata pb.EventMetadata) event.EventUnmarshaler { + rc := events.ResourceRetrievePending{ + ResourceRetrievePending: pb.ResourceRetrievePending{ + Id: resourceId, + ResourceInterface: resourceInterface, + AuditContext: &pb.AuditContext{ + UserId: "userId", + DeviceId: deviceId, + }, + EventMetadata: &eventMetadata, + }, + } + return event.EventUnmarshaler{ + Version: rc.EventMetadata.Version, + EventType: httpUtils.ProtobufContentType(&pb.ResourceRetrievePending{}), + AggregateId: rc.Id, + GroupId: deviceId, + Unmarshal: func(v interface{}) error { + if x, ok := v.(*events.ResourceRetrievePending); ok { + *x = rc + return nil + } + return fmt.Errorf("cannot unmarshal event") + }, + } +} + +func MakeResourceRetrieved(deviceId, resourceId string, status pb.Status, content pb.Content, eventMetadata pb.EventMetadata) event.EventUnmarshaler { + rc := events.ResourceRetrieved{ + ResourceRetrieved: pb.ResourceRetrieved{ + Id: resourceId, + Content: &content, + Status: status, + AuditContext: &pb.AuditContext{ + UserId: "userId", + DeviceId: deviceId, + }, + EventMetadata: &eventMetadata, + }, + } + return event.EventUnmarshaler{ + Version: rc.EventMetadata.Version, + EventType: httpUtils.ProtobufContentType(&pb.ResourceRetrieved{}), + AggregateId: rc.Id, + GroupId: deviceId, + Unmarshal: func(v interface{}) error { + if x, ok := v.(*events.ResourceRetrieved); ok { + *x = rc + return nil + } + return fmt.Errorf("cannot unmarshal event") + }, + } +} diff --git a/resource-aggregate/cqrs/eventstore/test/eventstore.go b/resource-aggregate/cqrs/eventstore/test/eventstore.go new file mode 100644 index 000000000..82875df86 --- /dev/null +++ b/resource-aggregate/cqrs/eventstore/test/eventstore.go @@ -0,0 +1,154 @@ +package test + +import ( + "context" + "errors" + "fmt" + + "github.com/go-ocf/cqrs/event" + "github.com/go-ocf/cqrs/eventstore" +) + +type MockEventStore struct { + events map[string]map[string][]event.EventUnmarshaler +} + +func (s *MockEventStore) Save(ctx context.Context, groupId, aggregateId string, events []event.Event) (concurrencyException bool, err error) { + return false, errors.New("not supported") +} + +func (s *MockEventStore) SaveSnapshot(ctx context.Context, groupId, aggregateId string, event event.Event) (concurrencyException bool, err error) { + return false, errors.New("not supported") +} + +func (s *MockEventStore) LoadFromVersion(ctx context.Context, queries []eventstore.VersionQuery, eventHandler event.Handler) error { + aggregates := make(map[string][]event.EventUnmarshaler) + for _, device := range s.events { + for aggrId, events := range device { + aggregates[aggrId] = events + } + } + + var events []event.EventUnmarshaler + for _, q := range queries { + var ok bool + var r []event.EventUnmarshaler + + if r, ok = aggregates[q.AggregateId]; !ok { + continue + } + + events = append(events, r...) + } + + return eventHandler.Handle(ctx, &iter{events: events}) +} + +// LoadUpToVersion loads aggragate events up to a specific version. +func (s *MockEventStore) LoadUpToVersion(ctx context.Context, queries []eventstore.VersionQuery, eventHandler event.Handler) error { + return errors.New("not supported") +} + +func makeModelId(groupId, aggregateId string) string { + return groupId + "." + aggregateId +} + +func (s *MockEventStore) allModels(queriesInt map[string]eventstore.VersionQuery) map[string]eventstore.VersionQuery { + for groupId, group := range s.events { + for aggrId, events := range group { + queriesInt[makeModelId(groupId, aggrId)] = eventstore.VersionQuery{AggregateId: aggrId, Version: events[0].Version} + } + } + return queriesInt +} + +func (s *MockEventStore) LoadFromSnapshot(ctx context.Context, queries []eventstore.SnapshotQuery, eventHandler event.Handler) error { + queriesInt := make(map[string]eventstore.VersionQuery) + if len(queries) == 0 { + queriesInt = s.allModels(queriesInt) + } else { + for _, query := range queries { + switch { + case query.GroupId == "" && query.AggregateId == "": + queriesInt = s.allModels(queriesInt) + break + case query.GroupId != "" && query.AggregateId == "": + if aggregates, ok := s.events[query.GroupId]; ok { + for aggrId, events := range aggregates { + queriesInt[makeModelId(query.GroupId, aggrId)] = eventstore.VersionQuery{AggregateId: aggrId, Version: events[0].Version} + } + } + case query.GroupId == "" && query.AggregateId != "": + for groupId, aggregates := range s.events { + if events, ok := aggregates[query.AggregateId]; ok { + queriesInt[makeModelId(groupId, query.AggregateId)] = eventstore.VersionQuery{AggregateId: query.AggregateId, Version: events[0].Version} + } + } + default: + if aggregates, ok := s.events[query.GroupId]; ok { + if events, ok := aggregates[query.AggregateId]; ok { + queriesInt[makeModelId(query.GroupId, query.AggregateId)] = eventstore.VersionQuery{AggregateId: query.AggregateId, Version: events[0].Version} + } + } + } + } + } + + ret := make([]eventstore.VersionQuery, 0, len(queriesInt)) + for _, q := range queriesInt { + ret = append(ret, q) + } + if len(ret) == 0 { + return fmt.Errorf("cannot load events: not found") + } + + return s.LoadFromVersion(ctx, ret, eventHandler) +} + +// RemoveUpToVersion deletes the aggragates events up to a specific version. +func (s *MockEventStore) RemoveUpToVersion(ctx context.Context, queries []eventstore.VersionQuery) error { + return errors.New("not supported") +} + +type iter struct { + idx int + events []event.EventUnmarshaler +} + +func (i *iter) Next(ctx context.Context, eu *event.EventUnmarshaler) bool { + if i.idx >= len(i.events) { + return false + } + *eu = i.events[i.idx] + i.idx++ + return true +} + +func (i *iter) Err() error { + return nil +} + +func (s *MockEventStore) GetInstanceId(ctx context.Context, groupId, aggregateId string) (int64, error) { + return -1, errors.New("not supported") +} +func (s *MockEventStore) RemoveInstanceId(ctx context.Context, instanceId int64) error { + return errors.New("not supported") +} + +func NewMockEventStore() *MockEventStore { + return &MockEventStore{make(map[string]map[string][]event.EventUnmarshaler)} +} + +func (e *MockEventStore) Append(groupId, aggregateId string, ev event.EventUnmarshaler) { + var m map[string][]event.EventUnmarshaler + var ok bool + if m, ok = e.events[groupId]; !ok { + m = make(map[string][]event.EventUnmarshaler) + e.events[groupId] = m + } + var r []event.EventUnmarshaler + if r, ok = m[aggregateId]; !ok { + r = make([]event.EventUnmarshaler, 0, 10) + } + m[aggregateId] = append(r, ev) +} diff --git a/resource-aggregate/cqrs/notification/retrieveNotificationContainer.go b/resource-aggregate/cqrs/notification/retrieveNotificationContainer.go new file mode 100644 index 000000000..9d91628f5 --- /dev/null +++ b/resource-aggregate/cqrs/notification/retrieveNotificationContainer.go @@ -0,0 +1,40 @@ +package notification + +import ( + "sync" + + raEvents "github.com/go-ocf/cloud/resource-aggregate/cqrs/events" +) + +type RetrieveNotificationContainer struct { + notifications map[string]chan raEvents.ResourceRetrieved + mutex sync.Mutex +} + +func NewRetrieveNotificationContainer() *RetrieveNotificationContainer { + return &RetrieveNotificationContainer{notifications: make(map[string]chan raEvents.ResourceRetrieved)} +} + +func (c *RetrieveNotificationContainer) Add(correlationID string) <-chan raEvents.ResourceRetrieved { + notify := make(chan raEvents.ResourceRetrieved, 1) + + c.mutex.Lock() + defer c.mutex.Unlock() + c.notifications[correlationID] = notify + return notify +} + +func (c *RetrieveNotificationContainer) Find(correlationID string) chan<- raEvents.ResourceRetrieved { + c.mutex.Lock() + defer c.mutex.Unlock() + if n, ok := c.notifications[correlationID]; ok { + return n + } + return nil +} + +func (c *RetrieveNotificationContainer) Remove(correlationID string) { + c.mutex.Lock() + defer c.mutex.Unlock() + delete(c.notifications, correlationID) +} diff --git a/resource-aggregate/cqrs/notification/updateNotificationContainer.go b/resource-aggregate/cqrs/notification/updateNotificationContainer.go new file mode 100644 index 000000000..676b6b324 --- /dev/null +++ b/resource-aggregate/cqrs/notification/updateNotificationContainer.go @@ -0,0 +1,40 @@ +package notification + +import ( + "sync" + + raEvents "github.com/go-ocf/cloud/resource-aggregate/cqrs/events" +) + +type UpdateNotificationContainer struct { + notifications map[string]chan raEvents.ResourceUpdated + mutex sync.Mutex +} + +func NewUpdateNotificationContainer() *UpdateNotificationContainer { + return &UpdateNotificationContainer{notifications: make(map[string]chan raEvents.ResourceUpdated)} +} + +func (c *UpdateNotificationContainer) Add(correlationID string) <-chan raEvents.ResourceUpdated { + notify := make(chan raEvents.ResourceUpdated, 1) + + c.mutex.Lock() + defer c.mutex.Unlock() + c.notifications[correlationID] = notify + return notify +} + +func (c *UpdateNotificationContainer) Find(correlationID string) chan<- raEvents.ResourceUpdated { + c.mutex.Lock() + defer c.mutex.Unlock() + if n, ok := c.notifications[correlationID]; ok { + return n + } + return nil +} + +func (c *UpdateNotificationContainer) Remove(correlationID string) { + c.mutex.Lock() + defer c.mutex.Unlock() + delete(c.notifications, correlationID) +} diff --git a/resource-aggregate/cqrs/projection/projection.go b/resource-aggregate/cqrs/projection/projection.go new file mode 100644 index 000000000..f8c0d4d49 --- /dev/null +++ b/resource-aggregate/cqrs/projection/projection.go @@ -0,0 +1,143 @@ +package projection + +import ( + "context" + "fmt" + + "github.com/go-ocf/cqrs" + "github.com/go-ocf/cqrs/eventbus" + "github.com/go-ocf/cqrs/eventstore" + "github.com/go-ocf/kit/log" + + raCqrsUtils "github.com/go-ocf/cloud/resource-aggregate/cqrs" +) + +// Projection projects events from resource aggregate. +type Projection struct { + projection *projection +} + +// NewProjection creates new resource projection. +func NewProjection(ctx context.Context, name string, store eventstore.EventStore, subscriber eventbus.Subscriber, factoryModel eventstore.FactoryModelFunc) (*Projection, error) { + projection, err := newProjection(ctx, name, store, subscriber, factoryModel, raCqrsUtils.GetTopics) + if err != nil { + return nil, fmt.Errorf("cannot create resource projection: %w", err) + } + return &Projection{projection: projection}, nil +} + +// Register registers deviceId, loads events from eventstore and subscribe to eventbus. +// It can be called multiple times for same deviceId but after successful the a call Unregister +// must be called same times to free resources. +func (p *Projection) Register(ctx context.Context, deviceId string) (loaded bool, err error) { + return p.projection.register(ctx, deviceId, []eventstore.SnapshotQuery{eventstore.SnapshotQuery{GroupId: deviceId}}) +} + +// Unregister unregisters device and his resource from projection. +func (p *Projection) Unregister(deviceId string) error { + return p.projection.unregister(deviceId) +} + +// Models returns models for device, resource or nil for non exist. +func (p *Projection) Models(deviceId, resourceId string) []eventstore.Model { + return p.projection.models([]eventstore.SnapshotQuery{eventstore.SnapshotQuery{GroupId: deviceId, AggregateId: resourceId}}) +} + +// ForceUpdate invokes update registered resource model from evenstore. +func (p *Projection) ForceUpdate(ctx context.Context, deviceId, resourceId string) error { + err := p.projection.forceUpdate(ctx, deviceId, []eventstore.SnapshotQuery{eventstore.SnapshotQuery{GroupId: deviceId, AggregateId: resourceId}}) + if err != nil { + return fmt.Errorf("cannot force update resource projection: %w", err) + } + return err +} + +type projection struct { + cqrsProjection *cqrs.Projection + + topicManager *TopicManager + refCountMap *RefCountMap +} + +func newProjection(ctx context.Context, name string, store eventstore.EventStore, subscriber eventbus.Subscriber, factoryModel eventstore.FactoryModelFunc, getTopics GetTopicsFunc) (*projection, error) { + cqrsProjection, err := cqrs.NewProjection(ctx, store, name, subscriber, factoryModel, log.Debugf) + if err != nil { + return nil, fmt.Errorf("cannot create Projection: %w", err) + } + return &projection{ + cqrsProjection: cqrsProjection, + topicManager: NewTopicManager(getTopics), + refCountMap: NewRefCountMap(), + }, nil +} + +// ForceUpdate invokes update registered resource model from evenstore. +func (p *projection) forceUpdate(ctx context.Context, registrationID string, query []eventstore.SnapshotQuery) error { + _, err := p.refCountMap.Inc(registrationID, false) + if err != nil { + return fmt.Errorf("cannot force update projection: %w", err) + } + + err = p.cqrsProjection.Project(ctx, query) + if err != nil { + return fmt.Errorf("cannot force update projection: %w", err) + } + _, err = p.refCountMap.Dec(registrationID) + if err != nil { + return fmt.Errorf("cannot force update projection: %w", err) + } + return nil +} + +func (p *projection) models(query []eventstore.SnapshotQuery) []eventstore.Model { + return p.cqrsProjection.Models(query) +} + +func (p *projection) register(ctx context.Context, registrationID string, query []eventstore.SnapshotQuery) (loaded bool, err error) { + created, err := p.refCountMap.Inc(registrationID, true) + if err != nil { + return false, fmt.Errorf("cannot register device: %w", err) + } + if !created { + return false, nil + } + + topics, updateSubscriber := p.topicManager.Add(registrationID) + + if updateSubscriber { + err := p.cqrsProjection.SubscribeTo(topics) + if err != nil { + p.refCountMap.Dec(registrationID) + return false, fmt.Errorf("cannot register device: %w", err) + } + } + + err = p.cqrsProjection.Project(ctx, query) + if err != nil { + return false, fmt.Errorf("cannot register device: %w", err) + } + + return true, nil +} + +func (p *projection) unregister(registrationID string) error { + deleted, err := p.refCountMap.Dec(registrationID) + if err != nil { + return fmt.Errorf("cannot unregister device from projection: %w", err) + } + if !deleted { + return nil + } + + topics, updateSubscriber := p.topicManager.Remove(registrationID) + + if updateSubscriber { + err := p.cqrsProjection.SubscribeTo(topics) + if err != nil { + log.Errorf("cannot change topics for projection: %w", err) + } + } + return p.cqrsProjection.Forget([]eventstore.SnapshotQuery{ + {GroupId: registrationID}, + }) +} diff --git a/resource-aggregate/cqrs/projection/projection_test.go b/resource-aggregate/cqrs/projection/projection_test.go new file mode 100644 index 000000000..086d66c8a --- /dev/null +++ b/resource-aggregate/cqrs/projection/projection_test.go @@ -0,0 +1,444 @@ +package projection + +import ( + "context" + "testing" + + "github.com/go-ocf/cqrs/event" + "github.com/go-ocf/cqrs/eventstore" + cqrsEventStore "github.com/go-ocf/cqrs/eventstore" + "github.com/go-ocf/kit/log" + "github.com/go-ocf/kit/net/http" + kitCqrsUtils "github.com/go-ocf/cloud/resource-aggregate/cqrs" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/events" + raEvents "github.com/go-ocf/cloud/resource-aggregate/cqrs/events" + mockEventStore "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/test" + mockEvents "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/test" + "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + "github.com/stretchr/testify/assert" +) + +type mockResourceCtx struct { + pbRA.ResourceStateSnapshotTaken + UpdatePending map[string]raEvents.ResourceUpdatePending +} + +func (m *mockResourceCtx) SnapshotEventType() string { + s := &raEvents.ResourceStateSnapshotTaken{} + return s.SnapshotEventType() +} + +func (m *mockResourceCtx) Handle(ctx context.Context, iter event.Iter) error { + var eu event.EventUnmarshaler + + for iter.Next(ctx, &eu) { + log.Debugf("resourceCtx.Handle: DeviceId: %v, ResourceId: %v, Version: %v, EventType: %v", eu.GroupId, eu.AggregateId, eu.Version, eu.EventType) + switch eu.EventType { + case http.ProtobufContentType(&pbRA.ResourceStateSnapshotTaken{}): + var s raEvents.ResourceStateSnapshotTaken + if err := eu.Unmarshal(&s); err != nil { + return err + } + m.LatestResourceChange = s.LatestResourceChange + m.Resource = s.Resource + m.Id = s.Id + m.IsPublished = s.IsPublished + m.EventMetadata = s.EventMetadata + case http.ProtobufContentType(&pbRA.ResourcePublished{}): + var s raEvents.ResourcePublished + if err := eu.Unmarshal(&s); err != nil { + return err + } + m.Id = s.Id + m.IsPublished = true + m.Resource = s.Resource + case http.ProtobufContentType(&pbRA.ResourceUnpublished{}): + m.IsPublished = false + case http.ProtobufContentType(&pbRA.ResourceUpdatePending{}): + var s raEvents.ResourceUpdatePending + if err := eu.Unmarshal(&s); err != nil { + return err + } + m.UpdatePending[s.AuditContext.CorrelationId] = s + case http.ProtobufContentType(&pbRA.ResourceUpdated{}): + var s raEvents.ResourceUpdated + if err := eu.Unmarshal(&s); err != nil { + return err + } + delete(m.UpdatePending, s.AuditContext.CorrelationId) + case http.ProtobufContentType(&pbRA.ResourceChanged{}): + var s raEvents.ResourceChanged + if err := eu.Unmarshal(&s); err != nil { + return err + } + m.LatestResourceChange = &s.ResourceChanged + } + } + return nil +} + +var res0 = pbRA.Resource{ + Id: "res0", + DeviceId: "dev0", + Href: "/res0", +} +var res1 = pbRA.Resource{ + Id: "res1", + DeviceId: "dev1", + Href: "/res1", +} + +var res2 = pbRA.Resource{ + Id: "res2", + DeviceId: "dev0", + Href: "/res2", +} + +var res3 = pbRA.Resource{ + Id: "res3", + DeviceId: "dev0", + Href: "/res3", +} + +var res4 = pbRA.Resource{ + Id: "res4", + DeviceId: "dev1", + Href: "/res4", +} + +func makeEventMeta(connectionId string, sequence, version uint64) pb.EventMetadata { + e := kitCqrsUtils.MakeEventMeta(connectionId, sequence, version) + e.TimestampMs = 12345 + return e +} + +func prepareResourceEventstore(t *testing.T) *mockEventStore.MockEventStore { + + eventstore := mockEventStore.NewMockEventStore() + + eventstore.Append(res0.DeviceId, res0.Id, mockEvents.MakeResourcePublishedEvent(res0, makeEventMeta("a", 0, 0))) + + eventstore.Append(res1.DeviceId, res1.Id, mockEvents.MakeResourcePublishedEvent(res1, makeEventMeta("a", 0, 0))) + eventstore.Append(res1.DeviceId, res1.Id, mockEvents.MakeResourceUnpublishedEvent(res1.Id, res1.DeviceId, makeEventMeta("a", 0, 1))) + + resourceChangedEventMetadata := makeEventMeta("", 0, 0) + eventstore.Append(res2.DeviceId, res2.Id, mockEvents.MakeResourcePublishedEvent(res2, makeEventMeta("a", 0, 0))) + eventstore.Append(res2.DeviceId, res2.Id, mockEvents.MakeResourceStateSnapshotTaken(true, res2, pbRA.ResourceChanged{Content: &pbRA.Content{}, EventMetadata: &resourceChangedEventMetadata}, makeEventMeta("a", 0, 1))) + + eventstore.Append(res3.DeviceId, res3.Id, mockEvents.MakeResourceStateSnapshotTaken(true, res3, pbRA.ResourceChanged{Content: &pbRA.Content{}, EventMetadata: &resourceChangedEventMetadata}, makeEventMeta("a", 0, 0))) + eventstore.Append(res3.DeviceId, res3.Id, mockEvents.MakeResourceUnpublishedEvent(res3.Id, res3.DeviceId, makeEventMeta("a", 0, 1))) + eventstore.Append(res3.DeviceId, res3.Id, mockEvents.MakeResourceUpdatePending(res3.DeviceId, res3.Id, pbRA.Content{}, makeEventMeta("a", 0, 2))) + eventstore.Append(res3.DeviceId, res3.Id, mockEvents.MakeResourcePublishedEvent(res3, makeEventMeta("a", 0, 3))) + + eventstore.Append(res4.DeviceId, res4.Id, mockEvents.MakeResourceStateSnapshotTaken(true, res4, pbRA.ResourceChanged{Content: &pbRA.Content{}, EventMetadata: &resourceChangedEventMetadata}, makeEventMeta("a", 0, 0))) + eventstore.Append(res4.DeviceId, res4.Id, mockEvents.MakeResourceUnpublishedEvent(res4.Id, res4.DeviceId, makeEventMeta("a", 0, 1))) + eventstore.Append(res4.DeviceId, res4.Id, mockEvents.MakeResourceUpdatePending(res4.DeviceId, res4.Id, pbRA.Content{}, makeEventMeta("a", 0, 2))) + eventstore.Append(res4.DeviceId, res4.Id, mockEvents.MakeResourceUpdated(res4.DeviceId, res4.Id, pbRA.Status_OK, pbRA.Content{}, makeEventMeta("a", 0, 3))) + + return eventstore +} + +func TestResourceProjection_Register(t *testing.T) { + type args struct { + deviceId string + } + tests := []struct { + name string + args args + wantLoaded bool + wantErr bool + }{ + { + name: "first valid", + args: args{ + deviceId: res0.DeviceId, + }, + wantLoaded: true, + }, + { + name: "second valid", + args: args{ + deviceId: res0.DeviceId, + }, + }, + { + name: "error", + args: args{ + deviceId: "error", + }, + wantErr: true, + }, + } + + eventstore := prepareResourceEventstore(t) + ctx := context.Background() + p, err := NewProjection( + ctx, + "test", + eventstore, + nil, + func(ctx context.Context) (cqrsEventStore.Model, error) { + return &mockResourceCtx{ + UpdatePending: make(map[string]raEvents.ResourceUpdatePending), + }, nil + }, + ) + assert.NoError(t, err) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotLoaded, err := p.Register(ctx, tt.args.deviceId) + if tt.wantLoaded { + assert.True(t, gotLoaded) + } else { + assert.False(t, gotLoaded) + } + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestResourceProjection_Unregister(t *testing.T) { + type args struct { + deviceId string + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "first time", + args: args{ + deviceId: res0.DeviceId, + }, + }, + { + name: "second second", + args: args{ + deviceId: res0.DeviceId, + }, + }, + { + name: "third error", + args: args{ + deviceId: res0.DeviceId, + }, + wantErr: true, + }, + { + name: "not registered", + args: args{ + deviceId: res1.DeviceId, + }, + wantErr: true, + }, + } + + eventstore := prepareResourceEventstore(t) + ctx := context.Background() + p, err := NewProjection( + ctx, + "test", + eventstore, + nil, + func(ctx context.Context) (cqrsEventStore.Model, error) { + return &mockResourceCtx{ + UpdatePending: make(map[string]raEvents.ResourceUpdatePending), + }, nil + }, + ) + assert.NoError(t, err) + _, err = p.Register(ctx, res0.DeviceId) + assert.NoError(t, err) + _, err = p.Register(ctx, res0.DeviceId) + assert.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) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestResourceProjection_Models(t *testing.T) { + type args struct { + deviceId string + resourceId string + } + tests := []struct { + name string + args args + want []eventstore.Model + }{ + { + name: "valid", + args: args{ + deviceId: res0.DeviceId, + }, + want: []eventstore.Model{ + &mockResourceCtx{ + ResourceStateSnapshotTaken: pbRA.ResourceStateSnapshotTaken{ + Id: res0.Id, + Resource: &res0, + IsPublished: true, + }, + UpdatePending: make(map[string]raEvents.ResourceUpdatePending), + }, + &mockResourceCtx{ + ResourceStateSnapshotTaken: pbRA.ResourceStateSnapshotTaken{ + Id: res2.Id, + Resource: &res2, + IsPublished: true, + LatestResourceChange: &pbRA.ResourceChanged{ + Content: &pbRA.Content{}, + EventMetadata: &pb.EventMetadata{ + TimestampMs: 12345, + }, + }, + EventMetadata: &pb.EventMetadata{ + Version: 1, + TimestampMs: 12345, + ConnectionId: "a", + }, + }, + UpdatePending: make(map[string]raEvents.ResourceUpdatePending), + }, + &mockResourceCtx{ + ResourceStateSnapshotTaken: pbRA.ResourceStateSnapshotTaken{ + Id: res3.Id, + Resource: &res3, + IsPublished: true, + LatestResourceChange: &pbRA.ResourceChanged{ + Content: &pbRA.Content{}, + EventMetadata: &pb.EventMetadata{ + TimestampMs: 12345, + }, + }, + EventMetadata: &pb.EventMetadata{ + TimestampMs: 12345, + ConnectionId: "a", + }, + }, + UpdatePending: map[string]raEvents.ResourceUpdatePending{ + "": events.ResourceUpdatePending{ + pbRA.ResourceUpdatePending{ + Id: "res3", + Content: &pbRA.Content{}, + EventMetadata: &pb.EventMetadata{ + ConnectionId: "a", + Sequence: 0, + Version: 2, + TimestampMs: 12345, + }, + AuditContext: &pb.AuditContext{ + UserId: "userId", + DeviceId: "dev0", + }, + }, + }, + }, + }, + }, + }, + } + + eventstore := prepareResourceEventstore(t) + ctx := context.Background() + p, err := NewProjection( + ctx, + "test", + eventstore, + nil, + func(ctx context.Context) (cqrsEventStore.Model, error) { + return &mockResourceCtx{ + UpdatePending: make(map[string]raEvents.ResourceUpdatePending), + }, nil + }, + ) + assert.NoError(t, err) + _, err = p.Register(ctx, res0.DeviceId) + assert.NoError(t, err) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := p.Models(tt.args.deviceId, tt.args.resourceId) + + mapWant := make(map[string]*mockResourceCtx) + for _, r := range tt.want { + m := r.(*mockResourceCtx) + mapWant[m.Id] = m + } + mapGot := make(map[string]*mockResourceCtx) + for _, r := range got { + m := r.(*mockResourceCtx) + mapGot[m.Id] = m + } + + assert.Equal(t, mapWant, mapGot) + }) + } +} + +func TestResourceProjection_ForceUpdate(t *testing.T) { + type args struct { + deviceId string + resourceId string + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "valid", + args: args{ + deviceId: res0.DeviceId, + }, + }, + { + name: "error", + args: args{ + deviceId: "error", + }, + wantErr: true, + }, + } + + eventstore := prepareResourceEventstore(t) + ctx := context.Background() + p, err := NewProjection( + ctx, + "test", + eventstore, + nil, + func(ctx context.Context) (cqrsEventStore.Model, error) { + return &mockResourceCtx{ + UpdatePending: make(map[string]raEvents.ResourceUpdatePending), + }, nil + }, + ) + assert.NoError(t, err) + _, err = p.Register(ctx, res0.DeviceId) + assert.NoError(t, err) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := p.ForceUpdate(ctx, tt.args.deviceId, tt.args.resourceId) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/resource-aggregate/cqrs/projection/refCountMap.go b/resource-aggregate/cqrs/projection/refCountMap.go new file mode 100644 index 000000000..76d96c1c3 --- /dev/null +++ b/resource-aggregate/cqrs/projection/refCountMap.go @@ -0,0 +1,54 @@ +package projection + +import ( + "fmt" + "sync" +) + +type RefCountMap struct { + countsLock sync.Mutex + counts map[string]int +} + +func NewRefCountMap() *RefCountMap { + return &RefCountMap{counts: make(map[string]int)} +} + +func (p *RefCountMap) Inc(id string, create bool) (created bool, err error) { + var ok bool + var count int + + p.countsLock.Lock() + defer p.countsLock.Unlock() + if count, ok = p.counts[id]; !ok && !create { + return false, fmt.Errorf("cannot increment reference counter: not found") + } + count++ + p.counts[id] = count + + if count == 1 { + created = true + } + + return created, nil +} + +func (p *RefCountMap) Dec(id string) (deleted bool, err error) { + var ok bool + var count int + + p.countsLock.Lock() + defer p.countsLock.Unlock() + if count, ok = p.counts[id]; !ok { + return false, fmt.Errorf("cannot decrement reference counter: not found") + } + count-- + if count == 0 { + delete(p.counts, id) + deleted = true + } else { + p.counts[id] = count + } + + return deleted, nil +} diff --git a/resource-aggregate/cqrs/projection/topicManager.go b/resource-aggregate/cqrs/projection/topicManager.go new file mode 100644 index 000000000..9532feca7 --- /dev/null +++ b/resource-aggregate/cqrs/projection/topicManager.go @@ -0,0 +1,63 @@ +package projection + +import ( + "sync" +) + +type GetTopicsFunc func(string) []string + +type TopicManager struct { + topicsLock sync.Mutex + topics map[string]int + getTopics GetTopicsFunc +} + +func NewTopicManager(getTopics GetTopicsFunc) *TopicManager { + return &TopicManager{ + topics: make(map[string]int), + getTopics: getTopics, + } +} + +func (p *TopicManager) Add(key string) ([]string, bool) { + var updateSubscriber bool + var topics []string + p.topicsLock.Lock() + defer p.topicsLock.Unlock() + for _, t := range p.getTopics(key) { + if _, ok := p.topics[t]; ok { + p.topics[t]++ + } else { + updateSubscriber = true + p.topics[t] = 1 + } + } + if updateSubscriber { + for t := range p.topics { + topics = append(topics, t) + } + } + return topics, updateSubscriber +} + +func (p *TopicManager) Remove(key string) ([]string, bool) { + var updateSubscriber bool + var topics []string + p.topicsLock.Lock() + defer p.topicsLock.Unlock() + for _, t := range p.getTopics(key) { + if _, ok := p.topics[t]; ok { + p.topics[t]-- + if p.topics[t] <= 0 { + delete(p.topics, t) + updateSubscriber = true + } + } + } + if updateSubscriber { + for t := range p.topics { + topics = append(topics, t) + } + } + return topics, updateSubscriber +} diff --git a/resource-aggregate/cqrs/utils.go b/resource-aggregate/cqrs/utils.go new file mode 100644 index 000000000..14d195504 --- /dev/null +++ b/resource-aggregate/cqrs/utils.go @@ -0,0 +1,74 @@ +package cqrs + +import ( + "fmt" + "time" + + "github.com/go-ocf/cloud/resource-aggregate/pb" + "github.com/gofrs/uuid" + "github.com/golang/snappy" +) + +func GetTopics(deviceId string) []string { + return []string{"events-" + deviceId + "-resource-aggregate"} +} + +func MakeResourceId(deviceID, href string) string { + return uuid.NewV5(uuid.NamespaceURL, deviceID+href).String() +} + +func TimeNowMs() uint64 { + now := time.Now() + unix := now.UnixNano() + return uint64(unix / int64(time.Millisecond)) +} + +//CreateEventMeta for creating EventMetadata for event. +func MakeEventMeta(connectionId string, sequence, version uint64) pb.EventMetadata { + return pb.EventMetadata{ + ConnectionId: connectionId, + Sequence: sequence, + Version: version, + TimestampMs: TimeNowMs(), + } +} + +func MakeAuditContext(a *pb.AuthorizationContext, correlationId string) pb.AuditContext { + return pb.AuditContext{ + UserId: a.GetUserId(), + DeviceId: a.GetDeviceId(), + CorrelationId: correlationId, + } +} + +type ProtobufMarshaler interface { + Marshal() ([]byte, error) +} + +type ProtobufUnmarshaler interface { + Unmarshal([]byte) error +} + +func Marshal(v interface{}) ([]byte, error) { + if p, ok := v.(ProtobufMarshaler); ok { + src, err := p.Marshal() + if err != nil { + return nil, fmt.Errorf("cannot marshal event: %w", err) + } + dst := make([]byte, 1024) + return snappy.Encode(dst, src), nil + } + return nil, fmt.Errorf("marshal is not supported by %T", v) +} + +func Unmarshal(b []byte, v interface{}) error { + if p, ok := v.(ProtobufUnmarshaler); ok { + dst := make([]byte, 1024) + dst, err := snappy.Decode(dst, b) + if err != nil { + return fmt.Errorf("cannot decode buffer: %w", err) + } + return p.Unmarshal(dst) + } + return fmt.Errorf("marshal is not supported by %T", v) +} diff --git a/resource-aggregate/cqrs/utils_test.go b/resource-aggregate/cqrs/utils_test.go new file mode 100644 index 000000000..80718e523 --- /dev/null +++ b/resource-aggregate/cqrs/utils_test.go @@ -0,0 +1,50 @@ +package cqrs + +import ( + "testing" + + "github.com/go-ocf/cloud/resource-aggregate/pb" + "github.com/stretchr/testify/assert" +) + +func TestDummyForCoverage(t *testing.T) { + device := "dev" + + GetTopics(device) + MakeResourceId(device, "/abc") + sequence := uint64(1234) + version := uint64(5) + connId := "c" + corId := "a" + userId := "u" + + TimeNowMs() + em := MakeEventMeta(connId, sequence, version) + assert.Equal(t, connId, em.ConnectionId) + assert.Equal(t, sequence, em.Sequence) + assert.Equal(t, version, em.Version) + ac := MakeAuditContext(&pb.AuthorizationContext{UserId: userId, DeviceId: device}, corId) + assert.Equal(t, corId, ac.CorrelationId) + assert.Equal(t, userId, ac.UserId) + assert.Equal(t, device, ac.DeviceId) +} + +func TestProtobufMarshaler(t *testing.T) { + req := pb.AuthorizationContext{} + + out, err := Marshal(&req) + assert.NoError(t, err) + assert.NotEmpty(t, out) + + a := struct { + }{} + _, err = Marshal(a) + assert.Error(t, err) + + resp := pb.AuthorizationContext{} + err = Unmarshal(out, &resp) + assert.NoError(t, err) + + err = Unmarshal(out, a) + assert.Error(t, err) +} diff --git a/resource-aggregate/pb/commands.pb.go b/resource-aggregate/pb/commands.pb.go new file mode 100644 index 000000000..021efa91d --- /dev/null +++ b/resource-aggregate/pb/commands.pb.go @@ -0,0 +1,4722 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: pb/commands.proto + +package pb + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type AuthorizationContext struct { + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + DeviceId string `protobuf:"bytes,2,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` +} + +func (m *AuthorizationContext) Reset() { *m = AuthorizationContext{} } +func (m *AuthorizationContext) String() string { return proto.CompactTextString(m) } +func (*AuthorizationContext) ProtoMessage() {} +func (*AuthorizationContext) Descriptor() ([]byte, []int) { + return fileDescriptor_edd018fc297633a7, []int{0} +} +func (m *AuthorizationContext) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AuthorizationContext) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AuthorizationContext.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AuthorizationContext) XXX_Merge(src proto.Message) { + xxx_messageInfo_AuthorizationContext.Merge(m, src) +} +func (m *AuthorizationContext) XXX_Size() int { + return m.Size() +} +func (m *AuthorizationContext) XXX_DiscardUnknown() { + xxx_messageInfo_AuthorizationContext.DiscardUnknown(m) +} + +var xxx_messageInfo_AuthorizationContext proto.InternalMessageInfo + +func (m *AuthorizationContext) GetUserId() string { + if m != nil { + return m.UserId + } + return "" +} + +func (m *AuthorizationContext) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +type CommandMetadata struct { + ConnectionId string `protobuf:"bytes,1,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty"` + Sequence uint64 `protobuf:"varint,2,opt,name=sequence,proto3" json:"sequence,omitempty"` +} + +func (m *CommandMetadata) Reset() { *m = CommandMetadata{} } +func (m *CommandMetadata) String() string { return proto.CompactTextString(m) } +func (*CommandMetadata) ProtoMessage() {} +func (*CommandMetadata) Descriptor() ([]byte, []int) { + return fileDescriptor_edd018fc297633a7, []int{1} +} +func (m *CommandMetadata) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *CommandMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_CommandMetadata.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *CommandMetadata) XXX_Merge(src proto.Message) { + xxx_messageInfo_CommandMetadata.Merge(m, src) +} +func (m *CommandMetadata) XXX_Size() int { + return m.Size() +} +func (m *CommandMetadata) XXX_DiscardUnknown() { + xxx_messageInfo_CommandMetadata.DiscardUnknown(m) +} + +var xxx_messageInfo_CommandMetadata proto.InternalMessageInfo + +func (m *CommandMetadata) GetConnectionId() string { + if m != nil { + return m.ConnectionId + } + return "" +} + +func (m *CommandMetadata) GetSequence() uint64 { + if m != nil { + return m.Sequence + } + return 0 +} + +// https://github.com/openconnectivityfoundation/core/blob/master/oic.wk.rd.raml#L65 +type PublishResourceRequest struct { + AuthorizationContext *AuthorizationContext `protobuf:"bytes,1,opt,name=authorization_context,json=authorizationContext,proto3" json:"authorization_context,omitempty"` + ResourceId string `protobuf:"bytes,2,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + Resource *Resource `protobuf:"bytes,3,opt,name=resource,proto3" json:"resource,omitempty"` + TimeToLive int32 `protobuf:"varint,4,opt,name=time_to_live,json=timeToLive,proto3" json:"time_to_live,omitempty"` + CommandMetadata *CommandMetadata `protobuf:"bytes,5,opt,name=command_metadata,json=commandMetadata,proto3" json:"command_metadata,omitempty"` +} + +func (m *PublishResourceRequest) Reset() { *m = PublishResourceRequest{} } +func (m *PublishResourceRequest) String() string { return proto.CompactTextString(m) } +func (*PublishResourceRequest) ProtoMessage() {} +func (*PublishResourceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_edd018fc297633a7, []int{2} +} +func (m *PublishResourceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PublishResourceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PublishResourceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PublishResourceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_PublishResourceRequest.Merge(m, src) +} +func (m *PublishResourceRequest) XXX_Size() int { + return m.Size() +} +func (m *PublishResourceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_PublishResourceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_PublishResourceRequest proto.InternalMessageInfo + +func (m *PublishResourceRequest) GetAuthorizationContext() *AuthorizationContext { + if m != nil { + return m.AuthorizationContext + } + return nil +} + +func (m *PublishResourceRequest) GetResourceId() string { + if m != nil { + return m.ResourceId + } + return "" +} + +func (m *PublishResourceRequest) GetResource() *Resource { + if m != nil { + return m.Resource + } + return nil +} + +func (m *PublishResourceRequest) GetTimeToLive() int32 { + if m != nil { + return m.TimeToLive + } + return 0 +} + +func (m *PublishResourceRequest) GetCommandMetadata() *CommandMetadata { + if m != nil { + return m.CommandMetadata + } + return nil +} + +type PublishResourceResponse struct { + AuditContext *AuditContext `protobuf:"bytes,1,opt,name=audit_context,json=auditContext,proto3" json:"audit_context,omitempty"` + InstanceId int64 `protobuf:"varint,2,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"` +} + +func (m *PublishResourceResponse) Reset() { *m = PublishResourceResponse{} } +func (m *PublishResourceResponse) String() string { return proto.CompactTextString(m) } +func (*PublishResourceResponse) ProtoMessage() {} +func (*PublishResourceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_edd018fc297633a7, []int{3} +} +func (m *PublishResourceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PublishResourceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PublishResourceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PublishResourceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_PublishResourceResponse.Merge(m, src) +} +func (m *PublishResourceResponse) XXX_Size() int { + return m.Size() +} +func (m *PublishResourceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_PublishResourceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_PublishResourceResponse proto.InternalMessageInfo + +func (m *PublishResourceResponse) GetAuditContext() *AuditContext { + if m != nil { + return m.AuditContext + } + return nil +} + +func (m *PublishResourceResponse) GetInstanceId() int64 { + if m != nil { + return m.InstanceId + } + return 0 +} + +// https://github.com/openconnectivityfoundation/core/blob/master/oic.wk.rd.raml #Specification CR needed +type UnpublishResourceRequest struct { + AuthorizationContext *AuthorizationContext `protobuf:"bytes,1,opt,name=authorization_context,json=authorizationContext,proto3" json:"authorization_context,omitempty"` + ResourceId string `protobuf:"bytes,2,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + CommandMetadata *CommandMetadata `protobuf:"bytes,3,opt,name=command_metadata,json=commandMetadata,proto3" json:"command_metadata,omitempty"` +} + +func (m *UnpublishResourceRequest) Reset() { *m = UnpublishResourceRequest{} } +func (m *UnpublishResourceRequest) String() string { return proto.CompactTextString(m) } +func (*UnpublishResourceRequest) ProtoMessage() {} +func (*UnpublishResourceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_edd018fc297633a7, []int{4} +} +func (m *UnpublishResourceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UnpublishResourceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UnpublishResourceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UnpublishResourceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UnpublishResourceRequest.Merge(m, src) +} +func (m *UnpublishResourceRequest) XXX_Size() int { + return m.Size() +} +func (m *UnpublishResourceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UnpublishResourceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_UnpublishResourceRequest proto.InternalMessageInfo + +func (m *UnpublishResourceRequest) GetAuthorizationContext() *AuthorizationContext { + if m != nil { + return m.AuthorizationContext + } + return nil +} + +func (m *UnpublishResourceRequest) GetResourceId() string { + if m != nil { + return m.ResourceId + } + return "" +} + +func (m *UnpublishResourceRequest) GetCommandMetadata() *CommandMetadata { + if m != nil { + return m.CommandMetadata + } + return nil +} + +type UnpublishResourceResponse struct { + AuditContext *AuditContext `protobuf:"bytes,1,opt,name=audit_context,json=auditContext,proto3" json:"audit_context,omitempty"` +} + +func (m *UnpublishResourceResponse) Reset() { *m = UnpublishResourceResponse{} } +func (m *UnpublishResourceResponse) String() string { return proto.CompactTextString(m) } +func (*UnpublishResourceResponse) ProtoMessage() {} +func (*UnpublishResourceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_edd018fc297633a7, []int{5} +} +func (m *UnpublishResourceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UnpublishResourceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UnpublishResourceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UnpublishResourceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_UnpublishResourceResponse.Merge(m, src) +} +func (m *UnpublishResourceResponse) XXX_Size() int { + return m.Size() +} +func (m *UnpublishResourceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_UnpublishResourceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_UnpublishResourceResponse proto.InternalMessageInfo + +func (m *UnpublishResourceResponse) GetAuditContext() *AuditContext { + if m != nil { + return m.AuditContext + } + return nil +} + +type NotifyResourceChangedRequest struct { + AuthorizationContext *AuthorizationContext `protobuf:"bytes,1,opt,name=authorization_context,json=authorizationContext,proto3" json:"authorization_context,omitempty"` + ResourceId string `protobuf:"bytes,2,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + Content *Content `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` + CommandMetadata *CommandMetadata `protobuf:"bytes,4,opt,name=command_metadata,json=commandMetadata,proto3" json:"command_metadata,omitempty"` + Status Status `protobuf:"varint,5,opt,name=status,proto3,enum=ocf.cloud.resourceaggregate.pb.Status" json:"status,omitempty"` +} + +func (m *NotifyResourceChangedRequest) Reset() { *m = NotifyResourceChangedRequest{} } +func (m *NotifyResourceChangedRequest) String() string { return proto.CompactTextString(m) } +func (*NotifyResourceChangedRequest) ProtoMessage() {} +func (*NotifyResourceChangedRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_edd018fc297633a7, []int{6} +} +func (m *NotifyResourceChangedRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NotifyResourceChangedRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NotifyResourceChangedRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NotifyResourceChangedRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_NotifyResourceChangedRequest.Merge(m, src) +} +func (m *NotifyResourceChangedRequest) XXX_Size() int { + return m.Size() +} +func (m *NotifyResourceChangedRequest) XXX_DiscardUnknown() { + xxx_messageInfo_NotifyResourceChangedRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_NotifyResourceChangedRequest proto.InternalMessageInfo + +func (m *NotifyResourceChangedRequest) GetAuthorizationContext() *AuthorizationContext { + if m != nil { + return m.AuthorizationContext + } + return nil +} + +func (m *NotifyResourceChangedRequest) GetResourceId() string { + if m != nil { + return m.ResourceId + } + return "" +} + +func (m *NotifyResourceChangedRequest) GetContent() *Content { + if m != nil { + return m.Content + } + return nil +} + +func (m *NotifyResourceChangedRequest) GetCommandMetadata() *CommandMetadata { + if m != nil { + return m.CommandMetadata + } + return nil +} + +func (m *NotifyResourceChangedRequest) GetStatus() Status { + if m != nil { + return m.Status + } + return Status_UNKNOWN +} + +type NotifyResourceChangedResponse struct { + AuditContext *AuditContext `protobuf:"bytes,1,opt,name=audit_context,json=auditContext,proto3" json:"audit_context,omitempty"` +} + +func (m *NotifyResourceChangedResponse) Reset() { *m = NotifyResourceChangedResponse{} } +func (m *NotifyResourceChangedResponse) String() string { return proto.CompactTextString(m) } +func (*NotifyResourceChangedResponse) ProtoMessage() {} +func (*NotifyResourceChangedResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_edd018fc297633a7, []int{7} +} +func (m *NotifyResourceChangedResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NotifyResourceChangedResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NotifyResourceChangedResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NotifyResourceChangedResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_NotifyResourceChangedResponse.Merge(m, src) +} +func (m *NotifyResourceChangedResponse) XXX_Size() int { + return m.Size() +} +func (m *NotifyResourceChangedResponse) XXX_DiscardUnknown() { + xxx_messageInfo_NotifyResourceChangedResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_NotifyResourceChangedResponse proto.InternalMessageInfo + +func (m *NotifyResourceChangedResponse) GetAuditContext() *AuditContext { + if m != nil { + return m.AuditContext + } + return nil +} + +type UpdateResourceRequest struct { + AuthorizationContext *AuthorizationContext `protobuf:"bytes,1,opt,name=authorization_context,json=authorizationContext,proto3" json:"authorization_context,omitempty"` + ResourceId string `protobuf:"bytes,2,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + CorrelationId string `protobuf:"bytes,3,opt,name=correlation_id,json=correlationId,proto3" json:"correlation_id,omitempty"` + Content *Content `protobuf:"bytes,4,opt,name=content,proto3" json:"content,omitempty"` + ResourceInterface string `protobuf:"bytes,5,opt,name=resource_interface,json=resourceInterface,proto3" json:"resource_interface,omitempty"` + CommandMetadata *CommandMetadata `protobuf:"bytes,100,opt,name=command_metadata,json=commandMetadata,proto3" json:"command_metadata,omitempty"` +} + +func (m *UpdateResourceRequest) Reset() { *m = UpdateResourceRequest{} } +func (m *UpdateResourceRequest) String() string { return proto.CompactTextString(m) } +func (*UpdateResourceRequest) ProtoMessage() {} +func (*UpdateResourceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_edd018fc297633a7, []int{8} +} +func (m *UpdateResourceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateResourceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateResourceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateResourceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateResourceRequest.Merge(m, src) +} +func (m *UpdateResourceRequest) XXX_Size() int { + return m.Size() +} +func (m *UpdateResourceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateResourceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdateResourceRequest proto.InternalMessageInfo + +func (m *UpdateResourceRequest) GetAuthorizationContext() *AuthorizationContext { + if m != nil { + return m.AuthorizationContext + } + return nil +} + +func (m *UpdateResourceRequest) GetResourceId() string { + if m != nil { + return m.ResourceId + } + return "" +} + +func (m *UpdateResourceRequest) GetCorrelationId() string { + if m != nil { + return m.CorrelationId + } + return "" +} + +func (m *UpdateResourceRequest) GetContent() *Content { + if m != nil { + return m.Content + } + return nil +} + +func (m *UpdateResourceRequest) GetResourceInterface() string { + if m != nil { + return m.ResourceInterface + } + return "" +} + +func (m *UpdateResourceRequest) GetCommandMetadata() *CommandMetadata { + if m != nil { + return m.CommandMetadata + } + return nil +} + +type UpdateResourceResponse struct { + AuditContext *AuditContext `protobuf:"bytes,1,opt,name=audit_context,json=auditContext,proto3" json:"audit_context,omitempty"` +} + +func (m *UpdateResourceResponse) Reset() { *m = UpdateResourceResponse{} } +func (m *UpdateResourceResponse) String() string { return proto.CompactTextString(m) } +func (*UpdateResourceResponse) ProtoMessage() {} +func (*UpdateResourceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_edd018fc297633a7, []int{9} +} +func (m *UpdateResourceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *UpdateResourceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_UpdateResourceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *UpdateResourceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_UpdateResourceResponse.Merge(m, src) +} +func (m *UpdateResourceResponse) XXX_Size() int { + return m.Size() +} +func (m *UpdateResourceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_UpdateResourceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_UpdateResourceResponse proto.InternalMessageInfo + +func (m *UpdateResourceResponse) GetAuditContext() *AuditContext { + if m != nil { + return m.AuditContext + } + return nil +} + +type ConfirmResourceUpdateRequest struct { + AuthorizationContext *AuthorizationContext `protobuf:"bytes,1,opt,name=authorization_context,json=authorizationContext,proto3" json:"authorization_context,omitempty"` + ResourceId string `protobuf:"bytes,2,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + CorrelationId string `protobuf:"bytes,3,opt,name=correlation_id,json=correlationId,proto3" json:"correlation_id,omitempty"` + Status Status `protobuf:"varint,4,opt,name=status,proto3,enum=ocf.cloud.resourceaggregate.pb.Status" json:"status,omitempty"` + Content *Content `protobuf:"bytes,5,opt,name=content,proto3" json:"content,omitempty"` + CommandMetadata *CommandMetadata `protobuf:"bytes,6,opt,name=command_metadata,json=commandMetadata,proto3" json:"command_metadata,omitempty"` +} + +func (m *ConfirmResourceUpdateRequest) Reset() { *m = ConfirmResourceUpdateRequest{} } +func (m *ConfirmResourceUpdateRequest) String() string { return proto.CompactTextString(m) } +func (*ConfirmResourceUpdateRequest) ProtoMessage() {} +func (*ConfirmResourceUpdateRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_edd018fc297633a7, []int{10} +} +func (m *ConfirmResourceUpdateRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConfirmResourceUpdateRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConfirmResourceUpdateRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ConfirmResourceUpdateRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConfirmResourceUpdateRequest.Merge(m, src) +} +func (m *ConfirmResourceUpdateRequest) XXX_Size() int { + return m.Size() +} +func (m *ConfirmResourceUpdateRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ConfirmResourceUpdateRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ConfirmResourceUpdateRequest proto.InternalMessageInfo + +func (m *ConfirmResourceUpdateRequest) GetAuthorizationContext() *AuthorizationContext { + if m != nil { + return m.AuthorizationContext + } + return nil +} + +func (m *ConfirmResourceUpdateRequest) GetResourceId() string { + if m != nil { + return m.ResourceId + } + return "" +} + +func (m *ConfirmResourceUpdateRequest) GetCorrelationId() string { + if m != nil { + return m.CorrelationId + } + return "" +} + +func (m *ConfirmResourceUpdateRequest) GetStatus() Status { + if m != nil { + return m.Status + } + return Status_UNKNOWN +} + +func (m *ConfirmResourceUpdateRequest) GetContent() *Content { + if m != nil { + return m.Content + } + return nil +} + +func (m *ConfirmResourceUpdateRequest) GetCommandMetadata() *CommandMetadata { + if m != nil { + return m.CommandMetadata + } + return nil +} + +type ConfirmResourceUpdateResponse struct { + AuditContext *AuditContext `protobuf:"bytes,1,opt,name=audit_context,json=auditContext,proto3" json:"audit_context,omitempty"` +} + +func (m *ConfirmResourceUpdateResponse) Reset() { *m = ConfirmResourceUpdateResponse{} } +func (m *ConfirmResourceUpdateResponse) String() string { return proto.CompactTextString(m) } +func (*ConfirmResourceUpdateResponse) ProtoMessage() {} +func (*ConfirmResourceUpdateResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_edd018fc297633a7, []int{11} +} +func (m *ConfirmResourceUpdateResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConfirmResourceUpdateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConfirmResourceUpdateResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ConfirmResourceUpdateResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConfirmResourceUpdateResponse.Merge(m, src) +} +func (m *ConfirmResourceUpdateResponse) XXX_Size() int { + return m.Size() +} +func (m *ConfirmResourceUpdateResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ConfirmResourceUpdateResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ConfirmResourceUpdateResponse proto.InternalMessageInfo + +func (m *ConfirmResourceUpdateResponse) GetAuditContext() *AuditContext { + if m != nil { + return m.AuditContext + } + return nil +} + +type RetrieveResourceRequest struct { + AuthorizationContext *AuthorizationContext `protobuf:"bytes,1,opt,name=authorization_context,json=authorizationContext,proto3" json:"authorization_context,omitempty"` + ResourceId string `protobuf:"bytes,2,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + CorrelationId string `protobuf:"bytes,3,opt,name=correlation_id,json=correlationId,proto3" json:"correlation_id,omitempty"` + ResourceInterface string `protobuf:"bytes,4,opt,name=resource_interface,json=resourceInterface,proto3" json:"resource_interface,omitempty"` + CommandMetadata *CommandMetadata `protobuf:"bytes,5,opt,name=command_metadata,json=commandMetadata,proto3" json:"command_metadata,omitempty"` +} + +func (m *RetrieveResourceRequest) Reset() { *m = RetrieveResourceRequest{} } +func (m *RetrieveResourceRequest) String() string { return proto.CompactTextString(m) } +func (*RetrieveResourceRequest) ProtoMessage() {} +func (*RetrieveResourceRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_edd018fc297633a7, []int{12} +} +func (m *RetrieveResourceRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RetrieveResourceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RetrieveResourceRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RetrieveResourceRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RetrieveResourceRequest.Merge(m, src) +} +func (m *RetrieveResourceRequest) XXX_Size() int { + return m.Size() +} +func (m *RetrieveResourceRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RetrieveResourceRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RetrieveResourceRequest proto.InternalMessageInfo + +func (m *RetrieveResourceRequest) GetAuthorizationContext() *AuthorizationContext { + if m != nil { + return m.AuthorizationContext + } + return nil +} + +func (m *RetrieveResourceRequest) GetResourceId() string { + if m != nil { + return m.ResourceId + } + return "" +} + +func (m *RetrieveResourceRequest) GetCorrelationId() string { + if m != nil { + return m.CorrelationId + } + return "" +} + +func (m *RetrieveResourceRequest) GetResourceInterface() string { + if m != nil { + return m.ResourceInterface + } + return "" +} + +func (m *RetrieveResourceRequest) GetCommandMetadata() *CommandMetadata { + if m != nil { + return m.CommandMetadata + } + return nil +} + +type RetrieveResourceResponse struct { + AuditContext *AuditContext `protobuf:"bytes,1,opt,name=audit_context,json=auditContext,proto3" json:"audit_context,omitempty"` +} + +func (m *RetrieveResourceResponse) Reset() { *m = RetrieveResourceResponse{} } +func (m *RetrieveResourceResponse) String() string { return proto.CompactTextString(m) } +func (*RetrieveResourceResponse) ProtoMessage() {} +func (*RetrieveResourceResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_edd018fc297633a7, []int{13} +} +func (m *RetrieveResourceResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RetrieveResourceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RetrieveResourceResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RetrieveResourceResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_RetrieveResourceResponse.Merge(m, src) +} +func (m *RetrieveResourceResponse) XXX_Size() int { + return m.Size() +} +func (m *RetrieveResourceResponse) XXX_DiscardUnknown() { + xxx_messageInfo_RetrieveResourceResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_RetrieveResourceResponse proto.InternalMessageInfo + +func (m *RetrieveResourceResponse) GetAuditContext() *AuditContext { + if m != nil { + return m.AuditContext + } + return nil +} + +type ConfirmResourceRetrieveRequest struct { + AuthorizationContext *AuthorizationContext `protobuf:"bytes,1,opt,name=authorization_context,json=authorizationContext,proto3" json:"authorization_context,omitempty"` + ResourceId string `protobuf:"bytes,2,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + CorrelationId string `protobuf:"bytes,3,opt,name=correlation_id,json=correlationId,proto3" json:"correlation_id,omitempty"` + Status Status `protobuf:"varint,4,opt,name=status,proto3,enum=ocf.cloud.resourceaggregate.pb.Status" json:"status,omitempty"` + Content *Content `protobuf:"bytes,5,opt,name=content,proto3" json:"content,omitempty"` + CommandMetadata *CommandMetadata `protobuf:"bytes,6,opt,name=command_metadata,json=commandMetadata,proto3" json:"command_metadata,omitempty"` +} + +func (m *ConfirmResourceRetrieveRequest) Reset() { *m = ConfirmResourceRetrieveRequest{} } +func (m *ConfirmResourceRetrieveRequest) String() string { return proto.CompactTextString(m) } +func (*ConfirmResourceRetrieveRequest) ProtoMessage() {} +func (*ConfirmResourceRetrieveRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_edd018fc297633a7, []int{14} +} +func (m *ConfirmResourceRetrieveRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConfirmResourceRetrieveRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConfirmResourceRetrieveRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ConfirmResourceRetrieveRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConfirmResourceRetrieveRequest.Merge(m, src) +} +func (m *ConfirmResourceRetrieveRequest) XXX_Size() int { + return m.Size() +} +func (m *ConfirmResourceRetrieveRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ConfirmResourceRetrieveRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ConfirmResourceRetrieveRequest proto.InternalMessageInfo + +func (m *ConfirmResourceRetrieveRequest) GetAuthorizationContext() *AuthorizationContext { + if m != nil { + return m.AuthorizationContext + } + return nil +} + +func (m *ConfirmResourceRetrieveRequest) GetResourceId() string { + if m != nil { + return m.ResourceId + } + return "" +} + +func (m *ConfirmResourceRetrieveRequest) GetCorrelationId() string { + if m != nil { + return m.CorrelationId + } + return "" +} + +func (m *ConfirmResourceRetrieveRequest) GetStatus() Status { + if m != nil { + return m.Status + } + return Status_UNKNOWN +} + +func (m *ConfirmResourceRetrieveRequest) GetContent() *Content { + if m != nil { + return m.Content + } + return nil +} + +func (m *ConfirmResourceRetrieveRequest) GetCommandMetadata() *CommandMetadata { + if m != nil { + return m.CommandMetadata + } + return nil +} + +type ConfirmResourceRetrieveResponse struct { + AuditContext *AuditContext `protobuf:"bytes,1,opt,name=audit_context,json=auditContext,proto3" json:"audit_context,omitempty"` +} + +func (m *ConfirmResourceRetrieveResponse) Reset() { *m = ConfirmResourceRetrieveResponse{} } +func (m *ConfirmResourceRetrieveResponse) String() string { return proto.CompactTextString(m) } +func (*ConfirmResourceRetrieveResponse) ProtoMessage() {} +func (*ConfirmResourceRetrieveResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_edd018fc297633a7, []int{15} +} +func (m *ConfirmResourceRetrieveResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ConfirmResourceRetrieveResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ConfirmResourceRetrieveResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ConfirmResourceRetrieveResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ConfirmResourceRetrieveResponse.Merge(m, src) +} +func (m *ConfirmResourceRetrieveResponse) XXX_Size() int { + return m.Size() +} +func (m *ConfirmResourceRetrieveResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ConfirmResourceRetrieveResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ConfirmResourceRetrieveResponse proto.InternalMessageInfo + +func (m *ConfirmResourceRetrieveResponse) GetAuditContext() *AuditContext { + if m != nil { + return m.AuditContext + } + return nil +} + +func init() { + proto.RegisterType((*AuthorizationContext)(nil), "ocf.cloud.resourceaggregate.pb.AuthorizationContext") + proto.RegisterType((*CommandMetadata)(nil), "ocf.cloud.resourceaggregate.pb.CommandMetadata") + proto.RegisterType((*PublishResourceRequest)(nil), "ocf.cloud.resourceaggregate.pb.PublishResourceRequest") + proto.RegisterType((*PublishResourceResponse)(nil), "ocf.cloud.resourceaggregate.pb.PublishResourceResponse") + proto.RegisterType((*UnpublishResourceRequest)(nil), "ocf.cloud.resourceaggregate.pb.UnpublishResourceRequest") + proto.RegisterType((*UnpublishResourceResponse)(nil), "ocf.cloud.resourceaggregate.pb.UnpublishResourceResponse") + proto.RegisterType((*NotifyResourceChangedRequest)(nil), "ocf.cloud.resourceaggregate.pb.NotifyResourceChangedRequest") + proto.RegisterType((*NotifyResourceChangedResponse)(nil), "ocf.cloud.resourceaggregate.pb.NotifyResourceChangedResponse") + proto.RegisterType((*UpdateResourceRequest)(nil), "ocf.cloud.resourceaggregate.pb.UpdateResourceRequest") + proto.RegisterType((*UpdateResourceResponse)(nil), "ocf.cloud.resourceaggregate.pb.UpdateResourceResponse") + proto.RegisterType((*ConfirmResourceUpdateRequest)(nil), "ocf.cloud.resourceaggregate.pb.ConfirmResourceUpdateRequest") + proto.RegisterType((*ConfirmResourceUpdateResponse)(nil), "ocf.cloud.resourceaggregate.pb.ConfirmResourceUpdateResponse") + proto.RegisterType((*RetrieveResourceRequest)(nil), "ocf.cloud.resourceaggregate.pb.RetrieveResourceRequest") + proto.RegisterType((*RetrieveResourceResponse)(nil), "ocf.cloud.resourceaggregate.pb.RetrieveResourceResponse") + proto.RegisterType((*ConfirmResourceRetrieveRequest)(nil), "ocf.cloud.resourceaggregate.pb.ConfirmResourceRetrieveRequest") + proto.RegisterType((*ConfirmResourceRetrieveResponse)(nil), "ocf.cloud.resourceaggregate.pb.ConfirmResourceRetrieveResponse") +} + +func init() { proto.RegisterFile("pb/commands.proto", fileDescriptor_edd018fc297633a7) } + +var fileDescriptor_edd018fc297633a7 = []byte{ + // 721 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x57, 0xcd, 0x6e, 0xd3, 0x4c, + 0x14, 0xad, 0xe3, 0x34, 0x6d, 0x6f, 0xff, 0xbe, 0x5a, 0xfd, 0xc9, 0x57, 0x5a, 0xb7, 0x32, 0x02, + 0xba, 0x20, 0xb6, 0x54, 0xba, 0x43, 0xaa, 0x54, 0xc2, 0x26, 0x52, 0xa9, 0xc0, 0xd0, 0x4d, 0x37, + 0xd1, 0x78, 0x66, 0x92, 0x8c, 0x88, 0x67, 0x8c, 0x3d, 0x8e, 0x80, 0x25, 0x3b, 0x16, 0x48, 0x2c, + 0x79, 0x0c, 0xc4, 0x53, 0xb0, 0xec, 0x0e, 0x96, 0xa8, 0xdd, 0xf0, 0x18, 0x28, 0xae, 0x1d, 0xa7, + 0xa9, 0x4b, 0x94, 0x2a, 0x91, 0x02, 0x62, 0x39, 0x77, 0xe6, 0xde, 0x73, 0xe7, 0x9c, 0x73, 0x27, + 0x31, 0x2c, 0x79, 0x8e, 0x85, 0x85, 0xeb, 0x22, 0x4e, 0x02, 0xd3, 0xf3, 0x85, 0x14, 0x9a, 0x2e, + 0x70, 0xcd, 0xc4, 0x4d, 0x11, 0x12, 0xd3, 0xa7, 0x81, 0x08, 0x7d, 0x4c, 0x51, 0xbd, 0xee, 0xd3, + 0x3a, 0x92, 0xd4, 0xf4, 0x9c, 0xf5, 0xfd, 0x3a, 0x93, 0x8d, 0xd0, 0x31, 0xb1, 0x70, 0xad, 0xba, + 0x28, 0x09, 0x5c, 0xb3, 0x04, 0xae, 0x95, 0xa2, 0x0c, 0x2b, 0xc9, 0x28, 0x75, 0x52, 0x2c, 0xcf, + 0xb1, 0x68, 0x8b, 0x72, 0x19, 0xd7, 0x5f, 0x3f, 0x18, 0x3c, 0x3f, 0x89, 0xc6, 0x25, 0x8c, 0x43, + 0x58, 0x3e, 0x08, 0x65, 0x43, 0xf8, 0xec, 0x2d, 0x92, 0x4c, 0xf0, 0xb2, 0xe0, 0x92, 0xbe, 0x96, + 0xda, 0x1a, 0x4c, 0x85, 0x01, 0xf5, 0xab, 0x8c, 0x14, 0x95, 0x6d, 0x65, 0x67, 0xc6, 0x2e, 0xb4, + 0x97, 0x15, 0xa2, 0xdd, 0x82, 0x19, 0x42, 0x5b, 0x0c, 0xd3, 0xf6, 0x56, 0x2e, 0xda, 0x9a, 0xbe, + 0x08, 0x54, 0x88, 0x61, 0xc3, 0x62, 0xf9, 0x82, 0x82, 0x27, 0x54, 0x22, 0x82, 0x24, 0xd2, 0x6e, + 0xc3, 0x3c, 0x16, 0x9c, 0x53, 0xdc, 0xae, 0x9e, 0x96, 0x9b, 0x4b, 0x83, 0x15, 0xa2, 0xad, 0xc3, + 0x74, 0x40, 0x5f, 0x85, 0x94, 0x63, 0x1a, 0xd5, 0xcc, 0xdb, 0x9d, 0xb5, 0xf1, 0x33, 0x07, 0xab, + 0x4f, 0x43, 0xa7, 0xc9, 0x82, 0x86, 0x1d, 0x37, 0x6f, 0xb7, 0xf7, 0x02, 0xa9, 0x31, 0x58, 0x41, + 0xdd, 0xcd, 0x57, 0xf1, 0x45, 0xf7, 0x11, 0xc6, 0xec, 0xee, 0x9e, 0xf9, 0x7b, 0xfe, 0xcd, 0xac, + 0x9b, 0xdb, 0xcb, 0x28, 0x8b, 0x8f, 0x2d, 0x98, 0x4d, 0x4a, 0xa4, 0x17, 0x87, 0x24, 0x54, 0x21, + 0xda, 0x63, 0x98, 0x4e, 0x56, 0x45, 0x35, 0x82, 0xdf, 0xe9, 0x07, 0xdf, 0xb9, 0x4e, 0x27, 0x53, + 0xdb, 0x86, 0x39, 0xc9, 0x5c, 0x5a, 0x95, 0xa2, 0xda, 0x64, 0x2d, 0x5a, 0xcc, 0x6f, 0x2b, 0x3b, + 0x93, 0x36, 0xb4, 0x63, 0x2f, 0xc4, 0x21, 0x6b, 0x51, 0xed, 0x04, 0xfe, 0x8b, 0x5d, 0x56, 0x75, + 0x63, 0x8e, 0x8b, 0x93, 0x11, 0x9e, 0xd5, 0x0f, 0xaf, 0x47, 0x1a, 0x7b, 0x11, 0x5f, 0x0e, 0x18, + 0x1f, 0x14, 0x58, 0xbb, 0x42, 0x75, 0xe0, 0x09, 0x1e, 0x50, 0xed, 0x19, 0xcc, 0xa3, 0x90, 0x30, + 0xd9, 0xc3, 0xf1, 0xfd, 0xfe, 0x1c, 0x13, 0x26, 0x13, 0x6e, 0xe7, 0x50, 0xd7, 0xaa, 0xcd, 0x29, + 0xe3, 0x81, 0x44, 0x3c, 0xe5, 0x54, 0xb5, 0x21, 0x09, 0x55, 0x88, 0xf1, 0x2e, 0x07, 0xc5, 0x63, + 0xee, 0x8d, 0xbf, 0xf8, 0x59, 0xa2, 0xa8, 0x43, 0x12, 0x85, 0xc3, 0xff, 0x19, 0x1c, 0x8c, 0x4c, + 0x15, 0xe3, 0xbd, 0x0a, 0x1b, 0x47, 0x42, 0xb2, 0xda, 0x9b, 0x04, 0xad, 0xdc, 0x40, 0xbc, 0x4e, + 0xc9, 0x38, 0x12, 0x7f, 0x00, 0x53, 0x11, 0x3a, 0x97, 0x31, 0xdf, 0xf7, 0xfa, 0xf3, 0x1d, 0x1d, + 0xb7, 0x93, 0xbc, 0x4c, 0xed, 0xf2, 0xc3, 0xd1, 0x4e, 0xdb, 0x87, 0x42, 0x20, 0x91, 0x0c, 0x83, + 0x68, 0x44, 0x17, 0x76, 0xef, 0xf6, 0xab, 0xf8, 0x3c, 0x3a, 0x6d, 0xc7, 0x59, 0x86, 0x0f, 0x9b, + 0xd7, 0x48, 0x31, 0x3a, 0xfd, 0x3f, 0xa9, 0xb0, 0x72, 0xec, 0x11, 0x24, 0xe9, 0x38, 0x4f, 0xdc, + 0x1d, 0x58, 0xc0, 0xc2, 0xf7, 0x69, 0x13, 0x25, 0xbf, 0x2b, 0x6a, 0x74, 0x66, 0xbe, 0x2b, 0x7a, + 0xd9, 0x1f, 0xf9, 0x1b, 0xfa, 0xa3, 0x04, 0x5a, 0xda, 0x0a, 0x97, 0xd4, 0xaf, 0x21, 0x4c, 0x23, + 0x3d, 0x67, 0xec, 0xa5, 0x4e, 0x47, 0xc9, 0x46, 0xa6, 0x9d, 0xc8, 0x90, 0x9e, 0x82, 0x97, 0xb0, + 0xda, 0xab, 0xcc, 0xe8, 0x7c, 0xf0, 0x59, 0x85, 0x8d, 0xb2, 0xe0, 0x35, 0xe6, 0xbb, 0x09, 0x5c, + 0x02, 0xfe, 0xc7, 0xda, 0x21, 0x9d, 0xc7, 0xfc, 0x4d, 0xe6, 0xb1, 0xdb, 0x4e, 0x93, 0x43, 0x7c, + 0x6e, 0x0a, 0x43, 0xf2, 0x87, 0x0f, 0x9b, 0xd7, 0x28, 0x36, 0x3a, 0x9b, 0x7c, 0xcb, 0xc1, 0x9a, + 0x4d, 0xa5, 0xcf, 0x68, 0xeb, 0x6f, 0x78, 0x30, 0xb2, 0xa7, 0x3d, 0x3f, 0xc8, 0xb4, 0x0f, 0xeb, + 0xdf, 0x98, 0x0b, 0xc5, 0xab, 0xc4, 0x8e, 0x4e, 0xc8, 0x2f, 0x2a, 0xe8, 0x3d, 0xee, 0x49, 0xe1, + 0xff, 0x4d, 0xfc, 0x78, 0x4e, 0xbc, 0x84, 0xad, 0x6b, 0x35, 0x1b, 0x99, 0x55, 0x1e, 0x1d, 0x7d, + 0x3d, 0xd3, 0x95, 0xd3, 0x33, 0x5d, 0xf9, 0x71, 0xa6, 0x2b, 0x1f, 0xcf, 0xf5, 0x89, 0xd3, 0x73, + 0x7d, 0xe2, 0xfb, 0xb9, 0x3e, 0x71, 0xb2, 0x37, 0xf0, 0x17, 0xe9, 0x43, 0xcf, 0x71, 0x0a, 0xd1, + 0xb7, 0xe8, 0x83, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xa1, 0xb9, 0x9c, 0xc9, 0x43, 0x0f, 0x00, + 0x00, +} + +func (m *AuthorizationContext) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AuthorizationContext) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AuthorizationContext) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintCommands(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0x12 + } + if len(m.UserId) > 0 { + i -= len(m.UserId) + copy(dAtA[i:], m.UserId) + i = encodeVarintCommands(dAtA, i, uint64(len(m.UserId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *CommandMetadata) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *CommandMetadata) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *CommandMetadata) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Sequence != 0 { + i = encodeVarintCommands(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x10 + } + if len(m.ConnectionId) > 0 { + i -= len(m.ConnectionId) + copy(dAtA[i:], m.ConnectionId) + i = encodeVarintCommands(dAtA, i, uint64(len(m.ConnectionId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PublishResourceRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PublishResourceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PublishResourceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.CommandMetadata != nil { + { + size, err := m.CommandMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if m.TimeToLive != 0 { + i = encodeVarintCommands(dAtA, i, uint64(m.TimeToLive)) + i-- + dAtA[i] = 0x20 + } + if m.Resource != nil { + { + size, err := m.Resource.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.ResourceId) > 0 { + i -= len(m.ResourceId) + copy(dAtA[i:], m.ResourceId) + i = encodeVarintCommands(dAtA, i, uint64(len(m.ResourceId))) + i-- + dAtA[i] = 0x12 + } + if m.AuthorizationContext != nil { + { + size, err := m.AuthorizationContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *PublishResourceResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PublishResourceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PublishResourceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.InstanceId != 0 { + i = encodeVarintCommands(dAtA, i, uint64(m.InstanceId)) + i-- + dAtA[i] = 0x10 + } + if m.AuditContext != nil { + { + size, err := m.AuditContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UnpublishResourceRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UnpublishResourceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UnpublishResourceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.CommandMetadata != nil { + { + size, err := m.CommandMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.ResourceId) > 0 { + i -= len(m.ResourceId) + copy(dAtA[i:], m.ResourceId) + i = encodeVarintCommands(dAtA, i, uint64(len(m.ResourceId))) + i-- + dAtA[i] = 0x12 + } + if m.AuthorizationContext != nil { + { + size, err := m.AuthorizationContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UnpublishResourceResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UnpublishResourceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UnpublishResourceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.AuditContext != nil { + { + size, err := m.AuditContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *NotifyResourceChangedRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NotifyResourceChangedRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NotifyResourceChangedRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Status != 0 { + i = encodeVarintCommands(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x28 + } + if m.CommandMetadata != nil { + { + size, err := m.CommandMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.Content != nil { + { + size, err := m.Content.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.ResourceId) > 0 { + i -= len(m.ResourceId) + copy(dAtA[i:], m.ResourceId) + i = encodeVarintCommands(dAtA, i, uint64(len(m.ResourceId))) + i-- + dAtA[i] = 0x12 + } + if m.AuthorizationContext != nil { + { + size, err := m.AuthorizationContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *NotifyResourceChangedResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NotifyResourceChangedResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NotifyResourceChangedResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.AuditContext != nil { + { + size, err := m.AuditContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UpdateResourceRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UpdateResourceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateResourceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.CommandMetadata != nil { + { + size, err := m.CommandMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0xa2 + } + if len(m.ResourceInterface) > 0 { + i -= len(m.ResourceInterface) + copy(dAtA[i:], m.ResourceInterface) + i = encodeVarintCommands(dAtA, i, uint64(len(m.ResourceInterface))) + i-- + dAtA[i] = 0x2a + } + if m.Content != nil { + { + size, err := m.Content.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if len(m.CorrelationId) > 0 { + i -= len(m.CorrelationId) + copy(dAtA[i:], m.CorrelationId) + i = encodeVarintCommands(dAtA, i, uint64(len(m.CorrelationId))) + i-- + dAtA[i] = 0x1a + } + if len(m.ResourceId) > 0 { + i -= len(m.ResourceId) + copy(dAtA[i:], m.ResourceId) + i = encodeVarintCommands(dAtA, i, uint64(len(m.ResourceId))) + i-- + dAtA[i] = 0x12 + } + if m.AuthorizationContext != nil { + { + size, err := m.AuthorizationContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *UpdateResourceResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *UpdateResourceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *UpdateResourceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.AuditContext != nil { + { + size, err := m.AuditContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ConfirmResourceUpdateRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ConfirmResourceUpdateRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConfirmResourceUpdateRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.CommandMetadata != nil { + { + size, err := m.CommandMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + if m.Content != nil { + { + size, err := m.Content.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if m.Status != 0 { + i = encodeVarintCommands(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x20 + } + if len(m.CorrelationId) > 0 { + i -= len(m.CorrelationId) + copy(dAtA[i:], m.CorrelationId) + i = encodeVarintCommands(dAtA, i, uint64(len(m.CorrelationId))) + i-- + dAtA[i] = 0x1a + } + if len(m.ResourceId) > 0 { + i -= len(m.ResourceId) + copy(dAtA[i:], m.ResourceId) + i = encodeVarintCommands(dAtA, i, uint64(len(m.ResourceId))) + i-- + dAtA[i] = 0x12 + } + if m.AuthorizationContext != nil { + { + size, err := m.AuthorizationContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ConfirmResourceUpdateResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ConfirmResourceUpdateResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConfirmResourceUpdateResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.AuditContext != nil { + { + size, err := m.AuditContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RetrieveResourceRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RetrieveResourceRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RetrieveResourceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.CommandMetadata != nil { + { + size, err := m.CommandMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if len(m.ResourceInterface) > 0 { + i -= len(m.ResourceInterface) + copy(dAtA[i:], m.ResourceInterface) + i = encodeVarintCommands(dAtA, i, uint64(len(m.ResourceInterface))) + i-- + dAtA[i] = 0x22 + } + if len(m.CorrelationId) > 0 { + i -= len(m.CorrelationId) + copy(dAtA[i:], m.CorrelationId) + i = encodeVarintCommands(dAtA, i, uint64(len(m.CorrelationId))) + i-- + dAtA[i] = 0x1a + } + if len(m.ResourceId) > 0 { + i -= len(m.ResourceId) + copy(dAtA[i:], m.ResourceId) + i = encodeVarintCommands(dAtA, i, uint64(len(m.ResourceId))) + i-- + dAtA[i] = 0x12 + } + if m.AuthorizationContext != nil { + { + size, err := m.AuthorizationContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *RetrieveResourceResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RetrieveResourceResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RetrieveResourceResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.AuditContext != nil { + { + size, err := m.AuditContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ConfirmResourceRetrieveRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ConfirmResourceRetrieveRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConfirmResourceRetrieveRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.CommandMetadata != nil { + { + size, err := m.CommandMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + if m.Content != nil { + { + size, err := m.Content.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if m.Status != 0 { + i = encodeVarintCommands(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x20 + } + if len(m.CorrelationId) > 0 { + i -= len(m.CorrelationId) + copy(dAtA[i:], m.CorrelationId) + i = encodeVarintCommands(dAtA, i, uint64(len(m.CorrelationId))) + i-- + dAtA[i] = 0x1a + } + if len(m.ResourceId) > 0 { + i -= len(m.ResourceId) + copy(dAtA[i:], m.ResourceId) + i = encodeVarintCommands(dAtA, i, uint64(len(m.ResourceId))) + i-- + dAtA[i] = 0x12 + } + if m.AuthorizationContext != nil { + { + size, err := m.AuthorizationContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ConfirmResourceRetrieveResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ConfirmResourceRetrieveResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ConfirmResourceRetrieveResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.AuditContext != nil { + { + size, err := m.AuditContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintCommands(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintCommands(dAtA []byte, offset int, v uint64) int { + offset -= sovCommands(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *AuthorizationContext) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.UserId) + if l > 0 { + n += 1 + l + sovCommands(uint64(l)) + } + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovCommands(uint64(l)) + } + return n +} + +func (m *CommandMetadata) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ConnectionId) + if l > 0 { + n += 1 + l + sovCommands(uint64(l)) + } + if m.Sequence != 0 { + n += 1 + sovCommands(uint64(m.Sequence)) + } + return n +} + +func (m *PublishResourceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AuthorizationContext != nil { + l = m.AuthorizationContext.Size() + n += 1 + l + sovCommands(uint64(l)) + } + l = len(m.ResourceId) + if l > 0 { + n += 1 + l + sovCommands(uint64(l)) + } + if m.Resource != nil { + l = m.Resource.Size() + n += 1 + l + sovCommands(uint64(l)) + } + if m.TimeToLive != 0 { + n += 1 + sovCommands(uint64(m.TimeToLive)) + } + if m.CommandMetadata != nil { + l = m.CommandMetadata.Size() + n += 1 + l + sovCommands(uint64(l)) + } + return n +} + +func (m *PublishResourceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AuditContext != nil { + l = m.AuditContext.Size() + n += 1 + l + sovCommands(uint64(l)) + } + if m.InstanceId != 0 { + n += 1 + sovCommands(uint64(m.InstanceId)) + } + return n +} + +func (m *UnpublishResourceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AuthorizationContext != nil { + l = m.AuthorizationContext.Size() + n += 1 + l + sovCommands(uint64(l)) + } + l = len(m.ResourceId) + if l > 0 { + n += 1 + l + sovCommands(uint64(l)) + } + if m.CommandMetadata != nil { + l = m.CommandMetadata.Size() + n += 1 + l + sovCommands(uint64(l)) + } + return n +} + +func (m *UnpublishResourceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AuditContext != nil { + l = m.AuditContext.Size() + n += 1 + l + sovCommands(uint64(l)) + } + return n +} + +func (m *NotifyResourceChangedRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AuthorizationContext != nil { + l = m.AuthorizationContext.Size() + n += 1 + l + sovCommands(uint64(l)) + } + l = len(m.ResourceId) + if l > 0 { + n += 1 + l + sovCommands(uint64(l)) + } + if m.Content != nil { + l = m.Content.Size() + n += 1 + l + sovCommands(uint64(l)) + } + if m.CommandMetadata != nil { + l = m.CommandMetadata.Size() + n += 1 + l + sovCommands(uint64(l)) + } + if m.Status != 0 { + n += 1 + sovCommands(uint64(m.Status)) + } + return n +} + +func (m *NotifyResourceChangedResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AuditContext != nil { + l = m.AuditContext.Size() + n += 1 + l + sovCommands(uint64(l)) + } + return n +} + +func (m *UpdateResourceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AuthorizationContext != nil { + l = m.AuthorizationContext.Size() + n += 1 + l + sovCommands(uint64(l)) + } + l = len(m.ResourceId) + if l > 0 { + n += 1 + l + sovCommands(uint64(l)) + } + l = len(m.CorrelationId) + if l > 0 { + n += 1 + l + sovCommands(uint64(l)) + } + if m.Content != nil { + l = m.Content.Size() + n += 1 + l + sovCommands(uint64(l)) + } + l = len(m.ResourceInterface) + if l > 0 { + n += 1 + l + sovCommands(uint64(l)) + } + if m.CommandMetadata != nil { + l = m.CommandMetadata.Size() + n += 2 + l + sovCommands(uint64(l)) + } + return n +} + +func (m *UpdateResourceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AuditContext != nil { + l = m.AuditContext.Size() + n += 1 + l + sovCommands(uint64(l)) + } + return n +} + +func (m *ConfirmResourceUpdateRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AuthorizationContext != nil { + l = m.AuthorizationContext.Size() + n += 1 + l + sovCommands(uint64(l)) + } + l = len(m.ResourceId) + if l > 0 { + n += 1 + l + sovCommands(uint64(l)) + } + l = len(m.CorrelationId) + if l > 0 { + n += 1 + l + sovCommands(uint64(l)) + } + if m.Status != 0 { + n += 1 + sovCommands(uint64(m.Status)) + } + if m.Content != nil { + l = m.Content.Size() + n += 1 + l + sovCommands(uint64(l)) + } + if m.CommandMetadata != nil { + l = m.CommandMetadata.Size() + n += 1 + l + sovCommands(uint64(l)) + } + return n +} + +func (m *ConfirmResourceUpdateResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AuditContext != nil { + l = m.AuditContext.Size() + n += 1 + l + sovCommands(uint64(l)) + } + return n +} + +func (m *RetrieveResourceRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AuthorizationContext != nil { + l = m.AuthorizationContext.Size() + n += 1 + l + sovCommands(uint64(l)) + } + l = len(m.ResourceId) + if l > 0 { + n += 1 + l + sovCommands(uint64(l)) + } + l = len(m.CorrelationId) + if l > 0 { + n += 1 + l + sovCommands(uint64(l)) + } + l = len(m.ResourceInterface) + if l > 0 { + n += 1 + l + sovCommands(uint64(l)) + } + if m.CommandMetadata != nil { + l = m.CommandMetadata.Size() + n += 1 + l + sovCommands(uint64(l)) + } + return n +} + +func (m *RetrieveResourceResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AuditContext != nil { + l = m.AuditContext.Size() + n += 1 + l + sovCommands(uint64(l)) + } + return n +} + +func (m *ConfirmResourceRetrieveRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AuthorizationContext != nil { + l = m.AuthorizationContext.Size() + n += 1 + l + sovCommands(uint64(l)) + } + l = len(m.ResourceId) + if l > 0 { + n += 1 + l + sovCommands(uint64(l)) + } + l = len(m.CorrelationId) + if l > 0 { + n += 1 + l + sovCommands(uint64(l)) + } + if m.Status != 0 { + n += 1 + sovCommands(uint64(m.Status)) + } + if m.Content != nil { + l = m.Content.Size() + n += 1 + l + sovCommands(uint64(l)) + } + if m.CommandMetadata != nil { + l = m.CommandMetadata.Size() + n += 1 + l + sovCommands(uint64(l)) + } + return n +} + +func (m *ConfirmResourceRetrieveResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AuditContext != nil { + l = m.AuditContext.Size() + n += 1 + l + sovCommands(uint64(l)) + } + return n +} + +func sovCommands(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozCommands(x uint64) (n int) { + return sovCommands(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *AuthorizationContext) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AuthorizationContext: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AuthorizationContext: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UserId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommands(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *CommandMetadata) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: CommandMetadata: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: CommandMetadata: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConnectionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipCommands(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PublishResourceRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PublishResourceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PublishResourceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthorizationContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuthorizationContext == nil { + m.AuthorizationContext = &AuthorizationContext{} + } + if err := m.AuthorizationContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Resource", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Resource == nil { + m.Resource = &Resource{} + } + if err := m.Resource.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TimeToLive", wireType) + } + m.TimeToLive = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TimeToLive |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CommandMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CommandMetadata == nil { + m.CommandMetadata = &CommandMetadata{} + } + if err := m.CommandMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommands(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PublishResourceResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PublishResourceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PublishResourceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuditContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuditContext == nil { + m.AuditContext = &AuditContext{} + } + if err := m.AuditContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field InstanceId", wireType) + } + m.InstanceId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.InstanceId |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipCommands(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UnpublishResourceRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UnpublishResourceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UnpublishResourceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthorizationContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuthorizationContext == nil { + m.AuthorizationContext = &AuthorizationContext{} + } + if err := m.AuthorizationContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CommandMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CommandMetadata == nil { + m.CommandMetadata = &CommandMetadata{} + } + if err := m.CommandMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommands(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UnpublishResourceResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UnpublishResourceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UnpublishResourceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuditContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuditContext == nil { + m.AuditContext = &AuditContext{} + } + if err := m.AuditContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommands(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NotifyResourceChangedRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NotifyResourceChangedRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NotifyResourceChangedRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthorizationContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuthorizationContext == nil { + m.AuthorizationContext = &AuthorizationContext{} + } + if err := m.AuthorizationContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Content == nil { + m.Content = &Content{} + } + if err := m.Content.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CommandMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CommandMetadata == nil { + m.CommandMetadata = &CommandMetadata{} + } + if err := m.CommandMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= Status(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipCommands(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NotifyResourceChangedResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NotifyResourceChangedResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NotifyResourceChangedResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuditContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuditContext == nil { + m.AuditContext = &AuditContext{} + } + if err := m.AuditContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommands(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UpdateResourceRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UpdateResourceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UpdateResourceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthorizationContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuthorizationContext == nil { + m.AuthorizationContext = &AuthorizationContext{} + } + if err := m.AuthorizationContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CorrelationId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CorrelationId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Content == nil { + m.Content = &Content{} + } + if err := m.Content.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceInterface", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceInterface = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 100: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CommandMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CommandMetadata == nil { + m.CommandMetadata = &CommandMetadata{} + } + if err := m.CommandMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommands(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *UpdateResourceResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: UpdateResourceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: UpdateResourceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuditContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuditContext == nil { + m.AuditContext = &AuditContext{} + } + if err := m.AuditContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommands(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConfirmResourceUpdateRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ConfirmResourceUpdateRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConfirmResourceUpdateRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthorizationContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuthorizationContext == nil { + m.AuthorizationContext = &AuthorizationContext{} + } + if err := m.AuthorizationContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CorrelationId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CorrelationId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= Status(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Content == nil { + m.Content = &Content{} + } + if err := m.Content.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CommandMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CommandMetadata == nil { + m.CommandMetadata = &CommandMetadata{} + } + if err := m.CommandMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommands(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConfirmResourceUpdateResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ConfirmResourceUpdateResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConfirmResourceUpdateResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuditContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuditContext == nil { + m.AuditContext = &AuditContext{} + } + if err := m.AuditContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommands(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RetrieveResourceRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RetrieveResourceRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RetrieveResourceRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthorizationContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuthorizationContext == nil { + m.AuthorizationContext = &AuthorizationContext{} + } + if err := m.AuthorizationContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CorrelationId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CorrelationId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceInterface", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceInterface = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CommandMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CommandMetadata == nil { + m.CommandMetadata = &CommandMetadata{} + } + if err := m.CommandMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommands(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *RetrieveResourceResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RetrieveResourceResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RetrieveResourceResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuditContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuditContext == nil { + m.AuditContext = &AuditContext{} + } + if err := m.AuditContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommands(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConfirmResourceRetrieveRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ConfirmResourceRetrieveRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConfirmResourceRetrieveRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthorizationContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuthorizationContext == nil { + m.AuthorizationContext = &AuthorizationContext{} + } + if err := m.AuthorizationContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CorrelationId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CorrelationId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= Status(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Content == nil { + m.Content = &Content{} + } + if err := m.Content.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CommandMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CommandMetadata == nil { + m.CommandMetadata = &CommandMetadata{} + } + if err := m.CommandMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommands(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ConfirmResourceRetrieveResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ConfirmResourceRetrieveResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ConfirmResourceRetrieveResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuditContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowCommands + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthCommands + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthCommands + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuditContext == nil { + m.AuditContext = &AuditContext{} + } + if err := m.AuditContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipCommands(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthCommands + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipCommands(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCommands + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCommands + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowCommands + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthCommands + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupCommands + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthCommands + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthCommands = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowCommands = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupCommands = fmt.Errorf("proto: unexpected end of group") +) diff --git a/resource-aggregate/pb/commands.proto b/resource-aggregate/pb/commands.proto new file mode 100644 index 000000000..ab40af32c --- /dev/null +++ b/resource-aggregate/pb/commands.proto @@ -0,0 +1,245 @@ +syntax = "proto3"; + +package ocf.cloud.resourceaggregate.pb; + +import "github.com/go-ocf/cloud/resource-aggregate/pb/events.proto"; +import "github.com/go-ocf/cloud/resource-aggregate/pb/resources.proto"; + +option go_package = "github.com/go-ocf/cloud/resource-aggregate/pb;pb"; + +message AuthorizationContext { + string user_id = 1; + string device_id = 2; +} + +message CommandMetadata { + string connection_id = 1; + uint64 sequence = 2; +} + +//****************************************************************************************************************************************************** +// Publish / Unpublish Resources +// +// ┌──────────┐ ┌───────┐ ┌──────────────────┐ ┌─────────┐ +// │OCF Server│ │Gateway│ │Resource Aggregate│ │Event Bus│ +// └──────────┘ └───┬───┘ └────────┬─────────┘ └─────────┘ +// ┌┴┐[UPDATE] '/oic/rd'┌┴┐ │ │ +// │ │ ───────────────> │ │ │ │ +// │ │ │ │ │ │ +// │ │ │ │ PublishResourceRequest ┌┴┐ │ +// │ │ │ │ ───────────────────────>│ │ │ +// │ │ │ │ │ │ │ +// │ │ │ │ PublishResourceResponse │ │ │ +// │ │ │ │ <───────────────────────│ │ │ +// │ │ └┬┘ │ │ │ +// │ │ │ │ │ ResourcePublished │ +// │ │ │ │ │ ──────────────────────> +// │ │ │ └┬┘ │ +// │ │ ┌┴┐ ResourcePublished │ +// │ │ │ │ <────────────────────────────────────────────────── +// │ │ │ │ │ │ +// │ │ OK │ │ │ │ +// │ │ <─────────────── │ │ │ │ +// ┌───└┬┘────┐ ┌──└┬┘──┐ ┌────────┴─────────┐ ┌─────────┐ +// │OCF Server│ │Gateway│ │Resource Aggregate│ │Event Bus│ +// └──────────┘ └───────┘ └──────────────────┘ └─────────┘ +//****************************************************************************************************************************************************** + +// https://github.com/openconnectivityfoundation/core/blob/master/oic.wk.rd.raml#L65 +message PublishResourceRequest { + AuthorizationContext authorization_context = 1; + string resource_id = 2; // spec 1.3 - uuidV5(device_id+href), otherwise uuidV5(href) + Resource resource = 3; // Resource publish is atomic - PublishResource command per link from list. The CoAP-Gateway is responsible for the transaction. + int32 time_to_live = 4; + CommandMetadata command_metadata = 5; +} + +message PublishResourceResponse { + AuditContext audit_context = 1; + int64 instance_id = 2; +} + +// https://github.com/openconnectivityfoundation/core/blob/master/oic.wk.rd.raml #Specification CR needed +message UnpublishResourceRequest { + AuthorizationContext authorization_context = 1; + string resource_id = 2; // spec 1.3 - uuidV5(device_id+href), otherwise uuidV5(href) + CommandMetadata command_metadata = 3; +} + +message UnpublishResourceResponse { + AuditContext audit_context = 1; +} + +//****************************************************************************************************************************************************** +// Resource content changed +// (changed from the cloud or locally) +// +// ┌──────────┐ ┌───────┐ ┌──────────────────┐ ┌─────────┐ +// │OCF Server│ │Gateway│ │Resource Aggregate│ │Event Bus│ +// └──────────┘ └───┬───┘ └────────┬─────────┘ └─────────┘ +// │[NOTIFY] 'oic.r.temperature' changed┌┴┐ │ │ +// │ ──────────────────────────────────>│ │ │ │ +// │ │ │ │ │ +// │ │ │ NotifyResourceChangedRequest ┌┴┐ │ +// │ │ │ ─────────────────────────────>│ │ │ +// │ └┬┘ │ │ │ +// │ │ NotifyResourceChangedResponse │ │ │ +// │ │ <──────────────────────────────│ │ │ +// │ │ └┬┘ │ +// │ │ │ ResourceChanged │ +// │ │ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ >│ +// ┌──────────┐ ┌───┴───┐ ┌────────┴─────────┐ ┌─────────┐ +// │OCF Server│ │Gateway│ │Resource Aggregate│ │Event Bus│ +// └──────────┘ └───────┘ └──────────────────┘ └─────────┘ +//****************************************************************************************************************************************************** + +message NotifyResourceChangedRequest { + AuthorizationContext authorization_context = 1; + string resource_id = 2; // spec 1.3 - uuidV5(device_id+href), otherwise uuidV5(href) + Content content = 3; + CommandMetadata command_metadata = 4; + Status status = 5; +} + +message NotifyResourceChangedResponse { + AuditContext audit_context = 1; +} + +//******************************************************************************************************************************************************* +// Update Resource +// +// ┌──────────┐ ┌───────┐ ┌──────────────────┐ ┌─────────┐ ┌──────┐ +// │OCF Server│ │Gateway│ │Resource Aggregate│ │Event Bus│ │Client│ +// └──────────┘ └───┬───┘ └────────┬─────────┘ └─────────┘ └──────┘ +// │ │ ┌┴┐ UpdateResourceRequest ┌┴┐ +// │ │ │ │ <────────────────────────────────────────│ │ +// │ │ │ │ │ │ │ +// │ │ │ │ UpdateResourceResponse │ │ +// │ │ │ │ ────────────────────────────────────────>│ │ +// │ │ └┬┘ │ │ │ +// │ │ │ ResourceUpdatePending │ │ │ +// │ │ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ >│ │ │ +// │ │ │ │ │ │ +// │ ┌┴┐ ResourceUpdatePending │ │ │ +// │ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ │ │ +// │ │ │ │ │ │ │ +// ┌┴┐ [UPDATE] 'oic.r.temperature' │ │ │ │ │ │ +// │ │ <─────────────────────────────────│ │ │ │ │ │ +// └┬┘ │ │ │ │ │ │ +// │ OK │ │ │ │ │ │ +// │ ──────────────────────────────────>│ │ │ │ │ │ +// │ │ │ │ │ │ │ +// │ │ │ ConfirmResourceUpdateRequest ┌┴┐ │ │ │ +// │ │ │ ─────────────────────────────>│ │ │ │ │ +// │ └┬┘ │ │ │ │ │ +// │ │ ConfirmResourceUpdateResponse │ │ │ │ │ +// │ │ <──────────────────────────────│ │ │ │ │ +// │ │ └┬┘ │ │ │ +// │ │ │ ResourceUpdated │ │ │ +// │ │ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ >│ │ │ +// │ │ │ │ └┬┘ +// │ │ │ │ ResourceUpdated │ +// │ │ │ │ ─ ─ ─ ─ ─ ─ ─ ─ >│ +// │ │ │ │ │ +// │ │ │ │ │ +// │ │ ╔══════════════════════════╗│ │ │ +// ═════╪═════════════════════════════════════╪═════╣ Resource content changed ╠╪═════════════════════════╪══════════════════╪═════════ +// │ │ ╚══════════════════════════╝│ │ │ +// │ │ │ │ │ +// │ [NOTIFY] 'oic.r.temperature' changed│ │ │ │ +// │ ────────────────────────────────────> │ │ │ +// ┌──────────┐ ┌───┴───┐ ┌────────┴─────────┐ ┌─────────┐ ┌──────┐ +// │OCF Server│ │Gateway│ │Resource Aggregate│ │Event Bus│ │Client│ +// └──────────┘ └───────┘ └──────────────────┘ └─────────┘ └──────┘ +//******************************************************************************************************************************************************* + +message UpdateResourceRequest { + AuthorizationContext authorization_context = 1; + string resource_id = 2; // spec 1.3 - uuidV5(device_id+href), otherwise uuidV5(href) + string correlation_id = 3; + Content content = 4; + string resource_interface = 5; + CommandMetadata command_metadata = 100; +} + +message UpdateResourceResponse { + AuditContext audit_context = 1; +} + +message ConfirmResourceUpdateRequest { + AuthorizationContext authorization_context = 1; + string resource_id = 2; // spec 1.3 - uuidV5(device_id+href), otherwise uuidV5(href) + string correlation_id = 3; + Status status = 4; + Content content = 5; + CommandMetadata command_metadata = 6; +} + +message ConfirmResourceUpdateResponse { + AuditContext audit_context = 1; +} + +//******************************************************************************************************************************************************* +// Retrieve Resource + +// ┌──────────┐ ┌───────┐ ┌──────────────────┐ ┌─────────┐ ┌──────┐ +// │OCF Server│ │Gateway│ │Resource Aggregate│ │Event Bus│ │Client│ +// └──────────┘ └───┬───┘ └────────┬─────────┘ └─────────┘ └──────┘ +// │ │ ┌┴┐ RetrieveResourceRequest ┌┴┐ +// │ │ │ │ <──────────────────────────────────────────│ │ +// │ │ │ │ │ │ │ +// │ │ │ │ RetrieveResourceResponse │ │ +// │ │ │ │ ──────────────────────────────────────────>│ │ +// │ │ └┬┘ │ │ │ +// │ │ │ ResourceRetrievePending │ │ │ +// │ │ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>│ │ │ +// │ │ │ │ │ │ +// │ ┌┴┐ ResourceRetrievePending │ │ │ +// │ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ │ +// │ │ │ │ │ │ │ +// ┌┴┐[RETRIEVE] '/oic/d' │ │ │ │ │ │ +// │ │ <───────────────── │ │ │ │ │ │ +// └┬┘ │ │ │ │ │ │ +// │ OK │ │ │ │ │ │ +// │ ───────────────────>│ │ │ │ │ │ +// │ │ │ │ │ │ │ +// │ │ │ ConfirmResourceRetrieveRequest ┌┴┐ │ │ │ +// │ │ │ ───────────────────────────────>│ │ │ │ │ +// │ └┬┘ │ │ │ │ │ +// │ │ConfirmResourceRetrieveResponse │ │ │ │ │ +// │ │<──────────────────────────────── │ │ │ │ │ +// │ │ └┬┘ │ │ │ +// │ │ │ ResourceRetrieved │ │ │ +// │ │ │ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>│ │ │ +// │ │ │ │ └┬┘ +// │ │ │ │ ResourceRetrieved │ +// │ │ │ │ ─ ─ ─ ─ ─ ─ ─ ─ ─> +// ┌──────────┐ ┌───┴───┐ ┌────────┴─────────┐ ┌─────────┐ ┌──────┐ +// │OCF Server│ │Gateway│ │Resource Aggregate│ │Event Bus│ │Client│ +// └──────────┘ └───────┘ └──────────────────┘ └─────────┘ └──────┘ +//******************************************************************************************************************************************************* + +message RetrieveResourceRequest { + AuthorizationContext authorization_context = 1; + string resource_id = 2; // spec 1.3 - uuidV5(device_id+href), otherwise uuidV5(href) + string correlation_id = 3; + string resource_interface = 4; + CommandMetadata command_metadata = 5; +} + +message RetrieveResourceResponse { + AuditContext audit_context = 1; +} + +message ConfirmResourceRetrieveRequest { + AuthorizationContext authorization_context = 1; + string resource_id = 2; // spec 1.3 - uuidV5(device_id+href), otherwise uuidV5(href) + string correlation_id = 3; + Status status = 4; + Content content = 5; + CommandMetadata command_metadata = 6; +} + +message ConfirmResourceRetrieveResponse { + AuditContext audit_context = 1; +} diff --git a/resource-aggregate/pb/derived.gen.go b/resource-aggregate/pb/derived.gen.go new file mode 100644 index 000000000..c7f2fd6fd --- /dev/null +++ b/resource-aggregate/pb/derived.gen.go @@ -0,0 +1,103 @@ +// Code generated by goderive DO NOT EDIT. + +package pb + +// deriveDeepCopy recursively copies the contents of src into dst. +func deriveDeepCopy(dst, src *Resource) { + dst.Id = src.Id + dst.Href = src.Href + if src.ResourceTypes == nil { + dst.ResourceTypes = nil + } else { + if dst.ResourceTypes != nil { + if len(src.ResourceTypes) > len(dst.ResourceTypes) { + if cap(dst.ResourceTypes) >= len(src.ResourceTypes) { + dst.ResourceTypes = (dst.ResourceTypes)[:len(src.ResourceTypes)] + } else { + dst.ResourceTypes = make([]string, len(src.ResourceTypes)) + } + } else if len(src.ResourceTypes) < len(dst.ResourceTypes) { + dst.ResourceTypes = (dst.ResourceTypes)[:len(src.ResourceTypes)] + } + } else { + dst.ResourceTypes = make([]string, len(src.ResourceTypes)) + } + copy(dst.ResourceTypes, src.ResourceTypes) + } + if src.Interfaces == nil { + dst.Interfaces = nil + } else { + if dst.Interfaces != nil { + if len(src.Interfaces) > len(dst.Interfaces) { + if cap(dst.Interfaces) >= len(src.Interfaces) { + dst.Interfaces = (dst.Interfaces)[:len(src.Interfaces)] + } else { + dst.Interfaces = make([]string, len(src.Interfaces)) + } + } else if len(src.Interfaces) < len(dst.Interfaces) { + dst.Interfaces = (dst.Interfaces)[:len(src.Interfaces)] + } + } else { + dst.Interfaces = make([]string, len(src.Interfaces)) + } + copy(dst.Interfaces, src.Interfaces) + } + dst.DeviceId = src.DeviceId + dst.InstanceId = src.InstanceId + dst.Anchor = src.Anchor + if src.Policies == nil { + dst.Policies = nil + } else { + dst.Policies = new(Policies) + *dst.Policies = *src.Policies + } + dst.Title = src.Title + if src.SupportedContentTypes == nil { + dst.SupportedContentTypes = nil + } else { + if dst.SupportedContentTypes != nil { + if len(src.SupportedContentTypes) > len(dst.SupportedContentTypes) { + if cap(dst.SupportedContentTypes) >= len(src.SupportedContentTypes) { + dst.SupportedContentTypes = (dst.SupportedContentTypes)[:len(src.SupportedContentTypes)] + } else { + dst.SupportedContentTypes = make([]string, len(src.SupportedContentTypes)) + } + } else if len(src.SupportedContentTypes) < len(dst.SupportedContentTypes) { + dst.SupportedContentTypes = (dst.SupportedContentTypes)[:len(src.SupportedContentTypes)] + } + } else { + dst.SupportedContentTypes = make([]string, len(src.SupportedContentTypes)) + } + copy(dst.SupportedContentTypes, src.SupportedContentTypes) + } + if src.EndpointInformations == nil { + dst.EndpointInformations = nil + } else { + if dst.EndpointInformations != nil { + if len(src.EndpointInformations) > len(dst.EndpointInformations) { + if cap(dst.EndpointInformations) >= len(src.EndpointInformations) { + dst.EndpointInformations = (dst.EndpointInformations)[:len(src.EndpointInformations)] + } else { + dst.EndpointInformations = make([]*EndpointInformation, len(src.EndpointInformations)) + } + } else if len(src.EndpointInformations) < len(dst.EndpointInformations) { + dst.EndpointInformations = (dst.EndpointInformations)[:len(src.EndpointInformations)] + } + } else { + dst.EndpointInformations = make([]*EndpointInformation, len(src.EndpointInformations)) + } + deriveDeepCopy_(dst.EndpointInformations, src.EndpointInformations) + } +} + +// deriveDeepCopy_ recursively copies the contents of src into dst. +func deriveDeepCopy_(dst, src []*EndpointInformation) { + for src_i, src_value := range src { + if src_value == nil { + dst[src_i] = nil + } else { + dst[src_i] = new(EndpointInformation) + *dst[src_i] = *src_value + } + } +} diff --git a/resource-aggregate/pb/events.pb.go b/resource-aggregate/pb/events.pb.go new file mode 100644 index 000000000..27f27f276 --- /dev/null +++ b/resource-aggregate/pb/events.pb.go @@ -0,0 +1,3792 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: pb/events.proto + +package pb + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type AuditContext struct { + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + DeviceId string `protobuf:"bytes,2,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` + CorrelationId string `protobuf:"bytes,3,opt,name=correlation_id,json=correlationId,proto3" json:"correlation_id,omitempty"` +} + +func (m *AuditContext) Reset() { *m = AuditContext{} } +func (m *AuditContext) String() string { return proto.CompactTextString(m) } +func (*AuditContext) ProtoMessage() {} +func (*AuditContext) Descriptor() ([]byte, []int) { + return fileDescriptor_6e7306e6c7a41ee5, []int{0} +} +func (m *AuditContext) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AuditContext) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AuditContext.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AuditContext) XXX_Merge(src proto.Message) { + xxx_messageInfo_AuditContext.Merge(m, src) +} +func (m *AuditContext) XXX_Size() int { + return m.Size() +} +func (m *AuditContext) XXX_DiscardUnknown() { + xxx_messageInfo_AuditContext.DiscardUnknown(m) +} + +var xxx_messageInfo_AuditContext proto.InternalMessageInfo + +func (m *AuditContext) GetUserId() string { + if m != nil { + return m.UserId + } + return "" +} + +func (m *AuditContext) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +func (m *AuditContext) GetCorrelationId() string { + if m != nil { + return m.CorrelationId + } + return "" +} + +type EventMetadata struct { + Version uint64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + TimestampMs uint64 `protobuf:"varint,2,opt,name=timestamp_ms,json=timestampMs,proto3" json:"timestamp_ms,omitempty"` + ConnectionId string `protobuf:"bytes,3,opt,name=connection_id,json=connectionId,proto3" json:"connection_id,omitempty"` + Sequence uint64 `protobuf:"varint,4,opt,name=sequence,proto3" json:"sequence,omitempty"` +} + +func (m *EventMetadata) Reset() { *m = EventMetadata{} } +func (m *EventMetadata) String() string { return proto.CompactTextString(m) } +func (*EventMetadata) ProtoMessage() {} +func (*EventMetadata) Descriptor() ([]byte, []int) { + return fileDescriptor_6e7306e6c7a41ee5, []int{1} +} +func (m *EventMetadata) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventMetadata.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EventMetadata) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventMetadata.Merge(m, src) +} +func (m *EventMetadata) XXX_Size() int { + return m.Size() +} +func (m *EventMetadata) XXX_DiscardUnknown() { + xxx_messageInfo_EventMetadata.DiscardUnknown(m) +} + +var xxx_messageInfo_EventMetadata proto.InternalMessageInfo + +func (m *EventMetadata) GetVersion() uint64 { + if m != nil { + return m.Version + } + return 0 +} + +func (m *EventMetadata) GetTimestampMs() uint64 { + if m != nil { + return m.TimestampMs + } + return 0 +} + +func (m *EventMetadata) GetConnectionId() string { + if m != nil { + return m.ConnectionId + } + return "" +} + +func (m *EventMetadata) GetSequence() uint64 { + if m != nil { + return m.Sequence + } + return 0 +} + +// https://github.com/openconnectivityfoundation/core/blob/master/oic.wk.rd.raml#L106 +type ResourcePublished struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Resource *Resource `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"` + TimeToLive int32 `protobuf:"varint,3,opt,name=time_to_live,json=timeToLive,proto3" json:"time_to_live,omitempty"` + AuditContext *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"` +} + +func (m *ResourcePublished) Reset() { *m = ResourcePublished{} } +func (m *ResourcePublished) String() string { return proto.CompactTextString(m) } +func (*ResourcePublished) ProtoMessage() {} +func (*ResourcePublished) Descriptor() ([]byte, []int) { + return fileDescriptor_6e7306e6c7a41ee5, []int{2} +} +func (m *ResourcePublished) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResourcePublished) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResourcePublished.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResourcePublished) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResourcePublished.Merge(m, src) +} +func (m *ResourcePublished) XXX_Size() int { + return m.Size() +} +func (m *ResourcePublished) XXX_DiscardUnknown() { + xxx_messageInfo_ResourcePublished.DiscardUnknown(m) +} + +var xxx_messageInfo_ResourcePublished proto.InternalMessageInfo + +func (m *ResourcePublished) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *ResourcePublished) GetResource() *Resource { + if m != nil { + return m.Resource + } + return nil +} + +func (m *ResourcePublished) GetTimeToLive() int32 { + if m != nil { + return m.TimeToLive + } + return 0 +} + +func (m *ResourcePublished) GetAuditContext() *AuditContext { + if m != nil { + return m.AuditContext + } + return nil +} + +func (m *ResourcePublished) GetEventMetadata() *EventMetadata { + if m != nil { + return m.EventMetadata + } + return nil +} + +// https://github.com/openconnectivityfoundation/core/blob/master/oic.wk.rd.raml #Specification CR needed +type ResourceUnpublished struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + AuditContext *AuditContext `protobuf:"bytes,2,opt,name=audit_context,json=auditContext,proto3" json:"audit_context,omitempty"` + EventMetadata *EventMetadata `protobuf:"bytes,3,opt,name=event_metadata,json=eventMetadata,proto3" json:"event_metadata,omitempty"` +} + +func (m *ResourceUnpublished) Reset() { *m = ResourceUnpublished{} } +func (m *ResourceUnpublished) String() string { return proto.CompactTextString(m) } +func (*ResourceUnpublished) ProtoMessage() {} +func (*ResourceUnpublished) Descriptor() ([]byte, []int) { + return fileDescriptor_6e7306e6c7a41ee5, []int{3} +} +func (m *ResourceUnpublished) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResourceUnpublished) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResourceUnpublished.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResourceUnpublished) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResourceUnpublished.Merge(m, src) +} +func (m *ResourceUnpublished) XXX_Size() int { + return m.Size() +} +func (m *ResourceUnpublished) XXX_DiscardUnknown() { + xxx_messageInfo_ResourceUnpublished.DiscardUnknown(m) +} + +var xxx_messageInfo_ResourceUnpublished proto.InternalMessageInfo + +func (m *ResourceUnpublished) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *ResourceUnpublished) GetAuditContext() *AuditContext { + if m != nil { + return m.AuditContext + } + return nil +} + +func (m *ResourceUnpublished) GetEventMetadata() *EventMetadata { + if m != nil { + return m.EventMetadata + } + return nil +} + +type ResourceChanged struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Content *Content `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"` + AuditContext *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"` + Status Status `protobuf:"varint,5,opt,name=status,proto3,enum=ocf.cloud.resourceaggregate.pb.Status" json:"status,omitempty"` +} + +func (m *ResourceChanged) Reset() { *m = ResourceChanged{} } +func (m *ResourceChanged) String() string { return proto.CompactTextString(m) } +func (*ResourceChanged) ProtoMessage() {} +func (*ResourceChanged) Descriptor() ([]byte, []int) { + return fileDescriptor_6e7306e6c7a41ee5, []int{4} +} +func (m *ResourceChanged) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResourceChanged) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResourceChanged.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResourceChanged) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResourceChanged.Merge(m, src) +} +func (m *ResourceChanged) XXX_Size() int { + return m.Size() +} +func (m *ResourceChanged) XXX_DiscardUnknown() { + xxx_messageInfo_ResourceChanged.DiscardUnknown(m) +} + +var xxx_messageInfo_ResourceChanged proto.InternalMessageInfo + +func (m *ResourceChanged) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *ResourceChanged) GetContent() *Content { + if m != nil { + return m.Content + } + return nil +} + +func (m *ResourceChanged) GetAuditContext() *AuditContext { + if m != nil { + return m.AuditContext + } + return nil +} + +func (m *ResourceChanged) GetEventMetadata() *EventMetadata { + if m != nil { + return m.EventMetadata + } + return nil +} + +func (m *ResourceChanged) GetStatus() Status { + if m != nil { + return m.Status + } + return Status_UNKNOWN +} + +type ResourceUpdatePending struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ResourceInterface string `protobuf:"bytes,2,opt,name=resource_interface,json=resourceInterface,proto3" json:"resource_interface,omitempty"` + Content *Content `protobuf:"bytes,4,opt,name=content,proto3" json:"content,omitempty"` + AuditContext *AuditContext `protobuf:"bytes,5,opt,name=audit_context,json=auditContext,proto3" json:"audit_context,omitempty"` + EventMetadata *EventMetadata `protobuf:"bytes,6,opt,name=event_metadata,json=eventMetadata,proto3" json:"event_metadata,omitempty"` +} + +func (m *ResourceUpdatePending) Reset() { *m = ResourceUpdatePending{} } +func (m *ResourceUpdatePending) String() string { return proto.CompactTextString(m) } +func (*ResourceUpdatePending) ProtoMessage() {} +func (*ResourceUpdatePending) Descriptor() ([]byte, []int) { + return fileDescriptor_6e7306e6c7a41ee5, []int{5} +} +func (m *ResourceUpdatePending) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResourceUpdatePending) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResourceUpdatePending.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResourceUpdatePending) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResourceUpdatePending.Merge(m, src) +} +func (m *ResourceUpdatePending) XXX_Size() int { + return m.Size() +} +func (m *ResourceUpdatePending) XXX_DiscardUnknown() { + xxx_messageInfo_ResourceUpdatePending.DiscardUnknown(m) +} + +var xxx_messageInfo_ResourceUpdatePending proto.InternalMessageInfo + +func (m *ResourceUpdatePending) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *ResourceUpdatePending) GetResourceInterface() string { + if m != nil { + return m.ResourceInterface + } + return "" +} + +func (m *ResourceUpdatePending) GetContent() *Content { + if m != nil { + return m.Content + } + return nil +} + +func (m *ResourceUpdatePending) GetAuditContext() *AuditContext { + if m != nil { + return m.AuditContext + } + return nil +} + +func (m *ResourceUpdatePending) GetEventMetadata() *EventMetadata { + if m != nil { + return m.EventMetadata + } + return nil +} + +type ResourceUpdated struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Status Status `protobuf:"varint,2,opt,name=status,proto3,enum=ocf.cloud.resourceaggregate.pb.Status" json:"status,omitempty"` + Content *Content `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` + AuditContext *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"` +} + +func (m *ResourceUpdated) Reset() { *m = ResourceUpdated{} } +func (m *ResourceUpdated) String() string { return proto.CompactTextString(m) } +func (*ResourceUpdated) ProtoMessage() {} +func (*ResourceUpdated) Descriptor() ([]byte, []int) { + return fileDescriptor_6e7306e6c7a41ee5, []int{6} +} +func (m *ResourceUpdated) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResourceUpdated) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResourceUpdated.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResourceUpdated) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResourceUpdated.Merge(m, src) +} +func (m *ResourceUpdated) XXX_Size() int { + return m.Size() +} +func (m *ResourceUpdated) XXX_DiscardUnknown() { + xxx_messageInfo_ResourceUpdated.DiscardUnknown(m) +} + +var xxx_messageInfo_ResourceUpdated proto.InternalMessageInfo + +func (m *ResourceUpdated) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *ResourceUpdated) GetStatus() Status { + if m != nil { + return m.Status + } + return Status_UNKNOWN +} + +func (m *ResourceUpdated) GetContent() *Content { + if m != nil { + return m.Content + } + return nil +} + +func (m *ResourceUpdated) GetAuditContext() *AuditContext { + if m != nil { + return m.AuditContext + } + return nil +} + +func (m *ResourceUpdated) GetEventMetadata() *EventMetadata { + if m != nil { + return m.EventMetadata + } + return nil +} + +type ResourceRetrievePending struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + ResourceInterface string `protobuf:"bytes,2,opt,name=resource_interface,json=resourceInterface,proto3" json:"resource_interface,omitempty"` + AuditContext *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"` +} + +func (m *ResourceRetrievePending) Reset() { *m = ResourceRetrievePending{} } +func (m *ResourceRetrievePending) String() string { return proto.CompactTextString(m) } +func (*ResourceRetrievePending) ProtoMessage() {} +func (*ResourceRetrievePending) Descriptor() ([]byte, []int) { + return fileDescriptor_6e7306e6c7a41ee5, []int{7} +} +func (m *ResourceRetrievePending) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResourceRetrievePending) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResourceRetrievePending.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResourceRetrievePending) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResourceRetrievePending.Merge(m, src) +} +func (m *ResourceRetrievePending) XXX_Size() int { + return m.Size() +} +func (m *ResourceRetrievePending) XXX_DiscardUnknown() { + xxx_messageInfo_ResourceRetrievePending.DiscardUnknown(m) +} + +var xxx_messageInfo_ResourceRetrievePending proto.InternalMessageInfo + +func (m *ResourceRetrievePending) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *ResourceRetrievePending) GetResourceInterface() string { + if m != nil { + return m.ResourceInterface + } + return "" +} + +func (m *ResourceRetrievePending) GetAuditContext() *AuditContext { + if m != nil { + return m.AuditContext + } + return nil +} + +func (m *ResourceRetrievePending) GetEventMetadata() *EventMetadata { + if m != nil { + return m.EventMetadata + } + return nil +} + +type ResourceRetrieved struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Status Status `protobuf:"varint,2,opt,name=status,proto3,enum=ocf.cloud.resourceaggregate.pb.Status" json:"status,omitempty"` + Content *Content `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` + AuditContext *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"` +} + +func (m *ResourceRetrieved) Reset() { *m = ResourceRetrieved{} } +func (m *ResourceRetrieved) String() string { return proto.CompactTextString(m) } +func (*ResourceRetrieved) ProtoMessage() {} +func (*ResourceRetrieved) Descriptor() ([]byte, []int) { + return fileDescriptor_6e7306e6c7a41ee5, []int{8} +} +func (m *ResourceRetrieved) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResourceRetrieved) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResourceRetrieved.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResourceRetrieved) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResourceRetrieved.Merge(m, src) +} +func (m *ResourceRetrieved) XXX_Size() int { + return m.Size() +} +func (m *ResourceRetrieved) XXX_DiscardUnknown() { + xxx_messageInfo_ResourceRetrieved.DiscardUnknown(m) +} + +var xxx_messageInfo_ResourceRetrieved proto.InternalMessageInfo + +func (m *ResourceRetrieved) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *ResourceRetrieved) GetStatus() Status { + if m != nil { + return m.Status + } + return Status_UNKNOWN +} + +func (m *ResourceRetrieved) GetContent() *Content { + if m != nil { + return m.Content + } + return nil +} + +func (m *ResourceRetrieved) GetAuditContext() *AuditContext { + if m != nil { + return m.AuditContext + } + return nil +} + +func (m *ResourceRetrieved) GetEventMetadata() *EventMetadata { + if m != nil { + return m.EventMetadata + } + return nil +} + +type ResourceStateSnapshotTaken struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Resource *Resource `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"` + LatestResourceChange *ResourceChanged `protobuf:"bytes,3,opt,name=latest_resource_change,json=latestResourceChange,proto3" json:"latest_resource_change,omitempty"` + PendingRequestsCount uint32 `protobuf:"varint,4,opt,name=pending_requests_count,json=pendingRequestsCount,proto3" json:"pending_requests_count,omitempty"` + TimeToLive int32 `protobuf:"varint,5,opt,name=time_to_live,json=timeToLive,proto3" json:"time_to_live,omitempty"` + IsPublished bool `protobuf:"varint,6,opt,name=is_published,json=isPublished,proto3" json:"is_published,omitempty"` + EventMetadata *EventMetadata `protobuf:"bytes,7,opt,name=event_metadata,json=eventMetadata,proto3" json:"event_metadata,omitempty"` +} + +func (m *ResourceStateSnapshotTaken) Reset() { *m = ResourceStateSnapshotTaken{} } +func (m *ResourceStateSnapshotTaken) String() string { return proto.CompactTextString(m) } +func (*ResourceStateSnapshotTaken) ProtoMessage() {} +func (*ResourceStateSnapshotTaken) Descriptor() ([]byte, []int) { + return fileDescriptor_6e7306e6c7a41ee5, []int{9} +} +func (m *ResourceStateSnapshotTaken) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResourceStateSnapshotTaken) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResourceStateSnapshotTaken.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResourceStateSnapshotTaken) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResourceStateSnapshotTaken.Merge(m, src) +} +func (m *ResourceStateSnapshotTaken) XXX_Size() int { + return m.Size() +} +func (m *ResourceStateSnapshotTaken) XXX_DiscardUnknown() { + xxx_messageInfo_ResourceStateSnapshotTaken.DiscardUnknown(m) +} + +var xxx_messageInfo_ResourceStateSnapshotTaken proto.InternalMessageInfo + +func (m *ResourceStateSnapshotTaken) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *ResourceStateSnapshotTaken) GetResource() *Resource { + if m != nil { + return m.Resource + } + return nil +} + +func (m *ResourceStateSnapshotTaken) GetLatestResourceChange() *ResourceChanged { + if m != nil { + return m.LatestResourceChange + } + return nil +} + +func (m *ResourceStateSnapshotTaken) GetPendingRequestsCount() uint32 { + if m != nil { + return m.PendingRequestsCount + } + return 0 +} + +func (m *ResourceStateSnapshotTaken) GetTimeToLive() int32 { + if m != nil { + return m.TimeToLive + } + return 0 +} + +func (m *ResourceStateSnapshotTaken) GetIsPublished() bool { + if m != nil { + return m.IsPublished + } + return false +} + +func (m *ResourceStateSnapshotTaken) GetEventMetadata() *EventMetadata { + if m != nil { + return m.EventMetadata + } + return nil +} + +func init() { + proto.RegisterType((*AuditContext)(nil), "ocf.cloud.resourceaggregate.pb.AuditContext") + proto.RegisterType((*EventMetadata)(nil), "ocf.cloud.resourceaggregate.pb.EventMetadata") + proto.RegisterType((*ResourcePublished)(nil), "ocf.cloud.resourceaggregate.pb.ResourcePublished") + proto.RegisterType((*ResourceUnpublished)(nil), "ocf.cloud.resourceaggregate.pb.ResourceUnpublished") + proto.RegisterType((*ResourceChanged)(nil), "ocf.cloud.resourceaggregate.pb.ResourceChanged") + proto.RegisterType((*ResourceUpdatePending)(nil), "ocf.cloud.resourceaggregate.pb.ResourceUpdatePending") + proto.RegisterType((*ResourceUpdated)(nil), "ocf.cloud.resourceaggregate.pb.ResourceUpdated") + proto.RegisterType((*ResourceRetrievePending)(nil), "ocf.cloud.resourceaggregate.pb.ResourceRetrievePending") + proto.RegisterType((*ResourceRetrieved)(nil), "ocf.cloud.resourceaggregate.pb.ResourceRetrieved") + proto.RegisterType((*ResourceStateSnapshotTaken)(nil), "ocf.cloud.resourceaggregate.pb.ResourceStateSnapshotTaken") +} + +func init() { proto.RegisterFile("pb/events.proto", fileDescriptor_6e7306e6c7a41ee5) } + +var fileDescriptor_6e7306e6c7a41ee5 = []byte{ + // 727 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x56, 0xcf, 0x6e, 0x13, 0x3f, + 0x10, 0x6e, 0x36, 0x7f, 0xeb, 0xfc, 0xa9, 0xea, 0x5f, 0x7f, 0x6d, 0x54, 0xa4, 0xa8, 0x0d, 0x02, + 0x7a, 0x20, 0x89, 0x54, 0x7a, 0x43, 0x42, 0x2a, 0x81, 0x43, 0x24, 0x8a, 0x8a, 0x5b, 0x2e, 0x5c, + 0x56, 0x8e, 0x77, 0x92, 0x58, 0x4d, 0xec, 0x65, 0xed, 0x8d, 0xb8, 0xf2, 0x00, 0x48, 0x3c, 0x07, + 0x67, 0x9e, 0x80, 0x13, 0x17, 0xa0, 0x47, 0x8e, 0xa8, 0x7d, 0x11, 0xb4, 0xce, 0xee, 0x36, 0xd5, + 0x02, 0x01, 0x9a, 0x48, 0x20, 0x71, 0xf4, 0x8c, 0x67, 0xc6, 0xdf, 0xf7, 0x8d, 0xed, 0x41, 0x2b, + 0x6e, 0xb7, 0x05, 0x63, 0x10, 0x5a, 0x35, 0x5d, 0x4f, 0x6a, 0x89, 0x6b, 0x92, 0xf5, 0x9a, 0x6c, + 0x28, 0x7d, 0xa7, 0xe9, 0x81, 0x92, 0xbe, 0xc7, 0x80, 0xf6, 0xfb, 0x1e, 0xf4, 0xa9, 0x86, 0xa6, + 0xdb, 0xdd, 0xdc, 0xef, 0x73, 0x3d, 0xf0, 0xbb, 0x4d, 0x26, 0x47, 0xad, 0xbe, 0x6c, 0x48, 0xd6, + 0x6b, 0x49, 0xd6, 0x6b, 0x98, 0x88, 0x56, 0x14, 0xd1, 0x88, 0x43, 0x5a, 0x6e, 0x37, 0xb6, 0x86, + 0x25, 0xea, 0x27, 0xa8, 0xb4, 0xef, 0x3b, 0x5c, 0xb7, 0xa5, 0xd0, 0xf0, 0x42, 0xe3, 0x0d, 0x94, + 0xf7, 0x15, 0x78, 0x36, 0x77, 0xaa, 0xa9, 0xad, 0xd4, 0xce, 0x32, 0xc9, 0x05, 0xcb, 0x8e, 0x83, + 0xaf, 0xa1, 0x65, 0x07, 0xc6, 0x9c, 0x41, 0xe0, 0xb2, 0x8c, 0xab, 0x30, 0x31, 0x74, 0x1c, 0x7c, + 0x03, 0x55, 0x98, 0xf4, 0x3c, 0x18, 0x52, 0xcd, 0xa5, 0x08, 0x76, 0xa4, 0xcd, 0x8e, 0xf2, 0x94, + 0xb5, 0xe3, 0xd4, 0x5f, 0xa5, 0x50, 0xf9, 0x61, 0x00, 0xf0, 0x00, 0x34, 0x75, 0xa8, 0xa6, 0xb8, + 0x8a, 0xf2, 0x63, 0xf0, 0x14, 0x97, 0xc2, 0x94, 0xcb, 0x90, 0x68, 0x89, 0xb7, 0x51, 0x49, 0xf3, + 0x11, 0x28, 0x4d, 0x47, 0xae, 0x3d, 0x52, 0xa6, 0x64, 0x86, 0x14, 0x63, 0xdb, 0x81, 0xc2, 0xd7, + 0x51, 0x99, 0x49, 0x21, 0x80, 0x5d, 0x2e, 0x5a, 0xba, 0x30, 0x76, 0x1c, 0xbc, 0x89, 0x0a, 0x0a, + 0x9e, 0xfb, 0x20, 0x18, 0x54, 0x33, 0x26, 0x47, 0xbc, 0xae, 0xbf, 0xb5, 0xd0, 0x2a, 0x09, 0x09, + 0x39, 0xf4, 0xbb, 0x43, 0xae, 0x06, 0xe0, 0xe0, 0x0a, 0xb2, 0x62, 0xf4, 0x16, 0x77, 0xf0, 0x03, + 0x54, 0x88, 0x58, 0x33, 0xa7, 0x28, 0xee, 0xee, 0x34, 0x7f, 0x2c, 0x4c, 0x33, 0x4a, 0x4a, 0xe2, + 0x48, 0xbc, 0x35, 0xc1, 0x63, 0x6b, 0x69, 0x0f, 0xf9, 0x18, 0xcc, 0x59, 0xb3, 0x04, 0x05, 0xb6, + 0x63, 0xf9, 0x88, 0x8f, 0x01, 0x3f, 0x41, 0x65, 0x1a, 0x48, 0x61, 0xb3, 0x89, 0x16, 0xe6, 0xb8, + 0xc5, 0xdd, 0xdb, 0xb3, 0x8a, 0x4d, 0xeb, 0x47, 0x4a, 0x74, 0x5a, 0xcd, 0x63, 0x54, 0x31, 0x0d, + 0x65, 0x8f, 0x42, 0xc2, 0xab, 0x59, 0x93, 0xb3, 0x31, 0x2b, 0xe7, 0x25, 0x95, 0x48, 0x19, 0xa6, + 0x97, 0xf5, 0x8f, 0x29, 0xf4, 0x5f, 0x84, 0xf0, 0xa9, 0x70, 0xbf, 0x4b, 0x5c, 0x02, 0x90, 0xb5, + 0x00, 0x40, 0xe9, 0x39, 0x00, 0xfa, 0x60, 0xa1, 0x95, 0x08, 0x50, 0x7b, 0x40, 0x45, 0xff, 0x1b, + 0x60, 0xf6, 0x51, 0xde, 0xc0, 0x10, 0x11, 0x8c, 0x5b, 0xb3, 0x4a, 0xb6, 0x27, 0xdb, 0x49, 0x14, + 0x97, 0xe4, 0x23, 0xbd, 0x00, 0x3e, 0x32, 0x57, 0xe7, 0x03, 0xdf, 0x43, 0x39, 0xa5, 0xa9, 0xf6, + 0x95, 0x69, 0x97, 0xca, 0xee, 0xcd, 0x59, 0xd9, 0x8e, 0xcc, 0x6e, 0x12, 0x46, 0xd5, 0xdf, 0x59, + 0xe8, 0xff, 0xb8, 0x41, 0x5c, 0x87, 0x6a, 0x38, 0x04, 0xe1, 0x70, 0xd1, 0x4f, 0xb0, 0xda, 0x40, + 0x38, 0x4a, 0x68, 0x73, 0xa1, 0xc1, 0xeb, 0xd1, 0xf0, 0x96, 0x2d, 0x93, 0xd5, 0xc8, 0xd3, 0x89, + 0x1c, 0xd3, 0x22, 0x64, 0xe6, 0x25, 0x42, 0x76, 0x01, 0x22, 0xe4, 0xe6, 0xdc, 0x94, 0x13, 0x12, + 0x93, 0x4d, 0x79, 0x21, 0x94, 0xf5, 0x3b, 0x42, 0x4d, 0xf3, 0x99, 0x9e, 0x17, 0x9f, 0x7f, 0xea, + 0xab, 0xf5, 0xd2, 0x42, 0x1b, 0xf1, 0xbb, 0x0c, 0xda, 0xe3, 0x30, 0x9e, 0x57, 0x5b, 0xfe, 0x2d, + 0x17, 0xbb, 0xfe, 0x69, 0xea, 0xc3, 0x8b, 0x38, 0xf8, 0xd7, 0x55, 0x57, 0x61, 0xf4, 0x4d, 0x1a, + 0x6d, 0x46, 0x8c, 0x06, 0x34, 0xc0, 0x91, 0xa0, 0xae, 0x1a, 0x48, 0x7d, 0x4c, 0x4f, 0x40, 0x2c, + 0x68, 0x96, 0x00, 0xb4, 0x3e, 0xa4, 0x1a, 0x94, 0xb6, 0xe3, 0x2e, 0x65, 0xe6, 0xdb, 0x0a, 0xf9, + 0x6e, 0xfd, 0x6c, 0xce, 0xf0, 0xb3, 0x23, 0x6b, 0x93, 0x74, 0x97, 0xcd, 0x78, 0x0f, 0xad, 0xbb, + 0x93, 0x0b, 0x62, 0x7b, 0xc1, 0xc8, 0xa4, 0xb4, 0xb2, 0x99, 0xf4, 0xc3, 0xc7, 0xb7, 0x4c, 0xd6, + 0x42, 0x2f, 0x09, 0x9d, 0xed, 0xc0, 0x97, 0x18, 0x74, 0xb2, 0x89, 0x41, 0x67, 0x1b, 0x95, 0xb8, + 0xb2, 0xe3, 0xb9, 0xc1, 0xbc, 0x96, 0x05, 0x52, 0xe4, 0xea, 0x62, 0x06, 0x4b, 0x8a, 0x95, 0xbf, + 0xba, 0x58, 0xf7, 0x1f, 0xbf, 0x3f, 0xab, 0xa5, 0x4e, 0xcf, 0x6a, 0xa9, 0x2f, 0x67, 0xb5, 0xd4, + 0xeb, 0xf3, 0xda, 0xd2, 0xe9, 0x79, 0x6d, 0xe9, 0xf3, 0x79, 0x6d, 0xe9, 0xd9, 0xde, 0x2f, 0x4f, + 0xd2, 0x77, 0xdd, 0x6e, 0x37, 0x67, 0x66, 0xe8, 0x3b, 0x5f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xb0, + 0xe0, 0xf5, 0x8a, 0xb9, 0x0b, 0x00, 0x00, +} + +func (m *AuditContext) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AuditContext) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AuditContext) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.CorrelationId) > 0 { + i -= len(m.CorrelationId) + copy(dAtA[i:], m.CorrelationId) + i = encodeVarintEvents(dAtA, i, uint64(len(m.CorrelationId))) + i-- + dAtA[i] = 0x1a + } + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintEvents(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0x12 + } + if len(m.UserId) > 0 { + i -= len(m.UserId) + copy(dAtA[i:], m.UserId) + i = encodeVarintEvents(dAtA, i, uint64(len(m.UserId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EventMetadata) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EventMetadata) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventMetadata) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Sequence != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.Sequence)) + i-- + dAtA[i] = 0x20 + } + if len(m.ConnectionId) > 0 { + i -= len(m.ConnectionId) + copy(dAtA[i:], m.ConnectionId) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ConnectionId))) + i-- + dAtA[i] = 0x1a + } + if m.TimestampMs != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.TimestampMs)) + i-- + dAtA[i] = 0x10 + } + if m.Version != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.Version)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *ResourcePublished) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResourcePublished) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResourcePublished) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.EventMetadata != nil { + { + size, err := m.EventMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if m.AuditContext != nil { + { + size, err := m.AuditContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.TimeToLive != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.TimeToLive)) + i-- + dAtA[i] = 0x18 + } + if m.Resource != nil { + { + size, err := m.Resource.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ResourceUnpublished) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResourceUnpublished) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResourceUnpublished) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.EventMetadata != nil { + { + size, err := m.EventMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.AuditContext != nil { + { + size, err := m.AuditContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ResourceChanged) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResourceChanged) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResourceChanged) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Status != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x28 + } + if m.EventMetadata != nil { + { + size, err := m.EventMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.AuditContext != nil { + { + size, err := m.AuditContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Content != nil { + { + size, err := m.Content.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ResourceUpdatePending) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResourceUpdatePending) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResourceUpdatePending) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.EventMetadata != nil { + { + size, err := m.EventMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + if m.AuditContext != nil { + { + size, err := m.AuditContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if m.Content != nil { + { + size, err := m.Content.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if len(m.ResourceInterface) > 0 { + i -= len(m.ResourceInterface) + copy(dAtA[i:], m.ResourceInterface) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ResourceInterface))) + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ResourceUpdated) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResourceUpdated) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResourceUpdated) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.EventMetadata != nil { + { + size, err := m.EventMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if m.AuditContext != nil { + { + size, err := m.AuditContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.Content != nil { + { + size, err := m.Content.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Status != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x10 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ResourceRetrievePending) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResourceRetrievePending) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResourceRetrievePending) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.EventMetadata != nil { + { + size, err := m.EventMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.AuditContext != nil { + { + size, err := m.AuditContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.ResourceInterface) > 0 { + i -= len(m.ResourceInterface) + copy(dAtA[i:], m.ResourceInterface) + i = encodeVarintEvents(dAtA, i, uint64(len(m.ResourceInterface))) + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ResourceRetrieved) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResourceRetrieved) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResourceRetrieved) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.EventMetadata != nil { + { + size, err := m.EventMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if m.AuditContext != nil { + { + size, err := m.AuditContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + if m.Content != nil { + { + size, err := m.Content.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Status != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x10 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ResourceStateSnapshotTaken) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResourceStateSnapshotTaken) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResourceStateSnapshotTaken) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.EventMetadata != nil { + { + size, err := m.EventMetadata.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + if m.IsPublished { + i-- + if m.IsPublished { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 + } + if m.TimeToLive != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.TimeToLive)) + i-- + dAtA[i] = 0x28 + } + if m.PendingRequestsCount != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.PendingRequestsCount)) + i-- + dAtA[i] = 0x20 + } + if m.LatestResourceChange != nil { + { + size, err := m.LatestResourceChange.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Resource != nil { + { + size, err := m.Resource.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintEvents(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintEvents(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintEvents(dAtA []byte, offset int, v uint64) int { + offset -= sovEvents(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *AuditContext) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.UserId) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.CorrelationId) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *EventMetadata) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Version != 0 { + n += 1 + sovEvents(uint64(m.Version)) + } + if m.TimestampMs != 0 { + n += 1 + sovEvents(uint64(m.TimestampMs)) + } + l = len(m.ConnectionId) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.Sequence != 0 { + n += 1 + sovEvents(uint64(m.Sequence)) + } + return n +} + +func (m *ResourcePublished) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.Resource != nil { + l = m.Resource.Size() + n += 1 + l + sovEvents(uint64(l)) + } + if m.TimeToLive != 0 { + n += 1 + sovEvents(uint64(m.TimeToLive)) + } + if m.AuditContext != nil { + l = m.AuditContext.Size() + n += 1 + l + sovEvents(uint64(l)) + } + if m.EventMetadata != nil { + l = m.EventMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *ResourceUnpublished) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.AuditContext != nil { + l = m.AuditContext.Size() + n += 1 + l + sovEvents(uint64(l)) + } + if m.EventMetadata != nil { + l = m.EventMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *ResourceChanged) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.Content != nil { + l = m.Content.Size() + n += 1 + l + sovEvents(uint64(l)) + } + if m.AuditContext != nil { + l = m.AuditContext.Size() + n += 1 + l + sovEvents(uint64(l)) + } + if m.EventMetadata != nil { + l = m.EventMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) + } + if m.Status != 0 { + n += 1 + sovEvents(uint64(m.Status)) + } + return n +} + +func (m *ResourceUpdatePending) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.ResourceInterface) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.Content != nil { + l = m.Content.Size() + n += 1 + l + sovEvents(uint64(l)) + } + if m.AuditContext != nil { + l = m.AuditContext.Size() + n += 1 + l + sovEvents(uint64(l)) + } + if m.EventMetadata != nil { + l = m.EventMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *ResourceUpdated) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.Status != 0 { + n += 1 + sovEvents(uint64(m.Status)) + } + if m.Content != nil { + l = m.Content.Size() + n += 1 + l + sovEvents(uint64(l)) + } + if m.AuditContext != nil { + l = m.AuditContext.Size() + n += 1 + l + sovEvents(uint64(l)) + } + if m.EventMetadata != nil { + l = m.EventMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *ResourceRetrievePending) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + l = len(m.ResourceInterface) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.AuditContext != nil { + l = m.AuditContext.Size() + n += 1 + l + sovEvents(uint64(l)) + } + if m.EventMetadata != nil { + l = m.EventMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *ResourceRetrieved) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.Status != 0 { + n += 1 + sovEvents(uint64(m.Status)) + } + if m.Content != nil { + l = m.Content.Size() + n += 1 + l + sovEvents(uint64(l)) + } + if m.AuditContext != nil { + l = m.AuditContext.Size() + n += 1 + l + sovEvents(uint64(l)) + } + if m.EventMetadata != nil { + l = m.EventMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func (m *ResourceStateSnapshotTaken) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovEvents(uint64(l)) + } + if m.Resource != nil { + l = m.Resource.Size() + n += 1 + l + sovEvents(uint64(l)) + } + if m.LatestResourceChange != nil { + l = m.LatestResourceChange.Size() + n += 1 + l + sovEvents(uint64(l)) + } + if m.PendingRequestsCount != 0 { + n += 1 + sovEvents(uint64(m.PendingRequestsCount)) + } + if m.TimeToLive != 0 { + n += 1 + sovEvents(uint64(m.TimeToLive)) + } + if m.IsPublished { + n += 2 + } + if m.EventMetadata != nil { + l = m.EventMetadata.Size() + n += 1 + l + sovEvents(uint64(l)) + } + return n +} + +func sovEvents(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozEvents(x uint64) (n int) { + return sovEvents(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *AuditContext) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AuditContext: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AuditContext: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UserId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.UserId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CorrelationId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CorrelationId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventMetadata) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EventMetadata: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventMetadata: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + m.Version = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Version |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TimestampMs", wireType) + } + m.TimestampMs = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TimestampMs |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ConnectionId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ConnectionId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) + } + m.Sequence = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Sequence |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResourcePublished) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResourcePublished: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResourcePublished: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Resource", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Resource == nil { + m.Resource = &Resource{} + } + if err := m.Resource.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TimeToLive", wireType) + } + m.TimeToLive = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TimeToLive |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuditContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuditContext == nil { + m.AuditContext = &AuditContext{} + } + if err := m.AuditContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EventMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.EventMetadata == nil { + m.EventMetadata = &EventMetadata{} + } + if err := m.EventMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResourceUnpublished) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResourceUnpublished: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResourceUnpublished: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuditContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuditContext == nil { + m.AuditContext = &AuditContext{} + } + if err := m.AuditContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EventMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.EventMetadata == nil { + m.EventMetadata = &EventMetadata{} + } + if err := m.EventMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResourceChanged) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResourceChanged: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResourceChanged: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Content == nil { + m.Content = &Content{} + } + if err := m.Content.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuditContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuditContext == nil { + m.AuditContext = &AuditContext{} + } + if err := m.AuditContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EventMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.EventMetadata == nil { + m.EventMetadata = &EventMetadata{} + } + if err := m.EventMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= Status(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResourceUpdatePending) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResourceUpdatePending: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResourceUpdatePending: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceInterface", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceInterface = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Content == nil { + m.Content = &Content{} + } + if err := m.Content.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuditContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuditContext == nil { + m.AuditContext = &AuditContext{} + } + if err := m.AuditContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EventMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.EventMetadata == nil { + m.EventMetadata = &EventMetadata{} + } + if err := m.EventMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResourceUpdated) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResourceUpdated: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResourceUpdated: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= Status(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Content == nil { + m.Content = &Content{} + } + if err := m.Content.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuditContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuditContext == nil { + m.AuditContext = &AuditContext{} + } + if err := m.AuditContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EventMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.EventMetadata == nil { + m.EventMetadata = &EventMetadata{} + } + if err := m.EventMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResourceRetrievePending) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResourceRetrievePending: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResourceRetrievePending: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceInterface", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceInterface = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuditContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuditContext == nil { + m.AuditContext = &AuditContext{} + } + if err := m.AuditContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EventMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.EventMetadata == nil { + m.EventMetadata = &EventMetadata{} + } + if err := m.EventMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResourceRetrieved) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResourceRetrieved: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResourceRetrieved: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= Status(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Content == nil { + m.Content = &Content{} + } + if err := m.Content.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuditContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuditContext == nil { + m.AuditContext = &AuditContext{} + } + if err := m.AuditContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EventMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.EventMetadata == nil { + m.EventMetadata = &EventMetadata{} + } + if err := m.EventMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResourceStateSnapshotTaken) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResourceStateSnapshotTaken: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResourceStateSnapshotTaken: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Resource", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Resource == nil { + m.Resource = &Resource{} + } + if err := m.Resource.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LatestResourceChange", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.LatestResourceChange == nil { + m.LatestResourceChange = &ResourceChanged{} + } + if err := m.LatestResourceChange.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PendingRequestsCount", wireType) + } + m.PendingRequestsCount = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PendingRequestsCount |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TimeToLive", wireType) + } + m.TimeToLive = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TimeToLive |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsPublished", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsPublished = bool(v != 0) + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EventMetadata", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthEvents + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthEvents + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.EventMetadata == nil { + m.EventMetadata = &EventMetadata{} + } + if err := m.EventMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvents(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthEvents + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipEvents(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowEvents + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowEvents + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowEvents + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthEvents + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupEvents + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthEvents + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthEvents = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowEvents = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupEvents = fmt.Errorf("proto: unexpected end of group") +) diff --git a/resource-aggregate/pb/events.proto b/resource-aggregate/pb/events.proto new file mode 100644 index 000000000..0a1e060c2 --- /dev/null +++ b/resource-aggregate/pb/events.proto @@ -0,0 +1,85 @@ +syntax = "proto3"; + +package ocf.cloud.resourceaggregate.pb; + +import "github.com/go-ocf/cloud/resource-aggregate/pb/resources.proto"; + +option go_package = "github.com/go-ocf/cloud/resource-aggregate/pb;pb"; + +message AuditContext { + string user_id = 1; + string device_id = 2; + string correlation_id = 3; +} + +message EventMetadata { + uint64 version = 1; + uint64 timestamp_ms = 2; + string connection_id = 3; + uint64 sequence = 4; +} + +// https://github.com/openconnectivityfoundation/core/blob/master/oic.wk.rd.raml#L106 +message ResourcePublished { + string id = 1; // calucalated from href as uuid.NewV5(uuid.NamespaceURL, device_id+href) + Resource resource = 2; // Resource publish is atomic. The CoAP-Gateway is responsible for the transaction. + int32 time_to_live = 3; + AuditContext audit_context = 4; + EventMetadata event_metadata = 5; +} + +// https://github.com/openconnectivityfoundation/core/blob/master/oic.wk.rd.raml #Specification CR needed +message ResourceUnpublished { + string id = 1; // calucalated from href as uuid.NewV5(uuid.NamespaceURL, device_id+href) + AuditContext audit_context = 2; + EventMetadata event_metadata = 3; +} + +message ResourceChanged { + string id = 1; // calucalated from href as uuid.NewV5(uuid.NamespaceURL, device_id+href) + Content content = 2; + AuditContext audit_context = 3; + EventMetadata event_metadata = 4; + Status status = 5; +} + +message ResourceUpdatePending { + string id = 1; // calucalated from href as uuid.NewV5(uuid.NamespaceURL, device_id+href) + string resource_interface = 2; + Content content = 4; + AuditContext audit_context = 5; + EventMetadata event_metadata = 6; +} + +message ResourceUpdated { + string id = 1; // calucalated from href as uuid.NewV5(uuid.NamespaceURL, device_id+href) + Status status = 2; + Content content = 3; + AuditContext audit_context = 4; + EventMetadata event_metadata = 5; +} + +message ResourceRetrievePending { + string id = 1; // calucalated from href as uuid.NewV5(uuid.NamespaceURL, device_id+href) + string resource_interface = 2; + AuditContext audit_context = 3; + EventMetadata event_metadata = 4; +} + +message ResourceRetrieved { + string id = 1; // calucalated from href as uuid.NewV5(uuid.NamespaceURL, device_id+href) + Status status = 2; + Content content = 3; + AuditContext audit_context = 4; + EventMetadata event_metadata = 5; +} + +message ResourceStateSnapshotTaken { + string id = 1; // calucalated from href as uuid.NewV5(uuid.NamespaceURL, device_id+href) + Resource resource = 2; + ResourceChanged latest_resource_change = 3; + uint32 pending_requests_count = 4; + int32 time_to_live = 5; + bool is_published = 6; + EventMetadata event_metadata = 7; +} diff --git a/resource-aggregate/pb/resources.go b/resource-aggregate/pb/resources.go new file mode 100644 index 000000000..6737f4a08 --- /dev/null +++ b/resource-aggregate/pb/resources.go @@ -0,0 +1,11 @@ +package pb + +// deriveDeepCopy func is generated by 'github.com/awalterschulze/goderive' +// goderive ./... + +// Clone clones recursively contents of resource +func (m *Resource) Clone() *Resource { + n := &Resource{} + deriveDeepCopy(n, m) + return n +} diff --git a/resource-aggregate/pb/resources.pb.go b/resource-aggregate/pb/resources.pb.go new file mode 100644 index 000000000..10c35cddb --- /dev/null +++ b/resource-aggregate/pb/resources.pb.go @@ -0,0 +1,1552 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: pb/resources.proto + +package pb + +import ( + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Status int32 + +const ( + Status_UNKNOWN Status = 0 + Status_OK Status = 1 + Status_BAD_REQUEST Status = 2 + Status_UNAUTHORIZED Status = 3 + Status_FORBIDDEN Status = 4 + Status_NOT_FOUND Status = 5 + Status_UNAVAILABLE Status = 6 + Status_NOT_IMPLEMENTED Status = 7 + Status_ACCEPTED Status = 8 +) + +var Status_name = map[int32]string{ + 0: "UNKNOWN", + 1: "OK", + 2: "BAD_REQUEST", + 3: "UNAUTHORIZED", + 4: "FORBIDDEN", + 5: "NOT_FOUND", + 6: "UNAVAILABLE", + 7: "NOT_IMPLEMENTED", + 8: "ACCEPTED", +} + +var Status_value = map[string]int32{ + "UNKNOWN": 0, + "OK": 1, + "BAD_REQUEST": 2, + "UNAUTHORIZED": 3, + "FORBIDDEN": 4, + "NOT_FOUND": 5, + "UNAVAILABLE": 6, + "NOT_IMPLEMENTED": 7, + "ACCEPTED": 8, +} + +func (x Status) String() string { + return proto.EnumName(Status_name, int32(x)) +} + +func (Status) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_557d59036b1bf976, []int{0} +} + +// https://github.com/openconnectivityfoundation/core/blob/master/schemas/oic.oic-link-schema.json +type Resource struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Href string `protobuf:"bytes,2,opt,name=href,proto3" json:"href"` + ResourceTypes []string `protobuf:"bytes,3,rep,name=resource_types,json=resourceTypes,proto3" json:"rt"` + Interfaces []string `protobuf:"bytes,4,rep,name=interfaces,proto3" json:"if"` + DeviceId string `protobuf:"bytes,5,opt,name=device_id,json=deviceId,proto3" json:"di"` + InstanceId int64 `protobuf:"varint,6,opt,name=instance_id,json=instanceId,proto3" json:"ins"` + Anchor string `protobuf:"bytes,7,opt,name=anchor,proto3" json:"anchor,omitempty"` + Policies *Policies `protobuf:"bytes,8,opt,name=policies,proto3" json:"p"` + Title string `protobuf:"bytes,9,opt,name=title,proto3" json:"title,omitempty"` + SupportedContentTypes []string `protobuf:"bytes,10,rep,name=supported_content_types,json=supportedContentTypes,proto3" json:"type"` + EndpointInformations []*EndpointInformation `protobuf:"bytes,11,rep,name=endpoint_informations,json=endpointInformations,proto3" json:"eps"` +} + +func (m *Resource) Reset() { *m = Resource{} } +func (m *Resource) String() string { return proto.CompactTextString(m) } +func (*Resource) ProtoMessage() {} +func (*Resource) Descriptor() ([]byte, []int) { + return fileDescriptor_557d59036b1bf976, []int{0} +} +func (m *Resource) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Resource) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Resource.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Resource) XXX_Merge(src proto.Message) { + xxx_messageInfo_Resource.Merge(m, src) +} +func (m *Resource) XXX_Size() int { + return m.Size() +} +func (m *Resource) XXX_DiscardUnknown() { + xxx_messageInfo_Resource.DiscardUnknown(m) +} + +var xxx_messageInfo_Resource proto.InternalMessageInfo + +func (m *Resource) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *Resource) GetHref() string { + if m != nil { + return m.Href + } + return "" +} + +func (m *Resource) GetResourceTypes() []string { + if m != nil { + return m.ResourceTypes + } + return nil +} + +func (m *Resource) GetInterfaces() []string { + if m != nil { + return m.Interfaces + } + return nil +} + +func (m *Resource) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +func (m *Resource) GetInstanceId() int64 { + if m != nil { + return m.InstanceId + } + return 0 +} + +func (m *Resource) GetAnchor() string { + if m != nil { + return m.Anchor + } + return "" +} + +func (m *Resource) GetPolicies() *Policies { + if m != nil { + return m.Policies + } + return nil +} + +func (m *Resource) GetTitle() string { + if m != nil { + return m.Title + } + return "" +} + +func (m *Resource) GetSupportedContentTypes() []string { + if m != nil { + return m.SupportedContentTypes + } + return nil +} + +func (m *Resource) GetEndpointInformations() []*EndpointInformation { + if m != nil { + return m.EndpointInformations + } + return nil +} + +type Policies struct { + BitFlags int32 `protobuf:"varint,1,opt,name=bit_flags,json=bitFlags,proto3" json:"bm"` +} + +func (m *Policies) Reset() { *m = Policies{} } +func (m *Policies) String() string { return proto.CompactTextString(m) } +func (*Policies) ProtoMessage() {} +func (*Policies) Descriptor() ([]byte, []int) { + return fileDescriptor_557d59036b1bf976, []int{1} +} +func (m *Policies) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Policies) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Policies.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Policies) XXX_Merge(src proto.Message) { + xxx_messageInfo_Policies.Merge(m, src) +} +func (m *Policies) XXX_Size() int { + return m.Size() +} +func (m *Policies) XXX_DiscardUnknown() { + xxx_messageInfo_Policies.DiscardUnknown(m) +} + +var xxx_messageInfo_Policies proto.InternalMessageInfo + +func (m *Policies) GetBitFlags() int32 { + if m != nil { + return m.BitFlags + } + return 0 +} + +type Content struct { + Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + ContentType string `protobuf:"bytes,2,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"` + CoapContentFormat int32 `protobuf:"varint,3,opt,name=coap_content_format,json=coapContentFormat,proto3" json:"coap_content_format,omitempty"` +} + +func (m *Content) Reset() { *m = Content{} } +func (m *Content) String() string { return proto.CompactTextString(m) } +func (*Content) ProtoMessage() {} +func (*Content) Descriptor() ([]byte, []int) { + return fileDescriptor_557d59036b1bf976, []int{2} +} +func (m *Content) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Content) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Content.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Content) XXX_Merge(src proto.Message) { + xxx_messageInfo_Content.Merge(m, src) +} +func (m *Content) XXX_Size() int { + return m.Size() +} +func (m *Content) XXX_DiscardUnknown() { + xxx_messageInfo_Content.DiscardUnknown(m) +} + +var xxx_messageInfo_Content proto.InternalMessageInfo + +func (m *Content) GetData() []byte { + if m != nil { + return m.Data + } + return nil +} + +func (m *Content) GetContentType() string { + if m != nil { + return m.ContentType + } + return "" +} + +func (m *Content) GetCoapContentFormat() int32 { + if m != nil { + return m.CoapContentFormat + } + return 0 +} + +type EndpointInformation struct { + Endpoint string `protobuf:"bytes,1,opt,name=endpoint,proto3" json:"ep"` + Priority int64 `protobuf:"varint,2,opt,name=priority,proto3" json:"pri"` +} + +func (m *EndpointInformation) Reset() { *m = EndpointInformation{} } +func (m *EndpointInformation) String() string { return proto.CompactTextString(m) } +func (*EndpointInformation) ProtoMessage() {} +func (*EndpointInformation) Descriptor() ([]byte, []int) { + return fileDescriptor_557d59036b1bf976, []int{3} +} +func (m *EndpointInformation) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EndpointInformation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EndpointInformation.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *EndpointInformation) XXX_Merge(src proto.Message) { + xxx_messageInfo_EndpointInformation.Merge(m, src) +} +func (m *EndpointInformation) XXX_Size() int { + return m.Size() +} +func (m *EndpointInformation) XXX_DiscardUnknown() { + xxx_messageInfo_EndpointInformation.DiscardUnknown(m) +} + +var xxx_messageInfo_EndpointInformation proto.InternalMessageInfo + +func (m *EndpointInformation) GetEndpoint() string { + if m != nil { + return m.Endpoint + } + return "" +} + +func (m *EndpointInformation) GetPriority() int64 { + if m != nil { + return m.Priority + } + return 0 +} + +func init() { + proto.RegisterEnum("ocf.cloud.resourceaggregate.pb.Status", Status_name, Status_value) + proto.RegisterType((*Resource)(nil), "ocf.cloud.resourceaggregate.pb.Resource") + proto.RegisterType((*Policies)(nil), "ocf.cloud.resourceaggregate.pb.Policies") + proto.RegisterType((*Content)(nil), "ocf.cloud.resourceaggregate.pb.Content") + proto.RegisterType((*EndpointInformation)(nil), "ocf.cloud.resourceaggregate.pb.EndpointInformation") +} + +func init() { proto.RegisterFile("pb/resources.proto", fileDescriptor_557d59036b1bf976) } + +var fileDescriptor_557d59036b1bf976 = []byte{ + // 697 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x54, 0x5d, 0x6f, 0xda, 0x48, + 0x14, 0xc5, 0x7c, 0x9a, 0x0b, 0x49, 0xbc, 0x93, 0x64, 0xd7, 0x5a, 0xad, 0x80, 0x25, 0xd2, 0x0a, + 0xad, 0x84, 0x91, 0x92, 0x7d, 0xdb, 0x97, 0xc5, 0xc1, 0x68, 0x51, 0x12, 0x93, 0x4e, 0xa0, 0x95, + 0xf2, 0x50, 0xe4, 0x8f, 0x31, 0x19, 0x89, 0x78, 0xa6, 0xf6, 0x50, 0x29, 0x7f, 0xa2, 0xaa, 0xfa, + 0xab, 0xfa, 0x98, 0xc7, 0x3e, 0xa1, 0x2a, 0x79, 0xe3, 0x57, 0x54, 0x1e, 0x1b, 0x92, 0xaa, 0x55, + 0xfb, 0x82, 0x7c, 0xee, 0x39, 0x73, 0xef, 0x3d, 0x77, 0xee, 0x00, 0x88, 0xbb, 0xbd, 0x88, 0xc4, + 0x6c, 0x19, 0x79, 0x24, 0x36, 0x78, 0xc4, 0x04, 0x43, 0x0d, 0xe6, 0x05, 0x86, 0xb7, 0x60, 0x4b, + 0xdf, 0xd8, 0x50, 0xce, 0x7c, 0x1e, 0x91, 0xb9, 0x23, 0x88, 0xc1, 0xdd, 0xdf, 0xbb, 0x73, 0x2a, + 0x6e, 0x96, 0xae, 0xe1, 0xb1, 0xdb, 0xde, 0x9c, 0xcd, 0x59, 0x4f, 0x1e, 0x73, 0x97, 0x81, 0x44, + 0x12, 0xc8, 0xaf, 0x34, 0x5d, 0xfb, 0x5d, 0x11, 0x54, 0x9c, 0xe5, 0x41, 0xbb, 0x90, 0xa7, 0xbe, + 0xae, 0xb4, 0x94, 0x4e, 0x15, 0xe7, 0xa9, 0x8f, 0xfe, 0x80, 0xe2, 0x4d, 0x44, 0x02, 0x3d, 0x9f, + 0x44, 0x4c, 0x75, 0xbd, 0x6a, 0x4a, 0x8c, 0xe5, 0x2f, 0xea, 0xc2, 0xee, 0xa6, 0x83, 0x99, 0xb8, + 0xe3, 0x24, 0xd6, 0x0b, 0xad, 0x42, 0xa7, 0x6a, 0x96, 0xd7, 0xab, 0x66, 0x3e, 0x12, 0x78, 0x67, + 0xc3, 0x4e, 0x12, 0x12, 0xfd, 0x05, 0x40, 0x43, 0x41, 0xa2, 0xc0, 0xf1, 0x48, 0xac, 0x17, 0x9f, + 0xa4, 0x34, 0xc0, 0xcf, 0x18, 0x74, 0x04, 0x55, 0x9f, 0xbc, 0xa5, 0x1e, 0x99, 0x51, 0x5f, 0x2f, + 0xc9, 0xca, 0x52, 0xe6, 0x53, 0xac, 0xa6, 0xc4, 0xc8, 0x47, 0x1d, 0xa8, 0xd1, 0x30, 0x16, 0x4e, + 0x98, 0xca, 0xca, 0x2d, 0xa5, 0x53, 0x30, 0x2b, 0xeb, 0x55, 0xb3, 0x40, 0xc3, 0x38, 0x49, 0x97, + 0x72, 0x23, 0x1f, 0xfd, 0x0a, 0x65, 0x27, 0xf4, 0x6e, 0x58, 0xa4, 0x57, 0xa4, 0xaf, 0x0c, 0xa1, + 0x33, 0x50, 0x39, 0x5b, 0x50, 0x8f, 0x92, 0x58, 0x57, 0x5b, 0x4a, 0xa7, 0x76, 0xdc, 0x31, 0x7e, + 0x3c, 0x5a, 0xe3, 0x32, 0xd3, 0x9b, 0xa5, 0xf5, 0xaa, 0xa9, 0x70, 0xbc, 0x4d, 0x80, 0x0e, 0xa0, + 0x24, 0xa8, 0x58, 0x10, 0xbd, 0x2a, 0x6b, 0xa4, 0x00, 0xfd, 0x07, 0xbf, 0xc5, 0x4b, 0xce, 0x59, + 0x24, 0x88, 0x3f, 0xf3, 0x58, 0x28, 0x48, 0x28, 0xb2, 0x49, 0x81, 0xb4, 0x2f, 0x27, 0x9a, 0x04, + 0xf0, 0xe1, 0x56, 0x78, 0x9a, 0xea, 0xd2, 0x99, 0xbd, 0x81, 0x43, 0x12, 0xfa, 0x9c, 0xd1, 0x50, + 0xcc, 0x68, 0x18, 0xb0, 0xe8, 0xd6, 0x11, 0x94, 0x85, 0xb1, 0x5e, 0x6b, 0x15, 0x3a, 0xb5, 0xe3, + 0x93, 0x9f, 0x75, 0x6c, 0x65, 0x87, 0x47, 0x4f, 0x67, 0xd3, 0x29, 0x11, 0x1e, 0xe3, 0x03, 0xf2, + 0x2d, 0x1b, 0xb7, 0x7b, 0xa0, 0x6e, 0x7c, 0x26, 0x57, 0xe1, 0x52, 0x31, 0x0b, 0x16, 0xce, 0x3c, + 0x96, 0x6b, 0x51, 0x4a, 0xaf, 0xc2, 0xbd, 0xc5, 0xaa, 0x4b, 0xc5, 0x30, 0x89, 0xb7, 0x39, 0x54, + 0xb2, 0x9e, 0x11, 0x82, 0xa2, 0xef, 0x08, 0x47, 0x4a, 0xeb, 0x58, 0x7e, 0xa3, 0x3f, 0xa1, 0xfe, + 0xdc, 0x7a, 0xba, 0x4b, 0xb8, 0xe6, 0x3d, 0xd9, 0x44, 0x06, 0xec, 0x7b, 0xcc, 0xe1, 0xdb, 0x11, + 0xa5, 0xdd, 0xe8, 0x85, 0xa4, 0x20, 0xfe, 0x25, 0xa1, 0xb2, 0x02, 0x43, 0x49, 0xb4, 0x5f, 0xc3, + 0xfe, 0x77, 0x8c, 0xa1, 0x36, 0xa8, 0x1b, 0x47, 0xe9, 0x0e, 0xa7, 0xcd, 0x12, 0x8e, 0xb7, 0x71, + 0x74, 0x04, 0x2a, 0x8f, 0x28, 0x8b, 0xa8, 0xb8, 0x93, 0x9d, 0x64, 0x4b, 0xc3, 0x23, 0x8a, 0xb7, + 0xc4, 0xdf, 0x1f, 0x14, 0x28, 0x5f, 0x09, 0x47, 0x2c, 0x63, 0x54, 0x83, 0xca, 0xd4, 0x3e, 0xb3, + 0xc7, 0xaf, 0x6c, 0x2d, 0x87, 0xca, 0x90, 0x1f, 0x9f, 0x69, 0x0a, 0xda, 0x83, 0x9a, 0xd9, 0x1f, + 0xcc, 0xb0, 0xf5, 0x62, 0x6a, 0x5d, 0x4d, 0xb4, 0x3c, 0xd2, 0xa0, 0x3e, 0xb5, 0xfb, 0xd3, 0xc9, + 0xff, 0x63, 0x3c, 0xba, 0xb6, 0x06, 0x5a, 0x01, 0xed, 0x40, 0x75, 0x38, 0xc6, 0xe6, 0x68, 0x30, + 0xb0, 0x6c, 0xad, 0x98, 0x40, 0x7b, 0x3c, 0x99, 0x0d, 0xc7, 0x53, 0x7b, 0xa0, 0x95, 0x92, 0x04, + 0x53, 0xbb, 0xff, 0xb2, 0x3f, 0x3a, 0xef, 0x9b, 0xe7, 0x96, 0x56, 0x46, 0xfb, 0xb0, 0x97, 0xf0, + 0xa3, 0x8b, 0xcb, 0x73, 0xeb, 0xc2, 0xb2, 0x27, 0xd6, 0x40, 0xab, 0xa0, 0x3a, 0xa8, 0xfd, 0xd3, + 0x53, 0xeb, 0x32, 0x41, 0xaa, 0x69, 0x7f, 0x7c, 0x68, 0x28, 0xf7, 0x0f, 0x0d, 0xe5, 0xf3, 0x43, + 0x43, 0x79, 0xff, 0xd8, 0xc8, 0xdd, 0x3f, 0x36, 0x72, 0x9f, 0x1e, 0x1b, 0xb9, 0xeb, 0x7f, 0xbe, + 0x7a, 0xf1, 0x5d, 0xe6, 0x05, 0x3d, 0xe6, 0x05, 0x5d, 0xb9, 0x16, 0xdb, 0xbf, 0x8f, 0xee, 0x76, + 0x2f, 0x7a, 0xdc, 0xfd, 0x97, 0xbb, 0x6e, 0x59, 0xbe, 0xff, 0x93, 0x2f, 0x01, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xdb, 0xc8, 0x80, 0x64, 0x04, 0x00, 0x00, +} + +func (m *Resource) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Resource) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Resource) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.EndpointInformations) > 0 { + for iNdEx := len(m.EndpointInformations) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.EndpointInformations[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintResources(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x5a + } + } + if len(m.SupportedContentTypes) > 0 { + for iNdEx := len(m.SupportedContentTypes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.SupportedContentTypes[iNdEx]) + copy(dAtA[i:], m.SupportedContentTypes[iNdEx]) + i = encodeVarintResources(dAtA, i, uint64(len(m.SupportedContentTypes[iNdEx]))) + i-- + dAtA[i] = 0x52 + } + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintResources(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0x4a + } + if m.Policies != nil { + { + size, err := m.Policies.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintResources(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + if len(m.Anchor) > 0 { + i -= len(m.Anchor) + copy(dAtA[i:], m.Anchor) + i = encodeVarintResources(dAtA, i, uint64(len(m.Anchor))) + i-- + dAtA[i] = 0x3a + } + if m.InstanceId != 0 { + i = encodeVarintResources(dAtA, i, uint64(m.InstanceId)) + i-- + dAtA[i] = 0x30 + } + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintResources(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0x2a + } + if len(m.Interfaces) > 0 { + for iNdEx := len(m.Interfaces) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Interfaces[iNdEx]) + copy(dAtA[i:], m.Interfaces[iNdEx]) + i = encodeVarintResources(dAtA, i, uint64(len(m.Interfaces[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.ResourceTypes) > 0 { + for iNdEx := len(m.ResourceTypes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ResourceTypes[iNdEx]) + copy(dAtA[i:], m.ResourceTypes[iNdEx]) + i = encodeVarintResources(dAtA, i, uint64(len(m.ResourceTypes[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.Href) > 0 { + i -= len(m.Href) + copy(dAtA[i:], m.Href) + i = encodeVarintResources(dAtA, i, uint64(len(m.Href))) + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintResources(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Policies) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Policies) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Policies) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.BitFlags != 0 { + i = encodeVarintResources(dAtA, i, uint64(m.BitFlags)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Content) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Content) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Content) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.CoapContentFormat != 0 { + i = encodeVarintResources(dAtA, i, uint64(m.CoapContentFormat)) + i-- + dAtA[i] = 0x18 + } + if len(m.ContentType) > 0 { + i -= len(m.ContentType) + copy(dAtA[i:], m.ContentType) + i = encodeVarintResources(dAtA, i, uint64(len(m.ContentType))) + i-- + dAtA[i] = 0x12 + } + if len(m.Data) > 0 { + i -= len(m.Data) + copy(dAtA[i:], m.Data) + i = encodeVarintResources(dAtA, i, uint64(len(m.Data))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *EndpointInformation) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *EndpointInformation) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EndpointInformation) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Priority != 0 { + i = encodeVarintResources(dAtA, i, uint64(m.Priority)) + i-- + dAtA[i] = 0x10 + } + if len(m.Endpoint) > 0 { + i -= len(m.Endpoint) + copy(dAtA[i:], m.Endpoint) + i = encodeVarintResources(dAtA, i, uint64(len(m.Endpoint))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintResources(dAtA []byte, offset int, v uint64) int { + offset -= sovResources(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Resource) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovResources(uint64(l)) + } + l = len(m.Href) + if l > 0 { + n += 1 + l + sovResources(uint64(l)) + } + if len(m.ResourceTypes) > 0 { + for _, s := range m.ResourceTypes { + l = len(s) + n += 1 + l + sovResources(uint64(l)) + } + } + if len(m.Interfaces) > 0 { + for _, s := range m.Interfaces { + l = len(s) + n += 1 + l + sovResources(uint64(l)) + } + } + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovResources(uint64(l)) + } + if m.InstanceId != 0 { + n += 1 + sovResources(uint64(m.InstanceId)) + } + l = len(m.Anchor) + if l > 0 { + n += 1 + l + sovResources(uint64(l)) + } + if m.Policies != nil { + l = m.Policies.Size() + n += 1 + l + sovResources(uint64(l)) + } + l = len(m.Title) + if l > 0 { + n += 1 + l + sovResources(uint64(l)) + } + if len(m.SupportedContentTypes) > 0 { + for _, s := range m.SupportedContentTypes { + l = len(s) + n += 1 + l + sovResources(uint64(l)) + } + } + if len(m.EndpointInformations) > 0 { + for _, e := range m.EndpointInformations { + l = e.Size() + n += 1 + l + sovResources(uint64(l)) + } + } + return n +} + +func (m *Policies) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BitFlags != 0 { + n += 1 + sovResources(uint64(m.BitFlags)) + } + return n +} + +func (m *Content) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Data) + if l > 0 { + n += 1 + l + sovResources(uint64(l)) + } + l = len(m.ContentType) + if l > 0 { + n += 1 + l + sovResources(uint64(l)) + } + if m.CoapContentFormat != 0 { + n += 1 + sovResources(uint64(m.CoapContentFormat)) + } + return n +} + +func (m *EndpointInformation) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Endpoint) + if l > 0 { + n += 1 + l + sovResources(uint64(l)) + } + if m.Priority != 0 { + n += 1 + sovResources(uint64(m.Priority)) + } + return n +} + +func sovResources(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozResources(x uint64) (n int) { + return sovResources(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Resource) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Resource: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Resource: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Href", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Href = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceTypes", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceTypes = append(m.ResourceTypes, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Interfaces", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Interfaces = append(m.Interfaces, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field InstanceId", wireType) + } + m.InstanceId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.InstanceId |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Anchor", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Anchor = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Policies", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Policies == nil { + m.Policies = &Policies{} + } + if err := m.Policies.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SupportedContentTypes", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SupportedContentTypes = append(m.SupportedContentTypes, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EndpointInformations", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.EndpointInformations = append(m.EndpointInformations, &EndpointInformation{}) + if err := m.EndpointInformations[len(m.EndpointInformations)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipResources(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthResources + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthResources + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Policies) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Policies: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Policies: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BitFlags", wireType) + } + m.BitFlags = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BitFlags |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipResources(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthResources + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthResources + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Content) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Content: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Content: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) + if m.Data == nil { + m.Data = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ContentType", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ContentType = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field CoapContentFormat", wireType) + } + m.CoapContentFormat = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.CoapContentFormat |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipResources(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthResources + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthResources + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EndpointInformation) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: EndpointInformation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EndpointInformation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Endpoint", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthResources + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthResources + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Endpoint = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Priority", wireType) + } + m.Priority = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowResources + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Priority |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipResources(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthResources + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthResources + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipResources(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowResources + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowResources + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowResources + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthResources + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupResources + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthResources + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthResources = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowResources = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupResources = fmt.Errorf("proto: unexpected end of group") +) diff --git a/resource-aggregate/pb/resources.proto b/resource-aggregate/pb/resources.proto new file mode 100644 index 000000000..b5606b935 --- /dev/null +++ b/resource-aggregate/pb/resources.proto @@ -0,0 +1,49 @@ +syntax = "proto3"; + +package ocf.cloud.resourceaggregate.pb; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +option go_package = "github.com/go-ocf/cloud/resource-aggregate/pb;pb"; + +// https://github.com/openconnectivityfoundation/core/blob/master/schemas/oic.oic-link-schema.json +message Resource { + string id = 1; // calucalated from href as uuid.NewV5(uuid.NamespaceURL, device_id+href) + string href = 2 [(gogoproto.jsontag)="href"]; + repeated string resource_types = 3 [(gogoproto.jsontag)="rt"]; + repeated string interfaces = 4 [(gogoproto.jsontag)="if"]; + string device_id = 5 [(gogoproto.jsontag)="di"]; + int64 instance_id = 6 [(gogoproto.jsontag)="ins"]; + string anchor = 7; + Policies policies = 8 [(gogoproto.jsontag)="p"]; + string title = 9; + repeated string supported_content_types = 10 [(gogoproto.jsontag)="type"]; + repeated EndpointInformation endpoint_informations = 11 [(gogoproto.jsontag)="eps"]; +} + +message Policies { + int32 bit_flags = 1 [(gogoproto.jsontag)="bm"]; +} + +message Content { + bytes data = 1; + string content_type = 2; + int32 coap_content_format = 3; // -1 means content-format was not provided +} + +message EndpointInformation { + string endpoint = 1 [(gogoproto.jsontag)="ep"]; + int64 priority = 2 [(gogoproto.jsontag)="pri"]; +} + +enum Status { + UNKNOWN = 0; + OK = 1; + BAD_REQUEST = 2; + UNAUTHORIZED = 3; + FORBIDDEN = 4; + NOT_FOUND = 5; + UNAVAILABLE = 6; + NOT_IMPLEMENTED = 7; + ACCEPTED = 8; +} diff --git a/resource-aggregate/pb/service.pb.go b/resource-aggregate/pb/service.pb.go new file mode 100644 index 000000000..568ef4856 --- /dev/null +++ b/resource-aggregate/pb/service.pb.go @@ -0,0 +1,350 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: pb/service.proto + +package pb + +import ( + context "context" + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +func init() { proto.RegisterFile("pb/service.proto", fileDescriptor_6ff5ab49d8a5fcc4) } + +var fileDescriptor_6ff5ab49d8a5fcc4 = []byte{ + // 351 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x94, 0x4d, 0x4b, 0xf3, 0x40, + 0x10, 0xc7, 0x9f, 0x5c, 0x1e, 0x64, 0x0f, 0xda, 0x06, 0x44, 0xe8, 0xc1, 0x83, 0xf7, 0xec, 0x82, + 0x2f, 0xb5, 0xe2, 0xbb, 0xbd, 0x8b, 0x14, 0x7a, 0xf1, 0x96, 0xdd, 0x4c, 0xb6, 0x81, 0x26, 0xb3, + 0xee, 0x6e, 0x0a, 0x1e, 0x05, 0x51, 0xf0, 0x03, 0x78, 0xf2, 0xc3, 0x0a, 0x89, 0x9b, 0xd2, 0x58, + 0x8d, 0xc9, 0x2d, 0xcb, 0xfc, 0x7f, 0x33, 0xbf, 0x1d, 0xc2, 0x92, 0x9e, 0xe2, 0xcc, 0x80, 0x5e, + 0x24, 0x02, 0xa8, 0xd2, 0x68, 0xd1, 0xdf, 0x45, 0x11, 0x53, 0x31, 0xc7, 0x3c, 0xa2, 0x1a, 0x0c, + 0xe6, 0x5a, 0x40, 0x28, 0xa5, 0x06, 0x19, 0x5a, 0xa0, 0x8a, 0x0f, 0x02, 0x99, 0xd8, 0x59, 0xce, + 0xa9, 0xc0, 0x94, 0x49, 0x94, 0xc8, 0x0a, 0x8c, 0xe7, 0x71, 0x71, 0x2a, 0x0e, 0xc5, 0x57, 0xd9, + 0x6e, 0x70, 0xb5, 0x12, 0x0f, 0x50, 0xc4, 0x0c, 0x45, 0x1c, 0x14, 0x03, 0x98, 0x1b, 0x10, 0x54, + 0x13, 0x98, 0xe2, 0x4c, 0x60, 0x9a, 0x86, 0x59, 0x64, 0xca, 0x0e, 0xfb, 0x2f, 0x1b, 0xa4, 0x3f, + 0xf9, 0x0a, 0x5e, 0xbb, 0x9c, 0xff, 0xec, 0x91, 0xad, 0xbb, 0x9c, 0xcf, 0x13, 0x33, 0x73, 0x45, + 0x7f, 0x48, 0x7f, 0x77, 0xa7, 0x35, 0x60, 0x02, 0x0f, 0x39, 0x18, 0x3b, 0x38, 0x6e, 0xcd, 0x19, + 0x85, 0x99, 0x81, 0xbd, 0x7f, 0xfe, 0x9b, 0x47, 0xfa, 0xd3, 0x4c, 0xd5, 0x44, 0x46, 0x4d, 0x0d, + 0xbf, 0x21, 0x4e, 0xe5, 0xa4, 0x03, 0x59, 0xc9, 0xbc, 0x7b, 0x64, 0xfb, 0x16, 0x6d, 0x12, 0x3f, + 0xba, 0xe2, 0x78, 0x16, 0x66, 0x12, 0x22, 0xff, 0xac, 0xa9, 0xed, 0x5a, 0xcc, 0x49, 0x9d, 0x77, + 0xa4, 0x2b, 0xb1, 0x27, 0x8f, 0x6c, 0x4e, 0x55, 0x14, 0x5a, 0xa8, 0x56, 0x74, 0xd4, 0x78, 0xd1, + 0x95, 0xbc, 0x53, 0x19, 0xb6, 0xc5, 0x56, 0x96, 0x33, 0xc6, 0x2c, 0x4e, 0x74, 0xea, 0xaa, 0x65, + 0xb6, 0x79, 0x39, 0x6b, 0xb1, 0x3f, 0x2f, 0xe7, 0x07, 0xba, 0x12, 0x7b, 0xf5, 0x48, 0x6f, 0x02, + 0x56, 0x27, 0xb0, 0x58, 0xae, 0xa7, 0xf1, 0x97, 0xac, 0x13, 0x4e, 0x67, 0xd4, 0x1e, 0xac, 0x4c, + 0x3e, 0x3c, 0xb2, 0x53, 0xb3, 0x75, 0x69, 0xff, 0xa2, 0xe5, 0x35, 0x97, 0x63, 0x4a, 0xaf, 0xcb, + 0xce, 0xbc, 0xd3, 0xbb, 0x19, 0xde, 0x1f, 0xb6, 0x7e, 0x4c, 0x4e, 0x15, 0xe7, 0xff, 0x8b, 0x77, + 0xe4, 0xe0, 0x33, 0x00, 0x00, 0xff, 0xff, 0x98, 0xfe, 0xfd, 0xf9, 0xec, 0x04, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// ResourceAggregateClient is the client API for ResourceAggregate service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type ResourceAggregateClient interface { + PublishResource(ctx context.Context, in *PublishResourceRequest, opts ...grpc.CallOption) (*PublishResourceResponse, error) + UnpublishResource(ctx context.Context, in *UnpublishResourceRequest, opts ...grpc.CallOption) (*UnpublishResourceResponse, error) + NotifyResourceChanged(ctx context.Context, in *NotifyResourceChangedRequest, opts ...grpc.CallOption) (*NotifyResourceChangedResponse, error) + UpdateResource(ctx context.Context, in *UpdateResourceRequest, opts ...grpc.CallOption) (*UpdateResourceResponse, error) + ConfirmResourceUpdate(ctx context.Context, in *ConfirmResourceUpdateRequest, opts ...grpc.CallOption) (*ConfirmResourceUpdateResponse, error) + RetrieveResource(ctx context.Context, in *RetrieveResourceRequest, opts ...grpc.CallOption) (*RetrieveResourceResponse, error) + ConfirmResourceRetrieve(ctx context.Context, in *ConfirmResourceRetrieveRequest, opts ...grpc.CallOption) (*ConfirmResourceRetrieveResponse, error) +} + +type resourceAggregateClient struct { + cc *grpc.ClientConn +} + +func NewResourceAggregateClient(cc *grpc.ClientConn) ResourceAggregateClient { + return &resourceAggregateClient{cc} +} + +func (c *resourceAggregateClient) PublishResource(ctx context.Context, in *PublishResourceRequest, opts ...grpc.CallOption) (*PublishResourceResponse, error) { + out := new(PublishResourceResponse) + err := c.cc.Invoke(ctx, "/ocf.cloud.resourceaggregate.pb.ResourceAggregate/PublishResource", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *resourceAggregateClient) UnpublishResource(ctx context.Context, in *UnpublishResourceRequest, opts ...grpc.CallOption) (*UnpublishResourceResponse, error) { + out := new(UnpublishResourceResponse) + err := c.cc.Invoke(ctx, "/ocf.cloud.resourceaggregate.pb.ResourceAggregate/UnpublishResource", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *resourceAggregateClient) NotifyResourceChanged(ctx context.Context, in *NotifyResourceChangedRequest, opts ...grpc.CallOption) (*NotifyResourceChangedResponse, error) { + out := new(NotifyResourceChangedResponse) + err := c.cc.Invoke(ctx, "/ocf.cloud.resourceaggregate.pb.ResourceAggregate/NotifyResourceChanged", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *resourceAggregateClient) UpdateResource(ctx context.Context, in *UpdateResourceRequest, opts ...grpc.CallOption) (*UpdateResourceResponse, error) { + out := new(UpdateResourceResponse) + err := c.cc.Invoke(ctx, "/ocf.cloud.resourceaggregate.pb.ResourceAggregate/UpdateResource", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *resourceAggregateClient) ConfirmResourceUpdate(ctx context.Context, in *ConfirmResourceUpdateRequest, opts ...grpc.CallOption) (*ConfirmResourceUpdateResponse, error) { + out := new(ConfirmResourceUpdateResponse) + err := c.cc.Invoke(ctx, "/ocf.cloud.resourceaggregate.pb.ResourceAggregate/ConfirmResourceUpdate", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *resourceAggregateClient) RetrieveResource(ctx context.Context, in *RetrieveResourceRequest, opts ...grpc.CallOption) (*RetrieveResourceResponse, error) { + out := new(RetrieveResourceResponse) + err := c.cc.Invoke(ctx, "/ocf.cloud.resourceaggregate.pb.ResourceAggregate/RetrieveResource", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *resourceAggregateClient) ConfirmResourceRetrieve(ctx context.Context, in *ConfirmResourceRetrieveRequest, opts ...grpc.CallOption) (*ConfirmResourceRetrieveResponse, error) { + out := new(ConfirmResourceRetrieveResponse) + err := c.cc.Invoke(ctx, "/ocf.cloud.resourceaggregate.pb.ResourceAggregate/ConfirmResourceRetrieve", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ResourceAggregateServer is the server API for ResourceAggregate service. +type ResourceAggregateServer interface { + PublishResource(context.Context, *PublishResourceRequest) (*PublishResourceResponse, error) + UnpublishResource(context.Context, *UnpublishResourceRequest) (*UnpublishResourceResponse, error) + NotifyResourceChanged(context.Context, *NotifyResourceChangedRequest) (*NotifyResourceChangedResponse, error) + UpdateResource(context.Context, *UpdateResourceRequest) (*UpdateResourceResponse, error) + ConfirmResourceUpdate(context.Context, *ConfirmResourceUpdateRequest) (*ConfirmResourceUpdateResponse, error) + RetrieveResource(context.Context, *RetrieveResourceRequest) (*RetrieveResourceResponse, error) + ConfirmResourceRetrieve(context.Context, *ConfirmResourceRetrieveRequest) (*ConfirmResourceRetrieveResponse, error) +} + +// UnimplementedResourceAggregateServer can be embedded to have forward compatible implementations. +type UnimplementedResourceAggregateServer struct { +} + +func (*UnimplementedResourceAggregateServer) PublishResource(ctx context.Context, req *PublishResourceRequest) (*PublishResourceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method PublishResource not implemented") +} +func (*UnimplementedResourceAggregateServer) UnpublishResource(ctx context.Context, req *UnpublishResourceRequest) (*UnpublishResourceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UnpublishResource not implemented") +} +func (*UnimplementedResourceAggregateServer) NotifyResourceChanged(ctx context.Context, req *NotifyResourceChangedRequest) (*NotifyResourceChangedResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method NotifyResourceChanged not implemented") +} +func (*UnimplementedResourceAggregateServer) UpdateResource(ctx context.Context, req *UpdateResourceRequest) (*UpdateResourceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateResource not implemented") +} +func (*UnimplementedResourceAggregateServer) ConfirmResourceUpdate(ctx context.Context, req *ConfirmResourceUpdateRequest) (*ConfirmResourceUpdateResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConfirmResourceUpdate not implemented") +} +func (*UnimplementedResourceAggregateServer) RetrieveResource(ctx context.Context, req *RetrieveResourceRequest) (*RetrieveResourceResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RetrieveResource not implemented") +} +func (*UnimplementedResourceAggregateServer) ConfirmResourceRetrieve(ctx context.Context, req *ConfirmResourceRetrieveRequest) (*ConfirmResourceRetrieveResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConfirmResourceRetrieve not implemented") +} + +func RegisterResourceAggregateServer(s *grpc.Server, srv ResourceAggregateServer) { + s.RegisterService(&_ResourceAggregate_serviceDesc, srv) +} + +func _ResourceAggregate_PublishResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PublishResourceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ResourceAggregateServer).PublishResource(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocf.cloud.resourceaggregate.pb.ResourceAggregate/PublishResource", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ResourceAggregateServer).PublishResource(ctx, req.(*PublishResourceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ResourceAggregate_UnpublishResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UnpublishResourceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ResourceAggregateServer).UnpublishResource(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocf.cloud.resourceaggregate.pb.ResourceAggregate/UnpublishResource", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ResourceAggregateServer).UnpublishResource(ctx, req.(*UnpublishResourceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ResourceAggregate_NotifyResourceChanged_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(NotifyResourceChangedRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ResourceAggregateServer).NotifyResourceChanged(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocf.cloud.resourceaggregate.pb.ResourceAggregate/NotifyResourceChanged", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ResourceAggregateServer).NotifyResourceChanged(ctx, req.(*NotifyResourceChangedRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ResourceAggregate_UpdateResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateResourceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ResourceAggregateServer).UpdateResource(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocf.cloud.resourceaggregate.pb.ResourceAggregate/UpdateResource", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ResourceAggregateServer).UpdateResource(ctx, req.(*UpdateResourceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ResourceAggregate_ConfirmResourceUpdate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ConfirmResourceUpdateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ResourceAggregateServer).ConfirmResourceUpdate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocf.cloud.resourceaggregate.pb.ResourceAggregate/ConfirmResourceUpdate", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ResourceAggregateServer).ConfirmResourceUpdate(ctx, req.(*ConfirmResourceUpdateRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ResourceAggregate_RetrieveResource_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RetrieveResourceRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ResourceAggregateServer).RetrieveResource(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocf.cloud.resourceaggregate.pb.ResourceAggregate/RetrieveResource", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ResourceAggregateServer).RetrieveResource(ctx, req.(*RetrieveResourceRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ResourceAggregate_ConfirmResourceRetrieve_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ConfirmResourceRetrieveRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ResourceAggregateServer).ConfirmResourceRetrieve(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ocf.cloud.resourceaggregate.pb.ResourceAggregate/ConfirmResourceRetrieve", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ResourceAggregateServer).ConfirmResourceRetrieve(ctx, req.(*ConfirmResourceRetrieveRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _ResourceAggregate_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ocf.cloud.resourceaggregate.pb.ResourceAggregate", + HandlerType: (*ResourceAggregateServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "PublishResource", + Handler: _ResourceAggregate_PublishResource_Handler, + }, + { + MethodName: "UnpublishResource", + Handler: _ResourceAggregate_UnpublishResource_Handler, + }, + { + MethodName: "NotifyResourceChanged", + Handler: _ResourceAggregate_NotifyResourceChanged_Handler, + }, + { + MethodName: "UpdateResource", + Handler: _ResourceAggregate_UpdateResource_Handler, + }, + { + MethodName: "ConfirmResourceUpdate", + Handler: _ResourceAggregate_ConfirmResourceUpdate_Handler, + }, + { + MethodName: "RetrieveResource", + Handler: _ResourceAggregate_RetrieveResource_Handler, + }, + { + MethodName: "ConfirmResourceRetrieve", + Handler: _ResourceAggregate_ConfirmResourceRetrieve_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "pb/service.proto", +} diff --git a/resource-aggregate/pb/service.proto b/resource-aggregate/pb/service.proto new file mode 100644 index 000000000..a157b9b92 --- /dev/null +++ b/resource-aggregate/pb/service.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +package ocf.cloud.resourceaggregate.pb; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/go-ocf/cloud/resource-aggregate/pb/commands.proto"; + +option go_package = "github.com/go-ocf/cloud/resource-aggregate/pb;pb"; + +service ResourceAggregate { + rpc PublishResource (PublishResourceRequest) returns (PublishResourceResponse) {} + rpc UnpublishResource (UnpublishResourceRequest) returns (UnpublishResourceResponse) {} + rpc NotifyResourceChanged (NotifyResourceChangedRequest) returns (NotifyResourceChangedResponse){} + rpc UpdateResource (UpdateResourceRequest) returns (UpdateResourceResponse) {} + rpc ConfirmResourceUpdate (ConfirmResourceUpdateRequest) returns (ConfirmResourceUpdateResponse) {} + rpc RetrieveResource (RetrieveResourceRequest) returns (RetrieveResourceResponse) {} + rpc ConfirmResourceRetrieve (ConfirmResourceRetrieveRequest) returns (ConfirmResourceRetrieveResponse) {} +} \ No newline at end of file diff --git a/resource-aggregate/refImpl/maintenance/maintenance.go b/resource-aggregate/refImpl/maintenance/maintenance.go new file mode 100644 index 000000000..5bf8cb089 --- /dev/null +++ b/resource-aggregate/refImpl/maintenance/maintenance.go @@ -0,0 +1,198 @@ +package maintenance + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "os" + "path" + "strings" + "sync" + "time" + + "github.com/go-ocf/cqrs/event" + "github.com/go-ocf/cqrs/eventstore" + "github.com/go-ocf/cqrs/eventstore/maintenance" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/mongodb" + "github.com/go-ocf/kit/log" + "github.com/jessevdk/go-flags" +) + +// Config represent application arguments +type Config struct { + NumAggregates int `long:"numAggregates" short:"n" default:"77" description:"a number of resource aggregates to perform cleanup onto"` + BackupPath string `long:"backupPath" short:"b" default:"/tmp/events.bkp" description:"backup text file path"` + Mongo mongodb.Config +} + +//String return string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} + +type recordHandler struct { + lock sync.Mutex + tasks []maintenance.Task +} + +func newRecordHandler() *recordHandler { + return &recordHandler{tasks: make([]maintenance.Task, 0, 77)} +} + +func (eh *recordHandler) SetElement(task maintenance.Task) { + eh.lock.Lock() + defer eh.lock.Unlock() + eh.tasks = append(eh.tasks, maintenance.Task{AggregateID: task.AggregateID, Version: task.Version}) +} + +func (eh *recordHandler) Handle(ctx context.Context, iter maintenance.Iter) error { + var task maintenance.Task + + for iter.Next(ctx, &task) { + eh.SetElement(task) + } + return nil +} + +type hEvent struct { + VersionI uint64 `json:"version"` + EventTypeI string `json:"eventtype"` + Data []byte `json:"data"` +} + +type eventHandler struct { + backupPath string +} + +func newEventHandler(backupPath string) *eventHandler { + return &eventHandler{backupPath: backupPath} +} + +func unmarshalPlain(data []byte, v interface{}) error { + if a, ok := v.(*[]byte); ok { + *a = data + return nil + } + return fmt.Errorf("unsupported type for unmarshaler %T", v) +} + +func handleBackupFile(file **os.File, aggregateID, backupPath string) error { + if *file != nil { + if err := (*file).Sync(); err != nil { + return err + } + if err := (*file).Close(); err != nil { + return nil + } + } + + ext := path.Ext(backupPath) + var err error + *file, err = os.Create(strings.TrimSuffix(backupPath, ext) + "_" + aggregateID + "_" + time.Now().Format("2006-01-02T15:04:05.000") + ext) + if err != nil { + return err + } + + return nil +} + +func backup(file *os.File, eu event.EventUnmarshaler) error { + var e []byte + err := eu.Unmarshal(&e) + if err != nil { + return err + } + event := hEvent{VersionI: eu.Version, EventTypeI: eu.EventType, Data: e} + + b, _ := json.MarshalIndent(event, "", " ") + text := fmt.Sprintf(string(b) + "\n") + if _, err = file.WriteString(text); err != nil { + return err + } + + return nil +} + +func (eh *eventHandler) Handle(ctx context.Context, iter event.Iter) error { + var eu event.EventUnmarshaler + + aggregateID := "" + var file *os.File + + for iter.Next(ctx, &eu) { + if eu.EventType == "" { + return errors.New("cannot determine type of event") + } + + if aggregateID != eu.AggregateId { + aggregateID = eu.AggregateId + + if err := handleBackupFile(&file, aggregateID, eh.backupPath); err != nil { + return err + } + } + + if err := backup(file, eu); err != nil { + return err + } + } + + if file != nil { + if err := file.Close(); err != nil { + return nil + } + } + + return nil +} + +// PerformMaintenance performs the backup & maintenance of the database +func PerformMaintenance() error { + ctx := context.Background() + + var config Config + parser := flags.NewParser(&config, flags.Default) + _, err := parser.Parse() + if err != nil { + log.Error(err) + os.Exit(2) + } + log.Info(config.String()) + + eventStore, err := mongodb.NewEventStore(config.Mongo, nil, mongodb.WithUnmarshaler(unmarshalPlain)) + if err != nil { + return err + } + + if err = performMaintenanceWithEventStore(ctx, config, eventStore); err != nil { + return err + } + + return nil +} + +func performMaintenanceWithEventStore(ctx context.Context, config Config, eventStore *mongodb.EventStore) error { + handler := newRecordHandler() + if err := eventStore.Query(ctx, config.NumAggregates, handler); err != nil { + return err + } + versionQueries := []eventstore.VersionQuery{} + for _, task := range handler.tasks { + versionQueries = append(versionQueries, eventstore.VersionQuery{AggregateId: task.AggregateID, Version: task.Version}) + } + + log.Info("backing up the events") + eventHandler := newEventHandler(config.BackupPath) + if err := eventStore.LoadUpToVersion(ctx, versionQueries, eventHandler); err != nil { + return err + } + + log.Info("deleting events...") + if err := eventStore.RemoveUpToVersion(ctx, versionQueries); err != nil { + return err + } + + return nil +} diff --git a/resource-aggregate/refImpl/maintenance/maintenance_test.go b/resource-aggregate/refImpl/maintenance/maintenance_test.go new file mode 100644 index 000000000..04c0df25a --- /dev/null +++ b/resource-aggregate/refImpl/maintenance/maintenance_test.go @@ -0,0 +1,150 @@ +package maintenance + +import ( + "context" + "fmt" + "testing" + + "github.com/go-ocf/cqrs/event" + "github.com/go-ocf/cqrs/eventstore/maintenance" + "github.com/go-ocf/kit/security/certManager" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/events" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/mongodb" + cqrs "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/mongodb" + "github.com/go-ocf/cloud/resource-aggregate/pb" + kitCqrsPb "github.com/go-ocf/cloud/resource-aggregate/pb" + "github.com/golang/snappy" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" + "go.mongodb.org/mongo-driver/bson" + + "github.com/stretchr/testify/require" +) + +type mockEvent struct { + VersionI uint64 `bson:"version"` + EventTypeI string `bson:"eventtype"` + Data []byte `bson:"data"` +} + +func (e mockEvent) Version() uint64 { + return e.VersionI +} + +func (e mockEvent) EventType() string { + return e.EventTypeI +} + +func (e mockEvent) Marshal() ([]byte, error) { + src, err := bson.Marshal(e) + if err != nil { + return nil, fmt.Errorf("cannot marshal event: %w", err) + } + dst := make([]byte, 1024) + return snappy.Encode(dst, src), nil +} + +func TestPerformMaintenance(t *testing.T) { + ctx := context.Background() + + config := Config{ + NumAggregates: 77, + BackupPath: "/tmp/events.txt", + Mongo: cqrs.Config{ + URI: "mongodb://localhost:27017", + DatabaseName: "maintenance_test", + BatchSize: 16, + MaxPoolSize: 16, + MaxConnIdleTime: 240000000000, + }} + + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + + tlsConfig := dialCertManager.GetClientTLSConfig() + + store, err := cqrs.NewEventStore(config.Mongo, nil, mongodb.WithMarshaler(bson.Marshal), mongodb.WithUnmarshaler(unmarshalPlain), mongodb.WithTLS(&tlsConfig)) + require.NoError(t, err) + require.NotNil(t, store) + + defer store.Close(ctx) + defer func() { + t.Log("clearing db") + err := store.Clear(ctx) + require.NoError(t, err) + }() + + aggregateID1 := "aggregateID1" + aggregateID2 := "aggregateID2" + eventsToSave := []event.Event{ + &events.ResourceStateSnapshotTaken{ + ResourceStateSnapshotTaken: pb.ResourceStateSnapshotTaken{ + EventMetadata: &kitCqrsPb.EventMetadata{Version: 0}, + }, + }, + mockEvent{ + VersionI: 1, + EventTypeI: "test1", + }, + mockEvent{ + VersionI: 2, + EventTypeI: "test2", + Data: []byte("data of event 2"), + }, + mockEvent{ + VersionI: 3, + EventTypeI: "test3", + Data: []byte("data of event 3"), + }, + mockEvent{ + VersionI: 4, + EventTypeI: "test4", + Data: []byte("data of event 4"), + }, + } + + t.Log("insert aggregateID = 1 events into the event store") + conExcep, err := store.Save(ctx, "default-group", aggregateID1, eventsToSave) + require.NoError(t, err) + require.False(t, conExcep) + + t.Log("insert aggregateID = 2 events into the event store") + conExcep, err = store.Save(ctx, "default-group", aggregateID2, eventsToSave) + require.NoError(t, err) + require.False(t, conExcep) + + tasksToSave := []maintenance.Task{ + maintenance.Task{ + AggregateID: aggregateID1, + Version: 3, + }, + maintenance.Task{ + AggregateID: aggregateID1, + Version: 4, + }, + maintenance.Task{ + AggregateID: aggregateID2, + Version: 3, + }, + } + + t.Log("perform maintenance") + err = store.Insert(ctx, tasksToSave[0]) + require.NoError(t, err) + + err = performMaintenanceWithEventStore(ctx, config, store) + require.NoError(t, err) + + t.Log("perform maintenance again") + err = store.Insert(ctx, tasksToSave[1]) + require.NoError(t, err) + err = store.Insert(ctx, tasksToSave[2]) + require.NoError(t, err) + + err = performMaintenanceWithEventStore(ctx, config, store) + require.NoError(t, err) +} diff --git a/resource-aggregate/refImpl/refImpl.go b/resource-aggregate/refImpl/refImpl.go new file mode 100644 index 000000000..bd52fae39 --- /dev/null +++ b/resource-aggregate/refImpl/refImpl.go @@ -0,0 +1,82 @@ +package refImpl + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/go-ocf/kit/log" + "github.com/go-ocf/kit/security/certManager" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventbus/nats" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/mongodb" + "github.com/go-ocf/cloud/resource-aggregate/service" +) + +type Config struct { + Service service.Config + Nats nats.Config `envconfig:"NATS"` + MongoDB mongodb.Config `envconfig:"MONGODB"` + Listen certManager.Config `envconfig:"LISTEN"` + Dial certManager.Config `envconfig:"DIAL"` + Log log.Config `envconfig:"LOG"` +} + +//String return string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} + +type RefImpl struct { + eventstore *mongodb.EventStore + service *service.Server + publisher *nats.Publisher + clientCertManager certManager.CertManager + serverCertManager certManager.CertManager +} + +func Init(config Config) (*RefImpl, error) { + log.Setup(config.Log) + + clientCertManager, err := certManager.NewCertManager(config.Dial) + if err != nil { + return nil, fmt.Errorf("cannot create client cert manager %w", err) + } + tlsConfig := clientCertManager.GetClientTLSConfig() + + eventstore, err := mongodb.NewEventStore(config.MongoDB, nil, mongodb.WithTLS(&tlsConfig)) + if err != nil { + return nil, fmt.Errorf("cannot create mongodb eventstore %w", err) + } + publisher, err := nats.NewPublisher(config.Nats, nats.WithTLS(&tlsConfig)) + if err != nil { + return nil, fmt.Errorf("cannot create kafka publisher %w", err) + } + + serverCertManager, err := certManager.NewCertManager(config.Listen) + if err != nil { + return nil, fmt.Errorf("cannot create server cert manager %w", err) + } + + log.Info(config.String()) + + return &RefImpl{ + eventstore: eventstore, + service: service.New(config.Service, clientCertManager, serverCertManager, eventstore, publisher), + publisher: publisher, + clientCertManager: clientCertManager, + serverCertManager: serverCertManager, + }, nil +} + +func (r *RefImpl) Serve() error { + return r.service.Serve() +} + +func (r *RefImpl) Shutdown() { + r.service.Shutdown() + r.eventstore.Close(context.Background()) + r.publisher.Close() + r.clientCertManager.Close() + r.serverCertManager.Close() +} diff --git a/resource-aggregate/refImpl/refImpl_test.go b/resource-aggregate/refImpl/refImpl_test.go new file mode 100644 index 000000000..ca2af2f8e --- /dev/null +++ b/resource-aggregate/refImpl/refImpl_test.go @@ -0,0 +1,18 @@ +package refImpl + +import ( + "testing" + + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/require" +) + +func TestInit(t *testing.T) { + var config Config + err := envconfig.Process("", &config) + require.NoError(t, err) + + got, err := Init(config) + require.NoError(t, err) + require.NotEmpty(t, got) +} diff --git a/resource-aggregate/service/aggregate.go b/resource-aggregate/service/aggregate.go new file mode 100644 index 000000000..343c1d0f6 --- /dev/null +++ b/resource-aggregate/service/aggregate.go @@ -0,0 +1,322 @@ +package service + +import ( + "context" + "fmt" + + "google.golang.org/grpc/status" + + cqrs "github.com/go-ocf/cqrs" + cqrsEvent "github.com/go-ocf/cqrs/event" + "github.com/go-ocf/cqrs/eventstore/maintenance" + "github.com/go-ocf/kit/log" + "github.com/go-ocf/kit/strings" + cqrsUtils "github.com/go-ocf/cloud/resource-aggregate/cqrs" + raEvents "github.com/go-ocf/cloud/resource-aggregate/cqrs/events" + "github.com/go-ocf/cloud/resource-aggregate/pb" + "google.golang.org/grpc/codes" +) + +type LogPublishErrFunc func(err error) +type aggregate struct { + model *raEvents.ResourceStateSnapshotTaken + ag *cqrs.Aggregate + resourceId string + userDeviceIds strings.Set + eventstore EventStore +} + +func (a *aggregate) factoryModel(context.Context) (cqrs.AggregateModel, error) { + a.model = raEvents.NewResourceStateSnapshotTaken(func(deviceId, resourceId string) error { + if a.userDeviceIds.HasOneOf(deviceId) { + return nil + } + return fmt.Errorf("access denied") + }) + return a.model, nil +} + +// NewAggregate creates new resource aggreate - it must be created for every run command. +func NewAggregate(ctx context.Context, resourceId string, userDeviceIds []string, SnapshotThreshold int, eventstore EventStore, retry cqrs.RetryFunc) (*aggregate, error) { + deviceIds := make(strings.Set) + deviceIds.Add(userDeviceIds...) + a := &aggregate{ + resourceId: resourceId, + userDeviceIds: deviceIds, + eventstore: eventstore, + } + cqrsAg, err := cqrs.NewAggregate(resourceId, retry, SnapshotThreshold, eventstore, a.factoryModel, log.Debugf) + if err != nil { + return nil, fmt.Errorf("cannot create aggregate for resource: %w", err) + } + a.ag = cqrsAg + return a, nil +} + +func validatePublish(request *pb.PublishResourceRequest) error { + if request.Resource == nil { + return status.Errorf(codes.InvalidArgument, "invalid Resource") + } + if request.ResourceId == "" { + return status.Errorf(codes.InvalidArgument, "invalid ResourceId") + } + if request.Resource.Id != request.ResourceId { + return status.Errorf(codes.InvalidArgument, "invalid Resource.Id") + } + if request.Resource.DeviceId == "" { + return status.Errorf(codes.InvalidArgument, "invalid Resource.DeviceId") + } + return nil +} + +func validateUnpublish(request *pb.UnpublishResourceRequest) error { + if request.ResourceId == "" { + return status.Errorf(codes.InvalidArgument, "invalid ResourceId") + } + return nil +} + +func validateNotifyContentChanged(request *pb.NotifyResourceChangedRequest) error { + if request.Content == nil { + return status.Errorf(codes.InvalidArgument, "invalid Content") + } + if request.ResourceId == "" { + return status.Errorf(codes.InvalidArgument, "invalid ResourceId") + } + return nil +} + +func validateUpdateResourceContent(request *pb.UpdateResourceRequest) error { + if request.Content == nil { + return status.Errorf(codes.InvalidArgument, "invalid Content") + } + if request.ResourceId == "" { + return status.Errorf(codes.InvalidArgument, "invalid ResourceId") + } + if request.CorrelationId == "" { + return status.Errorf(codes.InvalidArgument, "invalid CorrelationId") + } + return nil +} + +func validateRetrieveResource(request *pb.RetrieveResourceRequest) error { + if request.ResourceId == "" { + return status.Errorf(codes.InvalidArgument, "invalid ResourceId") + } + if request.CorrelationId == "" { + return status.Errorf(codes.InvalidArgument, "invalid CorrelationId") + } + return nil +} + +func validateConfirmResourceUpdate(request *pb.ConfirmResourceUpdateRequest) error { + if request.Content == nil { + return status.Errorf(codes.InvalidArgument, "invalid Content") + } + if request.ResourceId == "" { + return status.Errorf(codes.InvalidArgument, "invalid ResourceId") + } + if request.CorrelationId == "" { + return status.Errorf(codes.InvalidArgument, "invalid CorrelationId") + } + + return nil +} + +func validateConfirmResourceRetrieve(request *pb.ConfirmResourceRetrieveRequest) error { + if request.Content == nil { + return status.Errorf(codes.InvalidArgument, "invalid Content") + } + if request.ResourceId == "" { + return status.Errorf(codes.InvalidArgument, "invalid ResourceId") + } + if request.CorrelationId == "" { + return status.Errorf(codes.InvalidArgument, "invalid CorrelationId") + } + + return nil +} + +func insertMaintenanceDbRecord(ctx context.Context, aggregate *aggregate, events []cqrsEvent.Event) { + for _, event := range events { + if ru, ok := event.(*raEvents.ResourceStateSnapshotTaken); ok { + if err := aggregate.eventstore.Insert(ctx, maintenance.Task{AggregateID: ru.AggregateId(), Version: ru.Version()}); err != nil { + log.Info("unable to insert the snapshot information into the maintenance db") + } + break + } + } +} + +func (a *aggregate) DeviceID() string { + return a.model.GroupId() +} + +// HandlePublishResource handles a command PublishResource +func (a *aggregate) PublishResource(ctx context.Context, request *pb.PublishResourceRequest) (response *pb.PublishResourceResponse, events []cqrsEvent.Event, err error) { + if err = validatePublish(request); err != nil { + err = fmt.Errorf("invalid publish command: %w", err) + return + } + + instanceID, err := a.eventstore.GetInstanceId(ctx, request.ResourceId) + if err != nil { + err = status.Errorf(codes.InvalidArgument, "unable to get instanceID for publish command: %v", err) + return + } + request.Resource.InstanceId = instanceID + + events, err = a.ag.HandleCommand(ctx, request) + if err != nil { + a.eventstore.RemoveInstanceId(ctx, instanceID) + err = fmt.Errorf("unable to process publish command: %w", err) + return + } + for _, event := range events { + if rp, ok := event.(raEvents.ResourcePublished); ok { + // if resource is already published we need to use origin intanceId that was set by model. + if rp.Resource.InstanceId != instanceID { + a.eventstore.RemoveInstanceId(ctx, instanceID) + instanceID = rp.Resource.InstanceId + } + break + } + } + insertMaintenanceDbRecord(ctx, a, events) + + auditContext := cqrsUtils.MakeAuditContext(request.GetAuthorizationContext(), "") + response = &pb.PublishResourceResponse{ + AuditContext: &auditContext, + InstanceId: instanceID, + } + return +} + +// HandleUnpublishResource handles a command UnpublishResource +func (a *aggregate) UnpublishResource(ctx context.Context, request *pb.UnpublishResourceRequest) (response *pb.UnpublishResourceResponse, events []cqrsEvent.Event, err error) { + if err = validateUnpublish(request); err != nil { + err = fmt.Errorf("invalid unpublish command: %w", err) + return + } + + events, err = a.ag.HandleCommand(ctx, request) + if err != nil { + err = fmt.Errorf("unable to process unpublish command: %w", err) + return + } + insertMaintenanceDbRecord(ctx, a, events) + + err = a.eventstore.RemoveInstanceId(ctx, a.model.Resource.InstanceId) + if err != nil { + err = status.Errorf(codes.Internal, "unable remove instanceID: %v", err) + } + + auditContext := cqrsUtils.MakeAuditContext(request.GetAuthorizationContext(), "") + response = &pb.UnpublishResourceResponse{ + AuditContext: &auditContext, + } + return +} + +// NotifyContentChanged handles a command NotifyContentChanged +func (a *aggregate) NotifyResourceChanged(ctx context.Context, request *pb.NotifyResourceChangedRequest) (response *pb.NotifyResourceChangedResponse, events []cqrsEvent.Event, err error) { + if err = validateNotifyContentChanged(request); err != nil { + err = fmt.Errorf("invalid notify content changed command: %w", err) + return + } + + events, err = a.ag.HandleCommand(ctx, request) + if err != nil { + err = fmt.Errorf("unable to process notify content changed command: %w", err) + return + } + insertMaintenanceDbRecord(ctx, a, events) + + auditContext := cqrsUtils.MakeAuditContext(request.GetAuthorizationContext(), "") + response = &pb.NotifyResourceChangedResponse{ + AuditContext: &auditContext, + } + return +} + +// HandleUpdateResourceContent handles a command UpdateResource +func (a *aggregate) UpdateResource(ctx context.Context, request *pb.UpdateResourceRequest) (response *pb.UpdateResourceResponse, events []cqrsEvent.Event, err error) { + if err = validateUpdateResourceContent(request); err != nil { + err = fmt.Errorf("invalid update resource content command: %w", err) + return + } + + events, err = a.ag.HandleCommand(ctx, request) + if err != nil { + err = fmt.Errorf("unable to process update resource content command: %w", err) + return + } + insertMaintenanceDbRecord(ctx, a, events) + + auditContext := cqrsUtils.MakeAuditContext(request.GetAuthorizationContext(), request.CorrelationId) + response = &pb.UpdateResourceResponse{ + AuditContext: &auditContext, + } + return +} + +func (a *aggregate) ConfirmResourceUpdate(ctx context.Context, request *pb.ConfirmResourceUpdateRequest) (response *pb.ConfirmResourceUpdateResponse, events []cqrsEvent.Event, err error) { + if err = validateConfirmResourceUpdate(request); err != nil { + err = fmt.Errorf("invalid update resource content notification command: %w", err) + return + } + + events, err = a.ag.HandleCommand(ctx, request) + if err != nil { + err = fmt.Errorf("unable to process update resource content notification command: %w", err) + return + } + insertMaintenanceDbRecord(ctx, a, events) + + auditContext := cqrsUtils.MakeAuditContext(request.GetAuthorizationContext(), request.CorrelationId) + response = &pb.ConfirmResourceUpdateResponse{ + AuditContext: &auditContext, + } + return +} + +// RetrieveResource handles a command UpdateResource +func (a *aggregate) RetrieveResource(ctx context.Context, request *pb.RetrieveResourceRequest) (response *pb.RetrieveResourceResponse, events []cqrsEvent.Event, err error) { + if err = validateRetrieveResource(request); err != nil { + err = fmt.Errorf("invalid retrieve resource content command: %w", err) + return + } + + events, err = a.ag.HandleCommand(ctx, request) + if err != nil { + err = fmt.Errorf("unable to process retrieve resource content command: %w", err) + return + } + insertMaintenanceDbRecord(ctx, a, events) + + auditContext := cqrsUtils.MakeAuditContext(request.GetAuthorizationContext(), request.CorrelationId) + response = &pb.RetrieveResourceResponse{ + AuditContext: &auditContext, + } + return +} + +func (a *aggregate) ConfirmResourceRetrieve(ctx context.Context, request *pb.ConfirmResourceRetrieveRequest) (response *pb.ConfirmResourceRetrieveResponse, events []cqrsEvent.Event, err error) { + if err = validateConfirmResourceRetrieve(request); err != nil { + err = fmt.Errorf("invalid retrieve resource content notification command: %w", err) + return + } + + events, err = a.ag.HandleCommand(ctx, request) + if err != nil { + err = fmt.Errorf("unable to process retrieve resource content notification command: %w", err) + return + } + insertMaintenanceDbRecord(ctx, a, events) + + auditContext := cqrsUtils.MakeAuditContext(request.GetAuthorizationContext(), request.CorrelationId) + response = &pb.ConfirmResourceRetrieveResponse{ + AuditContext: &auditContext, + } + return +} diff --git a/resource-aggregate/service/aggregate_test.go b/resource-aggregate/service/aggregate_test.go new file mode 100644 index 000000000..c52274f74 --- /dev/null +++ b/resource-aggregate/service/aggregate_test.go @@ -0,0 +1,837 @@ +package service + +import ( + "context" + "fmt" + "testing" + + "github.com/go-ocf/go-coap" + "github.com/go-ocf/kit/security/certManager" + + cqrs "github.com/go-ocf/cqrs" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventbus/nats" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/mongodb" + "github.com/go-ocf/cloud/resource-aggregate/pb" + "github.com/gofrs/uuid" + "github.com/kelseyhightower/envconfig" + "github.com/panjf2000/ants" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +var testUnauthorizedUser = "testUnauthorizedUser" + +func TestAggregateHandle_PublishResource(t *testing.T) { + type args struct { + request *pb.PublishResourceRequest + } + test := []struct { + name string + args args + want codes.Code + wantErr bool + }{ + { + name: "valid", + args: args{ + request: testMakePublishResourceRequest("dev0", "res0", "user0"), + }, + want: codes.OK, + wantErr: false, + }, + { + name: "duplicit", + args: args{ + request: testMakePublishResourceRequest("dev0", "res0", "user0"), + }, + want: codes.OK, + wantErr: false, + }, + } + + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + assert.NoError(t, err) + publisher, err := nats.NewPublisher(natsCfg, nats.WithTLS(&tlsConfig)) + assert.NoError(t, err) + var mgoCfg mongodb.Config + err = envconfig.Process("", &mgoCfg) + assert.NoError(t, err) + eventstore, err := mongodb.NewEventStore(mgoCfg, nil, mongodb.WithTLS(&tlsConfig)) + assert.NoError(t, err) + defer func() { + err := eventstore.Clear(ctx) + assert.NoError(t, err) + }() + + assert.NoError(t, err) + for _, tt := range test { + tfunc := func(t *testing.T) { + deviceIds, _, err := testListDevicesOfUserFunc(ctx, "a0", tt.args.request.AuthorizationContext.UserId) + ag, err := NewAggregate(ctx, tt.args.request.ResourceId, deviceIds, 10, eventstore, cqrs.NewDefaultRetryFunc(1)) + assert.NoError(t, err) + _, events, err := ag.PublishResource(ctx, tt.args.request) + if tt.wantErr { + require.Error(t, err) + s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) + require.True(t, ok) + assert.Equal(t, tt.want, s.Code()) + } else { + require.NoError(t, err) + err = publishEvents(ctx, publisher, tt.args.request.AuthorizationContext.DeviceId, tt.args.request.ResourceId, events) + assert.NoError(t, err) + } + } + t.Run(tt.name, tfunc) + } +} + +func testHandlePublishResource(t *testing.T, ctx context.Context, publisher *nats.Publisher, eventstore *mongodb.EventStore, deviceId, resourceId, userId string, expStatusCode codes.Code, hasErr bool) { + pc := testMakePublishResourceRequest(deviceId, resourceId, userId) + + deviceIds, _, err := testListDevicesOfUserFunc(ctx, "a0", userId) + assert.NoError(t, err) + + ag, err := NewAggregate(ctx, resourceId, deviceIds, 10, eventstore, cqrs.NewDefaultRetryFunc(1)) + assert.NoError(t, err) + resp, events, err := ag.PublishResource(ctx, pc) + if hasErr { + require.Error(t, err) + s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) + require.True(t, ok) + assert.Equal(t, expStatusCode, s.Code()) + } else { + require.NoError(t, err) + if err == nil && !assert.NotNil(t, resp.AuditContext) { + assert.Equal(t, userId, resp.AuditContext.UserId) + assert.Equal(t, deviceId, resp.AuditContext.DeviceId) + } + err = publishEvents(ctx, publisher, deviceId, resourceId, events) + assert.NoError(t, err) + } +} + +func TestAggregateDuplicitPublishResource(t *testing.T) { + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "token") + + var mgoCfg mongodb.Config + err = envconfig.Process("", &mgoCfg) + assert.NoError(t, err) + + pool, err := ants.NewPool(16) + assert.NoError(t, err) + defer pool.Release() + + eventstore, err := mongodb.NewEventStore(mgoCfg, pool.Submit, mongodb.WithTLS(&tlsConfig)) + + deviceId := "dupDeviceId" + resourceId := "dupResourceId" + userId := "dupResourceId" + + ag, err := NewAggregate(ctx, resourceId, []string{deviceId}, 10, eventstore, cqrs.NewDefaultRetryFunc(1)) + assert.NoError(t, err) + pc1 := testMakePublishResourceRequest(deviceId, resourceId, userId) + + resp1, events, err := ag.PublishResource(ctx, pc1) + assert.NoError(t, err) + assert.Equal(t, 1, len(events)) + + ag2, err := NewAggregate(ctx, resourceId, []string{deviceId}, 10, eventstore, cqrs.NewDefaultRetryFunc(1)) + assert.NoError(t, err) + pc2 := testMakePublishResourceRequest(deviceId, resourceId, userId) + resp2, events, err := ag2.PublishResource(ctx, pc2) + assert.NoError(t, err) + assert.Equal(t, 1, len(events)) + + assert.Equal(t, resp1.InstanceId, resp2.InstanceId) +} + +func TestAggregateHandleUnpublishResource(t *testing.T) { + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + + var mgoCfg mongodb.Config + err = envconfig.Process("", &mgoCfg) + assert.NoError(t, err) + + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + assert.NoError(t, err) + publisher, err := nats.NewPublisher(natsCfg, nats.WithTLS(&tlsConfig)) + + assert.NoError(t, err) + pool, err := ants.NewPool(16) + assert.NoError(t, err) + defer pool.Release() + + eventstore, err := mongodb.NewEventStore(mgoCfg, pool.Submit, mongodb.WithTLS(&tlsConfig)) + assert.NoError(t, err) + defer func() { + err := eventstore.Clear(ctx) + assert.NoError(t, err) + }() + + deviceId := "dev0" + resourceId := "res0" + userId := "user0" + + testHandlePublishResource(t, ctx, publisher, eventstore, deviceId, resourceId, userId, codes.OK, false) + + pc := testMakeUnpublishResourceRequest(deviceId, resourceId, userId) + + ag, err := NewAggregate(ctx, resourceId, []string{deviceId}, 10, eventstore, cqrs.NewDefaultRetryFunc(1)) + assert.NoError(t, err) + resp, events, err := ag.UnpublishResource(ctx, pc) + assert.NoError(t, err) + assert.Equal(t, userId, resp.AuditContext.UserId) + assert.Equal(t, deviceId, resp.AuditContext.DeviceId) + + err = publishEvents(ctx, publisher, deviceId, resourceId, events) + assert.NoError(t, err) + + resp, events, err = ag.UnpublishResource(ctx, pc) + require.Error(t, err) + s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) + require.True(t, ok) + assert.Equal(t, codes.FailedPrecondition, s.Code()) + assert.Empty(t, events) +} + +func testGetResourceId(deviceId, href string) string { + return uuid.NewV5(uuid.NamespaceURL, deviceId+href).String() +} + +func testMakePublishResourceRequest(deviceId, resourceId, userId string) *pb.PublishResourceRequest { + href := "/oic/p" + r := pb.PublishResourceRequest{ + ResourceId: resourceId, + Resource: testNewResource(href, deviceId, resourceId), + AuthorizationContext: testNewAuthorizationContext(deviceId, userId), + TimeToLive: 1, + CommandMetadata: &pb.CommandMetadata{ + ConnectionId: uuid.Must(uuid.NewV4()).String(), + Sequence: 0, + }, + } + return &r +} + +func testMakeUnpublishResourceRequest(deviceId, resourceId string, userId string) *pb.UnpublishResourceRequest { + r := pb.UnpublishResourceRequest{ + ResourceId: resourceId, + AuthorizationContext: testNewAuthorizationContext(deviceId, userId), + CommandMetadata: &pb.CommandMetadata{ + ConnectionId: uuid.Must(uuid.NewV4()).String(), + Sequence: 0, + }, + } + return &r +} + +func testMakeNotifyResourceChangedRequest(deviceId, resourceId string, userId string, seqNum uint64) *pb.NotifyResourceChangedRequest { + + r := pb.NotifyResourceChangedRequest{ + ResourceId: resourceId, + AuthorizationContext: testNewAuthorizationContext(deviceId, userId), + Content: &pb.Content{ + Data: []byte("hello world"), + }, + CommandMetadata: &pb.CommandMetadata{ + ConnectionId: "test", + Sequence: seqNum, + }, + } + return &r +} + +func testMakeUpdateResourceRequest(deviceId, resourceId, resourceInterface, userId, correlationId string) *pb.UpdateResourceRequest { + r := pb.UpdateResourceRequest{ + ResourceId: resourceId, + ResourceInterface: resourceInterface, + CorrelationId: correlationId, + AuthorizationContext: testNewAuthorizationContext(deviceId, userId), + Content: &pb.Content{ + Data: []byte("hello world"), + }, + CommandMetadata: &pb.CommandMetadata{ + ConnectionId: uuid.Must(uuid.NewV4()).String(), + Sequence: 0, + }, + } + return &r +} + +func testMakeRetrieveResourceRequest(deviceId, resourceId string, userId string, correlationId string) *pb.RetrieveResourceRequest { + r := pb.RetrieveResourceRequest{ + ResourceId: resourceId, + CorrelationId: correlationId, + AuthorizationContext: testNewAuthorizationContext(deviceId, userId), + CommandMetadata: &pb.CommandMetadata{ + ConnectionId: uuid.Must(uuid.NewV4()).String(), + Sequence: 0, + }, + } + return &r +} + +func testMakeConfirmResourceUpdateRequest(deviceId, resourceId, userId, correlationId string) *pb.ConfirmResourceUpdateRequest { + r := pb.ConfirmResourceUpdateRequest{ + ResourceId: resourceId, + CorrelationId: correlationId, + AuthorizationContext: testNewAuthorizationContext(deviceId, userId), + Content: &pb.Content{ + Data: []byte("hello world"), + }, + Status: pb.Status_OK, + CommandMetadata: &pb.CommandMetadata{ + ConnectionId: uuid.Must(uuid.NewV4()).String(), + Sequence: 0, + }, + } + return &r +} + +func testMakeConfirmResourceRetrieveRequest(deviceId, resourceId, userId, correlationId string) *pb.ConfirmResourceRetrieveRequest { + r := pb.ConfirmResourceRetrieveRequest{ + ResourceId: resourceId, + CorrelationId: correlationId, + AuthorizationContext: testNewAuthorizationContext(deviceId, userId), + Content: &pb.Content{ + Data: []byte("hello world"), + }, + Status: pb.Status_OK, + CommandMetadata: &pb.CommandMetadata{ + ConnectionId: uuid.Must(uuid.NewV4()).String(), + Sequence: 0, + }, + } + return &r +} + +func testNewAuthorizationContext(deviceId string, userId string) *pb.AuthorizationContext { + ac := pb.AuthorizationContext{ + DeviceId: deviceId, + UserId: userId, + } + return &ac +} + +func testNewResource(href string, deviceId string, resourceId string) *pb.Resource { + return &pb.Resource{ + Id: resourceId, + Href: href, + ResourceTypes: []string{"oic.wk.d", "x.com.kistler.kiconnect.device"}, + Interfaces: []string{"oic.if.baseline"}, + DeviceId: deviceId, + InstanceId: 1, + Anchor: "ocf://" + deviceId + "/oic/p", + Policies: &pb.Policies{1}, + Title: "device", + SupportedContentTypes: []string{coap.TextPlain.String()}, + } +} + +func Test_aggregate_HandleNotifyContentChanged(t *testing.T) { + deviceId := "dev0" + resourceId := "res0" + userId := "user0" + + type args struct { + req *pb.NotifyResourceChangedRequest + } + tests := []struct { + name string + args args + wantResp bool + wantEvents bool + wantStatusCode codes.Code + wantErr bool + }{ + { + name: "invalid", + args: args{ + &pb.NotifyResourceChangedRequest{ + ResourceId: resourceId, + AuthorizationContext: testNewAuthorizationContext(deviceId, userId), + }, + }, + wantResp: false, + wantEvents: false, + wantStatusCode: codes.InvalidArgument, + wantErr: true, + }, + { + name: "valid", + args: args{ + testMakeNotifyResourceChangedRequest(deviceId, resourceId, userId, 3), + }, + wantResp: true, + wantEvents: true, + wantStatusCode: codes.OK, + wantErr: false, + }, + { + name: "valid - duplicit", + args: args{ + testMakeNotifyResourceChangedRequest(deviceId, resourceId, userId, 2), + }, + wantResp: true, + wantEvents: false, + wantStatusCode: codes.OK, + wantErr: false, + }, + { + name: "valid - new content", + args: args{ + testMakeNotifyResourceChangedRequest(deviceId, resourceId, userId, 5), + }, + wantResp: true, + wantEvents: true, + wantStatusCode: codes.OK, + wantErr: false, + }, + } + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + + var mgoCfg mongodb.Config + err = envconfig.Process("", &mgoCfg) + assert.NoError(t, err) + + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + assert.NoError(t, err) + publisher, err := nats.NewPublisher(natsCfg, nats.WithTLS(&tlsConfig)) + eventstore, err := mongodb.NewEventStore(mgoCfg, nil, mongodb.WithTLS(&tlsConfig)) + assert.NoError(t, err) + defer func() { + err := eventstore.Clear(ctx) + assert.NoError(t, err) + }() + + testHandlePublishResource(t, ctx, publisher, eventstore, deviceId, resourceId, userId, codes.OK, false) + + ag, err := NewAggregate(ctx, resourceId, []string{deviceId}, 10, eventstore, cqrs.NewDefaultRetryFunc(1)) + assert.NoError(t, err) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotResp, gotEvents, err := ag.NotifyResourceChanged(ctx, tt.args.req) + if tt.wantErr { + require.Error(t, err) + s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) + require.True(t, ok) + assert.Equal(t, tt.wantStatusCode, s.Code()) + return + } + if tt.wantResp { + assert.NotEmpty(t, gotResp) + } + if tt.wantEvents { + assert.NotEmpty(t, gotEvents) + } + }) + } +} + +func Test_aggregate_HandleUpdateResourceContent(t *testing.T) { + deviceId := "dev0" + resourceId := "res0" + userId := "user0" + + type args struct { + req *pb.UpdateResourceRequest + } + tests := []struct { + name string + args args + wantResp bool + wantEvents bool + wantStatusCode codes.Code + wantErr bool + }{ + { + name: "invalid", + args: args{ + &pb.UpdateResourceRequest{ + ResourceId: resourceId, + AuthorizationContext: testNewAuthorizationContext(deviceId, userId), + }, + }, + wantResp: false, + wantEvents: false, + wantStatusCode: codes.InvalidArgument, + wantErr: true, + }, + { + name: "valid", + args: args{ + testMakeUpdateResourceRequest(deviceId, resourceId, "", userId, "123"), + }, + wantResp: true, + wantEvents: true, + wantStatusCode: codes.OK, + wantErr: false, + }, + { + name: "valid with resource interface", + args: args{ + testMakeUpdateResourceRequest(deviceId, resourceId, "oic.if.baseline", userId, "123"), + }, + wantResp: true, + wantEvents: true, + wantStatusCode: codes.OK, + wantErr: false, + }, + } + + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + + var mgoCfg mongodb.Config + err = envconfig.Process("", &mgoCfg) + assert.NoError(t, err) + + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + assert.NoError(t, err) + publisher, err := nats.NewPublisher(natsCfg, nats.WithTLS(&tlsConfig)) + eventstore, err := mongodb.NewEventStore(mgoCfg, nil, mongodb.WithTLS(&tlsConfig)) + assert.NoError(t, err) + defer func() { + err := eventstore.Clear(ctx) + assert.NoError(t, err) + }() + + testHandlePublishResource(t, ctx, publisher, eventstore, deviceId, resourceId, userId, codes.OK, false) + + ag, err := NewAggregate(ctx, resourceId, []string{deviceId}, 10, eventstore, cqrs.NewDefaultRetryFunc(1)) + assert.NoError(t, err) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotResp, gotEvents, err := ag.UpdateResource(ctx, tt.args.req) + if tt.wantErr { + require.Error(t, err) + s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) + require.True(t, ok) + assert.Equal(t, tt.wantStatusCode, s.Code()) + return + } + if tt.wantResp { + assert.NotEmpty(t, gotResp) + } + if tt.wantEvents { + assert.NotEmpty(t, gotEvents) + } + }) + } +} + +func Test_aggregate_HandleConfirmResourceUpdate(t *testing.T) { + deviceId := "dev0" + resourceId := "res0" + userId := "user0" + + type args struct { + req *pb.ConfirmResourceUpdateRequest + } + tests := []struct { + name string + args args + wantResp bool + wantEvents bool + wantStatusCode codes.Code + wantErr bool + }{ + { + name: "invalid", + args: args{ + &pb.ConfirmResourceUpdateRequest{ + ResourceId: resourceId, + AuthorizationContext: testNewAuthorizationContext(deviceId, userId), + }, + }, + wantResp: false, + wantEvents: false, + wantStatusCode: codes.InvalidArgument, + wantErr: true, + }, + { + name: "valid", + args: args{ + testMakeConfirmResourceUpdateRequest(deviceId, resourceId, userId, "123"), + }, + wantResp: true, + wantEvents: true, + wantStatusCode: codes.OK, + wantErr: false, + }, + } + + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + + var mgoCfg mongodb.Config + err = envconfig.Process("", &mgoCfg) + assert.NoError(t, err) + + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + assert.NoError(t, err) + publisher, err := nats.NewPublisher(natsCfg, nats.WithTLS(&tlsConfig)) + assert.NoError(t, err) + eventstore, err := mongodb.NewEventStore(mgoCfg, nil, mongodb.WithTLS(&tlsConfig)) + assert.NoError(t, err) + defer func() { + err := eventstore.Clear(ctx) + assert.NoError(t, err) + }() + + testHandlePublishResource(t, ctx, publisher, eventstore, deviceId, resourceId, userId, codes.OK, false) + + ag, err := NewAggregate(ctx, resourceId, []string{deviceId}, 10, eventstore, cqrs.NewDefaultRetryFunc(1)) + assert.NoError(t, err) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotResp, gotEvents, err := ag.ConfirmResourceUpdate(ctx, tt.args.req) + if tt.wantErr { + require.Error(t, err) + s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) + require.True(t, ok) + assert.Equal(t, tt.wantStatusCode, s.Code()) + return + } + if tt.wantResp { + assert.NotEmpty(t, gotResp) + } + if tt.wantEvents { + assert.NotEmpty(t, gotEvents) + } + }) + } +} + +func Test_aggregate_HandleRetrieveResource(t *testing.T) { + deviceId := "dev0" + resourceId := "res0" + userId := "user0" + + type args struct { + req *pb.RetrieveResourceRequest + } + tests := []struct { + name string + args args + wantResp bool + wantEvents bool + wantStatusCode codes.Code + wantErr bool + }{ + { + name: "invalid", + args: args{ + &pb.RetrieveResourceRequest{ + ResourceId: resourceId, + AuthorizationContext: testNewAuthorizationContext(deviceId, userId), + }, + }, + wantResp: false, + wantEvents: false, + wantStatusCode: codes.InvalidArgument, + wantErr: true, + }, + { + name: "valid", + args: args{ + testMakeRetrieveResourceRequest(deviceId, resourceId, userId, "123"), + }, + wantResp: true, + wantEvents: true, + wantStatusCode: codes.OK, + wantErr: false, + }, + } + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + + var mgoCfg mongodb.Config + err = envconfig.Process("", &mgoCfg) + assert.NoError(t, err) + + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + assert.NoError(t, err) + publisher, err := nats.NewPublisher(natsCfg, nats.WithTLS(&tlsConfig)) + eventstore, err := mongodb.NewEventStore(mgoCfg, nil, mongodb.WithTLS(&tlsConfig)) + assert.NoError(t, err) + defer func() { + err := eventstore.Clear(ctx) + assert.NoError(t, err) + }() + + testHandlePublishResource(t, ctx, publisher, eventstore, deviceId, resourceId, userId, codes.OK, false) + + ag, err := NewAggregate(ctx, resourceId, []string{deviceId}, 10, eventstore, cqrs.NewDefaultRetryFunc(1)) + assert.NoError(t, err) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotResp, gotEvents, err := ag.RetrieveResource(ctx, tt.args.req) + if tt.wantErr { + require.Error(t, err) + s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) + require.True(t, ok) + assert.Equal(t, tt.wantStatusCode, s.Code()) + return + } + if tt.wantResp { + assert.NotEmpty(t, gotResp) + } + if tt.wantEvents { + assert.NotEmpty(t, gotEvents) + } + }) + } +} + +func Test_aggregate_HandleNotifyResourceContentResourceProcessed(t *testing.T) { + deviceId := "dev0" + resourceId := "res0" + userId := "user0" + + type args struct { + req *pb.ConfirmResourceRetrieveRequest + } + tests := []struct { + name string + args args + wantResp bool + wantEvents bool + wantStatusCode codes.Code + wantErr bool + }{ + { + name: "invalid", + args: args{ + &pb.ConfirmResourceRetrieveRequest{ + ResourceId: resourceId, + AuthorizationContext: testNewAuthorizationContext(deviceId, userId), + }, + }, + wantResp: false, + wantEvents: false, + wantStatusCode: codes.InvalidArgument, + wantErr: true, + }, + { + name: "valid", + args: args{ + testMakeConfirmResourceRetrieveRequest(deviceId, resourceId, userId, "123"), + }, + wantResp: true, + wantEvents: true, + wantStatusCode: codes.OK, + wantErr: false, + }, + } + + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + + var mgoCfg mongodb.Config + err = envconfig.Process("", &mgoCfg) + assert.NoError(t, err) + + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + assert.NoError(t, err) + publisher, err := nats.NewPublisher(natsCfg, nats.WithTLS(&tlsConfig)) + assert.NoError(t, err) + eventstore, err := mongodb.NewEventStore(mgoCfg, nil, mongodb.WithTLS(&tlsConfig)) + assert.NoError(t, err) + defer func() { + err := eventstore.Clear(ctx) + assert.NoError(t, err) + }() + + testHandlePublishResource(t, ctx, publisher, eventstore, deviceId, resourceId, userId, codes.OK, false) + + ag, err := NewAggregate(ctx, resourceId, []string{deviceId}, 10, eventstore, cqrs.NewDefaultRetryFunc(1)) + assert.NoError(t, err) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotResp, gotEvents, err := ag.ConfirmResourceRetrieve(ctx, tt.args.req) + if tt.wantErr { + require.Error(t, err) + s, ok := status.FromError(kitNetGrpc.ForwardFromError(codes.Unknown, err)) + require.True(t, ok) + assert.Equal(t, tt.wantStatusCode, s.Code()) + return + } + if tt.wantResp { + assert.NotEmpty(t, gotResp) + } + if tt.wantEvents { + assert.NotEmpty(t, gotEvents) + } + }) + } +} + +func testListDevicesOfUserFunc(ctx context.Context, correlationId, userId string) ([]string, codes.Code, error) { + if userId == testUnauthorizedUser { + return nil, codes.Unauthenticated, fmt.Errorf("unauthorized access") + } + deviceIds := []string{"dev0", "dupDeviceId"} + return deviceIds, codes.OK, nil +} diff --git a/resource-aggregate/service/config.go b/resource-aggregate/service/config.go new file mode 100644 index 000000000..f098a34a5 --- /dev/null +++ b/resource-aggregate/service/config.go @@ -0,0 +1,22 @@ +package service + +import ( + "encoding/json" + "fmt" + + "github.com/go-ocf/kit/net/grpc" +) + +//Config represent application configuration +type Config struct { + grpc.Config + AuthServerAddr string `envconfig:"AUTH_SERVER_ADDRESS" default:"127.0.0.1:9100"` + SnapshotThreshold int `envconfig:"SNAPSHOT_THRESHOLD" default:"128"` + ConcurrencyExceptionMaxRetry int `envconfig:"OCC_MAX_RETRY" default:"8"` +} + +//String return string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} diff --git a/resource-aggregate/service/grpcApi.go b/resource-aggregate/service/grpcApi.go new file mode 100644 index 000000000..0a167254d --- /dev/null +++ b/resource-aggregate/service/grpcApi.go @@ -0,0 +1,255 @@ +package service + +import ( + "context" + "fmt" + "io" + "time" + + "google.golang.org/grpc/codes" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + cqrs "github.com/go-ocf/cqrs" + cqrsEvent "github.com/go-ocf/cqrs/event" + cqrsEventBus "github.com/go-ocf/cqrs/eventbus" + "github.com/go-ocf/kit/log" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + cqrsUtils "github.com/go-ocf/cloud/resource-aggregate/cqrs" + "github.com/go-ocf/cloud/resource-aggregate/pb" + grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth" +) + +//RequestHandler for handling incoming request +type RequestHandler struct { + config Config + authClient pbAS.AuthorizationServiceClient + eventstore EventStore + publisher cqrsEventBus.Publisher +} + +//NewRequestHandler factory for new RequestHandler +func NewRequestHandler(config Config, eventstore EventStore, publisher cqrsEventBus.Publisher, authClient pbAS.AuthorizationServiceClient) *RequestHandler { + return &RequestHandler{ + config: config, + eventstore: eventstore, + publisher: publisher, + authClient: authClient, + } +} + +func publishEvents(ctx context.Context, publisher cqrsEventBus.Publisher, deviceId, resourceId string, events []cqrsEvent.Event) error { + t := time.Now() + defer func() { + log.Debugf("publishEvents takes %v", time.Since(t)) + }() + var errors []error + for _, event := range events { + err := publisher.Publish(ctx, cqrsUtils.GetTopics(deviceId), deviceId, resourceId, event) + if err != nil { + errors = append(errors, err) + } + } + if len(errors) > 0 { + return fmt.Errorf("cannot publish events: %v", errors) + } + return nil +} + +func logAndReturnError(err error) error { + log.Errorf("%v", err) + return err +} + +func (r RequestHandler) GetUsersDevices(ctx context.Context, authCtx *pb.AuthorizationContext) ([]string, error) { + userIdsFilter := []string(nil) + if authCtx.GetUserId() != "" { + userIdsFilter = []string{authCtx.GetUserId()} + } + token, err := grpc_auth.AuthFromMD(ctx, "bearer") + if err != nil { + return nil, fmt.Errorf("cannot get users devices: %w", err) + } + getUserDevicesClient, err := r.authClient.GetUserDevices(kitNetGrpc.CtxWithToken(ctx, token), &pbAS.GetUserDevicesRequest{ + UserIdsFilter: userIdsFilter, + }) + if err != nil { + return nil, fmt.Errorf("cannot get users devices: %w", err) + } + defer getUserDevicesClient.CloseSend() + userDevices := make([]string, 0, 32) + for { + userDevice, err := getUserDevicesClient.Recv() + if err == io.EOF { + break + } + if err != nil { + return nil, fmt.Errorf("cannot get users devices: %w", err) + } + if userDevice == nil { + continue + } + userDevices = append(userDevices, userDevice.DeviceId) + } + return userDevices, nil +} + +func (r RequestHandler) PublishResource(ctx context.Context, request *pb.PublishResourceRequest) (*pb.PublishResourceResponse, error) { + deviceIds, err := r.GetUsersDevices(ctx, request.GetAuthorizationContext()) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Unauthenticated, "cannot publish resource: %v", err)) + } + + aggregate, err := NewAggregate(ctx, request.ResourceId, deviceIds, r.config.SnapshotThreshold, r.eventstore, cqrs.NewDefaultRetryFunc(r.config.ConcurrencyExceptionMaxRetry)) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "cannot publish resource: %v", err)) + } + + response, events, err := aggregate.PublishResource(ctx, request) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, "cannot publish resource: %v", err)) + } + + err = publishEvents(ctx, r.publisher, aggregate.DeviceID(), request.ResourceId, events) + if err != nil { + log.Errorf("cannot publish events for publish command: %v", err) + } + return response, nil +} + +func (r RequestHandler) UnpublishResource(ctx context.Context, request *pb.UnpublishResourceRequest) (*pb.UnpublishResourceResponse, error) { + deviceIds, err := r.GetUsersDevices(ctx, request.GetAuthorizationContext()) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Unauthenticated, "cannot unpublish resource: %v", err)) + } + + aggregate, err := NewAggregate(ctx, request.ResourceId, deviceIds, r.config.SnapshotThreshold, r.eventstore, cqrs.NewDefaultRetryFunc(r.config.ConcurrencyExceptionMaxRetry)) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "cannot unpublish resource: %v", err)) + } + + response, events, err := aggregate.UnpublishResource(ctx, request) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, "cannot unpublish resource: %v", err)) + } + + err = publishEvents(ctx, r.publisher, aggregate.DeviceID(), request.ResourceId, events) + if err != nil { + log.Errorf("cannot publish events for unpublish command: %v", err) + } + return response, nil +} + +func (r RequestHandler) NotifyResourceChanged(ctx context.Context, request *pb.NotifyResourceChangedRequest) (*pb.NotifyResourceChangedResponse, error) { + deviceIds, err := r.GetUsersDevices(ctx, request.GetAuthorizationContext()) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Unauthenticated, "cannot notify resource content changed: %v", err)) + } + + aggregate, err := NewAggregate(ctx, request.ResourceId, deviceIds, r.config.SnapshotThreshold, r.eventstore, cqrs.NewDefaultRetryFunc(r.config.ConcurrencyExceptionMaxRetry)) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "cannot notify resource content changed: %v", err)) + } + + response, events, err := aggregate.NotifyResourceChanged(ctx, request) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, "cannot notify resource content changed: %v", err)) + } + + err = publishEvents(ctx, r.publisher, aggregate.DeviceID(), request.ResourceId, events) + if err != nil { + log.Errorf("cannot publish events for notify content changed command: %v", err) + } + return response, nil +} + +func (r RequestHandler) UpdateResource(ctx context.Context, request *pb.UpdateResourceRequest) (*pb.UpdateResourceResponse, error) { + deviceIds, err := r.GetUsersDevices(ctx, request.GetAuthorizationContext()) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Unauthenticated, "cannot update resource content: %v", err)) + } + + aggregate, err := NewAggregate(ctx, request.ResourceId, deviceIds, r.config.SnapshotThreshold, r.eventstore, cqrs.NewDefaultRetryFunc(r.config.ConcurrencyExceptionMaxRetry)) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "cannot update resource content: %v", err)) + } + + response, events, err := aggregate.UpdateResource(ctx, request) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, "cannot update resource content: %v", err)) + } + + err = publishEvents(ctx, r.publisher, aggregate.DeviceID(), request.ResourceId, events) + if err != nil { + log.Errorf("cannot publish events for update resource content command: %v", err) + } + return response, nil +} + +func (r RequestHandler) ConfirmResourceUpdate(ctx context.Context, request *pb.ConfirmResourceUpdateRequest) (*pb.ConfirmResourceUpdateResponse, error) { + deviceIds, err := r.GetUsersDevices(ctx, request.GetAuthorizationContext()) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Unauthenticated, "cannot notify resource content update processed: %v", err)) + } + + aggregate, err := NewAggregate(ctx, request.ResourceId, deviceIds, r.config.SnapshotThreshold, r.eventstore, cqrs.NewDefaultRetryFunc(r.config.ConcurrencyExceptionMaxRetry)) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "cannot notify resource content update processed: %v", err)) + } + + response, events, err := aggregate.ConfirmResourceUpdate(ctx, request) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, "cannot notify resource content update processed: %v", err)) + } + + err = publishEvents(ctx, r.publisher, aggregate.DeviceID(), request.ResourceId, events) + if err != nil { + log.Errorf("cannot publish events for notify resource content update processed command: %v", err) + } + return response, nil +} + +func (r RequestHandler) RetrieveResource(ctx context.Context, request *pb.RetrieveResourceRequest) (*pb.RetrieveResourceResponse, error) { + deviceIds, err := r.GetUsersDevices(ctx, request.GetAuthorizationContext()) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Unauthenticated, "cannot retrieve resource content: %v", err)) + } + + aggregate, err := NewAggregate(ctx, request.ResourceId, deviceIds, r.config.SnapshotThreshold, r.eventstore, cqrs.NewDefaultRetryFunc(r.config.ConcurrencyExceptionMaxRetry)) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "cannot retrieve resource content: %v", err)) + } + + response, events, err := aggregate.RetrieveResource(ctx, request) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, "cannot retrieve resource content: %v", err)) + } + + err = publishEvents(ctx, r.publisher, aggregate.DeviceID(), request.ResourceId, events) + if err != nil { + log.Errorf("cannot publish events for retrieve resource content command: %v", err) + } + return response, nil +} + +func (r RequestHandler) ConfirmResourceRetrieve(ctx context.Context, request *pb.ConfirmResourceRetrieveRequest) (*pb.ConfirmResourceRetrieveResponse, error) { + deviceIds, err := r.GetUsersDevices(ctx, request.GetAuthorizationContext()) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Unauthenticated, "cannot notify resource content retrieve processed: %v", err)) + } + + aggregate, err := NewAggregate(ctx, request.ResourceId, deviceIds, r.config.SnapshotThreshold, r.eventstore, cqrs.NewDefaultRetryFunc(r.config.ConcurrencyExceptionMaxRetry)) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.InvalidArgument, "cannot notify resource content retrieve processed: %v", err)) + } + + response, events, err := aggregate.ConfirmResourceRetrieve(ctx, request) + if err != nil { + return nil, logAndReturnError(kitNetGrpc.ForwardErrorf(codes.Internal, "cannot notify resource content retrieve processed: %v", err)) + } + + err = publishEvents(ctx, r.publisher, aggregate.DeviceID(), request.ResourceId, events) + if err != nil { + log.Errorf("cannot publish events for notify resource content retrieve processed command: %v", err) + } + return response, nil +} diff --git a/resource-aggregate/service/grpcApi_test.go b/resource-aggregate/service/grpcApi_test.go new file mode 100644 index 000000000..8c50ebaec --- /dev/null +++ b/resource-aggregate/service/grpcApi_test.go @@ -0,0 +1,659 @@ +package service + +import ( + "context" + "fmt" + "io" + "testing" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/kit/security/certManager" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventbus/nats" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/mongodb" + "github.com/go-ocf/cloud/resource-aggregate/pb" + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/status" +) + +func TestRequestHandler_PublishResource(t *testing.T) { + deviceId := "dev0" + resId := "res0" + user0 := "user0" + type args struct { + request *pb.PublishResourceRequest + } + test := []struct { + name string + args args + want *pb.PublishResourceResponse + wantError bool + }{ + { + name: "valid", + args: args{ + request: testMakePublishResourceRequest(deviceId, resId, user0), + }, + want: &pb.PublishResourceResponse{ + AuditContext: &pb.AuditContext{ + UserId: user0, + DeviceId: deviceId, + }, + }, + }, + { + name: "duplicit", + args: args{ + request: testMakePublishResourceRequest(deviceId, resId, user0), + }, + want: &pb.PublishResourceResponse{ + AuditContext: &pb.AuditContext{ + UserId: user0, + DeviceId: deviceId, + }, + }, + }, + { + args: args{ + request: &pb.PublishResourceRequest{}, + }, + name: "invalid", + wantError: true, + }, + } + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + + var mgoCfg mongodb.Config + err = envconfig.Process("", &mgoCfg) + assert.NoError(t, err) + var config Config + err = envconfig.Process("", &config) + assert.NoError(t, err) + + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + assert.NoError(t, err) + publisher, err := nats.NewPublisher(natsCfg, nats.WithTLS(&tlsConfig)) + assert.NoError(t, err) + eventstore, err := mongodb.NewEventStore(mgoCfg, nil, mongodb.WithTLS(&tlsConfig)) + assert.NoError(t, err) + defer func() { + err := eventstore.Clear(ctx) + assert.NoError(t, err) + }() + + requestHandler := NewRequestHandler(config, eventstore, publisher, &mockAuthorizationServiceClient{}) + + for _, tt := range test { + tfunc := func(t *testing.T) { + response, err := requestHandler.PublishResource(ctx, tt.args.request) + if tt.wantError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + if tt.want != nil { + assert.Equal(t, tt.want.AuditContext, response.AuditContext) + } + } + t.Run(tt.name, tfunc) + } +} + +func TestRequestHandler_UnpublishResource(t *testing.T) { + deviceId := "dev0" + resId := "res0" + user0 := "user0" + + type args struct { + request *pb.UnpublishResourceRequest + } + test := []struct { + name string + args args + want *pb.UnpublishResourceResponse + wantError bool + }{ + { + name: "valid", + args: args{request: testMakeUnpublishResourceRequest(deviceId, resId, user0)}, + want: &pb.UnpublishResourceResponse{ + AuditContext: &pb.AuditContext{ + UserId: user0, + DeviceId: deviceId, + }, + }, + }, + { + name: "duplicit", + args: args{request: testMakeUnpublishResourceRequest(deviceId, resId, user0)}, + wantError: true, + }, + { + name: "unauthorized", + args: args{request: testMakeUnpublishResourceRequest(deviceId, resId, testUnauthorizedUser)}, + wantError: true, + }, + { + name: "invalid", + args: args{ + request: &pb.UnpublishResourceRequest{}, + }, + wantError: true, + }, + } + + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + + var mgoCfg mongodb.Config + err = envconfig.Process("", &mgoCfg) + assert.NoError(t, err) + var config Config + err = envconfig.Process("", &config) + assert.NoError(t, err) + + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + assert.NoError(t, err) + publisher, err := nats.NewPublisher(natsCfg, nats.WithTLS(&tlsConfig)) + assert.NoError(t, err) + eventstore, err := mongodb.NewEventStore(mgoCfg, nil, mongodb.WithTLS(&tlsConfig)) + assert.NoError(t, err) + defer func() { + err := eventstore.Clear(ctx) + assert.NoError(t, err) + }() + + requestHandler := NewRequestHandler(config, eventstore, publisher, &mockAuthorizationServiceClient{}) + + pubReq := testMakePublishResourceRequest(deviceId, resId, user0) + _, err = requestHandler.PublishResource(ctx, pubReq) + assert.NoError(t, err) + + for _, tt := range test { + tfunc := func(t *testing.T) { + response, err := requestHandler.UnpublishResource(ctx, tt.args.request) + if tt.wantError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.want, response) + } + t.Run(tt.name, tfunc) + } +} + +func TestRequestHandler_NotifyResourceChanged(t *testing.T) { + deviceId := "dev0" + resId := "res0" + user0 := "user0" + + type args struct { + request *pb.NotifyResourceChangedRequest + } + test := []struct { + name string + args args + want *pb.NotifyResourceChangedResponse + wantError bool + }{ + { + name: "valid", + args: args{request: testMakeNotifyResourceChangedRequest(deviceId, resId, user0, 2)}, + want: &pb.NotifyResourceChangedResponse{ + AuditContext: &pb.AuditContext{ + UserId: user0, + DeviceId: deviceId, + }, + }, + }, + { + name: "invalid", + args: args{ + request: &pb.NotifyResourceChangedRequest{}, + }, + wantError: true, + }, + } + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + + var mgoCfg mongodb.Config + err = envconfig.Process("", &mgoCfg) + assert.NoError(t, err) + var config Config + err = envconfig.Process("", &config) + assert.NoError(t, err) + + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + assert.NoError(t, err) + publisher, err := nats.NewPublisher(natsCfg, nats.WithTLS(&tlsConfig)) + assert.NoError(t, err) + eventstore, err := mongodb.NewEventStore(mgoCfg, nil, mongodb.WithTLS(&tlsConfig)) + assert.NoError(t, err) + + defer func() { + err := eventstore.Clear(ctx) + assert.NoError(t, err) + }() + + requestHandler := NewRequestHandler(config, eventstore, publisher, &mockAuthorizationServiceClient{}) + + pubReq := testMakePublishResourceRequest(deviceId, resId, user0) + _, err = requestHandler.PublishResource(ctx, pubReq) + assert.NoError(t, err) + + for _, tt := range test { + tfunc := func(t *testing.T) { + response, err := requestHandler.NotifyResourceChanged(ctx, tt.args.request) + if tt.wantError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.want, response) + } + t.Run(tt.name, tfunc) + } +} + +func TestRequestHandler_UpdateResourceContent(t *testing.T) { + deviceId := "dev0" + resId := "res0" + user0 := "user0" + correlationId := "123" + + type args struct { + request *pb.UpdateResourceRequest + } + test := []struct { + name string + args args + want *pb.UpdateResourceResponse + wantError bool + }{ + { + name: "valid", + args: args{request: testMakeUpdateResourceRequest(deviceId, resId, "", user0, correlationId)}, + want: &pb.UpdateResourceResponse{ + AuditContext: &pb.AuditContext{ + UserId: user0, + DeviceId: deviceId, + CorrelationId: correlationId, + }, + }, + }, + { + name: "valid", + args: args{request: testMakeUpdateResourceRequest(deviceId, resId, "oic.if.baseline", user0, correlationId)}, + want: &pb.UpdateResourceResponse{ + AuditContext: &pb.AuditContext{ + UserId: user0, + DeviceId: deviceId, + CorrelationId: correlationId, + }, + }, + }, + { + name: "invalid", + args: args{ + request: &pb.UpdateResourceRequest{}, + }, + wantError: true, + }, + } + + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + + var mgoCfg mongodb.Config + err = envconfig.Process("", &mgoCfg) + assert.NoError(t, err) + var config Config + err = envconfig.Process("", &config) + assert.NoError(t, err) + + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + assert.NoError(t, err) + publisher, err := nats.NewPublisher(natsCfg, nats.WithTLS(&tlsConfig)) + assert.NoError(t, err) + eventstore, err := mongodb.NewEventStore(mgoCfg, nil, mongodb.WithTLS(&tlsConfig)) + assert.NoError(t, err) + defer func() { + err := eventstore.Clear(ctx) + assert.NoError(t, err) + }() + + requestHandler := NewRequestHandler(config, eventstore, publisher, &mockAuthorizationServiceClient{}) + + pubReq := testMakePublishResourceRequest(deviceId, resId, user0) + _, err = requestHandler.PublishResource(ctx, pubReq) + assert.NoError(t, err) + + for _, tt := range test { + tfunc := func(t *testing.T) { + response, err := requestHandler.UpdateResource(ctx, tt.args.request) + if tt.wantError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.want, response) + } + t.Run(tt.name, tfunc) + } +} + +func TestRequestHandler_ConfirmResourceUpdate(t *testing.T) { + deviceId := "dev0" + resId := "res0" + user0 := "user0" + correlationId := "123" + + type args struct { + request *pb.ConfirmResourceUpdateRequest + } + test := []struct { + name string + args args + want *pb.ConfirmResourceUpdateResponse + wantError bool + }{ + { + name: "valid", + args: args{request: testMakeConfirmResourceUpdateRequest(deviceId, resId, user0, correlationId)}, + want: &pb.ConfirmResourceUpdateResponse{ + AuditContext: &pb.AuditContext{ + UserId: user0, + DeviceId: deviceId, + CorrelationId: correlationId, + }, + }, + }, + { + name: "invalid", + args: args{ + request: &pb.ConfirmResourceUpdateRequest{}, + }, + wantError: true, + }, + } + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + + var mgoCfg mongodb.Config + err = envconfig.Process("", &mgoCfg) + assert.NoError(t, err) + var config Config + err = envconfig.Process("", &config) + assert.NoError(t, err) + + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + assert.NoError(t, err) + publisher, err := nats.NewPublisher(natsCfg, nats.WithTLS(&tlsConfig)) + assert.NoError(t, err) + eventstore, err := mongodb.NewEventStore(mgoCfg, nil, mongodb.WithTLS(&tlsConfig)) + assert.NoError(t, err) + defer func() { + err := eventstore.Clear(ctx) + assert.NoError(t, err) + }() + + requestHandler := NewRequestHandler(config, eventstore, publisher, &mockAuthorizationServiceClient{}) + + pubReq := testMakePublishResourceRequest(deviceId, resId, user0) + _, err = requestHandler.PublishResource(ctx, pubReq) + assert.NoError(t, err) + + for _, tt := range test { + tfunc := func(t *testing.T) { + response, err := requestHandler.ConfirmResourceUpdate(ctx, tt.args.request) + if tt.wantError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.want, response) + } + t.Run(tt.name, tfunc) + } +} + +func TestRequestHandler_RetrieveResource(t *testing.T) { + deviceId := "dev0" + resId := "res0" + user0 := "user0" + correlationId := "123" + + type args struct { + request *pb.RetrieveResourceRequest + } + test := []struct { + name string + args args + want *pb.RetrieveResourceResponse + wantError bool + }{ + { + name: "valid", + args: args{request: testMakeRetrieveResourceRequest(deviceId, resId, user0, correlationId)}, + want: &pb.RetrieveResourceResponse{ + AuditContext: &pb.AuditContext{ + UserId: user0, + DeviceId: deviceId, + CorrelationId: correlationId, + }, + }, + }, + { + name: "invalid", + args: args{ + request: &pb.RetrieveResourceRequest{}, + }, + wantError: true, + }, + } + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + + var mgoCfg mongodb.Config + err = envconfig.Process("", &mgoCfg) + assert.NoError(t, err) + var config Config + err = envconfig.Process("", &config) + assert.NoError(t, err) + + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + assert.NoError(t, err) + publisher, err := nats.NewPublisher(natsCfg, nats.WithTLS(&tlsConfig)) + assert.NoError(t, err) + eventstore, err := mongodb.NewEventStore(mgoCfg, nil, mongodb.WithTLS(&tlsConfig)) + assert.NoError(t, err) + defer func() { + err := eventstore.Clear(ctx) + assert.NoError(t, err) + }() + + requestHandler := NewRequestHandler(config, eventstore, publisher, &mockAuthorizationServiceClient{}) + + pubReq := testMakePublishResourceRequest(deviceId, resId, user0) + _, err = requestHandler.PublishResource(ctx, pubReq) + assert.NoError(t, err) + + for _, tt := range test { + tfunc := func(t *testing.T) { + response, err := requestHandler.RetrieveResource(ctx, tt.args.request) + if tt.wantError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.want, response) + } + t.Run(tt.name, tfunc) + } +} + +func TestRequestHandler_ConfirmResourceRetrieve(t *testing.T) { + deviceId := "dev0" + resId := "res0" + user0 := "user0" + correlationId := "123" + + type args struct { + request *pb.ConfirmResourceRetrieveRequest + } + test := []struct { + name string + args args + want *pb.ConfirmResourceRetrieveResponse + wantError bool + }{ + { + name: "valid", + args: args{request: testMakeConfirmResourceRetrieveRequest(deviceId, resId, user0, correlationId)}, + want: &pb.ConfirmResourceRetrieveResponse{ + AuditContext: &pb.AuditContext{ + UserId: user0, + DeviceId: deviceId, + CorrelationId: correlationId, + }, + }, + }, + { + name: "invalid", + args: args{ + request: &pb.ConfirmResourceRetrieveRequest{}, + }, + wantError: true, + }, + } + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + tlsConfig := dialCertManager.GetClientTLSConfig() + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + + var mgoCfg mongodb.Config + err = envconfig.Process("", &mgoCfg) + assert.NoError(t, err) + var config Config + err = envconfig.Process("", &config) + assert.NoError(t, err) + + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + assert.NoError(t, err) + publisher, err := nats.NewPublisher(natsCfg, nats.WithTLS(&tlsConfig)) + assert.NoError(t, err) + eventstore, err := mongodb.NewEventStore(mgoCfg, nil, mongodb.WithTLS(&tlsConfig)) + assert.NoError(t, err) + defer func() { + err := eventstore.Clear(ctx) + assert.NoError(t, err) + }() + + requestHandler := NewRequestHandler(config, eventstore, publisher, &mockAuthorizationServiceClient{}) + + pubReq := testMakePublishResourceRequest(deviceId, resId, user0) + _, err = requestHandler.PublishResource(ctx, pubReq) + assert.NoError(t, err) + + for _, tt := range test { + tfunc := func(t *testing.T) { + response, err := requestHandler.ConfirmResourceRetrieve(ctx, tt.args.request) + if tt.wantError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.want, response) + } + t.Run(tt.name, tfunc) + } +} + +type mockAuthorizationServiceClient struct { + pbAS.AuthorizationServiceClient +} + +type mockGetUserDevicesClientClient struct { + resourceLink []*pbAS.UserDevice + i int + grpc.ClientStream +} + +func (r *mockGetUserDevicesClientClient) Recv() (*pbAS.UserDevice, error) { + if r.i >= len(r.resourceLink) { + return nil, io.EOF + } + res := r.resourceLink[r.i] + r.i++ + return res, nil +} + +func (c *mockGetUserDevicesClientClient) CloseSend() error { + return nil +} + +func (c mockAuthorizationServiceClient) GetUserDevices(ctx context.Context, in *pbAS.GetUserDevicesRequest, opts ...grpc.CallOption) (pbAS.AuthorizationService_GetUserDevicesClient, error) { + if len(in.UserIdsFilter) == 0 { + return nil, fmt.Errorf("UserIdsFilter is empty") + } + + userID := in.UserIdsFilter[0] + + deviceIds, code, err := testListDevicesOfUserFunc(ctx, "0", userID) + if err != nil { + return nil, status.Errorf(code, "%v", err) + } + userDevices := make([]*pbAS.UserDevice, 0, 16) + for _, d := range deviceIds { + userDevices = append(userDevices, &pbAS.UserDevice{DeviceId: d, UserId: userID}) + } + return &mockGetUserDevicesClientClient{ + resourceLink: userDevices, + }, nil +} diff --git a/resource-aggregate/service/service.go b/resource-aggregate/service/service.go new file mode 100644 index 000000000..6caad2f5e --- /dev/null +++ b/resource-aggregate/service/service.go @@ -0,0 +1,103 @@ +package service + +import ( + "context" + "crypto/tls" + "os" + "os/signal" + "sync" + "syscall" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + cqrsEventBus "github.com/go-ocf/cqrs/eventbus" + cqrsEventStore "github.com/go-ocf/cqrs/eventstore" + cqrsMaintenance "github.com/go-ocf/cqrs/eventstore/maintenance" + "github.com/go-ocf/kit/log" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/cloud/resource-aggregate/pb" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +type EventStore interface { + cqrsEventStore.EventStore + cqrsMaintenance.EventStore + GetInstanceId(ctx context.Context, resourceId string) (int64, error) + RemoveInstanceId(ctx context.Context, instanceId int64) error +} + +//Server handle HTTP request +type Server struct { + server *kitNetGrpc.Server + cfg Config + handler *RequestHandler + sigs chan os.Signal +} + +type ClientCertManager = interface { + GetClientTLSConfig() tls.Config +} + +type ServerCertManager = interface { + GetServerTLSConfig() tls.Config +} + +// New creates new Server with provided store and publisher. +func New(config Config, clientCertManager ClientCertManager, serverCertManager ServerCertManager, eventStore EventStore, publisher cqrsEventBus.Publisher) *Server { + clientTLSConfig := clientCertManager.GetClientTLSConfig() + serverTLSConfig := serverCertManager.GetServerTLSConfig() + + authConn, err := grpc.Dial(config.AuthServerAddr, grpc.WithTransportCredentials(credentials.NewTLS(&clientTLSConfig))) + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + authClient := pbAS.NewAuthorizationServiceClient(authConn) + + grpcServer, err := kitNetGrpc.NewServer(config.Config.Addr, grpc.Creds(credentials.NewTLS(&serverTLSConfig))) + if err != nil { + log.Fatalf("cannot create server: %v", err) + } + + requestHandler := NewRequestHandler(config, eventStore, publisher, authClient) + pb.RegisterResourceAggregateServer(grpcServer.Server, requestHandler) + + server := Server{ + server: grpcServer, + cfg: config, + handler: requestHandler, + sigs: make(chan os.Signal, 1), + } + + return &server +} + +func (s *Server) serveWithHandlingSignal(serve func() error) error { + var wg sync.WaitGroup + var err error + wg.Add(1) + go func(s *Server) { + defer wg.Done() + err = serve() + }(s) + + signal.Notify(s.sigs, + syscall.SIGHUP, + syscall.SIGINT, + syscall.SIGTERM, + syscall.SIGQUIT) + <-s.sigs + + s.server.Stop() + wg.Wait() + return err +} + +// Serve serve starts the service's HTTP server and blocks. +func (s *Server) Serve() error { + return s.serveWithHandlingSignal(s.server.Serve) +} + +// Shutdown ends serving +func (s *Server) Shutdown() { + s.sigs <- syscall.SIGTERM +} diff --git a/resource-aggregate/service/service_test.go b/resource-aggregate/service/service_test.go new file mode 100644 index 000000000..12f8e97cd --- /dev/null +++ b/resource-aggregate/service/service_test.go @@ -0,0 +1,131 @@ +package service_test + +import ( + "context" + "strconv" + "testing" + + "github.com/go-ocf/kit/security/certManager" + + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + authProvider "github.com/go-ocf/cloud/authorization/provider" + authConfig "github.com/go-ocf/cloud/authorization/service" + authService "github.com/go-ocf/cloud/authorization/test/service" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/mongodb" + "github.com/go-ocf/cloud/resource-aggregate/pb" + "github.com/go-ocf/cloud/resource-aggregate/refImpl" + "github.com/gofrs/uuid" + "github.com/kelseyhightower/envconfig" +) + +func TestPublishUnpublish(t *testing.T) { + ctx := kitNetGrpc.CtxWithToken(context.Background(), "b") + var config refImpl.Config + err := envconfig.Process("", &config) + require.NoError(t, err) + config.Service.AuthServerAddr = "localhost:7000" + clientCertManager, err := certManager.NewCertManager(config.Dial) + require.NoError(t, err) + clientTLSConfig := clientCertManager.GetClientTLSConfig() + + eventstore, err := mongodb.NewEventStore(config.MongoDB, nil, mongodb.WithTLS(&clientTLSConfig)) + require.NoError(t, err) + defer eventstore.Clear(ctx) + + var authConfig authConfig.Config + envconfig.Process("", &authConfig) + authConfig.Addr = config.Service.AuthServerAddr + + port := 9888 + authServerShutdown := authService.NewAuthServer(t, authConfig) + defer authServerShutdown() + config.Service.Addr = "localhost:" + strconv.Itoa(port) + config.Service.SnapshotThreshold = 1 + + server, err := refImpl.Init(config) + require.NoError(t, err) + defer server.Shutdown() + go func() { + err := server.Serve() + require.NoError(t, err) + }() + + authConn, err := grpc.Dial(config.Service.AuthServerAddr, grpc.WithTransportCredentials(credentials.NewTLS(&clientTLSConfig))) + require.NoError(t, err) + authClient := pbAS.NewAuthorizationServiceClient(authConn) + + raConn, err := grpc.Dial(config.Service.Addr, grpc.WithTransportCredentials(credentials.NewTLS(&clientTLSConfig))) + require.NoError(t, err) + raClient := pb.NewResourceAggregateClient(raConn) + + deviceId := "dev0" + resId := "res0" + resp, err := authClient.SignUp(ctx, &pbAS.SignUpRequest{ + DeviceId: deviceId, + AuthorizationCode: "authcode", + AuthorizationProvider: authProvider.NewTestProvider().GetProviderName(), + }) + require.NoError(t, err) + + pubReq := testMakePublishResourceRequest(deviceId, resId, resp.UserId, resp.AccessToken) + _, err = raClient.PublishResource(ctx, pubReq) + require.NoError(t, err) + + unpubReq := testMakeUnpublishResourceRequest(deviceId, resId, resp.UserId, resp.AccessToken) + _, err = raClient.UnpublishResource(ctx, unpubReq) + require.NoError(t, err) +} + +func testMakePublishResourceRequest(deviceId, resourceId, userId, accesstoken string) *pb.PublishResourceRequest { + href := "/oic/p" + r := &pb.PublishResourceRequest{ + ResourceId: resourceId, + Resource: testNewResource(href, deviceId, resourceId), + AuthorizationContext: testNewAuthorizationContext(deviceId, userId, accesstoken), + TimeToLive: 1, + CommandMetadata: &pb.CommandMetadata{ + ConnectionId: uuid.Must(uuid.NewV4()).String(), + Sequence: 0, + }, + } + return r +} + +func testMakeUnpublishResourceRequest(deviceId, resourceId, userId, accesstoken string) *pb.UnpublishResourceRequest { + r := &pb.UnpublishResourceRequest{ + ResourceId: resourceId, + AuthorizationContext: testNewAuthorizationContext(deviceId, userId, accesstoken), + CommandMetadata: &pb.CommandMetadata{ + ConnectionId: uuid.Must(uuid.NewV4()).String(), + Sequence: 0, + }, + } + return r +} + +func testNewAuthorizationContext(deviceId, userId, accessToken string) *pb.AuthorizationContext { + ac := pb.AuthorizationContext{ + DeviceId: deviceId, + UserId: userId, + } + return &ac +} + +func testNewResource(href string, deviceId string, resourceId string) *pb.Resource { + return &pb.Resource{ + Id: resourceId, + Href: href, + ResourceTypes: []string{"oic.wk.d", "x.com.kistler.kiconnect.device"}, + Interfaces: []string{"oic.if.baseline"}, + DeviceId: deviceId, + InstanceId: 1, + Anchor: "ocf://" + deviceId + "/oic/p", + Policies: &pb.Policies{1}, + Title: "device", + } +} diff --git a/resource-aggregate/test/service/service.go b/resource-aggregate/test/service/service.go new file mode 100644 index 000000000..d3f237878 --- /dev/null +++ b/resource-aggregate/test/service/service.go @@ -0,0 +1,29 @@ +package service + +import ( + "sync" + "testing" + + "github.com/go-ocf/cloud/resource-aggregate/refImpl" + "github.com/stretchr/testify/require" +) + +func NewResourceAggregate(t *testing.T, cfg refImpl.Config) func() { + t.Log("NewResourceAggregate") + defer t.Log("NewResourceAggregate done") + s, err := refImpl.Init(cfg) + require.NoError(t, err) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + err := s.Serve() + require.NoError(t, err) + }() + + return func() { + s.Shutdown() + wg.Wait() + } +} diff --git a/resource-directory/.dockerignore b/resource-directory/.dockerignore new file mode 100644 index 000000000..f6f32bc9e --- /dev/null +++ b/resource-directory/.dockerignore @@ -0,0 +1,5 @@ +.git +.github +.gitignore +.travis.yml +Makefile diff --git a/resource-directory/Dockerfile b/resource-directory/Dockerfile new file mode 100644 index 000000000..bb331746a --- /dev/null +++ b/resource-directory/Dockerfile @@ -0,0 +1,11 @@ +FROM golang:1.13.5-alpine3.10 AS build +RUN apk add --no-cache curl git build-base +WORKDIR $GOPATH/src/github.com/go-ocf/cloud/resource-directory +COPY . . +RUN go mod download +RUN go build -o /go/bin/service ./cmd/service + +FROM alpine:3.11 as service +RUN apk add --no-cache ca-certificates +COPY --from=build /go/bin/service /usr/local/bin/service +ENTRYPOINT ["/usr/local/bin/service"] \ No newline at end of file diff --git a/resource-directory/LICENSE b/resource-directory/LICENSE new file mode 100644 index 000000000..5e301a795 --- /dev/null +++ b/resource-directory/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 go-ocf + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/resource-directory/Makefile b/resource-directory/Makefile new file mode 100644 index 000000000..6c1f69e5f --- /dev/null +++ b/resource-directory/Makefile @@ -0,0 +1,37 @@ +SHELL = /bin/bash +SERVICE_NAME = $(notdir $(CURDIR)) +LATEST_TAG = vnext +VERSION_TAG = vnext-$(shell git rev-parse --short=7 --verify HEAD) + +default: build + +define build-docker-image + docker build \ + --network=host \ + --tag ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) \ + --tag ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) \ + --target $(1) \ + --file Dockerfile \ + . +endef + +build-servicecontainer: + $(call build-docker-image,service) + +build: build-servicecontainer + + +push: build-servicecontainer + docker push ocfcloud/$(SERVICE_NAME):$(VERSION_TAG) + docker push ocfcloud/$(SERVICE_NAME):$(LATEST_TAG) + +proto/generate: + protoc -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf --gogofaster_out=${GOPATH}/src pb/device-directory/queries.proto + protoc -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf --gogofaster_out=${GOPATH}/src pb/resource-directory/queries.proto + protoc -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf --gogofaster_out=${GOPATH}/src pb/resource-shadow/queries.proto + protoc -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf --go_out=plugins=grpc:${GOPATH}/src pb/device-directory/service.proto + protoc -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf --go_out=plugins=grpc:${GOPATH}/src pb/resource-directory/service.proto + protoc -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf --go_out=plugins=grpc:${GOPATH}/src pb/resource-shadow/service.proto +.PHONY: proto/generate + +.PHONY: build-servicecontainer build push proto/generate diff --git a/resource-directory/README.md b/resource-directory/README.md new file mode 100644 index 000000000..3da00eaeb --- /dev/null +++ b/resource-directory/README.md @@ -0,0 +1,40 @@ +[![Go Report](https://goreportcard.com/badge/github.com/go-ocf/cloud/resource-directory)](https://goreportcard.com/report/github.com/go-ocf/cloud/resource-directory) +[![Gitter](https://badges.gitter.im/ocfcloud/Lobby.svg)](https://gitter.im/ocfcloud/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + +# resource-directory + +## Configuration +| Option | ENV variable | Type | Description | Default | +| ------ | --------- | ----------- | ------- | ------- | +| `-` | `ADDRESS` | string | tbd | `"0.0.0.0:5684"` | +| `-` | `FQDN` | string | tbd | `"coap-gw.ocf.cloud"` | +| `-` | `AUTH_SERVER_ADDRESS` | string | tbd | `"127.0.0.1:9100"` | +| `-` | `GOROUTINE_POOL_SIZE` | int | tbd | `16` | +| `-` | `CACHE_EXPIRATION` | string | tbd | `"30s"` | +| `-` | `NATS_URL` | string | tbd | `"nats://localhost:4222"` | +| `-` | `MONGODB_URI` | string | tbd | `"mongodb://localhost:27017"` | +| `-` | `MONGODB_DATABASE` | string | tbd | `"eventstore"` | +| `-` | `MONGODB_BATCH_SIZE` | int | tbd | `16` | +| `-` | `MONGODB_MAX_POOL_SIZE` | int | tbd | `16` | +| `-` | `MONGODB_MAX_CONN_IDLE_TIME` | string | tbd | `"240s"` | +| `-` | `DIAL_TYPE` | string | tbd | `"acme"` | +| `-` | `DIAL_ACME_CA_POOL` | string | tbd | `""` | +| `-` | `DIAL_ACME_DIRECTORY_URL` | string | tbd | `""` | +| `-` | `DIAL_ACME_DOMAINS` | string | tbd | `""` | +| `-` | `DIAL_ACME_REGISTRATION_EMAIL` | string | tbd | `""` | +| `-` | `DIAL_ACME_TICK_FREQUENCY` | string | tbd | `""` | +| `-` | `DIAL_FILE_CA_POOL` | string | tbd | `""` | +| `-` | `DIAL_FILE_CERT_KEY_NAME` | string | tbd | `""` | +| `-` | `DIAL_FILE_CERT_DIR_PATH` | string | tbd | `""` | +| `-` | `DIAL_FILE_CERT_NAME` | string | tbd | `""` | +| `-` | `LISTEN_TYPE` | string | tbd | `"acme"` | +| `-` | `LISTEN_ACME_CA_POOL` | string | tbd | `""` | +| `-` | `LISTEN_ACME_DIRECTORY_URL` | string | tbd | `""` | +| `-` | `LISTEN_ACME_DOMAINS` | string | tbd | `""` | +| `-` | `LISTEN_ACME_REGISTRATION_EMAIL` | string | tbd | `""` | +| `-` | `LISTEN_ACME_TICK_FREQUENCY` | string | tbd | `""` | +| `-` | `LISTEN_FILE_CA_POOL` | string | tbd | `""` | +| `-` | `LISTEN_FILE_CERT_KEY_NAME` | string | tbd | `""` | +| `-` | `LISTEN_FILE_CERT_DIR_PATH` | string | tbd | `""` | +| `-` | `LISTEN_FILE_CERT_NAME` | string | tbd | `""` | +| `-` | `LOG_ENABLE_DEBUG` | bool | tbd | `false` | diff --git a/resource-directory/cmd/service/main.go b/resource-directory/cmd/service/main.go new file mode 100644 index 000000000..a4802cf27 --- /dev/null +++ b/resource-directory/cmd/service/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "github.com/go-ocf/kit/log" + "github.com/go-ocf/cloud/resource-directory/refImpl" + "github.com/kelseyhightower/envconfig" +) + +func main() { + var config refImpl.Config + if err := envconfig.Process("", &config); err != nil { + log.Fatalf("cannot parse configuration: %v", err) + } + if server, err := refImpl.Init(config); err != nil { + log.Fatalf("cannot init server: %v", err) + } else { + if err = server.Serve(); err != nil { + log.Fatalf("unexpected ends: %v", err) + } + } +} diff --git a/resource-directory/pb/device-directory/queries.pb.go b/resource-directory/pb/device-directory/queries.pb.go new file mode 100644 index 000000000..6295020bb --- /dev/null +++ b/resource-directory/pb/device-directory/queries.pb.go @@ -0,0 +1,1433 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: pb/device-directory/queries.proto + +package device_directory + +import ( + fmt "fmt" + pb "github.com/go-ocf/cloud/resource-aggregate/pb" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Status int32 + +const ( + Status_ONLINE Status = 0 + Status_OFFLINE Status = 1 +) + +var Status_name = map[int32]string{ + 0: "ONLINE", + 1: "OFFLINE", +} + +var Status_value = map[string]int32{ + "ONLINE": 0, + "OFFLINE": 1, +} + +func (x Status) String() string { + return proto.EnumName(Status_name, int32(x)) +} + +func (Status) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_26681580767e0867, []int{0} +} + +type GetDevicesRequest struct { + AuthorizationContext *pb.AuthorizationContext `protobuf:"bytes,1,opt,name=authorization_context,json=authorizationContext,proto3" json:"authorization_context,omitempty"` + TypeFilter []string `protobuf:"bytes,2,rep,name=type_filter,json=typeFilter,proto3" json:"type_filter,omitempty"` + StatusFilter []Status `protobuf:"varint,3,rep,packed,name=status_filter,json=statusFilter,proto3,enum=ocf.cloud.device.directory.pb.Status" json:"status_filter,omitempty"` + DeviceIdsFilter []string `protobuf:"bytes,4,rep,name=device_ids_filter,json=deviceIdsFilter,proto3" json:"device_ids_filter,omitempty"` +} + +func (m *GetDevicesRequest) Reset() { *m = GetDevicesRequest{} } +func (m *GetDevicesRequest) String() string { return proto.CompactTextString(m) } +func (*GetDevicesRequest) ProtoMessage() {} +func (*GetDevicesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_26681580767e0867, []int{0} +} +func (m *GetDevicesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetDevicesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetDevicesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetDevicesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetDevicesRequest.Merge(m, src) +} +func (m *GetDevicesRequest) XXX_Size() int { + return m.Size() +} +func (m *GetDevicesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetDevicesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetDevicesRequest proto.InternalMessageInfo + +func (m *GetDevicesRequest) GetAuthorizationContext() *pb.AuthorizationContext { + if m != nil { + return m.AuthorizationContext + } + return nil +} + +func (m *GetDevicesRequest) GetTypeFilter() []string { + if m != nil { + return m.TypeFilter + } + return nil +} + +func (m *GetDevicesRequest) GetStatusFilter() []Status { + if m != nil { + return m.StatusFilter + } + return nil +} + +func (m *GetDevicesRequest) GetDeviceIdsFilter() []string { + if m != nil { + return m.DeviceIdsFilter + } + return nil +} + +type LocalizedString struct { + Language string `protobuf:"bytes,1,opt,name=language,proto3" json:"language"` + Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value"` +} + +func (m *LocalizedString) Reset() { *m = LocalizedString{} } +func (m *LocalizedString) String() string { return proto.CompactTextString(m) } +func (*LocalizedString) ProtoMessage() {} +func (*LocalizedString) Descriptor() ([]byte, []int) { + return fileDescriptor_26681580767e0867, []int{1} +} +func (m *LocalizedString) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *LocalizedString) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_LocalizedString.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *LocalizedString) XXX_Merge(src proto.Message) { + xxx_messageInfo_LocalizedString.Merge(m, src) +} +func (m *LocalizedString) XXX_Size() int { + return m.Size() +} +func (m *LocalizedString) XXX_DiscardUnknown() { + xxx_messageInfo_LocalizedString.DiscardUnknown(m) +} + +var xxx_messageInfo_LocalizedString proto.InternalMessageInfo + +func (m *LocalizedString) GetLanguage() string { + if m != nil { + return m.Language + } + return "" +} + +func (m *LocalizedString) GetValue() string { + if m != nil { + return m.Value + } + return "" +} + +type Resource struct { + ResourceTypes []string `protobuf:"bytes,1,rep,name=resource_types,json=resourceTypes,proto3" json:"rt"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"n"` + ManufacturerName []*LocalizedString `protobuf:"bytes,3,rep,name=manufacturer_name,json=manufacturerName,proto3" json:"dmn"` + ModelNumber string `protobuf:"bytes,4,opt,name=model_number,json=modelNumber,proto3" json:"dmno"` +} + +func (m *Resource) Reset() { *m = Resource{} } +func (m *Resource) String() string { return proto.CompactTextString(m) } +func (*Resource) ProtoMessage() {} +func (*Resource) Descriptor() ([]byte, []int) { + return fileDescriptor_26681580767e0867, []int{2} +} +func (m *Resource) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Resource) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Resource.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Resource) XXX_Merge(src proto.Message) { + xxx_messageInfo_Resource.Merge(m, src) +} +func (m *Resource) XXX_Size() int { + return m.Size() +} +func (m *Resource) XXX_DiscardUnknown() { + xxx_messageInfo_Resource.DiscardUnknown(m) +} + +var xxx_messageInfo_Resource proto.InternalMessageInfo + +func (m *Resource) GetResourceTypes() []string { + if m != nil { + return m.ResourceTypes + } + return nil +} + +func (m *Resource) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Resource) GetManufacturerName() []*LocalizedString { + if m != nil { + return m.ManufacturerName + } + return nil +} + +func (m *Resource) GetModelNumber() string { + if m != nil { + return m.ModelNumber + } + return "" +} + +type Device struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Resource *Resource `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"` + IsOnline bool `protobuf:"varint,3,opt,name=is_online,json=isOnline,proto3" json:"is_online,omitempty"` +} + +func (m *Device) Reset() { *m = Device{} } +func (m *Device) String() string { return proto.CompactTextString(m) } +func (*Device) ProtoMessage() {} +func (*Device) Descriptor() ([]byte, []int) { + return fileDescriptor_26681580767e0867, []int{3} +} +func (m *Device) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Device) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Device.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Device) XXX_Merge(src proto.Message) { + xxx_messageInfo_Device.Merge(m, src) +} +func (m *Device) XXX_Size() int { + return m.Size() +} +func (m *Device) XXX_DiscardUnknown() { + xxx_messageInfo_Device.DiscardUnknown(m) +} + +var xxx_messageInfo_Device proto.InternalMessageInfo + +func (m *Device) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *Device) GetResource() *Resource { + if m != nil { + return m.Resource + } + return nil +} + +func (m *Device) GetIsOnline() bool { + if m != nil { + return m.IsOnline + } + return false +} + +func init() { + proto.RegisterEnum("ocf.cloud.device.directory.pb.Status", Status_name, Status_value) + proto.RegisterType((*GetDevicesRequest)(nil), "ocf.cloud.device.directory.pb.GetDevicesRequest") + proto.RegisterType((*LocalizedString)(nil), "ocf.cloud.device.directory.pb.LocalizedString") + proto.RegisterType((*Resource)(nil), "ocf.cloud.device.directory.pb.Resource") + proto.RegisterType((*Device)(nil), "ocf.cloud.device.directory.pb.Device") +} + +func init() { proto.RegisterFile("pb/device-directory/queries.proto", fileDescriptor_26681580767e0867) } + +var fileDescriptor_26681580767e0867 = []byte{ + // 601 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0x4f, 0x6b, 0xdb, 0x3e, + 0x18, 0x8e, 0x93, 0x36, 0x4d, 0x94, 0xfe, 0x15, 0xbf, 0x1f, 0x64, 0x1d, 0x8b, 0xdb, 0xc0, 0x58, + 0xe8, 0x88, 0x0d, 0xd9, 0x6e, 0xbb, 0x6c, 0xe9, 0xd6, 0xd1, 0x51, 0x52, 0x50, 0x77, 0x1a, 0x03, + 0x23, 0x4b, 0x8a, 0x2b, 0xb0, 0xa5, 0x54, 0x96, 0xca, 0xda, 0x4f, 0xb1, 0xdb, 0xbe, 0xd2, 0x8e, + 0x3d, 0xee, 0x14, 0x46, 0x73, 0x18, 0xe4, 0x53, 0x0c, 0xcb, 0xb1, 0xdb, 0x95, 0xb2, 0xee, 0x64, + 0xbf, 0xcf, 0xfb, 0xbc, 0x7f, 0x9e, 0x07, 0xbd, 0x60, 0x77, 0x12, 0xfa, 0x94, 0x9d, 0x73, 0xc2, + 0xfa, 0x94, 0x2b, 0x46, 0xb4, 0x54, 0x17, 0xfe, 0x99, 0x61, 0x8a, 0xb3, 0xd4, 0x9b, 0x28, 0xa9, + 0x25, 0x7c, 0x22, 0xc9, 0xd8, 0x23, 0xb1, 0x34, 0xd4, 0xcb, 0x99, 0x5e, 0xc9, 0xf4, 0x26, 0xe1, + 0xf6, 0xeb, 0x88, 0xeb, 0x53, 0x13, 0x7a, 0x44, 0x26, 0x7e, 0x24, 0xfb, 0x92, 0x8c, 0x7d, 0x49, + 0xc6, 0x7d, 0x5b, 0xe0, 0x2b, 0x96, 0x4a, 0xa3, 0x08, 0xeb, 0xe3, 0x28, 0x52, 0x2c, 0xc2, 0x9a, + 0xf9, 0x93, 0xd0, 0x27, 0x32, 0x49, 0xb0, 0xa0, 0x8b, 0x01, 0xdb, 0xfd, 0x3f, 0x3a, 0x44, 0xd2, + 0xb7, 0x70, 0x68, 0xc6, 0x36, 0xb2, 0x81, 0xfd, 0xcb, 0xe9, 0xdd, 0x6f, 0x55, 0xb0, 0xf5, 0x9e, + 0xe9, 0xb7, 0x76, 0x97, 0x14, 0xb1, 0x33, 0xc3, 0x52, 0x0d, 0x39, 0xf8, 0x1f, 0x1b, 0x7d, 0x2a, + 0x15, 0xbf, 0xc4, 0x9a, 0x4b, 0x11, 0x10, 0x29, 0x34, 0xfb, 0xa2, 0xdb, 0xce, 0x8e, 0xd3, 0x6b, + 0x0d, 0x5e, 0x7a, 0x37, 0x2a, 0x8a, 0xa5, 0xca, 0x9d, 0xbc, 0x49, 0xe8, 0xbd, 0xb9, 0x5d, 0xbc, + 0x9f, 0xd7, 0xa2, 0xff, 0xf0, 0x3d, 0x28, 0x74, 0x41, 0x4b, 0x5f, 0x4c, 0x58, 0x30, 0xe6, 0xb1, + 0x66, 0xaa, 0x5d, 0xdd, 0xa9, 0xf5, 0x9a, 0x08, 0x64, 0xd0, 0x81, 0x45, 0xe0, 0x07, 0xb0, 0x96, + 0x6a, 0xac, 0x4d, 0x5a, 0x50, 0x6a, 0x3b, 0xb5, 0xde, 0xfa, 0xe0, 0xa9, 0xf7, 0x57, 0x27, 0xbd, + 0x13, 0x5b, 0x83, 0x56, 0xf3, 0xda, 0x45, 0xaf, 0x3d, 0xb0, 0x95, 0x73, 0x03, 0x4e, 0xcb, 0x7e, + 0x4b, 0x76, 0xe4, 0x46, 0x9e, 0x38, 0xa4, 0x0b, 0x6e, 0xf7, 0x33, 0xd8, 0x38, 0x92, 0x04, 0xc7, + 0xfc, 0x92, 0xd1, 0x13, 0xad, 0xb8, 0x88, 0x60, 0x0f, 0x34, 0x62, 0x2c, 0x22, 0x83, 0x23, 0x66, + 0x9d, 0x68, 0x0e, 0x57, 0xe7, 0x53, 0xb7, 0xc4, 0x50, 0xf9, 0x07, 0x5d, 0xb0, 0x7c, 0x8e, 0x63, + 0xc3, 0xda, 0x55, 0x4b, 0x6b, 0xce, 0xa7, 0x6e, 0x0e, 0xa0, 0xfc, 0xd3, 0xfd, 0xe5, 0x80, 0x06, + 0x5a, 0x58, 0x07, 0xfb, 0x60, 0xbd, 0xb0, 0x31, 0xc8, 0x94, 0xa7, 0x6d, 0x27, 0xdb, 0x69, 0x58, + 0x9f, 0x4f, 0xdd, 0xaa, 0xd2, 0x68, 0xad, 0xc8, 0x7e, 0xcc, 0x92, 0xf0, 0x11, 0x58, 0x12, 0x38, + 0x29, 0x7a, 0x2f, 0xcf, 0xa7, 0xae, 0x23, 0x90, 0x85, 0x20, 0x05, 0x5b, 0x09, 0x16, 0x66, 0x8c, + 0x89, 0x36, 0x8a, 0xa9, 0xc0, 0xf2, 0x32, 0xc3, 0x5a, 0x03, 0xef, 0x01, 0xc3, 0xee, 0x88, 0x1d, + 0xae, 0xcc, 0xa7, 0x6e, 0x8d, 0x26, 0x02, 0x6d, 0xde, 0xee, 0x38, 0xca, 0xa6, 0x3c, 0x07, 0xab, + 0x89, 0xa4, 0x2c, 0x0e, 0x84, 0x49, 0x42, 0xeb, 0x60, 0xb6, 0x48, 0x63, 0x3e, 0x75, 0x97, 0x68, + 0x22, 0x24, 0x6a, 0xd9, 0xec, 0xc8, 0x26, 0xbb, 0x97, 0xa0, 0x9e, 0xbf, 0x2e, 0xb8, 0x0e, 0xaa, + 0x9c, 0xe6, 0xc6, 0xa1, 0x2a, 0xa7, 0x70, 0x1f, 0x34, 0x0a, 0x61, 0x56, 0x4b, 0x6b, 0xf0, 0xec, + 0x81, 0x1d, 0x0b, 0xc7, 0x50, 0x59, 0x08, 0x1f, 0x83, 0x26, 0x4f, 0x03, 0x29, 0x62, 0x2e, 0x32, + 0xa5, 0x4e, 0xaf, 0x81, 0x1a, 0x3c, 0x3d, 0xb6, 0xf1, 0xde, 0x2e, 0xa8, 0xe7, 0xef, 0x00, 0x02, + 0x50, 0x3f, 0x1e, 0x1d, 0x1d, 0x8e, 0xde, 0x6d, 0x56, 0x60, 0x0b, 0xac, 0x1c, 0x1f, 0x1c, 0xd8, + 0xc0, 0x19, 0x26, 0xdf, 0xaf, 0x3b, 0xce, 0xd5, 0x75, 0xc7, 0xf9, 0x79, 0xdd, 0x71, 0xbe, 0xce, + 0x3a, 0x95, 0xab, 0x59, 0xa7, 0xf2, 0x63, 0xd6, 0xa9, 0x7c, 0x3a, 0xf9, 0xa7, 0x5b, 0xbc, 0x39, + 0xf4, 0x7b, 0x8e, 0xff, 0xd5, 0x5d, 0x20, 0xac, 0xdb, 0xb3, 0x7b, 0xf1, 0x3b, 0x00, 0x00, 0xff, + 0xff, 0x9f, 0x4c, 0x9b, 0xa4, 0x2b, 0x04, 0x00, 0x00, +} + +func (m *GetDevicesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetDevicesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetDevicesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DeviceIdsFilter) > 0 { + for iNdEx := len(m.DeviceIdsFilter) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.DeviceIdsFilter[iNdEx]) + copy(dAtA[i:], m.DeviceIdsFilter[iNdEx]) + i = encodeVarintQueries(dAtA, i, uint64(len(m.DeviceIdsFilter[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.StatusFilter) > 0 { + dAtA2 := make([]byte, len(m.StatusFilter)*10) + var j1 int + for _, num := range m.StatusFilter { + for num >= 1<<7 { + dAtA2[j1] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j1++ + } + dAtA2[j1] = uint8(num) + j1++ + } + i -= j1 + copy(dAtA[i:], dAtA2[:j1]) + i = encodeVarintQueries(dAtA, i, uint64(j1)) + i-- + dAtA[i] = 0x1a + } + if len(m.TypeFilter) > 0 { + for iNdEx := len(m.TypeFilter) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.TypeFilter[iNdEx]) + copy(dAtA[i:], m.TypeFilter[iNdEx]) + i = encodeVarintQueries(dAtA, i, uint64(len(m.TypeFilter[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if m.AuthorizationContext != nil { + { + size, err := m.AuthorizationContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQueries(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *LocalizedString) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *LocalizedString) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *LocalizedString) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Value) > 0 { + i -= len(m.Value) + copy(dAtA[i:], m.Value) + i = encodeVarintQueries(dAtA, i, uint64(len(m.Value))) + i-- + dAtA[i] = 0x12 + } + if len(m.Language) > 0 { + i -= len(m.Language) + copy(dAtA[i:], m.Language) + i = encodeVarintQueries(dAtA, i, uint64(len(m.Language))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Resource) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Resource) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Resource) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.ModelNumber) > 0 { + i -= len(m.ModelNumber) + copy(dAtA[i:], m.ModelNumber) + i = encodeVarintQueries(dAtA, i, uint64(len(m.ModelNumber))) + i-- + dAtA[i] = 0x22 + } + if len(m.ManufacturerName) > 0 { + for iNdEx := len(m.ManufacturerName) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.ManufacturerName[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQueries(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintQueries(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if len(m.ResourceTypes) > 0 { + for iNdEx := len(m.ResourceTypes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ResourceTypes[iNdEx]) + copy(dAtA[i:], m.ResourceTypes[iNdEx]) + i = encodeVarintQueries(dAtA, i, uint64(len(m.ResourceTypes[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *Device) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Device) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Device) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.IsOnline { + i-- + if m.IsOnline { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x18 + } + if m.Resource != nil { + { + size, err := m.Resource.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQueries(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintQueries(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintQueries(dAtA []byte, offset int, v uint64) int { + offset -= sovQueries(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GetDevicesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AuthorizationContext != nil { + l = m.AuthorizationContext.Size() + n += 1 + l + sovQueries(uint64(l)) + } + if len(m.TypeFilter) > 0 { + for _, s := range m.TypeFilter { + l = len(s) + n += 1 + l + sovQueries(uint64(l)) + } + } + if len(m.StatusFilter) > 0 { + l = 0 + for _, e := range m.StatusFilter { + l += sovQueries(uint64(e)) + } + n += 1 + sovQueries(uint64(l)) + l + } + if len(m.DeviceIdsFilter) > 0 { + for _, s := range m.DeviceIdsFilter { + l = len(s) + n += 1 + l + sovQueries(uint64(l)) + } + } + return n +} + +func (m *LocalizedString) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Language) + if l > 0 { + n += 1 + l + sovQueries(uint64(l)) + } + l = len(m.Value) + if l > 0 { + n += 1 + l + sovQueries(uint64(l)) + } + return n +} + +func (m *Resource) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.ResourceTypes) > 0 { + for _, s := range m.ResourceTypes { + l = len(s) + n += 1 + l + sovQueries(uint64(l)) + } + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovQueries(uint64(l)) + } + if len(m.ManufacturerName) > 0 { + for _, e := range m.ManufacturerName { + l = e.Size() + n += 1 + l + sovQueries(uint64(l)) + } + } + l = len(m.ModelNumber) + if l > 0 { + n += 1 + l + sovQueries(uint64(l)) + } + return n +} + +func (m *Device) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovQueries(uint64(l)) + } + if m.Resource != nil { + l = m.Resource.Size() + n += 1 + l + sovQueries(uint64(l)) + } + if m.IsOnline { + n += 2 + } + return n +} + +func sovQueries(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQueries(x uint64) (n int) { + return sovQueries(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GetDevicesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetDevicesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetDevicesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthorizationContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuthorizationContext == nil { + m.AuthorizationContext = &pb.AuthorizationContext{} + } + if err := m.AuthorizationContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TypeFilter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TypeFilter = append(m.TypeFilter, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType == 0 { + var v Status + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= Status(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.StatusFilter = append(m.StatusFilter, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + if elementCount != 0 && len(m.StatusFilter) == 0 { + m.StatusFilter = make([]Status, 0, elementCount) + } + for iNdEx < postIndex { + var v Status + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= Status(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.StatusFilter = append(m.StatusFilter, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field StatusFilter", wireType) + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceIdsFilter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceIdsFilter = append(m.DeviceIdsFilter, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQueries(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthQueries + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthQueries + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *LocalizedString) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: LocalizedString: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: LocalizedString: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Language", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Language = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Value = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQueries(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthQueries + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthQueries + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Resource) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Resource: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Resource: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceTypes", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceTypes = append(m.ResourceTypes, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ManufacturerName", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ManufacturerName = append(m.ManufacturerName, &LocalizedString{}) + if err := m.ManufacturerName[len(m.ManufacturerName)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ModelNumber", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ModelNumber = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQueries(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthQueries + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthQueries + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Device) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Device: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Device: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Resource", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Resource == nil { + m.Resource = &Resource{} + } + if err := m.Resource.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsOnline", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsOnline = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipQueries(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthQueries + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthQueries + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQueries(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQueries + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQueries + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQueries + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthQueries + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQueries + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQueries + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQueries = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQueries = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQueries = fmt.Errorf("proto: unexpected end of group") +) diff --git a/resource-directory/pb/device-directory/queries.proto b/resource-directory/pb/device-directory/queries.proto new file mode 100644 index 000000000..e7200119f --- /dev/null +++ b/resource-directory/pb/device-directory/queries.proto @@ -0,0 +1,39 @@ +syntax = "proto3"; + +package ocf.cloud.device.directory.pb; + +import "github.com/go-ocf/cloud/resource-aggregate/pb/commands.proto"; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +option go_package = "github.com/go-ocf/cloud/resource-directory/pb/device-directory;device-directory"; + +enum Status { + ONLINE = 0; + OFFLINE = 1; +} + +message GetDevicesRequest { + ocf.cloud.resourceaggregate.pb.AuthorizationContext authorization_context = 1; + repeated string type_filter = 2; + repeated Status status_filter = 3; + repeated string device_ids_filter = 4; +} + +message LocalizedString { + string language = 1 [(gogoproto.jsontag)="language"]; + string value = 2 [(gogoproto.jsontag)="value"]; +} + +message Resource { + repeated string resource_types = 1 [(gogoproto.jsontag)="rt"]; + string name = 2 [(gogoproto.jsontag)="n"]; + repeated LocalizedString manufacturer_name = 3 [(gogoproto.jsontag)="dmn"]; + string model_number = 4 [(gogoproto.jsontag)="dmno"]; +} + +message Device { + string id = 1; + Resource resource = 2; // if it is nil, resource eventstore doesn't have any information about resource {device}/oic/d + bool is_online = 3; +} \ No newline at end of file diff --git a/resource-directory/pb/device-directory/service.pb.go b/resource-directory/pb/device-directory/service.pb.go new file mode 100644 index 000000000..bbda1932b --- /dev/null +++ b/resource-directory/pb/device-directory/service.pb.go @@ -0,0 +1,152 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: pb/device-directory/service.proto + +package device_directory + +import ( + context "context" + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +func init() { proto.RegisterFile("pb/device-directory/service.proto", fileDescriptor_381435165b454caf) } + +var fileDescriptor_381435165b454caf = []byte{ + // 199 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x8f, 0x3f, 0x0b, 0xc2, 0x30, + 0x10, 0xc5, 0x75, 0x71, 0xc8, 0x22, 0x74, 0x2c, 0x38, 0x28, 0x38, 0xe6, 0x52, 0x74, 0x74, 0x93, + 0x82, 0xab, 0x28, 0x2e, 0x8e, 0x49, 0x2f, 0x35, 0xa0, 0x5c, 0x9b, 0x3f, 0x82, 0xe0, 0x87, 0x17, + 0x13, 0x54, 0x2a, 0xa2, 0x83, 0xdb, 0xdd, 0xe3, 0xf7, 0x1e, 0xef, 0xb1, 0x71, 0x23, 0x45, 0x85, + 0x67, 0xa3, 0x90, 0x57, 0xc6, 0xa2, 0xf2, 0x64, 0x2f, 0xc2, 0xa1, 0xbd, 0x2b, 0xd0, 0x58, 0xf2, + 0x94, 0x8d, 0x48, 0x69, 0x50, 0x47, 0x0a, 0x15, 0x24, 0x12, 0x9e, 0x24, 0x34, 0x32, 0xe7, 0xb5, + 0xf1, 0x87, 0x20, 0x41, 0xd1, 0x49, 0xd4, 0x54, 0x93, 0x88, 0x2e, 0x19, 0x74, 0xfc, 0xe2, 0x13, + 0xaf, 0x94, 0x96, 0xaf, 0x3b, 0x38, 0x27, 0xa5, 0x05, 0x29, 0xcd, 0x63, 0xbe, 0xb0, 0xe8, 0x28, + 0xd8, 0x4e, 0x97, 0x4f, 0xfd, 0xda, 0x80, 0xd6, 0xa0, 0x4b, 0x89, 0xb3, 0x2b, 0x1b, 0x96, 0x91, + 0x28, 0x1f, 0x40, 0x66, 0x18, 0x5b, 0xa1, 0x4f, 0xaa, 0xcb, 0x0a, 0xf8, 0xba, 0x00, 0x5e, 0xe8, + 0x06, 0xdb, 0x80, 0xce, 0xe7, 0xd3, 0x1f, 0x8e, 0x84, 0x4f, 0x7a, 0x45, 0x7f, 0xb9, 0xdb, 0x6f, + 0xff, 0x5f, 0xb4, 0x78, 0x17, 0xe4, 0x20, 0x6e, 0x9b, 0xdf, 0x02, 0x00, 0x00, 0xff, 0xff, 0x46, + 0xbd, 0x73, 0x3f, 0xa0, 0x01, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// DeviceDirectoryClient is the client API for DeviceDirectory service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type DeviceDirectoryClient interface { + GetDevices(ctx context.Context, in *GetDevicesRequest, opts ...grpc.CallOption) (DeviceDirectory_GetDevicesClient, error) +} + +type deviceDirectoryClient struct { + cc *grpc.ClientConn +} + +func NewDeviceDirectoryClient(cc *grpc.ClientConn) DeviceDirectoryClient { + return &deviceDirectoryClient{cc} +} + +func (c *deviceDirectoryClient) GetDevices(ctx context.Context, in *GetDevicesRequest, opts ...grpc.CallOption) (DeviceDirectory_GetDevicesClient, error) { + stream, err := c.cc.NewStream(ctx, &_DeviceDirectory_serviceDesc.Streams[0], "/ocf.cloud.device.directory.pb.DeviceDirectory/GetDevices", opts...) + if err != nil { + return nil, err + } + x := &deviceDirectoryGetDevicesClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type DeviceDirectory_GetDevicesClient interface { + Recv() (*Device, error) + grpc.ClientStream +} + +type deviceDirectoryGetDevicesClient struct { + grpc.ClientStream +} + +func (x *deviceDirectoryGetDevicesClient) Recv() (*Device, error) { + m := new(Device) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// DeviceDirectoryServer is the server API for DeviceDirectory service. +type DeviceDirectoryServer interface { + GetDevices(*GetDevicesRequest, DeviceDirectory_GetDevicesServer) error +} + +// UnimplementedDeviceDirectoryServer can be embedded to have forward compatible implementations. +type UnimplementedDeviceDirectoryServer struct { +} + +func (*UnimplementedDeviceDirectoryServer) GetDevices(req *GetDevicesRequest, srv DeviceDirectory_GetDevicesServer) error { + return status.Errorf(codes.Unimplemented, "method GetDevices not implemented") +} + +func RegisterDeviceDirectoryServer(s *grpc.Server, srv DeviceDirectoryServer) { + s.RegisterService(&_DeviceDirectory_serviceDesc, srv) +} + +func _DeviceDirectory_GetDevices_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(GetDevicesRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(DeviceDirectoryServer).GetDevices(m, &deviceDirectoryGetDevicesServer{stream}) +} + +type DeviceDirectory_GetDevicesServer interface { + Send(*Device) error + grpc.ServerStream +} + +type deviceDirectoryGetDevicesServer struct { + grpc.ServerStream +} + +func (x *deviceDirectoryGetDevicesServer) Send(m *Device) error { + return x.ServerStream.SendMsg(m) +} + +var _DeviceDirectory_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ocf.cloud.device.directory.pb.DeviceDirectory", + HandlerType: (*DeviceDirectoryServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "GetDevices", + Handler: _DeviceDirectory_GetDevices_Handler, + ServerStreams: true, + }, + }, + Metadata: "pb/device-directory/service.proto", +} diff --git a/resource-directory/pb/device-directory/service.proto b/resource-directory/pb/device-directory/service.proto new file mode 100644 index 000000000..f0c921451 --- /dev/null +++ b/resource-directory/pb/device-directory/service.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package ocf.cloud.device.directory.pb; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/go-ocf/cloud/resource-directory/pb/device-directory/queries.proto"; + +option go_package = "github.com/go-ocf/cloud/resource-directory/pb/device-directory;device-directory"; + +service DeviceDirectory { + rpc GetDevices (GetDevicesRequest) returns (stream Device) {} +} \ No newline at end of file diff --git a/resource-directory/pb/resource-directory/queries.pb.go b/resource-directory/pb/resource-directory/queries.pb.go new file mode 100644 index 000000000..82d0f780f --- /dev/null +++ b/resource-directory/pb/resource-directory/queries.pb.go @@ -0,0 +1,632 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: pb/resource-directory/queries.proto + +package resource_directory + +import ( + fmt "fmt" + pb "github.com/go-ocf/cloud/resource-aggregate/pb" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type GetResourceLinksRequest struct { + AuthorizationContext *pb.AuthorizationContext `protobuf:"bytes,1,opt,name=authorization_context,json=authorizationContext,proto3" json:"authorization_context,omitempty"` + TypeFilter []string `protobuf:"bytes,2,rep,name=type_filter,json=typeFilter,proto3" json:"type_filter,omitempty"` + DeviceIdsFilter []string `protobuf:"bytes,3,rep,name=device_ids_filter,json=deviceIdsFilter,proto3" json:"device_ids_filter,omitempty"` +} + +func (m *GetResourceLinksRequest) Reset() { *m = GetResourceLinksRequest{} } +func (m *GetResourceLinksRequest) String() string { return proto.CompactTextString(m) } +func (*GetResourceLinksRequest) ProtoMessage() {} +func (*GetResourceLinksRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_c8d424b2674f7bb2, []int{0} +} +func (m *GetResourceLinksRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GetResourceLinksRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GetResourceLinksRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GetResourceLinksRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetResourceLinksRequest.Merge(m, src) +} +func (m *GetResourceLinksRequest) XXX_Size() int { + return m.Size() +} +func (m *GetResourceLinksRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetResourceLinksRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetResourceLinksRequest proto.InternalMessageInfo + +func (m *GetResourceLinksRequest) GetAuthorizationContext() *pb.AuthorizationContext { + if m != nil { + return m.AuthorizationContext + } + return nil +} + +func (m *GetResourceLinksRequest) GetTypeFilter() []string { + if m != nil { + return m.TypeFilter + } + return nil +} + +func (m *GetResourceLinksRequest) GetDeviceIdsFilter() []string { + if m != nil { + return m.DeviceIdsFilter + } + return nil +} + +type ResourceLink struct { + Resource *pb.Resource `protobuf:"bytes,1,opt,name=resource,proto3" json:"resource,omitempty"` +} + +func (m *ResourceLink) Reset() { *m = ResourceLink{} } +func (m *ResourceLink) String() string { return proto.CompactTextString(m) } +func (*ResourceLink) ProtoMessage() {} +func (*ResourceLink) Descriptor() ([]byte, []int) { + return fileDescriptor_c8d424b2674f7bb2, []int{1} +} +func (m *ResourceLink) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResourceLink) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResourceLink.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResourceLink) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResourceLink.Merge(m, src) +} +func (m *ResourceLink) XXX_Size() int { + return m.Size() +} +func (m *ResourceLink) XXX_DiscardUnknown() { + xxx_messageInfo_ResourceLink.DiscardUnknown(m) +} + +var xxx_messageInfo_ResourceLink proto.InternalMessageInfo + +func (m *ResourceLink) GetResource() *pb.Resource { + if m != nil { + return m.Resource + } + return nil +} + +func init() { + proto.RegisterType((*GetResourceLinksRequest)(nil), "ocf.cloud.resource.directory.pb.GetResourceLinksRequest") + proto.RegisterType((*ResourceLink)(nil), "ocf.cloud.resource.directory.pb.ResourceLink") +} + +func init() { + proto.RegisterFile("pb/resource-directory/queries.proto", fileDescriptor_c8d424b2674f7bb2) +} + +var fileDescriptor_c8d424b2674f7bb2 = []byte{ + // 333 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0x41, 0x4b, 0xfb, 0x40, + 0x10, 0xc5, 0x9b, 0x7f, 0xe1, 0x8f, 0x6e, 0x05, 0x31, 0x28, 0x96, 0x1e, 0xd2, 0x52, 0x2f, 0x45, + 0xe8, 0x06, 0xd4, 0x9b, 0x17, 0xab, 0xa2, 0x08, 0x9e, 0x82, 0x20, 0x78, 0x29, 0xc9, 0x66, 0x92, + 0x2e, 0xb6, 0x99, 0x74, 0x77, 0x56, 0xac, 0x9f, 0xc2, 0x8f, 0xe5, 0xb1, 0xde, 0x3c, 0x4a, 0xfb, + 0x45, 0xc4, 0x34, 0x69, 0x0b, 0x2d, 0xa8, 0xd7, 0xb7, 0xbf, 0x37, 0xef, 0x2d, 0x33, 0xec, 0x20, + 0x0d, 0x5c, 0x05, 0x1a, 0x8d, 0x12, 0xd0, 0x0e, 0xa5, 0x02, 0x41, 0xa8, 0x46, 0xee, 0xd0, 0x80, + 0x92, 0xa0, 0x79, 0xaa, 0x90, 0xd0, 0xae, 0xa3, 0x88, 0xb8, 0xe8, 0xa3, 0x09, 0x79, 0xc1, 0xf2, + 0x39, 0xcb, 0xd3, 0xa0, 0xd6, 0x89, 0x25, 0xf5, 0x4c, 0xc0, 0x05, 0x0e, 0xdc, 0x18, 0xdb, 0x28, + 0x22, 0x17, 0x45, 0xd4, 0xce, 0x2c, 0x8b, 0xf1, 0x7e, 0x1c, 0x2b, 0x88, 0x7d, 0x02, 0x77, 0x29, + 0x34, 0xcf, 0xa8, 0x9d, 0xfd, 0x7d, 0x84, 0xc0, 0xc1, 0xc0, 0x4f, 0xc2, 0x7c, 0x42, 0xf3, 0xdd, + 0x62, 0xfb, 0xd7, 0x40, 0x5e, 0xce, 0xde, 0xca, 0xe4, 0x51, 0x7b, 0x30, 0x34, 0xa0, 0xc9, 0x96, + 0x6c, 0xcf, 0x37, 0xd4, 0x43, 0x25, 0x5f, 0x7c, 0x92, 0x98, 0x74, 0x05, 0x26, 0x04, 0xcf, 0x54, + 0xb5, 0x1a, 0x56, 0xab, 0x72, 0x74, 0xc2, 0x57, 0x7f, 0x38, 0x8f, 0xe2, 0x69, 0xc0, 0x3b, 0xcb, + 0xe6, 0x8b, 0x99, 0xd7, 0xdb, 0xf5, 0xd7, 0xa8, 0x76, 0x9d, 0x55, 0x68, 0x94, 0x42, 0x37, 0x92, + 0x7d, 0x02, 0x55, 0xfd, 0xd7, 0x28, 0xb7, 0x36, 0x3d, 0xf6, 0x2d, 0x5d, 0x65, 0x8a, 0x7d, 0xc8, + 0x76, 0x42, 0x78, 0x92, 0x02, 0xba, 0x32, 0xd4, 0x05, 0x56, 0xce, 0xb0, 0xed, 0xd9, 0xc3, 0x4d, + 0xa8, 0x67, 0x6c, 0xf3, 0x8e, 0x6d, 0x2d, 0xff, 0xc7, 0xbe, 0x64, 0x1b, 0x45, 0xbf, 0xbc, 0x7a, + 0xeb, 0xa7, 0xea, 0x85, 0xdf, 0x9b, 0x3b, 0xcf, 0x87, 0x6f, 0x13, 0xc7, 0x1a, 0x4f, 0x1c, 0xeb, + 0x73, 0xe2, 0x58, 0xaf, 0x53, 0xa7, 0x34, 0x9e, 0x3a, 0xa5, 0x8f, 0xa9, 0x53, 0x7a, 0xb8, 0xff, + 0xd5, 0x16, 0x16, 0x77, 0xb2, 0xf6, 0x7a, 0x4e, 0x57, 0xa5, 0xe0, 0x7f, 0xb6, 0xa3, 0xe3, 0xaf, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x61, 0x74, 0x03, 0xe2, 0x70, 0x02, 0x00, 0x00, +} + +func (m *GetResourceLinksRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GetResourceLinksRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GetResourceLinksRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.DeviceIdsFilter) > 0 { + for iNdEx := len(m.DeviceIdsFilter) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.DeviceIdsFilter[iNdEx]) + copy(dAtA[i:], m.DeviceIdsFilter[iNdEx]) + i = encodeVarintQueries(dAtA, i, uint64(len(m.DeviceIdsFilter[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.TypeFilter) > 0 { + for iNdEx := len(m.TypeFilter) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.TypeFilter[iNdEx]) + copy(dAtA[i:], m.TypeFilter[iNdEx]) + i = encodeVarintQueries(dAtA, i, uint64(len(m.TypeFilter[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if m.AuthorizationContext != nil { + { + size, err := m.AuthorizationContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQueries(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ResourceLink) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResourceLink) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResourceLink) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Resource != nil { + { + size, err := m.Resource.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQueries(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintQueries(dAtA []byte, offset int, v uint64) int { + offset -= sovQueries(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GetResourceLinksRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AuthorizationContext != nil { + l = m.AuthorizationContext.Size() + n += 1 + l + sovQueries(uint64(l)) + } + if len(m.TypeFilter) > 0 { + for _, s := range m.TypeFilter { + l = len(s) + n += 1 + l + sovQueries(uint64(l)) + } + } + if len(m.DeviceIdsFilter) > 0 { + for _, s := range m.DeviceIdsFilter { + l = len(s) + n += 1 + l + sovQueries(uint64(l)) + } + } + return n +} + +func (m *ResourceLink) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Resource != nil { + l = m.Resource.Size() + n += 1 + l + sovQueries(uint64(l)) + } + return n +} + +func sovQueries(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQueries(x uint64) (n int) { + return sovQueries(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GetResourceLinksRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GetResourceLinksRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GetResourceLinksRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthorizationContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuthorizationContext == nil { + m.AuthorizationContext = &pb.AuthorizationContext{} + } + if err := m.AuthorizationContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TypeFilter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TypeFilter = append(m.TypeFilter, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceIdsFilter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceIdsFilter = append(m.DeviceIdsFilter, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQueries(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthQueries + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthQueries + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResourceLink) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResourceLink: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResourceLink: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Resource", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Resource == nil { + m.Resource = &pb.Resource{} + } + if err := m.Resource.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQueries(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthQueries + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthQueries + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQueries(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQueries + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQueries + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQueries + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthQueries + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQueries + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQueries + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQueries = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQueries = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQueries = fmt.Errorf("proto: unexpected end of group") +) diff --git a/resource-directory/pb/resource-directory/queries.proto b/resource-directory/pb/resource-directory/queries.proto new file mode 100644 index 000000000..9623f47dd --- /dev/null +++ b/resource-directory/pb/resource-directory/queries.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package ocf.cloud.resource.directory.pb; + +import "github.com/go-ocf/cloud/resource-aggregate/pb/resources.proto"; +import "github.com/go-ocf/cloud/resource-aggregate/pb/commands.proto"; + + +option go_package = "github.com/go-ocf/cloud/resource-directory/pb/resource-directory;resource-directory"; + +message GetResourceLinksRequest { + ocf.cloud.resourceaggregate.pb.AuthorizationContext authorization_context = 1; + repeated string type_filter = 2; + repeated string device_ids_filter = 3; +} + +message ResourceLink { + ocf.cloud.resourceaggregate.pb.Resource resource = 1; +} diff --git a/resource-directory/pb/resource-directory/service.pb.go b/resource-directory/pb/resource-directory/service.pb.go new file mode 100644 index 000000000..e0a2e2d25 --- /dev/null +++ b/resource-directory/pb/resource-directory/service.pb.go @@ -0,0 +1,154 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: pb/resource-directory/service.proto + +package resource_directory + +import ( + context "context" + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +func init() { + proto.RegisterFile("pb/resource-directory/service.proto", fileDescriptor_ac13a7e262d65b22) +} + +var fileDescriptor_ac13a7e262d65b22 = []byte{ + // 207 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x2e, 0x48, 0xd2, 0x2f, + 0x4a, 0x2d, 0xce, 0x2f, 0x2d, 0x4a, 0x4e, 0xd5, 0x4d, 0xc9, 0x2c, 0x4a, 0x4d, 0x2e, 0xc9, 0x2f, + 0xaa, 0xd4, 0x2f, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, + 0x92, 0xcf, 0x4f, 0x4e, 0xd3, 0x4b, 0xce, 0xc9, 0x2f, 0x4d, 0xd1, 0x83, 0xa9, 0xd5, 0x83, 0xab, + 0xd5, 0x2b, 0x48, 0x92, 0xd2, 0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, + 0x4f, 0xcf, 0x4f, 0xcf, 0xd7, 0x07, 0xeb, 0x4b, 0x2a, 0x4d, 0x03, 0xf3, 0xc0, 0x1c, 0x30, 0x0b, + 0x62, 0x9e, 0x54, 0x10, 0x8a, 0x72, 0xdd, 0xfc, 0xe4, 0x34, 0xfd, 0xfc, 0xe4, 0x34, 0x5d, 0xb0, + 0x0d, 0xd8, 0x5c, 0x83, 0xdd, 0x8d, 0x85, 0xa5, 0xa9, 0x45, 0x99, 0xa9, 0xc5, 0x10, 0x33, 0x8d, + 0xa6, 0x30, 0x72, 0x09, 0x06, 0x41, 0x15, 0xb9, 0xc0, 0xd4, 0x08, 0xd5, 0x73, 0x09, 0xb8, 0xa7, + 0x96, 0xc0, 0xc4, 0x7d, 0x32, 0xf3, 0xb2, 0x8b, 0x85, 0x2c, 0xf4, 0x08, 0x78, 0x47, 0x0f, 0x5d, + 0x4b, 0x50, 0x6a, 0x61, 0x69, 0x6a, 0x71, 0x89, 0x94, 0x2e, 0x41, 0x9d, 0xc8, 0xda, 0x94, 0x18, + 0x0c, 0x18, 0x9d, 0x22, 0xa3, 0xc2, 0xa9, 0xe1, 0x59, 0x6b, 0x4c, 0xa1, 0x24, 0x36, 0xb0, 0xc7, + 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x14, 0x88, 0x67, 0x15, 0xc3, 0x01, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// ResourceDirectoryClient is the client API for ResourceDirectory service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type ResourceDirectoryClient interface { + GetResourceLinks(ctx context.Context, in *GetResourceLinksRequest, opts ...grpc.CallOption) (ResourceDirectory_GetResourceLinksClient, error) +} + +type resourceDirectoryClient struct { + cc *grpc.ClientConn +} + +func NewResourceDirectoryClient(cc *grpc.ClientConn) ResourceDirectoryClient { + return &resourceDirectoryClient{cc} +} + +func (c *resourceDirectoryClient) GetResourceLinks(ctx context.Context, in *GetResourceLinksRequest, opts ...grpc.CallOption) (ResourceDirectory_GetResourceLinksClient, error) { + stream, err := c.cc.NewStream(ctx, &_ResourceDirectory_serviceDesc.Streams[0], "/ocf.cloud.resource.directory.pb.ResourceDirectory/GetResourceLinks", opts...) + if err != nil { + return nil, err + } + x := &resourceDirectoryGetResourceLinksClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type ResourceDirectory_GetResourceLinksClient interface { + Recv() (*ResourceLink, error) + grpc.ClientStream +} + +type resourceDirectoryGetResourceLinksClient struct { + grpc.ClientStream +} + +func (x *resourceDirectoryGetResourceLinksClient) Recv() (*ResourceLink, error) { + m := new(ResourceLink) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// ResourceDirectoryServer is the server API for ResourceDirectory service. +type ResourceDirectoryServer interface { + GetResourceLinks(*GetResourceLinksRequest, ResourceDirectory_GetResourceLinksServer) error +} + +// UnimplementedResourceDirectoryServer can be embedded to have forward compatible implementations. +type UnimplementedResourceDirectoryServer struct { +} + +func (*UnimplementedResourceDirectoryServer) GetResourceLinks(req *GetResourceLinksRequest, srv ResourceDirectory_GetResourceLinksServer) error { + return status.Errorf(codes.Unimplemented, "method GetResourceLinks not implemented") +} + +func RegisterResourceDirectoryServer(s *grpc.Server, srv ResourceDirectoryServer) { + s.RegisterService(&_ResourceDirectory_serviceDesc, srv) +} + +func _ResourceDirectory_GetResourceLinks_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(GetResourceLinksRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(ResourceDirectoryServer).GetResourceLinks(m, &resourceDirectoryGetResourceLinksServer{stream}) +} + +type ResourceDirectory_GetResourceLinksServer interface { + Send(*ResourceLink) error + grpc.ServerStream +} + +type resourceDirectoryGetResourceLinksServer struct { + grpc.ServerStream +} + +func (x *resourceDirectoryGetResourceLinksServer) Send(m *ResourceLink) error { + return x.ServerStream.SendMsg(m) +} + +var _ResourceDirectory_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ocf.cloud.resource.directory.pb.ResourceDirectory", + HandlerType: (*ResourceDirectoryServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "GetResourceLinks", + Handler: _ResourceDirectory_GetResourceLinks_Handler, + ServerStreams: true, + }, + }, + Metadata: "pb/resource-directory/service.proto", +} diff --git a/resource-directory/pb/resource-directory/service.proto b/resource-directory/pb/resource-directory/service.proto new file mode 100644 index 000000000..76a2dba9a --- /dev/null +++ b/resource-directory/pb/resource-directory/service.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package ocf.cloud.resource.directory.pb; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/go-ocf/cloud/resource-directory/pb/resource-directory/queries.proto"; + +option go_package = "github.com/go-ocf/cloud/resource-directory/pb/resource-directory;resource-directory"; + +service ResourceDirectory { + rpc GetResourceLinks(GetResourceLinksRequest) returns (stream ResourceLink) {} +} \ No newline at end of file diff --git a/resource-directory/pb/resource-shadow/queries.pb.go b/resource-directory/pb/resource-shadow/queries.pb.go new file mode 100644 index 000000000..c1b720677 --- /dev/null +++ b/resource-directory/pb/resource-shadow/queries.pb.go @@ -0,0 +1,934 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: pb/resource-shadow/queries.proto + +package resource_shadow + +import ( + fmt "fmt" + pb "github.com/go-ocf/cloud/resource-aggregate/pb" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type RetrieveResourcesValuesRequest struct { + AuthorizationContext *pb.AuthorizationContext `protobuf:"bytes,1,opt,name=authorization_context,json=authorizationContext,proto3" json:"authorization_context,omitempty"` + ResourceIdsFilter []string `protobuf:"bytes,2,rep,name=resource_ids_filter,json=resourceIdsFilter,proto3" json:"resource_ids_filter,omitempty"` + DeviceIdsFilter []string `protobuf:"bytes,3,rep,name=device_ids_filter,json=deviceIdsFilter,proto3" json:"device_ids_filter,omitempty"` + TypeFilter []string `protobuf:"bytes,4,rep,name=type_filter,json=typeFilter,proto3" json:"type_filter,omitempty"` +} + +func (m *RetrieveResourcesValuesRequest) Reset() { *m = RetrieveResourcesValuesRequest{} } +func (m *RetrieveResourcesValuesRequest) String() string { return proto.CompactTextString(m) } +func (*RetrieveResourcesValuesRequest) ProtoMessage() {} +func (*RetrieveResourcesValuesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_74bd43318fcf3a1f, []int{0} +} +func (m *RetrieveResourcesValuesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RetrieveResourcesValuesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RetrieveResourcesValuesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *RetrieveResourcesValuesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_RetrieveResourcesValuesRequest.Merge(m, src) +} +func (m *RetrieveResourcesValuesRequest) XXX_Size() int { + return m.Size() +} +func (m *RetrieveResourcesValuesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_RetrieveResourcesValuesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_RetrieveResourcesValuesRequest proto.InternalMessageInfo + +func (m *RetrieveResourcesValuesRequest) GetAuthorizationContext() *pb.AuthorizationContext { + if m != nil { + return m.AuthorizationContext + } + return nil +} + +func (m *RetrieveResourcesValuesRequest) GetResourceIdsFilter() []string { + if m != nil { + return m.ResourceIdsFilter + } + return nil +} + +func (m *RetrieveResourcesValuesRequest) GetDeviceIdsFilter() []string { + if m != nil { + return m.DeviceIdsFilter + } + return nil +} + +func (m *RetrieveResourcesValuesRequest) GetTypeFilter() []string { + if m != nil { + return m.TypeFilter + } + return nil +} + +type ResourceValue struct { + ResourceId string `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` + DeviceId string `protobuf:"bytes,2,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` + Href string `protobuf:"bytes,3,opt,name=href,proto3" json:"href,omitempty"` + Types []string `protobuf:"bytes,4,rep,name=types,proto3" json:"types,omitempty"` + Content *pb.Content `protobuf:"bytes,5,opt,name=content,proto3" json:"content,omitempty"` + Status pb.Status `protobuf:"varint,6,opt,name=status,proto3,enum=ocf.cloud.resourceaggregate.pb.Status" json:"status,omitempty"` +} + +func (m *ResourceValue) Reset() { *m = ResourceValue{} } +func (m *ResourceValue) String() string { return proto.CompactTextString(m) } +func (*ResourceValue) ProtoMessage() {} +func (*ResourceValue) Descriptor() ([]byte, []int) { + return fileDescriptor_74bd43318fcf3a1f, []int{1} +} +func (m *ResourceValue) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResourceValue) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResourceValue.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ResourceValue) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResourceValue.Merge(m, src) +} +func (m *ResourceValue) XXX_Size() int { + return m.Size() +} +func (m *ResourceValue) XXX_DiscardUnknown() { + xxx_messageInfo_ResourceValue.DiscardUnknown(m) +} + +var xxx_messageInfo_ResourceValue proto.InternalMessageInfo + +func (m *ResourceValue) GetResourceId() string { + if m != nil { + return m.ResourceId + } + return "" +} + +func (m *ResourceValue) GetDeviceId() string { + if m != nil { + return m.DeviceId + } + return "" +} + +func (m *ResourceValue) GetHref() string { + if m != nil { + return m.Href + } + return "" +} + +func (m *ResourceValue) GetTypes() []string { + if m != nil { + return m.Types + } + return nil +} + +func (m *ResourceValue) GetContent() *pb.Content { + if m != nil { + return m.Content + } + return nil +} + +func (m *ResourceValue) GetStatus() pb.Status { + if m != nil { + return m.Status + } + return pb.Status_UNKNOWN +} + +func init() { + proto.RegisterType((*RetrieveResourcesValuesRequest)(nil), "ocf.cloud.resource.shadow.pb.RetrieveResourcesValuesRequest") + proto.RegisterType((*ResourceValue)(nil), "ocf.cloud.resource.shadow.pb.ResourceValue") +} + +func init() { proto.RegisterFile("pb/resource-shadow/queries.proto", fileDescriptor_74bd43318fcf3a1f) } + +var fileDescriptor_74bd43318fcf3a1f = []byte{ + // 429 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x93, 0x4f, 0x6f, 0xd3, 0x30, + 0x18, 0xc6, 0x9b, 0x76, 0x2b, 0xd4, 0x13, 0xa0, 0x99, 0x21, 0x45, 0x03, 0x85, 0x68, 0x07, 0xa8, + 0x90, 0xea, 0x48, 0x83, 0x1b, 0x12, 0xa2, 0x4c, 0x42, 0xe2, 0x88, 0x91, 0x38, 0x70, 0xa9, 0x1c, + 0xe7, 0x4d, 0x62, 0xd1, 0xc6, 0x99, 0xff, 0x0c, 0xc6, 0x27, 0xe0, 0xc8, 0xc7, 0xe2, 0xb8, 0x23, + 0x47, 0xd4, 0x7e, 0x0b, 0x4e, 0xa8, 0x4e, 0xdc, 0x16, 0x8a, 0x54, 0x76, 0x8b, 0xfd, 0xfe, 0x9e, + 0xe7, 0xb1, 0x1f, 0x39, 0x28, 0xae, 0xd3, 0x44, 0x81, 0x96, 0x56, 0x71, 0x18, 0xe9, 0x92, 0x65, + 0xf2, 0x53, 0x72, 0x6e, 0x41, 0x09, 0xd0, 0xa4, 0x56, 0xd2, 0x48, 0xfc, 0x40, 0xf2, 0x9c, 0xf0, + 0xa9, 0xb4, 0x19, 0xf1, 0x20, 0x69, 0x40, 0x52, 0xa7, 0xc7, 0xe3, 0x42, 0x98, 0xd2, 0xa6, 0x84, + 0xcb, 0x59, 0x52, 0xc8, 0x91, 0xe4, 0x79, 0x22, 0x79, 0x3e, 0x72, 0xfc, 0xda, 0x98, 0x15, 0x85, + 0x82, 0x82, 0x19, 0x48, 0x36, 0xe2, 0xda, 0x80, 0xe3, 0x97, 0xd7, 0xb7, 0xe0, 0x72, 0x36, 0x63, + 0x55, 0xd6, 0x3a, 0x9c, 0x7c, 0xed, 0xa2, 0x88, 0x82, 0x51, 0x02, 0x2e, 0x80, 0x7a, 0xf7, 0xf7, + 0x6c, 0x6a, 0x41, 0x53, 0x38, 0xb7, 0xa0, 0x0d, 0x16, 0xe8, 0x1e, 0xb3, 0xa6, 0x94, 0x4a, 0x7c, + 0x61, 0x46, 0xc8, 0x6a, 0xc2, 0x65, 0x65, 0xe0, 0xb3, 0x09, 0x83, 0x38, 0x18, 0x1e, 0x9c, 0x3e, + 0x23, 0xdb, 0xb7, 0x5c, 0x25, 0x92, 0x3a, 0x25, 0xe3, 0x4d, 0xf1, 0x59, 0xa3, 0xa5, 0x47, 0xec, + 0x1f, 0xbb, 0x98, 0xa0, 0xbb, 0xde, 0x62, 0x22, 0x32, 0x3d, 0xc9, 0xc5, 0xd4, 0x80, 0x0a, 0xbb, + 0x71, 0x6f, 0x38, 0xa0, 0x87, 0x7e, 0xf4, 0x26, 0xd3, 0xaf, 0xdd, 0x00, 0x3f, 0x41, 0x87, 0x19, + 0x5c, 0x88, 0x3f, 0xe9, 0x9e, 0xa3, 0xef, 0x34, 0x83, 0x35, 0xfb, 0x10, 0x1d, 0x98, 0xcb, 0x1a, + 0x3c, 0xb5, 0xe7, 0x28, 0xb4, 0xdc, 0x6a, 0x80, 0x93, 0x5f, 0x01, 0xba, 0xe5, 0x2b, 0x70, 0x0d, + 0x2c, 0x25, 0x1b, 0xc7, 0x71, 0xf7, 0x1d, 0x50, 0xb4, 0x3e, 0x06, 0xbe, 0x8f, 0x06, 0xab, 0xfc, + 0xb0, 0xeb, 0xc6, 0x37, 0x7d, 0x2e, 0xc6, 0x68, 0xaf, 0x54, 0x90, 0x87, 0x3d, 0xb7, 0xef, 0xbe, + 0xf1, 0x11, 0xda, 0x5f, 0x26, 0xea, 0x36, 0xbe, 0x59, 0xe0, 0x31, 0xba, 0xe1, 0x3a, 0xad, 0x4c, + 0xb8, 0xef, 0x3a, 0x7d, 0xbc, 0xab, 0xd3, 0xb3, 0x06, 0xa7, 0x5e, 0x87, 0x5f, 0xa0, 0xbe, 0x36, + 0xcc, 0x58, 0x1d, 0xf6, 0xe3, 0x60, 0x78, 0xfb, 0xf4, 0xd1, 0x2e, 0x87, 0x77, 0x8e, 0xa6, 0xad, + 0xea, 0xd5, 0xc7, 0xef, 0xf3, 0x28, 0xb8, 0x9a, 0x47, 0xc1, 0xcf, 0x79, 0x14, 0x7c, 0x5b, 0x44, + 0x9d, 0xab, 0x45, 0xd4, 0xf9, 0xb1, 0x88, 0x3a, 0x1f, 0xde, 0xfe, 0xd7, 0x1b, 0xcb, 0x84, 0x02, + 0x6e, 0xa4, 0xba, 0x4c, 0xb6, 0xff, 0x8a, 0xe7, 0x7f, 0xad, 0xd3, 0xbe, 0x7b, 0x7b, 0x4f, 0x7f, + 0x07, 0x00, 0x00, 0xff, 0xff, 0x10, 0xe7, 0x2e, 0xf3, 0x42, 0x03, 0x00, 0x00, +} + +func (m *RetrieveResourcesValuesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RetrieveResourcesValuesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RetrieveResourcesValuesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.TypeFilter) > 0 { + for iNdEx := len(m.TypeFilter) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.TypeFilter[iNdEx]) + copy(dAtA[i:], m.TypeFilter[iNdEx]) + i = encodeVarintQueries(dAtA, i, uint64(len(m.TypeFilter[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.DeviceIdsFilter) > 0 { + for iNdEx := len(m.DeviceIdsFilter) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.DeviceIdsFilter[iNdEx]) + copy(dAtA[i:], m.DeviceIdsFilter[iNdEx]) + i = encodeVarintQueries(dAtA, i, uint64(len(m.DeviceIdsFilter[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if len(m.ResourceIdsFilter) > 0 { + for iNdEx := len(m.ResourceIdsFilter) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ResourceIdsFilter[iNdEx]) + copy(dAtA[i:], m.ResourceIdsFilter[iNdEx]) + i = encodeVarintQueries(dAtA, i, uint64(len(m.ResourceIdsFilter[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if m.AuthorizationContext != nil { + { + size, err := m.AuthorizationContext.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQueries(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ResourceValue) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ResourceValue) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResourceValue) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Status != 0 { + i = encodeVarintQueries(dAtA, i, uint64(m.Status)) + i-- + dAtA[i] = 0x30 + } + if m.Content != nil { + { + size, err := m.Content.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQueries(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + if len(m.Types) > 0 { + for iNdEx := len(m.Types) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Types[iNdEx]) + copy(dAtA[i:], m.Types[iNdEx]) + i = encodeVarintQueries(dAtA, i, uint64(len(m.Types[iNdEx]))) + i-- + dAtA[i] = 0x22 + } + } + if len(m.Href) > 0 { + i -= len(m.Href) + copy(dAtA[i:], m.Href) + i = encodeVarintQueries(dAtA, i, uint64(len(m.Href))) + i-- + dAtA[i] = 0x1a + } + if len(m.DeviceId) > 0 { + i -= len(m.DeviceId) + copy(dAtA[i:], m.DeviceId) + i = encodeVarintQueries(dAtA, i, uint64(len(m.DeviceId))) + i-- + dAtA[i] = 0x12 + } + if len(m.ResourceId) > 0 { + i -= len(m.ResourceId) + copy(dAtA[i:], m.ResourceId) + i = encodeVarintQueries(dAtA, i, uint64(len(m.ResourceId))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintQueries(dAtA []byte, offset int, v uint64) int { + offset -= sovQueries(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *RetrieveResourcesValuesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.AuthorizationContext != nil { + l = m.AuthorizationContext.Size() + n += 1 + l + sovQueries(uint64(l)) + } + if len(m.ResourceIdsFilter) > 0 { + for _, s := range m.ResourceIdsFilter { + l = len(s) + n += 1 + l + sovQueries(uint64(l)) + } + } + if len(m.DeviceIdsFilter) > 0 { + for _, s := range m.DeviceIdsFilter { + l = len(s) + n += 1 + l + sovQueries(uint64(l)) + } + } + if len(m.TypeFilter) > 0 { + for _, s := range m.TypeFilter { + l = len(s) + n += 1 + l + sovQueries(uint64(l)) + } + } + return n +} + +func (m *ResourceValue) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ResourceId) + if l > 0 { + n += 1 + l + sovQueries(uint64(l)) + } + l = len(m.DeviceId) + if l > 0 { + n += 1 + l + sovQueries(uint64(l)) + } + l = len(m.Href) + if l > 0 { + n += 1 + l + sovQueries(uint64(l)) + } + if len(m.Types) > 0 { + for _, s := range m.Types { + l = len(s) + n += 1 + l + sovQueries(uint64(l)) + } + } + if m.Content != nil { + l = m.Content.Size() + n += 1 + l + sovQueries(uint64(l)) + } + if m.Status != 0 { + n += 1 + sovQueries(uint64(m.Status)) + } + return n +} + +func sovQueries(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQueries(x uint64) (n int) { + return sovQueries(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *RetrieveResourcesValuesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RetrieveResourcesValuesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RetrieveResourcesValuesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AuthorizationContext", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.AuthorizationContext == nil { + m.AuthorizationContext = &pb.AuthorizationContext{} + } + if err := m.AuthorizationContext.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceIdsFilter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceIdsFilter = append(m.ResourceIdsFilter, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceIdsFilter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceIdsFilter = append(m.DeviceIdsFilter, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TypeFilter", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TypeFilter = append(m.TypeFilter, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQueries(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthQueries + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthQueries + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ResourceValue) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ResourceValue: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResourceValue: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceId = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Href", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Href = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Types", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Types = append(m.Types, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQueries + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQueries + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Content == nil { + m.Content = &pb.Content{} + } + if err := m.Content.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + m.Status = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQueries + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Status |= pb.Status(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQueries(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthQueries + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthQueries + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQueries(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQueries + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQueries + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQueries + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthQueries + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQueries + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQueries + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQueries = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQueries = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQueries = fmt.Errorf("proto: unexpected end of group") +) diff --git a/resource-directory/pb/resource-shadow/queries.proto b/resource-directory/pb/resource-shadow/queries.proto new file mode 100644 index 000000000..a08bf2e8b --- /dev/null +++ b/resource-directory/pb/resource-shadow/queries.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; + +package ocf.cloud.resource.shadow.pb; + +import "github.com/go-ocf/cloud/resource-aggregate/pb/resources.proto"; +import "github.com/go-ocf/cloud/resource-aggregate/pb/commands.proto"; + + +option go_package = "github.com/go-ocf/cloud/resource-directory/pb/resource-shadow;resource-shadow"; + +message RetrieveResourcesValuesRequest { + ocf.cloud.resourceaggregate.pb.AuthorizationContext authorization_context = 1; + repeated string resource_ids_filter = 2; + repeated string device_ids_filter = 3; + repeated string type_filter = 4; +} + +message ResourceValue { + string resource_id = 1; + string device_id = 2; + string href = 3; + repeated string types = 4; + ocf.cloud.resourceaggregate.pb.Content content = 5; + ocf.cloud.resourceaggregate.pb.Status status = 6; +} \ No newline at end of file diff --git a/resource-directory/pb/resource-shadow/service.pb.go b/resource-directory/pb/resource-shadow/service.pb.go new file mode 100644 index 000000000..486654fbd --- /dev/null +++ b/resource-directory/pb/resource-shadow/service.pb.go @@ -0,0 +1,153 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: pb/resource-shadow/service.proto + +package resource_shadow + +import ( + context "context" + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +func init() { proto.RegisterFile("pb/resource-shadow/service.proto", fileDescriptor_3c780121d0f01ad2) } + +var fileDescriptor_3c780121d0f01ad2 = []byte{ + // 222 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x8f, 0x3d, 0x4e, 0xc5, 0x30, + 0x10, 0x84, 0x49, 0x43, 0x91, 0x82, 0x22, 0x0d, 0x52, 0x44, 0x81, 0x68, 0x91, 0xd7, 0x08, 0x4a, + 0xa8, 0xb8, 0x00, 0x22, 0x4f, 0xa2, 0xa0, 0x7b, 0xde, 0xac, 0xf3, 0x2c, 0x05, 0x6d, 0xb2, 0xb6, + 0x83, 0xb8, 0x01, 0x77, 0xe0, 0xb2, 0x48, 0x6b, 0x22, 0x04, 0xe2, 0xa7, 0x78, 0x9d, 0xc7, 0xfb, + 0xcd, 0x68, 0xa6, 0x3e, 0x9d, 0x9c, 0x15, 0x8a, 0x9c, 0x05, 0xc9, 0xc4, 0xdd, 0xb6, 0xe7, 0x67, + 0x1b, 0x49, 0x96, 0x80, 0x04, 0x93, 0x70, 0xe2, 0xe6, 0x84, 0xd1, 0x03, 0x8e, 0x9c, 0x7b, 0x58, + 0x41, 0x28, 0x20, 0x4c, 0xae, 0x35, 0x43, 0x48, 0xbb, 0xec, 0x00, 0xf9, 0xc9, 0x0e, 0x3c, 0xb0, + 0x55, 0x93, 0xcb, 0x5e, 0x95, 0x0a, 0x7d, 0x95, 0xb0, 0xf6, 0xee, 0x0b, 0x6e, 0x18, 0xbd, 0x65, + 0xf4, 0x46, 0xe3, 0x3f, 0x7b, 0xf4, 0x41, 0x08, 0x13, 0xcb, 0x8b, 0xfd, 0xa1, 0xdd, 0x9c, 0x49, + 0x02, 0xc5, 0x12, 0x78, 0xf9, 0x56, 0xd5, 0x47, 0xdd, 0x07, 0xb1, 0x51, 0xa0, 0x79, 0xad, 0xea, + 0xe3, 0x8e, 0x92, 0x04, 0x5a, 0x68, 0x3d, 0xc5, 0x87, 0xed, 0x98, 0x29, 0x36, 0x37, 0xf0, 0xd7, + 0x1a, 0xf8, 0xc5, 0xd6, 0xd1, 0x9c, 0x29, 0xa6, 0xf6, 0xfc, 0x3f, 0x77, 0xf9, 0x52, 0xd3, 0xd9, + 0xc1, 0x45, 0x75, 0xbb, 0x79, 0xbc, 0xdf, 0x7b, 0xf0, 0xf5, 0x37, 0xed, 0x0e, 0x75, 0xf9, 0xd5, + 0x7b, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x4f, 0x90, 0x1b, 0xbb, 0x01, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// ResourceShadowClient is the client API for ResourceShadow service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type ResourceShadowClient interface { + RetrieveResourcesValues(ctx context.Context, in *RetrieveResourcesValuesRequest, opts ...grpc.CallOption) (ResourceShadow_RetrieveResourcesValuesClient, error) +} + +type resourceShadowClient struct { + cc *grpc.ClientConn +} + +func NewResourceShadowClient(cc *grpc.ClientConn) ResourceShadowClient { + return &resourceShadowClient{cc} +} + +func (c *resourceShadowClient) RetrieveResourcesValues(ctx context.Context, in *RetrieveResourcesValuesRequest, opts ...grpc.CallOption) (ResourceShadow_RetrieveResourcesValuesClient, error) { + stream, err := c.cc.NewStream(ctx, &_ResourceShadow_serviceDesc.Streams[0], "/ocf.cloud.resource.shadow.pb.ResourceShadow/RetrieveResourcesValues", opts...) + if err != nil { + return nil, err + } + x := &resourceShadowRetrieveResourcesValuesClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type ResourceShadow_RetrieveResourcesValuesClient interface { + Recv() (*ResourceValue, error) + grpc.ClientStream +} + +type resourceShadowRetrieveResourcesValuesClient struct { + grpc.ClientStream +} + +func (x *resourceShadowRetrieveResourcesValuesClient) Recv() (*ResourceValue, error) { + m := new(ResourceValue) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// ResourceShadowServer is the server API for ResourceShadow service. +type ResourceShadowServer interface { + RetrieveResourcesValues(*RetrieveResourcesValuesRequest, ResourceShadow_RetrieveResourcesValuesServer) error +} + +// UnimplementedResourceShadowServer can be embedded to have forward compatible implementations. +type UnimplementedResourceShadowServer struct { +} + +func (*UnimplementedResourceShadowServer) RetrieveResourcesValues(req *RetrieveResourcesValuesRequest, srv ResourceShadow_RetrieveResourcesValuesServer) error { + return status.Errorf(codes.Unimplemented, "method RetrieveResourcesValues not implemented") +} + +func RegisterResourceShadowServer(s *grpc.Server, srv ResourceShadowServer) { + s.RegisterService(&_ResourceShadow_serviceDesc, srv) +} + +func _ResourceShadow_RetrieveResourcesValues_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(RetrieveResourcesValuesRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(ResourceShadowServer).RetrieveResourcesValues(m, &resourceShadowRetrieveResourcesValuesServer{stream}) +} + +type ResourceShadow_RetrieveResourcesValuesServer interface { + Send(*ResourceValue) error + grpc.ServerStream +} + +type resourceShadowRetrieveResourcesValuesServer struct { + grpc.ServerStream +} + +func (x *resourceShadowRetrieveResourcesValuesServer) Send(m *ResourceValue) error { + return x.ServerStream.SendMsg(m) +} + +var _ResourceShadow_serviceDesc = grpc.ServiceDesc{ + ServiceName: "ocf.cloud.resource.shadow.pb.ResourceShadow", + HandlerType: (*ResourceShadowServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "RetrieveResourcesValues", + Handler: _ResourceShadow_RetrieveResourcesValues_Handler, + ServerStreams: true, + }, + }, + Metadata: "pb/resource-shadow/service.proto", +} diff --git a/resource-directory/pb/resource-shadow/service.proto b/resource-directory/pb/resource-shadow/service.proto new file mode 100644 index 000000000..f0f9cfc25 --- /dev/null +++ b/resource-directory/pb/resource-shadow/service.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package ocf.cloud.resource.shadow.pb; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "github.com/go-ocf/cloud/resource-directory/pb/resource-shadow/queries.proto"; + +option go_package = "github.com/go-ocf/cloud/resource-directory/pb/resource-shadow;resource-shadow"; + +service ResourceShadow { + rpc RetrieveResourcesValues(RetrieveResourcesValuesRequest) returns (stream ResourceValue) {} +} \ No newline at end of file diff --git a/resource-directory/refImpl/refImpl.go b/resource-directory/refImpl/refImpl.go new file mode 100644 index 000000000..a704e8ce7 --- /dev/null +++ b/resource-directory/refImpl/refImpl.go @@ -0,0 +1,133 @@ +package refImpl + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/go-ocf/kit/security/certManager" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + "github.com/go-ocf/kit/log" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + pbAS "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventbus/nats" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/mongodb" + pbDD "github.com/go-ocf/cloud/resource-directory/pb/device-directory" + pbRD "github.com/go-ocf/cloud/resource-directory/pb/resource-directory" + pbRS "github.com/go-ocf/cloud/resource-directory/pb/resource-shadow" + "github.com/go-ocf/cloud/resource-directory/service" + "github.com/panjf2000/ants" +) + +type Config struct { + Service service.Config + GoRoutinePoolSize int `envconfig:"GOROUTINE_POOL_SIZE" default:"16"` + CacheExpiration time.Duration `envconfig:"CACHE_EXPIRATION" default:"30s"` + Nats nats.Config `envconfig:"NATS"` + MongoDB mongodb.Config `envconfig:"MONGODB"` + Log log.Config `envconfig:"LOG"` + Listen certManager.Config `envconfig:"LISTEN"` + Dial certManager.Config `envconfig:"DIAL"` +} + +//String return string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} + +type RefImpl struct { + eventstore *mongodb.EventStore + handle *service.RequestHandler + subscriber *nats.Subscriber + clientCertManager certManager.CertManager + serverCertManager certManager.CertManager + server *kitNetGrpc.Server +} + +func Init(config Config) (*RefImpl, error) { + log.Setup(config.Log) + + log.Info(config.String()) + + impl, err := NewRequestHandlerFromConfig(config) + if err != nil { + return nil, err + } + + serverTLSConfig := impl.serverCertManager.GetServerTLSConfig() + + svr, err := kitNetGrpc.NewServer(config.Service.Addr, grpc.Creds(credentials.NewTLS(&serverTLSConfig))) + if err != nil { + return nil, err + } + pbRS.RegisterResourceShadowServer(svr.Server, impl.handle) + pbRD.RegisterResourceDirectoryServer(svr.Server, impl.handle) + pbDD.RegisterDeviceDirectoryServer(svr.Server, impl.handle) + impl.server = svr + return impl, nil +} + +func (r *RefImpl) Serve() error { + return r.server.Serve() +} + +func (r *RefImpl) Shutdown() { + r.server.Stop() + r.eventstore.Close(context.Background()) + r.subscriber.Close() + r.clientCertManager.Close() + r.serverCertManager.Close() +} + +// NewRequestHandlerFromConfig creates RegisterGrpcGatewayServer with all dependencies. +func NewRequestHandlerFromConfig(config Config) (*RefImpl, error) { + + clientCertManager, err := certManager.NewCertManager(config.Dial) + if err != nil { + return nil, fmt.Errorf("cannot create client cert manager %v", err) + } + serverCertManager, err := certManager.NewCertManager(config.Listen) + if err != nil { + return nil, fmt.Errorf("cannot create server cert manager %v", err) + } + + svc := config.Service + clientTLSConfig := clientCertManager.GetClientTLSConfig() + + authServiceConn, err := grpc.Dial(svc.AuthServerAddr, grpc.WithTransportCredentials(credentials.NewTLS(&clientTLSConfig))) + authServiceClient := pbAS.NewAuthorizationServiceClient(authServiceConn) + + pool, err := ants.NewPool(config.GoRoutinePoolSize) + if err != nil { + return nil, fmt.Errorf("cannot create goroutine pool: %v", err) + } + + eventstore, err := mongodb.NewEventStore(config.MongoDB, pool.Submit, mongodb.WithTLS(&clientTLSConfig)) + if err != nil { + return nil, fmt.Errorf("cannot create resource mongodb eventstore %v", err) + } + + subscriber, err := nats.NewSubscriber(config.Nats, pool.Submit, func(err error) { log.Errorf("resource-directory: error occurs during receiving event: %v", err) }, nats.WithTLS(&clientTLSConfig)) + if err != nil { + return nil, fmt.Errorf("cannot create resource nats subscriber %v", err) + } + + ctx := context.Background() + + resourceProjection, err := service.NewProjection(ctx, svc.FQDN, eventstore, subscriber, config.CacheExpiration) + if err != nil { + return nil, fmt.Errorf("cannot create server: %v", err) + } + + return &RefImpl{ + eventstore: eventstore, + handle: service.NewRequestHandler(authServiceClient, resourceProjection), + subscriber: subscriber, + clientCertManager: clientCertManager, + serverCertManager: serverCertManager, + }, nil +} diff --git a/resource-directory/refImpl/refImpl_test.go b/resource-directory/refImpl/refImpl_test.go new file mode 100644 index 000000000..ca2af2f8e --- /dev/null +++ b/resource-directory/refImpl/refImpl_test.go @@ -0,0 +1,18 @@ +package refImpl + +import ( + "testing" + + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/require" +) + +func TestInit(t *testing.T) { + var config Config + err := envconfig.Process("", &config) + require.NoError(t, err) + + got, err := Init(config) + require.NoError(t, err) + require.NotEmpty(t, got) +} diff --git a/resource-directory/service/config.go b/resource-directory/service/config.go new file mode 100644 index 000000000..d6e191480 --- /dev/null +++ b/resource-directory/service/config.go @@ -0,0 +1,22 @@ +package service + +import ( + "encoding/json" + "fmt" + + "github.com/go-ocf/kit/net/grpc" +) + +// Config represent application configuration +type Config struct { + grpc.Config + + FQDN string `envconfig:"FQDN" default:"grpc-gateway"` + AuthServerAddr string `envconfig:"AUTH_SERVER_ADDRESS" default:"127.0.0.1:9100"` +} + +//String return string representation of Config +func (c Config) String() string { + b, _ := json.MarshalIndent(c, "", " ") + return fmt.Sprintf("config: \n%v\n", string(b)) +} diff --git a/resource-directory/service/deviceDirectory.go b/resource-directory/service/deviceDirectory.go new file mode 100644 index 000000000..f177d93f9 --- /dev/null +++ b/resource-directory/service/deviceDirectory.go @@ -0,0 +1,174 @@ +package service + +import ( + "context" + "fmt" + + pbDD "github.com/go-ocf/cloud/resource-directory/pb/device-directory" + "github.com/go-ocf/sdk/schema/cloud" + + coap "github.com/go-ocf/go-coap" + "github.com/go-ocf/kit/codec/cbor" + "github.com/go-ocf/kit/codec/json" + "github.com/go-ocf/kit/strings" + "google.golang.org/grpc/codes" + + "github.com/go-ocf/cloud/resource-aggregate/cqrs" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +// hasMatchingStatus returns true for matching a device state. +// An empty status_filter matches all device states. +func hasMatchingStatus(isOnline bool, status_filter []pbDD.Status) bool { + if len(status_filter) == 0 { + return true + } + for _, f := range status_filter { + switch f { + case pbDD.Status_ONLINE: + if isOnline { + return true + } + case pbDD.Status_OFFLINE: + if !isOnline { + return true + } + } + } + return false +} + +type DeviceDirectory struct { + projection *Projection + userDeviceIds strings.Set +} + +// NewDeviceDirectory creates new device directory. +func NewDeviceDirectory(projection *Projection, deviceIds []string) *DeviceDirectory { + mapDeviceIds := make(strings.Set) + mapDeviceIds.Add(deviceIds...) + + return &DeviceDirectory{projection: projection, userDeviceIds: mapDeviceIds} +} + +func decodeContent(content *pbRA.Content, v interface{}) error { + if content == nil { + return fmt.Errorf("cannot parse empty content") + } + + var decoder func([]byte, interface{}) error + + switch content.ContentType { + case coap.AppCBOR.String(), coap.AppOcfCbor.String(): + decoder = cbor.Decode + case coap.AppJSON.String(): + decoder = json.Decode + default: + return fmt.Errorf("unsupported content type: %v", content.ContentType) + } + + return decoder(content.Data, v) +} + +type Device struct { + pbDD.Device + cloudStateUpdated bool +} + +func updateDevice(dev Device, resource *resourceCtx) (Device, error) { + cloudResourceTypes := make(strings.Set) + cloudResourceTypes.Add(cloud.StatusResourceTypes...) + + switch { + case resource.snapshot.Resource.Href == "/oic/d": + var devContent pbDD.Resource + err := decodeContent(resource.snapshot.GetLatestResourceChange().GetContent(), &devContent) + if err != nil { + return dev, err + } + dev.Resource = &devContent + dev.Id = resource.snapshot.GroupId() + case cloudResourceTypes.HasOneOf(resource.snapshot.Resource.ResourceTypes...): + var cloudStatus cloud.Status + err := decodeContent(resource.snapshot.GetLatestResourceChange().GetContent(), &cloudStatus) + if err != nil { + return dev, err + } + dev.IsOnline = cloudStatus.Online + dev.cloudStateUpdated = true + } + return dev, nil +} + +func filterDevicesByStatus(resourceValues map[string]map[string]*resourceCtx, req *pbDD.GetDevicesRequest) ([]Device, error) { + devices := make([]Device, 0, len(resourceValues)) + for deviceID, resources := range resourceValues { + var device Device + for _, resource := range resources { + dev, err := updateDevice(device, resource) + if err != nil { + return nil, fmt.Errorf("cannot process device resources: %w", err) + } + device = dev + } + if device.Resource == nil { + device.Id = deviceID + } + if !device.cloudStateUpdated { + continue + } + if hasMatchingStatus(device.IsOnline, req.StatusFilter) { + devices = append(devices, device) + } + } + return devices, nil +} + +// GetDevices provides list state of devices. +func (dd *DeviceDirectory) GetDevices(ctx context.Context, req *pbDD.GetDevicesRequest, responseHandler func(*pbDD.Device) error) (statusCode codes.Code, err error) { + deviceIds := filterDevices(dd.userDeviceIds, req.DeviceIdsFilter) + if len(deviceIds) == 0 { + err = fmt.Errorf("not found") + statusCode = codes.NotFound + return + } + + typeFilter := make(strings.Set) + typeFilter.Add(req.TypeFilter...) + resourceIdsFilter := make(strings.Set) + for deviceID := range deviceIds { + resourceIdsFilter.Add(cqrs.MakeResourceId(deviceID, "/oic/d")) + resourceIdsFilter.Add(cqrs.MakeResourceId(deviceID, cloud.StatusHref)) + } + + resourceValues, err := dd.projection.GetResourceCtxs(ctx, resourceIdsFilter, typeFilter, deviceIds) + if err != nil { + err = fmt.Errorf("cannot get resource links by device ids: %w", err) + statusCode = codes.Internal + return + } + + devices, err := filterDevicesByStatus(resourceValues, req) + if err != nil { + statusCode = codes.Internal + return + } + + if len(devices) == 0 { + err = fmt.Errorf("not found") + statusCode = codes.NotFound + return + } + + for _, device := range devices { + dev := device + if err = responseHandler(&dev.Device); err != nil { + err = fmt.Errorf("cannot retrieve resources values: %w", err) + statusCode = codes.Canceled + return + } + } + + statusCode = codes.OK + return +} diff --git a/resource-directory/service/deviceDirectory_test.go b/resource-directory/service/deviceDirectory_test.go new file mode 100644 index 000000000..a740d2b30 --- /dev/null +++ b/resource-directory/service/deviceDirectory_test.go @@ -0,0 +1,306 @@ +package service + +import ( + "context" + "log" + "testing" + "time" + + "github.com/go-ocf/sdk/schema/cloud" + "github.com/kelseyhightower/envconfig" + "github.com/panjf2000/ants" + + cbor "github.com/go-ocf/kit/codec/cbor" + "github.com/go-ocf/kit/security/certManager" + + coap "github.com/go-ocf/go-coap" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/cloud/resource-aggregate/cqrs" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventbus/nats" + mockEventStore "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/test" + mockEvents "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/test" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + pbDD "github.com/go-ocf/cloud/resource-directory/pb/device-directory" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" +) + +func TestDeviceDirectory_GetDevices(t *testing.T) { + type args struct { + request pbDD.GetDevicesRequest + } + tests := []struct { + name string + args args + wantResponse map[string]*pbDD.Device + wantStatusCode codes.Code + wantErr bool + }{ + { + name: "project_ONLINE", + args: args{ + request: pbDD.GetDevicesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + StatusFilter: []pbDD.Status{pbDD.Status_ONLINE}, + }, + }, + wantStatusCode: codes.OK, + wantResponse: map[string]*pbDD.Device{ + ddResource2.Resource.DeviceId: &pbDD.Device{ + Id: ddResource2.Resource.DeviceId, + Resource: testMakeDeviceResouceProtobuf(ddResource2.Resource.DeviceId, deviceResourceTypes), + IsOnline: true, + }, + ddResource4.Resource.DeviceId: &pbDD.Device{ + Id: ddResource4.Resource.DeviceId, + IsOnline: true, + }, + }, + }, + + { + name: "project_OFFLINE", + args: args{ + request: pbDD.GetDevicesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + StatusFilter: []pbDD.Status{pbDD.Status_OFFLINE}, + }, + }, + wantStatusCode: codes.OK, + wantResponse: map[string]*pbDD.Device{ + ddResource1.Resource.DeviceId: &pbDD.Device{ + Id: ddResource1.Resource.DeviceId, + Resource: testMakeDeviceResouceProtobuf(ddResource1.Resource.DeviceId, deviceResourceTypes), + IsOnline: false, + }, + }, + }, + { + name: "project_ONLINE_OFFLINE", + args: args{ + request: pbDD.GetDevicesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + }, + }, + wantStatusCode: codes.OK, + wantResponse: map[string]*pbDD.Device{ + ddResource1.Resource.DeviceId: &pbDD.Device{ + Id: ddResource1.Resource.DeviceId, + Resource: testMakeDeviceResouceProtobuf(ddResource1.Resource.DeviceId, deviceResourceTypes), + IsOnline: false, + }, + ddResource2.Resource.DeviceId: &pbDD.Device{ + Id: ddResource2.Resource.DeviceId, + Resource: testMakeDeviceResouceProtobuf(ddResource2.Resource.DeviceId, deviceResourceTypes), + IsOnline: true, + }, + ddResource4.Resource.DeviceId: &pbDD.Device{ + Id: ddResource4.Resource.DeviceId, + IsOnline: true, + }, + }, + }, + { + name: "project_type_filter", + args: args{ + request: pbDD.GetDevicesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + TypeFilter: []string{"customType"}, + }, + }, + wantStatusCode: codes.NotFound, + wantErr: true, + }, + { + name: "project_one_device", + args: args{ + request: pbDD.GetDevicesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + DeviceIdsFilter: []string{ddResource1.Resource.DeviceId}, + }, + }, + wantStatusCode: codes.OK, + wantResponse: map[string]*pbDD.Device{ + ddResource1.Resource.DeviceId: &pbDD.Device{ + Id: ddResource1.Resource.DeviceId, + Resource: testMakeDeviceResouceProtobuf(ddResource1.Resource.DeviceId, deviceResourceTypes), + IsOnline: false, + }, + }, + }, + } + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + defer dialCertManager.Close() + tlsConfig := dialCertManager.GetClientTLSConfig() + + pool, err := ants.NewPool(1) + require.NoError(t, err) + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + require.NoError(t, err) + resourceSubscriber, err := nats.NewSubscriber(natsCfg, pool.Submit, func(err error) { require.NoError(t, err) }, nats.WithTLS(&tlsConfig)) + require.NoError(t, err) + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + resourceProjection, err := NewProjection(ctx, "test", testCreateResourceDeviceEventstores(), resourceSubscriber, time.Second) + require.NoError(t, err) + + rd := NewDeviceDirectory(resourceProjection, []string{ + ddResource0.DeviceId, + ddResource1.DeviceId, + ddResource2.DeviceId, + ddResource4.DeviceId, + }) + + for _, tt := range tests { + fn := func(t *testing.T) { + var got map[string]*pbDD.Device + statusCode, err := rd.GetDevices(context.Background(), &tt.args.request, func(device *pbDD.Device) error { + if got == nil { + got = make(map[string]*pbDD.Device) + } + got[device.Id] = device + return nil + }) + if tt.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + require.Equal(t, tt.wantStatusCode, statusCode) + require.Equal(t, tt.wantResponse, got) + } + t.Run(tt.name, fn) + } +} + +type testGeneratePublishEvent struct { + version uint64 + ResourceContent +} + +type testGenerateUnpublishEvent struct { + version uint64 + id string + deviceID string +} + +type testGenerateUnknownEvent struct { + version uint64 + id string +} + +func testMakeDeviceResource(deviceId string, href string, types []string) pbRA.Resource { + return pbRA.Resource{ + Id: cqrs.MakeResourceId(deviceId, href), + DeviceId: deviceId, + Href: href, + ResourceTypes: types, + } +} + +func testMakeDeviceResouceProtobuf(deviceId string, types []string) *pbDD.Resource { + return &pbDD.Resource{ + ResourceTypes: types, + Name: "Name." + deviceId, + ManufacturerName: []*pbDD.LocalizedString{ + &pbDD.LocalizedString{ + Language: "en", + Value: "test device resource", + }, + &pbDD.LocalizedString{ + Language: "sk", + Value: "testovaci prostriedok pre zariadenie", + }, + }, + ModelNumber: "ModelNumber." + deviceId, + } +} + +var deviceResourceTypes = []string{"oic.wk.d", "x.test.d"} + +func testMakeDeviceResourceContent(deviceId string) pbRA.Content { + dr := testMakeDeviceResouceProtobuf(deviceId, deviceResourceTypes) + + d, err := cbor.Encode(dr) + if err != nil { + log.Fatalf("cannot decode content: %v", err) + } + + return pbRA.Content{ + Data: d, + ContentType: coap.AppCBOR.String(), + } +} + +func testMakeCloudResourceContent(deviceId string, online bool) pbRA.Content { + s := cloud.Status{ + ResourceTypes: cloud.StatusResourceTypes, + Interfaces: cloud.StatusInterfaces, + Online: online, + } + d, err := cbor.Encode(s) + if err != nil { + log.Fatalf("cannot decode content: %v", err) + } + + return pbRA.Content{ + Data: d, + ContentType: coap.AppCBOR.String(), + } +} + +func makeTestDeviceResourceContent(deviceId string) ResourceContent { + return ResourceContent{ + Resource: testMakeDeviceResource(deviceId, "/oic/d", []string{"oic.wk.d", "x.test.d"}), + Content: testMakeDeviceResourceContent(deviceId), + } +} + +func makeTestCloudResourceContent(deviceId string, online bool) ResourceContent { + return ResourceContent{ + Resource: testMakeDeviceResource(deviceId, cloud.StatusHref, cloud.StatusResourceTypes), + Content: testMakeCloudResourceContent(deviceId, online), + } +} + +var ddResource0 = makeTestDeviceResourceContent("0") + +var ddResource1 = makeTestDeviceResourceContent("1") +var ddResource1Cloud = makeTestCloudResourceContent("1", false) + +var ddResource2 = makeTestDeviceResourceContent("2") +var ddResource2Cloud = makeTestCloudResourceContent("2", true) + +var ddResource4 = makeTestDeviceResourceContent("4") +var ddResource4Cloud = makeTestCloudResourceContent("4", true) + +func testCreateResourceDeviceEventstores() (resourceEventStore *mockEventStore.MockEventStore) { + resourceEventStore = mockEventStore.NewMockEventStore() + + //without cloud state + resourceEventStore.Append(ddResource0.DeviceId, ddResource0.Id, mockEvents.MakeResourcePublishedEvent(ddResource0.Resource, cqrs.MakeEventMeta("a", 0, 0))) + resourceEventStore.Append(ddResource0.DeviceId, ddResource0.Id, mockEvents.MakeResourceChangedEvent(ddResource0.Id, ddResource0.DeviceId, ddResource0.Content, cqrs.MakeEventMeta("a", 0, 1))) + + resourceEventStore.Append(ddResource1.DeviceId, ddResource1.Id, mockEvents.MakeResourcePublishedEvent(ddResource1.Resource, cqrs.MakeEventMeta("a", 0, 0))) + resourceEventStore.Append(ddResource1.DeviceId, ddResource1.Id, mockEvents.MakeResourceChangedEvent(ddResource1.Id, ddResource1.DeviceId, ddResource1.Content, cqrs.MakeEventMeta("a", 0, 1))) + resourceEventStore.Append(ddResource1Cloud.DeviceId, ddResource1Cloud.Id, mockEvents.MakeResourcePublishedEvent(ddResource1Cloud.Resource, cqrs.MakeEventMeta("a", 0, 0))) + resourceEventStore.Append(ddResource1Cloud.DeviceId, ddResource1Cloud.Id, mockEvents.MakeResourceChangedEvent(ddResource1Cloud.Id, ddResource1Cloud.DeviceId, ddResource1Cloud.Content, cqrs.MakeEventMeta("a", 0, 1))) + + //with cloud state - online + resourceEventStore.Append(ddResource2.DeviceId, ddResource2.Id, mockEvents.MakeResourcePublishedEvent(ddResource2.Resource, cqrs.MakeEventMeta("a", 0, 0))) + resourceEventStore.Append(ddResource2.DeviceId, ddResource2.Id, mockEvents.MakeResourceChangedEvent(ddResource2.Id, ddResource2.DeviceId, ddResource2.Content, cqrs.MakeEventMeta("a", 0, 1))) + resourceEventStore.Append(ddResource2Cloud.DeviceId, ddResource2Cloud.Id, mockEvents.MakeResourcePublishedEvent(ddResource2Cloud.Resource, cqrs.MakeEventMeta("a", 0, 0))) + resourceEventStore.Append(ddResource2Cloud.DeviceId, ddResource2Cloud.Id, mockEvents.MakeResourceChangedEvent(ddResource2Cloud.Id, ddResource2Cloud.DeviceId, ddResource2Cloud.Content, cqrs.MakeEventMeta("a", 0, 1))) + + //without device resource + resourceEventStore.Append(ddResource4Cloud.DeviceId, ddResource4Cloud.Id, mockEvents.MakeResourcePublishedEvent(ddResource4Cloud.Resource, cqrs.MakeEventMeta("a", 0, 0))) + resourceEventStore.Append(ddResource4Cloud.DeviceId, ddResource4Cloud.Id, mockEvents.MakeResourceChangedEvent(ddResource4Cloud.Id, ddResource4Cloud.DeviceId, ddResource4Cloud.Content, cqrs.MakeEventMeta("a", 0, 1))) + + return resourceEventStore +} diff --git a/resource-directory/service/grpcApi.go b/resource-directory/service/grpcApi.go new file mode 100644 index 000000000..16ef0052f --- /dev/null +++ b/resource-directory/service/grpcApi.go @@ -0,0 +1,140 @@ +package service + +import ( + "context" + "fmt" + "io" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + "github.com/go-ocf/kit/log" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbDD "github.com/go-ocf/cloud/resource-directory/pb/device-directory" + pbRD "github.com/go-ocf/cloud/resource-directory/pb/resource-directory" + pbRS "github.com/go-ocf/cloud/resource-directory/pb/resource-shadow" + grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +//RequestHandler for handling incoming request +type RequestHandler struct { + authClient pbAS.AuthorizationServiceClient + projection *Projection +} + +//NewRequestHandler factory for new RequestHandler +func NewRequestHandler(authClient pbAS.AuthorizationServiceClient, projection *Projection) *RequestHandler { + return &RequestHandler{ + authClient: authClient, + projection: projection, + } +} + +func logAndReturnError(err error) error { + log.Errorf("%v", err) + return err +} + +func (r *RequestHandler) GetUsersDevices(ctx context.Context, authCtx *pbCQRS.AuthorizationContext, deviceIdsFilter []string) ([]string, error) { + userIdsFilter := []string(nil) + if authCtx.GetUserId() != "" { + userIdsFilter = []string{authCtx.GetUserId() } + } + token, err := grpc_auth.AuthFromMD(ctx, "bearer") + if err != nil { + return nil, status.Errorf(codes.Unauthenticated, "cannot get users devices: %v", err) + } + getUserDevicesClient, err := r.authClient.GetUserDevices(kitNetGrpc.CtxWithToken(ctx, token), &pbAS.GetUserDevicesRequest{ + UserIdsFilter: userIdsFilter, + DeviceIdsFilter: deviceIdsFilter, + }) + if err != nil { + return nil, status.Errorf(status.Convert(err).Code(), "cannot get users devices: %v", err) + } + userDevices := make([]string, 0, 32) + for { + userDevice, err := getUserDevicesClient.Recv() + if err == io.EOF { + break + } + if err != nil { + return nil, status.Errorf(status.Convert(err).Code(), "cannot get users devices: %v", err) + } + if userDevice == nil { + continue + } + userDevices = append(userDevices, userDevice.DeviceId) + } + return userDevices, nil +} + +func (r *RequestHandler) RetrieveResourcesValues(req *pbRS.RetrieveResourcesValuesRequest, srv pbRS.ResourceShadow_RetrieveResourcesValuesServer) error { + deviceIds, err := r.GetUsersDevices(srv.Context(), req.GetAuthorizationContext(), req.DeviceIdsFilter) + if err != nil { + return logAndReturnError(status.Errorf(status.Convert(err).Code(), "cannot retrieve resources values: %v", err)) + } + if len(deviceIds) == 0 { + return logAndReturnError(status.Errorf(codes.NotFound, "cannot retrieve resources values: not found")) + } + + rd := NewResourceShadow(r.projection, deviceIds) + + statusCode, err := rd.RetrieveResourcesValues(srv.Context(), req, func(resourceLink *pbRS.ResourceValue) error { + err := srv.Send(resourceLink) + if err != nil { + return fmt.Errorf("cannot send resource value to client: %v", err) + } + return nil + }) + if err != nil { + return logAndReturnError(status.Errorf(statusCode, "cannot retrieve resources values: %v", err)) + } + return nil +} + +func (r *RequestHandler) GetResourceLinks(req *pbRD.GetResourceLinksRequest, srv pbRD.ResourceDirectory_GetResourceLinksServer) error { + deviceIds, err := r.GetUsersDevices(srv.Context(), req.GetAuthorizationContext(), req.DeviceIdsFilter) + if err != nil { + return logAndReturnError(status.Errorf(status.Convert(err).Code(), "cannot retrieve resources values: %v", err)) + } + if len(deviceIds) == 0 { + return logAndReturnError(status.Errorf(codes.NotFound, "cannot retrieve resources values: not found")) + } + + rd := NewResourceDirectory(r.projection, deviceIds) + + code, err := rd.GetResourceLinks(srv.Context(), req, func(resourceLink *pbRD.ResourceLink) error { + err := srv.Send(resourceLink) + if err != nil { + return fmt.Errorf("cannot send resource link to client: %v", err) + } + return nil + }) + + if err != nil { + return logAndReturnError(status.Errorf(code, "cannot get resource links: %v", err)) + } + return nil +} + +func (r *RequestHandler) GetDevices(req *pbDD.GetDevicesRequest, srv pbDD.DeviceDirectory_GetDevicesServer) error { + deviceIds, err := r.GetUsersDevices(srv.Context(), req.GetAuthorizationContext(), req.DeviceIdsFilter) + if err != nil { + return logAndReturnError(status.Errorf(status.Convert(err).Code(), "cannot get devices contents: %v", err)) + } + + rd := NewDeviceDirectory(r.projection, deviceIds) + + code, err := rd.GetDevices(srv.Context(), req, func(device *pbDD.Device) error { + err := srv.Send(device) + if err != nil { + return fmt.Errorf("cannot send device to client: %v", err) + } + return nil + }) + if err != nil { + return logAndReturnError(status.Errorf(code, "cannot get devices contents: %v", err)) + } + return err +} diff --git a/resource-directory/service/grpcApi_test.go b/resource-directory/service/grpcApi_test.go new file mode 100644 index 000000000..07d509f3a --- /dev/null +++ b/resource-directory/service/grpcApi_test.go @@ -0,0 +1,538 @@ +package service + +import ( + "context" + "io" + "testing" + "time" + + "github.com/kelseyhightower/envconfig" + "github.com/stretchr/testify/require" + + pbAS "github.com/go-ocf/cloud/authorization/pb" + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/kit/security/certManager" + "github.com/go-ocf/cloud/resource-aggregate/cqrs" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventbus/nats" + mockEventStore "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/test" + mockEvents "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventstore/test" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" + pbDD "github.com/go-ocf/cloud/resource-directory/pb/device-directory" + pbRD "github.com/go-ocf/cloud/resource-directory/pb/resource-directory" + pbRS "github.com/go-ocf/cloud/resource-directory/pb/resource-shadow" + "github.com/panjf2000/ants" + "github.com/stretchr/testify/assert" + "google.golang.org/grpc" +) + +func TestRequestHandler_RetrieveResourcesValues(t *testing.T) { + type args struct { + req *pbRS.RetrieveResourcesValuesRequest + } + tests := []struct { + name string + args args + wantErr bool + want map[string]*pbRS.ResourceValue + }{ + { + name: "list unauthorized device", + args: args{ + req: &pbRS.RetrieveResourcesValuesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + DeviceIdsFilter: []string{Resource0.DeviceId}, + }, + }, + wantErr: true, + }, + { + name: "filter by resource Id", + args: args{ + req: &pbRS.RetrieveResourcesValuesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + ResourceIdsFilter: []string{Resource1.Id, Resource2.Id}, + }, + }, + want: map[string]*pbRS.ResourceValue{ + Resource1.Id: &pbRS.ResourceValue{ + ResourceId: Resource1.Id, + DeviceId: Resource1.DeviceId, + Href: Resource1.Href, + Content: &Resource1.Content, + Types: Resource1.ResourceTypes, + }, + Resource2.Id: &pbRS.ResourceValue{ + ResourceId: Resource2.Id, + DeviceId: Resource2.DeviceId, + Href: Resource2.Href, + Content: &Resource2.Content, + Types: Resource2.ResourceTypes, + }, + }, + }, + { + name: "filter by device Id", + args: args{ + req: &pbRS.RetrieveResourcesValuesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + DeviceIdsFilter: []string{Resource1.DeviceId}, + }, + }, + want: map[string]*pbRS.ResourceValue{ + Resource1.Id: &pbRS.ResourceValue{ + ResourceId: Resource1.Id, + DeviceId: Resource1.DeviceId, + Href: Resource1.Href, + Content: &Resource1.Content, + Types: Resource1.ResourceTypes, + }, + Resource3.Id: &pbRS.ResourceValue{ + ResourceId: Resource3.Id, + DeviceId: Resource3.DeviceId, + Href: Resource3.Href, + Content: &Resource3.Content, + Types: Resource3.ResourceTypes, + }, + }, + }, + { + name: "filter by type", + args: args{ + req: &pbRS.RetrieveResourcesValuesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + TypeFilter: []string{Resource2.ResourceTypes[0]}, + }, + }, + want: map[string]*pbRS.ResourceValue{ + Resource1.Id: &pbRS.ResourceValue{ + ResourceId: Resource1.Id, + DeviceId: Resource1.DeviceId, + Href: Resource1.Href, + Content: &Resource1.Content, + Types: Resource1.ResourceTypes, + }, + Resource2.Id: &pbRS.ResourceValue{ + ResourceId: Resource2.Id, + DeviceId: Resource2.DeviceId, + Href: Resource2.Href, + Content: &Resource2.Content, + Types: Resource2.ResourceTypes, + }, + }, + }, + { + name: "filter by device Id and type", + args: args{ + req: &pbRS.RetrieveResourcesValuesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + DeviceIdsFilter: []string{Resource1.DeviceId}, + TypeFilter: []string{Resource1.ResourceTypes[0]}, + }, + }, + want: map[string]*pbRS.ResourceValue{ + Resource1.Id: &pbRS.ResourceValue{ + ResourceId: Resource1.Id, + DeviceId: Resource1.DeviceId, + Href: Resource1.Href, + Content: &Resource1.Content, + Types: Resource1.ResourceTypes, + }, + }, + }, + { + name: "list all resources of user", + args: args{ + req: &pbRS.RetrieveResourcesValuesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + }, + }, + want: map[string]*pbRS.ResourceValue{ + Resource1.Id: &pbRS.ResourceValue{ + ResourceId: Resource1.Id, + DeviceId: Resource1.DeviceId, + Href: Resource1.Href, + Content: &Resource1.Content, + Types: Resource1.ResourceTypes, + }, + Resource2.Id: &pbRS.ResourceValue{ + ResourceId: Resource2.Id, + DeviceId: Resource2.DeviceId, + Href: Resource2.Href, + Content: &Resource2.Content, + Types: Resource2.ResourceTypes, + }, + Resource3.Id: &pbRS.ResourceValue{ + ResourceId: Resource3.Id, + DeviceId: Resource3.DeviceId, + Href: Resource3.Href, + Content: &Resource3.Content, + Types: Resource3.ResourceTypes, + }, + }, + }, + } + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + defer dialCertManager.Close() + tlsConfig := dialCertManager.GetClientTLSConfig() + + pool, err := ants.NewPool(1) + require.NoError(t, err) + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + require.NoError(t, err) + resourceSubscriber, err := nats.NewSubscriber(natsCfg, pool.Submit, func(err error) { require.NoError(t, err) }, nats.WithTLS(&tlsConfig)) + require.NoError(t, err) + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + resourceProjection, err := NewProjection(ctx, "test", testCreateEventstore(), resourceSubscriber, time.Second) + require.NoError(t, err) + r := NewRequestHandler(mockAuthorizationServiceClient{}, resourceProjection) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + srv := newMockRetrieveResourcesValues(ctx) + err := r.RetrieveResourcesValues(tt.args.req, srv) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.want, srv.resourceValues) + }) + } +} + +func TestRequestHandler_GetResourceLinks(t *testing.T) { + type args struct { + request pbRD.GetResourceLinksRequest + } + tests := []struct { + name string + args args + want map[string]*pbRD.ResourceLink + wantErr bool + }{ + { + name: "list unauthorized device", + args: args{ + request: pbRD.GetResourceLinksRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + DeviceIdsFilter: []string{Resource0.DeviceId}, + }, + }, + wantErr: true, + }, + { + name: "list one device - filter by device Id", + args: args{ + request: pbRD.GetResourceLinksRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + DeviceIdsFilter: []string{Resource1.DeviceId}, + }, + }, + want: map[string]*pbRD.ResourceLink{ + Resource1.Id: &pbRD.ResourceLink{ + Resource: &Resource1.Resource, + }, + Resource3.Id: &pbRD.ResourceLink{ + Resource: &Resource3.Resource, + }, + }, + }, + { + name: "list all devices", + args: args{ + request: pbRD.GetResourceLinksRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + }, + }, + want: map[string]*pbRD.ResourceLink{ + Resource1.Id: &pbRD.ResourceLink{ + Resource: &Resource1.Resource, + }, + Resource3.Id: &pbRD.ResourceLink{ + Resource: &Resource3.Resource, + }, + Resource2.Id: &pbRD.ResourceLink{ + Resource: &Resource2.Resource, + }, + }, + }, + { + name: "list one device - filter by resource type", + args: args{ + request: pbRD.GetResourceLinksRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + TypeFilter: []string{Resource2.ResourceTypes[0]}, + }, + }, + want: map[string]*pbRD.ResourceLink{ + Resource1.Id: &pbRD.ResourceLink{ + Resource: &Resource1.Resource, + }, + Resource2.Id: &pbRD.ResourceLink{ + Resource: &Resource2.Resource, + }, + }, + }, + { + name: "list one device - combination", + args: args{ + request: pbRD.GetResourceLinksRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + DeviceIdsFilter: []string{Resource1.Resource.DeviceId}, + TypeFilter: []string{Resource3.Resource.ResourceTypes[1]}, + }, + }, + want: map[string]*pbRD.ResourceLink{ + Resource3.Id: &pbRD.ResourceLink{ + Resource: &Resource3.Resource, + }, + }, + }, + } + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + defer dialCertManager.Close() + tlsConfig := dialCertManager.GetClientTLSConfig() + + pool, err := ants.NewPool(1) + require.NoError(t, err) + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + require.NoError(t, err) + resourceSubscriber, err := nats.NewSubscriber(natsCfg, pool.Submit, func(err error) { require.NoError(t, err) }, nats.WithTLS(&tlsConfig)) + require.NoError(t, err) + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + resourceProjection, err := NewProjection(ctx, "test", testCreateEventstore(), resourceSubscriber, time.Second) + require.NoError(t, err) + r := NewRequestHandler(mockAuthorizationServiceClient{}, resourceProjection) + + for _, tt := range tests { + fn := func(t *testing.T) { + got := newMockResourceDirectoryGetResourceLinks(ctx) + err := r.GetResourceLinks(&tt.args.request, got) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.want, got.resourceLinks) + } + t.Run(tt.name, fn) + } +} + +func TestRequestHandler_GetDevices(t *testing.T) { + type args struct { + request pbDD.GetDevicesRequest + } + tests := []struct { + name string + args args + want map[string]*pbDD.Device + wantErr bool + }{ + { + name: "list ONLINE devices", + args: args{ + request: pbDD.GetDevicesRequest{ + StatusFilter: []pbDD.Status{pbDD.Status_ONLINE}, + }, + }, + want: map[string]*pbDD.Device{ + ddResource2.Resource.DeviceId: &pbDD.Device{ + Id: Resource2.Resource.DeviceId, + Resource: testMakeDeviceResouceProtobuf(Resource2.Resource.DeviceId, deviceResourceTypes), + IsOnline: true, + }, + }, + }, + } + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + defer dialCertManager.Close() + tlsConfig := dialCertManager.GetClientTLSConfig() + + pool, err := ants.NewPool(1) + require.NoError(t, err) + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + require.NoError(t, err) + resourceSubscriber, err := nats.NewSubscriber(natsCfg, pool.Submit, func(err error) { require.NoError(t, err) }, nats.WithTLS(&tlsConfig)) + require.NoError(t, err) + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + resourceProjection, err := NewProjection(ctx, "test", testCreateResourceDeviceEventstores(), resourceSubscriber, time.Second) + require.NoError(t, err) + r := NewRequestHandler(mockAuthorizationServiceClient{}, resourceProjection) + + for _, rr := range tests { + fn := func(t *testing.T) { + got := newMockDeviceDirectoryGetDevicesServer(ctx) + err := r.GetDevices(&rr.args.request, got) + if rr.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, rr.want, got.devices) + } + t.Run(rr.name, fn) + } +} + +type ResourceContent struct { + pbRA.Resource + pbRA.Content +} + +var Resource0 = ResourceContent{ + pbRA.Resource{Id: "a", DeviceId: "0", ResourceTypes: []string{"t0"}}, + pbRA.Content{Data: []byte("0.a")}, +} + +var Resource1 = ResourceContent{ + pbRA.Resource{Id: "b", DeviceId: "1", ResourceTypes: []string{"t1", "t2"}}, + pbRA.Content{Data: []byte("1.b")}, +} + +var Resource2 = ResourceContent{ + pbRA.Resource{Id: "c", DeviceId: "2", ResourceTypes: []string{"t1"}}, + pbRA.Content{Data: []byte("2.c")}, +} + +var Resource3 = ResourceContent{ + pbRA.Resource{Id: "d", DeviceId: "1", ResourceTypes: []string{"t3", "t8"}}, + pbRA.Content{Data: []byte("1.d")}, +} + +func testCreateEventstore() *mockEventStore.MockEventStore { + store := mockEventStore.NewMockEventStore() + store.Append(Resource0.DeviceId, Resource0.Id, mockEvents.MakeResourcePublishedEvent(Resource0.Resource, cqrs.MakeEventMeta("a", 0, 0))) + store.Append(Resource0.DeviceId, Resource0.Id, mockEvents.MakeResourceChangedEvent(Resource0.Id, Resource0.DeviceId, Resource0.Content, cqrs.MakeEventMeta("a", 0, 1))) + store.Append(Resource1.DeviceId, Resource1.Id, mockEvents.MakeResourcePublishedEvent(Resource1.Resource, cqrs.MakeEventMeta("a", 0, 0))) + store.Append(Resource1.DeviceId, Resource1.Id, mockEvents.MakeResourceChangedEvent(Resource1.Id, Resource1.DeviceId, Resource1.Content, cqrs.MakeEventMeta("a", 0, 1))) + store.Append(Resource2.DeviceId, Resource2.Id, mockEvents.MakeResourcePublishedEvent(Resource2.Resource, cqrs.MakeEventMeta("a", 0, 0))) + store.Append(Resource2.DeviceId, Resource2.Id, mockEvents.MakeResourceChangedEvent(Resource2.Id, Resource2.DeviceId, Resource2.Content, cqrs.MakeEventMeta("a", 0, 1))) + store.Append(Resource3.DeviceId, Resource3.Id, mockEvents.MakeResourcePublishedEvent(Resource3.Resource, cqrs.MakeEventMeta("a", 0, 0))) + store.Append(Resource3.DeviceId, Resource3.Id, mockEvents.MakeResourceChangedEvent(Resource3.Id, Resource3.DeviceId, Resource3.Content, cqrs.MakeEventMeta("a", 0, 1))) + return store +} + +type mockRetrieveResourcesValues struct { + resourceValues map[string]*pbRS.ResourceValue + ctx context.Context + grpc.ServerStream +} + +func newMockRetrieveResourcesValues(ctx context.Context) *mockRetrieveResourcesValues { + return &mockRetrieveResourcesValues{ + ctx: ctx, + } +} + +func (d *mockRetrieveResourcesValues) Send(r *pbRS.ResourceValue) error { + if d.resourceValues == nil { + d.resourceValues = make(map[string]*pbRS.ResourceValue) + } + d.resourceValues[r.ResourceId] = r + return nil +} + +func (d *mockRetrieveResourcesValues) Context() context.Context { + return d.ctx +} + +type mockAuthorizationServiceClient struct { + pbAS.AuthorizationServiceClient +} + +type mockGetUserDevicesClientClient struct { + resourceLink []*pbAS.UserDevice + i int + grpc.ClientStream +} + +func (r *mockGetUserDevicesClientClient) Recv() (*pbAS.UserDevice, error) { + if r.i >= len(r.resourceLink) { + return nil, io.EOF + } + res := r.resourceLink[r.i] + r.i++ + return res, nil +} + +func (c mockAuthorizationServiceClient) GetUserDevices(ctx context.Context, in *pbAS.GetUserDevicesRequest, opts ...grpc.CallOption) (pbAS.AuthorizationService_GetUserDevicesClient, error) { + deviceIds := []string{ /*Resource0.DeviceId,*/ Resource1.DeviceId, Resource2.DeviceId} + userDevices := make([]*pbAS.UserDevice, 0, 16) + for _, d := range deviceIds { + if len(in.UserIdsFilter) == 0 { + userDevices = append(userDevices, &pbAS.UserDevice{DeviceId: d, UserId: ""}) + } else { + for _, userID := range in.UserIdsFilter { + userDevices = append(userDevices, &pbAS.UserDevice{DeviceId: d, UserId: userID}) + } + } + } + return &mockGetUserDevicesClientClient{ + resourceLink: userDevices, + }, nil +} + +type mockResourceDirectoryGetResourceLinks struct { + resourceLinks map[string]*pbRD.ResourceLink + ctx context.Context + grpc.ServerStream +} + +func newMockResourceDirectoryGetResourceLinks(ctx context.Context) *mockResourceDirectoryGetResourceLinks { + return &mockResourceDirectoryGetResourceLinks{ + ctx: ctx, + } +} + +func (d *mockResourceDirectoryGetResourceLinks) Send(link *pbRD.ResourceLink) error { + if d.resourceLinks == nil { + d.resourceLinks = make(map[string]*pbRD.ResourceLink) + } + d.resourceLinks[link.Resource.Id] = link + return nil +} + +func (d *mockResourceDirectoryGetResourceLinks) Context() context.Context { + return d.ctx +} + +type mockDeviceDirectoryGetDevicesServer struct { + devices map[string]*pbDD.Device + ctx context.Context + grpc.ServerStream +} + +func newMockDeviceDirectoryGetDevicesServer(ctx context.Context) *mockDeviceDirectoryGetDevicesServer { + return &mockDeviceDirectoryGetDevicesServer{ + ctx: ctx, + } +} + +func (d *mockDeviceDirectoryGetDevicesServer) Send(device *pbDD.Device) error { + if d.devices == nil { + d.devices = make(map[string]*pbDD.Device) + } + d.devices[device.Id] = device + return nil +} + +func (d *mockDeviceDirectoryGetDevicesServer) Context() context.Context { + return d.ctx +} diff --git a/resource-directory/service/projection.go b/resource-directory/service/projection.go new file mode 100644 index 000000000..da9f8e27b --- /dev/null +++ b/resource-directory/service/projection.go @@ -0,0 +1,89 @@ +package service + +import ( + "context" + "fmt" + "time" + + "github.com/go-ocf/cqrs/eventbus" + "github.com/go-ocf/cqrs/eventstore" + "github.com/go-ocf/kit/strings" + projectionRA "github.com/go-ocf/cloud/resource-aggregate/cqrs/projection" + cache "github.com/patrickmn/go-cache" +) + +// hasMatchingType returns true for matching a resource type. +// An empty typeFilter matches all resource types. +func hasMatchingType(resourceTypes []string, typeFilter strings.Set) bool { + if len(typeFilter) == 0 { + return true + } + return typeFilter.HasOneOf(resourceTypes...) +} + +type Projection struct { + projection *projectionRA.Projection + cache *cache.Cache +} + +func NewProjection(ctx context.Context, name string, store eventstore.EventStore, subscriber eventbus.Subscriber, expiration time.Duration) (*Projection, error) { + projection, err := projectionRA.NewProjection(ctx, name, store, subscriber, NewResourceCtx()) + if err != nil { + return nil, fmt.Errorf("cannot create server: %v", err) + } + cache := cache.New(expiration, expiration) + cache.OnEvicted(func(deviceId string, _ interface{}) { + projection.Unregister(deviceId) + }) + return &Projection{projection: projection, cache: cache}, nil +} + +func (p *Projection) GetResourceCtxs(ctx context.Context, resourceIdsFilter, typeFilter, deviceIds strings.Set) (map[string]map[string]*resourceCtx, error) { + models := make([]eventstore.Model, 0, 32) + + for deviceId, _ := range deviceIds { + loaded, err := p.projection.Register(ctx, deviceId) + if err != nil { + return nil, fmt.Errorf("cannot register to projection %v", err) + } + if !loaded { + defer func() { + p.projection.Unregister(deviceId) + }() + + } + p.cache.Set(deviceId, loaded, cache.DefaultExpiration) + if len(resourceIdsFilter) > 0 { + for resourceId, _ := range resourceIdsFilter { + m := p.projection.Models(deviceId, resourceId) + if len(m) > 0 { + models = append(models, m...) + } + } + } else { + m := p.projection.Models(deviceId, "") + if len(m) > 0 { + models = append(models, m...) + } + } + } + + clonedModels := make(map[string]map[string]*resourceCtx) + for _, m := range models { + model := m.(*resourceCtx).Clone() + if !model.snapshot.IsPublished { + continue + } + if !hasMatchingType(model.snapshot.Resource.ResourceTypes, typeFilter) { + continue + } + resources, ok := clonedModels[model.snapshot.GroupId()] + if !ok { + resources = make(map[string]*resourceCtx) + clonedModels[model.snapshot.GroupId()] = resources + } + resources[model.snapshot.AggregateId()] = model + } + + return clonedModels, nil +} diff --git a/resource-directory/service/resourceCtx.go b/resource-directory/service/resourceCtx.go new file mode 100644 index 000000000..76aa9cba5 --- /dev/null +++ b/resource-directory/service/resourceCtx.go @@ -0,0 +1,55 @@ +package service + +import ( + "context" + "sync" + + "github.com/go-ocf/cqrs/event" + "github.com/go-ocf/cqrs/eventstore" + httpUtils "github.com/go-ocf/kit/net/http" + raEvents "github.com/go-ocf/cloud/resource-aggregate/cqrs/events" + pbRA "github.com/go-ocf/cloud/resource-aggregate/pb" +) + +type resourceCtx struct { + lock sync.Mutex + snapshot *raEvents.ResourceStateSnapshotTaken +} + +func NewResourceCtx() func(context.Context) (eventstore.Model, error) { + return func(context.Context) (eventstore.Model, error) { + return &resourceCtx{ + snapshot: raEvents.NewResourceStateSnapshotTaken(func(string, string) error { return nil }), + }, nil + } +} + +func (m *resourceCtx) cloneLocked() *resourceCtx { + ra := raEvents.NewResourceStateSnapshotTaken(func(string, string) error { return nil }) + ra.ResourceStateSnapshotTaken.LatestResourceChange = m.snapshot.LatestResourceChange + ra.ResourceStateSnapshotTaken.EventMetadata = m.snapshot.EventMetadata + ra.ResourceStateSnapshotTaken.Resource = m.snapshot.Resource + ra.ResourceStateSnapshotTaken.TimeToLive = m.snapshot.TimeToLive + ra.ResourceStateSnapshotTaken.IsPublished = m.snapshot.IsPublished + ra.ResourceStateSnapshotTaken.Id = m.snapshot.Id + return &resourceCtx{ + snapshot: ra, + } +} + +func (m *resourceCtx) Clone() *resourceCtx { + m.lock.Lock() + defer m.lock.Unlock() + + return m.cloneLocked() +} + +func (m *resourceCtx) Handle(ctx context.Context, iter event.Iter) error { + m.lock.Lock() + defer m.lock.Unlock() + return m.snapshot.Handle(ctx, iter) +} + +func (m *resourceCtx) SnapshotEventType() string { + return httpUtils.ProtobufContentType(&pbRA.ResourceStateSnapshotTaken{}) +} diff --git a/resource-directory/service/resourceDirectory.go b/resource-directory/service/resourceDirectory.go new file mode 100644 index 000000000..c845c5342 --- /dev/null +++ b/resource-directory/service/resourceDirectory.go @@ -0,0 +1,65 @@ +package service + +import ( + "context" + "fmt" + + "google.golang.org/grpc/codes" + + "github.com/go-ocf/kit/strings" + pbRD "github.com/go-ocf/cloud/resource-directory/pb/resource-directory" +) + +func toResourceLink(model *resourceCtx) pbRD.ResourceLink { + return pbRD.ResourceLink{Resource: model.snapshot.Resource} +} + +type ResourceDirectory struct { + projection *Projection + userDeviceIds strings.Set +} + +func NewResourceDirectory(projection *Projection, deviceIds []string) *ResourceDirectory { + mapDeviceIds := make(strings.Set) + mapDeviceIds.Add(deviceIds...) + + return &ResourceDirectory{projection: projection, userDeviceIds: mapDeviceIds} +} + +func (rd *ResourceDirectory) GetResourceLinks(ctx context.Context, in *pbRD.GetResourceLinksRequest, responseHandler func(*pbRD.ResourceLink) error) (statusCode codes.Code, err error) { + deviceIds := filterDevices(rd.userDeviceIds, in.DeviceIdsFilter) + if len(deviceIds) == 0 { + err = fmt.Errorf("not found") + statusCode = codes.NotFound + return + } + + typeFilter := make(strings.Set) + typeFilter.Add(in.TypeFilter...) + resourceIdsFilter := make(strings.Set) + + resourceValues, err := rd.projection.GetResourceCtxs(ctx, resourceIdsFilter, typeFilter, deviceIds) + if err != nil { + err = fmt.Errorf("cannot get resource links by device ids: %w", err) + statusCode = codes.Internal + return + } + if len(resourceValues) == 0 { + err = fmt.Errorf("not found") + statusCode = codes.NotFound + return + } + + for _, resources := range resourceValues { + for _, resource := range resources { + resourceLink := toResourceLink(resource) + if err = responseHandler(&resourceLink); err != nil { + err = fmt.Errorf("cannot handle response: %w", err) + statusCode = codes.Canceled + return + } + } + } + statusCode = codes.OK + return +} diff --git a/resource-directory/service/resourceDirectory_test.go b/resource-directory/service/resourceDirectory_test.go new file mode 100644 index 000000000..9d9251797 --- /dev/null +++ b/resource-directory/service/resourceDirectory_test.go @@ -0,0 +1,90 @@ +package service + +import ( + "context" + "testing" + "time" + + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/kit/security/certManager" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventbus/nats" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRD "github.com/go-ocf/cloud/resource-directory/pb/resource-directory" + "github.com/kelseyhightower/envconfig" + "github.com/panjf2000/ants" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" +) + +func TestResourceDirectory_GetResourceLinks(t *testing.T) { + type args struct { + request pbRD.GetResourceLinksRequest + } + test := []struct { + name string + args args + want map[string]*pbRD.ResourceLink + wantCode codes.Code + wantErr bool + }{ + { + name: "list one device - filter by device Id", + args: args{ + request: pbRD.GetResourceLinksRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + DeviceIdsFilter: []string{Resource1.DeviceId}, + }, + }, + want: map[string]*pbRD.ResourceLink{ + Resource1.Id: &pbRD.ResourceLink{ + Resource: &Resource1.Resource, + }, + Resource3.Id: &pbRD.ResourceLink{ + &Resource3.Resource, + }, + }, + }, + } + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + defer dialCertManager.Close() + tlsConfig := dialCertManager.GetClientTLSConfig() + + pool, err := ants.NewPool(1) + require.NoError(t, err) + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + require.NoError(t, err) + resourceSubscriber, err := nats.NewSubscriber(natsCfg, pool.Submit, func(err error) { require.NoError(t, err) }, nats.WithTLS(&tlsConfig)) + require.NoError(t, err) + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + resourceProjection, err := NewProjection(ctx, "test", testCreateEventstore(), resourceSubscriber, time.Second) + require.NoError(t, err) + + rd := NewResourceDirectory(resourceProjection, []string{ /*Resource0.DeviceId,*/ Resource1.DeviceId, Resource2.DeviceId}) + + for _, tt := range test { + fn := func(t *testing.T) { + var got map[string]*pbRD.ResourceLink + statusCode, err := rd.GetResourceLinks(ctx, &tt.args.request, func(resourceLink *pbRD.ResourceLink) error { + if got == nil { + got = make(map[string]*pbRD.ResourceLink) + } + got[resourceLink.Resource.Id] = resourceLink + return nil + }) + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.wantCode, statusCode) + assert.Equal(t, tt.want, got) + } + t.Run(tt.name, fn) + } +} diff --git a/resource-directory/service/resourceShadow.go b/resource-directory/service/resourceShadow.go new file mode 100644 index 000000000..584bc4c86 --- /dev/null +++ b/resource-directory/service/resourceShadow.go @@ -0,0 +1,87 @@ +package service + +import ( + "context" + "fmt" + + "github.com/go-ocf/kit/strings" + "google.golang.org/grpc/codes" + + pbRS "github.com/go-ocf/cloud/resource-directory/pb/resource-shadow" +) + +func toResourceValue(m *resourceCtx) pbRS.ResourceValue { + return pbRS.ResourceValue{ + ResourceId: m.snapshot.GetResource().GetId(), + DeviceId: m.snapshot.GetResource().GetDeviceId(), + Href: m.snapshot.GetResource().GetHref(), + Content: m.snapshot.GetLatestResourceChange().GetContent(), + Types: m.snapshot.GetResource().GetResourceTypes(), + Status: m.snapshot.GetLatestResourceChange().GetStatus(), + } +} + +type ResourceShadow struct { + projection *Projection + userDeviceIds strings.Set +} + +func NewResourceShadow(projection *Projection, deviceIds []string) *ResourceShadow { + mapDeviceIds := make(strings.Set) + mapDeviceIds.Add(deviceIds...) + + return &ResourceShadow{projection: projection, userDeviceIds: mapDeviceIds} +} + +// filterDevices returns filtered device ids that match filter. +// An empty deviceIdsFilter matches all device ids. +func filterDevices(deviceIds strings.Set, deviceIdsFilter []string) strings.Set { + if len(deviceIdsFilter) == 0 { + return deviceIds + } + result := make(strings.Set) + for _, deviceId := range deviceIdsFilter { + if deviceIds.HasOneOf(deviceId) { + result.Add(deviceId) + } + } + return result +} + +func (rd *ResourceShadow) RetrieveResourcesValues(ctx context.Context, req *pbRS.RetrieveResourcesValuesRequest, responseHandler func(*pbRS.ResourceValue) error) (statusCode codes.Code, err error) { + deviceIds := filterDevices(rd.userDeviceIds, req.DeviceIdsFilter) + if len(deviceIds) == 0 { + err = fmt.Errorf("not found") + statusCode = codes.NotFound + return + } + typeFilter := make(strings.Set) + typeFilter.Add(req.TypeFilter...) + resourceIdsFilter := make(strings.Set) + resourceIdsFilter.Add(req.ResourceIdsFilter...) + + resourceValues, err := rd.projection.GetResourceCtxs(ctx, resourceIdsFilter, typeFilter, deviceIds) + if err != nil { + err = fmt.Errorf("cannot retrieve resources values: %w", err) + statusCode = codes.Internal + return + } + if len(resourceValues) == 0 { + err = fmt.Errorf("not found") + statusCode = codes.NotFound + return + } + + for _, resources := range resourceValues { + for _, resource := range resources { + val := toResourceValue(resource) + if err = responseHandler(&val); err != nil { + err = fmt.Errorf("cannot retrieve resources values: %w", err) + statusCode = codes.Canceled + return + } + } + } + statusCode = codes.OK + return +} diff --git a/resource-directory/service/resourceShadow_test.go b/resource-directory/service/resourceShadow_test.go new file mode 100644 index 000000000..819ffa475 --- /dev/null +++ b/resource-directory/service/resourceShadow_test.go @@ -0,0 +1,218 @@ +package service + +import ( + "context" + "fmt" + "testing" + "time" + + kitNetGrpc "github.com/go-ocf/kit/net/grpc" + "github.com/go-ocf/kit/security/certManager" + "github.com/go-ocf/cloud/resource-aggregate/cqrs/eventbus/nats" + pbCQRS "github.com/go-ocf/cloud/resource-aggregate/pb" + pbRS "github.com/go-ocf/cloud/resource-directory/pb/resource-shadow" + "github.com/kelseyhightower/envconfig" + "github.com/panjf2000/ants" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" +) + +func TestResourceShadow_RetrieveResourcesValues(t *testing.T) { + type args struct { + req *pbRS.RetrieveResourcesValuesRequest + } + tests := []struct { + name string + args args + wantStatusCode codes.Code + wantErr bool + want map[string]*pbRS.ResourceValue + }{ + + { + name: "list unauthorized device", + args: args{ + req: &pbRS.RetrieveResourcesValuesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + DeviceIdsFilter: []string{Resource0.DeviceId}, + }, + }, + wantStatusCode: codes.NotFound, + wantErr: true, + }, + + { + name: "filter by resource Id", + args: args{ + req: &pbRS.RetrieveResourcesValuesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + ResourceIdsFilter: []string{Resource1.Id, Resource2.Id}, + }, + }, + want: map[string]*pbRS.ResourceValue{ + Resource1.Id: &pbRS.ResourceValue{ + ResourceId: Resource1.Id, + DeviceId: Resource1.DeviceId, + Href: Resource1.Href, + Content: &Resource1.Content, + Types: Resource1.ResourceTypes, + }, + Resource2.Id: &pbRS.ResourceValue{ + ResourceId: Resource2.Id, + DeviceId: Resource2.DeviceId, + Href: Resource2.Href, + Content: &Resource2.Content, + Types: Resource2.ResourceTypes, + }, + }, + }, + + { + name: "filter by device Id", + args: args{ + req: &pbRS.RetrieveResourcesValuesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + DeviceIdsFilter: []string{Resource1.DeviceId}, + }, + }, + want: map[string]*pbRS.ResourceValue{ + Resource1.Id: &pbRS.ResourceValue{ + ResourceId: Resource1.Id, + DeviceId: Resource1.DeviceId, + Href: Resource1.Href, + Content: &Resource1.Content, + Types: Resource1.ResourceTypes, + }, + Resource3.Id: &pbRS.ResourceValue{ + ResourceId: Resource3.Id, + DeviceId: Resource3.DeviceId, + Href: Resource3.Href, + Content: &Resource3.Content, + Types: Resource3.ResourceTypes, + }, + }, + }, + + { + name: "filter by type", + args: args{ + req: &pbRS.RetrieveResourcesValuesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + TypeFilter: []string{Resource2.ResourceTypes[0]}, + }, + }, + want: map[string]*pbRS.ResourceValue{ + Resource1.Id: &pbRS.ResourceValue{ + ResourceId: Resource1.Id, + DeviceId: Resource1.DeviceId, + Href: Resource1.Href, + Content: &Resource1.Content, + Types: Resource1.ResourceTypes, + }, + Resource2.Id: &pbRS.ResourceValue{ + ResourceId: Resource2.Id, + DeviceId: Resource2.DeviceId, + Href: Resource2.Href, + Content: &Resource2.Content, + Types: Resource2.ResourceTypes, + }, + }, + }, + + { + name: "filter by device Id and type", + args: args{ + req: &pbRS.RetrieveResourcesValuesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + DeviceIdsFilter: []string{Resource1.DeviceId}, + TypeFilter: []string{Resource1.ResourceTypes[0]}, + }, + }, + want: map[string]*pbRS.ResourceValue{ + Resource1.Id: &pbRS.ResourceValue{ + ResourceId: Resource1.Id, + DeviceId: Resource1.DeviceId, + Href: Resource1.Href, + Content: &Resource1.Content, + Types: Resource1.ResourceTypes, + }, + }, + }, + + { + name: "list all resources of user", + args: args{ + req: &pbRS.RetrieveResourcesValuesRequest{ + AuthorizationContext: &pbCQRS.AuthorizationContext{}, + }, + }, + want: map[string]*pbRS.ResourceValue{ + Resource1.Id: &pbRS.ResourceValue{ + ResourceId: Resource1.Id, + DeviceId: Resource1.DeviceId, + Href: Resource1.Href, + Content: &Resource1.Content, + Types: Resource1.ResourceTypes, + }, + Resource2.Id: &pbRS.ResourceValue{ + ResourceId: Resource2.Id, + DeviceId: Resource2.DeviceId, + Href: Resource2.Href, + Content: &Resource2.Content, + Types: Resource2.ResourceTypes, + }, + Resource3.Id: &pbRS.ResourceValue{ + ResourceId: Resource3.Id, + DeviceId: Resource3.DeviceId, + Href: Resource3.Href, + Content: &Resource3.Content, + Types: Resource3.ResourceTypes, + }, + }, + }, + } + var cmconfig certManager.Config + err := envconfig.Process("DIAL", &cmconfig) + assert.NoError(t, err) + dialCertManager, err := certManager.NewCertManager(cmconfig) + require.NoError(t, err) + defer dialCertManager.Close() + tlsConfig := dialCertManager.GetClientTLSConfig() + + pool, err := ants.NewPool(1) + require.NoError(t, err) + var natsCfg nats.Config + err = envconfig.Process("", &natsCfg) + require.NoError(t, err) + resourceSubscriber, err := nats.NewSubscriber(natsCfg, pool.Submit, func(err error) { require.NoError(t, err) }, nats.WithTLS(&tlsConfig)) + require.NoError(t, err) + ctx := kitNetGrpc.CtxWithIncomingToken(context.Background(), "b") + resourceProjection, err := NewProjection(ctx, "test", testCreateEventstore(), resourceSubscriber, time.Second) + require.NoError(t, err) + + rd := NewResourceShadow(resourceProjection, []string{ /*Resource0.DeviceId,*/ Resource1.DeviceId, Resource2.DeviceId}) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fmt.Println(tt.name) + var got map[string]*pbRS.ResourceValue + gotStatusCode, err := rd.RetrieveResourcesValues(context.Background(), tt.args.req, func(r *pbRS.ResourceValue) error { + if got == nil { + got = make(map[string]*pbRS.ResourceValue) + } + got[r.ResourceId] = r + return nil + }) + + if tt.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.wantStatusCode, gotStatusCode) + assert.Equal(t, tt.want, got) + got = nil + }) + } +} diff --git a/resource-directory/test/service/service.go b/resource-directory/test/service/service.go new file mode 100644 index 000000000..efeb79afb --- /dev/null +++ b/resource-directory/test/service/service.go @@ -0,0 +1,29 @@ +package service + +import ( + "sync" + "testing" + + "github.com/go-ocf/cloud/resource-directory/refImpl" + "github.com/stretchr/testify/require" +) + +func NewResourceDirectory(t *testing.T, cfg refImpl.Config) func() { + t.Log("NewResourceDirectory") + defer t.Log("NewResourceDirectory done") + s, err := refImpl.Init(cfg) + require.NoError(t, err) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + err := s.Serve() + require.NoError(t, err) + }() + + return func() { + s.Shutdown() + wg.Wait() + } +}