diff --git a/.github/workflows/build-publish.yaml b/.github/workflows/build-publish.yaml
index df982ef88..82815262d 100644
--- a/.github/workflows/build-publish.yaml
+++ b/.github/workflows/build-publish.yaml
@@ -109,6 +109,10 @@ jobs:
- name: cert-tool
directory: tools/cert-tool
file: tools/cert-tool/Dockerfile
+ - name: snippet-service
+ directory: snippet-service
+ file: .tmp/docker/snippet-service/Dockerfile
+ template-file: tools/docker/Dockerfile.in
uses: ./.github/workflows/build-publish-cfg.yaml
with:
name: ${{ matrix.name }}
diff --git a/Makefile b/Makefile
index 14ec3c7ee..d0b18fcb0 100644
--- a/Makefile
+++ b/Makefile
@@ -42,7 +42,7 @@ CERT_TOOL_SIGN_ALG ?= ECDSA-SHA256
CERT_TOOL_ELLIPTIC_CURVE ?= P256
CERT_TOOL_IMAGE = ghcr.io/plgd-dev/hub/cert-tool:vnext
-SUBDIRS := bundle certificate-authority cloud2cloud-connector cloud2cloud-gateway coap-gateway grpc-gateway resource-aggregate resource-directory http-gateway identity-store test/oauth-server tools/cert-tool
+SUBDIRS := bundle certificate-authority cloud2cloud-connector cloud2cloud-gateway coap-gateway grpc-gateway resource-aggregate resource-directory http-gateway identity-store snippet-service test/oauth-server tools/cert-tool
.PHONY: $(SUBDIRS) push proto/generate clean build test env mongo nats certificates hub-build http-gateway-www simulators
default: build
diff --git a/bundle/Dockerfile b/bundle/Dockerfile
index a31a14308..b925b6277 100644
--- a/bundle/Dockerfile
+++ b/bundle/Dockerfile
@@ -105,6 +105,13 @@ RUN go build \
-o "/go/bin/$tool" \
./
+#snippet-service
+ARG service=snippet-service
+WORKDIR $root_directory/$service
+RUN go build -ldflags "-linkmode external -extldflags -static -X github.com/plgd-dev/hub/v2/pkg/build.CommitDate=$COMMIT_DATE -X github.com/plgd-dev/hub/v2/pkg/build.CommitHash=$SHORT_COMMIT -X github.com/plgd-dev/hub/v2/pkg/build.BuildDate=$DATE -X github.com/plgd-dev/hub/v2/pkg/build.Version=$VERSION -X github.com/plgd-dev/hub/v2/pkg/build.ReleaseURL=$RELEASE_URL" \
+ -o "/go/bin/$service" \
+ ./cmd/service
+
#nats
WORKDIR $root_directory
RUN apkArch="$(apk --print-arch)"; \
@@ -168,6 +175,8 @@ COPY --from=build /go/bin/cloud2cloud-connector /usr/local/bin/cloud2cloud-conne
COPY --from=build /go/src/github.com/plgd-dev/hub/cloud2cloud-connector/config.yaml /configs/cloud2cloud-connector.yaml
COPY --from=build /go/src/github.com/plgd-dev/hub/bundle/run.sh /usr/local/bin/run.sh
COPY --from=build /go/src/github.com/plgd-dev/hub/bundle/nginx /nginx
+COPY --from=build /go/bin/snippet-service /usr/local/bin/snippet-service
+COPY --from=build /go/src/github.com/plgd-dev/hub/snippet-service/config.yaml /configs/snippet-service.yaml
# install scylla
RUN curl -sSf get.scylladb.com/server | sudo bash -s -- --scylla-version 5.2
@@ -222,6 +231,8 @@ ENV NATS_PORT=10001
ENV SCYLLA_SMP=1
ENV SCYLLA_DEVELOPER_MODE=true
ENV SCYLLA_PORT=29142
+ENV SNIPPET_SERVICE_PORT=9091
+ENV HTTP_SNIPPET_SERVICE_PORT=9092
# OAuth
ENV DEVICE_PROVIDER=plgd
diff --git a/bundle/nginx/nginx.conf.template b/bundle/nginx/nginx.conf.template
index 8ad47eb2c..3a20465d5 100644
--- a/bundle/nginx/nginx.conf.template
+++ b/bundle/nginx/nginx.conf.template
@@ -107,6 +107,20 @@ http {
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
}
+ location ~ ^(/snippet-service) {
+ set $upstream_snippet_service https://127.0.0.1:REPLACE_HTTP_SNIPPET_SERVICE_PORT;
+ proxy_pass $upstream_snippet_service;
+ proxy_ssl_certificate /data/certs/internal/endpoint.crt;
+ proxy_ssl_certificate_key /data/certs/internal/endpoint.key;
+ proxy_ssl_trusted_certificate /data/certs/root_ca.crt;
+ proxy_ssl_verify on;
+ set $cors_headers 'Authority,Method,Path,Scheme,Accept,Accept-Encoding,Accept-Language,Content-Type,Origin,Refer,Sec-Fetch-Dest,Sec-Fetch-Mode,Sec-Fetch-Site,Authorization,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,correlation-id';
+ include /nginx/cors.conf;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection $connection_upgrade;
+ proxy_set_header Host $host;
+ }
location ~ ^/ {
set $upstream_http_gateway https://127.0.0.1:REPLACE_HTTP_GATEWAY_PORT;
proxy_pass $upstream_http_gateway;
diff --git a/bundle/run.sh b/bundle/run.sh
index 112f400ca..a139e2b9a 100755
--- a/bundle/run.sh
+++ b/bundle/run.sh
@@ -15,6 +15,7 @@ export NGINX_PATH="/data/nginx"
export JETSTREAM_PATH="/data/jetstream"
export CERTIFICATE_AUTHORITY_ADDRESS="localhost:${CERTIFICATE_AUTHORITY_PORT}"
+export CERTIFICATE_AUTHORITY_HTTP_ADDRESS="localhost:${HTTP_CERTIFICATE_AUTHORITY_PORT}"
export MOCK_OAUTH_SERVER_ADDRESS="localhost:${MOCK_OAUTH_SERVER_PORT}"
export RESOURCE_AGGREGATE_ADDRESS="localhost:${RESOURCE_AGGREGATE_PORT}"
export RESOURCE_DIRECTORY_ADDRESS="localhost:${RESOURCE_DIRECTORY_PORT}"
@@ -23,6 +24,8 @@ export GRPC_GATEWAY_ADDRESS="localhost:${GRPC_GATEWAY_PORT}"
export HTTP_GATEWAY_ADDRESS="localhost:${HTTP_GATEWAY_PORT}"
export CLOUD2CLOUD_GATEWAY_ADDRESS="localhost:${CLOUD2CLOUD_GATEWAY_PORT}"
export CLOUD2CLOUD_CONNECTOR_ADDRESS="localhost:${CLOUD2CLOUD_CONNECTOR_PORT}"
+export SNIPPET_SERVICE_ADDRESS="localhost:${SNIPPET_SERVICE_PORT}"
+export SNIPPET_SERVICE_HTTP_ADDRESS="localhost:${HTTP_SNIPPET_SERVICE_PORT}"
export INTERNAL_CERT_DIR_PATH="$CERTIFICATES_PATH/internal"
export GRPC_INTERNAL_CERT_NAME="endpoint.crt"
@@ -451,6 +454,7 @@ if [ "${OVERRIDE_FILES}" = "true" ] || [ ! -f "${NGINX_PATH}/nginx.conf" ]; then
sed -i "s/REPLACE_CLOUD2CLOUD_GATEWAY_PORT/$CLOUD2CLOUD_GATEWAY_PORT/g" ${NGINX_PATH}/nginx.conf
sed -i "s/REPLACE_CLOUD2CLOUD_CONNECTOR_PORT/$CLOUD2CLOUD_CONNECTOR_PORT/g" ${NGINX_PATH}/nginx.conf
sed -i "s/REPLACE_HTTP_CERTIFICATE_AUTHORITY_PORT/$HTTP_CERTIFICATE_AUTHORITY_PORT/g" ${NGINX_PATH}/nginx.conf
+ sed -i "s/REPLACE_HTTP_SNIPPET_SERVICE_PORT/$HTTP_SNIPPET_SERVICE_PORT/g" ${NGINX_PATH}/nginx.conf
fi
# nats
@@ -917,6 +921,7 @@ cat /configs/certificate-authority.yaml | yq e "\
.apis.grpc.authorization.http.tls.useSystemCAPool = true |
.apis.grpc.authorization.authority = \"https://${OAUTH_ENDPOINT}\" |
.apis.grpc.authorization.ownerClaim = \"${OWNER_CLAIM}\" |
+ .apis.http.address = \"${CERTIFICATE_AUTHORITY_HTTP_ADDRESS}\" |
.clients.storage.use = \"${DATABASE_USE}\" |
.clients.storage.mongoDB.uri = \"${MONGODB_URI}\" |
.clients.storage.cqlDB.hosts = [ \"${SCYLLA_HOSTNAME}\" ] |
@@ -1144,7 +1149,51 @@ while true; do
sleep 1
done
+# snippet-service
+echo "starting snippet-service"
+## configuration
+if [ "${OVERRIDE_FILES}" = "true" ] || [ ! -f "/data/snippet-service.yaml" ]; then
+cat /configs/snippet-service.yaml | yq e "\
+ .hubID = \"${HUB_ID}\" |
+ .log.level = \"${LOG_LEVEL}\" |
+ .apis.grpc.address = \"${SNIPPET_SERVICE_ADDRESS}\" |
+ .apis.grpc.authorization.audience = \"${SERVICE_OAUTH_AUDIENCE}\" |
+ .apis.grpc.authorization.http.tls.useSystemCAPool = true |
+ .apis.grpc.authorization.authority = \"https://${OAUTH_ENDPOINT}\" |
+ .apis.grpc.authorization.ownerClaim = \"${OWNER_CLAIM}\" |
+ .apis.http.address = \"${SNIPPET_SERVICE_HTTP_ADDRESS}\" |
+ .clients.storage.use = \"${DATABASE_USE}\" |
+ .clients.storage.mongoDB.uri = \"${MONGODB_URI}\" |
+ .clients.storage.cqlDB.hosts = [ \"${SCYLLA_HOSTNAME}\" ] |
+ .clients.storage.cqlDB.port = ${SCYLLA_PORT} |
+ .clients.openTelemetryCollector.grpc.enabled = ${OPEN_TELEMETRY_EXPORTER_ENABLED} |
+ .clients.openTelemetryCollector.grpc.address = \"${OPEN_TELEMETRY_EXPORTER_ADDRESS}\" |
+ .clients.openTelemetryCollector.grpc.tls.caPool = \"${OPEN_TELEMETRY_EXPORTER_CA_POOL}\" |
+ .clients.openTelemetryCollector.grpc.tls.keyFile = \"${OPEN_TELEMETRY_EXPORTER_KEY_FILE}\" |
+ .clients.openTelemetryCollector.grpc.tls.certFile = \"${OPEN_TELEMETRY_EXPORTER_CERT_FILE}\" |
+ .clients.openTelemetryCollector.grpc.tls.useSystemCAPool = true
+" - > /data/snippet-service.yaml
+fi
+snippet-service --config /data/snippet-service.yaml >$LOGS_PATH/snippet-service.log 2>&1 &
+status=$?
+snippet_service_pid=$!
+if [ $status -ne 0 ]; then
+ echo "Failed to start snippet-service: $status"
+ sync
+ cat $LOGS_PATH/snippet-service.log
+ exit $status
+fi
+# waiting for ca. Without wait, sometimes the service didn't connect.
+i=0
+while true; do
+ i=$((i+1))
+ if openssl s_client -connect ${SNIPPET_SERVICE_ADDRESS} -cert ${INTERNAL_CERT_DIR_PATH}/${GRPC_INTERNAL_CERT_NAME} -key ${INTERNAL_CERT_DIR_PATH}/${GRPC_INTERNAL_CERT_KEY_NAME} <<< "Q" 2>/dev/null > /dev/null; then
+ break
+ fi
+ echo "Try to reconnect to snippet-service(${SNIPPET_SERVICE_ADDRESS}) $i"
+ sleep 1
+done
echo "Open browser at https://${DOMAIN}"
@@ -1263,4 +1312,11 @@ while sleep 10; do
exit 1
fi
fi
+ ps aux |grep $snippet_service_pid |grep -q -v grep
+ if [ $? -ne 0 ]; then
+ echo "snippet-service has already exited."
+ sync
+ cat $LOGS_PATH/snippet-service.log
+ exit 1
+ fi
done
diff --git a/charts/plgd-hub/templates/snippet-service/_helpers.tpl b/charts/plgd-hub/templates/snippet-service/_helpers.tpl
new file mode 100644
index 000000000..6bcb656b5
--- /dev/null
+++ b/charts/plgd-hub/templates/snippet-service/_helpers.tpl
@@ -0,0 +1,60 @@
+{{- define "plgd-hub.snippetservice.fullname" -}}
+{{- if .Values.snippetservice.fullnameOverride }}
+{{- .Values.snippetservice.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Values.snippetservice.name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s-%s" .Release.Name $name .Values.snippetservice.name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{- define "plgd-hub.snippetservice.image" -}}
+ {{- $registryName := .Values.snippetservice.image.registry | default "" -}}
+ {{- $repositoryName := .Values.snippetservice.image.repository -}}
+ {{- $tag := .Values.snippetservice.image.tag | default .Chart.AppVersion | toString -}}
+ {{- printf "%s%s:%s" $registryName $repositoryName $tag -}}
+{{- end -}}
+
+{{- define "plgd-hub.snippetservice.configName" -}}
+ {{- $fullName := include "plgd-hub.snippetservice.fullname" . -}}
+ {{- printf "%s-cfg" $fullName }}
+{{- end -}}
+
+{{- define "plgd-hub.snippetservice.createServiceCertByCm" }}
+ {{- $serviceTls := .Values.snippetservice.apis.grpc.tls.certFile }}
+ {{- if $serviceTls }}
+ {{- printf "" -}}
+ {{- else }}
+ {{- printf "true" -}}
+ {{- end }}
+{{- end }}
+
+{{- define "plgd-hub.snippetservice.domain" -}}
+ {{- if .Values.snippetservice.domain }}
+ {{- printf "%s" .Values.snippetservice.domain }}
+ {{- else }}
+ {{- printf "api.%s" .Values.global.domain }}
+ {{- end }}
+{{- end }}
+
+{{- define "plgd-hub.snippetservice.serviceCertName" -}}
+ {{- $fullName := include "plgd-hub.snippetservice.fullname" . -}}
+ {{- printf "%s-crt" $fullName -}}
+{{- end }}
+
+{{- define "plgd-hub.snippetservice.domainCertName" -}}
+ {{- if .Values.snippetservice.ingress.secretName }}
+ {{- printf "%s" .Values.snippetservice.ingress.secretName -}}
+ {{- else }}
+ {{- $fullName := include "plgd-hub.snippetservice.fullname" . -}}
+ {{- printf "%s-domain-crt" $fullName -}}
+ {{- end }}
+{{- end }}
+
+{{- define "plgd-hub.snippetservice.selectorLabels" -}}
+app.kubernetes.io/name: {{ .Values.snippetservice.name }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
\ No newline at end of file
diff --git a/charts/plgd-hub/templates/snippet-service/config.yaml b/charts/plgd-hub/templates/snippet-service/config.yaml
new file mode 100644
index 000000000..67ad643cc
--- /dev/null
+++ b/charts/plgd-hub/templates/snippet-service/config.yaml
@@ -0,0 +1,96 @@
+{{- if .Values.snippetservice.enabled }}
+{{- $cert := "/certs" }}
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ include "plgd-hub.snippetservice.configName" . }}
+ namespace: {{ .Release.Namespace }}
+data:
+ {{ .Values.snippetservice.config.fileName }}: |
+ {{- with .Values.snippetservice }}
+ hubID: {{ required "snippetservice.hubId or global.hubId is required" ( .hubId | default $.Values.global.hubId) | quote }}
+ log:
+ level: {{ .log.level }}
+ dumpBody: {{ .log.dumpBody }}
+ encoding: {{ .log.encoding }}
+ stacktrace:
+ enabled: {{ .log.stacktrace.enabled }}
+ level: {{ .log.stacktrace.level }}
+ encoderConfig:
+ timeEncoder: {{ .log.encoderConfig.timeEncoder }}
+ apis:
+ grpc:
+ address: {{ .apis.grpc.address | default (printf "0.0.0.0:%v" .port) | quote }}
+ sendMsgSize: {{ int64 .apis.grpc.sendMsgSize | default 4194304 }}
+ recvMsgSize: {{ int64 .apis.grpc.recvMsgSize | default 4194304 }}
+ enforcementPolicy:
+ minTime: {{ .apis.grpc.enforcementPolicy.minTime }}
+ permitWithoutStream: {{ .apis.grpc.enforcementPolicy.permitWithoutStream }}
+ keepAlive:
+ # 0s - means infinity
+ maxConnectionIdle: {{ .apis.grpc.keepAlive.maxConnectionIdle }}
+ # 0s - means infinity
+ maxConnectionAge: {{ .apis.grpc.keepAlive.maxConnectionIdle }}
+ # 0s - means infinity
+ maxConnectionAgeGrace: {{ .apis.grpc.keepAlive.maxConnectionAgeGrace }}
+ time: {{ .apis.grpc.keepAlive.maxConnectionIdle }}
+ timeout: {{ .apis.grpc.keepAlive.maxConnectionIdle }}
+ tls:
+ {{- $tls := .apis.grpc.tls }}
+ {{- include "plgd-hub.certificateConfig" (list $ $tls $cert ) | indent 8 }}
+ clientCertificateRequired: {{ .apis.grpc.tls.clientCertificateRequired }}
+ authorization:
+ {{- $authorization := .apis.grpc.authorization }}
+ {{- include "plgd-hub.authorizationConfig" (list $ $authorization "snippetservice" ) | indent 8 }}
+ http:
+ maxIdleConns: {{ .apis.grpc.authorization.http.maxIdleConns }}
+ maxConnsPerHost: {{ .apis.grpc.authorization.http.maxIdleConnsPerHost }}
+ maxIdleConnsPerHost: {{ .apis.grpc.authorization.http.maxIdleConnsPerHost }}
+ idleConnTimeout: {{ .apis.grpc.authorization.http.idleConnTimeout }}
+ timeout: {{ .apis.grpc.authorization.http.timeout }}
+ tls:
+ {{- $grpcTls := .apis.grpc.authorization.http.tls }}
+ {{- include "plgd-hub.authorizationCaCertificateConfig" (list $ $grpcTls $cert ) | indent 12 }}
+ useSystemCAPool: {{ .apis.grpc.authorization.http.tls.useSystemCAPool }}
+ http:
+ address: {{ .apis.http.address | default (printf "0.0.0.0:%v" .httpPort) | quote }}
+ readTimeout: {{ .apis.http.readTimeout }}
+ readHeaderTimeout: {{ .apis.http.readHeaderTimeout }}
+ writeTimeout: {{ .apis.http.writeTimeout }}
+ idleTimeout: {{ .apis.http.idleTimeout }}
+ clients:
+ storage:
+ use: {{ include "plgd-hub.useDatabase" (list $ . .clients.storage.use) | quote }}
+ mongoDB:
+ uri: {{ include "plgd-hub.mongoDBUri" (list $ .clients.storage.mongoDB.uri ) | quote }}
+ database: {{ .clients.storage.mongoDB.database }}
+ maxPoolSize: {{ .clients.storage.mongoDB.maxPoolSize }}
+ maxConnIdleTime: {{ .clients.storage.mongoDB.maxConnIdleTime }}
+ tls:
+ {{- $mongoDbTls := .clients.storage.mongoDB.tls }}
+ {{- include "plgd-hub.certificateConfig" (list $ $mongoDbTls $cert ) | indent 10 }}
+ useSystemCAPool: {{ .clients.storage.mongoDB.tls.useSystemCAPool }}
+ cqlDB:
+ hosts:
+ {{- include "plgd-hub.cqlDBHosts" (list $ .clients.storage.cqlDB.hosts ) | indent 8 }}
+ port: {{ .clients.storage.cqlDB.port | default 9142 }}
+ table: {{ .clients.storage.cqlDB.table | quote }}
+ numConnections: {{ .clients.storage.cqlDB.numConnections }}
+ connectTimeout: {{ .clients.storage.cqlDB.connectTimeout }}
+ useHostnameResolution: {{ .clients.storage.cqlDB.useHostnameResolution }}
+ reconnectionPolicy:
+ constant:
+ interval: {{ .clients.storage.cqlDB.reconnectionPolicy.constant.interval }}
+ maxRetries: {{ .clients.storage.cqlDB.reconnectionPolicy.constant.maxRetries }}
+ keyspace:
+ name: {{ .clients.storage.cqlDB.keyspace.name }}
+ create: {{ .clients.storage.cqlDB.keyspace.create }}
+ replication:
+ {{- toYaml .clients.storage.cqlDB.keyspace.replication | nindent 14 }}
+ tls:
+ {{- $cqlDbTls := .clients.storage.cqlDB.tls }}
+ {{- include "plgd-hub.certificateConfig" (list $ $cqlDbTls $cert ) | indent 10 }}
+ useSystemCAPool: {{ .clients.storage.cqlDB.tls.useSystemCAPool }}
+ {{- include "plgd-hub.openTelemetryExporterConfig" (list $ $cert ) | nindent 6 }}
+ {{- end }}
+{{- end }}
diff --git a/charts/plgd-hub/templates/snippet-service/deployment.yaml b/charts/plgd-hub/templates/snippet-service/deployment.yaml
new file mode 100644
index 000000000..04b20317e
--- /dev/null
+++ b/charts/plgd-hub/templates/snippet-service/deployment.yaml
@@ -0,0 +1,129 @@
+{{- if .Values.snippetservice.enabled }}
+{{- $cert := "/certs" }}
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "plgd-hub.snippetservice.fullname" . }}
+ namespace: {{ .Release.Namespace }}
+ labels:
+ {{- include "plgd-hub.labels" . | nindent 4 }}
+ {{- with .Values.snippetservice.deploymentLabels }}
+ {{- . | toYaml | nindent 4 }}
+ {{- end }}
+ {{- with .Values.snippetservice.deploymentAnnotations }}
+ annotations:
+ {{- . | toYaml | nindent 4 }}
+ {{- end }}
+spec:
+ replicas: {{ .Values.snippetservice.replicas }}
+ selector:
+ matchLabels:
+ {{- include "plgd-hub.snippetservice.selectorLabels" . | nindent 6 }}
+ template:
+ metadata:
+ {{- with .Values.snippetservice.podAnnotations }}
+ annotations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ labels:
+ {{- include "plgd-hub.snippetservice.selectorLabels" . | nindent 8 }}
+ spec:
+ {{- with .Values.snippetservice.securityContext }}
+ securityContext:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- if .Values.snippetservice.rbac.enabled }}
+ serviceAccountName: {{ .Values.snippetservice.rbac.serviceAccountName }}
+ {{- end }}
+ {{- with .Values.snippetservice.image.imagePullSecrets }}
+ imagePullSecrets:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ restartPolicy: {{ .Values.snippetservice.restartPolicy }}
+ {{- if .Values.snippetservice.initContainersTpl }}
+ initContainers:
+ {{- tpl .Values.snippetservice.initContainersTpl . | nindent 8 }}
+ {{- end }}
+ containers:
+ - name: {{ .Values.snippetservice.name }}
+ {{- with .Values.snippetservice.podSecurityContext }}
+ securityContext:
+ {{- toYaml . | nindent 12 }}
+ {{- end }}
+ image: {{ include "plgd-hub.snippetservice.image" . | quote }}
+ imagePullPolicy: {{ .Values.snippetservice.image.pullPolicy }}
+ {{- if .Values.snippetservice.command }}
+ command:
+ {{- range .Values.snippetservice.command }}
+ - {{ . | quote }}
+ {{- end }}
+ {{- end}}
+ args:
+ - "--config"
+ - {{ printf "%s/%s" .Values.snippetservice.config.mountPath .Values.snippetservice.config.fileName | quote }}
+ ports:
+ - name: grpc
+ containerPort: {{ .Values.snippetservice.port }}
+ protocol: TCP
+ - name: http
+ containerPort: {{ .Values.snippetservice.httpPort }}
+ protocol: TCP
+ {{- with .Values.snippetservice.livenessProbe }}
+ livenessProbe:
+ {{- toYaml . | nindent 12 }}
+ {{- end }}
+ {{- with .Values.snippetservice.readinessProbe }}
+ readinessProbe:
+ {{- toYaml . | nindent 12 }}
+ {{- end }}
+ {{- with .Values.snippetservice.resources }}
+ resources:
+ {{- toYaml . | nindent 12 }}
+ {{- end }}
+ volumeMounts:
+ - name: {{ .Values.snippetservice.config.volume }}
+ mountPath: {{ .Values.snippetservice.config.mountPath }}
+ {{- if ( include "plgd-hub.snippetservice.createServiceCertByCm" . ) }}
+ - name: service-crt
+ mountPath: {{ $cert }}
+ {{- end }}
+ {{- if .Values.global.authorizationCAPool }}
+ - name: {{ .Values.extraAuthorizationCAPool.name }}
+ mountPath: {{ .Values.extraAuthorizationCAPool.mountPath }}
+ {{- end }}
+ {{- with .Values.snippetservice.extraVolumeMounts }}
+ {{- toYaml . | nindent 12 }}
+ {{- end }}
+ {{- if .Values.snippetservice.extraContainers }}
+ {{- include "plgd-hub.tplvalues.render" ( dict "value" .Values.snippetservice.extraContainers "context" $ ) | nindent 8 }}
+ {{- end }}
+ volumes:
+ - name: {{ .Values.resourcedirectory.config.volume }}
+ configMap:
+ name: {{ include "plgd-hub.snippetservice.configName" . }}
+ {{- if ( include "plgd-hub.snippetservice.createServiceCertByCm" . ) }}
+ - name: service-crt
+ secret:
+ secretName: {{ include "plgd-hub.snippetservice.serviceCertName" . }}
+ {{- end }}
+ {{- if .Values.global.authorizationCAPool }}
+ - name: {{ .Values.extraAuthorizationCAPool.name }}
+ secret:
+ secretName: {{ .Values.extraAuthorizationCAPool.name }}
+ {{- end }}
+ {{- with .Values.snippetservice.extraVolumes }}
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.snippetservice.nodeSelector }}
+ nodeSelector:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.snippetservice.affinity }}
+ affinity:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+ {{- with .Values.snippetservice.tolerations }}
+ tolerations:
+ {{- toYaml . | nindent 8 }}
+ {{- end }}
+{{- end }}
diff --git a/charts/plgd-hub/templates/snippet-service/domain-crt.yaml b/charts/plgd-hub/templates/snippet-service/domain-crt.yaml
new file mode 100644
index 000000000..c1d06ff62
--- /dev/null
+++ b/charts/plgd-hub/templates/snippet-service/domain-crt.yaml
@@ -0,0 +1,34 @@
+{{- $domainCrt := include "plgd-hub.snippetservice.domainCertName" . }}
+{{- if and $domainCrt .Values.certmanager.enabled .Values.snippetservice.enabled (not $.Values.global.enableWildCartCert ) }}
+{{- $serviceDns := include "plgd-hub.snippetservice.fullname" . }}
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+ name: {{ $domainCrt }}
+ namespace: {{ .Release.Namespace }}
+ labels:
+ {{- include "plgd-hub.labels" . | nindent 4 }}
+ {{- with .Values.certmanager.external.labels }}
+ {{- . | toYaml | nindent 4 }}
+ {{- end }}
+ {{- with .Values.certmanager.external.annotations }}
+ annotations:
+ {{- . | toYaml | nindent 4 }}
+ {{- end }}
+spec:
+ secretName: {{ $domainCrt }}
+ privateKey:
+ algorithm: {{ .Values.certmanager.external.cert.key.algorithm | default .Values.certmanager.default.cert.key.algorithm }}
+ size: {{ .Values.certmanager.external.cert.key.size | default .Values.certmanager.default.cert.key.size }}
+ usages:
+ - server auth
+ - client auth
+ dnsNames:
+ - {{ include "plgd-hub.snippetservice.domain" . | quote }}
+ duration: {{ .Values.certmanager.external.cert.duration | default .Values.certmanager.default.cert.duration }}
+ renewBefore: {{ .Values.certmanager.external.cert.renewBefore | default .Values.certmanager.default.cert.renewBefore }}
+ issuerRef:
+ name: {{ .Values.certmanager.external.issuer.name | default .Values.certmanager.default.issuer.name }}
+ kind: {{ .Values.certmanager.external.issuer.kind | default .Values.certmanager.default.issuer.kind }}
+ group: {{ .Values.certmanager.external.issuer.group | default .Values.certmanager.default.issuer.group }}
+{{- end }}
diff --git a/charts/plgd-hub/templates/snippet-service/grpc-ingress.yaml b/charts/plgd-hub/templates/snippet-service/grpc-ingress.yaml
new file mode 100644
index 000000000..12ef47117
--- /dev/null
+++ b/charts/plgd-hub/templates/snippet-service/grpc-ingress.yaml
@@ -0,0 +1,40 @@
+{{- if and .Values.snippetservice.enabled .Values.snippetservice.ingress.grpc.enabled }}
+{{- $fullname := include "plgd-hub.snippetservice.fullname" . }}
+{{- $port := .Values.snippetservice.port }}
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: {{ $fullname }}-grpc
+ namespace: {{ .Release.Namespace }}
+ labels:
+ {{- include "plgd-hub.labels" . | nindent 4 }}
+ annotations:
+ {{- if .Values.snippetservice.ingress.grpc.annotations }}
+ {{- include "plgd-hub.tplvalues.render" ( dict "value" .Values.snippetservice.ingress.grpc.annotations "context" $ ) | nindent 4 }}
+ {{- end }}
+ {{- if .Values.snippetservice.ingress.grpc.customAnnotations }}
+ {{- include "plgd-hub.tplvalues.render" ( dict "value" .Values.snippetservice.ingress.grpc.customAnnotations "context" $ ) | nindent 4 }}
+ {{- end }}
+spec:
+ tls:
+ - hosts:
+ - {{ include "plgd-hub.snippetservice.domain" . | quote }}
+ {{- if $.Values.global.enableWildCartCert }}
+ secretName: {{ include "plgd-hub.wildCardCertName" . | quote }}
+ {{- else }}
+ secretName: {{ include "plgd-hub.snippetservice.domainCertName" . | quote }}
+ {{- end }}
+ rules:
+ - host: {{ include "plgd-hub.snippetservice.domain" . | quote }}
+ http:
+ paths:
+ {{- range .Values.snippetservice.ingress.grpc.paths }}
+ - path: {{ . }}
+ pathType: Prefix
+ backend:
+ service:
+ name: {{ $fullname }}-grpc
+ port:
+ number: {{ $port }}
+ {{- end }}
+{{- end }}
diff --git a/charts/plgd-hub/templates/snippet-service/grpc-service.yaml b/charts/plgd-hub/templates/snippet-service/grpc-service.yaml
new file mode 100644
index 000000000..e2d37681f
--- /dev/null
+++ b/charts/plgd-hub/templates/snippet-service/grpc-service.yaml
@@ -0,0 +1,25 @@
+{{- if .Values.snippetservice.enabled -}}
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "plgd-hub.snippetservice.fullname" . }}-grpc
+ namespace: {{ .Release.Namespace }}
+ labels:
+ {{- include "plgd-hub.labels" . | nindent 4 }}
+ {{- with .Values.snippetservice.service.grpc.labels }}
+ {{- . | toYaml | nindent 4 }}
+ {{- end }}
+ {{- if .Values.snippetservice.service.grpc.annotations }}
+ annotations:
+ {{- include "plgd-hub.tplvalues.render" ( dict "value" .Values.snippetservice.service.grpc.annotations "context" $ ) | nindent 4 }}
+ {{- end }}
+spec:
+ type: {{ .Values.snippetservice.service.grpc.type | default "ClusterIP" }}
+ ports:
+ - port: {{ .Values.snippetservice.port }}
+ targetPort: {{ .Values.snippetservice.service.grpc.targetPort }}
+ protocol: {{ .Values.snippetservice.service.grpc.protocol }}
+ name: {{ .Values.snippetservice.service.grpc.name }}
+ selector:
+ {{- include "plgd-hub.snippetservice.selectorLabels" . | nindent 4 }}
+{{- end }}
\ No newline at end of file
diff --git a/charts/plgd-hub/templates/snippet-service/http-ingress.yaml b/charts/plgd-hub/templates/snippet-service/http-ingress.yaml
new file mode 100644
index 000000000..3ee3cd2b2
--- /dev/null
+++ b/charts/plgd-hub/templates/snippet-service/http-ingress.yaml
@@ -0,0 +1,40 @@
+{{- if and .Values.snippetservice.enabled .Values.snippetservice.ingress.http.enabled }}
+{{- $fullname := include "plgd-hub.snippetservice.fullname" . }}
+{{- $port := .Values.snippetservice.httpPort }}
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: {{ $fullname }}-http
+ namespace: {{ .Release.Namespace }}
+ labels:
+ {{- include "plgd-hub.labels" . | nindent 4 }}
+ annotations:
+ {{- if .Values.snippetservice.ingress.http.annotations }}
+ {{- include "plgd-hub.tplvalues.render" ( dict "value" .Values.snippetservice.ingress.http.annotations "context" $ ) | nindent 4 }}
+ {{- end }}
+ {{- if .Values.snippetservice.ingress.http.customAnnotations }}
+ {{- include "plgd-hub.tplvalues.render" ( dict "value" .Values.snippetservice.ingress.http.customAnnotations "context" $ ) | nindent 4 }}
+ {{- end }}
+spec:
+ tls:
+ - hosts:
+ - {{ include "plgd-hub.snippetservice.domain" . | quote }}
+ {{- if $.Values.global.enableWildCartCert }}
+ secretName: {{ include "plgd-hub.wildCardCertName" . | quote }}
+ {{- else }}
+ secretName: {{ include "plgd-hub.snippetservice.domainCertName" . | quote }}
+ {{- end }}
+ rules:
+ - host: {{ include "plgd-hub.snippetservice.domain" . | quote }}
+ http:
+ paths:
+ {{- range .Values.snippetservice.ingress.http.paths }}
+ - path: {{ . }}
+ pathType: Prefix
+ backend:
+ service:
+ name: {{ $fullname }}-http
+ port:
+ number: {{ $port }}
+ {{- end }}
+{{- end }}
diff --git a/charts/plgd-hub/templates/snippet-service/http-service.yaml b/charts/plgd-hub/templates/snippet-service/http-service.yaml
new file mode 100644
index 000000000..1e6cf659c
--- /dev/null
+++ b/charts/plgd-hub/templates/snippet-service/http-service.yaml
@@ -0,0 +1,25 @@
+{{- if .Values.snippetservice.enabled -}}
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "plgd-hub.snippetservice.fullname" . }}-http
+ namespace: {{ .Release.Namespace }}
+ labels:
+ {{- include "plgd-hub.labels" . | nindent 4 }}
+ {{- with .Values.snippetservice.service.http.labels }}
+ {{- . | toYaml | nindent 4 }}
+ {{- end }}
+ {{- if .Values.snippetservice.service.http.annotations }}
+ annotations:
+ {{- include "plgd-hub.tplvalues.render" ( dict "value" .Values.snippetservice.service.http.annotations "context" $ ) | nindent 4 }}
+ {{- end }}
+spec:
+ type: {{ .Values.snippetservice.service.http.type | default "ClusterIP" }}
+ ports:
+ - port: {{ .Values.snippetservice.httpPort }}
+ targetPort: {{ .Values.snippetservice.service.http.targetPort }}
+ protocol: {{ .Values.snippetservice.service.http.protocol }}
+ name: {{ .Values.snippetservice.service.http.name }}
+ selector:
+ {{- include "plgd-hub.snippetservice.selectorLabels" . | nindent 4 }}
+{{- end }}
\ No newline at end of file
diff --git a/charts/plgd-hub/templates/snippet-service/role.yaml b/charts/plgd-hub/templates/snippet-service/role.yaml
new file mode 100644
index 000000000..0dc01b10c
--- /dev/null
+++ b/charts/plgd-hub/templates/snippet-service/role.yaml
@@ -0,0 +1,3 @@
+{{- if and .Values.snippetservice.rbac.enabled .Values.snippetservice.rbac.roleBindingDefitionTpl }}
+{{- tpl .Values.snippetservice.rbac.roleBindingDefitionTpl $ }}
+{{- end }}
diff --git a/charts/plgd-hub/templates/snippet-service/service-account.yaml b/charts/plgd-hub/templates/snippet-service/service-account.yaml
new file mode 100644
index 000000000..b1a4a7081
--- /dev/null
+++ b/charts/plgd-hub/templates/snippet-service/service-account.yaml
@@ -0,0 +1,9 @@
+{{- if and .Values.snippetservice.rbac.enabled .Values.snippetservice.enabled }}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ .Values.snippetservice.rbac.serviceAccountName }}
+ namespace: {{ .Release.Namespace }}
+ labels:
+ {{- include "plgd-hub.labels" . | nindent 4 }}
+{{- end }}
diff --git a/charts/plgd-hub/templates/snippet-service/service-crt.yaml b/charts/plgd-hub/templates/snippet-service/service-crt.yaml
new file mode 100644
index 000000000..afce0d74b
--- /dev/null
+++ b/charts/plgd-hub/templates/snippet-service/service-crt.yaml
@@ -0,0 +1,44 @@
+{{- $createServiceCert := include "plgd-hub.snippetservice.serviceCertName" . }}
+{{- if and $createServiceCert .Values.certmanager.enabled .Values.snippetservice.enabled }}
+{{- $serviceCertName := include "plgd-hub.snippetservice.serviceCertName" . }}
+{{- $serviceDns := include "plgd-hub.snippetservice.fullname" . }}
+apiVersion: cert-manager.io/v1
+kind: Certificate
+metadata:
+ name: {{ $serviceCertName }}
+ namespace: {{ .Release.Namespace }}
+ labels:
+ {{- include "plgd-hub.labels" . | nindent 4 }}
+ {{- with .Values.certmanager.internal.labels }}
+ {{- . | toYaml | nindent 4 }}
+ {{- end }}
+ {{- with .Values.certmanager.internal.annotations }}
+ annotations:
+ {{- . | toYaml | nindent 4 }}
+ {{- end }}
+spec:
+ secretName: {{ $serviceCertName }}
+ privateKey:
+ algorithm: {{ .Values.certmanager.internal.cert.key.algorithm | default .Values.certmanager.default.cert.key.algorithm }}
+ size: {{ .Values.certmanager.internal.cert.key.size | default .Values.certmanager.default.cert.key.size }}
+ usages:
+ - server auth
+ - client auth
+ dnsNames:
+ - {{ printf "%s-http.%s.svc.%s" $serviceDns .Release.Namespace .Values.cluster.dns | quote }}
+ - {{ printf "%s-grpc.%s.svc.%s" $serviceDns .Release.Namespace .Values.cluster.dns | quote }}
+ - {{ printf "%s-http" $serviceDns | quote }}
+ - {{ printf "%s-grpc" $serviceDns | quote }}
+ {{- if .Values.snippetservice.service.grpc.crt.extraDnsNames }}
+ {{- toYaml .Values.snippetservice.service.grpc.crt.extraDnsNames | nindent 4}}
+ {{- end }}
+ {{- if .Values.snippetservice.service.http.crt.extraDnsNames }}
+ {{- toYaml .Values.snippetservice.service.http.crt.extraDnsNames | nindent 4}}
+ {{- end }}
+ duration: {{ .Values.certmanager.internal.cert.duration | default .Values.certmanager.default.cert.duration }}
+ renewBefore: {{ .Values.certmanager.internal.cert.renewBefore | default .Values.certmanager.default.cert.renewBefore }}
+ issuerRef:
+ name: {{ .Values.certmanager.internal.issuer.name | default .Values.certmanager.default.issuer.name }}
+ kind: {{ .Values.certmanager.internal.issuer.kind | default .Values.certmanager.default.issuer.kind }}
+ group: {{ .Values.certmanager.internal.issuer.group | default .Values.certmanager.default.issuer.group }}
+{{- end }}
diff --git a/charts/plgd-hub/values.yaml b/charts/plgd-hub/values.yaml
index 811daff00..83b31e12b 100644
--- a/charts/plgd-hub/values.yaml
+++ b/charts/plgd-hub/values.yaml
@@ -2042,6 +2042,257 @@ certificateauthority:
validFrom: "now-1h"
expiresIn: "87600h"
+snippetservice:
+ # -- Enable certificate-authority service
+ enabled: true
+ # -- Name of component. Used in label selectors
+ name: snippet-service
+ # -- Full name to override
+ fullnameOverride:
+ # -- Number of replicas
+ replicas: 1
+ # -- Additional labels for certificate-authority deployment
+ deploymentLabels: {}
+ # -- Additional annotations for certificate-authority deployment
+ deploymentAnnotations: {}
+ # -- Pod security context
+ podSecurityContext: {}
+ # -- Labels for certificate-authority pod
+ podLabels: {}
+ # -- Annotations for certificate-authority pod
+ podAnnotations: {}
+ service:
+ grpc:
+ # -- Service type
+ type: ClusterIP
+ # -- Labels for certificate-authority service
+ labels: {}
+ # -- Annotations for certificate-authority service
+ annotations: {}
+ # -- Target port
+ targetPort: grpc
+ # -- Protocol
+ protocol: TCP
+ # -- Name
+ name: grpc
+ crt:
+ # -- Extra DNS names for service certificate
+ extraDnsNames: []
+ http:
+ # -- Service type
+ type: ClusterIP
+ # -- Labels for snippet service
+ labels: {}
+ # -- Annotations for snippet service
+ annotations: {}
+ # -- Target port
+ targetPort: http
+ # -- Protocol
+ protocol: TCP
+ # -- Name
+ name: http
+ crt:
+ # -- Extra DNS names for service certificate
+ extraDnsNames: []
+ # -- RBAC configuration
+ rbac:
+ # -- Enable RBAC
+ enabled: false
+ # -- Name of snippet service SA
+ serviceAccountName: snippet-service
+ # -- Template definition for Role/binding etc..
+ roleBindingDefitionTpl:
+ # -- Security context for pod
+ securityContext:
+ # -- Image pull secrets
+ imagePullSecrets:
+ # -- Restart policy for pod
+ restartPolicy: Always
+ # -- Init containers definition
+ initContainersTpl:
+ # -- Extra POD containers
+ extraContainers: {}
+ image:
+ # -- Image registry
+ registry: ghcr.io/
+ # -- Image repository
+ repository: plgd-dev/hub/snippet-service
+ # -- Image tag.
+ tag:
+ # -- Image pull policy
+ pullPolicy: Always
+ # -- Image pull secrets
+ imagePullSecrets:
+ # -- Liveness probe. snippet-service doesn't have any default liveness probe
+ livenessProbe:
+ # -- Readiness probe. snippet-service doesn't have aby default readiness probe
+ readinessProbe:
+ # -- Resources limit
+ resources:
+ # -- Node selector
+ nodeSelector:
+ # -- Affinity definition
+ affinity:
+ # -- Toleration definition
+ tolerations:
+ # -- Optional extra volumes
+ extraVolumes:
+ # -- Optional extra volume mounts
+ extraVolumeMounts:
+ # -- External domain for snippet-service. Default: api.{{ global.domain }}
+ domain:
+ ingress:
+ http:
+ # -- Enable ingress
+ enabled: true
+ # -- Override name of host/tls secret. If not specified, it will be generated
+ secretName:
+ # -- Pre defined map of Ingress annotation
+ annotations:
+ nginx.org/grpc-services: "{{ include \"plgd-hub.snippetservice.fullname\" . }}-http"
+ nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
+ ingress.kubernetes.io/force-ssl-redirect: "true"
+ nginx.ingress.kubernetes.io/enable-cors: "true"
+ cert-manager.io/private-key-rotation-policy: always
+ # -- Custom map of Ingress annotation
+ customAnnotations: {}
+ # -- Ingress path
+ paths:
+ - /snippet-service
+ grpc:
+ # -- Enable ingress
+ enabled: true
+ # -- Override name of host/tls secret. If not specified, it will be generated
+ secretName:
+ # -- Pre defined map of Ingress annotation
+ annotations:
+ nginx.org/grpc-services: "{{ include \"plgd-hub.snippetservice.fullname\" . }}-grpc"
+ nginx.ingress.kubernetes.io/backend-protocol: "GRPCS"
+ ingress.kubernetes.io/force-ssl-redirect: "true"
+ nginx.ingress.kubernetes.io/enable-cors: "true"
+ cert-manager.io/private-key-rotation-policy: always
+ # -- Custom map of Ingress annotation
+ customAnnotations: { }
+ # -- Paths
+ paths:
+ - /snippetservice.pb.SnippetService
+ # -- Service configuration
+ config:
+ # -- File name for config file
+ fileName: service.yaml
+ # -- Config file volume name
+ volume: config
+ # -- Mount path
+ mountPath: /config
+ # -- Service and POD port
+ port: 9100
+ httpPort: 9101
+ # -- Hub ID. Overrides the global.hubId
+ hubId:
+ # -- Log section
+ log:
+ # -- Logging enabled from level
+ level: info
+ # -- Dump grpc messages
+ dumpBody: false
+ # -- The supported values are: "json", "console"
+ encoding: json
+ stacktrace:
+ # -- Log stacktrace
+ enabled: false
+ # -- Stacktrace from level
+ level: warn
+ encoderConfig:
+ # -- Time format for logs. The supported values are: "rfc3339nano", "rfc3339"
+ timeEncoder: rfc3339nano
+ # -- For complete certificate-authority service configuration see [plgd/certificate-authority](https://github.com/plgd-dev/hub/tree/main/certificate-authority)
+ apis:
+ grpc:
+ address:
+ sendMsgSize: 4194304
+ recvMsgSize: 4194304
+ enforcementPolicy:
+ minTime: 5s
+ permitWithoutStream: true
+ keepAlive:
+ # 0s - means infinity
+ maxConnectionIdle: 0s
+ # 0s - means infinity
+ maxConnectionAge: 0s
+ # 0s - means infinity
+ maxConnectionAgeGrace: 0s
+ time: 2h
+ timeout: 20s
+ tls:
+ caPool:
+ keyFile:
+ certFile:
+ clientCertificateRequired: false
+ authorization:
+ ownerClaim:
+ authority:
+ audience:
+ http:
+ maxIdleConns: 16
+ maxConnsPerHost: 32
+ maxIdleConnsPerHost: 16
+ idleConnTimeout: "30s"
+ timeout: "10s"
+ tls:
+ caPool:
+ keyFile:
+ certFile:
+ useSystemCAPool: true
+ http:
+ address:
+ readTimeout: 8s
+ readHeaderTimeout: 4s
+ writeTimeout: 16s
+ idleTimeout: 30s
+ clients:
+ storage:
+ use: mongoDB
+ mongoDB:
+ uri:
+ database: snippetService
+ maxPoolSize: 16
+ maxConnIdleTime: 4m0s
+ tls:
+ caPool:
+ keyFile:
+ certFile:
+ useSystemCAPool: false
+ bulkWrite:
+ # -- A time limit for write bulk to mongodb. A Timeout of zero means no timeout.
+ timeout: 1m0s
+ # -- The amount of time to wait until a record is written to mongodb. Any records collected during the throttle time will also be written. A throttle time of zero writes immediately. If recordLimit is reached, all records are written immediately
+ throttleTime: 500ms
+ # -- The maximum number of documents to cache before an immediate write.
+ documentLimit: 1000
+ cqlDB:
+ table: snippetServiceRecords
+ hosts: []
+ port: 9142
+ numConnections: 16
+ connectTimeout: 10s
+ # -- Resolve IP address to hostname before validate certificate. If false, the TLS validator will use ip/hostname advertised by the Cassandra node.
+ useHostnameResolution: true
+ reconnectionPolicy:
+ constant:
+ interval: 3s
+ # 0 - means infinity
+ maxRetries: 3
+ keyspace:
+ name: plgdhub
+ create: true
+ replication:
+ class: SimpleStrategy
+ replication_factor: 1
+ tls:
+ caPool:
+ keyFile:
+ certFile:
+ useSystemCAPool: false
mockoauthserver:
oauth:
diff --git a/go.mod b/go.mod
index 63c65499a..d54c33d0e 100644
--- a/go.mod
+++ b/go.mod
@@ -30,7 +30,7 @@ require (
github.com/panjf2000/ants/v2 v2.9.1
github.com/pion/dtls/v2 v2.2.8-0.20240501061905-2c36d63320a0
github.com/pion/logging v0.2.2
- github.com/plgd-dev/device/v2 v2.5.1-0.20240513064831-b553d1a87e1c
+ github.com/plgd-dev/device/v2 v2.5.2-0.20240528123838-d8ca4ace7baf
github.com/plgd-dev/go-coap/v3 v3.3.4
github.com/plgd-dev/kit/v2 v2.0.0-20211006190727-057b33161b90
github.com/pseudomuto/protoc-gen-doc v1.5.1
@@ -40,7 +40,7 @@ require (
github.com/tidwall/sjson v1.2.5
github.com/ugorji/go/codec v1.2.12
github.com/vincent-petithory/dataurl v1.0.0
- github.com/web-of-things-open-source/thingdescription-go v0.0.0-20240510130416-741fef736e1e
+ github.com/web-of-things-open-source/thingdescription-go v0.0.0-20240513190706-79b5f39190eb
go.mongodb.org/mongo-driver v1.15.0
go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.49.0
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0
diff --git a/go.sum b/go.sum
index 01aafa37e..b5be7de7a 100644
--- a/go.sum
+++ b/go.sum
@@ -226,8 +226,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
-github.com/plgd-dev/device/v2 v2.5.1-0.20240513064831-b553d1a87e1c h1:kNF2KvyCzA8IMERdHUrL/LMdsuZM/tXGjLVzEX2lcg4=
-github.com/plgd-dev/device/v2 v2.5.1-0.20240513064831-b553d1a87e1c/go.mod h1:2mFPs55x2Li76zkrHdRNY3yOqVWSh59hiUw+6FYXA0k=
+github.com/plgd-dev/device/v2 v2.5.2-0.20240528123838-d8ca4ace7baf h1:fAmllEckbJW4kpOIUod9kL42X/UdANyzyMukIVL+jRA=
+github.com/plgd-dev/device/v2 v2.5.2-0.20240528123838-d8ca4ace7baf/go.mod h1:uplTm15S+2OBsGBpPdJ+YPUcnRnHdUbbRO87SHnnQA0=
github.com/plgd-dev/go-coap/v2 v2.0.4-0.20200819112225-8eb712b901bc/go.mod h1:+tCi9Q78H/orWRtpVWyBgrr4vKFo2zYtbbxUllerBp4=
github.com/plgd-dev/go-coap/v2 v2.4.1-0.20210517130748-95c37ac8e1fa/go.mod h1:rA7fc7ar+B/qa+Q0hRqv7yj/EMtIlmo1l7vkQGSrHPU=
github.com/plgd-dev/go-coap/v3 v3.3.4 h1:clDLFOXXmXfhZqB0eSk6WJs2iYfjC2J22Ixwu5MHiO0=
@@ -288,8 +288,8 @@ github.com/valyala/fasthttp v1.12.0/go.mod h1:229t1eWu9UXTPmoUkbpN/fctKPBY4IJoFX
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI=
github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=
-github.com/web-of-things-open-source/thingdescription-go v0.0.0-20240510130416-741fef736e1e h1:blQyU8WqqyRcBmaAPLiU5cTg9BSQu04CJZ/ffEzgI1s=
-github.com/web-of-things-open-source/thingdescription-go v0.0.0-20240510130416-741fef736e1e/go.mod h1:L/jWuWf+v7rmuFykpUP/runRXTnnA0QdGGgou8vzPrw=
+github.com/web-of-things-open-source/thingdescription-go v0.0.0-20240513190706-79b5f39190eb h1:6qIx0S8XOgCXkIMWew2dET+0Fcw2qTYZsDJ9LT8xQXg=
+github.com/web-of-things-open-source/thingdescription-go v0.0.0-20240513190706-79b5f39190eb/go.mod h1:L/jWuWf+v7rmuFykpUP/runRXTnnA0QdGGgou8vzPrw=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
diff --git a/snippet-service/Makefile b/snippet-service/Makefile
new file mode 100644
index 000000000..25a1a528d
--- /dev/null
+++ b/snippet-service/Makefile
@@ -0,0 +1,64 @@
+SHELL = /bin/bash
+SERVICE_NAME = $(notdir $(CURDIR))
+LATEST_TAG ?= vnext
+BRANCH_TAG ?= $(shell git rev-parse --abbrev-ref HEAD | sed 's/[^a-zA-Z0-9]/-/g')
+ifneq ($(BRANCH_TAG),main)
+ LATEST_TAG = $(BRANCH_TAG)
+endif
+VERSION_TAG ?= $(LATEST_TAG)-$(shell git rev-parse --short=7 --verify HEAD)
+GOPATH ?= $(shell go env GOPATH)
+WORKING_DIRECTORY := $(shell pwd)
+REPOSITORY_DIRECTORY := $(shell cd .. && pwd)
+BUILD_COMMIT_DATE ?= $(shell date -u +%FT%TZ --date=@`git show --format='%ct' HEAD --quiet`)
+BUILD_SHORT_COMMIT ?= $(shell git show --format=%h HEAD --quiet)
+BUILD_DATE ?= $(shell date -u +%FT%TZ)
+BUILD_VERSION ?= $(shell git tag --sort version:refname | tail -1 | sed -e "s/^v//")
+
+default: build
+
+define build-docker-image
+ cd .. && \
+ mkdir -p .tmp/docker/$(SERVICE_NAME) && \
+ awk '{gsub("@NAME@","$(SERVICE_NAME)")} {gsub("@DIRECTORY@","$(SERVICE_NAME)")} {print}' tools/docker/Dockerfile.in > .tmp/docker/$(SERVICE_NAME)/Dockerfile && \
+ docker build \
+ --network=host \
+ --tag ghcr.io/plgd-dev/hub/$(SERVICE_NAME):$(VERSION_TAG) \
+ --tag ghcr.io/plgd-dev/hub/$(SERVICE_NAME):$(LATEST_TAG) \
+ --tag ghcr.io/plgd-dev/hub/$(SERVICE_NAME):$(BRANCH_TAG) \
+ --build-arg COMMIT_DATE="$(BUILD_COMMIT_DATE)" \
+ --build-arg SHORT_COMMIT="$(BUILD_SHORT_COMMIT)" \
+ --build-arg DATE="$(BUILD_DATE)" \
+ --build-arg VERSION="$(BUILD_VERSION)" \
+ --target $(1) \
+ -f .tmp/docker/$(SERVICE_NAME)/Dockerfile \
+ .
+endef
+
+build-servicecontainer:
+ $(call build-docker-image,service)
+
+build: build-servicecontainer
+
+push: build-servicecontainer
+ docker push plgd/$(SERVICE_NAME):$(VERSION_TAG)
+ docker push plgd/$(SERVICE_NAME):$(LATEST_TAG)
+
+GOOGLEAPIS_PATH := $(REPOSITORY_DIRECTORY)/dependency/googleapis
+GRPCGATEWAY_MODULE_PATH := $(shell go list -m -f '{{.Dir}}' github.com/grpc-ecosystem/grpc-gateway/v2 | head -1)
+
+proto/generate:
+ protoc -I=. -I=$(REPOSITORY_DIRECTORY) -I=$(GOPATH)/src -I=$(GOOGLEAPIS_PATH) -I=$(GRPCGATEWAY_MODULE_PATH) --go_out=$(GOPATH)/src $(WORKING_DIRECTORY)/pb/service.proto
+ protoc-go-inject-tag -remove_tag_comment -input=$(WORKING_DIRECTORY)/pb/service.pb.go
+ protoc -I=. -I=$(REPOSITORY_DIRECTORY) -I=$(GOPATH)/src -I=$(GOOGLEAPIS_PATH) -I=$(GRPCGATEWAY_MODULE_PATH) --openapiv2_out=$(REPOSITORY_DIRECTORY) \
+ --openapiv2_opt logtostderr=true \
+ $(WORKING_DIRECTORY)/pb/service.proto
+ protoc -I=. -I=$(REPOSITORY_DIRECTORY) -I=$(GOPATH)/src -I=$(GOOGLEAPIS_PATH) -I=$(GRPCGATEWAY_MODULE_PATH) --grpc-gateway_out=$(REPOSITORY_DIRECTORY) \
+ --grpc-gateway_opt logtostderr=true \
+ --grpc-gateway_opt paths=source_relative \
+ $(WORKING_DIRECTORY)/pb/service.proto
+ protoc -I=. -I=$(REPOSITORY_DIRECTORY) -I=$(GOPATH)/src -I=$(GOOGLEAPIS_PATH) -I=$(GRPCGATEWAY_MODULE_PATH) --go-grpc_out=$(GOPATH)/src \
+ $(WORKING_DIRECTORY)/pb/service.proto
+ protoc -I=. -I=$(REPOSITORY_DIRECTORY) -I=$(GOPATH)/src -I=$(GOOGLEAPIS_PATH) -I=$(GRPCGATEWAY_MODULE_PATH) --doc_out=$(WORKING_DIRECTORY)/pb --doc_opt=markdown,README.md $(WORKING_DIRECTORY)/pb/*.proto
+ protoc -I=. -I=$(REPOSITORY_DIRECTORY) -I=$(GOPATH)/src -I=$(GOOGLEAPIS_PATH) -I=$(GRPCGATEWAY_MODULE_PATH) --doc_out=$(WORKING_DIRECTORY)/pb --doc_opt=html,doc.html $(WORKING_DIRECTORY)/pb/*.proto
+
+.PHONY: build-servicecontainer build push proto/generate
diff --git a/snippet-service/cmd/service/main.go b/snippet-service/cmd/service/main.go
new file mode 100644
index 000000000..ce496526c
--- /dev/null
+++ b/snippet-service/cmd/service/main.go
@@ -0,0 +1,48 @@
+package main
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/plgd-dev/hub/v2/pkg/build"
+ "github.com/plgd-dev/hub/v2/pkg/config"
+ "github.com/plgd-dev/hub/v2/pkg/fsnotify"
+ "github.com/plgd-dev/hub/v2/pkg/log"
+ "github.com/plgd-dev/hub/v2/snippet-service/service"
+)
+
+func run(cfg service.Config, logger log.Logger) error {
+ fileWatcher, err := fsnotify.NewWatcher(logger)
+ if err != nil {
+ return fmt.Errorf("cannot create file fileWatcher: %w", err)
+ }
+ defer func() {
+ _ = fileWatcher.Close()
+ }()
+
+ s, err := service.New(context.Background(), cfg, fileWatcher, logger)
+ if err != nil {
+ return fmt.Errorf("cannot create service: %w", err)
+ }
+ err = s.Serve()
+ if err != nil {
+ return fmt.Errorf("cannot serve service: %w", err)
+ }
+
+ return nil
+}
+
+func main() {
+ var cfg service.Config
+ if err := config.LoadAndValidateConfig(&cfg); err != nil {
+ log.Fatalf("cannot load config: %v", err)
+ }
+ logger := log.NewLogger(cfg.Log)
+ log.Set(logger)
+ logger.Debugf("version: %v, buildDate: %v, buildRevision %v", build.Version, build.BuildDate, build.CommitHash)
+ log.Infof("config: %v", cfg.String())
+
+ if err := run(cfg, logger); err != nil {
+ log.Fatalf("cannot run service: %v", err)
+ }
+}
diff --git a/snippet-service/config.yaml b/snippet-service/config.yaml
new file mode 100644
index 000000000..b9d27fd01
--- /dev/null
+++ b/snippet-service/config.yaml
@@ -0,0 +1,106 @@
+hubID: ""
+log:
+ level: info
+ encoding: json
+ stacktrace:
+ enabled: false
+ level: warn
+ encoderConfig:
+ timeEncoder: rfc3339nano
+apis:
+ grpc:
+ address: "0.0.0.0:9100"
+ sendMsgSize: 4194304
+ recvMsgSize: 4194304
+ enforcementPolicy:
+ minTime: 5s
+ permitWithoutStream: true
+ keepAlive:
+ # 0s - means infinity
+ maxConnectionIdle: 0s
+ # 0s - means infinity
+ maxConnectionAge: 0s
+ # 0s - means infinity
+ maxConnectionAgeGrace: 0s
+ time: 2h
+ timeout: 20s
+ tls:
+ caPool: "/secrets/public/rootca.crt"
+ keyFile: "/secrets/private/cert.key"
+ certFile: "/secrets/private/cert.crt"
+ clientCertificateRequired: true
+ authorization:
+ ownerClaim: "sub"
+ authority: ""
+ audience: ""
+ http:
+ maxIdleConns: 16
+ maxConnsPerHost: 32
+ maxIdleConnsPerHost: 16
+ idleConnTimeout: "30s"
+ timeout: "10s"
+ tls:
+ caPool: "/secrets/public/rootca.crt"
+ keyFile: "/secrets/private/cert.key"
+ certFile: "/secrets/public/cert.crt"
+ useSystemCAPool: false
+ http:
+ address: "0.0.0.0:9101"
+ readTimeout: 8s
+ readHeaderTimeout: 4s
+ writeTimeout: 16s
+ idleTimeout: 30s
+clients:
+ storage:
+ use: mongoDB
+ mongoDB:
+ uri:
+ database: snippetService
+ maxPoolSize: 16
+ maxConnIdleTime: 4m0s
+ tls:
+ caPool: "/secrets/public/rootca.crt"
+ keyFile: "/secrets/private/cert.key"
+ certFile: "/secrets/public/cert.crt"
+ useSystemCAPool: false
+ bulkWrite:
+ timeout: 1m0s
+ throttleTime: 500ms
+ documentLimit: 1000
+ cqlDB:
+ table: "snippets"
+ hosts: []
+ port: 9142
+ numConnections: 16
+ connectTimeout: 10s
+ useHostnameResolution: true
+ reconnectionPolicy:
+ constant:
+ interval: 3s
+ maxRetries: 3
+ keyspace:
+ name: plgdhub
+ create: true
+ replication:
+ class: SimpleStrategy
+ replication_factor: 1
+ tls:
+ caPool: "/secrets/public/rootca.crt"
+ keyFile: "/secrets/private/cert.key"
+ certFile: "/secrets/public/cert.crt"
+ useSystemCAPool: false
+ openTelemetryCollector:
+ grpc:
+ enabled: false
+ address: ""
+ sendMsgSize: 4194304
+ recvMsgSize: 4194304
+ keepAlive:
+ time: 10s
+ timeout: 20s
+ permitWithoutStream: true
+ tls:
+ caPool: "/secrets/public/rootca.crt"
+ keyFile: "/secrets/private/cert.key"
+ certFile: "/secrets/public/cert.crt"
+ useSystemCAPool: false
diff --git a/snippet-service/pb/README.md b/snippet-service/pb/README.md
new file mode 100644
index 000000000..e021a4c5b
--- /dev/null
+++ b/snippet-service/pb/README.md
@@ -0,0 +1,416 @@
+# Protocol Documentation
+
+
+## Table of Contents
+
+- [snippet-service/pb/service.proto](#snippet-service_pb_service-proto)
+ - [AppliedDeviceConfiguration](#snippetservice-pb-AppliedDeviceConfiguration)
+ - [AppliedDeviceConfiguration.RelationTo](#snippetservice-pb-AppliedDeviceConfiguration-RelationTo)
+ - [AppliedDeviceConfiguration.Resource](#snippetservice-pb-AppliedDeviceConfiguration-Resource)
+ - [Condition](#snippetservice-pb-Condition)
+ - [Configuration](#snippetservice-pb-Configuration)
+ - [Configuration.Resource](#snippetservice-pb-Configuration-Resource)
+ - [DeleteAppliedDeviceConfigurationsRequest](#snippetservice-pb-DeleteAppliedDeviceConfigurationsRequest)
+ - [DeleteAppliedDeviceConfigurationsResponse](#snippetservice-pb-DeleteAppliedDeviceConfigurationsResponse)
+ - [DeleteConditionsRequest](#snippetservice-pb-DeleteConditionsRequest)
+ - [DeleteConditionsResponse](#snippetservice-pb-DeleteConditionsResponse)
+ - [DeleteConfigurationsRequest](#snippetservice-pb-DeleteConfigurationsRequest)
+ - [DeleteConfigurationsResponse](#snippetservice-pb-DeleteConfigurationsResponse)
+ - [GetAppliedDeviceConfigurationsRequest](#snippetservice-pb-GetAppliedDeviceConfigurationsRequest)
+ - [GetConditionsRequest](#snippetservice-pb-GetConditionsRequest)
+ - [GetConfigurationsRequest](#snippetservice-pb-GetConfigurationsRequest)
+ - [IDFilter](#snippetservice-pb-IDFilter)
+ - [InvokeConfigurationRequest](#snippetservice-pb-InvokeConfigurationRequest)
+
+ - [AppliedDeviceConfiguration.Resource.Status](#snippetservice-pb-AppliedDeviceConfiguration-Resource-Status)
+
+ - [SnippetService](#snippetservice-pb-SnippetService)
+
+- [Scalar Value Types](#scalar-value-types)
+
+
+
+
+
Top
+
+## snippet-service/pb/service.proto
+
+
+
+
+
+### AppliedDeviceConfiguration
+TODO naming
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| id | [string](#string) | | |
+| device_id | [string](#string) | | |
+| configuration_id | [AppliedDeviceConfiguration.RelationTo](#snippetservice-pb-AppliedDeviceConfiguration-RelationTo) | | |
+| on_demand | [bool](#bool) | | |
+| condition_id | [AppliedDeviceConfiguration.RelationTo](#snippetservice-pb-AppliedDeviceConfiguration-RelationTo) | | TODO Naming |
+| resources | [AppliedDeviceConfiguration.Resource](#snippetservice-pb-AppliedDeviceConfiguration-Resource) | repeated | TODO naming |
+| owner | [string](#string) | | |
+| timestamp | [int64](#int64) | | Unix timestamp in ns when the applied device configuration has been created/updated
+
+@gotags: bson:"timestamp" |
+
+
+
+
+
+
+
+
+### AppliedDeviceConfiguration.RelationTo
+TODO naming
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| id | [string](#string) | | |
+| version | [uint64](#uint64) | | |
+
+
+
+
+
+
+
+
+### AppliedDeviceConfiguration.Resource
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| resource_id | [resourceaggregate.pb.ResourceId](#resourceaggregate-pb-ResourceId) | | TODO Jozo href only? |
+| correlation_id | [string](#string) | | Reused from invoke command or generated. Can be used to retrieve corresponding pending command. |
+| status | [AppliedDeviceConfiguration.Resource.Status](#snippetservice-pb-AppliedDeviceConfiguration-Resource-Status) | | |
+| resource_updated | [resourceaggregate.pb.ResourceUpdated](#resourceaggregate-pb-ResourceUpdated) | | |
+
+
+
+
+
+
+
+
+### Condition
+driven by resource change event
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| id | [string](#string) | | Condition ID
+
+@gotags: bson:"id" |
+| version | [uint64](#uint64) | | @gotags: bson:"version" |
+| name | [string](#string) | | @gotags: bson:"name,omitempty" |
+| enabled | [bool](#bool) | | @gotags: bson:"enabled,omitempty" |
+| configuration_id | [string](#string) | | ID of the configuration to be applied when the condition is satisfied
+
+@gotags: bson:"configurationId" |
+| device_id_filter | [string](#string) | repeated | list of device IDs to which the condition applies
+
+@gotags: bson:"deviceIdFilter,omitempty" |
+| resource_type_filter | [string](#string) | repeated | @gotags: bson:"resourceTypeFilter,omitempty" |
+| resource_href_filter | [string](#string) | repeated | list of resource hrefs to which the condition applies
+
+@gotags: bson:"resourceHrefFilter,omitempty" |
+| jq_expression_filter | [string](#string) | | @gotags: bson:"jqExpressionFilter,omitempty" |
+| api_access_token | [string](#string) | | Token used to update resources in the configuration
+
+@gotags: bson:"apiAccessToken,omitempty" |
+| owner | [string](#string) | | Condition owner
+
+@gotags: bson:"owner" |
+| timestamp | [int64](#int64) | | Unix timestamp in ns when the condition has been created/updated
+
+@gotags: bson:"timestamp" |
+
+
+
+
+
+
+
+
+### Configuration
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| id | [string](#string) | | Configuration ID |
+| version | [uint64](#uint64) | | Configuration version |
+| name | [string](#string) | | User-friendly configuration name
+
+@gotags: bson:"name,omitempty" |
+| resources | [Configuration.Resource](#snippetservice-pb-Configuration-Resource) | repeated | List of resource updates to be applied |
+| owner | [string](#string) | | Configuration owner |
+| timestamp | [int64](#int64) | | Unix timestamp in ns when the configuration has been created/updated
+
+@gotags: bson:"timestamp" |
+
+
+
+
+
+
+
+
+### Configuration.Resource
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| href | [string](#string) | | href of the resource |
+| content | [resourceaggregate.pb.Content](#resourceaggregate-pb-Content) | | content update of the resource |
+| time_to_live | [int64](#int64) | | optional update command time to live, 0 is infinite |
+
+
+
+
+
+
+
+
+### DeleteAppliedDeviceConfigurationsRequest
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| id_filter | [string](#string) | repeated | |
+
+
+
+
+
+
+
+
+### DeleteAppliedDeviceConfigurationsResponse
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| count | [int64](#int64) | | |
+
+
+
+
+
+
+
+
+### DeleteConditionsRequest
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| id_filter | [IDFilter](#snippetservice-pb-IDFilter) | repeated | |
+
+
+
+
+
+
+
+
+### DeleteConditionsResponse
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| count | [int64](#int64) | | |
+
+
+
+
+
+
+
+
+### DeleteConfigurationsRequest
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| id_filter | [IDFilter](#snippetservice-pb-IDFilter) | repeated | |
+
+
+
+
+
+
+
+
+### DeleteConfigurationsResponse
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| count | [int64](#int64) | | |
+
+
+
+
+
+
+
+
+### GetAppliedDeviceConfigurationsRequest
+TODO Naming
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| id_filter | [string](#string) | repeated | |
+| configuration_id_filter | [IDFilter](#snippetservice-pb-IDFilter) | repeated | |
+| device_id_filter | [string](#string) | repeated | |
+| condition_id_filter | [IDFilter](#snippetservice-pb-IDFilter) | repeated | |
+
+
+
+
+
+
+
+
+### GetConditionsRequest
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| id_filter | [IDFilter](#snippetservice-pb-IDFilter) | repeated | |
+
+
+
+
+
+
+
+
+### GetConfigurationsRequest
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| id_filter | [IDFilter](#snippetservice-pb-IDFilter) | repeated | |
+| http_id_filter | [string](#string) | repeated | **Deprecated.** Format: {id}/{version}, e.g., "ae424c58-e517-4494-6de7-583536c48213/all" or "ae424c58-e517-4494-6de7-583536c48213/latest" or "ae424c58-e517-4494-6de7-583536c48213/{version}" |
+
+
+
+
+
+
+
+
+### IDFilter
+TODO: /configurations/123?version=latest
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| id | [string](#string) | | |
+| value | [uint64](#uint64) | | |
+| all | [bool](#bool) | | |
+| latest | [bool](#bool) | | |
+
+
+
+
+
+
+
+
+### InvokeConfigurationRequest
+
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| configuration_id | [string](#string) | | applies latest configuration |
+| device_id | [string](#string) | | |
+| force | [bool](#bool) | | force update even if the configuration has already been applied to device |
+| correlation_id | [string](#string) | | propagated down to the resource update command |
+
+
+
+
+
+
+
+
+
+
+### AppliedDeviceConfiguration.Resource.Status
+
+
+| Name | Number | Description |
+| ---- | ------ | ----------- |
+| QUEUED | 0 | |
+| PENDING | 1 | |
+| DONE | 2 | If done look to resource_updated even update resource failed for resource aggregate. |
+| TIMEOUT | 3 | |
+
+
+
+
+
+
+
+
+
+### SnippetService
+
+
+| Method Name | Request Type | Response Type | Description |
+| ----------- | ------------ | ------------- | ------------|
+| CreateCondition | [Condition](#snippetservice-pb-Condition) | [Condition](#snippetservice-pb-Condition) | |
+| GetConditions | [GetConditionsRequest](#snippetservice-pb-GetConditionsRequest) | [Condition](#snippetservice-pb-Condition) stream | |
+| DeleteConditions | [DeleteConditionsRequest](#snippetservice-pb-DeleteConditionsRequest) | [DeleteConditionsResponse](#snippetservice-pb-DeleteConditionsResponse) | |
+| UpdateCondition | [Condition](#snippetservice-pb-Condition) | [Condition](#snippetservice-pb-Condition) | |
+| CreateConfiguration | [Configuration](#snippetservice-pb-Configuration) | [Configuration](#snippetservice-pb-Configuration) | |
+| GetConfigurations | [GetConfigurationsRequest](#snippetservice-pb-GetConfigurationsRequest) | [Configuration](#snippetservice-pb-Configuration) stream | |
+| DeleteConfigurations | [DeleteConfigurationsRequest](#snippetservice-pb-DeleteConfigurationsRequest) | [DeleteConfigurationsResponse](#snippetservice-pb-DeleteConfigurationsResponse) | |
+| UpdateConfiguration | [Configuration](#snippetservice-pb-Configuration) | [Configuration](#snippetservice-pb-Configuration) | |
+| InvokeConfiguration | [InvokeConfigurationRequest](#snippetservice-pb-InvokeConfigurationRequest) | [AppliedDeviceConfiguration](#snippetservice-pb-AppliedDeviceConfiguration) stream | streaming process of update configuration to invoker |
+| GetAppliedConfigurations | [GetAppliedDeviceConfigurationsRequest](#snippetservice-pb-GetAppliedDeviceConfigurationsRequest) | [AppliedDeviceConfiguration](#snippetservice-pb-AppliedDeviceConfiguration) stream | |
+| DeleteAppliedConfigurations | [DeleteAppliedDeviceConfigurationsRequest](#snippetservice-pb-DeleteAppliedDeviceConfigurationsRequest) | [DeleteAppliedDeviceConfigurationsResponse](#snippetservice-pb-DeleteAppliedDeviceConfigurationsResponse) | |
+
+
+
+
+
+## Scalar Value Types
+
+| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby |
+| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- |
+| double | | double | double | float | float64 | double | float | Float |
+| float | | float | float | float | float32 | float | float | Float |
+| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
+| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum |
+| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) |
+| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) |
+| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
+| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum |
+| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) |
+| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum |
+| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) |
+| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum |
+| bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass |
+| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) |
+| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) |
+
diff --git a/snippet-service/pb/configuration.go b/snippet-service/pb/configuration.go
new file mode 100644
index 000000000..c39b996ad
--- /dev/null
+++ b/snippet-service/pb/configuration.go
@@ -0,0 +1,26 @@
+package pb
+
+import (
+ "errors"
+ "fmt"
+ "slices"
+ "strings"
+
+ "github.com/google/uuid"
+)
+
+func (c *Configuration) ValidateAndNormalize() error {
+ if _, err := uuid.Parse(c.GetId()); err != nil {
+ return fmt.Errorf("invalid configuration ID(%v): %w", c.GetId(), err)
+ }
+ if len(c.GetResources()) == 0 {
+ return errors.New("invalid configuration resources")
+ }
+ if c.GetOwner() == "" {
+ return errors.New("empty configuration owner")
+ }
+ slices.SortFunc(c.GetResources(), func(i, j *Configuration_Resource) int {
+ return strings.Compare(i.GetHref(), j.GetHref())
+ })
+ return nil
+}
diff --git a/snippet-service/pb/doc.html b/snippet-service/pb/doc.html
new file mode 100644
index 000000000..3617c39de
--- /dev/null
+++ b/snippet-service/pb/doc.html
@@ -0,0 +1,1448 @@
+
+
+
+
+ Protocol Documentation
+
+
+
+
+
+
+
+
+
+
+ Protocol Documentation
+
+ Table of Contents
+
+
+
+
+
+
+
snippet-service/pb/service.proto Top
+
+
+
+
+ AppliedDeviceConfiguration
+ TODO naming
+
+
+
+
+
+
+
+
+ AppliedDeviceConfiguration.RelationTo
+ TODO naming
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ id
+ string
+
+
+
+
+
+ version
+ uint64
+
+
+
+
+
+
+
+
+
+
+
+ AppliedDeviceConfiguration.Resource
+
+
+
+
+
+
+
+
+
+ Condition
+ driven by resource change event
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ id
+ string
+
+ Condition ID
+
+@gotags: bson:"id"
+
+
+
+ version
+ uint64
+
+ @gotags: bson:"version"
+
+
+
+ name
+ string
+
+ @gotags: bson:"name,omitempty"
+
+
+
+ enabled
+ bool
+
+ @gotags: bson:"enabled,omitempty"
+
+
+
+ configuration_id
+ string
+
+ ID of the configuration to be applied when the condition is satisfied
+
+@gotags: bson:"configurationId"
+
+
+
+ device_id_filter
+ string
+ repeated
+ list of device IDs to which the condition applies
+
+@gotags: bson:"deviceIdFilter,omitempty"
+
+
+
+ resource_type_filter
+ string
+ repeated
+ @gotags: bson:"resourceTypeFilter,omitempty"
+
+
+
+ resource_href_filter
+ string
+ repeated
+ list of resource hrefs to which the condition applies
+
+@gotags: bson:"resourceHrefFilter,omitempty"
+
+
+
+ jq_expression_filter
+ string
+
+ @gotags: bson:"jqExpressionFilter,omitempty"
+
+
+
+ api_access_token
+ string
+
+ Token used to update resources in the configuration
+
+@gotags: bson:"apiAccessToken,omitempty"
+
+
+
+ owner
+ string
+
+ Condition owner
+
+@gotags: bson:"owner"
+
+
+
+ timestamp
+ int64
+
+ Unix timestamp in ns when the condition has been created/updated
+
+@gotags: bson:"timestamp"
+
+
+
+
+
+
+
+
+
+ Configuration
+
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ id
+ string
+
+ Configuration ID
+
+
+
+ version
+ uint64
+
+ Configuration version
+
+
+
+ name
+ string
+
+ User-friendly configuration name
+
+@gotags: bson:"name,omitempty"
+
+
+
+ resources
+ Configuration.Resource
+ repeated
+ List of resource updates to be applied
+
+
+
+ owner
+ string
+
+ Configuration owner
+
+
+
+ timestamp
+ int64
+
+ Unix timestamp in ns when the configuration has been created/updated
+
+@gotags: bson:"timestamp"
+
+
+
+
+
+
+
+
+
+ Configuration.Resource
+
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ href
+ string
+
+ href of the resource
+
+
+
+ content
+ resourceaggregate.pb.Content
+
+ content update of the resource
+
+
+
+ time_to_live
+ int64
+
+ optional update command time to live, 0 is infinite
+
+
+
+
+
+
+
+
+
+ DeleteAppliedDeviceConfigurationsRequest
+
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ id_filter
+ string
+ repeated
+
+
+
+
+
+
+
+
+
+
+ DeleteAppliedDeviceConfigurationsResponse
+
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ count
+ int64
+
+
+
+
+
+
+
+
+
+
+
+ DeleteConditionsRequest
+
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ id_filter
+ IDFilter
+ repeated
+
+
+
+
+
+
+
+
+
+
+ DeleteConditionsResponse
+
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ count
+ int64
+
+
+
+
+
+
+
+
+
+
+
+ DeleteConfigurationsRequest
+
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ id_filter
+ IDFilter
+ repeated
+
+
+
+
+
+
+
+
+
+
+ DeleteConfigurationsResponse
+
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ count
+ int64
+
+
+
+
+
+
+
+
+
+
+
+ GetAppliedDeviceConfigurationsRequest
+ TODO Naming
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ id_filter
+ string
+ repeated
+
+
+
+
+ configuration_id_filter
+ IDFilter
+ repeated
+
+
+
+
+ device_id_filter
+ string
+ repeated
+
+
+
+
+ condition_id_filter
+ IDFilter
+ repeated
+
+
+
+
+
+
+
+
+
+
+ GetConditionsRequest
+
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ id_filter
+ IDFilter
+ repeated
+
+
+
+
+
+
+
+
+
+
+ GetConfigurationsRequest
+
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ id_filter
+ IDFilter
+ repeated
+
+
+
+
+ http_id_filter
+ string
+ repeated
+ Deprecated. Format: {id}/{version}, e.g., "ae424c58-e517-4494-6de7-583536c48213/all" or "ae424c58-e517-4494-6de7-583536c48213/latest" or "ae424c58-e517-4494-6de7-583536c48213/{version}"
+
+
+
+
+
+
+
+
+ Fields with deprecated option
+
+
+
+ Name
+ Option
+
+
+
+
+
+ http_id_filter
+ true
+
+
+
+
+
+
+
+
+
+ IDFilter
+ TODO: /configurations/123?version=latest
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ id
+ string
+
+
+
+
+
+ value
+ uint64
+
+
+
+
+
+ all
+ bool
+
+
+
+
+
+ latest
+ bool
+
+
+
+
+
+
+
+
+
+
+
+ InvokeConfigurationRequest
+
+
+
+
+
+ Field Type Label Description
+
+
+
+
+ configuration_id
+ string
+
+ applies latest configuration
+
+
+
+ device_id
+ string
+
+
+
+
+
+ force
+ bool
+
+ force update even if the configuration has already been applied to device
+
+
+
+ correlation_id
+ string
+
+ propagated down to the resource update command
+
+
+
+
+
+
+
+
+
+
+
+ AppliedDeviceConfiguration.Resource.Status
+
+
+
+ Name Number Description
+
+
+
+
+ QUEUED
+ 0
+
+
+
+
+ PENDING
+ 1
+
+
+
+
+ DONE
+ 2
+ If done look to resource_updated even update resource failed for resource aggregate.
+
+
+
+ TIMEOUT
+ 3
+
+
+
+
+
+
+
+
+
+
+ SnippetService
+
+
+
+
+
+
+ Methods with HTTP bindings
+
+
+
+ Method Name
+ Method
+ Pattern
+ Body
+
+
+
+
+
+
+
+ CreateCondition
+ POST
+ /snippet-service/api/v1/conditions
+ *
+
+
+
+
+
+
+ GetConditions
+ GET
+ /snippet-service/api/v1/conditions
+
+
+
+
+
+
+
+ DeleteConditions
+ DELETE
+ /snippet-service/api/v1/conditions
+
+
+
+
+
+
+
+ UpdateCondition
+ PUT
+ /snippet-service/api/v1/conditions/{id}
+ *
+
+
+
+
+
+
+ CreateConfiguration
+ POST
+ /snippet-service/api/v1/configurations
+ *
+
+
+
+
+
+
+ GetConfigurations
+ GET
+ /snippet-service/api/v1/configurations
+
+
+
+
+
+
+
+ DeleteConfigurations
+ DELETE
+ /snippet-service/api/v1/configurations
+
+
+
+
+
+
+
+ UpdateConfiguration
+ PUT
+ /snippet-service/api/v1/configurations/{id}
+ *
+
+
+
+
+
+
+ InvokeConfiguration
+ POST
+ /snippet-service/api/v1/configurations/{configuration_id}
+ *
+
+
+
+
+
+
+ GetAppliedConfigurations
+ GET
+ /snippet-service/api/v1/configurations/applied
+
+
+
+
+
+
+
+ DeleteAppliedConfigurations
+ DELETE
+ /snippet-service/api/v1/configurations/applied
+
+
+
+
+
+
+
+
+
+
+ Scalar Value Types
+
+
+ .proto Type Notes C++ Java Python Go C# PHP Ruby
+
+
+
+
+ double
+
+ double
+ double
+ float
+ float64
+ double
+ float
+ Float
+
+
+
+ float
+
+ float
+ float
+ float
+ float32
+ float
+ float
+ Float
+
+
+
+ int32
+ Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead.
+ int32
+ int
+ int
+ int32
+ int
+ integer
+ Bignum or Fixnum (as required)
+
+
+
+ int64
+ Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead.
+ int64
+ long
+ int/long
+ int64
+ long
+ integer/string
+ Bignum
+
+
+
+ uint32
+ Uses variable-length encoding.
+ uint32
+ int
+ int/long
+ uint32
+ uint
+ integer
+ Bignum or Fixnum (as required)
+
+
+
+ uint64
+ Uses variable-length encoding.
+ uint64
+ long
+ int/long
+ uint64
+ ulong
+ integer/string
+ Bignum or Fixnum (as required)
+
+
+
+ sint32
+ Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s.
+ int32
+ int
+ int
+ int32
+ int
+ integer
+ Bignum or Fixnum (as required)
+
+
+
+ sint64
+ Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s.
+ int64
+ long
+ int/long
+ int64
+ long
+ integer/string
+ Bignum
+
+
+
+ fixed32
+ Always four bytes. More efficient than uint32 if values are often greater than 2^28.
+ uint32
+ int
+ int
+ uint32
+ uint
+ integer
+ Bignum or Fixnum (as required)
+
+
+
+ fixed64
+ Always eight bytes. More efficient than uint64 if values are often greater than 2^56.
+ uint64
+ long
+ int/long
+ uint64
+ ulong
+ integer/string
+ Bignum
+
+
+
+ sfixed32
+ Always four bytes.
+ int32
+ int
+ int
+ int32
+ int
+ integer
+ Bignum or Fixnum (as required)
+
+
+
+ sfixed64
+ Always eight bytes.
+ int64
+ long
+ int/long
+ int64
+ long
+ integer/string
+ Bignum
+
+
+
+ bool
+
+ bool
+ boolean
+ boolean
+ bool
+ bool
+ boolean
+ TrueClass/FalseClass
+
+
+
+ string
+ A string must always contain UTF-8 encoded or 7-bit ASCII text.
+ string
+ String
+ str/unicode
+ string
+ string
+ string
+ String (UTF-8)
+
+
+
+ bytes
+ May contain any arbitrary sequence of bytes.
+ string
+ ByteString
+ str
+ []byte
+ ByteString
+ string
+ String (ASCII-8BIT)
+
+
+
+
+
+
+
diff --git a/snippet-service/pb/service.pb.go b/snippet-service/pb/service.pb.go
new file mode 100644
index 000000000..c36093c54
--- /dev/null
+++ b/snippet-service/pb/service.pb.go
@@ -0,0 +1,1936 @@
+// TODO overit ze pending command sa nezmaze na neexistujucom resource ak sa device pripoji a nepublishne hned resource
+// Overit correlation id - ak sa pouziva rovnake napriec viacerymi resourcami
+
+// scenare
+// - Uzivatel vie vytvorit config, automaticka (backend) inkrementacia verzie
+// - Uzivatel updatne config, verzia sa inkrementuje, Modal -> chces aplikovat na vsetky uz provisionnute devici? Informovat uzivatela, ze niektore devici mozu byt offline a command moze vyexpirovat.
+// - Uzivatel updatne config, verzia sa inkrementuje, informujeme uzivatela ze vsetky pending commandy z predoslej verzie budu cancelnute ako aj dalsie sekvencne updaty resourcov pre predoslu verziu
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.34.1
+// protoc v5.26.1
+// source: snippet-service/pb/service.proto
+
+package pb
+
+import (
+ _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
+ commands "github.com/plgd-dev/hub/v2/resource-aggregate/commands"
+ events "github.com/plgd-dev/hub/v2/resource-aggregate/events"
+ _ "google.golang.org/genproto/googleapis/api/annotations"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type AppliedDeviceConfiguration_Resource_Status int32
+
+const (
+ AppliedDeviceConfiguration_Resource_QUEUED AppliedDeviceConfiguration_Resource_Status = 0
+ AppliedDeviceConfiguration_Resource_PENDING AppliedDeviceConfiguration_Resource_Status = 1
+ AppliedDeviceConfiguration_Resource_DONE AppliedDeviceConfiguration_Resource_Status = 2 // If done look to resource_updated even update resource failed for resource aggregate.
+ AppliedDeviceConfiguration_Resource_TIMEOUT AppliedDeviceConfiguration_Resource_Status = 3
+)
+
+// Enum value maps for AppliedDeviceConfiguration_Resource_Status.
+var (
+ AppliedDeviceConfiguration_Resource_Status_name = map[int32]string{
+ 0: "QUEUED",
+ 1: "PENDING",
+ 2: "DONE",
+ 3: "TIMEOUT",
+ }
+ AppliedDeviceConfiguration_Resource_Status_value = map[string]int32{
+ "QUEUED": 0,
+ "PENDING": 1,
+ "DONE": 2,
+ "TIMEOUT": 3,
+ }
+)
+
+func (x AppliedDeviceConfiguration_Resource_Status) Enum() *AppliedDeviceConfiguration_Resource_Status {
+ p := new(AppliedDeviceConfiguration_Resource_Status)
+ *p = x
+ return p
+}
+
+func (x AppliedDeviceConfiguration_Resource_Status) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (AppliedDeviceConfiguration_Resource_Status) Descriptor() protoreflect.EnumDescriptor {
+ return file_snippet_service_pb_service_proto_enumTypes[0].Descriptor()
+}
+
+func (AppliedDeviceConfiguration_Resource_Status) Type() protoreflect.EnumType {
+ return &file_snippet_service_pb_service_proto_enumTypes[0]
+}
+
+func (x AppliedDeviceConfiguration_Resource_Status) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use AppliedDeviceConfiguration_Resource_Status.Descriptor instead.
+func (AppliedDeviceConfiguration_Resource_Status) EnumDescriptor() ([]byte, []int) {
+ return file_snippet_service_pb_service_proto_rawDescGZIP(), []int{9, 0, 0}
+}
+
+// TODO: /configurations/123?version=latest
+type IDFilter struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ // Types that are assignable to Version:
+ //
+ // *IDFilter_Value
+ // *IDFilter_All
+ // *IDFilter_Latest
+ Version isIDFilter_Version `protobuf_oneof:"version"`
+}
+
+func (x *IDFilter) Reset() {
+ *x = IDFilter{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *IDFilter) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*IDFilter) ProtoMessage() {}
+
+func (x *IDFilter) ProtoReflect() protoreflect.Message {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use IDFilter.ProtoReflect.Descriptor instead.
+func (*IDFilter) Descriptor() ([]byte, []int) {
+ return file_snippet_service_pb_service_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *IDFilter) GetId() string {
+ if x != nil {
+ return x.Id
+ }
+ return ""
+}
+
+func (m *IDFilter) GetVersion() isIDFilter_Version {
+ if m != nil {
+ return m.Version
+ }
+ return nil
+}
+
+func (x *IDFilter) GetValue() uint64 {
+ if x, ok := x.GetVersion().(*IDFilter_Value); ok {
+ return x.Value
+ }
+ return 0
+}
+
+func (x *IDFilter) GetAll() bool {
+ if x, ok := x.GetVersion().(*IDFilter_All); ok {
+ return x.All
+ }
+ return false
+}
+
+func (x *IDFilter) GetLatest() bool {
+ if x, ok := x.GetVersion().(*IDFilter_Latest); ok {
+ return x.Latest
+ }
+ return false
+}
+
+type isIDFilter_Version interface {
+ isIDFilter_Version()
+}
+
+type IDFilter_Value struct {
+ Value uint64 `protobuf:"varint,2,opt,name=value,proto3,oneof"`
+}
+
+type IDFilter_All struct {
+ All bool `protobuf:"varint,3,opt,name=all,proto3,oneof"`
+}
+
+type IDFilter_Latest struct {
+ Latest bool `protobuf:"varint,4,opt,name=latest,proto3,oneof"`
+}
+
+func (*IDFilter_Value) isIDFilter_Version() {}
+
+func (*IDFilter_All) isIDFilter_Version() {}
+
+func (*IDFilter_Latest) isIDFilter_Version() {}
+
+type Condition struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Condition ID
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" bson:"id"`
+ Version uint64 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty" bson:"version"`
+ Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty" bson:"name,omitempty"`
+ Enabled bool `protobuf:"varint,4,opt,name=enabled,proto3" json:"enabled,omitempty" bson:"enabled,omitempty"`
+ // ID of the configuration to be applied when the condition is satisfied
+ ConfigurationId string `protobuf:"bytes,5,opt,name=configuration_id,json=configurationId,proto3" json:"configuration_id,omitempty" bson:"configurationId"`
+ // list of device IDs to which the condition applies
+ DeviceIdFilter []string `protobuf:"bytes,6,rep,name=device_id_filter,json=deviceIdFilter,proto3" json:"device_id_filter,omitempty" bson:"deviceIdFilter,omitempty"`
+ ResourceTypeFilter []string `protobuf:"bytes,7,rep,name=resource_type_filter,json=resourceTypeFilter,proto3" json:"resource_type_filter,omitempty" bson:"resourceTypeFilter,omitempty"`
+ // list of resource hrefs to which the condition applies
+ ResourceHrefFilter []string `protobuf:"bytes,8,rep,name=resource_href_filter,json=resourceHrefFilter,proto3" json:"resource_href_filter,omitempty" bson:"resourceHrefFilter,omitempty"`
+ JqExpressionFilter string `protobuf:"bytes,9,opt,name=jq_expression_filter,json=jqExpressionFilter,proto3" json:"jq_expression_filter,omitempty" bson:"jqExpressionFilter,omitempty"`
+ // Token used to update resources in the configuration
+ ApiAccessToken string `protobuf:"bytes,10,opt,name=api_access_token,json=apiAccessToken,proto3" json:"api_access_token,omitempty" bson:"apiAccessToken,omitempty"`
+ // Condition owner
+ Owner string `protobuf:"bytes,11,opt,name=owner,proto3" json:"owner,omitempty" bson:"owner"`
+ // Unix timestamp in ns when the condition has been created/updated
+ Timestamp int64 `protobuf:"varint,12,opt,name=timestamp,proto3" json:"timestamp,omitempty" bson:"timestamp"`
+}
+
+func (x *Condition) Reset() {
+ *x = Condition{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Condition) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Condition) ProtoMessage() {}
+
+func (x *Condition) ProtoReflect() protoreflect.Message {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Condition.ProtoReflect.Descriptor instead.
+func (*Condition) Descriptor() ([]byte, []int) {
+ return file_snippet_service_pb_service_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *Condition) GetId() string {
+ if x != nil {
+ return x.Id
+ }
+ return ""
+}
+
+func (x *Condition) GetVersion() uint64 {
+ if x != nil {
+ return x.Version
+ }
+ return 0
+}
+
+func (x *Condition) GetName() string {
+ if x != nil {
+ return x.Name
+ }
+ return ""
+}
+
+func (x *Condition) GetEnabled() bool {
+ if x != nil {
+ return x.Enabled
+ }
+ return false
+}
+
+func (x *Condition) GetConfigurationId() string {
+ if x != nil {
+ return x.ConfigurationId
+ }
+ return ""
+}
+
+func (x *Condition) GetDeviceIdFilter() []string {
+ if x != nil {
+ return x.DeviceIdFilter
+ }
+ return nil
+}
+
+func (x *Condition) GetResourceTypeFilter() []string {
+ if x != nil {
+ return x.ResourceTypeFilter
+ }
+ return nil
+}
+
+func (x *Condition) GetResourceHrefFilter() []string {
+ if x != nil {
+ return x.ResourceHrefFilter
+ }
+ return nil
+}
+
+func (x *Condition) GetJqExpressionFilter() string {
+ if x != nil {
+ return x.JqExpressionFilter
+ }
+ return ""
+}
+
+func (x *Condition) GetApiAccessToken() string {
+ if x != nil {
+ return x.ApiAccessToken
+ }
+ return ""
+}
+
+func (x *Condition) GetOwner() string {
+ if x != nil {
+ return x.Owner
+ }
+ return ""
+}
+
+func (x *Condition) GetTimestamp() int64 {
+ if x != nil {
+ return x.Timestamp
+ }
+ return 0
+}
+
+type GetConditionsRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ IdFilter []*IDFilter `protobuf:"bytes,1,rep,name=id_filter,json=idFilter,proto3" json:"id_filter,omitempty"`
+}
+
+func (x *GetConditionsRequest) Reset() {
+ *x = GetConditionsRequest{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *GetConditionsRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetConditionsRequest) ProtoMessage() {}
+
+func (x *GetConditionsRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[2]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetConditionsRequest.ProtoReflect.Descriptor instead.
+func (*GetConditionsRequest) Descriptor() ([]byte, []int) {
+ return file_snippet_service_pb_service_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *GetConditionsRequest) GetIdFilter() []*IDFilter {
+ if x != nil {
+ return x.IdFilter
+ }
+ return nil
+}
+
+type DeleteConditionsRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ IdFilter []*IDFilter `protobuf:"bytes,1,rep,name=id_filter,json=idFilter,proto3" json:"id_filter,omitempty"`
+}
+
+func (x *DeleteConditionsRequest) Reset() {
+ *x = DeleteConditionsRequest{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *DeleteConditionsRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DeleteConditionsRequest) ProtoMessage() {}
+
+func (x *DeleteConditionsRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[3]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use DeleteConditionsRequest.ProtoReflect.Descriptor instead.
+func (*DeleteConditionsRequest) Descriptor() ([]byte, []int) {
+ return file_snippet_service_pb_service_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *DeleteConditionsRequest) GetIdFilter() []*IDFilter {
+ if x != nil {
+ return x.IdFilter
+ }
+ return nil
+}
+
+type DeleteConditionsResponse struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Count int64 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"`
+}
+
+func (x *DeleteConditionsResponse) Reset() {
+ *x = DeleteConditionsResponse{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *DeleteConditionsResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DeleteConditionsResponse) ProtoMessage() {}
+
+func (x *DeleteConditionsResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[4]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use DeleteConditionsResponse.ProtoReflect.Descriptor instead.
+func (*DeleteConditionsResponse) Descriptor() ([]byte, []int) {
+ return file_snippet_service_pb_service_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *DeleteConditionsResponse) GetCount() int64 {
+ if x != nil {
+ return x.Count
+ }
+ return 0
+}
+
+type Configuration struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // Configuration ID
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ // Configuration version
+ Version uint64 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"`
+ // User-friendly configuration name
+ Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty" bson:"name,omitempty"`
+ // List of resource updates to be applied
+ Resources []*Configuration_Resource `protobuf:"bytes,4,rep,name=resources,proto3" json:"resources,omitempty"`
+ // Configuration owner
+ Owner string `protobuf:"bytes,5,opt,name=owner,proto3" json:"owner,omitempty"`
+ // Unix timestamp in ns when the configuration has been created/updated
+ Timestamp int64 `protobuf:"varint,6,opt,name=timestamp,proto3" json:"timestamp,omitempty" bson:"timestamp"`
+}
+
+func (x *Configuration) Reset() {
+ *x = Configuration{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[5]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Configuration) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Configuration) ProtoMessage() {}
+
+func (x *Configuration) ProtoReflect() protoreflect.Message {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[5]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Configuration.ProtoReflect.Descriptor instead.
+func (*Configuration) Descriptor() ([]byte, []int) {
+ return file_snippet_service_pb_service_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *Configuration) GetId() string {
+ if x != nil {
+ return x.Id
+ }
+ return ""
+}
+
+func (x *Configuration) GetVersion() uint64 {
+ if x != nil {
+ return x.Version
+ }
+ return 0
+}
+
+func (x *Configuration) GetName() string {
+ if x != nil {
+ return x.Name
+ }
+ return ""
+}
+
+func (x *Configuration) GetResources() []*Configuration_Resource {
+ if x != nil {
+ return x.Resources
+ }
+ return nil
+}
+
+func (x *Configuration) GetOwner() string {
+ if x != nil {
+ return x.Owner
+ }
+ return ""
+}
+
+func (x *Configuration) GetTimestamp() int64 {
+ if x != nil {
+ return x.Timestamp
+ }
+ return 0
+}
+
+type GetConfigurationsRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ IdFilter []*IDFilter `protobuf:"bytes,1,rep,name=id_filter,json=idFilter,proto3" json:"id_filter,omitempty"`
+ // Format: {id}/{version}, e.g., "ae424c58-e517-4494-6de7-583536c48213/all" or "ae424c58-e517-4494-6de7-583536c48213/latest" or "ae424c58-e517-4494-6de7-583536c48213/{version}"
+ //
+ // Deprecated: Marked as deprecated in snippet-service/pb/service.proto.
+ HttpIdFilter []string `protobuf:"bytes,2,rep,name=http_id_filter,json=httpIdFilter,proto3" json:"http_id_filter,omitempty"`
+}
+
+func (x *GetConfigurationsRequest) Reset() {
+ *x = GetConfigurationsRequest{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[6]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *GetConfigurationsRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetConfigurationsRequest) ProtoMessage() {}
+
+func (x *GetConfigurationsRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[6]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetConfigurationsRequest.ProtoReflect.Descriptor instead.
+func (*GetConfigurationsRequest) Descriptor() ([]byte, []int) {
+ return file_snippet_service_pb_service_proto_rawDescGZIP(), []int{6}
+}
+
+func (x *GetConfigurationsRequest) GetIdFilter() []*IDFilter {
+ if x != nil {
+ return x.IdFilter
+ }
+ return nil
+}
+
+// Deprecated: Marked as deprecated in snippet-service/pb/service.proto.
+func (x *GetConfigurationsRequest) GetHttpIdFilter() []string {
+ if x != nil {
+ return x.HttpIdFilter
+ }
+ return nil
+}
+
+type DeleteConfigurationsRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ IdFilter []*IDFilter `protobuf:"bytes,1,rep,name=id_filter,json=idFilter,proto3" json:"id_filter,omitempty"`
+}
+
+func (x *DeleteConfigurationsRequest) Reset() {
+ *x = DeleteConfigurationsRequest{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[7]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *DeleteConfigurationsRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DeleteConfigurationsRequest) ProtoMessage() {}
+
+func (x *DeleteConfigurationsRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[7]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use DeleteConfigurationsRequest.ProtoReflect.Descriptor instead.
+func (*DeleteConfigurationsRequest) Descriptor() ([]byte, []int) {
+ return file_snippet_service_pb_service_proto_rawDescGZIP(), []int{7}
+}
+
+func (x *DeleteConfigurationsRequest) GetIdFilter() []*IDFilter {
+ if x != nil {
+ return x.IdFilter
+ }
+ return nil
+}
+
+type DeleteConfigurationsResponse struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Count int64 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"`
+}
+
+func (x *DeleteConfigurationsResponse) Reset() {
+ *x = DeleteConfigurationsResponse{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[8]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *DeleteConfigurationsResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DeleteConfigurationsResponse) ProtoMessage() {}
+
+func (x *DeleteConfigurationsResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[8]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use DeleteConfigurationsResponse.ProtoReflect.Descriptor instead.
+func (*DeleteConfigurationsResponse) Descriptor() ([]byte, []int) {
+ return file_snippet_service_pb_service_proto_rawDescGZIP(), []int{8}
+}
+
+func (x *DeleteConfigurationsResponse) GetCount() int64 {
+ if x != nil {
+ return x.Count
+ }
+ return 0
+}
+
+type AppliedDeviceConfiguration struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ DeviceId string `protobuf:"bytes,2,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"`
+ ConfigurationId *AppliedDeviceConfiguration_RelationTo `protobuf:"bytes,3,opt,name=configuration_id,json=configurationId,proto3" json:"configuration_id,omitempty"`
+ // Types that are assignable to ExecutedBy:
+ //
+ // *AppliedDeviceConfiguration_OnDemand
+ // *AppliedDeviceConfiguration_ConditionId
+ ExecutedBy isAppliedDeviceConfiguration_ExecutedBy `protobuf_oneof:"executed_by"`
+ Resources []*AppliedDeviceConfiguration_Resource `protobuf:"bytes,6,rep,name=resources,proto3" json:"resources,omitempty"` //TODO naming
+ Owner string `protobuf:"bytes,7,opt,name=owner,proto3" json:"owner,omitempty"`
+ // Unix timestamp in ns when the applied device configuration has been created/updated
+ Timestamp int64 `protobuf:"varint,8,opt,name=timestamp,proto3" json:"timestamp,omitempty" bson:"timestamp"`
+}
+
+func (x *AppliedDeviceConfiguration) Reset() {
+ *x = AppliedDeviceConfiguration{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[9]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *AppliedDeviceConfiguration) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AppliedDeviceConfiguration) ProtoMessage() {}
+
+func (x *AppliedDeviceConfiguration) ProtoReflect() protoreflect.Message {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[9]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use AppliedDeviceConfiguration.ProtoReflect.Descriptor instead.
+func (*AppliedDeviceConfiguration) Descriptor() ([]byte, []int) {
+ return file_snippet_service_pb_service_proto_rawDescGZIP(), []int{9}
+}
+
+func (x *AppliedDeviceConfiguration) GetId() string {
+ if x != nil {
+ return x.Id
+ }
+ return ""
+}
+
+func (x *AppliedDeviceConfiguration) GetDeviceId() string {
+ if x != nil {
+ return x.DeviceId
+ }
+ return ""
+}
+
+func (x *AppliedDeviceConfiguration) GetConfigurationId() *AppliedDeviceConfiguration_RelationTo {
+ if x != nil {
+ return x.ConfigurationId
+ }
+ return nil
+}
+
+func (m *AppliedDeviceConfiguration) GetExecutedBy() isAppliedDeviceConfiguration_ExecutedBy {
+ if m != nil {
+ return m.ExecutedBy
+ }
+ return nil
+}
+
+func (x *AppliedDeviceConfiguration) GetOnDemand() bool {
+ if x, ok := x.GetExecutedBy().(*AppliedDeviceConfiguration_OnDemand); ok {
+ return x.OnDemand
+ }
+ return false
+}
+
+func (x *AppliedDeviceConfiguration) GetConditionId() *AppliedDeviceConfiguration_RelationTo {
+ if x, ok := x.GetExecutedBy().(*AppliedDeviceConfiguration_ConditionId); ok {
+ return x.ConditionId
+ }
+ return nil
+}
+
+func (x *AppliedDeviceConfiguration) GetResources() []*AppliedDeviceConfiguration_Resource {
+ if x != nil {
+ return x.Resources
+ }
+ return nil
+}
+
+func (x *AppliedDeviceConfiguration) GetOwner() string {
+ if x != nil {
+ return x.Owner
+ }
+ return ""
+}
+
+func (x *AppliedDeviceConfiguration) GetTimestamp() int64 {
+ if x != nil {
+ return x.Timestamp
+ }
+ return 0
+}
+
+type isAppliedDeviceConfiguration_ExecutedBy interface {
+ isAppliedDeviceConfiguration_ExecutedBy()
+}
+
+type AppliedDeviceConfiguration_OnDemand struct {
+ OnDemand bool `protobuf:"varint,4,opt,name=on_demand,json=onDemand,proto3,oneof"`
+}
+
+type AppliedDeviceConfiguration_ConditionId struct {
+ ConditionId *AppliedDeviceConfiguration_RelationTo `protobuf:"bytes,5,opt,name=condition_id,json=conditionId,proto3,oneof"` //TODO Naming
+}
+
+func (*AppliedDeviceConfiguration_OnDemand) isAppliedDeviceConfiguration_ExecutedBy() {}
+
+func (*AppliedDeviceConfiguration_ConditionId) isAppliedDeviceConfiguration_ExecutedBy() {}
+
+type InvokeConfigurationRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ ConfigurationId string `protobuf:"bytes,1,opt,name=configuration_id,json=configurationId,proto3" json:"configuration_id,omitempty"` // applies latest configuration
+ DeviceId string `protobuf:"bytes,2,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"`
+ Force bool `protobuf:"varint,3,opt,name=force,proto3" json:"force,omitempty"` // force update even if the configuration has already been applied to device
+ CorrelationId string `protobuf:"bytes,4,opt,name=correlation_id,json=correlationId,proto3" json:"correlation_id,omitempty"` // propagated down to the resource update command
+}
+
+func (x *InvokeConfigurationRequest) Reset() {
+ *x = InvokeConfigurationRequest{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[10]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *InvokeConfigurationRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*InvokeConfigurationRequest) ProtoMessage() {}
+
+func (x *InvokeConfigurationRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[10]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use InvokeConfigurationRequest.ProtoReflect.Descriptor instead.
+func (*InvokeConfigurationRequest) Descriptor() ([]byte, []int) {
+ return file_snippet_service_pb_service_proto_rawDescGZIP(), []int{10}
+}
+
+func (x *InvokeConfigurationRequest) GetConfigurationId() string {
+ if x != nil {
+ return x.ConfigurationId
+ }
+ return ""
+}
+
+func (x *InvokeConfigurationRequest) GetDeviceId() string {
+ if x != nil {
+ return x.DeviceId
+ }
+ return ""
+}
+
+func (x *InvokeConfigurationRequest) GetForce() bool {
+ if x != nil {
+ return x.Force
+ }
+ return false
+}
+
+func (x *InvokeConfigurationRequest) GetCorrelationId() string {
+ if x != nil {
+ return x.CorrelationId
+ }
+ return ""
+}
+
+type GetAppliedDeviceConfigurationsRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ IdFilter []string `protobuf:"bytes,1,rep,name=id_filter,json=idFilter,proto3" json:"id_filter,omitempty"`
+ ConfigurationIdFilter []*IDFilter `protobuf:"bytes,2,rep,name=configuration_id_filter,json=configurationIdFilter,proto3" json:"configuration_id_filter,omitempty"`
+ DeviceIdFilter []string `protobuf:"bytes,3,rep,name=device_id_filter,json=deviceIdFilter,proto3" json:"device_id_filter,omitempty"`
+ ConditionIdFilter []*IDFilter `protobuf:"bytes,4,rep,name=condition_id_filter,json=conditionIdFilter,proto3" json:"condition_id_filter,omitempty"`
+}
+
+func (x *GetAppliedDeviceConfigurationsRequest) Reset() {
+ *x = GetAppliedDeviceConfigurationsRequest{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[11]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *GetAppliedDeviceConfigurationsRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetAppliedDeviceConfigurationsRequest) ProtoMessage() {}
+
+func (x *GetAppliedDeviceConfigurationsRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[11]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetAppliedDeviceConfigurationsRequest.ProtoReflect.Descriptor instead.
+func (*GetAppliedDeviceConfigurationsRequest) Descriptor() ([]byte, []int) {
+ return file_snippet_service_pb_service_proto_rawDescGZIP(), []int{11}
+}
+
+func (x *GetAppliedDeviceConfigurationsRequest) GetIdFilter() []string {
+ if x != nil {
+ return x.IdFilter
+ }
+ return nil
+}
+
+func (x *GetAppliedDeviceConfigurationsRequest) GetConfigurationIdFilter() []*IDFilter {
+ if x != nil {
+ return x.ConfigurationIdFilter
+ }
+ return nil
+}
+
+func (x *GetAppliedDeviceConfigurationsRequest) GetDeviceIdFilter() []string {
+ if x != nil {
+ return x.DeviceIdFilter
+ }
+ return nil
+}
+
+func (x *GetAppliedDeviceConfigurationsRequest) GetConditionIdFilter() []*IDFilter {
+ if x != nil {
+ return x.ConditionIdFilter
+ }
+ return nil
+}
+
+type DeleteAppliedDeviceConfigurationsRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ IdFilter []string `protobuf:"bytes,1,rep,name=id_filter,json=idFilter,proto3" json:"id_filter,omitempty"`
+}
+
+func (x *DeleteAppliedDeviceConfigurationsRequest) Reset() {
+ *x = DeleteAppliedDeviceConfigurationsRequest{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[12]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *DeleteAppliedDeviceConfigurationsRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DeleteAppliedDeviceConfigurationsRequest) ProtoMessage() {}
+
+func (x *DeleteAppliedDeviceConfigurationsRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[12]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use DeleteAppliedDeviceConfigurationsRequest.ProtoReflect.Descriptor instead.
+func (*DeleteAppliedDeviceConfigurationsRequest) Descriptor() ([]byte, []int) {
+ return file_snippet_service_pb_service_proto_rawDescGZIP(), []int{12}
+}
+
+func (x *DeleteAppliedDeviceConfigurationsRequest) GetIdFilter() []string {
+ if x != nil {
+ return x.IdFilter
+ }
+ return nil
+}
+
+type DeleteAppliedDeviceConfigurationsResponse struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Count int64 `protobuf:"varint,1,opt,name=count,proto3" json:"count,omitempty"`
+}
+
+func (x *DeleteAppliedDeviceConfigurationsResponse) Reset() {
+ *x = DeleteAppliedDeviceConfigurationsResponse{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[13]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *DeleteAppliedDeviceConfigurationsResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DeleteAppliedDeviceConfigurationsResponse) ProtoMessage() {}
+
+func (x *DeleteAppliedDeviceConfigurationsResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[13]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use DeleteAppliedDeviceConfigurationsResponse.ProtoReflect.Descriptor instead.
+func (*DeleteAppliedDeviceConfigurationsResponse) Descriptor() ([]byte, []int) {
+ return file_snippet_service_pb_service_proto_rawDescGZIP(), []int{13}
+}
+
+func (x *DeleteAppliedDeviceConfigurationsResponse) GetCount() int64 {
+ if x != nil {
+ return x.Count
+ }
+ return 0
+}
+
+type Configuration_Resource struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // href of the resource
+ Href string `protobuf:"bytes,1,opt,name=href,proto3" json:"href,omitempty"`
+ // content update of the resource
+ Content *commands.Content `protobuf:"bytes,2,opt,name=content,proto3" json:"content,omitempty"`
+ // optional update command time to live, 0 is infinite
+ TimeToLive int64 `protobuf:"varint,3,opt,name=time_to_live,json=timeToLive,proto3" json:"time_to_live,omitempty"`
+}
+
+func (x *Configuration_Resource) Reset() {
+ *x = Configuration_Resource{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[14]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Configuration_Resource) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Configuration_Resource) ProtoMessage() {}
+
+func (x *Configuration_Resource) ProtoReflect() protoreflect.Message {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[14]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Configuration_Resource.ProtoReflect.Descriptor instead.
+func (*Configuration_Resource) Descriptor() ([]byte, []int) {
+ return file_snippet_service_pb_service_proto_rawDescGZIP(), []int{5, 0}
+}
+
+func (x *Configuration_Resource) GetHref() string {
+ if x != nil {
+ return x.Href
+ }
+ return ""
+}
+
+func (x *Configuration_Resource) GetContent() *commands.Content {
+ if x != nil {
+ return x.Content
+ }
+ return nil
+}
+
+func (x *Configuration_Resource) GetTimeToLive() int64 {
+ if x != nil {
+ return x.TimeToLive
+ }
+ return 0
+}
+
+type AppliedDeviceConfiguration_Resource struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ ResourceId *commands.ResourceId `protobuf:"bytes,1,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` // TODO Jozo href only?
+ CorrelationId string `protobuf:"bytes,2,opt,name=correlation_id,json=correlationId,proto3" json:"correlation_id,omitempty"` // Reused from invoke command or generated. Can be used to retrieve corresponding pending command.
+ Status AppliedDeviceConfiguration_Resource_Status `protobuf:"varint,3,opt,name=status,proto3,enum=snippetservice.pb.AppliedDeviceConfiguration_Resource_Status" json:"status,omitempty"`
+ ResourceUpdated *events.ResourceUpdated `protobuf:"bytes,4,opt,name=resource_updated,json=resourceUpdated,proto3" json:"resource_updated,omitempty"`
+}
+
+func (x *AppliedDeviceConfiguration_Resource) Reset() {
+ *x = AppliedDeviceConfiguration_Resource{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[15]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *AppliedDeviceConfiguration_Resource) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AppliedDeviceConfiguration_Resource) ProtoMessage() {}
+
+func (x *AppliedDeviceConfiguration_Resource) ProtoReflect() protoreflect.Message {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[15]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use AppliedDeviceConfiguration_Resource.ProtoReflect.Descriptor instead.
+func (*AppliedDeviceConfiguration_Resource) Descriptor() ([]byte, []int) {
+ return file_snippet_service_pb_service_proto_rawDescGZIP(), []int{9, 0}
+}
+
+func (x *AppliedDeviceConfiguration_Resource) GetResourceId() *commands.ResourceId {
+ if x != nil {
+ return x.ResourceId
+ }
+ return nil
+}
+
+func (x *AppliedDeviceConfiguration_Resource) GetCorrelationId() string {
+ if x != nil {
+ return x.CorrelationId
+ }
+ return ""
+}
+
+func (x *AppliedDeviceConfiguration_Resource) GetStatus() AppliedDeviceConfiguration_Resource_Status {
+ if x != nil {
+ return x.Status
+ }
+ return AppliedDeviceConfiguration_Resource_QUEUED
+}
+
+func (x *AppliedDeviceConfiguration_Resource) GetResourceUpdated() *events.ResourceUpdated {
+ if x != nil {
+ return x.ResourceUpdated
+ }
+ return nil
+}
+
+type AppliedDeviceConfiguration_RelationTo struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ Version uint64 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"`
+}
+
+func (x *AppliedDeviceConfiguration_RelationTo) Reset() {
+ *x = AppliedDeviceConfiguration_RelationTo{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[16]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *AppliedDeviceConfiguration_RelationTo) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AppliedDeviceConfiguration_RelationTo) ProtoMessage() {}
+
+func (x *AppliedDeviceConfiguration_RelationTo) ProtoReflect() protoreflect.Message {
+ mi := &file_snippet_service_pb_service_proto_msgTypes[16]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use AppliedDeviceConfiguration_RelationTo.ProtoReflect.Descriptor instead.
+func (*AppliedDeviceConfiguration_RelationTo) Descriptor() ([]byte, []int) {
+ return file_snippet_service_pb_service_proto_rawDescGZIP(), []int{9, 1}
+}
+
+func (x *AppliedDeviceConfiguration_RelationTo) GetId() string {
+ if x != nil {
+ return x.Id
+ }
+ return ""
+}
+
+func (x *AppliedDeviceConfiguration_RelationTo) GetVersion() uint64 {
+ if x != nil {
+ return x.Version
+ }
+ return 0
+}
+
+var File_snippet_service_pb_service_proto protoreflect.FileDescriptor
+
+var file_snippet_service_pb_service_proto_rawDesc = []byte{
+ 0x0a, 0x20, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
+ 0x65, 0x2f, 0x70, 0x62, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x12, 0x11, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69,
+ 0x63, 0x65, 0x2e, 0x70, 0x62, 0x1a, 0x25, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2d,
+ 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2f, 0x70, 0x62, 0x2f, 0x72, 0x65, 0x73,
+ 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22, 0x72, 0x65,
+ 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2d, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65,
+ 0x2f, 0x70, 0x62, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e,
+ 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61,
+ 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e,
+ 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6b,
+ 0x0a, 0x08, 0x49, 0x44, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x05, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x05, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x12, 0x12, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x48,
+ 0x00, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x18, 0x0a, 0x06, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74,
+ 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x06, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74,
+ 0x42, 0x09, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xac, 0x03, 0x0a, 0x09,
+ 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72,
+ 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73,
+ 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c,
+ 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
+ 0x64, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6f, 0x6e,
+ 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10,
+ 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72,
+ 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64,
+ 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
+ 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x07,
+ 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79,
+ 0x70, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x14, 0x72, 0x65, 0x73, 0x6f,
+ 0x75, 0x72, 0x63, 0x65, 0x5f, 0x68, 0x72, 0x65, 0x66, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72,
+ 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x12, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+ 0x48, 0x72, 0x65, 0x66, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x14, 0x6a, 0x71,
+ 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x6c, 0x74,
+ 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6a, 0x71, 0x45, 0x78, 0x70, 0x72,
+ 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x10,
+ 0x61, 0x70, 0x69, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e,
+ 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x70, 0x69, 0x41, 0x63, 0x63, 0x65, 0x73,
+ 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18,
+ 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09,
+ 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52,
+ 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x50, 0x0a, 0x14, 0x47, 0x65,
+ 0x74, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x69, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18,
+ 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73,
+ 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x44, 0x46, 0x69, 0x6c, 0x74,
+ 0x65, 0x72, 0x52, 0x08, 0x69, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x53, 0x0a, 0x17,
+ 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x69, 0x64, 0x5f, 0x66, 0x69,
+ 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x6e, 0x69,
+ 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x49,
+ 0x44, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x08, 0x69, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65,
+ 0x72, 0x22, 0x30, 0x0a, 0x18, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a,
+ 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f,
+ 0x75, 0x6e, 0x74, 0x22, 0xc5, 0x02, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12,
+ 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
+ 0x61, 0x6d, 0x65, 0x12, 0x47, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
+ 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74,
+ 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+ 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
+ 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05,
+ 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x77, 0x6e,
+ 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18,
+ 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
+ 0x1a, 0x79, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04,
+ 0x68, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x72, 0x65, 0x66,
+ 0x12, 0x37, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x1d, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72,
+ 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74,
+ 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x74, 0x69, 0x6d,
+ 0x65, 0x5f, 0x74, 0x6f, 0x5f, 0x6c, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52,
+ 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x54, 0x6f, 0x4c, 0x69, 0x76, 0x65, 0x22, 0x7e, 0x0a, 0x18, 0x47,
+ 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x69, 0x64, 0x5f, 0x66, 0x69,
+ 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x6e, 0x69,
+ 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x49,
+ 0x44, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x08, 0x69, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65,
+ 0x72, 0x12, 0x28, 0x0a, 0x0e, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x69, 0x64, 0x5f, 0x66, 0x69, 0x6c,
+ 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x68,
+ 0x74, 0x74, 0x70, 0x49, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x57, 0x0a, 0x1b, 0x44,
+ 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x69, 0x64,
+ 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e,
+ 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70,
+ 0x62, 0x2e, 0x49, 0x44, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x08, 0x69, 0x64, 0x46, 0x69,
+ 0x6c, 0x74, 0x65, 0x72, 0x22, 0x34, 0x0a, 0x1c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70,
+ 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x03, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xd7, 0x06, 0x0a, 0x1a, 0x41,
+ 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66,
+ 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76,
+ 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65,
+ 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x63, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+ 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x38, 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
+ 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x44, 0x65, 0x76, 0x69,
+ 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
+ 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66,
+ 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x09, 0x6f,
+ 0x6e, 0x5f, 0x64, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00,
+ 0x52, 0x08, 0x6f, 0x6e, 0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x5d, 0x0a, 0x0c, 0x63, 0x6f,
+ 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x38, 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
+ 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x44, 0x65, 0x76, 0x69,
+ 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e,
+ 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x6f,
+ 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x54, 0x0a, 0x09, 0x72, 0x65, 0x73,
+ 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x73,
+ 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62,
+ 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f,
+ 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12,
+ 0x14, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
+ 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
+ 0x6d, 0x70, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
+ 0x61, 0x6d, 0x70, 0x1a, 0xd7, 0x02, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+ 0x12, 0x41, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+ 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73,
+ 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
+ 0x65, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x72,
+ 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x55, 0x0a, 0x06, 0x73, 0x74,
+ 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x3d, 0x2e, 0x73, 0x6e, 0x69,
+ 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41,
+ 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66,
+ 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
+ 0x63, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
+ 0x73, 0x12, 0x50, 0x0a, 0x10, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x75, 0x70,
+ 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x72, 0x65,
+ 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x2e,
+ 0x70, 0x62, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74,
+ 0x65, 0x64, 0x52, 0x0f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x55, 0x70, 0x64, 0x61,
+ 0x74, 0x65, 0x64, 0x22, 0x38, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0a, 0x0a,
+ 0x06, 0x51, 0x55, 0x45, 0x55, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x4e,
+ 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x4f, 0x4e, 0x45, 0x10, 0x02,
+ 0x12, 0x0b, 0x0a, 0x07, 0x54, 0x49, 0x4d, 0x45, 0x4f, 0x55, 0x54, 0x10, 0x03, 0x1a, 0x36, 0x0a,
+ 0x0a, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69,
+ 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76,
+ 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x76, 0x65,
+ 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x0d, 0x0a, 0x0b, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65,
+ 0x64, 0x5f, 0x62, 0x79, 0x22, 0xa1, 0x01, 0x0a, 0x1a, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x43,
+ 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63,
+ 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1b,
+ 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66,
+ 0x6f, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63,
+ 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65,
+ 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0x90, 0x02, 0x0a, 0x25, 0x47, 0x65, 0x74,
+ 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e,
+ 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18,
+ 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x69, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12,
+ 0x53, 0x0a, 0x17, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x5f, 0x69, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
+ 0x32, 0x1b, 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
+ 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x44, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x15, 0x63,
+ 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x46, 0x69,
+ 0x6c, 0x74, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x69,
+ 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e,
+ 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x4b,
+ 0x0a, 0x13, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x5f, 0x66,
+ 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x73, 0x6e,
+ 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e,
+ 0x49, 0x44, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x11, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74,
+ 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x47, 0x0a, 0x28, 0x44,
+ 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x44, 0x65, 0x76, 0x69,
+ 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x64, 0x5f, 0x66, 0x69,
+ 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x69, 0x64, 0x46, 0x69,
+ 0x6c, 0x74, 0x65, 0x72, 0x22, 0x41, 0x0a, 0x29, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x70,
+ 0x70, 0x6c, 0x69, 0x65, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+ 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+ 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03,
+ 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x32, 0xaa, 0x0f, 0x0a, 0x0e, 0x53, 0x6e, 0x69, 0x70,
+ 0x70, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x8b, 0x01, 0x0a, 0x0f, 0x43,
+ 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c,
+ 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e,
+ 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x1c, 0x2e, 0x73,
+ 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62,
+ 0x2e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3c, 0x92, 0x41, 0x0c, 0x0a,
+ 0x0a, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02,
+ 0x27, 0x3a, 0x01, 0x2a, 0x22, 0x22, 0x2f, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x2d, 0x73,
+ 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f,
+ 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x93, 0x01, 0x0a, 0x0d, 0x47, 0x65, 0x74,
+ 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27, 0x2e, 0x73, 0x6e, 0x69,
+ 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x47,
+ 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72,
+ 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f,
+ 0x6e, 0x22, 0x39, 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, 0x22, 0x2f, 0x73, 0x6e, 0x69, 0x70, 0x70,
+ 0x65, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76,
+ 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x30, 0x01, 0x12, 0xa6,
+ 0x01, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x12, 0x2a, 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72,
+ 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f,
+ 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+ 0x2b, 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+ 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x92, 0x41,
+ 0x0c, 0x0a, 0x0a, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x82, 0xd3, 0xe4,
+ 0x93, 0x02, 0x24, 0x2a, 0x22, 0x2f, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x2d, 0x73, 0x65,
+ 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e,
+ 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x90, 0x01, 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61,
+ 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x2e, 0x73, 0x6e,
+ 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e,
+ 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x1c, 0x2e, 0x73, 0x6e, 0x69, 0x70,
+ 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f,
+ 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x41, 0x92, 0x41, 0x0c, 0x0a, 0x0a, 0x43, 0x6f,
+ 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2c, 0x3a, 0x01,
+ 0x2a, 0x1a, 0x27, 0x2f, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76,
+ 0x69, 0x63, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x64, 0x69,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0x9f, 0x01, 0x0a, 0x13, 0x43,
+ 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x12, 0x20, 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76,
+ 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x20, 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65,
+ 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x44, 0x92, 0x41, 0x10, 0x0a, 0x0e, 0x43, 0x6f, 0x6e,
+ 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02,
+ 0x2b, 0x3a, 0x01, 0x2a, 0x22, 0x26, 0x2f, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x2d, 0x73,
+ 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0xa7, 0x01, 0x0a,
+ 0x11, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x12, 0x2b, 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76,
+ 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+ 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+ 0x20, 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+ 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x22, 0x41, 0x92, 0x41, 0x10, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x12, 0x26, 0x2f, 0x73,
+ 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x61,
+ 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x30, 0x01, 0x12, 0xba, 0x01, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74,
+ 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12,
+ 0x2e, 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+ 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+ 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+ 0x2f, 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+ 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+ 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+ 0x22, 0x41, 0x92, 0x41, 0x10, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x2a, 0x26, 0x2f, 0x73, 0x6e,
+ 0x69, 0x70, 0x70, 0x65, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x61, 0x70,
+ 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x12, 0xa4, 0x01, 0x0a, 0x13, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x2e, 0x73, 0x6e,
+ 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e,
+ 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x20, 0x2e,
+ 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70,
+ 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22,
+ 0x49, 0x92, 0x41, 0x10, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x30, 0x3a, 0x01, 0x2a, 0x1a, 0x2b, 0x2f,
+ 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f,
+ 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x69, 0x64, 0x7d, 0x12, 0xce, 0x01, 0x0a, 0x13, 0x49,
+ 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x12, 0x2d, 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76,
+ 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x76, 0x6f, 0x6b, 0x65, 0x43, 0x6f, 0x6e,
+ 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x1a, 0x2d, 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69,
+ 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x44, 0x65, 0x76,
+ 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x22, 0x57, 0x92, 0x41, 0x10, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3e, 0x3a, 0x01, 0x2a, 0x22, 0x39,
+ 0x2f, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+ 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x7b, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x7d, 0x30, 0x01, 0x12, 0xd0, 0x01, 0x0a, 0x18,
+ 0x47, 0x65, 0x74, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+ 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x38, 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70,
+ 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74,
+ 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e,
+ 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76,
+ 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x44, 0x65,
+ 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x22, 0x49, 0x92, 0x41, 0x10, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x30, 0x12, 0x2e, 0x2f, 0x73,
+ 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x61,
+ 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x30, 0x01, 0x12, 0xe3,
+ 0x01, 0x0a, 0x1b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64,
+ 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3b,
+ 0x2e, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e,
+ 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64,
+ 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3c, 0x2e, 0x73, 0x6e,
+ 0x69, 0x70, 0x70, 0x65, 0x74, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x62, 0x2e,
+ 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x44, 0x65, 0x76,
+ 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x49, 0x92, 0x41, 0x10, 0x0a, 0x0e,
+ 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x82, 0xd3,
+ 0xe4, 0x93, 0x02, 0x30, 0x2a, 0x2e, 0x2f, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x2d, 0x73,
+ 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x70, 0x70,
+ 0x6c, 0x69, 0x65, 0x64, 0x42, 0xd5, 0x02, 0x92, 0x41, 0x9f, 0x02, 0x12, 0xc7, 0x01, 0x0a, 0x14,
+ 0x50, 0x4c, 0x47, 0x44, 0x20, 0x52, 0x75, 0x6c, 0x65, 0x20, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65,
+ 0x20, 0x41, 0x50, 0x49, 0x12, 0x27, 0x41, 0x50, 0x49, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x63, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x72, 0x75, 0x6c, 0x65, 0x20, 0x65,
+ 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x50, 0x4c, 0x47, 0x44, 0x22, 0x3a, 0x0a,
+ 0x08, 0x70, 0x6c, 0x67, 0x64, 0x2e, 0x64, 0x65, 0x76, 0x12, 0x1f, 0x68, 0x74, 0x74, 0x70, 0x73,
+ 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6c,
+ 0x67, 0x64, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x68, 0x75, 0x62, 0x1a, 0x0d, 0x69, 0x6e, 0x66, 0x6f,
+ 0x40, 0x70, 0x6c, 0x67, 0x64, 0x2e, 0x64, 0x65, 0x76, 0x2a, 0x45, 0x0a, 0x12, 0x41, 0x70, 0x61,
+ 0x63, 0x68, 0x65, 0x20, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x32, 0x2e, 0x30, 0x12,
+ 0x2f, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6c, 0x67, 0x64, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x68, 0x75, 0x62,
+ 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x76, 0x32, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45,
+ 0x32, 0x03, 0x31, 0x2e, 0x30, 0x2a, 0x01, 0x02, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x32, 0x15, 0x61, 0x70, 0x70, 0x6c,
+ 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6a, 0x73, 0x6f,
+ 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a,
+ 0x73, 0x6f, 0x6e, 0x3a, 0x15, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x6a, 0x73, 0x6f, 0x6e, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68,
+ 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6c, 0x67, 0x64, 0x2d, 0x64, 0x65, 0x76, 0x2f,
+ 0x68, 0x75, 0x62, 0x2f, 0x76, 0x32, 0x2f, 0x73, 0x6e, 0x69, 0x70, 0x70, 0x65, 0x74, 0x2d, 0x73,
+ 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x70, 0x62, 0x3b, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_snippet_service_pb_service_proto_rawDescOnce sync.Once
+ file_snippet_service_pb_service_proto_rawDescData = file_snippet_service_pb_service_proto_rawDesc
+)
+
+func file_snippet_service_pb_service_proto_rawDescGZIP() []byte {
+ file_snippet_service_pb_service_proto_rawDescOnce.Do(func() {
+ file_snippet_service_pb_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_snippet_service_pb_service_proto_rawDescData)
+ })
+ return file_snippet_service_pb_service_proto_rawDescData
+}
+
+var file_snippet_service_pb_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_snippet_service_pb_service_proto_msgTypes = make([]protoimpl.MessageInfo, 17)
+var file_snippet_service_pb_service_proto_goTypes = []interface{}{
+ (AppliedDeviceConfiguration_Resource_Status)(0), // 0: snippetservice.pb.AppliedDeviceConfiguration.Resource.Status
+ (*IDFilter)(nil), // 1: snippetservice.pb.IDFilter
+ (*Condition)(nil), // 2: snippetservice.pb.Condition
+ (*GetConditionsRequest)(nil), // 3: snippetservice.pb.GetConditionsRequest
+ (*DeleteConditionsRequest)(nil), // 4: snippetservice.pb.DeleteConditionsRequest
+ (*DeleteConditionsResponse)(nil), // 5: snippetservice.pb.DeleteConditionsResponse
+ (*Configuration)(nil), // 6: snippetservice.pb.Configuration
+ (*GetConfigurationsRequest)(nil), // 7: snippetservice.pb.GetConfigurationsRequest
+ (*DeleteConfigurationsRequest)(nil), // 8: snippetservice.pb.DeleteConfigurationsRequest
+ (*DeleteConfigurationsResponse)(nil), // 9: snippetservice.pb.DeleteConfigurationsResponse
+ (*AppliedDeviceConfiguration)(nil), // 10: snippetservice.pb.AppliedDeviceConfiguration
+ (*InvokeConfigurationRequest)(nil), // 11: snippetservice.pb.InvokeConfigurationRequest
+ (*GetAppliedDeviceConfigurationsRequest)(nil), // 12: snippetservice.pb.GetAppliedDeviceConfigurationsRequest
+ (*DeleteAppliedDeviceConfigurationsRequest)(nil), // 13: snippetservice.pb.DeleteAppliedDeviceConfigurationsRequest
+ (*DeleteAppliedDeviceConfigurationsResponse)(nil), // 14: snippetservice.pb.DeleteAppliedDeviceConfigurationsResponse
+ (*Configuration_Resource)(nil), // 15: snippetservice.pb.Configuration.Resource
+ (*AppliedDeviceConfiguration_Resource)(nil), // 16: snippetservice.pb.AppliedDeviceConfiguration.Resource
+ (*AppliedDeviceConfiguration_RelationTo)(nil), // 17: snippetservice.pb.AppliedDeviceConfiguration.RelationTo
+ (*commands.Content)(nil), // 18: resourceaggregate.pb.Content
+ (*commands.ResourceId)(nil), // 19: resourceaggregate.pb.ResourceId
+ (*events.ResourceUpdated)(nil), // 20: resourceaggregate.pb.ResourceUpdated
+}
+var file_snippet_service_pb_service_proto_depIdxs = []int32{
+ 1, // 0: snippetservice.pb.GetConditionsRequest.id_filter:type_name -> snippetservice.pb.IDFilter
+ 1, // 1: snippetservice.pb.DeleteConditionsRequest.id_filter:type_name -> snippetservice.pb.IDFilter
+ 15, // 2: snippetservice.pb.Configuration.resources:type_name -> snippetservice.pb.Configuration.Resource
+ 1, // 3: snippetservice.pb.GetConfigurationsRequest.id_filter:type_name -> snippetservice.pb.IDFilter
+ 1, // 4: snippetservice.pb.DeleteConfigurationsRequest.id_filter:type_name -> snippetservice.pb.IDFilter
+ 17, // 5: snippetservice.pb.AppliedDeviceConfiguration.configuration_id:type_name -> snippetservice.pb.AppliedDeviceConfiguration.RelationTo
+ 17, // 6: snippetservice.pb.AppliedDeviceConfiguration.condition_id:type_name -> snippetservice.pb.AppliedDeviceConfiguration.RelationTo
+ 16, // 7: snippetservice.pb.AppliedDeviceConfiguration.resources:type_name -> snippetservice.pb.AppliedDeviceConfiguration.Resource
+ 1, // 8: snippetservice.pb.GetAppliedDeviceConfigurationsRequest.configuration_id_filter:type_name -> snippetservice.pb.IDFilter
+ 1, // 9: snippetservice.pb.GetAppliedDeviceConfigurationsRequest.condition_id_filter:type_name -> snippetservice.pb.IDFilter
+ 18, // 10: snippetservice.pb.Configuration.Resource.content:type_name -> resourceaggregate.pb.Content
+ 19, // 11: snippetservice.pb.AppliedDeviceConfiguration.Resource.resource_id:type_name -> resourceaggregate.pb.ResourceId
+ 0, // 12: snippetservice.pb.AppliedDeviceConfiguration.Resource.status:type_name -> snippetservice.pb.AppliedDeviceConfiguration.Resource.Status
+ 20, // 13: snippetservice.pb.AppliedDeviceConfiguration.Resource.resource_updated:type_name -> resourceaggregate.pb.ResourceUpdated
+ 2, // 14: snippetservice.pb.SnippetService.CreateCondition:input_type -> snippetservice.pb.Condition
+ 3, // 15: snippetservice.pb.SnippetService.GetConditions:input_type -> snippetservice.pb.GetConditionsRequest
+ 4, // 16: snippetservice.pb.SnippetService.DeleteConditions:input_type -> snippetservice.pb.DeleteConditionsRequest
+ 2, // 17: snippetservice.pb.SnippetService.UpdateCondition:input_type -> snippetservice.pb.Condition
+ 6, // 18: snippetservice.pb.SnippetService.CreateConfiguration:input_type -> snippetservice.pb.Configuration
+ 7, // 19: snippetservice.pb.SnippetService.GetConfigurations:input_type -> snippetservice.pb.GetConfigurationsRequest
+ 8, // 20: snippetservice.pb.SnippetService.DeleteConfigurations:input_type -> snippetservice.pb.DeleteConfigurationsRequest
+ 6, // 21: snippetservice.pb.SnippetService.UpdateConfiguration:input_type -> snippetservice.pb.Configuration
+ 11, // 22: snippetservice.pb.SnippetService.InvokeConfiguration:input_type -> snippetservice.pb.InvokeConfigurationRequest
+ 12, // 23: snippetservice.pb.SnippetService.GetAppliedConfigurations:input_type -> snippetservice.pb.GetAppliedDeviceConfigurationsRequest
+ 13, // 24: snippetservice.pb.SnippetService.DeleteAppliedConfigurations:input_type -> snippetservice.pb.DeleteAppliedDeviceConfigurationsRequest
+ 2, // 25: snippetservice.pb.SnippetService.CreateCondition:output_type -> snippetservice.pb.Condition
+ 2, // 26: snippetservice.pb.SnippetService.GetConditions:output_type -> snippetservice.pb.Condition
+ 5, // 27: snippetservice.pb.SnippetService.DeleteConditions:output_type -> snippetservice.pb.DeleteConditionsResponse
+ 2, // 28: snippetservice.pb.SnippetService.UpdateCondition:output_type -> snippetservice.pb.Condition
+ 6, // 29: snippetservice.pb.SnippetService.CreateConfiguration:output_type -> snippetservice.pb.Configuration
+ 6, // 30: snippetservice.pb.SnippetService.GetConfigurations:output_type -> snippetservice.pb.Configuration
+ 9, // 31: snippetservice.pb.SnippetService.DeleteConfigurations:output_type -> snippetservice.pb.DeleteConfigurationsResponse
+ 6, // 32: snippetservice.pb.SnippetService.UpdateConfiguration:output_type -> snippetservice.pb.Configuration
+ 10, // 33: snippetservice.pb.SnippetService.InvokeConfiguration:output_type -> snippetservice.pb.AppliedDeviceConfiguration
+ 10, // 34: snippetservice.pb.SnippetService.GetAppliedConfigurations:output_type -> snippetservice.pb.AppliedDeviceConfiguration
+ 14, // 35: snippetservice.pb.SnippetService.DeleteAppliedConfigurations:output_type -> snippetservice.pb.DeleteAppliedDeviceConfigurationsResponse
+ 25, // [25:36] is the sub-list for method output_type
+ 14, // [14:25] is the sub-list for method input_type
+ 14, // [14:14] is the sub-list for extension type_name
+ 14, // [14:14] is the sub-list for extension extendee
+ 0, // [0:14] is the sub-list for field type_name
+}
+
+func init() { file_snippet_service_pb_service_proto_init() }
+func file_snippet_service_pb_service_proto_init() {
+ if File_snippet_service_pb_service_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_snippet_service_pb_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*IDFilter); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_snippet_service_pb_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Condition); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_snippet_service_pb_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*GetConditionsRequest); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_snippet_service_pb_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*DeleteConditionsRequest); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_snippet_service_pb_service_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*DeleteConditionsResponse); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_snippet_service_pb_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Configuration); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_snippet_service_pb_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*GetConfigurationsRequest); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_snippet_service_pb_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*DeleteConfigurationsRequest); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_snippet_service_pb_service_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*DeleteConfigurationsResponse); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_snippet_service_pb_service_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*AppliedDeviceConfiguration); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_snippet_service_pb_service_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*InvokeConfigurationRequest); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_snippet_service_pb_service_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*GetAppliedDeviceConfigurationsRequest); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_snippet_service_pb_service_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*DeleteAppliedDeviceConfigurationsRequest); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_snippet_service_pb_service_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*DeleteAppliedDeviceConfigurationsResponse); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_snippet_service_pb_service_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Configuration_Resource); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_snippet_service_pb_service_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*AppliedDeviceConfiguration_Resource); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_snippet_service_pb_service_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*AppliedDeviceConfiguration_RelationTo); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ file_snippet_service_pb_service_proto_msgTypes[0].OneofWrappers = []interface{}{
+ (*IDFilter_Value)(nil),
+ (*IDFilter_All)(nil),
+ (*IDFilter_Latest)(nil),
+ }
+ file_snippet_service_pb_service_proto_msgTypes[9].OneofWrappers = []interface{}{
+ (*AppliedDeviceConfiguration_OnDemand)(nil),
+ (*AppliedDeviceConfiguration_ConditionId)(nil),
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_snippet_service_pb_service_proto_rawDesc,
+ NumEnums: 1,
+ NumMessages: 17,
+ NumExtensions: 0,
+ NumServices: 1,
+ },
+ GoTypes: file_snippet_service_pb_service_proto_goTypes,
+ DependencyIndexes: file_snippet_service_pb_service_proto_depIdxs,
+ EnumInfos: file_snippet_service_pb_service_proto_enumTypes,
+ MessageInfos: file_snippet_service_pb_service_proto_msgTypes,
+ }.Build()
+ File_snippet_service_pb_service_proto = out.File
+ file_snippet_service_pb_service_proto_rawDesc = nil
+ file_snippet_service_pb_service_proto_goTypes = nil
+ file_snippet_service_pb_service_proto_depIdxs = nil
+}
diff --git a/snippet-service/pb/service.pb.gw.go b/snippet-service/pb/service.pb.gw.go
new file mode 100644
index 000000000..14d3b3609
--- /dev/null
+++ b/snippet-service/pb/service.pb.gw.go
@@ -0,0 +1,977 @@
+// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
+// source: snippet-service/pb/service.proto
+
+/*
+Package pb is a reverse proxy.
+
+It translates gRPC into RESTful JSON APIs.
+*/
+package pb
+
+import (
+ "context"
+ "io"
+ "net/http"
+
+ "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
+ "github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/grpclog"
+ "google.golang.org/grpc/metadata"
+ "google.golang.org/grpc/status"
+ "google.golang.org/protobuf/proto"
+)
+
+// Suppress "imported and not used" errors
+var _ codes.Code
+var _ io.Reader
+var _ status.Status
+var _ = runtime.String
+var _ = utilities.NewDoubleArray
+var _ = metadata.Join
+
+func request_SnippetService_CreateCondition_0(ctx context.Context, marshaler runtime.Marshaler, client SnippetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq Condition
+ var metadata runtime.ServerMetadata
+
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ msg, err := client.CreateCondition(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+ return msg, metadata, err
+
+}
+
+func local_request_SnippetService_CreateCondition_0(ctx context.Context, marshaler runtime.Marshaler, server SnippetServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq Condition
+ var metadata runtime.ServerMetadata
+
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ msg, err := server.CreateCondition(ctx, &protoReq)
+ return msg, metadata, err
+
+}
+
+var (
+ filter_SnippetService_GetConditions_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_SnippetService_GetConditions_0(ctx context.Context, marshaler runtime.Marshaler, client SnippetServiceClient, req *http.Request, pathParams map[string]string) (SnippetService_GetConditionsClient, runtime.ServerMetadata, error) {
+ var protoReq GetConditionsRequest
+ var metadata runtime.ServerMetadata
+
+ if err := req.ParseForm(); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+ if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_SnippetService_GetConditions_0); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ stream, err := client.GetConditions(ctx, &protoReq)
+ if err != nil {
+ return nil, metadata, err
+ }
+ header, err := stream.Header()
+ if err != nil {
+ return nil, metadata, err
+ }
+ metadata.HeaderMD = header
+ return stream, metadata, nil
+
+}
+
+var (
+ filter_SnippetService_DeleteConditions_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_SnippetService_DeleteConditions_0(ctx context.Context, marshaler runtime.Marshaler, client SnippetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq DeleteConditionsRequest
+ var metadata runtime.ServerMetadata
+
+ if err := req.ParseForm(); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+ if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_SnippetService_DeleteConditions_0); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ msg, err := client.DeleteConditions(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+ return msg, metadata, err
+
+}
+
+func local_request_SnippetService_DeleteConditions_0(ctx context.Context, marshaler runtime.Marshaler, server SnippetServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq DeleteConditionsRequest
+ var metadata runtime.ServerMetadata
+
+ if err := req.ParseForm(); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+ if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_SnippetService_DeleteConditions_0); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ msg, err := server.DeleteConditions(ctx, &protoReq)
+ return msg, metadata, err
+
+}
+
+func request_SnippetService_UpdateCondition_0(ctx context.Context, marshaler runtime.Marshaler, client SnippetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq Condition
+ var metadata runtime.ServerMetadata
+
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ var (
+ val string
+ ok bool
+ err error
+ _ = err
+ )
+
+ val, ok = pathParams["id"]
+ if !ok {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
+ }
+
+ protoReq.Id, err = runtime.String(val)
+ if err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
+ }
+
+ msg, err := client.UpdateCondition(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+ return msg, metadata, err
+
+}
+
+func local_request_SnippetService_UpdateCondition_0(ctx context.Context, marshaler runtime.Marshaler, server SnippetServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq Condition
+ var metadata runtime.ServerMetadata
+
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ var (
+ val string
+ ok bool
+ err error
+ _ = err
+ )
+
+ val, ok = pathParams["id"]
+ if !ok {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
+ }
+
+ protoReq.Id, err = runtime.String(val)
+ if err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
+ }
+
+ msg, err := server.UpdateCondition(ctx, &protoReq)
+ return msg, metadata, err
+
+}
+
+func request_SnippetService_CreateConfiguration_0(ctx context.Context, marshaler runtime.Marshaler, client SnippetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq Configuration
+ var metadata runtime.ServerMetadata
+
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ msg, err := client.CreateConfiguration(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+ return msg, metadata, err
+
+}
+
+func local_request_SnippetService_CreateConfiguration_0(ctx context.Context, marshaler runtime.Marshaler, server SnippetServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq Configuration
+ var metadata runtime.ServerMetadata
+
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ msg, err := server.CreateConfiguration(ctx, &protoReq)
+ return msg, metadata, err
+
+}
+
+var (
+ filter_SnippetService_GetConfigurations_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_SnippetService_GetConfigurations_0(ctx context.Context, marshaler runtime.Marshaler, client SnippetServiceClient, req *http.Request, pathParams map[string]string) (SnippetService_GetConfigurationsClient, runtime.ServerMetadata, error) {
+ var protoReq GetConfigurationsRequest
+ var metadata runtime.ServerMetadata
+
+ if err := req.ParseForm(); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+ if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_SnippetService_GetConfigurations_0); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ stream, err := client.GetConfigurations(ctx, &protoReq)
+ if err != nil {
+ return nil, metadata, err
+ }
+ header, err := stream.Header()
+ if err != nil {
+ return nil, metadata, err
+ }
+ metadata.HeaderMD = header
+ return stream, metadata, nil
+
+}
+
+var (
+ filter_SnippetService_DeleteConfigurations_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_SnippetService_DeleteConfigurations_0(ctx context.Context, marshaler runtime.Marshaler, client SnippetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq DeleteConfigurationsRequest
+ var metadata runtime.ServerMetadata
+
+ if err := req.ParseForm(); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+ if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_SnippetService_DeleteConfigurations_0); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ msg, err := client.DeleteConfigurations(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+ return msg, metadata, err
+
+}
+
+func local_request_SnippetService_DeleteConfigurations_0(ctx context.Context, marshaler runtime.Marshaler, server SnippetServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq DeleteConfigurationsRequest
+ var metadata runtime.ServerMetadata
+
+ if err := req.ParseForm(); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+ if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_SnippetService_DeleteConfigurations_0); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ msg, err := server.DeleteConfigurations(ctx, &protoReq)
+ return msg, metadata, err
+
+}
+
+func request_SnippetService_UpdateConfiguration_0(ctx context.Context, marshaler runtime.Marshaler, client SnippetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq Configuration
+ var metadata runtime.ServerMetadata
+
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ var (
+ val string
+ ok bool
+ err error
+ _ = err
+ )
+
+ val, ok = pathParams["id"]
+ if !ok {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
+ }
+
+ protoReq.Id, err = runtime.String(val)
+ if err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
+ }
+
+ msg, err := client.UpdateConfiguration(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+ return msg, metadata, err
+
+}
+
+func local_request_SnippetService_UpdateConfiguration_0(ctx context.Context, marshaler runtime.Marshaler, server SnippetServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq Configuration
+ var metadata runtime.ServerMetadata
+
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ var (
+ val string
+ ok bool
+ err error
+ _ = err
+ )
+
+ val, ok = pathParams["id"]
+ if !ok {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "id")
+ }
+
+ protoReq.Id, err = runtime.String(val)
+ if err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "id", err)
+ }
+
+ msg, err := server.UpdateConfiguration(ctx, &protoReq)
+ return msg, metadata, err
+
+}
+
+func request_SnippetService_InvokeConfiguration_0(ctx context.Context, marshaler runtime.Marshaler, client SnippetServiceClient, req *http.Request, pathParams map[string]string) (SnippetService_InvokeConfigurationClient, runtime.ServerMetadata, error) {
+ var protoReq InvokeConfigurationRequest
+ var metadata runtime.ServerMetadata
+
+ if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ var (
+ val string
+ ok bool
+ err error
+ _ = err
+ )
+
+ val, ok = pathParams["configuration_id"]
+ if !ok {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "configuration_id")
+ }
+
+ protoReq.ConfigurationId, err = runtime.String(val)
+ if err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "configuration_id", err)
+ }
+
+ stream, err := client.InvokeConfiguration(ctx, &protoReq)
+ if err != nil {
+ return nil, metadata, err
+ }
+ header, err := stream.Header()
+ if err != nil {
+ return nil, metadata, err
+ }
+ metadata.HeaderMD = header
+ return stream, metadata, nil
+
+}
+
+var (
+ filter_SnippetService_GetAppliedConfigurations_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_SnippetService_GetAppliedConfigurations_0(ctx context.Context, marshaler runtime.Marshaler, client SnippetServiceClient, req *http.Request, pathParams map[string]string) (SnippetService_GetAppliedConfigurationsClient, runtime.ServerMetadata, error) {
+ var protoReq GetAppliedDeviceConfigurationsRequest
+ var metadata runtime.ServerMetadata
+
+ if err := req.ParseForm(); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+ if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_SnippetService_GetAppliedConfigurations_0); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ stream, err := client.GetAppliedConfigurations(ctx, &protoReq)
+ if err != nil {
+ return nil, metadata, err
+ }
+ header, err := stream.Header()
+ if err != nil {
+ return nil, metadata, err
+ }
+ metadata.HeaderMD = header
+ return stream, metadata, nil
+
+}
+
+var (
+ filter_SnippetService_DeleteAppliedConfigurations_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
+)
+
+func request_SnippetService_DeleteAppliedConfigurations_0(ctx context.Context, marshaler runtime.Marshaler, client SnippetServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq DeleteAppliedDeviceConfigurationsRequest
+ var metadata runtime.ServerMetadata
+
+ if err := req.ParseForm(); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+ if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_SnippetService_DeleteAppliedConfigurations_0); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ msg, err := client.DeleteAppliedConfigurations(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+ return msg, metadata, err
+
+}
+
+func local_request_SnippetService_DeleteAppliedConfigurations_0(ctx context.Context, marshaler runtime.Marshaler, server SnippetServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+ var protoReq DeleteAppliedDeviceConfigurationsRequest
+ var metadata runtime.ServerMetadata
+
+ if err := req.ParseForm(); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+ if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_SnippetService_DeleteAppliedConfigurations_0); err != nil {
+ return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+ }
+
+ msg, err := server.DeleteAppliedConfigurations(ctx, &protoReq)
+ return msg, metadata, err
+
+}
+
+// RegisterSnippetServiceHandlerServer registers the http handlers for service SnippetService to "mux".
+// UnaryRPC :call SnippetServiceServer directly.
+// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
+// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterSnippetServiceHandlerFromEndpoint instead.
+func RegisterSnippetServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server SnippetServiceServer) error {
+
+ mux.Handle("POST", pattern_SnippetService_CreateCondition_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ var stream runtime.ServerTransportStream
+ ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/snippetservice.pb.SnippetService/CreateCondition", runtime.WithHTTPPathPattern("/snippet-service/api/v1/conditions"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := local_request_SnippetService_CreateCondition_0(annotatedContext, inboundMarshaler, server, req, pathParams)
+ md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_SnippetService_CreateCondition_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("GET", pattern_SnippetService_GetConditions_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport")
+ _, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ })
+
+ mux.Handle("DELETE", pattern_SnippetService_DeleteConditions_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ var stream runtime.ServerTransportStream
+ ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/snippetservice.pb.SnippetService/DeleteConditions", runtime.WithHTTPPathPattern("/snippet-service/api/v1/conditions"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := local_request_SnippetService_DeleteConditions_0(annotatedContext, inboundMarshaler, server, req, pathParams)
+ md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_SnippetService_DeleteConditions_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("PUT", pattern_SnippetService_UpdateCondition_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ var stream runtime.ServerTransportStream
+ ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/snippetservice.pb.SnippetService/UpdateCondition", runtime.WithHTTPPathPattern("/snippet-service/api/v1/conditions/{id}"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := local_request_SnippetService_UpdateCondition_0(annotatedContext, inboundMarshaler, server, req, pathParams)
+ md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_SnippetService_UpdateCondition_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("POST", pattern_SnippetService_CreateConfiguration_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ var stream runtime.ServerTransportStream
+ ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/snippetservice.pb.SnippetService/CreateConfiguration", runtime.WithHTTPPathPattern("/snippet-service/api/v1/configurations"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := local_request_SnippetService_CreateConfiguration_0(annotatedContext, inboundMarshaler, server, req, pathParams)
+ md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_SnippetService_CreateConfiguration_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("GET", pattern_SnippetService_GetConfigurations_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport")
+ _, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ })
+
+ mux.Handle("DELETE", pattern_SnippetService_DeleteConfigurations_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ var stream runtime.ServerTransportStream
+ ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/snippetservice.pb.SnippetService/DeleteConfigurations", runtime.WithHTTPPathPattern("/snippet-service/api/v1/configurations"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := local_request_SnippetService_DeleteConfigurations_0(annotatedContext, inboundMarshaler, server, req, pathParams)
+ md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_SnippetService_DeleteConfigurations_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("PUT", pattern_SnippetService_UpdateConfiguration_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ var stream runtime.ServerTransportStream
+ ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/snippetservice.pb.SnippetService/UpdateConfiguration", runtime.WithHTTPPathPattern("/snippet-service/api/v1/configurations/{id}"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := local_request_SnippetService_UpdateConfiguration_0(annotatedContext, inboundMarshaler, server, req, pathParams)
+ md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_SnippetService_UpdateConfiguration_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("POST", pattern_SnippetService_InvokeConfiguration_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport")
+ _, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ })
+
+ mux.Handle("GET", pattern_SnippetService_GetAppliedConfigurations_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ err := status.Error(codes.Unimplemented, "streaming calls are not yet supported in the in-process transport")
+ _, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ })
+
+ mux.Handle("DELETE", pattern_SnippetService_DeleteAppliedConfigurations_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ var stream runtime.ServerTransportStream
+ ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/snippetservice.pb.SnippetService/DeleteAppliedConfigurations", runtime.WithHTTPPathPattern("/snippet-service/api/v1/configurations/applied"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := local_request_SnippetService_DeleteAppliedConfigurations_0(annotatedContext, inboundMarshaler, server, req, pathParams)
+ md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_SnippetService_DeleteAppliedConfigurations_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ return nil
+}
+
+// RegisterSnippetServiceHandlerFromEndpoint is same as RegisterSnippetServiceHandler but
+// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
+func RegisterSnippetServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
+ conn, err := grpc.NewClient(endpoint, opts...)
+ if err != nil {
+ return err
+ }
+ defer func() {
+ if err != nil {
+ if cerr := conn.Close(); cerr != nil {
+ grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
+ }
+ return
+ }
+ go func() {
+ <-ctx.Done()
+ if cerr := conn.Close(); cerr != nil {
+ grpclog.Errorf("Failed to close conn to %s: %v", endpoint, cerr)
+ }
+ }()
+ }()
+
+ return RegisterSnippetServiceHandler(ctx, mux, conn)
+}
+
+// RegisterSnippetServiceHandler registers the http handlers for service SnippetService to "mux".
+// The handlers forward requests to the grpc endpoint over "conn".
+func RegisterSnippetServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+ return RegisterSnippetServiceHandlerClient(ctx, mux, NewSnippetServiceClient(conn))
+}
+
+// RegisterSnippetServiceHandlerClient registers the http handlers for service SnippetService
+// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "SnippetServiceClient".
+// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "SnippetServiceClient"
+// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
+// "SnippetServiceClient" to call the correct interceptors.
+func RegisterSnippetServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client SnippetServiceClient) error {
+
+ mux.Handle("POST", pattern_SnippetService_CreateCondition_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/snippetservice.pb.SnippetService/CreateCondition", runtime.WithHTTPPathPattern("/snippet-service/api/v1/conditions"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := request_SnippetService_CreateCondition_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_SnippetService_CreateCondition_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("GET", pattern_SnippetService_GetConditions_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/snippetservice.pb.SnippetService/GetConditions", runtime.WithHTTPPathPattern("/snippet-service/api/v1/conditions"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := request_SnippetService_GetConditions_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_SnippetService_GetConditions_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("DELETE", pattern_SnippetService_DeleteConditions_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/snippetservice.pb.SnippetService/DeleteConditions", runtime.WithHTTPPathPattern("/snippet-service/api/v1/conditions"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := request_SnippetService_DeleteConditions_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_SnippetService_DeleteConditions_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("PUT", pattern_SnippetService_UpdateCondition_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/snippetservice.pb.SnippetService/UpdateCondition", runtime.WithHTTPPathPattern("/snippet-service/api/v1/conditions/{id}"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := request_SnippetService_UpdateCondition_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_SnippetService_UpdateCondition_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("POST", pattern_SnippetService_CreateConfiguration_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/snippetservice.pb.SnippetService/CreateConfiguration", runtime.WithHTTPPathPattern("/snippet-service/api/v1/configurations"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := request_SnippetService_CreateConfiguration_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_SnippetService_CreateConfiguration_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("GET", pattern_SnippetService_GetConfigurations_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/snippetservice.pb.SnippetService/GetConfigurations", runtime.WithHTTPPathPattern("/snippet-service/api/v1/configurations"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := request_SnippetService_GetConfigurations_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_SnippetService_GetConfigurations_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("DELETE", pattern_SnippetService_DeleteConfigurations_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/snippetservice.pb.SnippetService/DeleteConfigurations", runtime.WithHTTPPathPattern("/snippet-service/api/v1/configurations"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := request_SnippetService_DeleteConfigurations_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_SnippetService_DeleteConfigurations_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("PUT", pattern_SnippetService_UpdateConfiguration_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/snippetservice.pb.SnippetService/UpdateConfiguration", runtime.WithHTTPPathPattern("/snippet-service/api/v1/configurations/{id}"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := request_SnippetService_UpdateConfiguration_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_SnippetService_UpdateConfiguration_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("POST", pattern_SnippetService_InvokeConfiguration_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/snippetservice.pb.SnippetService/InvokeConfiguration", runtime.WithHTTPPathPattern("/snippet-service/api/v1/configurations/{configuration_id}"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := request_SnippetService_InvokeConfiguration_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_SnippetService_InvokeConfiguration_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("GET", pattern_SnippetService_GetAppliedConfigurations_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/snippetservice.pb.SnippetService/GetAppliedConfigurations", runtime.WithHTTPPathPattern("/snippet-service/api/v1/configurations/applied"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := request_SnippetService_GetAppliedConfigurations_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_SnippetService_GetAppliedConfigurations_0(annotatedContext, mux, outboundMarshaler, w, req, func() (proto.Message, error) { return resp.Recv() }, mux.GetForwardResponseOptions()...)
+
+ })
+
+ mux.Handle("DELETE", pattern_SnippetService_DeleteAppliedConfigurations_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+ ctx, cancel := context.WithCancel(req.Context())
+ defer cancel()
+ inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+ var err error
+ var annotatedContext context.Context
+ annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/snippetservice.pb.SnippetService/DeleteAppliedConfigurations", runtime.WithHTTPPathPattern("/snippet-service/api/v1/configurations/applied"))
+ if err != nil {
+ runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+ return
+ }
+ resp, md, err := request_SnippetService_DeleteAppliedConfigurations_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+ annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+ if err != nil {
+ runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+ return
+ }
+
+ forward_SnippetService_DeleteAppliedConfigurations_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+ })
+
+ return nil
+}
+
+var (
+ pattern_SnippetService_CreateCondition_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"snippet-service", "api", "v1", "conditions"}, ""))
+
+ pattern_SnippetService_GetConditions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"snippet-service", "api", "v1", "conditions"}, ""))
+
+ pattern_SnippetService_DeleteConditions_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"snippet-service", "api", "v1", "conditions"}, ""))
+
+ pattern_SnippetService_UpdateCondition_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"snippet-service", "api", "v1", "conditions", "id"}, ""))
+
+ pattern_SnippetService_CreateConfiguration_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"snippet-service", "api", "v1", "configurations"}, ""))
+
+ pattern_SnippetService_GetConfigurations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"snippet-service", "api", "v1", "configurations"}, ""))
+
+ pattern_SnippetService_DeleteConfigurations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"snippet-service", "api", "v1", "configurations"}, ""))
+
+ pattern_SnippetService_UpdateConfiguration_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"snippet-service", "api", "v1", "configurations", "id"}, ""))
+
+ pattern_SnippetService_InvokeConfiguration_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"snippet-service", "api", "v1", "configurations", "configuration_id"}, ""))
+
+ pattern_SnippetService_GetAppliedConfigurations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"snippet-service", "api", "v1", "configurations", "applied"}, ""))
+
+ pattern_SnippetService_DeleteAppliedConfigurations_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"snippet-service", "api", "v1", "configurations", "applied"}, ""))
+)
+
+var (
+ forward_SnippetService_CreateCondition_0 = runtime.ForwardResponseMessage
+
+ forward_SnippetService_GetConditions_0 = runtime.ForwardResponseStream
+
+ forward_SnippetService_DeleteConditions_0 = runtime.ForwardResponseMessage
+
+ forward_SnippetService_UpdateCondition_0 = runtime.ForwardResponseMessage
+
+ forward_SnippetService_CreateConfiguration_0 = runtime.ForwardResponseMessage
+
+ forward_SnippetService_GetConfigurations_0 = runtime.ForwardResponseStream
+
+ forward_SnippetService_DeleteConfigurations_0 = runtime.ForwardResponseMessage
+
+ forward_SnippetService_UpdateConfiguration_0 = runtime.ForwardResponseMessage
+
+ forward_SnippetService_InvokeConfiguration_0 = runtime.ForwardResponseStream
+
+ forward_SnippetService_GetAppliedConfigurations_0 = runtime.ForwardResponseStream
+
+ forward_SnippetService_DeleteAppliedConfigurations_0 = runtime.ForwardResponseMessage
+)
diff --git a/snippet-service/pb/service.proto b/snippet-service/pb/service.proto
new file mode 100644
index 000000000..7fab50eac
--- /dev/null
+++ b/snippet-service/pb/service.proto
@@ -0,0 +1,273 @@
+// TODO overit ze pending command sa nezmaze na neexistujucom resource ak sa device pripoji a nepublishne hned resource
+// Overit correlation id - ak sa pouziva rovnake napriec viacerymi resourcami
+
+// scenare
+// - Uzivatel vie vytvorit config, automaticka (backend) inkrementacia verzie
+// - Uzivatel updatne config, verzia sa inkrementuje, Modal -> chces aplikovat na vsetky uz provisionnute devici? Informovat uzivatela, ze niektore devici mozu byt offline a command moze vyexpirovat.
+// - Uzivatel updatne config, verzia sa inkrementuje, informujeme uzivatela ze vsetky pending commandy z predoslej verzie budu cancelnute ako aj dalsie sekvencne updaty resourcov pre predoslu verziu
+
+syntax = "proto3";
+
+package snippetservice.pb;
+
+import "resource-aggregate/pb/resources.proto";
+import "resource-aggregate/pb/events.proto";
+
+import "google/api/annotations.proto";
+import "protoc-gen-openapiv2/options/annotations.proto";
+
+option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
+ info: {
+ title: "PLGD Rule Engine API";
+ version: "1.0";
+ description: "API for configuring rule engine in PLGD";
+ contact: {
+ name: "plgd.dev";
+ url: "https://github.com/plgd-dev/hub";
+ email: "info@plgd.dev";
+ };
+ license: {
+ name: "Apache License 2.0";
+ url: "https://github.com/plgd-dev/hub/blob/v2/LICENSE";
+ };
+ };
+ schemes: [HTTPS];
+ consumes: ["application/json", "application/protojson"];
+ produces: ["application/json", "application/protojson"];
+};
+
+option go_package = "github.com/plgd-dev/hub/v2/snippet-service/pb;pb";
+
+// TODO: /configurations/123?version=latest
+message IDFilter {
+ string id = 1;
+ oneof version {
+ uint64 value = 2;
+ bool all = 3;
+ bool latest = 4;
+ }
+}
+
+message Condition { // driven by resource change event
+ // Condition ID
+ string id = 1; // @gotags: bson:"id"
+ uint64 version = 2; // @gotags: bson:"version"
+ string name = 3; // @gotags: bson:"name,omitempty"
+ bool enabled = 4; // @gotags: bson:"enabled,omitempty"
+ // ID of the configuration to be applied when the condition is satisfied
+ string configuration_id = 5; // @gotags: bson:"configurationId"
+ // list of device IDs to which the condition applies
+ repeated string device_id_filter = 6; // @gotags: bson:"deviceIdFilter,omitempty"
+ repeated string resource_type_filter = 7; // @gotags: bson:"resourceTypeFilter,omitempty"
+ // list of resource hrefs to which the condition applies
+ repeated string resource_href_filter = 8; // @gotags: bson:"resourceHrefFilter,omitempty"
+ string jq_expression_filter = 9; // @gotags: bson:"jqExpressionFilter,omitempty"
+ // Token used to update resources in the configuration
+ string api_access_token = 10; // @gotags: bson:"apiAccessToken,omitempty"
+ // Condition owner
+ string owner = 11; // @gotags: bson:"owner"
+ // Unix timestamp in ns when the condition has been created/updated
+ int64 timestamp = 12; // @gotags: bson:"timestamp"
+}
+
+message GetConditionsRequest { repeated IDFilter id_filter = 1; }
+message DeleteConditionsRequest { repeated IDFilter id_filter = 1; }
+message DeleteConditionsResponse { int64 count = 1; }
+
+message Configuration {
+ message Resource {
+ // href of the resource
+ string href = 1;
+ // content update of the resource
+ resourceaggregate.pb.Content content = 2;
+ // optional update command time to live, 0 is infinite
+ int64 time_to_live = 3;
+ }
+ // Configuration ID
+ string id = 1;
+ // Configuration version
+ uint64 version = 2;
+ // User-friendly configuration name
+ string name = 3; // @gotags: bson:"name,omitempty"
+ // List of resource updates to be applied
+ repeated Resource resources = 4;
+ // Configuration owner
+ string owner = 5;
+ // Unix timestamp in ns when the configuration has been created/updated
+ int64 timestamp = 6; // @gotags: bson:"timestamp"
+}
+
+message GetConfigurationsRequest {
+ repeated IDFilter id_filter = 1;
+ // Format: {id}/{version}, e.g., "ae424c58-e517-4494-6de7-583536c48213/all" or "ae424c58-e517-4494-6de7-583536c48213/latest" or "ae424c58-e517-4494-6de7-583536c48213/{version}"
+ repeated string http_id_filter = 2 [ deprecated = true ];
+}
+message DeleteConfigurationsRequest { repeated IDFilter id_filter = 1; }
+message DeleteConfigurationsResponse { int64 count = 1; }
+
+message AppliedDeviceConfiguration { //TODO naming
+ message Resource {
+ resourceaggregate.pb.ResourceId resource_id = 1; // TODO Jozo href only?
+ string correlation_id = 2; // Reused from invoke command or generated. Can be used to retrieve corresponding pending command.
+ enum Status {
+ QUEUED = 0;
+ PENDING = 1;
+ DONE = 2; // If done look to resource_updated even update resource failed for resource aggregate.
+ TIMEOUT = 3;
+ };
+ Status status = 3;
+ resourceaggregate.pb.ResourceUpdated resource_updated = 4;
+ }
+ string id = 1;
+ string device_id = 2;
+ message RelationTo { //TODO naming
+ string id = 1;
+ uint64 version = 2;
+ }
+ RelationTo configuration_id = 3;
+ oneof executed_by {
+ bool on_demand = 4;
+ RelationTo condition_id = 5; //TODO Naming
+ }
+ repeated Resource resources = 6; //TODO naming
+ string owner = 7;
+ // Unix timestamp in ns when the applied device configuration has been created/updated
+ int64 timestamp = 8; // @gotags: bson:"timestamp"
+}
+
+message InvokeConfigurationRequest {
+ string configuration_id = 1; // applies latest configuration
+ string device_id = 2;
+ bool force = 3; // force update even if the configuration has already been applied to device
+ string correlation_id = 4; // propagated down to the resource update command
+}
+
+message GetAppliedDeviceConfigurationsRequest { //TODO Naming
+ repeated string id_filter = 1;
+ repeated IDFilter configuration_id_filter = 2;
+ repeated string device_id_filter = 3;
+ repeated IDFilter condition_id_filter = 4;
+}
+
+message DeleteAppliedDeviceConfigurationsRequest { repeated string id_filter = 1; }
+message DeleteAppliedDeviceConfigurationsResponse { int64 count = 1; }
+
+service SnippetService {
+ rpc CreateCondition(Condition) returns (Condition) {
+ option (google.api.http) = {
+ post: "/snippet-service/api/v1/conditions";
+ body: "*";
+ };
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+ tags: [ "Conditions" ];
+ };
+ }
+
+ rpc GetConditions(GetConditionsRequest) returns (stream Condition) {
+ option (google.api.http) = {
+ get: "/snippet-service/api/v1/conditions";
+ };
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+ tags: [ "Conditions" ];
+ };
+ }
+
+ rpc DeleteConditions(DeleteConditionsRequest) returns (DeleteConditionsResponse) {
+ option (google.api.http) = {
+ delete: "/snippet-service/api/v1/conditions";
+ };
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+ tags: [ "Conditions" ];
+ };
+ }
+
+ rpc UpdateCondition(Condition) returns (Condition) {
+ option (google.api.http) = {
+ put: "/snippet-service/api/v1/conditions/{id}";
+ body: "*";
+ };
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+ tags: [ "Conditions" ];
+ };
+ }
+
+ rpc CreateConfiguration(Configuration) returns (Configuration) {
+ option (google.api.http) = {
+ post: "/snippet-service/api/v1/configurations";
+ body: "*";
+ };
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+ tags: [ "Configurations" ];
+ };
+ }
+
+ rpc GetConfigurations(GetConfigurationsRequest) returns (stream Configuration) {
+ option (google.api.http) = {
+ get: "/snippet-service/api/v1/configurations";
+ };
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+ tags: [ "Configurations" ];
+ };
+ }
+
+ rpc DeleteConfigurations(DeleteConfigurationsRequest) returns (DeleteConfigurationsResponse) {
+ option (google.api.http) = {
+ delete: "/snippet-service/api/v1/configurations";
+ };
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+ tags: [ "Configurations" ];
+ };
+ }
+
+ rpc UpdateConfiguration(Configuration) returns (Configuration) {
+ option (google.api.http) = {
+ put: "/snippet-service/api/v1/configurations/{id}";
+ body: "*";
+ };
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+ tags: [ "Configurations" ];
+ };
+ }
+
+ // streaming process of update configuration to invoker
+ rpc InvokeConfiguration(InvokeConfigurationRequest) returns (stream AppliedDeviceConfiguration) {
+ option (google.api.http) = {
+ post: "/snippet-service/api/v1/configurations/{configuration_id}";
+ body: "*";
+ };
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+ tags: [ "Configurations" ];
+ };
+ }
+
+ rpc GetAppliedConfigurations(GetAppliedDeviceConfigurationsRequest) returns (stream AppliedDeviceConfiguration) {
+ option (google.api.http) = {
+ get: "/snippet-service/api/v1/configurations/applied";
+ };
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+ tags: [ "Configurations" ];
+ };
+ }
+
+ rpc DeleteAppliedConfigurations(DeleteAppliedDeviceConfigurationsRequest) returns (DeleteAppliedDeviceConfigurationsResponse) {
+ option (google.api.http) = {
+ delete: "/snippet-service/api/v1/configurations/applied";
+ };
+ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+ tags: [ "Configurations" ];
+ };
+ }
+}
+
+// Trigger resource changed event from NATs.
+// 1. Check if condition is satisfied.
+// 2. Invoke configuration with ID.
+// 3. If the configuration has already been applied to the device, skip applying the configuration.
+// 4. Create applied configuration with ConfigurationId and for each resource with resourceId, QUEUED status, valid until the theoretical maximum timeout.
+// 5. Iterate over resources and
+// 6. Set start time, valid until, and status to IN PROGRESS.
+// 7. Register NATs for resource changed event.
+// 8. If the resource has not been published yet (href, resource types not exist), set status to WAITING RESOURCE and wait for resource changed event until timeout.
+// 9. If getting resource content fails or not found, set status to WAITING RESOURCE and wait for resource changed event until timeout.
+// 10. If timeout occurs, set status to TIMEOUT.
+// 11. Otherwise, set status to IN PROGRESS and update resource with new content. If the update fails, set status to FAIL; otherwise, set status to DONE.
diff --git a/snippet-service/pb/service.swagger.json b/snippet-service/pb/service.swagger.json
new file mode 100644
index 000000000..e9559eaa2
--- /dev/null
+++ b/snippet-service/pb/service.swagger.json
@@ -0,0 +1,944 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "title": "PLGD Rule Engine API",
+ "description": "API for configuring rule engine in PLGD",
+ "version": "1.0",
+ "contact": {
+ "name": "plgd.dev",
+ "url": "https://github.com/plgd-dev/hub",
+ "email": "info@plgd.dev"
+ },
+ "license": {
+ "name": "Apache License 2.0",
+ "url": "https://github.com/plgd-dev/hub/blob/v2/LICENSE"
+ }
+ },
+ "tags": [
+ {
+ "name": "SnippetService"
+ }
+ ],
+ "schemes": [
+ "https"
+ ],
+ "consumes": [
+ "application/json",
+ "application/protojson"
+ ],
+ "produces": [
+ "application/json",
+ "application/protojson"
+ ],
+ "paths": {
+ "/snippet-service/api/v1/conditions": {
+ "get": {
+ "operationId": "SnippetService_GetConditions",
+ "responses": {
+ "200": {
+ "description": "A successful response.(streaming responses)",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "result": {
+ "$ref": "#/definitions/pbCondition"
+ },
+ "error": {
+ "$ref": "#/definitions/googlerpcStatus"
+ }
+ },
+ "title": "Stream result of pbCondition"
+ }
+ },
+ "default": {
+ "description": "An unexpected error response.",
+ "schema": {
+ "$ref": "#/definitions/googlerpcStatus"
+ }
+ }
+ },
+ "tags": [
+ "Conditions"
+ ]
+ },
+ "delete": {
+ "operationId": "SnippetService_DeleteConditions",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/pbDeleteConditionsResponse"
+ }
+ },
+ "default": {
+ "description": "An unexpected error response.",
+ "schema": {
+ "$ref": "#/definitions/googlerpcStatus"
+ }
+ }
+ },
+ "tags": [
+ "Conditions"
+ ]
+ },
+ "post": {
+ "operationId": "SnippetService_CreateCondition",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/pbCondition"
+ }
+ },
+ "default": {
+ "description": "An unexpected error response.",
+ "schema": {
+ "$ref": "#/definitions/googlerpcStatus"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/pbCondition"
+ }
+ }
+ ],
+ "tags": [
+ "Conditions"
+ ]
+ }
+ },
+ "/snippet-service/api/v1/conditions/{id}": {
+ "put": {
+ "operationId": "SnippetService_UpdateCondition",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/pbCondition"
+ }
+ },
+ "default": {
+ "description": "An unexpected error response.",
+ "schema": {
+ "$ref": "#/definitions/googlerpcStatus"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "description": "Condition ID\n\n@gotags: bson:\"id\"",
+ "in": "path",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/SnippetServiceUpdateConditionBody"
+ }
+ }
+ ],
+ "tags": [
+ "Conditions"
+ ]
+ }
+ },
+ "/snippet-service/api/v1/configurations": {
+ "get": {
+ "operationId": "SnippetService_GetConfigurations",
+ "responses": {
+ "200": {
+ "description": "A successful response.(streaming responses)",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "result": {
+ "$ref": "#/definitions/pbConfiguration"
+ },
+ "error": {
+ "$ref": "#/definitions/googlerpcStatus"
+ }
+ },
+ "title": "Stream result of pbConfiguration"
+ }
+ },
+ "default": {
+ "description": "An unexpected error response.",
+ "schema": {
+ "$ref": "#/definitions/googlerpcStatus"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "httpIdFilter",
+ "description": "Format: {id}/{version}, e.g., \"ae424c58-e517-4494-6de7-583536c48213/all\" or \"ae424c58-e517-4494-6de7-583536c48213/latest\" or \"ae424c58-e517-4494-6de7-583536c48213/{version}\"",
+ "in": "query",
+ "required": false,
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "collectionFormat": "multi"
+ }
+ ],
+ "tags": [
+ "Configurations"
+ ]
+ },
+ "delete": {
+ "operationId": "SnippetService_DeleteConfigurations",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/pbDeleteConfigurationsResponse"
+ }
+ },
+ "default": {
+ "description": "An unexpected error response.",
+ "schema": {
+ "$ref": "#/definitions/googlerpcStatus"
+ }
+ }
+ },
+ "tags": [
+ "Configurations"
+ ]
+ },
+ "post": {
+ "operationId": "SnippetService_CreateConfiguration",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/pbConfiguration"
+ }
+ },
+ "default": {
+ "description": "An unexpected error response.",
+ "schema": {
+ "$ref": "#/definitions/googlerpcStatus"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/pbConfiguration"
+ }
+ }
+ ],
+ "tags": [
+ "Configurations"
+ ]
+ }
+ },
+ "/snippet-service/api/v1/configurations/applied": {
+ "get": {
+ "operationId": "SnippetService_GetAppliedConfigurations",
+ "responses": {
+ "200": {
+ "description": "A successful response.(streaming responses)",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "result": {
+ "$ref": "#/definitions/pbAppliedDeviceConfiguration"
+ },
+ "error": {
+ "$ref": "#/definitions/googlerpcStatus"
+ }
+ },
+ "title": "Stream result of pbAppliedDeviceConfiguration"
+ }
+ },
+ "default": {
+ "description": "An unexpected error response.",
+ "schema": {
+ "$ref": "#/definitions/googlerpcStatus"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "idFilter",
+ "in": "query",
+ "required": false,
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "collectionFormat": "multi"
+ },
+ {
+ "name": "deviceIdFilter",
+ "in": "query",
+ "required": false,
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "collectionFormat": "multi"
+ }
+ ],
+ "tags": [
+ "Configurations"
+ ]
+ },
+ "delete": {
+ "operationId": "SnippetService_DeleteAppliedConfigurations",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/pbDeleteAppliedDeviceConfigurationsResponse"
+ }
+ },
+ "default": {
+ "description": "An unexpected error response.",
+ "schema": {
+ "$ref": "#/definitions/googlerpcStatus"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "idFilter",
+ "in": "query",
+ "required": false,
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "collectionFormat": "multi"
+ }
+ ],
+ "tags": [
+ "Configurations"
+ ]
+ }
+ },
+ "/snippet-service/api/v1/configurations/{configurationId}": {
+ "post": {
+ "summary": "streaming process of update configuration to invoker",
+ "operationId": "SnippetService_InvokeConfiguration",
+ "responses": {
+ "200": {
+ "description": "A successful response.(streaming responses)",
+ "schema": {
+ "type": "object",
+ "properties": {
+ "result": {
+ "$ref": "#/definitions/pbAppliedDeviceConfiguration"
+ },
+ "error": {
+ "$ref": "#/definitions/googlerpcStatus"
+ }
+ },
+ "title": "Stream result of pbAppliedDeviceConfiguration"
+ }
+ },
+ "default": {
+ "description": "An unexpected error response.",
+ "schema": {
+ "$ref": "#/definitions/googlerpcStatus"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "configurationId",
+ "description": "applies latest configuration",
+ "in": "path",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/SnippetServiceInvokeConfigurationBody"
+ }
+ }
+ ],
+ "tags": [
+ "Configurations"
+ ]
+ }
+ },
+ "/snippet-service/api/v1/configurations/{id}": {
+ "put": {
+ "operationId": "SnippetService_UpdateConfiguration",
+ "responses": {
+ "200": {
+ "description": "A successful response.",
+ "schema": {
+ "$ref": "#/definitions/pbConfiguration"
+ }
+ },
+ "default": {
+ "description": "An unexpected error response.",
+ "schema": {
+ "$ref": "#/definitions/googlerpcStatus"
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "description": "Configuration ID",
+ "in": "path",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "body",
+ "in": "body",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/SnippetServiceUpdateConfigurationBody"
+ }
+ }
+ ],
+ "tags": [
+ "Configurations"
+ ]
+ }
+ }
+ },
+ "definitions": {
+ "AppliedDeviceConfigurationRelationTo": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string",
+ "format": "uint64"
+ }
+ },
+ "title": "TODO naming"
+ },
+ "AppliedDeviceConfigurationResourceStatus": {
+ "type": "string",
+ "enum": [
+ "QUEUED",
+ "PENDING",
+ "DONE",
+ "TIMEOUT"
+ ],
+ "default": "QUEUED",
+ "description": " - DONE: If done look to resource_updated even update resource failed for resource aggregate."
+ },
+ "SnippetServiceInvokeConfigurationBody": {
+ "type": "object",
+ "properties": {
+ "deviceId": {
+ "type": "string"
+ },
+ "force": {
+ "type": "boolean",
+ "title": "force update even if the configuration has already been applied to device"
+ },
+ "correlationId": {
+ "type": "string",
+ "title": "propagated down to the resource update command"
+ }
+ }
+ },
+ "SnippetServiceUpdateConditionBody": {
+ "type": "object",
+ "properties": {
+ "version": {
+ "type": "string",
+ "format": "uint64",
+ "title": "@gotags: bson:\"version\""
+ },
+ "name": {
+ "type": "string",
+ "title": "@gotags: bson:\"name,omitempty\""
+ },
+ "enabled": {
+ "type": "boolean",
+ "title": "@gotags: bson:\"enabled,omitempty\""
+ },
+ "configurationId": {
+ "type": "string",
+ "description": "@gotags: bson:\"configurationId\"",
+ "title": "ID of the configuration to be applied when the condition is satisfied"
+ },
+ "deviceIdFilter": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "@gotags: bson:\"deviceIdFilter,omitempty\"",
+ "title": "list of device IDs to which the condition applies"
+ },
+ "resourceTypeFilter": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "title": "@gotags: bson:\"resourceTypeFilter,omitempty\""
+ },
+ "resourceHrefFilter": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "@gotags: bson:\"resourceHrefFilter,omitempty\"",
+ "title": "list of resource hrefs to which the condition applies"
+ },
+ "jqExpressionFilter": {
+ "type": "string",
+ "title": "@gotags: bson:\"jqExpressionFilter,omitempty\""
+ },
+ "apiAccessToken": {
+ "type": "string",
+ "description": "@gotags: bson:\"apiAccessToken,omitempty\"",
+ "title": "Token used to update resources in the configuration"
+ },
+ "owner": {
+ "type": "string",
+ "description": "@gotags: bson:\"owner\"",
+ "title": "Condition owner"
+ },
+ "timestamp": {
+ "type": "string",
+ "format": "int64",
+ "description": "@gotags: bson:\"timestamp\"",
+ "title": "Unix timestamp in ns when the condition has been created/updated"
+ }
+ },
+ "title": "driven by resource change event"
+ },
+ "SnippetServiceUpdateConfigurationBody": {
+ "type": "object",
+ "properties": {
+ "version": {
+ "type": "string",
+ "format": "uint64",
+ "title": "Configuration version"
+ },
+ "name": {
+ "type": "string",
+ "description": "@gotags: bson:\"name,omitempty\"",
+ "title": "User-friendly configuration name"
+ },
+ "resources": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "$ref": "#/definitions/pbConfigurationResource"
+ },
+ "title": "List of resource updates to be applied"
+ },
+ "owner": {
+ "type": "string",
+ "title": "Configuration owner"
+ },
+ "timestamp": {
+ "type": "string",
+ "format": "int64",
+ "description": "@gotags: bson:\"timestamp\"",
+ "title": "Unix timestamp in ns when the configuration has been created/updated"
+ }
+ }
+ },
+ "googlerpcStatus": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "message": {
+ "type": "string"
+ },
+ "details": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "$ref": "#/definitions/protobufAny"
+ }
+ }
+ }
+ },
+ "pbAppliedDeviceConfiguration": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "deviceId": {
+ "type": "string"
+ },
+ "configurationId": {
+ "$ref": "#/definitions/AppliedDeviceConfigurationRelationTo"
+ },
+ "onDemand": {
+ "type": "boolean"
+ },
+ "conditionId": {
+ "$ref": "#/definitions/AppliedDeviceConfigurationRelationTo",
+ "title": "TODO Naming"
+ },
+ "resources": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "$ref": "#/definitions/pbAppliedDeviceConfigurationResource"
+ },
+ "title": "TODO naming"
+ },
+ "owner": {
+ "type": "string"
+ },
+ "timestamp": {
+ "type": "string",
+ "format": "int64",
+ "description": "@gotags: bson:\"timestamp\"",
+ "title": "Unix timestamp in ns when the applied device configuration has been created/updated"
+ }
+ },
+ "title": "TODO naming"
+ },
+ "pbAppliedDeviceConfigurationResource": {
+ "type": "object",
+ "properties": {
+ "resourceId": {
+ "$ref": "#/definitions/pbResourceId",
+ "title": "TODO Jozo href only?"
+ },
+ "correlationId": {
+ "type": "string",
+ "description": "Reused from invoke command or generated. Can be used to retrieve corresponding pending command."
+ },
+ "status": {
+ "$ref": "#/definitions/AppliedDeviceConfigurationResourceStatus"
+ },
+ "resourceUpdated": {
+ "$ref": "#/definitions/pbResourceUpdated"
+ }
+ }
+ },
+ "pbAuditContext": {
+ "type": "object",
+ "properties": {
+ "userId": {
+ "type": "string"
+ },
+ "correlationId": {
+ "type": "string"
+ },
+ "owner": {
+ "type": "string"
+ }
+ }
+ },
+ "pbCondition": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "@gotags: bson:\"id\"",
+ "title": "Condition ID"
+ },
+ "version": {
+ "type": "string",
+ "format": "uint64",
+ "title": "@gotags: bson:\"version\""
+ },
+ "name": {
+ "type": "string",
+ "title": "@gotags: bson:\"name,omitempty\""
+ },
+ "enabled": {
+ "type": "boolean",
+ "title": "@gotags: bson:\"enabled,omitempty\""
+ },
+ "configurationId": {
+ "type": "string",
+ "description": "@gotags: bson:\"configurationId\"",
+ "title": "ID of the configuration to be applied when the condition is satisfied"
+ },
+ "deviceIdFilter": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "@gotags: bson:\"deviceIdFilter,omitempty\"",
+ "title": "list of device IDs to which the condition applies"
+ },
+ "resourceTypeFilter": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "title": "@gotags: bson:\"resourceTypeFilter,omitempty\""
+ },
+ "resourceHrefFilter": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "@gotags: bson:\"resourceHrefFilter,omitempty\"",
+ "title": "list of resource hrefs to which the condition applies"
+ },
+ "jqExpressionFilter": {
+ "type": "string",
+ "title": "@gotags: bson:\"jqExpressionFilter,omitempty\""
+ },
+ "apiAccessToken": {
+ "type": "string",
+ "description": "@gotags: bson:\"apiAccessToken,omitempty\"",
+ "title": "Token used to update resources in the configuration"
+ },
+ "owner": {
+ "type": "string",
+ "description": "@gotags: bson:\"owner\"",
+ "title": "Condition owner"
+ },
+ "timestamp": {
+ "type": "string",
+ "format": "int64",
+ "description": "@gotags: bson:\"timestamp\"",
+ "title": "Unix timestamp in ns when the condition has been created/updated"
+ }
+ },
+ "title": "driven by resource change event"
+ },
+ "pbConfiguration": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string",
+ "title": "Configuration ID"
+ },
+ "version": {
+ "type": "string",
+ "format": "uint64",
+ "title": "Configuration version"
+ },
+ "name": {
+ "type": "string",
+ "description": "@gotags: bson:\"name,omitempty\"",
+ "title": "User-friendly configuration name"
+ },
+ "resources": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "$ref": "#/definitions/pbConfigurationResource"
+ },
+ "title": "List of resource updates to be applied"
+ },
+ "owner": {
+ "type": "string",
+ "title": "Configuration owner"
+ },
+ "timestamp": {
+ "type": "string",
+ "format": "int64",
+ "description": "@gotags: bson:\"timestamp\"",
+ "title": "Unix timestamp in ns when the configuration has been created/updated"
+ }
+ }
+ },
+ "pbConfigurationResource": {
+ "type": "object",
+ "properties": {
+ "href": {
+ "type": "string",
+ "title": "href of the resource"
+ },
+ "content": {
+ "$ref": "#/definitions/pbContent",
+ "title": "content update of the resource"
+ },
+ "timeToLive": {
+ "type": "string",
+ "format": "int64",
+ "title": "optional update command time to live, 0 is infinite"
+ }
+ }
+ },
+ "pbContent": {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "string",
+ "format": "byte"
+ },
+ "contentType": {
+ "type": "string"
+ },
+ "coapContentFormat": {
+ "type": "integer",
+ "format": "int32",
+ "title": "-1 means content-format was not provided"
+ }
+ }
+ },
+ "pbDeleteAppliedDeviceConfigurationsResponse": {
+ "type": "object",
+ "properties": {
+ "count": {
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ },
+ "pbDeleteConditionsResponse": {
+ "type": "object",
+ "properties": {
+ "count": {
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ },
+ "pbDeleteConfigurationsResponse": {
+ "type": "object",
+ "properties": {
+ "count": {
+ "type": "string",
+ "format": "int64"
+ }
+ }
+ },
+ "pbEventMetadata": {
+ "type": "object",
+ "properties": {
+ "version": {
+ "type": "string",
+ "format": "uint64"
+ },
+ "timestamp": {
+ "type": "string",
+ "format": "int64"
+ },
+ "connectionId": {
+ "type": "string"
+ },
+ "sequence": {
+ "type": "string",
+ "format": "uint64",
+ "title": "sequence number within the same connection_id; the ResourceChanged event uses the value to skip old events, other event types might not fill the value"
+ },
+ "hubId": {
+ "type": "string",
+ "title": "the hub which sent the event"
+ }
+ }
+ },
+ "pbIDFilter": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string",
+ "format": "uint64"
+ },
+ "all": {
+ "type": "boolean"
+ },
+ "latest": {
+ "type": "boolean"
+ }
+ },
+ "title": "TODO: /configurations/123?version=latest"
+ },
+ "pbResourceId": {
+ "type": "object",
+ "properties": {
+ "deviceId": {
+ "type": "string"
+ },
+ "href": {
+ "type": "string"
+ }
+ }
+ },
+ "pbResourceUpdated": {
+ "type": "object",
+ "properties": {
+ "resourceId": {
+ "$ref": "#/definitions/pbResourceId"
+ },
+ "status": {
+ "$ref": "#/definitions/resourceaggregatepbStatus"
+ },
+ "content": {
+ "$ref": "#/definitions/pbContent"
+ },
+ "auditContext": {
+ "$ref": "#/definitions/pbAuditContext"
+ },
+ "eventMetadata": {
+ "$ref": "#/definitions/pbEventMetadata"
+ },
+ "resourceTypes": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "openTelemetryCarrier": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ },
+ "title": "Open telemetry data propagated to asynchronous events"
+ }
+ }
+ },
+ "protobufAny": {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": {}
+ },
+ "resourceaggregatepbStatus": {
+ "type": "string",
+ "enum": [
+ "UNKNOWN",
+ "OK",
+ "BAD_REQUEST",
+ "UNAUTHORIZED",
+ "FORBIDDEN",
+ "NOT_FOUND",
+ "UNAVAILABLE",
+ "NOT_IMPLEMENTED",
+ "ACCEPTED",
+ "ERROR",
+ "METHOD_NOT_ALLOWED",
+ "CREATED",
+ "CANCELED",
+ "NOT_MODIFIED"
+ ],
+ "default": "UNKNOWN",
+ "description": " - CANCELED: Canceled indicates the operation was canceled (typically by the user).\n - NOT_MODIFIED: Valid indicates the content hasn't changed. (provided etag in GET request is same as the resource etag)."
+ }
+ }
+}
diff --git a/snippet-service/pb/service_grpc.pb.go b/snippet-service/pb/service_grpc.pb.go
new file mode 100644
index 000000000..a31219a27
--- /dev/null
+++ b/snippet-service/pb/service_grpc.pb.go
@@ -0,0 +1,598 @@
+// TODO overit ze pending command sa nezmaze na neexistujucom resource ak sa device pripoji a nepublishne hned resource
+// Overit correlation id - ak sa pouziva rovnake napriec viacerymi resourcami
+
+// scenare
+// - Uzivatel vie vytvorit config, automaticka (backend) inkrementacia verzie
+// - Uzivatel updatne config, verzia sa inkrementuje, Modal -> chces aplikovat na vsetky uz provisionnute devici? Informovat uzivatela, ze niektore devici mozu byt offline a command moze vyexpirovat.
+// - Uzivatel updatne config, verzia sa inkrementuje, informujeme uzivatela ze vsetky pending commandy z predoslej verzie budu cancelnute ako aj dalsie sekvencne updaty resourcov pre predoslu verziu
+
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.3.0
+// - protoc v5.26.1
+// source: snippet-service/pb/service.proto
+
+package pb
+
+import (
+ context "context"
+ grpc "google.golang.org/grpc"
+ codes "google.golang.org/grpc/codes"
+ status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.32.0 or later.
+const _ = grpc.SupportPackageIsVersion7
+
+const (
+ SnippetService_CreateCondition_FullMethodName = "/snippetservice.pb.SnippetService/CreateCondition"
+ SnippetService_GetConditions_FullMethodName = "/snippetservice.pb.SnippetService/GetConditions"
+ SnippetService_DeleteConditions_FullMethodName = "/snippetservice.pb.SnippetService/DeleteConditions"
+ SnippetService_UpdateCondition_FullMethodName = "/snippetservice.pb.SnippetService/UpdateCondition"
+ SnippetService_CreateConfiguration_FullMethodName = "/snippetservice.pb.SnippetService/CreateConfiguration"
+ SnippetService_GetConfigurations_FullMethodName = "/snippetservice.pb.SnippetService/GetConfigurations"
+ SnippetService_DeleteConfigurations_FullMethodName = "/snippetservice.pb.SnippetService/DeleteConfigurations"
+ SnippetService_UpdateConfiguration_FullMethodName = "/snippetservice.pb.SnippetService/UpdateConfiguration"
+ SnippetService_InvokeConfiguration_FullMethodName = "/snippetservice.pb.SnippetService/InvokeConfiguration"
+ SnippetService_GetAppliedConfigurations_FullMethodName = "/snippetservice.pb.SnippetService/GetAppliedConfigurations"
+ SnippetService_DeleteAppliedConfigurations_FullMethodName = "/snippetservice.pb.SnippetService/DeleteAppliedConfigurations"
+)
+
+// SnippetServiceClient is the client API for SnippetService service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type SnippetServiceClient interface {
+ CreateCondition(ctx context.Context, in *Condition, opts ...grpc.CallOption) (*Condition, error)
+ GetConditions(ctx context.Context, in *GetConditionsRequest, opts ...grpc.CallOption) (SnippetService_GetConditionsClient, error)
+ DeleteConditions(ctx context.Context, in *DeleteConditionsRequest, opts ...grpc.CallOption) (*DeleteConditionsResponse, error)
+ UpdateCondition(ctx context.Context, in *Condition, opts ...grpc.CallOption) (*Condition, error)
+ CreateConfiguration(ctx context.Context, in *Configuration, opts ...grpc.CallOption) (*Configuration, error)
+ GetConfigurations(ctx context.Context, in *GetConfigurationsRequest, opts ...grpc.CallOption) (SnippetService_GetConfigurationsClient, error)
+ DeleteConfigurations(ctx context.Context, in *DeleteConfigurationsRequest, opts ...grpc.CallOption) (*DeleteConfigurationsResponse, error)
+ UpdateConfiguration(ctx context.Context, in *Configuration, opts ...grpc.CallOption) (*Configuration, error)
+ // streaming process of update configuration to invoker
+ InvokeConfiguration(ctx context.Context, in *InvokeConfigurationRequest, opts ...grpc.CallOption) (SnippetService_InvokeConfigurationClient, error)
+ GetAppliedConfigurations(ctx context.Context, in *GetAppliedDeviceConfigurationsRequest, opts ...grpc.CallOption) (SnippetService_GetAppliedConfigurationsClient, error)
+ DeleteAppliedConfigurations(ctx context.Context, in *DeleteAppliedDeviceConfigurationsRequest, opts ...grpc.CallOption) (*DeleteAppliedDeviceConfigurationsResponse, error)
+}
+
+type snippetServiceClient struct {
+ cc grpc.ClientConnInterface
+}
+
+func NewSnippetServiceClient(cc grpc.ClientConnInterface) SnippetServiceClient {
+ return &snippetServiceClient{cc}
+}
+
+func (c *snippetServiceClient) CreateCondition(ctx context.Context, in *Condition, opts ...grpc.CallOption) (*Condition, error) {
+ out := new(Condition)
+ err := c.cc.Invoke(ctx, SnippetService_CreateCondition_FullMethodName, in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *snippetServiceClient) GetConditions(ctx context.Context, in *GetConditionsRequest, opts ...grpc.CallOption) (SnippetService_GetConditionsClient, error) {
+ stream, err := c.cc.NewStream(ctx, &SnippetService_ServiceDesc.Streams[0], SnippetService_GetConditions_FullMethodName, opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &snippetServiceGetConditionsClient{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 SnippetService_GetConditionsClient interface {
+ Recv() (*Condition, error)
+ grpc.ClientStream
+}
+
+type snippetServiceGetConditionsClient struct {
+ grpc.ClientStream
+}
+
+func (x *snippetServiceGetConditionsClient) Recv() (*Condition, error) {
+ m := new(Condition)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func (c *snippetServiceClient) DeleteConditions(ctx context.Context, in *DeleteConditionsRequest, opts ...grpc.CallOption) (*DeleteConditionsResponse, error) {
+ out := new(DeleteConditionsResponse)
+ err := c.cc.Invoke(ctx, SnippetService_DeleteConditions_FullMethodName, in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *snippetServiceClient) UpdateCondition(ctx context.Context, in *Condition, opts ...grpc.CallOption) (*Condition, error) {
+ out := new(Condition)
+ err := c.cc.Invoke(ctx, SnippetService_UpdateCondition_FullMethodName, in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *snippetServiceClient) CreateConfiguration(ctx context.Context, in *Configuration, opts ...grpc.CallOption) (*Configuration, error) {
+ out := new(Configuration)
+ err := c.cc.Invoke(ctx, SnippetService_CreateConfiguration_FullMethodName, in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *snippetServiceClient) GetConfigurations(ctx context.Context, in *GetConfigurationsRequest, opts ...grpc.CallOption) (SnippetService_GetConfigurationsClient, error) {
+ stream, err := c.cc.NewStream(ctx, &SnippetService_ServiceDesc.Streams[1], SnippetService_GetConfigurations_FullMethodName, opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &snippetServiceGetConfigurationsClient{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 SnippetService_GetConfigurationsClient interface {
+ Recv() (*Configuration, error)
+ grpc.ClientStream
+}
+
+type snippetServiceGetConfigurationsClient struct {
+ grpc.ClientStream
+}
+
+func (x *snippetServiceGetConfigurationsClient) Recv() (*Configuration, error) {
+ m := new(Configuration)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func (c *snippetServiceClient) DeleteConfigurations(ctx context.Context, in *DeleteConfigurationsRequest, opts ...grpc.CallOption) (*DeleteConfigurationsResponse, error) {
+ out := new(DeleteConfigurationsResponse)
+ err := c.cc.Invoke(ctx, SnippetService_DeleteConfigurations_FullMethodName, in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *snippetServiceClient) UpdateConfiguration(ctx context.Context, in *Configuration, opts ...grpc.CallOption) (*Configuration, error) {
+ out := new(Configuration)
+ err := c.cc.Invoke(ctx, SnippetService_UpdateConfiguration_FullMethodName, in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *snippetServiceClient) InvokeConfiguration(ctx context.Context, in *InvokeConfigurationRequest, opts ...grpc.CallOption) (SnippetService_InvokeConfigurationClient, error) {
+ stream, err := c.cc.NewStream(ctx, &SnippetService_ServiceDesc.Streams[2], SnippetService_InvokeConfiguration_FullMethodName, opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &snippetServiceInvokeConfigurationClient{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 SnippetService_InvokeConfigurationClient interface {
+ Recv() (*AppliedDeviceConfiguration, error)
+ grpc.ClientStream
+}
+
+type snippetServiceInvokeConfigurationClient struct {
+ grpc.ClientStream
+}
+
+func (x *snippetServiceInvokeConfigurationClient) Recv() (*AppliedDeviceConfiguration, error) {
+ m := new(AppliedDeviceConfiguration)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func (c *snippetServiceClient) GetAppliedConfigurations(ctx context.Context, in *GetAppliedDeviceConfigurationsRequest, opts ...grpc.CallOption) (SnippetService_GetAppliedConfigurationsClient, error) {
+ stream, err := c.cc.NewStream(ctx, &SnippetService_ServiceDesc.Streams[3], SnippetService_GetAppliedConfigurations_FullMethodName, opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &snippetServiceGetAppliedConfigurationsClient{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 SnippetService_GetAppliedConfigurationsClient interface {
+ Recv() (*AppliedDeviceConfiguration, error)
+ grpc.ClientStream
+}
+
+type snippetServiceGetAppliedConfigurationsClient struct {
+ grpc.ClientStream
+}
+
+func (x *snippetServiceGetAppliedConfigurationsClient) Recv() (*AppliedDeviceConfiguration, error) {
+ m := new(AppliedDeviceConfiguration)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func (c *snippetServiceClient) DeleteAppliedConfigurations(ctx context.Context, in *DeleteAppliedDeviceConfigurationsRequest, opts ...grpc.CallOption) (*DeleteAppliedDeviceConfigurationsResponse, error) {
+ out := new(DeleteAppliedDeviceConfigurationsResponse)
+ err := c.cc.Invoke(ctx, SnippetService_DeleteAppliedConfigurations_FullMethodName, in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// SnippetServiceServer is the server API for SnippetService service.
+// All implementations must embed UnimplementedSnippetServiceServer
+// for forward compatibility
+type SnippetServiceServer interface {
+ CreateCondition(context.Context, *Condition) (*Condition, error)
+ GetConditions(*GetConditionsRequest, SnippetService_GetConditionsServer) error
+ DeleteConditions(context.Context, *DeleteConditionsRequest) (*DeleteConditionsResponse, error)
+ UpdateCondition(context.Context, *Condition) (*Condition, error)
+ CreateConfiguration(context.Context, *Configuration) (*Configuration, error)
+ GetConfigurations(*GetConfigurationsRequest, SnippetService_GetConfigurationsServer) error
+ DeleteConfigurations(context.Context, *DeleteConfigurationsRequest) (*DeleteConfigurationsResponse, error)
+ UpdateConfiguration(context.Context, *Configuration) (*Configuration, error)
+ // streaming process of update configuration to invoker
+ InvokeConfiguration(*InvokeConfigurationRequest, SnippetService_InvokeConfigurationServer) error
+ GetAppliedConfigurations(*GetAppliedDeviceConfigurationsRequest, SnippetService_GetAppliedConfigurationsServer) error
+ DeleteAppliedConfigurations(context.Context, *DeleteAppliedDeviceConfigurationsRequest) (*DeleteAppliedDeviceConfigurationsResponse, error)
+ mustEmbedUnimplementedSnippetServiceServer()
+}
+
+// UnimplementedSnippetServiceServer must be embedded to have forward compatible implementations.
+type UnimplementedSnippetServiceServer struct {
+}
+
+func (UnimplementedSnippetServiceServer) CreateCondition(context.Context, *Condition) (*Condition, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method CreateCondition not implemented")
+}
+func (UnimplementedSnippetServiceServer) GetConditions(*GetConditionsRequest, SnippetService_GetConditionsServer) error {
+ return status.Errorf(codes.Unimplemented, "method GetConditions not implemented")
+}
+func (UnimplementedSnippetServiceServer) DeleteConditions(context.Context, *DeleteConditionsRequest) (*DeleteConditionsResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method DeleteConditions not implemented")
+}
+func (UnimplementedSnippetServiceServer) UpdateCondition(context.Context, *Condition) (*Condition, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method UpdateCondition not implemented")
+}
+func (UnimplementedSnippetServiceServer) CreateConfiguration(context.Context, *Configuration) (*Configuration, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method CreateConfiguration not implemented")
+}
+func (UnimplementedSnippetServiceServer) GetConfigurations(*GetConfigurationsRequest, SnippetService_GetConfigurationsServer) error {
+ return status.Errorf(codes.Unimplemented, "method GetConfigurations not implemented")
+}
+func (UnimplementedSnippetServiceServer) DeleteConfigurations(context.Context, *DeleteConfigurationsRequest) (*DeleteConfigurationsResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method DeleteConfigurations not implemented")
+}
+func (UnimplementedSnippetServiceServer) UpdateConfiguration(context.Context, *Configuration) (*Configuration, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method UpdateConfiguration not implemented")
+}
+func (UnimplementedSnippetServiceServer) InvokeConfiguration(*InvokeConfigurationRequest, SnippetService_InvokeConfigurationServer) error {
+ return status.Errorf(codes.Unimplemented, "method InvokeConfiguration not implemented")
+}
+func (UnimplementedSnippetServiceServer) GetAppliedConfigurations(*GetAppliedDeviceConfigurationsRequest, SnippetService_GetAppliedConfigurationsServer) error {
+ return status.Errorf(codes.Unimplemented, "method GetAppliedConfigurations not implemented")
+}
+func (UnimplementedSnippetServiceServer) DeleteAppliedConfigurations(context.Context, *DeleteAppliedDeviceConfigurationsRequest) (*DeleteAppliedDeviceConfigurationsResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method DeleteAppliedConfigurations not implemented")
+}
+func (UnimplementedSnippetServiceServer) mustEmbedUnimplementedSnippetServiceServer() {}
+
+// UnsafeSnippetServiceServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to SnippetServiceServer will
+// result in compilation errors.
+type UnsafeSnippetServiceServer interface {
+ mustEmbedUnimplementedSnippetServiceServer()
+}
+
+func RegisterSnippetServiceServer(s grpc.ServiceRegistrar, srv SnippetServiceServer) {
+ s.RegisterService(&SnippetService_ServiceDesc, srv)
+}
+
+func _SnippetService_CreateCondition_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(Condition)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(SnippetServiceServer).CreateCondition(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: SnippetService_CreateCondition_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(SnippetServiceServer).CreateCondition(ctx, req.(*Condition))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _SnippetService_GetConditions_Handler(srv interface{}, stream grpc.ServerStream) error {
+ m := new(GetConditionsRequest)
+ if err := stream.RecvMsg(m); err != nil {
+ return err
+ }
+ return srv.(SnippetServiceServer).GetConditions(m, &snippetServiceGetConditionsServer{stream})
+}
+
+type SnippetService_GetConditionsServer interface {
+ Send(*Condition) error
+ grpc.ServerStream
+}
+
+type snippetServiceGetConditionsServer struct {
+ grpc.ServerStream
+}
+
+func (x *snippetServiceGetConditionsServer) Send(m *Condition) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func _SnippetService_DeleteConditions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(DeleteConditionsRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(SnippetServiceServer).DeleteConditions(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: SnippetService_DeleteConditions_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(SnippetServiceServer).DeleteConditions(ctx, req.(*DeleteConditionsRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _SnippetService_UpdateCondition_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(Condition)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(SnippetServiceServer).UpdateCondition(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: SnippetService_UpdateCondition_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(SnippetServiceServer).UpdateCondition(ctx, req.(*Condition))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _SnippetService_CreateConfiguration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(Configuration)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(SnippetServiceServer).CreateConfiguration(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: SnippetService_CreateConfiguration_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(SnippetServiceServer).CreateConfiguration(ctx, req.(*Configuration))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _SnippetService_GetConfigurations_Handler(srv interface{}, stream grpc.ServerStream) error {
+ m := new(GetConfigurationsRequest)
+ if err := stream.RecvMsg(m); err != nil {
+ return err
+ }
+ return srv.(SnippetServiceServer).GetConfigurations(m, &snippetServiceGetConfigurationsServer{stream})
+}
+
+type SnippetService_GetConfigurationsServer interface {
+ Send(*Configuration) error
+ grpc.ServerStream
+}
+
+type snippetServiceGetConfigurationsServer struct {
+ grpc.ServerStream
+}
+
+func (x *snippetServiceGetConfigurationsServer) Send(m *Configuration) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func _SnippetService_DeleteConfigurations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(DeleteConfigurationsRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(SnippetServiceServer).DeleteConfigurations(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: SnippetService_DeleteConfigurations_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(SnippetServiceServer).DeleteConfigurations(ctx, req.(*DeleteConfigurationsRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _SnippetService_UpdateConfiguration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(Configuration)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(SnippetServiceServer).UpdateConfiguration(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: SnippetService_UpdateConfiguration_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(SnippetServiceServer).UpdateConfiguration(ctx, req.(*Configuration))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _SnippetService_InvokeConfiguration_Handler(srv interface{}, stream grpc.ServerStream) error {
+ m := new(InvokeConfigurationRequest)
+ if err := stream.RecvMsg(m); err != nil {
+ return err
+ }
+ return srv.(SnippetServiceServer).InvokeConfiguration(m, &snippetServiceInvokeConfigurationServer{stream})
+}
+
+type SnippetService_InvokeConfigurationServer interface {
+ Send(*AppliedDeviceConfiguration) error
+ grpc.ServerStream
+}
+
+type snippetServiceInvokeConfigurationServer struct {
+ grpc.ServerStream
+}
+
+func (x *snippetServiceInvokeConfigurationServer) Send(m *AppliedDeviceConfiguration) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func _SnippetService_GetAppliedConfigurations_Handler(srv interface{}, stream grpc.ServerStream) error {
+ m := new(GetAppliedDeviceConfigurationsRequest)
+ if err := stream.RecvMsg(m); err != nil {
+ return err
+ }
+ return srv.(SnippetServiceServer).GetAppliedConfigurations(m, &snippetServiceGetAppliedConfigurationsServer{stream})
+}
+
+type SnippetService_GetAppliedConfigurationsServer interface {
+ Send(*AppliedDeviceConfiguration) error
+ grpc.ServerStream
+}
+
+type snippetServiceGetAppliedConfigurationsServer struct {
+ grpc.ServerStream
+}
+
+func (x *snippetServiceGetAppliedConfigurationsServer) Send(m *AppliedDeviceConfiguration) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func _SnippetService_DeleteAppliedConfigurations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(DeleteAppliedDeviceConfigurationsRequest)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(SnippetServiceServer).DeleteAppliedConfigurations(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: SnippetService_DeleteAppliedConfigurations_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(SnippetServiceServer).DeleteAppliedConfigurations(ctx, req.(*DeleteAppliedDeviceConfigurationsRequest))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+// SnippetService_ServiceDesc is the grpc.ServiceDesc for SnippetService service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var SnippetService_ServiceDesc = grpc.ServiceDesc{
+ ServiceName: "snippetservice.pb.SnippetService",
+ HandlerType: (*SnippetServiceServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "CreateCondition",
+ Handler: _SnippetService_CreateCondition_Handler,
+ },
+ {
+ MethodName: "DeleteConditions",
+ Handler: _SnippetService_DeleteConditions_Handler,
+ },
+ {
+ MethodName: "UpdateCondition",
+ Handler: _SnippetService_UpdateCondition_Handler,
+ },
+ {
+ MethodName: "CreateConfiguration",
+ Handler: _SnippetService_CreateConfiguration_Handler,
+ },
+ {
+ MethodName: "DeleteConfigurations",
+ Handler: _SnippetService_DeleteConfigurations_Handler,
+ },
+ {
+ MethodName: "UpdateConfiguration",
+ Handler: _SnippetService_UpdateConfiguration_Handler,
+ },
+ {
+ MethodName: "DeleteAppliedConfigurations",
+ Handler: _SnippetService_DeleteAppliedConfigurations_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{
+ {
+ StreamName: "GetConditions",
+ Handler: _SnippetService_GetConditions_Handler,
+ ServerStreams: true,
+ },
+ {
+ StreamName: "GetConfigurations",
+ Handler: _SnippetService_GetConfigurations_Handler,
+ ServerStreams: true,
+ },
+ {
+ StreamName: "InvokeConfiguration",
+ Handler: _SnippetService_InvokeConfiguration_Handler,
+ ServerStreams: true,
+ },
+ {
+ StreamName: "GetAppliedConfigurations",
+ Handler: _SnippetService_GetAppliedConfigurations_Handler,
+ ServerStreams: true,
+ },
+ },
+ Metadata: "snippet-service/pb/service.proto",
+}
diff --git a/snippet-service/service/config.go b/snippet-service/service/config.go
new file mode 100644
index 000000000..99f8c6755
--- /dev/null
+++ b/snippet-service/service/config.go
@@ -0,0 +1,120 @@
+package service
+
+import (
+ "fmt"
+ "net"
+ "time"
+
+ "github.com/go-co-op/gocron/v2"
+ "github.com/google/uuid"
+ "github.com/plgd-dev/hub/v2/pkg/config"
+ "github.com/plgd-dev/hub/v2/pkg/log"
+ httpServer "github.com/plgd-dev/hub/v2/pkg/net/http/server"
+ otelClient "github.com/plgd-dev/hub/v2/pkg/opentelemetry/collector/client"
+ grpcService "github.com/plgd-dev/hub/v2/snippet-service/service/grpc"
+ storeConfig "github.com/plgd-dev/hub/v2/snippet-service/store/config"
+)
+
+type Config struct {
+ HubID string `yaml:"hubID" json:"hubId"`
+ Log log.Config `yaml:"log" json:"log"`
+ APIs APIsConfig `yaml:"apis" json:"apis"`
+ Clients ClientsConfig `yaml:"clients" json:"clients"`
+}
+
+func (c *Config) Validate() error {
+ if err := c.Log.Validate(); err != nil {
+ return fmt.Errorf("log.%w", err)
+ }
+ if err := c.APIs.Validate(); err != nil {
+ return fmt.Errorf("apis.%w", err)
+ }
+ if err := c.Clients.Validate(); err != nil {
+ return fmt.Errorf("clients.%w", err)
+ }
+ if _, err := uuid.Parse(c.HubID); err != nil {
+ return fmt.Errorf("hubID('%v') - %w", c.HubID, err)
+ }
+
+ return nil
+}
+
+// Config represent application configuration
+type APIsConfig struct {
+ GRPC grpcService.Config `yaml:"grpc" json:"grpc"`
+ HTTP HTTPConfig `yaml:"http" json:"http"`
+}
+
+func (c *APIsConfig) Validate() error {
+ if err := c.GRPC.Validate(); err != nil {
+ return fmt.Errorf("grpc.%w", err)
+ }
+ if err := c.HTTP.Validate(); err != nil {
+ return fmt.Errorf("http.%w", err)
+ }
+ return nil
+}
+
+type HTTPConfig struct {
+ Addr string `yaml:"address" json:"address"`
+ Server httpServer.Config `yaml:",inline" json:",inline"`
+}
+
+func (c *HTTPConfig) Validate() error {
+ if _, err := net.ResolveTCPAddr("tcp", c.Addr); err != nil {
+ return fmt.Errorf("address('%v') - %w", c.Addr, err)
+ }
+ return nil
+}
+
+type StorageConfig struct {
+ Embedded storeConfig.Config `yaml:",inline" json:",inline"`
+ ExtendCronParserBySeconds bool `yaml:"-" json:"-"`
+ CleanUpRecords string `yaml:"cleanUpRecords" json:"cleanUpRecords"`
+}
+
+func (c *StorageConfig) Validate() error {
+ if err := c.Embedded.Validate(); err != nil {
+ return err
+ }
+ if c.CleanUpRecords == "" {
+ return nil
+ }
+ s, err := gocron.NewScheduler(gocron.WithLocation(time.Local)) //nolint:gosmopolitan
+ if err != nil {
+ return fmt.Errorf("cannot create cron job: %w", err)
+ }
+ defer func() {
+ if errS := s.Shutdown(); errS != nil {
+ log.Errorf("failed to shutdown cron job: %w", errS)
+ }
+ }()
+ _, err = s.NewJob(gocron.CronJob(c.CleanUpRecords, c.ExtendCronParserBySeconds),
+ gocron.NewTask(func() {
+ // do nothing
+ }))
+ if err != nil {
+ return fmt.Errorf("cleanUpRecords('%v') - %w", c.CleanUpRecords, err)
+ }
+ return nil
+}
+
+type ClientsConfig struct {
+ Storage StorageConfig `yaml:"storage" json:"storage"`
+ OpenTelemetryCollector otelClient.Config `yaml:"openTelemetryCollector" json:"openTelemetryCollector"`
+}
+
+func (c *ClientsConfig) Validate() error {
+ if err := c.Storage.Validate(); err != nil {
+ return fmt.Errorf("storage.%w", err)
+ }
+ if err := c.OpenTelemetryCollector.Validate(); err != nil {
+ return fmt.Errorf("openTelemetryCollector.%w", err)
+ }
+ return nil
+}
+
+// String return string representation of Config
+func (c Config) String() string {
+ return config.ToString(c)
+}
diff --git a/snippet-service/service/grpc/config.go b/snippet-service/service/grpc/config.go
new file mode 100644
index 000000000..655e8170e
--- /dev/null
+++ b/snippet-service/service/grpc/config.go
@@ -0,0 +1,7 @@
+package grpc
+
+import (
+ "github.com/plgd-dev/hub/v2/pkg/net/grpc/server"
+)
+
+type Config = server.Config
diff --git a/snippet-service/service/grpc/server.go b/snippet-service/service/grpc/server.go
new file mode 100644
index 000000000..aff5ba003
--- /dev/null
+++ b/snippet-service/service/grpc/server.go
@@ -0,0 +1,121 @@
+package grpc
+
+import (
+ "context"
+ "errors"
+
+ "github.com/plgd-dev/hub/v2/pkg/log"
+ "github.com/plgd-dev/hub/v2/pkg/net/grpc"
+ "github.com/plgd-dev/hub/v2/snippet-service/pb"
+ "github.com/plgd-dev/hub/v2/snippet-service/store"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
+)
+
+// SnippetServiceServer handles incoming requests.
+type SnippetServiceServer struct {
+ pb.UnimplementedSnippetServiceServer
+
+ logger log.Logger
+ ownerClaim string
+ store store.Store
+ hubID string
+}
+
+func NewSnippetServiceServer(ownerClaim string, hubID string, store store.Store, logger log.Logger) (*SnippetServiceServer, error) {
+ s := &SnippetServiceServer{
+ logger: logger,
+ ownerClaim: ownerClaim,
+ store: store,
+ hubID: hubID,
+ }
+
+ return s, nil
+}
+
+func (s *SnippetServiceServer) checkOwner(ctx context.Context, owner string) (string, error) {
+ ownerFromToken, err := grpc.OwnerFromTokenMD(ctx, s.ownerClaim)
+ if err != nil {
+ return "", err
+ }
+ if owner != "" && ownerFromToken != owner {
+ return "", errors.New("owner mismatch")
+ }
+ return ownerFromToken, nil
+}
+
+func (s *SnippetServiceServer) CreateConfiguration(ctx context.Context, conf *pb.Configuration) (*pb.Configuration, error) {
+ owner, err := s.checkOwner(ctx, conf.GetOwner())
+ if err != nil {
+ return nil, s.logger.LogAndReturnError(status.Errorf(codes.PermissionDenied, "cannot create configuration: %v", err))
+ }
+
+ conf.Owner = owner
+ c, err := s.store.CreateConfiguration(ctx, conf)
+ if err != nil {
+ return nil, s.logger.LogAndReturnError(status.Errorf(codes.Internal, "cannot create configuration: %v", err))
+ }
+ return c, nil
+}
+
+func (s *SnippetServiceServer) UpdateConfiguration(ctx context.Context, conf *pb.Configuration) (*pb.Configuration, error) {
+ owner, err := s.checkOwner(ctx, conf.GetOwner())
+ if err != nil {
+ return nil, s.logger.LogAndReturnError(status.Errorf(codes.PermissionDenied, "cannot update configuration: %v", err))
+ }
+
+ conf.Owner = owner
+ c, err := s.store.UpdateConfiguration(ctx, conf)
+ if err != nil {
+ return nil, s.logger.LogAndReturnError(status.Errorf(codes.Internal, "cannot update configuration: %v", err))
+ }
+ return c, nil
+}
+
+func (s *SnippetServiceServer) GetConfigurations(req *pb.GetConfigurationsRequest, srv pb.SnippetService_GetConfigurationsServer) error {
+ owner, err := s.checkOwner(srv.Context(), "")
+ if err != nil {
+ return s.logger.LogAndReturnError(status.Errorf(codes.PermissionDenied, "cannot get configurations: %v", err))
+ }
+
+ err = s.store.GetConfigurations(srv.Context(), owner, req, func(ctx context.Context, iter store.Iterator[store.Configuration]) error {
+ storedCfg := store.Configuration{}
+ for iter.Next(ctx, &storedCfg) {
+ for _, version := range storedCfg.Versions {
+ errS := srv.Send(&pb.Configuration{
+ Id: storedCfg.Id,
+ Owner: storedCfg.Owner,
+ Name: storedCfg.Name,
+ Version: version.Version,
+ Resources: version.Resources,
+ })
+ if errS != nil {
+ return errS
+ }
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ return s.logger.LogAndReturnError(status.Errorf(codes.Internal, "cannot get configurations: %v", err))
+ }
+ return nil
+}
+
+func (s *SnippetServiceServer) DeleteConfigurations(ctx context.Context, req *pb.DeleteConfigurationsRequest) (*pb.DeleteConfigurationsResponse, error) {
+ owner, err := s.checkOwner(ctx, "")
+ if err != nil {
+ return nil, s.logger.LogAndReturnError(status.Errorf(codes.PermissionDenied, "cannot delete configurations: %v", err))
+ }
+ count, err := s.store.DeleteConfigurations(ctx, owner, req)
+ if err != nil {
+ return nil, s.logger.LogAndReturnError(status.Errorf(codes.Internal, "cannot delete configurations: %v", err))
+ }
+ return &pb.DeleteConfigurationsResponse{
+ Count: count,
+ }, nil
+}
+
+func (s *SnippetServiceServer) Close(ctx context.Context) error {
+ return s.store.Close(ctx)
+}
diff --git a/snippet-service/service/grpc/service.go b/snippet-service/service/grpc/service.go
new file mode 100644
index 000000000..0ee12344e
--- /dev/null
+++ b/snippet-service/service/grpc/service.go
@@ -0,0 +1,35 @@
+package grpc
+
+import (
+ "fmt"
+
+ "github.com/plgd-dev/hub/v2/pkg/fsnotify"
+ "github.com/plgd-dev/hub/v2/pkg/log"
+ "github.com/plgd-dev/hub/v2/pkg/net/grpc/server"
+ "github.com/plgd-dev/hub/v2/pkg/security/jwt/validator"
+ "github.com/plgd-dev/hub/v2/snippet-service/pb"
+ "go.opentelemetry.io/otel/trace"
+)
+
+type Service struct {
+ *server.Server
+}
+
+func New(config Config, snippetServiceServer *SnippetServiceServer, validator *validator.Validator, fileWatcher *fsnotify.Watcher, logger log.Logger, tracerProvider trace.TracerProvider) (*Service, error) {
+ opts, err := server.MakeDefaultOptions(server.NewAuth(validator), logger, tracerProvider)
+ if err != nil {
+ return nil, fmt.Errorf("cannot create grpc server options: %w", err)
+ }
+ server, err := server.New(config, fileWatcher, logger, opts...)
+ if err != nil {
+ return nil, err
+ }
+ pb.RegisterSnippetServiceServer(server.Server, snippetServiceServer)
+
+ // SnippetService needs to stop gracefully to ensure that all commands are processed.
+ server.SetGracefulStop(true)
+
+ return &Service{
+ Server: server,
+ }, nil
+}
diff --git a/snippet-service/service/http/config.go b/snippet-service/service/http/config.go
new file mode 100644
index 000000000..5ae74d064
--- /dev/null
+++ b/snippet-service/service/http/config.go
@@ -0,0 +1,13 @@
+package http
+
+import (
+ "github.com/plgd-dev/hub/v2/pkg/net/http/server"
+ "github.com/plgd-dev/hub/v2/pkg/net/listener"
+ "github.com/plgd-dev/hub/v2/pkg/security/jwt/validator"
+)
+
+type Config struct {
+ Connection listener.Config `yaml:",inline" json:",inline"`
+ Authorization validator.Config `yaml:"authorization" json:"authorization"`
+ Server server.Config `yaml:",inline" json:",inline"`
+}
diff --git a/snippet-service/service/http/createConfiguration_test.go b/snippet-service/service/http/createConfiguration_test.go
new file mode 100644
index 000000000..76de8fdad
--- /dev/null
+++ b/snippet-service/service/http/createConfiguration_test.go
@@ -0,0 +1,204 @@
+package http_test
+
+import (
+ "bytes"
+ "context"
+ "net/http"
+ "testing"
+
+ "github.com/google/uuid"
+ "github.com/plgd-dev/go-coap/v3/message"
+ "github.com/plgd-dev/hub/v2/grpc-gateway/pb"
+ pkgHttp "github.com/plgd-dev/hub/v2/pkg/net/http"
+ "github.com/plgd-dev/hub/v2/resource-aggregate/commands"
+ snippetPb "github.com/plgd-dev/hub/v2/snippet-service/pb"
+ snippetHttp "github.com/plgd-dev/hub/v2/snippet-service/service/http"
+ snippetTest "github.com/plgd-dev/hub/v2/snippet-service/test"
+ "github.com/plgd-dev/hub/v2/test"
+ "github.com/plgd-dev/hub/v2/test/config"
+ httpTest "github.com/plgd-dev/hub/v2/test/http"
+ oauthService "github.com/plgd-dev/hub/v2/test/oauth-server/service"
+ oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test"
+ "github.com/plgd-dev/hub/v2/test/service"
+ "github.com/stretchr/testify/require"
+)
+
+func makeTestResource(t *testing.T, href string, power int) *snippetPb.Configuration_Resource {
+ return &snippetPb.Configuration_Resource{
+ Href: href,
+ Content: &commands.Content{
+ Data: test.EncodeToCbor(t, map[string]interface{}{"power": power}),
+ ContentType: message.AppOcfCbor.String(),
+ CoapContentFormat: int32(message.AppOcfCbor),
+ },
+ TimeToLive: 60,
+ }
+}
+
+func TestRequestHandlerCreateConfiguration(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), config.TEST_TIMEOUT)
+ defer cancel()
+
+ shutDown := service.SetUpServices(ctx, t, service.SetUpServicesOAuth)
+ defer shutDown()
+
+ snippetCfg := snippetTest.MakeConfig(t)
+ shutdownHttp := snippetTest.New(t, snippetCfg)
+ defer shutdownHttp()
+
+ token := oauthTest.GetDefaultAccessToken(t)
+ confID1 := uuid.NewString()
+
+ type args struct {
+ accept string
+ conf *snippetPb.Configuration
+ token string
+ }
+ tests := []struct {
+ name string
+ args args
+ wantHTTPCode int
+ wantErr bool
+ }{
+ {
+ name: "create",
+ args: args{
+ accept: pkgHttp.ApplicationProtoJsonContentType,
+ conf: &snippetPb.Configuration{
+ Id: confID1,
+ Name: "first",
+ Resources: []*snippetPb.Configuration_Resource{
+ makeTestResource(t, "/test/1", 41),
+ },
+ },
+ token: token,
+ },
+ wantHTTPCode: http.StatusOK,
+ },
+ {
+ name: "create (with owner)",
+ args: args{
+ accept: pkgHttp.ApplicationProtoJsonContentType,
+ conf: &snippetPb.Configuration{
+ Id: uuid.New().String(),
+ Owner: oauthService.DeviceUserID,
+ Name: "second",
+ Resources: []*snippetPb.Configuration_Resource{
+ makeTestResource(t, "/test/2", 42),
+ },
+ },
+ token: token,
+ },
+ wantHTTPCode: http.StatusOK,
+ },
+ {
+ name: "non-matching owner",
+ args: args{
+ accept: pkgHttp.ApplicationProtoJsonContentType,
+ conf: &snippetPb.Configuration{
+ Id: uuid.New().String(),
+ Owner: "non-matching-owner",
+ Name: "third",
+ Resources: []*snippetPb.Configuration_Resource{
+ makeTestResource(t, "/test/3", 43),
+ },
+ },
+ token: token,
+ },
+ wantHTTPCode: http.StatusForbidden,
+ wantErr: true,
+ },
+ {
+ name: "missing id",
+ args: args{
+ accept: pkgHttp.ApplicationProtoJsonContentType,
+ conf: &snippetPb.Configuration{
+ Name: "fourth",
+ Resources: []*snippetPb.Configuration_Resource{
+ makeTestResource(t, "/test/4", 44),
+ },
+ },
+ token: token,
+ },
+ wantHTTPCode: http.StatusInternalServerError,
+ wantErr: true,
+ },
+ {
+ name: "duplicit ID",
+ args: args{
+ accept: pkgHttp.ApplicationProtoJsonContentType,
+ conf: &snippetPb.Configuration{
+ Id: confID1,
+ Name: "fifth",
+ Resources: []*snippetPb.Configuration_Resource{
+ makeTestResource(t, "/test/5", 45),
+ },
+ },
+ token: token,
+ },
+ wantHTTPCode: http.StatusInternalServerError,
+ wantErr: true,
+ },
+ {
+ name: "missing resources",
+ args: args{
+ accept: pkgHttp.ApplicationProtoJsonContentType,
+ conf: &snippetPb.Configuration{
+ Id: uuid.New().String(),
+ Name: "fifth",
+ },
+ token: token,
+ },
+ wantHTTPCode: http.StatusInternalServerError,
+ wantErr: true,
+ },
+ {
+ name: "missing owner in token",
+ args: args{
+ accept: pkgHttp.ApplicationProtoJsonContentType,
+ conf: &snippetPb.Configuration{
+ Id: uuid.New().String(),
+ Name: "sixth",
+ Resources: []*snippetPb.Configuration_Resource{
+ makeTestResource(t, "/test/6", 46),
+ },
+ },
+ token: oauthTest.GetAccessToken(t, config.OAUTH_SERVER_HOST, oauthTest.ClientTest, map[string]interface{}{
+ snippetCfg.APIs.GRPC.Authorization.OwnerClaim: nil,
+ }),
+ },
+ wantHTTPCode: http.StatusForbidden,
+ wantErr: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ data, err := httpTest.GetContentData(&pb.Content{
+ ContentType: message.AppOcfCbor.String(),
+ Data: test.EncodeToCbor(t, tt.args.conf),
+ }, message.AppJSON.String())
+ require.NoError(t, err)
+
+ rb := httpTest.NewRequest(http.MethodPost, snippetTest.HTTPURI(snippetHttp.Configurations), bytes.NewReader(data)).AuthToken(tt.args.token)
+ rb.Accept(tt.args.accept).ContentType(message.AppJSON.String())
+ resp := httpTest.Do(t, rb.Build(ctx, t))
+ defer func() {
+ _ = resp.Body.Close()
+ }()
+ require.Equal(t, tt.wantHTTPCode, resp.StatusCode)
+
+ var got snippetPb.Configuration
+ err = httpTest.Unmarshal(resp.StatusCode, resp.Body, &got)
+ if tt.wantErr {
+ require.Error(t, err)
+ return
+ }
+ require.NoError(t, err)
+
+ want := tt.args.conf
+ want.Owner = oauthService.DeviceUserID
+ snippetTest.CmpConfiguration(t, want, &got)
+ })
+ }
+}
diff --git a/snippet-service/service/http/deleteConfiguration_test.go b/snippet-service/service/http/deleteConfiguration_test.go
new file mode 100644
index 000000000..0959ee24e
--- /dev/null
+++ b/snippet-service/service/http/deleteConfiguration_test.go
@@ -0,0 +1,115 @@
+package http_test
+
+import (
+ "context"
+ "crypto/tls"
+ "errors"
+ "io"
+ "net/http"
+ "testing"
+
+ "github.com/plgd-dev/go-coap/v3/message"
+ pkgHttp "github.com/plgd-dev/hub/v2/pkg/net/http"
+ "github.com/plgd-dev/hub/v2/snippet-service/pb"
+ snippetHttp "github.com/plgd-dev/hub/v2/snippet-service/service/http"
+ "github.com/plgd-dev/hub/v2/snippet-service/test"
+ hubTest "github.com/plgd-dev/hub/v2/test"
+ "github.com/plgd-dev/hub/v2/test/config"
+ httpTest "github.com/plgd-dev/hub/v2/test/http"
+ oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test"
+ "github.com/plgd-dev/hub/v2/test/service"
+ "github.com/stretchr/testify/require"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/credentials"
+)
+
+func TestRequestHandlerDeleteConfigurations(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), config.TEST_TIMEOUT)
+ defer cancel()
+
+ shutDown := service.SetUpServices(context.Background(), t, service.SetUpServicesOAuth)
+ defer shutDown()
+
+ snippetCfg := test.MakeConfig(t)
+ shutdownHttp := test.New(t, snippetCfg)
+ defer shutdownHttp()
+
+ conn, err := grpc.NewClient(config.SNIPPET_SERVICE_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
+ RootCAs: hubTest.GetRootCertificatePool(t),
+ })))
+ require.NoError(t, err)
+ defer func() {
+ _ = conn.Close()
+ }()
+ c := pb.NewSnippetServiceClient(conn)
+ /*confs :*/ _ = test.AddConfigurations(ctx, t, snippetCfg.APIs.GRPC.Authorization.OwnerClaim, c, 30, nil)
+
+ type args struct {
+ accept string
+ token string
+ }
+ tests := []struct {
+ name string
+ args args
+ wantHTTPCode int
+ wantErr bool
+ want func(*testing.T)
+ }{
+ {
+ name: "missing owner",
+ args: args{
+ accept: pkgHttp.ApplicationProtoJsonContentType,
+ token: oauthTest.GetAccessToken(t, config.OAUTH_SERVER_HOST, oauthTest.ClientTest, map[string]interface{}{
+ snippetCfg.APIs.GRPC.Authorization.OwnerClaim: nil,
+ }),
+ },
+ wantHTTPCode: http.StatusForbidden,
+ wantErr: true,
+ },
+ {
+ name: "owner1/all",
+ args: args{
+ accept: pkgHttp.ApplicationProtoJsonContentType,
+ token: oauthTest.GetAccessToken(t, config.OAUTH_SERVER_HOST, oauthTest.ClientTest, map[string]interface{}{
+ snippetCfg.APIs.GRPC.Authorization.OwnerClaim: test.ConfigurationOwner(1),
+ }),
+ },
+ wantHTTPCode: http.StatusOK,
+ want: func(t *testing.T) {
+ getClient, errG := c.GetConfigurations(ctx, &pb.GetConfigurationsRequest{})
+ require.NoError(t, errG)
+ defer func() {
+ _ = getClient.CloseSend()
+ }()
+ for {
+ conf, errR := getClient.Recv()
+ if errors.Is(errR, io.EOF) {
+ break
+ }
+ require.NoError(t, errR)
+ require.Equal(t, test.ConfigurationOwner(1), conf.GetOwner())
+ }
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ rb := httpTest.NewRequest(http.MethodDelete, test.HTTPURI(snippetHttp.Configurations), nil).AuthToken(tt.args.token)
+ rb = rb.Accept(tt.args.accept).ContentType(message.AppCBOR.String())
+ resp := httpTest.Do(t, rb.Build(ctx, t))
+ defer func() {
+ _ = resp.Body.Close()
+ }()
+ require.Equal(t, tt.wantHTTPCode, resp.StatusCode)
+
+ var deleteResp pb.DeleteConfigurationsResponse
+ err = httpTest.Unmarshal(resp.StatusCode, resp.Body, &deleteResp)
+ if tt.wantErr {
+ require.Error(t, err)
+ return
+ }
+ require.NoError(t, err)
+ })
+ }
+}
diff --git a/snippet-service/service/http/getConfigurations_test.go b/snippet-service/service/http/getConfigurations_test.go
new file mode 100644
index 000000000..c9a0e763a
--- /dev/null
+++ b/snippet-service/service/http/getConfigurations_test.go
@@ -0,0 +1,137 @@
+package http_test
+
+import (
+ "context"
+ "crypto/tls"
+ "errors"
+ "io"
+ "net/http"
+ "testing"
+
+ "github.com/plgd-dev/go-coap/v3/message"
+ pkgHttp "github.com/plgd-dev/hub/v2/pkg/net/http"
+ "github.com/plgd-dev/hub/v2/snippet-service/pb"
+ snippetHttp "github.com/plgd-dev/hub/v2/snippet-service/service/http"
+ "github.com/plgd-dev/hub/v2/snippet-service/test"
+ hubTest "github.com/plgd-dev/hub/v2/test"
+ "github.com/plgd-dev/hub/v2/test/config"
+ httpTest "github.com/plgd-dev/hub/v2/test/http"
+ oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test"
+ "github.com/plgd-dev/hub/v2/test/service"
+ "github.com/stretchr/testify/require"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/credentials"
+)
+
+func TestRequestHandlerGetConfigurations(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), config.TEST_TIMEOUT)
+ defer cancel()
+
+ shutDown := service.SetUpServices(context.Background(), t, service.SetUpServicesOAuth)
+ defer shutDown()
+
+ snippetCfg := test.MakeConfig(t)
+ shutdownHttp := test.New(t, snippetCfg)
+ defer shutdownHttp()
+
+ conn, err := grpc.NewClient(config.SNIPPET_SERVICE_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
+ RootCAs: hubTest.GetRootCertificatePool(t),
+ })))
+ require.NoError(t, err)
+ defer func() {
+ _ = conn.Close()
+ }()
+ c := pb.NewSnippetServiceClient(conn)
+ confs := test.AddConfigurations(ctx, t, snippetCfg.APIs.GRPC.Authorization.OwnerClaim, c, 30, nil)
+
+ type args struct {
+ accept string
+ token string
+ uri string
+ id string
+ version string
+ }
+ tests := []struct {
+ name string
+ args args
+ wantHTTPCode int
+ wantErr bool
+ want func(*testing.T, []*pb.Configuration)
+ }{
+ {
+ name: "missing owner",
+ args: args{
+ accept: pkgHttp.ApplicationProtoJsonContentType,
+ token: oauthTest.GetAccessToken(t, config.OAUTH_SERVER_HOST, oauthTest.ClientTest, map[string]interface{}{
+ snippetCfg.APIs.GRPC.Authorization.OwnerClaim: nil,
+ }),
+ uri: test.HTTPURI(snippetHttp.Configurations),
+ },
+ wantHTTPCode: http.StatusForbidden,
+ wantErr: true,
+ },
+ {
+ name: "owner1/all",
+ args: args{
+ accept: pkgHttp.ApplicationProtoJsonContentType,
+ token: oauthTest.GetAccessToken(t, config.OAUTH_SERVER_HOST, oauthTest.ClientTest, map[string]interface{}{
+ snippetCfg.APIs.GRPC.Authorization.OwnerClaim: test.ConfigurationOwner(1),
+ }),
+ uri: test.HTTPURI(snippetHttp.Configurations),
+ },
+ wantHTTPCode: http.StatusOK,
+ want: func(t *testing.T, values []*pb.Configuration) {
+ require.NotEmpty(t, values)
+ for _, v := range values {
+ conf, ok := confs[v.GetId()]
+ require.True(t, ok)
+ test.ConfigurationContains(t, conf, v)
+ }
+ },
+ },
+ // {
+ // name: "owner0/id0?version=all",
+ // args: args{
+ // accept: pkgHttp.ApplicationProtoJsonContentType,
+ // token: oauthTest.GetAccessToken(t, config.OAUTH_SERVER_HOST, oauthTest.ClientTest, map[string]interface{}{
+ // snippetCfg.APIs.GRPC.Authorization.OwnerClaim: test.ConfigurationOwner(0),
+ // }),
+ // uri: test.HTTPURI(snippetHttp.AliasConfigurations),
+ // id: test.ConfigurationID(0),
+ // version: "all",
+ // },
+ // wantHTTPCode: http.StatusOK,
+ // want: func(t *testing.T, values []*pb.Configuration) {
+ // require.NotEmpty(t, values)
+ // },
+ // },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ rb := httpTest.NewRequest(http.MethodGet, tt.args.uri, nil).AuthToken(tt.args.token)
+ rb = rb.Accept(tt.args.accept).ContentType(message.AppCBOR.String()).ID(tt.args.id).Version(tt.args.version)
+ resp := httpTest.Do(t, rb.Build(ctx, t))
+ defer func() {
+ _ = resp.Body.Close()
+ }()
+ require.Equal(t, tt.wantHTTPCode, resp.StatusCode)
+
+ values := make([]*pb.Configuration, 0, 1)
+ for {
+ var value pb.Configuration
+ err = httpTest.Unmarshal(resp.StatusCode, resp.Body, &value)
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ if tt.wantErr {
+ require.Error(t, err)
+ return
+ }
+ require.NoError(t, err)
+ values = append(values, &value)
+ }
+ tt.want(t, values)
+ })
+ }
+}
diff --git a/snippet-service/service/http/requestHandler.go b/snippet-service/service/http/requestHandler.go
new file mode 100644
index 000000000..a5e284e0c
--- /dev/null
+++ b/snippet-service/service/http/requestHandler.go
@@ -0,0 +1,78 @@
+package http
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/fullstorydev/grpchan/inprocgrpc"
+ "github.com/gorilla/mux"
+ "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
+ "github.com/plgd-dev/hub/v2/http-gateway/serverMux"
+ "github.com/plgd-dev/hub/v2/snippet-service/pb"
+ grpcService "github.com/plgd-dev/hub/v2/snippet-service/service/grpc"
+)
+
+// RequestHandler for handling incoming request
+type RequestHandler struct {
+ config *Config
+ mux *runtime.ServeMux
+}
+
+/*
+// TODO: the GRPC query parser doesn't seem to support oneOf fields, so we have to manually encode and decode the query
+func (requestHandler *RequestHandler) getConfigurationVersion(w http.ResponseWriter, r *http.Request) {
+ // /api/v1/configuration/{id}?version=latest -> rpc GetConfigurations + IDFilter{IDFilter_Latest}
+ // /api/v1/configuration/{id}?version=all -> rpc GetConfigurations + IDFilter{IDFilter_All}
+ // /api/v1/configuration/{id}?version={version} -> rpc GetConfigurations + IDFilter{IDFilter_Version{version}}
+ vars := mux.Vars(r)
+ configurationID := vars[IDKey]
+
+ versionStr := r.URL.Query().Get(VersionQueryKey)
+ if versionStr != "" && versionStr != "all" && versionStr == "latest" {
+ var err error
+ _, err = strconv.ParseUint(versionStr, 10, 64)
+ if err != nil {
+ serverMux.WriteError(w, fmt.Errorf("invalid configuration('%v') version: %w", configurationID, err))
+ return
+ }
+ }
+
+ type Options struct {
+ HTTPIDFilter []string `url:"httpIdFilter"`
+ }
+ opt := &Options{
+ HTTPIDFilter: []string{configurationID + "/" + versionStr},
+ }
+ q, err := query.Values(opt)
+ if err != nil {
+ serverMux.WriteError(w, fmt.Errorf("invalid configuration('%v') version: %w", configurationID, err))
+ return
+ }
+
+ r.URL.Path = Configurations
+ r.URL.RawQuery = q.Encode()
+ requestHandler.mux.ServeHTTP(w, r)
+}
+*/
+
+// NewHTTP returns HTTP handler
+func NewRequestHandler(config *Config, r *mux.Router, snippetServiceServer *grpcService.SnippetServiceServer) (*RequestHandler, error) {
+ requestHandler := &RequestHandler{
+ config: config,
+ mux: serverMux.New(),
+ }
+
+ // Aliases
+ // r.HandleFunc(AliasConfigurations, requestHandler.getConfigurationVersion).Methods(http.MethodGet)
+
+ ch := new(inprocgrpc.Channel)
+ pb.RegisterSnippetServiceServer(ch, snippetServiceServer)
+ grpcClient := pb.NewSnippetServiceClient(ch)
+ // register grpc-proxy handler
+ if err := pb.RegisterSnippetServiceHandlerClient(context.Background(), requestHandler.mux, grpcClient); err != nil {
+ return nil, fmt.Errorf("failed to register snippet-service handler: %w", err)
+ }
+ r.PathPrefix("/").Handler(requestHandler.mux)
+
+ return requestHandler, nil
+}
diff --git a/snippet-service/service/http/service.go b/snippet-service/service/http/service.go
new file mode 100644
index 000000000..d9ca71cae
--- /dev/null
+++ b/snippet-service/service/http/service.go
@@ -0,0 +1,48 @@
+package http
+
+import (
+ "fmt"
+
+ "github.com/plgd-dev/hub/v2/http-gateway/uri"
+ "github.com/plgd-dev/hub/v2/pkg/fsnotify"
+ "github.com/plgd-dev/hub/v2/pkg/log"
+ pkgHttp "github.com/plgd-dev/hub/v2/pkg/net/http"
+ httpService "github.com/plgd-dev/hub/v2/pkg/net/http/service"
+ "github.com/plgd-dev/hub/v2/pkg/security/jwt/validator"
+ grpcService "github.com/plgd-dev/hub/v2/snippet-service/service/grpc"
+ "go.opentelemetry.io/otel/trace"
+)
+
+// Service handle HTTP request
+type Service struct {
+ *httpService.Service
+ requestHandler *RequestHandler
+}
+
+// New parses configuration and creates new Server with provided store and bus
+func New(serviceName string, config Config, snippetServiceServer *grpcService.SnippetServiceServer, validator *validator.Validator, fileWatcher *fsnotify.Watcher, logger log.Logger, tracerProvider trace.TracerProvider) (*Service, error) {
+ service, err := httpService.New(httpService.Config{
+ HTTPConnection: config.Connection,
+ HTTPServer: config.Server,
+ ServiceName: serviceName,
+ AuthRules: pkgHttp.NewDefaultAuthorizationRules(uri.API),
+ FileWatcher: fileWatcher,
+ Logger: logger,
+ TraceProvider: tracerProvider,
+ Validator: validator,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("cannot create http service: %w", err)
+ }
+
+ requestHandler, err := NewRequestHandler(&config, service.GetRouter(), snippetServiceServer)
+ if err != nil {
+ _ = service.Close()
+ return nil, err
+ }
+
+ return &Service{
+ Service: service,
+ requestHandler: requestHandler,
+ }, nil
+}
diff --git a/snippet-service/service/http/updateConfiguration_test.go b/snippet-service/service/http/updateConfiguration_test.go
new file mode 100644
index 000000000..2d016acb4
--- /dev/null
+++ b/snippet-service/service/http/updateConfiguration_test.go
@@ -0,0 +1,198 @@
+package http_test
+
+import (
+ "bytes"
+ "context"
+ "crypto/tls"
+ "net/http"
+ "testing"
+
+ "github.com/google/uuid"
+ "github.com/plgd-dev/go-coap/v3/message"
+ "github.com/plgd-dev/hub/v2/grpc-gateway/pb"
+ pkgGrpc "github.com/plgd-dev/hub/v2/pkg/net/grpc"
+ pkgHttp "github.com/plgd-dev/hub/v2/pkg/net/http"
+ snippetPb "github.com/plgd-dev/hub/v2/snippet-service/pb"
+ snippetHttp "github.com/plgd-dev/hub/v2/snippet-service/service/http"
+ snippetTest "github.com/plgd-dev/hub/v2/snippet-service/test"
+ "github.com/plgd-dev/hub/v2/test"
+ "github.com/plgd-dev/hub/v2/test/config"
+ httpTest "github.com/plgd-dev/hub/v2/test/http"
+ oauthService "github.com/plgd-dev/hub/v2/test/oauth-server/service"
+ oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test"
+ "github.com/plgd-dev/hub/v2/test/service"
+ "github.com/stretchr/testify/require"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/credentials"
+)
+
+func TestRequestHandlerUpdateConfiguration(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), config.TEST_TIMEOUT)
+ defer cancel()
+
+ shutDown := service.SetUpServices(context.Background(), t, service.SetUpServicesOAuth)
+ defer shutDown()
+
+ shutdownHttp := snippetTest.SetUp(t)
+ defer shutdownHttp()
+
+ token := oauthTest.GetDefaultAccessToken(t)
+
+ conn, err := grpc.NewClient(config.SNIPPET_SERVICE_HOST, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
+ RootCAs: test.GetRootCertificatePool(t),
+ })))
+ require.NoError(t, err)
+ defer func() {
+ _ = conn.Close()
+ }()
+ c := snippetPb.NewSnippetServiceClient(conn)
+ conf := &snippetPb.Configuration{
+ Id: uuid.NewString(),
+ Version: 0,
+ Name: "configurationToUpdate",
+ Resources: []*snippetPb.Configuration_Resource{
+ makeTestResource(t, "/test/1", 1),
+ },
+ }
+ _, err = c.CreateConfiguration(pkgGrpc.CtxWithToken(ctx, token), conf)
+ require.NoError(t, err)
+
+ type args struct {
+ accept string
+ id string
+ conf *snippetPb.Configuration
+ token string
+ }
+ tests := []struct {
+ name string
+ args args
+ wantHTTPCode int
+ wantErr bool
+ }{
+ {
+ name: "update",
+ args: args{
+ accept: pkgHttp.ApplicationProtoJsonContentType,
+ id: conf.GetId(),
+ conf: &snippetPb.Configuration{
+ Version: 1,
+ Resources: []*snippetPb.Configuration_Resource{
+ makeTestResource(t, "/test/1", 42),
+ makeTestResource(t, "/test/2", 52),
+ },
+ },
+ token: token,
+ },
+ wantHTTPCode: http.StatusOK,
+ },
+ {
+ name: "update (with owner)",
+ args: args{
+ accept: pkgHttp.ApplicationProtoJsonContentType,
+ id: conf.GetId(),
+ conf: &snippetPb.Configuration{
+ Version: 2,
+ Owner: oauthService.DeviceUserID,
+ Resources: []*snippetPb.Configuration_Resource{
+ makeTestResource(t, "/test/3", 62),
+ },
+ },
+ token: token,
+ },
+ wantHTTPCode: http.StatusOK,
+ },
+ {
+ name: "update (with overwritten ID)",
+ args: args{
+ accept: pkgHttp.ApplicationProtoJsonContentType,
+ id: conf.GetId(),
+ conf: &snippetPb.Configuration{
+ Id: uuid.NewString(), // this ID will get overwritten by the ID in the query
+ Version: 3,
+ Resources: []*snippetPb.Configuration_Resource{
+ makeTestResource(t, "/test/4", 72),
+ makeTestResource(t, "/test/5", 82),
+ },
+ },
+ token: token,
+ },
+ wantHTTPCode: http.StatusOK,
+ },
+ {
+ name: "invalid ID",
+ args: args{
+ accept: pkgHttp.ApplicationProtoJsonContentType,
+ id: "invalid",
+ conf: &snippetPb.Configuration{
+ Version: 42,
+ Resources: []*snippetPb.Configuration_Resource{
+ makeTestResource(t, "/test/6", 92),
+ },
+ },
+ token: token,
+ },
+ wantHTTPCode: http.StatusInternalServerError,
+ wantErr: true,
+ },
+ {
+ name: "duplicit version",
+ args: args{
+ accept: pkgHttp.ApplicationProtoJsonContentType,
+ id: conf.GetId(),
+ conf: &snippetPb.Configuration{
+ Version: 1,
+ Resources: []*snippetPb.Configuration_Resource{
+ makeTestResource(t, "/test/7", 102),
+ },
+ },
+ token: token,
+ },
+ wantHTTPCode: http.StatusInternalServerError,
+ wantErr: true,
+ },
+ {
+ name: "missing resources",
+ args: args{
+ accept: pkgHttp.ApplicationProtoJsonContentType,
+ id: conf.GetId(),
+ conf: &snippetPb.Configuration{
+ Version: 42,
+ },
+ token: token,
+ },
+ wantHTTPCode: http.StatusInternalServerError,
+ wantErr: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ data, err := httpTest.GetContentData(&pb.Content{
+ ContentType: message.AppOcfCbor.String(),
+ Data: test.EncodeToCbor(t, tt.args.conf),
+ }, message.AppJSON.String())
+ require.NoError(t, err)
+
+ rb := httpTest.NewRequest(http.MethodPut, snippetTest.HTTPURI(snippetHttp.AliasConfigurations), bytes.NewReader(data)).AuthToken(tt.args.token)
+ rb.Accept(tt.args.accept).ContentType(message.AppJSON.String()).ID(tt.args.id)
+ resp := httpTest.Do(t, rb.Build(ctx, t))
+ defer func() {
+ _ = resp.Body.Close()
+ }()
+ require.Equal(t, tt.wantHTTPCode, resp.StatusCode)
+
+ var got snippetPb.Configuration
+ err = httpTest.Unmarshal(resp.StatusCode, resp.Body, &got)
+ if tt.wantErr {
+ require.Error(t, err)
+ return
+ }
+ require.NoError(t, err)
+
+ want := tt.args.conf
+ want.Id = tt.args.id
+ want.Owner = oauthService.DeviceUserID
+ snippetTest.CmpConfiguration(t, want, &got)
+ })
+ }
+}
diff --git a/snippet-service/service/http/uri.go b/snippet-service/service/http/uri.go
new file mode 100644
index 000000000..1cb4daccb
--- /dev/null
+++ b/snippet-service/service/http/uri.go
@@ -0,0 +1,26 @@
+package http
+
+const (
+ IDKey = "id"
+ ConfigurationIDKey = "configurationId"
+
+ VersionQueryKey = "version"
+
+ API string = "/snippet-service/api/v1"
+
+ // GET /api/v1/conditions -> rpc GetConditions
+ // DELETE /api/v1/conditions -> rpc DeleteConditions
+ // POST /api/v1/conditions -> rpc CreateCondition
+ Conditions = API + "/conditions"
+
+ // GET /api/v1/configurations -> rpc GetConfigurations
+ // DELETE /api/v1/configurations -> rpc DeleteConfigurations
+ // POST /api/v1/configurations -> rpc CreateConfiguration
+ Configurations = API + "/configurations"
+
+ // PUT /api/v1/configurations/{id} -> rpc Update Configuration
+ // GET /api/v1/configuration/{id}?version=latest -> rpc GetConfigurations + IDFilter{IDFilter_Latest}
+ // GET /api/v1/configuration/{id}?version=all -> rpc GetConfigurations + IDFilter{IDFilter_All}
+ // GET /api/v1/configuration/{id}?version={version} -> rpc GetConfigurations + IDFilter{IDFilter_Version{version}}
+ AliasConfigurations = Configurations + "/{" + IDKey + "}"
+)
diff --git a/snippet-service/service/service.go b/snippet-service/service/service.go
new file mode 100644
index 000000000..5549c410d
--- /dev/null
+++ b/snippet-service/service/service.go
@@ -0,0 +1,147 @@
+package service
+
+import (
+ "context"
+ "fmt"
+ "time"
+
+ "github.com/go-co-op/gocron/v2"
+ "github.com/plgd-dev/hub/v2/pkg/config/database"
+ "github.com/plgd-dev/hub/v2/pkg/fn"
+ "github.com/plgd-dev/hub/v2/pkg/fsnotify"
+ "github.com/plgd-dev/hub/v2/pkg/log"
+ "github.com/plgd-dev/hub/v2/pkg/net/listener"
+ otelClient "github.com/plgd-dev/hub/v2/pkg/opentelemetry/collector/client"
+ "github.com/plgd-dev/hub/v2/pkg/security/jwt/validator"
+ "github.com/plgd-dev/hub/v2/pkg/service"
+ grpcService "github.com/plgd-dev/hub/v2/snippet-service/service/grpc"
+ httpService "github.com/plgd-dev/hub/v2/snippet-service/service/http"
+ "github.com/plgd-dev/hub/v2/snippet-service/store"
+ storeConfig "github.com/plgd-dev/hub/v2/snippet-service/store/config"
+ "github.com/plgd-dev/hub/v2/snippet-service/store/cqldb"
+ "github.com/plgd-dev/hub/v2/snippet-service/store/mongodb"
+ "go.opentelemetry.io/otel/trace"
+)
+
+const serviceName = "snippet-service"
+
+func createStore(ctx context.Context, config storeConfig.Config, fileWatcher *fsnotify.Watcher, logger log.Logger, tracerProvider trace.TracerProvider) (store.Store, error) {
+ switch config.Use {
+ case database.MongoDB:
+ s, err := mongodb.New(ctx, config.MongoDB, fileWatcher, logger, tracerProvider)
+ if err != nil {
+ return nil, fmt.Errorf("mongodb: %w", err)
+ }
+ return s, nil
+ case database.CqlDB:
+ s, err := cqldb.New(ctx, config.CqlDB, fileWatcher, logger, tracerProvider)
+ if err != nil {
+ return nil, fmt.Errorf("cqldb: %w", err)
+ }
+ return s, nil
+ }
+ return nil, fmt.Errorf("invalid store use('%v')", config.Use)
+}
+
+func newStore(ctx context.Context, config StorageConfig, fileWatcher *fsnotify.Watcher, logger log.Logger, tracerProvider trace.TracerProvider) (store.Store, func(), error) {
+ var fl fn.FuncList
+ db, err := createStore(ctx, config.Embedded, fileWatcher, logger, tracerProvider)
+ if err != nil {
+ fl.Execute()
+ return nil, nil, err
+ }
+ fl.AddFunc(func() {
+ if errC := db.Close(ctx); errC != nil {
+ log.Errorf("failed to close mongodb store: %w", errC)
+ }
+ })
+ if config.CleanUpRecords == "" {
+ return db, fl.ToFunction(), nil
+ }
+ s, err := gocron.NewScheduler(gocron.WithLocation(time.Local)) //nolint:gosmopolitan
+ if err != nil {
+ fl.Execute()
+ return nil, nil, fmt.Errorf("cannot create cron job: %w", err)
+ }
+ _, err = s.NewJob(gocron.CronJob(config.CleanUpRecords, config.ExtendCronParserBySeconds), gocron.NewTask(func() {
+ /*
+ _, errDel := db.DeleteNonDeviceExpiredRecords(ctx, time.Now())
+ if errDel != nil && !errors.Is(errDel, store.ErrNotSupported) {
+ log.Errorf("failed to delete expired signing records: %w", errDel)
+ }
+ */
+ }))
+ if err != nil {
+ fl.Execute()
+ return nil, nil, fmt.Errorf("cannot create cron job: %w", err)
+ }
+ fl.AddFunc(func() {
+ if errS := s.Shutdown(); errS != nil {
+ log.Errorf("failed to shutdown cron job: %w", errS)
+ }
+ })
+ s.Start()
+ return db, fl.ToFunction(), nil
+}
+
+func New(ctx context.Context, config Config, fileWatcher *fsnotify.Watcher, logger log.Logger) (*service.Service, error) {
+ otelClient, err := otelClient.New(ctx, config.Clients.OpenTelemetryCollector, serviceName, fileWatcher, logger)
+ if err != nil {
+ return nil, fmt.Errorf("cannot create open telemetry collector client: %w", err)
+ }
+ var closerFn fn.FuncList
+ closerFn.AddFunc(otelClient.Close)
+ tracerProvider := otelClient.GetTracerProvider()
+
+ dbStorage, closeStore, err := newStore(ctx, config.Clients.Storage, fileWatcher, logger, tracerProvider)
+ if err != nil {
+ closerFn.Execute()
+ return nil, fmt.Errorf("cannot create store: %w", err)
+ }
+ closerFn.AddFunc(closeStore)
+
+ ca, err := grpcService.NewSnippetServiceServer(config.APIs.GRPC.Authorization.OwnerClaim, config.HubID, dbStorage, logger)
+ if err != nil {
+ closerFn.Execute()
+ return nil, fmt.Errorf("cannot create grpc %s server: %w", serviceName, err)
+ }
+ closerFn.AddFunc(func() {
+ errC := ca.Close(ctx)
+ if errC != nil {
+ log.Errorf("failed to close grpc %s server: %w", serviceName, errC)
+ }
+ })
+ httpValidator, err := validator.New(ctx, config.APIs.GRPC.Authorization.Config, fileWatcher, logger, tracerProvider)
+ if err != nil {
+ closerFn.Execute()
+ return nil, fmt.Errorf("cannot create http validator: %w", err)
+ }
+ closerFn.AddFunc(httpValidator.Close)
+ httpService, err := httpService.New(serviceName, httpService.Config{
+ Connection: listener.Config{
+ Addr: config.APIs.HTTP.Addr,
+ TLS: config.APIs.GRPC.TLS,
+ },
+ Authorization: config.APIs.GRPC.Authorization.Config,
+ Server: config.APIs.HTTP.Server,
+ }, ca, httpValidator, fileWatcher, logger, tracerProvider)
+ if err != nil {
+ closerFn.Execute()
+ return nil, fmt.Errorf("cannot create http service: %w", err)
+ }
+ grpcValidator, err := validator.New(ctx, config.APIs.GRPC.Authorization.Config, fileWatcher, logger, tracerProvider)
+ if err != nil {
+ closerFn.Execute()
+ _ = httpService.Close()
+ return nil, fmt.Errorf("cannot create grpc validator: %w", err)
+ }
+ grpcService, err := grpcService.New(config.APIs.GRPC, ca, grpcValidator, fileWatcher, logger, tracerProvider)
+ if err != nil {
+ closerFn.Execute()
+ _ = httpService.Close()
+ return nil, fmt.Errorf("cannot create grpc service: %w", err)
+ }
+ s := service.New(httpService, grpcService)
+ s.AddCloseFunc(closerFn.Execute)
+ return s, nil
+}
diff --git a/snippet-service/service/service_test.go b/snippet-service/service/service_test.go
new file mode 100644
index 000000000..367b6fe22
--- /dev/null
+++ b/snippet-service/service/service_test.go
@@ -0,0 +1,38 @@
+// ************************************************************************
+// Copyright (C) 2022 plgd.dev, s.r.o.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ************************************************************************
+
+package service_test
+
+import (
+ "context"
+ "fmt"
+ "testing"
+
+ "github.com/plgd-dev/hub/v2/snippet-service/test"
+ testService "github.com/plgd-dev/hub/v2/test/service"
+ "github.com/stretchr/testify/require"
+)
+
+func TestServiceServe(t *testing.T) {
+ fmt.Printf("%v\n\n", test.MakeConfig(t))
+
+ shutDown := testService.SetUpServices(context.Background(), t, testService.SetUpServicesCertificateAuthority|testService.SetUpServicesOAuth)
+ defer shutDown()
+
+ cfg := test.MakeConfig(t)
+ cfg.Clients.Storage.CleanUpRecords = "/2 * * * *"
+ require.Error(t, cfg.Validate())
+}
diff --git a/snippet-service/store/condition.go b/snippet-service/store/condition.go
new file mode 100644
index 000000000..a4d71103e
--- /dev/null
+++ b/snippet-service/store/condition.go
@@ -0,0 +1,7 @@
+package store
+
+import (
+ "github.com/plgd-dev/hub/v2/snippet-service/pb"
+)
+
+type Condition = pb.Condition
diff --git a/snippet-service/store/config/config.go b/snippet-service/store/config/config.go
new file mode 100644
index 000000000..20220af87
--- /dev/null
+++ b/snippet-service/store/config/config.go
@@ -0,0 +1,9 @@
+package config
+
+import (
+ "github.com/plgd-dev/hub/v2/pkg/config/database"
+ "github.com/plgd-dev/hub/v2/snippet-service/store/cqldb"
+ "github.com/plgd-dev/hub/v2/snippet-service/store/mongodb"
+)
+
+type Config = database.Config[*mongodb.Config, *cqldb.Config]
diff --git a/snippet-service/store/configuration.go b/snippet-service/store/configuration.go
new file mode 100644
index 000000000..8d6b2f781
--- /dev/null
+++ b/snippet-service/store/configuration.go
@@ -0,0 +1,41 @@
+package store
+
+import (
+ "slices"
+
+ "github.com/plgd-dev/hub/v2/snippet-service/pb"
+)
+
+const (
+ VersionsKey = "versions" // must match with Versions field tag
+)
+
+type ConfigurationVersion struct {
+ Version uint64 `bson:"version"`
+ Resources []*pb.Configuration_Resource `bson:"resources"`
+}
+
+type Configuration struct {
+ Id string `bson:"_id"`
+ Name string `bson:"name,omitempty"`
+ Owner string `bson:"owner"`
+ Versions []ConfigurationVersion `bson:"versions,omitempty"`
+}
+
+func MakeConfiguration(c *pb.Configuration) Configuration {
+ return Configuration{
+ Id: c.GetId(),
+ Name: c.GetName(),
+ Owner: c.GetOwner(),
+ Versions: []ConfigurationVersion{{Version: c.GetVersion(), Resources: c.GetResources()}},
+ }
+}
+
+func (c *Configuration) Clone() *Configuration {
+ return &Configuration{
+ Id: c.Id,
+ Name: c.Name,
+ Owner: c.Owner,
+ Versions: slices.Clone(c.Versions),
+ }
+}
diff --git a/snippet-service/store/cqldb/config.go b/snippet-service/store/cqldb/config.go
new file mode 100644
index 000000000..9ef1fcdd7
--- /dev/null
+++ b/snippet-service/store/cqldb/config.go
@@ -0,0 +1,15 @@
+package cqldb
+
+import (
+ "github.com/plgd-dev/hub/v2/pkg/cqldb"
+)
+
+// Config provides Mongo DB configuration options
+type Config struct {
+ Embedded cqldb.Config `yaml:",inline" json:",inline"`
+ Table string `yaml:"table" json:"table"`
+}
+
+func (c *Config) Validate() error {
+ return c.Embedded.Validate()
+}
diff --git a/snippet-service/store/cqldb/configuration.go b/snippet-service/store/cqldb/configuration.go
new file mode 100644
index 000000000..ecb08bf68
--- /dev/null
+++ b/snippet-service/store/cqldb/configuration.go
@@ -0,0 +1,24 @@
+package cqldb
+
+import (
+ "context"
+
+ "github.com/plgd-dev/hub/v2/snippet-service/pb"
+ "github.com/plgd-dev/hub/v2/snippet-service/store"
+)
+
+func (s *Store) CreateConfiguration(context.Context, *pb.Configuration) (*pb.Configuration, error) {
+ return nil, store.ErrNotSupported
+}
+
+func (s *Store) DeleteConfigurations(context.Context, string, *pb.DeleteConfigurationsRequest) (int64, error) {
+ return 0, store.ErrNotSupported
+}
+
+func (s *Store) GetConfigurations(context.Context, string, *pb.GetConfigurationsRequest, store.GetConfigurationsFunc) error {
+ return store.ErrNotSupported
+}
+
+func (s *Store) UpdateConfiguration(context.Context, *pb.Configuration) (*pb.Configuration, error) {
+ return nil, store.ErrNotSupported
+}
diff --git a/snippet-service/store/cqldb/store.go b/snippet-service/store/cqldb/store.go
new file mode 100644
index 000000000..01d6fa75c
--- /dev/null
+++ b/snippet-service/store/cqldb/store.go
@@ -0,0 +1,95 @@
+package cqldb
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "strings"
+
+ "github.com/plgd-dev/hub/v2/pkg/cqldb"
+ "github.com/plgd-dev/hub/v2/pkg/fsnotify"
+ "github.com/plgd-dev/hub/v2/pkg/log"
+ "github.com/plgd-dev/hub/v2/pkg/security/certManager/client"
+ "go.opentelemetry.io/otel/trace"
+)
+
+// Document
+const (
+ // cqldb has all keys in lowercase
+ idKey = "id"
+ ownerKey = "owner"
+ deviceIDKey = "deviceid"
+ commonNameKey = "commonname"
+ dataKey = "data"
+)
+
+type Index struct {
+ Name string
+ PartitionKey string
+ SecondaryColumn string
+}
+
+// partition key: idKey
+// clustering key: deviceIDKey
+var primaryKey = []string{idKey, ownerKey, commonNameKey}
+
+// Store implements an Store for cqldb.
+type Store struct {
+ *cqldb.Store
+}
+
+func New(ctx context.Context, config *Config, fileWatcher *fsnotify.Watcher, logger log.Logger, tracerProvider trace.TracerProvider) (*Store, error) {
+ certManager, err := client.New(config.Embedded.TLS, fileWatcher, logger)
+ if err != nil {
+ return nil, fmt.Errorf("could not create cert manager: %w", err)
+ }
+ cqldbClient, err := cqldb.New(ctx, config.Embedded, certManager.GetTLSConfig(), logger, tracerProvider)
+ if err != nil {
+ certManager.Close()
+ return nil, err
+ }
+ store, err := newEventStoreWithClient(ctx, cqldbClient, config, logger)
+ if err != nil {
+ cqldbClient.Close()
+ certManager.Close()
+ return nil, err
+ }
+ store.AddCloseFunc(certManager.Close)
+ return store, nil
+}
+
+func createEventsTable(ctx context.Context, client *cqldb.Client, table string) error {
+ q := "create table if not exists " + client.Keyspace() + "." + table + " (" +
+ idKey + " " + cqldb.UUIDType + "," +
+ ownerKey + " " + cqldb.StringType + "," +
+ deviceIDKey + " " + cqldb.UUIDType + "," +
+ commonNameKey + " " + cqldb.StringType + "," +
+ dataKey + " " + cqldb.BytesType + "," +
+ "primary key (" + strings.Join(primaryKey, ",") + ")" +
+ ")"
+ err := client.Session().Query(q).WithContext(ctx).Exec()
+ if err != nil {
+ return fmt.Errorf("failed to create table(%v): %w", table, err)
+ }
+ return nil
+}
+
+// NewEventStoreWithClient creates a new Store with a session.
+func newEventStoreWithClient(ctx context.Context, client *cqldb.Client, config *Config, logger log.Logger) (*Store, error) {
+ if client == nil {
+ return nil, errors.New("invalid client")
+ }
+
+ if config.Table == "" {
+ config.Table = "snippets"
+ }
+
+ err := createEventsTable(ctx, client, config.Table)
+ if err != nil {
+ return nil, err
+ }
+
+ return &Store{
+ Store: cqldb.NewStore(config.Table, client, logger),
+ }, nil
+}
diff --git a/snippet-service/store/mongodb/config.go b/snippet-service/store/mongodb/config.go
new file mode 100644
index 000000000..8034a68c7
--- /dev/null
+++ b/snippet-service/store/mongodb/config.go
@@ -0,0 +1,13 @@
+package mongodb
+
+import (
+ pkgMongo "github.com/plgd-dev/hub/v2/pkg/mongodb"
+)
+
+type Config struct {
+ Mongo pkgMongo.Config `yaml:",inline"`
+}
+
+func (c *Config) Validate() error {
+ return c.Mongo.Validate()
+}
diff --git a/snippet-service/store/mongodb/configuration.go b/snippet-service/store/mongodb/configuration.go
new file mode 100644
index 000000000..b5721a3da
--- /dev/null
+++ b/snippet-service/store/mongodb/configuration.go
@@ -0,0 +1,365 @@
+package mongodb
+
+import (
+ "cmp"
+ "context"
+ "slices"
+ "strings"
+
+ "github.com/hashicorp/go-multierror"
+ "github.com/plgd-dev/hub/v2/snippet-service/pb"
+ "github.com/plgd-dev/hub/v2/snippet-service/store"
+ "go.mongodb.org/mongo-driver/bson"
+ "go.mongodb.org/mongo-driver/mongo"
+)
+
+func (s *Store) CreateConfiguration(ctx context.Context, conf *pb.Configuration) (*pb.Configuration, error) {
+ if err := conf.ValidateAndNormalize(); err != nil {
+ return nil, err
+ }
+
+ _, err := s.Collection(configurationsCol).InsertOne(ctx, store.MakeConfiguration(conf))
+ if err != nil {
+ return nil, err
+ }
+ return conf, nil
+}
+
+func (s *Store) UpdateConfiguration(ctx context.Context, conf *pb.Configuration) (*pb.Configuration, error) {
+ if err := conf.ValidateAndNormalize(); err != nil {
+ return nil, err
+ }
+ upd, err := s.Collection(configurationsCol).UpdateOne(ctx,
+ bson.M{
+ "_id": conf.GetId(),
+ store.OwnerKey: conf.GetOwner(),
+ store.VersionsKey + "." + store.VersionKey: bson.M{"$ne": conf.GetVersion()},
+ },
+ bson.M{"$push": bson.M{
+ "versions": store.ConfigurationVersion{
+ Version: conf.GetVersion(),
+ Resources: conf.GetResources(),
+ },
+ }},
+ )
+ if err != nil {
+ return nil, err
+ }
+ if upd.MatchedCount == 0 {
+ return nil, store.ErrNotFound
+ }
+ return conf, nil
+}
+
+func compareIdFilter(i, j *pb.IDFilter) int {
+ if i.GetId() != j.GetId() {
+ return strings.Compare(i.GetId(), j.GetId())
+ }
+ if i.GetAll() {
+ if j.GetAll() {
+ return 0
+ }
+ return -1
+ }
+ if i.GetLatest() {
+ if j.GetLatest() {
+ return 0
+ }
+ if j.GetAll() {
+ return 1
+ }
+ return -1
+ }
+ if j.GetAll() || j.GetLatest() {
+ return 1
+ }
+ return cmp.Compare(i.GetValue(), j.GetValue())
+}
+
+func checkEmptyIdFilter(idfilter []*pb.IDFilter) []*pb.IDFilter {
+ // if an empty query is provided, return all
+ if len(idfilter) == 0 {
+ return nil
+ }
+ slices.SortFunc(idfilter, compareIdFilter)
+ // if the first filter is All, we can ignore all other filters
+ first := idfilter[0]
+ if first.GetId() == "" && first.GetAll() {
+ return nil
+ }
+ return idfilter
+}
+
+func normalizeIdFilter(idfilter []*pb.IDFilter) []*pb.IDFilter {
+ idfilter = checkEmptyIdFilter(idfilter)
+ if len(idfilter) == 0 {
+ return nil
+ }
+
+ updatedFilter := make([]*pb.IDFilter, 0)
+ var idAll bool
+ var idLatest bool
+ var idValue bool
+ var idValueVersion uint64
+ setNextLatest := func(idf *pb.IDFilter) {
+ // we already have the latest filter
+ if idLatest {
+ // skip
+ return
+ }
+ idLatest = true
+ updatedFilter = append(updatedFilter, idf)
+ }
+ setNextValue := func(idf *pb.IDFilter) {
+ value := idf.GetValue()
+ if idValue && value == idValueVersion {
+ // skip
+ return
+ }
+ idValue = true
+ idValueVersion = value
+ updatedFilter = append(updatedFilter, idf)
+ }
+ prevID := ""
+ for _, idf := range idfilter {
+ if idf.GetId() != prevID {
+ idAll = idf.GetAll()
+ idLatest = idf.GetLatest()
+ idValue = !idAll && !idLatest
+ idValueVersion = idf.GetValue()
+ updatedFilter = append(updatedFilter, idf)
+ }
+
+ if idAll {
+ goto next
+ }
+
+ if idf.GetLatest() {
+ setNextLatest(idf)
+ goto next
+ }
+
+ setNextValue(idf)
+
+ next:
+ prevID = idf.GetId()
+ }
+ return updatedFilter
+}
+
+type versionFilter struct {
+ latest bool
+ versions []uint64
+}
+
+func partitionQuery(idfilter []*pb.IDFilter) ([]string, map[string]versionFilter) {
+ idFilter := normalizeIdFilter(idfilter)
+ if len(idFilter) == 0 {
+ return nil, nil
+ }
+ idVersionAll := make([]string, 0)
+ idVersions := make(map[string]versionFilter, 0)
+ hasAllIdsLatest := func() bool {
+ vf, ok := idVersions[""]
+ return ok && vf.latest
+ }
+ hasAllIdsVersion := func(version uint64) bool {
+ vf, ok := idVersions[""]
+ return ok && slices.Contains(vf.versions, version)
+ }
+ for _, idf := range idFilter {
+ if idf.GetAll() {
+ idVersionAll = append(idVersionAll, idf.GetId())
+ continue
+ }
+ vf := idVersions[idf.GetId()]
+ if idf.GetLatest() {
+ if hasAllIdsLatest() {
+ continue
+ }
+ vf.latest = true
+ idVersions[idf.GetId()] = vf
+ continue
+ }
+ version := idf.GetValue()
+ if hasAllIdsVersion(version) {
+ continue
+ }
+ vf.versions = append(vf.versions, version)
+ idVersions[idf.GetId()] = vf
+ }
+ return idVersionAll, idVersions
+}
+
+func toIdFilterQuery(owner string, idfAlls []string) interface{} {
+ filters := make([]interface{}, 0, 2)
+ if owner != "" {
+ filters = append(filters, bson.D{{Key: store.OwnerKey, Value: owner}})
+ }
+ if len(idfAlls) > 0 {
+ idfilter := make([]bson.D, 0, len(idfAlls))
+ for _, idfall := range idfAlls {
+ idfilter = append(idfilter, bson.D{{Key: "_id", Value: idfall}})
+ }
+ filters = append(filters, bson.M{"$or": idfilter})
+ }
+ if len(filters) == 0 {
+ return bson.D{}
+ }
+ if len(filters) == 1 {
+ return filters[0]
+ }
+ return bson.M{"$and": filters}
+}
+
+func processCursor(ctx context.Context, cr *mongo.Cursor, h store.GetConfigurationsFunc) error {
+ if h == nil {
+ return nil
+ }
+ var errors *multierror.Error
+ i := store.MongoIterator[store.Configuration]{
+ Cursor: cr,
+ }
+ err := h(ctx, &i)
+ errors = multierror.Append(errors, err)
+ errClose := cr.Close(ctx)
+ errors = multierror.Append(errors, errClose)
+ return errors.ErrorOrNil()
+}
+
+func (s *Store) getConfigurationsByFind(ctx context.Context, owner string, idfAlls []string, h store.GetConfigurationsFunc) error {
+ cur, err := s.Collection(configurationsCol).Find(ctx, toIdFilterQuery(owner, idfAlls))
+ if err != nil {
+ return err
+ }
+ return processCursor(ctx, cur, h)
+}
+
+func addMatchCondition(owner, id string) bson.D {
+ match := bson.D{
+ {Key: store.VersionsKey + ".0", Value: bson.M{"$exists": true}},
+ }
+ if id != "" {
+ match = append(match, bson.E{Key: "_id", Value: id})
+ }
+ if owner != "" {
+ match = append(match, bson.E{Key: store.OwnerKey, Value: owner})
+ }
+ return match
+}
+
+func addLatestVersionField() bson.D {
+ return bson.D{{Key: "$addFields", Value: bson.M{
+ "latestVersion": bson.M{"$reduce": bson.M{
+ "input": "$versions",
+ "initialValue": bson.M{"version": 0},
+ "in": bson.M{
+ "$cond": bson.A{
+ bson.M{"$gte": bson.A{"$$this.version", "$$value.version"}},
+ "$$this.version",
+ "$$value.version",
+ },
+ },
+ }},
+ }}}
+}
+
+func getVersionsPipeline(pl mongo.Pipeline, vf versionFilter, exclude bool) mongo.Pipeline {
+ versions := make([]interface{}, 0, len(vf.versions)+1)
+ for _, version := range vf.versions {
+ versions = append(versions, version)
+ }
+
+ if vf.latest {
+ pl = append(pl, addLatestVersionField())
+ versions = append(versions, "$latestVersion")
+ }
+
+ if len(versions) > 0 {
+ cond := bson.M{"$in": bson.A{"$$version.version", versions}}
+ if exclude {
+ cond = bson.M{"$not": cond}
+ }
+ pl = append(pl, bson.D{{Key: "$addFields", Value: bson.M{
+ "versions": bson.M{
+ "$filter": bson.M{
+ "input": "$versions",
+ "as": "version",
+ "cond": cond,
+ },
+ },
+ }}})
+ }
+ return pl
+}
+
+func (s *Store) getConfigurationsByAggregation(ctx context.Context, owner, id string, vf versionFilter, h store.GetConfigurationsFunc) error {
+ pl := mongo.Pipeline{bson.D{{Key: "$match", Value: addMatchCondition(owner, id)}}}
+ pl = getVersionsPipeline(pl, vf, false)
+ cur, err := s.Collection(configurationsCol).Aggregate(ctx, pl)
+ if err != nil {
+ return err
+ }
+ return processCursor(ctx, cur, h)
+}
+
+func (s *Store) GetConfigurations(ctx context.Context, owner string, query *pb.GetConfigurationsRequest, h store.GetConfigurationsFunc) error {
+ idVersionAll, idVersions := partitionQuery(query.GetIdFilter())
+ var errors *multierror.Error
+ if len(idVersionAll) > 0 || len(idVersions) == 0 {
+ err := s.getConfigurationsByFind(ctx, owner, idVersionAll, h)
+ errors = multierror.Append(errors, err)
+ }
+ if len(idVersions) > 0 {
+ for id, vf := range idVersions {
+ err := s.getConfigurationsByAggregation(ctx, owner, id, vf, h)
+ errors = multierror.Append(errors, err)
+ }
+ }
+ return errors.ErrorOrNil()
+}
+
+func (s *Store) removeDocument(ctx context.Context, owner string, idfAlls []string) error {
+ _, err := s.Collection(configurationsCol).DeleteMany(ctx, toIdFilterQuery(owner, idfAlls))
+ return err
+}
+
+func (s *Store) removeVersion(ctx context.Context, owner string, id string, vf versionFilter) error {
+ pl := getVersionsPipeline(mongo.Pipeline{}, vf, true)
+ if vf.latest {
+ pl = append(pl, bson.D{{Key: "$unset", Value: "latestVersion"}})
+ }
+ _, err := s.Collection(configurationsCol).UpdateMany(ctx, addMatchCondition(owner, id), pl)
+ // TODO: delete document if no versions remain in the array
+ return err
+}
+
+func (s *Store) DeleteConfigurations(ctx context.Context, owner string, query *pb.DeleteConfigurationsRequest) (int64, error) {
+ success := false
+ idVersionAll, idVersions := partitionQuery(query.GetIdFilter())
+ var errors *multierror.Error
+ if len(idVersionAll) > 0 || len(idVersions) == 0 {
+ err := s.removeDocument(ctx, owner, idVersionAll)
+ if err == nil {
+ success = true
+ }
+ errors = multierror.Append(errors, err)
+ }
+ if len(idVersions) > 0 {
+ for id, vf := range idVersions {
+ err := s.removeVersion(ctx, owner, id, vf)
+ if err == nil {
+ success = true
+ }
+ errors = multierror.Append(errors, err)
+ }
+ }
+ err := errors.ErrorOrNil()
+ if err != nil {
+ if success {
+ return 2, err
+ }
+ return 0, err
+ }
+ return 1, nil
+}
diff --git a/snippet-service/store/mongodb/configuration_internal_test.go b/snippet-service/store/mongodb/configuration_internal_test.go
new file mode 100644
index 000000000..c5179ad21
--- /dev/null
+++ b/snippet-service/store/mongodb/configuration_internal_test.go
@@ -0,0 +1,112 @@
+package mongodb
+
+import (
+ "testing"
+
+ "github.com/plgd-dev/hub/v2/snippet-service/pb"
+ "github.com/stretchr/testify/require"
+)
+
+func TestConfigurationNormalizeQuery(t *testing.T) {
+ tests := []struct {
+ name string
+ query *pb.GetConfigurationsRequest
+ want []*pb.IDFilter
+ }{
+ {
+ name: "nil",
+ query: nil,
+ want: nil,
+ },
+ {
+ name: "empty",
+ query: &pb.GetConfigurationsRequest{},
+ want: nil,
+ },
+ {
+ // if the query contains an IDFilter with an empty ID and All set to true, then all other filters are ignored
+ name: "all",
+ query: &pb.GetConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {Id: "id1", Version: &pb.IDFilter_Latest{Latest: true}},
+ {Id: "id2", Version: &pb.IDFilter_Value{Value: 1}},
+ {Version: &pb.IDFilter_All{All: true}},
+ },
+ },
+ want: nil,
+ },
+ {
+ // if the query contains an IDFilter with All set to true then other filters for the ID are ignored
+ name: "remove non-All",
+ query: &pb.GetConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {Id: "id1", Version: &pb.IDFilter_Latest{Latest: true}},
+ {Id: "id1", Version: &pb.IDFilter_Value{Value: 1}},
+ {Id: "id1", Version: &pb.IDFilter_All{All: true}},
+ },
+ },
+ want: []*pb.IDFilter{
+ {Id: "id1", Version: &pb.IDFilter_All{All: true}},
+ },
+ },
+ {
+ name: "remove duplicates",
+ query: &pb.GetConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {Id: "id1", Version: &pb.IDFilter_All{All: true}},
+ {Id: "id1", Version: &pb.IDFilter_All{All: true}},
+ {Id: "id2", Version: &pb.IDFilter_Latest{Latest: true}},
+ {Id: "id2", Version: &pb.IDFilter_Latest{Latest: true}},
+ {Id: "id2", Version: &pb.IDFilter_Value{Value: 42}},
+ {Id: "id2", Version: &pb.IDFilter_Value{Value: 42}},
+ {Id: "id2", Version: &pb.IDFilter_Value{Value: 1}},
+ {Id: "id2", Version: &pb.IDFilter_Value{Value: 1}},
+ {Id: "id2", Version: &pb.IDFilter_Latest{Latest: true}},
+ {Id: "id2", Version: &pb.IDFilter_Value{Value: 42}},
+ },
+ },
+ want: []*pb.IDFilter{
+ {Id: "id1", Version: &pb.IDFilter_All{All: true}},
+ {Id: "id2", Version: &pb.IDFilter_Latest{Latest: true}},
+ {Id: "id2", Version: &pb.IDFilter_Value{Value: 1}},
+ {Id: "id2", Version: &pb.IDFilter_Value{Value: 42}},
+ },
+ },
+ {
+ name: "normalize",
+ query: &pb.GetConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {Id: "id3", Version: &pb.IDFilter_Value{Value: 3}},
+ {Id: "id1", Version: &pb.IDFilter_Value{Value: 0}},
+ {Id: "id3", Version: &pb.IDFilter_Value{Value: 3}},
+ {Id: "id2", Version: &pb.IDFilter_All{All: true}},
+ {Id: "id3", Version: &pb.IDFilter_Latest{Latest: true}},
+ {Id: "id1", Version: &pb.IDFilter_Value{Value: 1}},
+ {Id: "id3", Version: &pb.IDFilter_Value{Value: 1}},
+ {Id: "id2", Version: &pb.IDFilter_Latest{Latest: true}},
+ {Id: "id3", Version: &pb.IDFilter_Value{Value: 2}},
+ {Id: "id2", Version: &pb.IDFilter_Value{Value: 42}},
+ {Id: "id1", Version: &pb.IDFilter_Latest{Latest: true}},
+ {Id: "id3", Version: &pb.IDFilter_Latest{Latest: true}},
+ {Id: "id1", Version: &pb.IDFilter_Latest{Latest: true}},
+ },
+ },
+ want: []*pb.IDFilter{
+ {Id: "id1", Version: &pb.IDFilter_Latest{Latest: true}},
+ {Id: "id1", Version: &pb.IDFilter_Value{Value: 0}},
+ {Id: "id1", Version: &pb.IDFilter_Value{Value: 1}},
+ {Id: "id2", Version: &pb.IDFilter_All{All: true}},
+ {Id: "id3", Version: &pb.IDFilter_Latest{Latest: true}},
+ {Id: "id3", Version: &pb.IDFilter_Value{Value: 1}},
+ {Id: "id3", Version: &pb.IDFilter_Value{Value: 2}},
+ {Id: "id3", Version: &pb.IDFilter_Value{Value: 3}},
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ require.EqualValues(t, tt.want, normalizeIdFilter(tt.query.GetIdFilter()))
+ })
+ }
+}
diff --git a/snippet-service/store/mongodb/createConfiguration_test.go b/snippet-service/store/mongodb/createConfiguration_test.go
new file mode 100644
index 000000000..6a62fcca4
--- /dev/null
+++ b/snippet-service/store/mongodb/createConfiguration_test.go
@@ -0,0 +1,138 @@
+package mongodb_test
+
+import (
+ "context"
+ "testing"
+
+ "github.com/google/uuid"
+ "github.com/plgd-dev/go-coap/v3/message"
+ "github.com/plgd-dev/hub/v2/resource-aggregate/commands"
+ "github.com/plgd-dev/hub/v2/snippet-service/pb"
+ "github.com/plgd-dev/hub/v2/snippet-service/test"
+ hubTest "github.com/plgd-dev/hub/v2/test"
+ "github.com/plgd-dev/hub/v2/test/config"
+ "github.com/stretchr/testify/require"
+)
+
+func makeLightResourceConfiguration(t *testing.T, id string, power int, ttl int64) *pb.Configuration_Resource {
+ return &pb.Configuration_Resource{
+ Href: hubTest.TestResourceLightInstanceHref(id),
+ Content: &commands.Content{
+ Data: hubTest.EncodeToCbor(t, map[string]interface{}{
+ "power": power,
+ }),
+ ContentType: message.AppOcfCbor.String(),
+ CoapContentFormat: int32(message.AppOcfCbor),
+ },
+ TimeToLive: ttl,
+ }
+}
+
+func TestStoreCreateConfiguration(t *testing.T) {
+ s, cleanUpStore := test.NewMongoStore(t)
+ defer cleanUpStore()
+
+ ctx, cancel := context.WithTimeout(context.Background(), config.TEST_TIMEOUT)
+ defer cancel()
+
+ confID := uuid.New().String()
+ const owner = "owner1"
+ resources := []*pb.Configuration_Resource{
+ makeLightResourceConfiguration(t, "1", 1, 1337),
+ }
+
+ type args struct {
+ create *pb.Configuration
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ }{
+ {
+ name: "valid",
+ args: args{
+ create: &pb.Configuration{
+ Id: confID,
+ Name: "valid",
+ Owner: owner,
+ Version: 0,
+ Resources: resources,
+ },
+ },
+ },
+ {
+ name: "duplicit item (ID)",
+ args: args{
+ create: &pb.Configuration{
+ Id: confID,
+ Name: "duplicit ID",
+ Owner: owner,
+ Resources: resources,
+ Version: 42,
+ },
+ },
+ wantErr: true,
+ },
+ {
+ name: "missing ID",
+ args: args{
+ create: &pb.Configuration{
+ Name: "missing ID",
+ Owner: owner,
+ Version: 0,
+ Resources: resources,
+ },
+ },
+ wantErr: true,
+ },
+ {
+ name: "invalid ID",
+ args: args{
+ create: &pb.Configuration{
+ Id: "invalid",
+ Name: "invalid ID",
+ Owner: owner,
+ Version: 0,
+ Resources: resources,
+ },
+ },
+ wantErr: true,
+ },
+ {
+ name: "missing owner",
+ args: args{
+ create: &pb.Configuration{
+ Id: confID,
+ Name: "missing owner",
+ Version: 0,
+ Resources: resources,
+ },
+ },
+ wantErr: true,
+ },
+ {
+ name: "missing resources",
+ args: args{
+ create: &pb.Configuration{
+ Id: confID,
+ Name: "missing resources",
+ Owner: owner,
+ Version: 0,
+ },
+ },
+ wantErr: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ _, err := s.CreateConfiguration(ctx, tt.args.create)
+ if tt.wantErr {
+ require.Error(t, err)
+ return
+ }
+ require.NoError(t, err)
+ })
+ }
+}
diff --git a/snippet-service/store/mongodb/deleteConfigurations_test.go b/snippet-service/store/mongodb/deleteConfigurations_test.go
new file mode 100644
index 000000000..6cf8a6159
--- /dev/null
+++ b/snippet-service/store/mongodb/deleteConfigurations_test.go
@@ -0,0 +1,366 @@
+package mongodb_test
+
+import (
+ "context"
+ "testing"
+
+ "github.com/plgd-dev/hub/v2/snippet-service/pb"
+ "github.com/plgd-dev/hub/v2/snippet-service/store"
+ "github.com/plgd-dev/hub/v2/snippet-service/store/mongodb"
+ "github.com/plgd-dev/hub/v2/snippet-service/test"
+ "github.com/plgd-dev/hub/v2/test/config"
+ "github.com/stretchr/testify/require"
+)
+
+func TestStoreDeleteConfigurations(t *testing.T) {
+ ctx, cancel := context.WithTimeout(context.Background(), config.TEST_TIMEOUT)
+ defer cancel()
+
+ getConfigurations := func(t *testing.T, s *mongodb.Store, owner string, query *pb.GetConfigurationsRequest) []*store.Configuration {
+ var configurations []*store.Configuration
+ err := s.GetConfigurations(ctx, owner, query, func(iterCtx context.Context, iter store.Iterator[store.Configuration]) error {
+ var conf store.Configuration
+ for iter.Next(iterCtx, &conf) {
+ configurations = append(configurations, conf.Clone())
+ }
+ return iter.Err()
+ })
+ require.NoError(t, err)
+ return configurations
+ }
+
+ type args struct {
+ owner string
+ query *pb.DeleteConfigurationsRequest
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ want func(t *testing.T, s *mongodb.Store, stored map[string]store.Configuration)
+ }{
+ {
+ name: "all",
+ args: args{
+ owner: "",
+ query: nil,
+ },
+ want: func(t *testing.T, s *mongodb.Store, _ map[string]store.Configuration) {
+ confs := getConfigurations(t, s, "", nil)
+ require.Empty(t, confs)
+ },
+ },
+ {
+ name: "owner1",
+ args: args{
+ owner: test.ConfigurationOwner(1),
+ query: nil,
+ },
+ want: func(t *testing.T, s *mongodb.Store, stored map[string]store.Configuration) {
+ confs := getConfigurations(t, s, "", nil)
+ require.NotEmpty(t, confs)
+ newCount := 0
+ for _, conf := range confs {
+ require.NotEqual(t, test.ConfigurationOwner(1), conf.Owner)
+ newCount += len(conf.Versions)
+ }
+ storedCount := 0
+ for _, conf := range stored {
+ if conf.Owner != test.ConfigurationOwner(1) {
+ storedCount += len(conf.Versions)
+ }
+ }
+ require.Equal(t, storedCount, newCount)
+ },
+ },
+ {
+ name: "id{1,3,4,5}",
+ args: args{
+ owner: "",
+ query: &pb.DeleteConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {
+ Id: test.ConfigurationID(1),
+ Version: &pb.IDFilter_All{All: true},
+ },
+ {
+ Id: test.ConfigurationID(3),
+ Version: &pb.IDFilter_All{All: true},
+ },
+ {
+ Id: test.ConfigurationID(4),
+ Version: &pb.IDFilter_All{All: true},
+ },
+ {
+ Id: test.ConfigurationID(5),
+ Version: &pb.IDFilter_All{All: true},
+ },
+ },
+ },
+ },
+ want: func(t *testing.T, s *mongodb.Store, stored map[string]store.Configuration) {
+ confs := getConfigurations(t, s, "", nil)
+ require.NotEmpty(t, confs)
+ newCount := 0
+ for _, conf := range confs {
+ require.NotEqual(t, test.ConfigurationID(1), conf.Id)
+ require.NotEqual(t, test.ConfigurationID(3), conf.Id)
+ require.NotEqual(t, test.ConfigurationID(4), conf.Id)
+ require.NotEqual(t, test.ConfigurationID(5), conf.Id)
+ newCount += len(conf.Versions)
+ }
+ storedCount := 0
+ for _, conf := range stored {
+ if conf.Id == test.ConfigurationID(1) ||
+ conf.Id == test.ConfigurationID(3) ||
+ conf.Id == test.ConfigurationID(4) ||
+ conf.Id == test.ConfigurationID(5) {
+ continue
+ }
+ storedCount += len(conf.Versions)
+ }
+ require.Equal(t, storedCount, newCount)
+ },
+ },
+ {
+ name: "owner2/id2",
+ args: args{
+ owner: test.ConfigurationOwner(2),
+ query: &pb.DeleteConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {
+ Id: test.ConfigurationID(2),
+ Version: &pb.IDFilter_All{All: true},
+ },
+ // Ids not owned by owner2 should not be deleted
+ {
+ Id: test.ConfigurationID(1),
+ Version: &pb.IDFilter_All{All: true},
+ },
+ {
+ Id: test.ConfigurationID(3),
+ Version: &pb.IDFilter_All{All: true},
+ },
+ },
+ },
+ },
+ want: func(t *testing.T, s *mongodb.Store, stored map[string]store.Configuration) {
+ confs := getConfigurations(t, s, "", nil)
+ require.NotEmpty(t, confs)
+ newCount := 0
+ for _, conf := range confs {
+ require.NotEqual(t, test.ConfigurationID(2), conf.Id)
+ newCount += len(conf.Versions)
+ }
+ storedCount := 0
+ for _, conf := range stored {
+ if conf.Id == test.ConfigurationID(2) {
+ continue
+ }
+ storedCount += len(conf.Versions)
+ }
+ require.Equal(t, storedCount, newCount)
+ },
+ },
+ {
+ name: "latest",
+ args: args{
+ query: &pb.DeleteConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {
+ Version: &pb.IDFilter_Latest{
+ Latest: true,
+ },
+ },
+ },
+ },
+ },
+ want: func(t *testing.T, s *mongodb.Store, stored map[string]store.Configuration) {
+ storedLatest := make(map[string]store.ConfigurationVersion)
+ storedCount := 0
+ for _, conf := range stored {
+ storedLatest[conf.Id] = conf.Versions[len(conf.Versions)-1]
+ storedCount += len(conf.Versions)
+ }
+ confs := getConfigurations(t, s, "", nil)
+ require.NotEmpty(t, confs)
+ count := 0
+ for _, conf := range confs {
+ require.NotEqual(t, storedLatest[conf.Id], conf.Versions[len(conf.Versions)-1])
+ count += len(conf.Versions)
+ }
+ require.Equal(t, storedCount-len(storedLatest), count)
+ },
+ },
+ {
+ name: "owner1/latest",
+ args: args{
+ owner: test.ConfigurationOwner(1),
+ query: &pb.DeleteConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {
+ Version: &pb.IDFilter_Latest{
+ Latest: true,
+ },
+ },
+ // duplicates should be ignored
+ {
+ Version: &pb.IDFilter_Latest{
+ Latest: true,
+ },
+ },
+ {
+ Version: &pb.IDFilter_Latest{
+ Latest: true,
+ },
+ },
+ },
+ },
+ },
+ want: func(t *testing.T, s *mongodb.Store, stored map[string]store.Configuration) {
+ storedLatest := make(map[string]store.ConfigurationVersion)
+ storedCount := 0
+ for _, conf := range stored {
+ storedLatest[conf.Id] = conf.Versions[len(conf.Versions)-1]
+ storedCount += len(conf.Versions)
+ }
+ confs := getConfigurations(t, s, "", nil)
+ require.NotEmpty(t, confs)
+ count := 0
+ removed := 0
+ for _, conf := range confs {
+ if conf.Owner == test.ConfigurationOwner(1) {
+ require.NotEqual(t, storedLatest[conf.Id], conf.Versions[len(conf.Versions)-1])
+ removed++
+ } else {
+ require.Equal(t, storedLatest[conf.Id], conf.Versions[len(conf.Versions)-1])
+ }
+ count += len(conf.Versions)
+ }
+ require.Equal(t, storedCount-removed, count)
+ },
+ },
+ {
+ name: "owner2/id1/latest - non-matching owner",
+ args: args{
+ owner: test.ConfigurationOwner(2),
+ query: &pb.DeleteConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {
+ Id: test.ConfigurationID(1),
+ Version: &pb.IDFilter_Latest{
+ Latest: true,
+ },
+ },
+ },
+ },
+ },
+ want: func(t *testing.T, s *mongodb.Store, stored map[string]store.Configuration) {
+ confs := getConfigurations(t, s, "", nil)
+ confsMap := make(map[string]store.Configuration)
+ for _, conf := range confs {
+ confsMap[conf.Id] = *conf
+ }
+ test.CmpStoredConfigurationMaps(t, stored, confsMap)
+ },
+ },
+ {
+ name: "version/{42, 142, 242, 342, 442, 542}", args: args{
+ owner: "",
+ query: &pb.DeleteConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {Version: &pb.IDFilter_Value{Value: 42}},
+ {Version: &pb.IDFilter_Value{Value: 142}},
+ {Version: &pb.IDFilter_Value{Value: 242}},
+ {Version: &pb.IDFilter_Value{Value: 342}},
+ {Version: &pb.IDFilter_Value{Value: 442}},
+ {Version: &pb.IDFilter_Value{Value: 542}},
+ },
+ },
+ },
+ want: func(t *testing.T, s *mongodb.Store, stored map[string]store.Configuration) {
+ confs := getConfigurations(t, s, "", nil)
+ confsMap := make(map[string]store.Configuration)
+ for _, conf := range confs {
+ confsMap[conf.Id] = *conf
+ }
+ for _, conf := range stored {
+ versions := make([]store.ConfigurationVersion, 0)
+ for _, version := range conf.Versions {
+ if version.Version == 42 ||
+ version.Version == 142 ||
+ version.Version == 242 ||
+ version.Version == 342 ||
+ version.Version == 442 ||
+ version.Version == 542 {
+ continue
+ }
+ versions = append(versions, version)
+ }
+ conf.Versions = versions
+ stored[conf.Id] = conf
+ }
+ test.CmpStoredConfigurationMaps(t, stored, confsMap)
+ },
+ },
+ {
+ name: "owner2/version/{213, 237, 242}", args: args{
+ owner: test.ConfigurationOwner(2),
+ query: &pb.DeleteConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {Version: &pb.IDFilter_Value{Value: 213}},
+ {Version: &pb.IDFilter_Value{Value: 237}},
+ {Version: &pb.IDFilter_Value{Value: 242}},
+ // duplicates should be ignored
+ {Version: &pb.IDFilter_Value{Value: 237}},
+ // filter with Id should be ignored if there are filters without Id
+ {
+ Id: test.ConfigurationID(2),
+ Version: &pb.IDFilter_Value{Value: 237},
+ },
+ },
+ },
+ },
+ want: func(t *testing.T, s *mongodb.Store, stored map[string]store.Configuration) {
+ confs := getConfigurations(t, s, "", nil)
+ confsMap := make(map[string]store.Configuration)
+ for _, conf := range confs {
+ confsMap[conf.Id] = *conf
+ }
+ for _, conf := range stored {
+ if conf.Owner == test.ConfigurationOwner(2) {
+ versions := make([]store.ConfigurationVersion, 0)
+ for _, version := range conf.Versions {
+ if version.Version == 213 ||
+ version.Version == 237 ||
+ version.Version == 242 {
+ continue
+ }
+ versions = append(versions, version)
+ }
+ conf.Versions = versions
+ stored[conf.Id] = conf
+ }
+ }
+ test.CmpStoredConfigurationMaps(t, stored, confsMap)
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ s, cleanUpStore := test.NewMongoStore(t)
+ defer cleanUpStore()
+ inserted := test.AddConfigurationsToStore(ctx, t, s, 500, func(iteration int) uint64 {
+ return uint64(iteration * 100)
+ })
+ _, err := s.DeleteConfigurations(ctx, tt.args.owner, tt.args.query)
+ if tt.wantErr {
+ require.Error(t, err)
+ return
+ }
+ require.NoError(t, err)
+ tt.want(t, s, inserted)
+ })
+ }
+}
diff --git a/snippet-service/store/mongodb/getConfigurations_test.go b/snippet-service/store/mongodb/getConfigurations_test.go
new file mode 100644
index 000000000..2dac743ca
--- /dev/null
+++ b/snippet-service/store/mongodb/getConfigurations_test.go
@@ -0,0 +1,301 @@
+package mongodb_test
+
+import (
+ "cmp"
+ "context"
+ "slices"
+ "testing"
+
+ "github.com/plgd-dev/hub/v2/snippet-service/pb"
+ "github.com/plgd-dev/hub/v2/snippet-service/store"
+ "github.com/plgd-dev/hub/v2/snippet-service/test"
+ "github.com/plgd-dev/hub/v2/test/config"
+ "github.com/stretchr/testify/require"
+)
+
+func TestStoreGetConfigurations(t *testing.T) {
+ s, cleanUpStore := test.NewMongoStore(t)
+ defer cleanUpStore()
+
+ ctx, cancel := context.WithTimeout(context.Background(), config.TEST_TIMEOUT)
+ defer cancel()
+ confs := test.AddConfigurationsToStore(ctx, t, s, 500, nil)
+
+ type args struct {
+ owner string
+ query *pb.GetConfigurationsRequest
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ want func(t *testing.T, configurations []*store.Configuration)
+ }{
+ {
+ name: "all",
+ args: args{
+ owner: "",
+ query: nil,
+ },
+ want: func(t *testing.T, configurations []*store.Configuration) {
+ require.Len(t, configurations, len(confs))
+ },
+ },
+ {
+ name: "owner0",
+ args: args{
+ owner: test.ConfigurationOwner(0),
+ query: nil,
+ },
+ want: func(t *testing.T, configurations []*store.Configuration) {
+ require.NotEmpty(t, configurations)
+ for _, c := range configurations {
+ require.Equal(t, test.ConfigurationOwner(0), c.Owner)
+ conf, ok := confs[c.Id]
+ require.True(t, ok)
+ test.CmpJSON(t, &conf, c)
+ }
+ },
+ },
+ {
+ name: "id1/all",
+ args: args{
+ owner: "",
+ query: &pb.GetConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {
+ Id: test.ConfigurationID(1),
+ Version: &pb.IDFilter_All{
+ All: true,
+ },
+ },
+ },
+ },
+ },
+ want: func(t *testing.T, configurations []*store.Configuration) {
+ require.Len(t, configurations, 1)
+ c := configurations[0]
+ conf, ok := confs[c.Id]
+ require.True(t, ok)
+ test.CmpJSON(t, &conf, c)
+ },
+ },
+ {
+ name: "owner2/id2/all",
+ args: args{
+ owner: test.ConfigurationOwner(2),
+ query: &pb.GetConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {
+ Id: test.ConfigurationID(2),
+ Version: &pb.IDFilter_All{
+ All: true,
+ },
+ },
+ },
+ },
+ },
+ want: func(t *testing.T, configurations []*store.Configuration) {
+ require.Len(t, configurations, 1)
+ c := configurations[0]
+ conf, ok := confs[c.Id]
+ require.True(t, ok)
+ require.Equal(t, test.ConfigurationID(2), conf.Id)
+ require.Equal(t, test.ConfigurationOwner(2), conf.Owner)
+ test.CmpJSON(t, &conf, c)
+ },
+ },
+ {
+ name: "latest",
+ args: args{
+ query: &pb.GetConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {
+ Version: &pb.IDFilter_Latest{
+ Latest: true,
+ },
+ },
+ },
+ },
+ },
+ want: func(t *testing.T, configurations []*store.Configuration) {
+ require.Len(t, configurations, 10)
+ for _, c := range configurations {
+ _, ok := confs[c.Id]
+ require.True(t, ok)
+ require.Len(t, c.Versions, 1)
+ }
+ },
+ },
+ {
+ name: "owner1/latest",
+ args: args{
+ owner: test.ConfigurationOwner(1),
+ query: &pb.GetConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {
+ Version: &pb.IDFilter_Latest{
+ Latest: true,
+ },
+ },
+ },
+ },
+ },
+ want: func(t *testing.T, configurations []*store.Configuration) {
+ require.Len(t, configurations, 3)
+ for _, c := range configurations {
+ conf, ok := confs[c.Id]
+ require.True(t, ok)
+ require.Equal(t, test.ConfigurationOwner(1), conf.Owner)
+ require.Len(t, c.Versions, 1)
+ }
+ },
+ },
+ {
+ name: "owner2/id1/latest - non-matching owner",
+ args: args{
+ owner: test.ConfigurationOwner(2),
+ query: &pb.GetConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {
+ Id: test.ConfigurationID(1),
+ Version: &pb.IDFilter_Latest{
+ Latest: true,
+ },
+ },
+ },
+ },
+ },
+ want: func(t *testing.T, configurations []*store.Configuration) {
+ require.Empty(t, configurations)
+ },
+ },
+ {
+ name: "owner2{latest, id2/latest, id5/latest} - non-matching owner", args: args{
+ owner: test.ConfigurationOwner(2),
+ query: &pb.GetConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {
+ Version: &pb.IDFilter_Latest{
+ Latest: true,
+ },
+ },
+ {
+ Id: test.ConfigurationID(2),
+ Version: &pb.IDFilter_Latest{
+ Latest: true,
+ },
+ },
+ {
+ Id: test.ConfigurationID(5),
+ Version: &pb.IDFilter_Latest{
+ Latest: true,
+ },
+ },
+ },
+ },
+ },
+ want: func(t *testing.T, configurations []*store.Configuration) {
+ require.Len(t, configurations, 3)
+ for _, c := range configurations {
+ conf, ok := confs[c.Id]
+ require.True(t, ok)
+ require.Equal(t, test.ConfigurationOwner(2), conf.Owner)
+ require.Len(t, c.Versions, 1)
+ }
+ },
+ },
+ {
+ name: "version/42", args: args{
+ query: &pb.GetConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {
+ Version: &pb.IDFilter_Value{
+ Value: 42,
+ },
+ },
+ },
+ },
+ },
+ want: func(t *testing.T, configurations []*store.Configuration) {
+ require.Len(t, configurations, 10)
+ for _, c := range configurations {
+ _, ok := confs[c.Id]
+ require.True(t, ok)
+ require.Len(t, c.Versions, 1)
+ require.Equal(t, uint64(42), c.Versions[0].Version)
+ }
+ },
+ },
+ {
+ name: "owner2/version/{13, 37, 42}", args: args{
+ owner: test.ConfigurationOwner(2),
+ query: &pb.GetConfigurationsRequest{
+ IdFilter: []*pb.IDFilter{
+ {
+ Version: &pb.IDFilter_Value{
+ Value: 13,
+ },
+ },
+ {
+ Version: &pb.IDFilter_Value{
+ Value: 37,
+ },
+ },
+ {
+ Version: &pb.IDFilter_Value{
+ Value: 42,
+ },
+ },
+ // duplicates should be ignored
+ {
+ Version: &pb.IDFilter_Value{
+ Value: 37,
+ },
+ },
+ // filter with Id should be ignored if there are filters without Id
+ {
+ Id: test.ConfigurationID(2),
+ Version: &pb.IDFilter_Value{
+ Value: 37,
+ },
+ },
+ },
+ },
+ },
+ want: func(t *testing.T, configurations []*store.Configuration) {
+ require.Len(t, configurations, 3)
+ for _, c := range configurations {
+ _, ok := confs[c.Id]
+ require.True(t, ok)
+ require.Len(t, c.Versions, 3)
+ slices.SortFunc(c.Versions, func(i, j store.ConfigurationVersion) int {
+ return cmp.Compare(i.Version, j.Version)
+ })
+ require.Equal(t, uint64(13), c.Versions[0].Version)
+ require.Equal(t, uint64(37), c.Versions[1].Version)
+ require.Equal(t, uint64(42), c.Versions[2].Version)
+ }
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ var configurations []*store.Configuration
+ err := s.GetConfigurations(ctx, tt.args.owner, tt.args.query, func(iterCtx context.Context, iter store.Iterator[store.Configuration]) error {
+ var conf store.Configuration
+ for iter.Next(iterCtx, &conf) {
+ configurations = append(configurations, conf.Clone())
+ }
+ return iter.Err()
+ })
+ if tt.wantErr {
+ require.Error(t, err)
+ return
+ }
+ require.NoError(t, err)
+ tt.want(t, configurations)
+ })
+ }
+}
diff --git a/snippet-service/store/mongodb/store.go b/snippet-service/store/mongodb/store.go
new file mode 100644
index 000000000..20cfe5ad9
--- /dev/null
+++ b/snippet-service/store/mongodb/store.go
@@ -0,0 +1,68 @@
+package mongodb
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/go-multierror"
+ "github.com/plgd-dev/hub/v2/pkg/fsnotify"
+ "github.com/plgd-dev/hub/v2/pkg/log"
+ pkgMongo "github.com/plgd-dev/hub/v2/pkg/mongodb"
+ "github.com/plgd-dev/hub/v2/pkg/security/certManager/client"
+ "github.com/plgd-dev/hub/v2/snippet-service/store"
+ "go.mongodb.org/mongo-driver/bson"
+ "go.mongodb.org/mongo-driver/mongo"
+ "go.mongodb.org/mongo-driver/mongo/options"
+ "go.opentelemetry.io/otel/trace"
+)
+
+type Store struct {
+ *pkgMongo.Store
+}
+
+const (
+ // conditionsCol = "conditions"
+ configurationsCol = "configurations"
+)
+
+var configurationIdVersionUniqueIndex = mongo.IndexModel{
+ Keys: bson.D{
+ {Key: "_id", Value: 1},
+ {Key: store.VersionsKey + "." + store.VersionKey, Value: 1},
+ },
+ Options: options.Index().SetUnique(true),
+}
+
+func New(ctx context.Context, cfg *Config, fileWatcher *fsnotify.Watcher, logger log.Logger, tracerProvider trace.TracerProvider) (*Store, error) {
+ certManager, err := client.New(cfg.Mongo.TLS, fileWatcher, logger)
+ if err != nil {
+ return nil, fmt.Errorf("could not create cert manager: %w", err)
+ }
+
+ m, err := pkgMongo.NewStoreWithCollections(ctx, &cfg.Mongo, certManager.GetTLSConfig(), tracerProvider, map[string][]mongo.IndexModel{
+ // conditionsCol: {deviceIDFilterAndOwnerIndex},
+ configurationsCol: {configurationIdVersionUniqueIndex},
+ })
+ if err != nil {
+ certManager.Close()
+ return nil, err
+ }
+ s := Store{Store: m}
+ s.SetOnClear(s.clearDatabases)
+ s.AddCloseFunc(certManager.Close)
+ return &s, nil
+}
+
+func (s *Store) clearDatabases(ctx context.Context) error {
+ var errors *multierror.Error
+ collections := []string{configurationsCol}
+ for _, collection := range collections {
+ err := s.Collection(collection).Drop(ctx)
+ errors = multierror.Append(errors, err)
+ }
+ return errors.ErrorOrNil()
+}
+
+func (s *Store) Close(ctx context.Context) error {
+ return s.Store.Close(ctx)
+}
diff --git a/snippet-service/store/mongodb/updateConfiguration_test.go b/snippet-service/store/mongodb/updateConfiguration_test.go
new file mode 100644
index 000000000..f7e35d0c7
--- /dev/null
+++ b/snippet-service/store/mongodb/updateConfiguration_test.go
@@ -0,0 +1,103 @@
+package mongodb_test
+
+import (
+ "context"
+ "testing"
+
+ "github.com/google/uuid"
+ "github.com/plgd-dev/hub/v2/snippet-service/pb"
+ "github.com/plgd-dev/hub/v2/snippet-service/test"
+ "github.com/plgd-dev/hub/v2/test/config"
+ "github.com/stretchr/testify/require"
+)
+
+func TestUpdateConfiguration(t *testing.T) {
+ s, cleanUpStore := test.NewMongoStore(t)
+ defer cleanUpStore()
+
+ ctx, cancel := context.WithTimeout(context.Background(), config.TEST_TIMEOUT)
+ defer cancel()
+
+ confID := uuid.New().String()
+ const owner = "owner1"
+ resources := []*pb.Configuration_Resource{
+ makeLightResourceConfiguration(t, "1", 1, 1337),
+ }
+ _, err := s.CreateConfiguration(ctx, &pb.Configuration{
+ Id: confID,
+ Name: "valid",
+ Owner: owner,
+ Version: 0,
+ Resources: resources,
+ })
+ require.NoError(t, err)
+
+ type args struct {
+ update *pb.Configuration
+ }
+ tests := []struct {
+ name string
+ args args
+ wantErr bool
+ }{
+ {
+ name: "non-matching owner",
+ args: args{
+ update: &pb.Configuration{
+ Id: confID,
+ Owner: "invalid",
+ Version: 0,
+ Resources: resources,
+ },
+ },
+ wantErr: true,
+ },
+ {
+ name: "duplicit version",
+ args: args{
+ update: &pb.Configuration{
+ Id: confID,
+ Owner: owner,
+ Version: 0,
+ Resources: resources,
+ },
+ },
+ wantErr: true,
+ },
+ {
+ name: "missing ID",
+ args: args{
+ update: &pb.Configuration{
+ Owner: owner,
+ Version: 1,
+ Resources: resources,
+ },
+ },
+ wantErr: true,
+ },
+ {
+ name: "valid",
+ args: args{
+ update: &pb.Configuration{
+ Id: confID,
+ Owner: owner,
+ Version: 1,
+ Resources: []*pb.Configuration_Resource{
+ makeLightResourceConfiguration(t, "2", 2, 42),
+ },
+ },
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ _, err := s.UpdateConfiguration(ctx, tt.args.update)
+ if tt.wantErr {
+ require.Error(t, err)
+ return
+ }
+ require.NoError(t, err)
+ })
+ }
+}
diff --git a/snippet-service/store/store.go b/snippet-service/store/store.go
new file mode 100644
index 000000000..a2fbc0040
--- /dev/null
+++ b/snippet-service/store/store.go
@@ -0,0 +1,79 @@
+package store
+
+import (
+ "context"
+ "errors"
+
+ "github.com/plgd-dev/hub/v2/snippet-service/pb"
+ "go.mongodb.org/mongo-driver/mongo"
+)
+
+const (
+ IDKey = "id" // must match with Id field tag
+ VersionKey = "version" // must match with Version field tag
+ OwnerKey = "owner" // must match with Owner field tag
+ // DeviceIDFilterKey = "deviceIdFilter" // must match with Condition.DeviceIdFilter tag
+ // ResourceHrefFilterKey = "resourceHrefFilter" // must match with Condition.ResourceHrefFilter tag
+ // ResourceTypeFilterKey = "resourceTypeFilter" // must match with Condition.ResourceTypeFilter tag
+
+)
+
+// type (
+// ConditionsQuery struct {
+// DeviceID string
+// ResourceHref string
+// ResourceTypeFilter []string
+// }
+// )
+
+type Iterator[T any] interface {
+ Next(ctx context.Context, v *T) bool
+ Err() error
+}
+
+type (
+ // LoadConditionsFunc = func(ctx context.Context, iter Iterator[Condition]) (err error)
+ GetConfigurationsFunc = func(ctx context.Context, iter Iterator[Configuration]) (err error)
+)
+
+var (
+ ErrNotSupported = errors.New("not supported")
+ ErrNotFound = errors.New("not found")
+ ErrInvalidArgument = errors.New("invalid argument")
+)
+
+type MongoIterator[T any] struct {
+ Cursor *mongo.Cursor
+}
+
+func (i *MongoIterator[T]) Next(ctx context.Context, s *T) bool {
+ if !i.Cursor.Next(ctx) {
+ return false
+ }
+ err := i.Cursor.Decode(s)
+ return err == nil
+}
+
+func (i *MongoIterator[T]) Err() error {
+ return i.Cursor.Err()
+}
+
+type Store interface {
+ // CreateCondition creates a new condition. If the condition already exists, it will throw an error.
+ // CreateCondition(ctx context.Context, condition *Condition) error
+ // UpdateSigningRecord updates an existing signing record. If the record does not exist, it will create a new one.
+ // UpdateSigningRecord(ctx context.Context, record *SigningRecord) error
+ // DeleteSigningRecords(ctx context.Context, ownerID string, query *DeleteSigningRecordsQuery) (int64, error)
+ // LoadConditions(ctx context.Context, ownerID string, query *ConditionsQuery, h LoadConditionsFunc) error
+
+ // CreateConfiguration creates a new configuration in the database.
+ CreateConfiguration(ctx context.Context, conf *pb.Configuration) (*pb.Configuration, error)
+ // UpdateConfiguration updates an existing configuration in the database.
+ UpdateConfiguration(ctx context.Context, conf *pb.Configuration) (*pb.Configuration, error)
+ // GetConfigurations loads a configuration from the database.
+ GetConfigurations(ctx context.Context, owner string, query *pb.GetConfigurationsRequest, h GetConfigurationsFunc) error
+ // DeleteConfigurations deletes configurations from the database.
+ DeleteConfigurations(ctx context.Context, owner string, query *pb.DeleteConfigurationsRequest) (int64, error)
+
+ Close(ctx context.Context) error
+}
diff --git a/snippet-service/test/configuration.go b/snippet-service/test/configuration.go
new file mode 100644
index 000000000..d295de3de
--- /dev/null
+++ b/snippet-service/test/configuration.go
@@ -0,0 +1,145 @@
+package test
+
+import (
+ "context"
+ "strconv"
+ "testing"
+
+ "github.com/google/uuid"
+ "github.com/plgd-dev/go-coap/v3/message"
+ pkgGrpc "github.com/plgd-dev/hub/v2/pkg/net/grpc"
+ "github.com/plgd-dev/hub/v2/resource-aggregate/commands"
+ "github.com/plgd-dev/hub/v2/snippet-service/pb"
+ "github.com/plgd-dev/hub/v2/snippet-service/store"
+ hubTest "github.com/plgd-dev/hub/v2/test"
+ "github.com/plgd-dev/hub/v2/test/config"
+ oauthTest "github.com/plgd-dev/hub/v2/test/oauth-server/test"
+ "github.com/stretchr/testify/require"
+)
+
+var testConfigurationIDs = make(map[int]string)
+
+func ConfigurationID(i int) string {
+ if id, ok := testConfigurationIDs[i]; ok {
+ return id
+ }
+ id := uuid.New().String()
+ testConfigurationIDs[i] = id
+ return id
+}
+
+func ConfigurationName(i int) string {
+ return "cfg" + strconv.Itoa(i)
+}
+
+func ConfigurationOwner(i int) string {
+ return "owner" + strconv.Itoa(i)
+}
+
+func ConfigurationResources(t *testing.T, start, n int) []*pb.Configuration_Resource {
+ resources := make([]*pb.Configuration_Resource, 0, n)
+ for i := start; i < start+n; i++ {
+ resources = append(resources, &pb.Configuration_Resource{
+ Href: hubTest.TestResourceLightInstanceHref(strconv.Itoa(i)),
+ Content: &commands.Content{
+ Data: hubTest.EncodeToCbor(t, map[string]interface{}{
+ "power": i,
+ }),
+ ContentType: message.AppOcfCbor.String(),
+ CoapContentFormat: int32(message.AppOcfCbor),
+ },
+ TimeToLive: 1337,
+ })
+ }
+ return resources
+}
+
+type (
+ onCreateConfiguration = func(ctx context.Context, conf *pb.Configuration) (*pb.Configuration, error)
+ onUpdateConfiguration = func(ctx context.Context, conf *pb.Configuration) (*pb.Configuration, error)
+ calculateInitialVersionNumber = func(iteration int) uint64
+)
+
+func addConfigurations(ctx context.Context, t *testing.T, n int, calcVersion calculateInitialVersionNumber, create onCreateConfiguration, update onUpdateConfiguration) map[string]store.Configuration {
+ const numConfigs = 10
+ const numOwners = 3
+ versions := make(map[int]uint64, numConfigs)
+ owners := make(map[int]string, numConfigs)
+ configurations := make(map[string]store.Configuration)
+ for i := 0; i < n; i++ {
+ version, ok := versions[i%numConfigs]
+ if !ok {
+ version = 0
+ if calcVersion != nil {
+ version = calcVersion(i)
+ }
+ versions[i%numConfigs] = version
+ }
+ versions[i%numConfigs]++
+ owner, ok := owners[i%numConfigs]
+ if !ok {
+ owner = ConfigurationOwner(i % numOwners)
+ owners[i%numConfigs] = owner
+ }
+ confIn := &pb.Configuration{
+ Id: ConfigurationID(i % numConfigs),
+ Version: version,
+ Resources: ConfigurationResources(t, i%16, (i%5)+1),
+ Owner: owner,
+ }
+ var conf *pb.Configuration
+ var err error
+ if !ok {
+ confIn.Name = ConfigurationName(i % numConfigs)
+ conf, err = create(ctx, confIn)
+ require.NoError(t, err)
+ } else {
+ conf, err = update(ctx, confIn)
+ require.NoError(t, err)
+ }
+
+ configuration, ok := configurations[conf.GetId()]
+ if !ok {
+ configuration = store.Configuration{
+ Id: conf.GetId(),
+ Owner: conf.GetOwner(),
+ Name: conf.GetName(),
+ }
+ configurations[conf.GetId()] = configuration
+ }
+ configuration.Versions = append(configuration.Versions, store.ConfigurationVersion{
+ Version: conf.GetVersion(),
+ Resources: conf.GetResources(),
+ })
+ configurations[conf.GetId()] = configuration
+ }
+ return configurations
+}
+
+func AddConfigurationsToStore(ctx context.Context, t *testing.T, s store.Store, n int, calcVersion calculateInitialVersionNumber) map[string]store.Configuration {
+ return addConfigurations(ctx, t, n, calcVersion, s.CreateConfiguration, s.UpdateConfiguration)
+}
+
+func AddConfigurations(ctx context.Context, t *testing.T, ownerClaim string, c pb.SnippetServiceClient, n int, calcVersion calculateInitialVersionNumber) map[string]store.Configuration {
+ tokens := make(map[string]string)
+ getTokenWithOwnerClaim := func(owner string) string {
+ token, ok := tokens[owner]
+ if ok {
+ return token
+ }
+ token = oauthTest.GetAccessToken(t, config.OAUTH_SERVER_HOST, oauthTest.ClientTest, map[string]interface{}{
+ ownerClaim: owner,
+ })
+ tokens[owner] = token
+ return token
+ }
+
+ return addConfigurations(ctx, t, n, calcVersion, func(ctx context.Context, conf *pb.Configuration) (*pb.Configuration, error) {
+ ctxWithToken := pkgGrpc.CtxWithToken(ctx, getTokenWithOwnerClaim(conf.GetOwner()))
+ return c.CreateConfiguration(ctxWithToken, conf)
+ }, func(ctx context.Context, conf *pb.Configuration) (*pb.Configuration, error) {
+ ctxWithToken := pkgGrpc.CtxWithToken(ctx, getTokenWithOwnerClaim(conf.GetOwner()))
+ return c.UpdateConfiguration(ctxWithToken, conf)
+ },
+ )
+}
diff --git a/snippet-service/test/service.go b/snippet-service/test/service.go
new file mode 100644
index 000000000..8cea9d0f2
--- /dev/null
+++ b/snippet-service/test/service.go
@@ -0,0 +1,120 @@
+package test
+
+import (
+ "context"
+ "sync"
+ "time"
+
+ "github.com/plgd-dev/hub/v2/pkg/config/database"
+ "github.com/plgd-dev/hub/v2/pkg/fsnotify"
+ "github.com/plgd-dev/hub/v2/pkg/log"
+ "github.com/plgd-dev/hub/v2/pkg/mongodb"
+ "github.com/plgd-dev/hub/v2/snippet-service/service"
+ storeConfig "github.com/plgd-dev/hub/v2/snippet-service/store/config"
+ storeCqlDB "github.com/plgd-dev/hub/v2/snippet-service/store/cqldb"
+ storeMongo "github.com/plgd-dev/hub/v2/snippet-service/store/mongodb"
+ "github.com/plgd-dev/hub/v2/test/config"
+ httpTest "github.com/plgd-dev/hub/v2/test/http"
+ "github.com/stretchr/testify/require"
+ "go.opentelemetry.io/otel/trace/noop"
+)
+
+func HTTPURI(uri string) string {
+ return httpTest.HTTPS_SCHEME + config.SNIPPET_SERVICE_HTTP_HOST + uri
+}
+
+func MakeConfig(t require.TestingT) service.Config {
+ var cfg service.Config
+
+ cfg.HubID = config.HubID()
+ cfg.Log = log.MakeDefaultConfig()
+
+ cfg.APIs.GRPC = config.MakeGrpcServerConfig(config.SNIPPET_SERVICE_HOST)
+ cfg.APIs.HTTP.Addr = config.SNIPPET_SERVICE_HTTP_HOST
+ cfg.APIs.HTTP.Server = config.MakeHttpServerConfig()
+ cfg.APIs.GRPC.TLS.ClientCertificateRequired = false
+
+ cfg.Clients.OpenTelemetryCollector = config.MakeOpenTelemetryCollectorClient()
+ cfg.Clients.Storage = MakeStorageConfig()
+
+ err := cfg.Validate()
+ require.NoError(t, err)
+
+ return cfg
+}
+
+func SetUp(t require.TestingT) (tearDown func()) {
+ return New(t, MakeConfig(t))
+}
+
+func New(t require.TestingT, cfg service.Config) func() {
+ ctx := context.Background()
+ logger := log.NewLogger(cfg.Log)
+
+ fileWatcher, err := fsnotify.NewWatcher(logger)
+ require.NoError(t, err)
+
+ s, err := service.New(ctx, cfg, fileWatcher, logger)
+ require.NoError(t, err)
+
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ _ = s.Serve()
+ }()
+
+ return func() {
+ _ = s.Close()
+ wg.Wait()
+ err = fileWatcher.Close()
+ require.NoError(t, err)
+ }
+}
+
+func MakeStorageConfig() service.StorageConfig {
+ return service.StorageConfig{
+ CleanUpRecords: "0 1 * * *",
+ Embedded: storeConfig.Config{
+ // TODO: add cqldb support
+ // Use: config.ACTIVE_DATABASE(),
+ Use: database.MongoDB,
+ MongoDB: &storeMongo.Config{
+ Mongo: mongodb.Config{
+ MaxPoolSize: 16,
+ MaxConnIdleTime: time.Minute * 4,
+ URI: config.MONGODB_URI,
+ Database: "snippetService",
+ TLS: config.MakeTLSClientConfig(),
+ },
+ },
+ CqlDB: &storeCqlDB.Config{
+ Embedded: config.MakeCqlDBConfig(),
+ Table: "snippets",
+ },
+ },
+ }
+}
+
+func NewMongoStore(t require.TestingT) (*storeMongo.Store, func()) {
+ cfg := MakeConfig(t)
+ logger := log.NewLogger(cfg.Log)
+
+ fileWatcher, err := fsnotify.NewWatcher(logger)
+ require.NoError(t, err)
+
+ ctx := context.Background()
+ store, err := storeMongo.New(ctx, cfg.Clients.Storage.Embedded.MongoDB, fileWatcher, logger, noop.NewTracerProvider())
+ require.NoError(t, err)
+
+ cleanUp := func() {
+ err := store.Clear(ctx)
+ require.NoError(t, err)
+ _ = store.Close(ctx)
+
+ err = fileWatcher.Close()
+ require.NoError(t, err)
+ }
+
+ return store, cleanUp
+}
diff --git a/snippet-service/test/test.go b/snippet-service/test/test.go
new file mode 100644
index 000000000..1c913bfbd
--- /dev/null
+++ b/snippet-service/test/test.go
@@ -0,0 +1,65 @@
+package test
+
+import (
+ "testing"
+
+ "github.com/plgd-dev/hub/v2/snippet-service/pb"
+ "github.com/plgd-dev/hub/v2/snippet-service/store"
+ "github.com/plgd-dev/hub/v2/test"
+ "github.com/plgd-dev/kit/v2/codec/json"
+ "github.com/stretchr/testify/require"
+)
+
+func CmpJSON(t *testing.T, want, got interface{}) {
+ wantJson, err := json.Encode(want)
+ require.NoError(t, err)
+ gotJson, err := json.Encode(got)
+ require.NoError(t, err)
+ require.JSONEq(t, string(wantJson), string(gotJson))
+}
+
+func cmpConfigurationResources(t *testing.T, want, got []*pb.Configuration_Resource) {
+ require.Len(t, got, len(want))
+ for i := range want {
+ wantData, ok := test.DecodeCbor(t, want[i].GetContent().GetData()).(map[interface{}]interface{})
+ require.True(t, ok)
+ gotData, ok := test.DecodeCbor(t, got[i].GetContent().GetData()).(map[interface{}]interface{})
+ require.True(t, ok)
+ require.Equal(t, wantData, gotData)
+ want[i].Content.Data = nil
+ got[i].Content.Data = nil
+ }
+ CmpJSON(t, want, got)
+}
+
+func CmpConfiguration(t *testing.T, want, got *pb.Configuration) {
+ if want.GetResources() != nil && got.GetResources() != nil {
+ cmpConfigurationResources(t, want.GetResources(), got.GetResources())
+ want.Resources = nil
+ got.Resources = nil
+ }
+ CmpJSON(t, want, got)
+}
+
+func ConfigurationContains(t *testing.T, storeConf store.Configuration, conf *pb.Configuration) {
+ require.Equal(t, storeConf.Id, conf.GetId())
+ require.Equal(t, storeConf.Owner, conf.GetOwner())
+ require.Equal(t, storeConf.Name, conf.GetName())
+ for _, v := range storeConf.Versions {
+ if v.Version != conf.GetVersion() {
+ continue
+ }
+ test.CheckProtobufs(t, v.Resources, conf.GetResources(), test.RequireToCheckFunc(require.Equal))
+ return
+ }
+ require.Fail(t, "version not found")
+}
+
+func CmpStoredConfigurationMaps(t *testing.T, want, got map[string]store.Configuration) {
+ require.Len(t, got, len(want))
+ for _, v := range want {
+ gotV, ok := got[v.Id]
+ require.True(t, ok)
+ CmpJSON(t, v, gotV)
+ }
+}
diff --git a/test/config/config.go b/test/config/config.go
index b77415684..7e39c1d74 100644
--- a/test/config/config.go
+++ b/test/config/config.go
@@ -43,6 +43,8 @@ const (
RESOURCE_DIRECTORY_HOST = "localhost:20004"
CERTIFICATE_AUTHORITY_HOST = "localhost:20011"
CERTIFICATE_AUTHORITY_HTTP_HOST = "localhost:20012"
+ SNIPPET_SERVICE_HOST = "localhost:20013"
+ SNIPPET_SERVICE_HTTP_HOST = "localhost:20014"
GRPC_GW_HOST = "localhost:20005"
C2C_CONNECTOR_HOST = "localhost:20006"
C2C_CONNECTOR_DB = "cloud2cloudConnector"
diff --git a/test/http/request.go b/test/http/request.go
index a86a9faf5..668963725 100644
--- a/test/http/request.go
+++ b/test/http/request.go
@@ -102,6 +102,14 @@ func (c *RequestBuilder) DeviceId(deviceID string) *RequestBuilder {
return c
}
+func (c *RequestBuilder) ID(id string) *RequestBuilder {
+ if id == "" {
+ return c
+ }
+ c.uriParams[IDKey] = id
+ return c
+}
+
func (c *RequestBuilder) ResourceHref(resourceHref string) *RequestBuilder {
if resourceHref == "" {
return c
@@ -121,6 +129,14 @@ func (c *RequestBuilder) SubscriptionID(subscriptionID string) *RequestBuilder {
return c
}
+func (c *RequestBuilder) Version(version string) *RequestBuilder {
+ if version == "" {
+ return c
+ }
+ c.AddQuery(VersionKey, version)
+ return c
+}
+
func (c *RequestBuilder) SetQuery(value string) *RequestBuilder {
c.query = value
return c
diff --git a/test/http/uri.go b/test/http/uri.go
index f3ba99c3f..e4f221989 100644
--- a/test/http/uri.go
+++ b/test/http/uri.go
@@ -3,9 +3,12 @@ package http
const (
HTTPS_SCHEME = "https://"
- DeviceIDKey = "deviceID"
- ResourceHrefKey = "resourceHref"
- SubscriptionIDKey = "subscriptionID"
+ IDKey = "id"
+ DeviceIDKey = "deviceID"
+ ResourceHrefKey = "resourceHref"
+ SubscriptionIDKey = "subscriptionID"
+ ConfigurationIDKey = "configurationId"
+ VersionKey = "version"
ContentQueryKey = "content"
)
diff --git a/test/iotivity-lite/service/offboard_test.go b/test/iotivity-lite/service/offboard_test.go
index 36941eeb4..f8c09c2df 100644
--- a/test/iotivity-lite/service/offboard_test.go
+++ b/test/iotivity-lite/service/offboard_test.go
@@ -55,7 +55,7 @@ func TestOffboard(t *testing.T) {
require.Equal(t, 1, publishCount)
singOffCount, ok := h.CallCounter.Data[iotService.SignOffKey]
require.True(t, ok)
- require.Positive(t, singOffCount, 0)
+ require.Positive(t, singOffCount)
}
coapShutdown := coapgwTest.SetUp(t, makeHandler, validateHandler)
@@ -337,7 +337,7 @@ func TestOffboardWithSignInByRefreshToken(t *testing.T) {
require.Greater(t, signInCount, 1)
refreshCount, ok := h.CallCounter.Data[iotService.RefreshTokenKey]
require.True(t, ok)
- require.Positive(t, refreshCount, 0)
+ require.Positive(t, refreshCount)
signOffCount, ok := h.CallCounter.Data[iotService.SignOffKey]
require.True(t, ok)
require.Equal(t, 1, signOffCount)
diff --git a/test/iotivity-lite/service/republish_test.go b/test/iotivity-lite/service/republish_test.go
index 7ee0a2093..a6367aae6 100644
--- a/test/iotivity-lite/service/republish_test.go
+++ b/test/iotivity-lite/service/republish_test.go
@@ -45,7 +45,7 @@ func TestRepublishAfterRefresh(t *testing.T) {
require.Greater(t, signInCount, 1)
refreshCount, ok := h.CallCounter.Data[iotService.RefreshTokenKey]
require.True(t, ok)
- require.Positive(t, refreshCount, 0)
+ require.Positive(t, refreshCount)
publishCount, ok := h.CallCounter.Data[iotService.PublishKey]
require.True(t, ok)
require.Equal(t, 1, publishCount)