mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-09-10 12:48:29 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83360f9c89 | ||
|
|
b3b8461ea9 | ||
|
|
4ba5af3ed8 | ||
|
|
6e021e5d3f | ||
|
|
315256699a | ||
|
|
f9fdff0705 | ||
|
|
95423b6002 | ||
|
|
1b8393e9d3 | ||
|
|
0cd1efabf8 | ||
|
|
b6223069df | ||
|
|
bd931bd69c | ||
|
|
41d0365903 | ||
|
|
aa2f5d9667 | ||
|
|
60fc81c222 | ||
|
|
31bb970de9 | ||
|
|
5cf62ff817 | ||
|
|
085f9181aa | ||
|
|
31e9aaa522 | ||
|
|
36c496c230 | ||
|
|
b76f10950b | ||
|
|
f75a27e79c | ||
|
|
8bf2100c01 | ||
|
|
1f6cbaf39e | ||
|
|
fbc08a5ccf | ||
|
|
c3dc7b370f | ||
|
|
6bd81b7302 | ||
|
|
e381df016e | ||
|
|
31340e30fb | ||
|
|
c9fb429b1a | ||
|
|
d561ab1ebc | ||
|
|
11b6d7793d | ||
|
|
9d36da169b | ||
|
|
6ca9a3e1c4 | ||
|
|
3e875017a6 | ||
|
|
69b9031b2d | ||
|
|
74bf60cbd9 | ||
|
|
d9c775d250 | ||
|
|
5dba8ecc4e | ||
|
|
552e5dd289 | ||
|
|
5ad4ea4413 | ||
|
|
a023172421 | ||
|
|
aac062c9e2 | ||
|
|
d7f287c813 | ||
|
|
52b05be436 | ||
|
|
a609940cb8 | ||
|
|
3c9af15d54 | ||
|
|
a5ead45c87 | ||
|
|
96478695cf | ||
|
|
44cef0ae65 | ||
|
|
69989fea49 | ||
|
|
b7cddc215d | ||
|
|
f86da12a5e | ||
|
|
0ebbe33d3a | ||
|
|
49726b8af8 | ||
|
|
6287c9e885 | ||
|
|
217b49a3d1 | ||
|
|
4cf1331dc9 | ||
|
|
370a685482 | ||
|
|
19333cb99a | ||
|
|
bf397732e7 | ||
|
|
da0b9417cd | ||
|
|
1162e26b16 | ||
|
|
a28998e536 | ||
|
|
81572a0a24 | ||
|
|
5efe80577f | ||
|
|
c445f1fc93 | ||
|
|
70c0f4388b | ||
|
|
719bb2123c | ||
|
|
c98cca36c6 | ||
|
|
caa182cce3 | ||
|
|
0c0b6d7403 | ||
|
|
bceaf4811f | ||
|
|
5b9ec396c4 | ||
|
|
b5e8c1a268 | ||
|
|
a69786e933 | ||
|
|
613288e02a | ||
|
|
071b09c504 | ||
|
|
6bb2324233 | ||
|
|
e8d5249c93 | ||
|
|
ca347ad0e0 | ||
|
|
cff56490cb | ||
|
|
68c0dceb49 | ||
|
|
6c5e486924 | ||
|
|
29f0387323 | ||
|
|
c336e4ae34 | ||
|
|
fbc868522d | ||
|
|
882d20cc7f | ||
|
|
b189fadbff | ||
|
|
97d541d956 | ||
|
|
bdff962639 | ||
|
|
3cfc1609ae | ||
|
|
39d8979360 | ||
|
|
075bddd1cb | ||
|
|
17c27372fe | ||
|
|
9de4e24395 |
No files matched your search
+3
-3
@@ -83,11 +83,11 @@ $(PIGEON): $(BINGO_DIR)/pigeon.mod
|
||||
@echo "(re)installing $(GOBIN)/pigeon-v1.3.0"
|
||||
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=pigeon.mod -o=$(GOBIN)/pigeon-v1.3.0 "github.com/mna/pigeon"
|
||||
|
||||
PROTOC_GEN_GO_GRPC := $(GOBIN)/protoc-gen-go-grpc-v1.81.0
|
||||
PROTOC_GEN_GO_GRPC := $(GOBIN)/protoc-gen-go-grpc-v1.6.2
|
||||
$(PROTOC_GEN_GO_GRPC): $(BINGO_DIR)/protoc-gen-go-grpc.mod
|
||||
@# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies.
|
||||
@echo "(re)installing $(GOBIN)/protoc-gen-go-grpc-v1.81.0"
|
||||
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=protoc-gen-go-grpc.mod -o=$(GOBIN)/protoc-gen-go-grpc-v1.81.0 "google.golang.org/grpc/cmd/protoc-gen-go-grpc"
|
||||
@echo "(re)installing $(GOBIN)/protoc-gen-go-grpc-v1.6.2"
|
||||
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=protoc-gen-go-grpc.mod -o=$(GOBIN)/protoc-gen-go-grpc-v1.6.2 "google.golang.org/grpc/cmd/protoc-gen-go-grpc"
|
||||
|
||||
PROTOC_GEN_GO := $(GOBIN)/protoc-gen-go-v1.28.1
|
||||
$(PROTOC_GEN_GO): $(BINGO_DIR)/protoc-gen-go.mod
|
||||
|
||||
@@ -30,7 +30,7 @@ MUTAGEN="${GOBIN}/mutagen-v0.18.1"
|
||||
|
||||
PIGEON="${GOBIN}/pigeon-v1.3.0"
|
||||
|
||||
PROTOC_GEN_GO_GRPC="${GOBIN}/protoc-gen-go-grpc-v1.81.0"
|
||||
PROTOC_GEN_GO_GRPC="${GOBIN}/protoc-gen-go-grpc-v1.6.2"
|
||||
|
||||
PROTOC_GEN_GO="${GOBIN}/protoc-gen-go-v1.28.1"
|
||||
|
||||
|
||||
@@ -4,11 +4,12 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 2
|
||||
open-pull-requests-limit: 4
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/services/idp"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 2
|
||||
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 4
|
||||
cooldown:
|
||||
default-days: 1
|
||||
@@ -11,6 +11,7 @@ protoc-deps: $(BINGO)
|
||||
@cd ../.. && GOPATH="" GOBIN=".bingo" $(BINGO) get -l github.com/owncloud/protoc-gen-microweb
|
||||
@cd ../.. && GOPATH="" GOBIN=".bingo" $(BINGO) get -l github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2
|
||||
@cd ../.. && GOPATH="" GOBIN=".bingo" $(BINGO) get -l github.com/favadi/protoc-go-inject-tag
|
||||
@cd ../.. && GOPATH="" GOBIN=".bingo" $(BINGO) get -l google.golang.org/grpc/cmd/protoc-gen-go-grpc
|
||||
|
||||
.PHONY: buf-generate
|
||||
buf-generate: $(SHA1_LOCK_FILE)
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# The test runner source for UI tests
|
||||
WEB_COMMITID=9de6541fc8bb116fcc78b09da697563faa5e61bd
|
||||
WEB_COMMITID=2cee98bb27fab223fc4ce2455f5483ce7767ff1a
|
||||
WEB_BRANCH=main
|
||||
|
||||
+14
-14
@@ -109,10 +109,10 @@ config = {
|
||||
},
|
||||
"basic2": {
|
||||
"suites": [
|
||||
"apiDownloads",
|
||||
"apiDepthInfinity",
|
||||
"apiLocks",
|
||||
"apiActivities",
|
||||
"apiArchiver",
|
||||
"apiContract",
|
||||
"apiCors",
|
||||
"apiAsyncUpload",
|
||||
],
|
||||
"skip": False,
|
||||
},
|
||||
@@ -121,7 +121,7 @@ config = {
|
||||
"apiSettings",
|
||||
],
|
||||
"skip": False,
|
||||
"withRemotePhp": [True],
|
||||
"withRemotePhp": [False],
|
||||
"emailNeeded": True,
|
||||
"extraTestEnvironment": {
|
||||
"EMAIL_HOST": "email",
|
||||
@@ -144,14 +144,14 @@ config = {
|
||||
#"collaborativePosix",
|
||||
],
|
||||
"skip": False,
|
||||
"withRemotePhp": [True],
|
||||
"withRemotePhp": [False],
|
||||
},
|
||||
"graphUserGroup": {
|
||||
"suites": [
|
||||
"apiGraphUserGroup",
|
||||
],
|
||||
"skip": False,
|
||||
"withRemotePhp": [True],
|
||||
"withRemotePhp": [False],
|
||||
},
|
||||
"spaces": {
|
||||
"suites": [
|
||||
@@ -209,7 +209,7 @@ config = {
|
||||
"apiNotification",
|
||||
],
|
||||
"skip": False,
|
||||
"withRemotePhp": [True],
|
||||
"withRemotePhp": [False],
|
||||
"emailNeeded": True,
|
||||
"extraTestEnvironment": {
|
||||
"EMAIL_HOST": "email",
|
||||
@@ -251,7 +251,7 @@ config = {
|
||||
"apiOcm",
|
||||
],
|
||||
"skip": False,
|
||||
"withRemotePhp": [True],
|
||||
"withRemotePhp": [False],
|
||||
"federationServer": True,
|
||||
"emailNeeded": True,
|
||||
"extraTestEnvironment": {
|
||||
@@ -287,14 +287,14 @@ config = {
|
||||
"apiAuthApp",
|
||||
],
|
||||
"skip": False,
|
||||
"withRemotePhp": [True],
|
||||
"withRemotePhp": [False],
|
||||
},
|
||||
"cliCommands": {
|
||||
"suites": [
|
||||
"cliCommands",
|
||||
],
|
||||
"skip": False,
|
||||
"withRemotePhp": [True],
|
||||
"withRemotePhp": [False],
|
||||
"antivirusNeeded": True,
|
||||
"extraServerEnvironment": {
|
||||
"ANTIVIRUS_SCANNER_TYPE": "clamav",
|
||||
@@ -309,7 +309,7 @@ config = {
|
||||
"apiTenancy",
|
||||
],
|
||||
"skip": False,
|
||||
"withRemotePhp": [True],
|
||||
"withRemotePhp": [False],
|
||||
"ldapNeeded": True,
|
||||
"extraTestEnvironment": {
|
||||
"USE_PREPARED_LDAP_USERS": True,
|
||||
@@ -1199,7 +1199,7 @@ def localApiTestPipeline(ctx):
|
||||
"federationServer": False,
|
||||
"collaborationServiceNeeded": False,
|
||||
"extraCollaborationEnvironment": {},
|
||||
"withRemotePhp": [True],
|
||||
"withRemotePhp": [False],
|
||||
"enableWatchFs": [False],
|
||||
"ldapNeeded": False,
|
||||
}
|
||||
@@ -1316,7 +1316,7 @@ def localApiTest(suites, storage = "decomposed", extra_environment = {}, with_re
|
||||
|
||||
def coreApiTestPipeline(ctx):
|
||||
defaults = {
|
||||
"withRemotePhp": [True],
|
||||
"withRemotePhp": [False],
|
||||
"enableWatchFs": [False],
|
||||
"storages": ["posix"],
|
||||
"numberOfParts": 7,
|
||||
|
||||
@@ -1,5 +1,43 @@
|
||||
# Changelog
|
||||
|
||||
## [7.1.0](https://github.com/opencloud-eu/opencloud/releases/tag/v7.1.0) - 2026-06-02
|
||||
|
||||
### ❤️ Thanks to all contributors! ❤️
|
||||
|
||||
@ScharfViktor, @aduffeck, @dragonchaser, @kulmann, @micbar, @rhafer
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- Prevent personal space creation for service- and lightweight users [[#2876](https://github.com/opencloud-eu/opencloud/pull/2876)]
|
||||
- chore: bump reva to 2.46.1 [[#2869](https://github.com/opencloud-eu/opencloud/pull/2869)]
|
||||
- fix: Send SSE events for SpaceCreated/-Disabled/-Deleted [[#2851](https://github.com/opencloud-eu/opencloud/pull/2851)]
|
||||
- Only try to limit search to spaces if there's a space id to limit to [[#2834](https://github.com/opencloud-eu/opencloud/pull/2834)]
|
||||
- fix(init): Only log admin password if it was generated [[#2839](https://github.com/opencloud-eu/opencloud/pull/2839)]
|
||||
- fix: translations for activities and others [[#2836](https://github.com/opencloud-eu/opencloud/pull/2836)]
|
||||
- fix-2824. run tests without remote.php [[#2826](https://github.com/opencloud-eu/opencloud/pull/2826)]
|
||||
|
||||
### 📈 Enhancement
|
||||
|
||||
- chore: bump web to v7.1.0 [[#2870](https://github.com/opencloud-eu/opencloud/pull/2870)]
|
||||
|
||||
### 📚 Documentation
|
||||
|
||||
- docs(adr): Remove erroneous mention of kanidm [[#2783](https://github.com/opencloud-eu/opencloud/pull/2783)]
|
||||
|
||||
### 📦️ Dependencies
|
||||
|
||||
- build(deps): bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc from 1.43.0 to 1.44.0 [[#2855](https://github.com/opencloud-eu/opencloud/pull/2855)]
|
||||
- build(deps-dev): bump license-checker-rseidelsohn from 4.4.2 to 5.0.1 in /services/idp [[#2854](https://github.com/opencloud-eu/opencloud/pull/2854)]
|
||||
- build(deps-dev): bump cldr from 7.9.0 to 8.0.0 in /services/idp [[#2853](https://github.com/opencloud-eu/opencloud/pull/2853)]
|
||||
- build(deps): bump i18next from 26.1.0 to 26.3.0 in /services/idp [[#2849](https://github.com/opencloud-eu/opencloud/pull/2849)]
|
||||
- build(deps-dev): bump sass-loader from 16.0.8 to 17.0.0 in /services/idp [[#2845](https://github.com/opencloud-eu/opencloud/pull/2845)]
|
||||
- build(deps): bump google.golang.org/grpc from 1.80.0 to 1.81.1 [[#2848](https://github.com/opencloud-eu/opencloud/pull/2848)]
|
||||
- build(deps): bump go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc from 0.68.0 to 0.69.0 [[#2847](https://github.com/opencloud-eu/opencloud/pull/2847)]
|
||||
- build(deps-dev): bump typescript from 5.9.3 to 6.0.3 in /services/idp [[#2846](https://github.com/opencloud-eu/opencloud/pull/2846)]
|
||||
- build(deps-dev): bump postcss-loader from 4.3.0 to 8.2.1 in /services/idp [[#2830](https://github.com/opencloud-eu/opencloud/pull/2830)]
|
||||
- build(deps): bump github.com/riandyrn/otelchi from 0.12.2 to 0.12.3 [[#2814](https://github.com/opencloud-eu/opencloud/pull/2814)]
|
||||
- build(deps-dev): bump workbox-webpack-plugin from 7.4.0 to 7.4.1 in /services/idp [[#2781](https://github.com/opencloud-eu/opencloud/pull/2781)]
|
||||
|
||||
## [7.0.0](https://github.com/opencloud-eu/opencloud/releases/tag/v7.0.0) - 2026-05-21
|
||||
|
||||
### ❤️ Thanks to all contributors! ❤️
|
||||
|
||||
@@ -18,7 +18,7 @@ OpenCloud with various existing identity providers. For example:
|
||||
- Authentik basically creates a different issuer URL for each client. As OpenCloud
|
||||
can only work with a single issuer URL, all OpenCloud clients need to use the
|
||||
same client id to work with Authentik.
|
||||
- Some IDPs (kanidm) are not able to work with user-supplied client ids. They generate
|
||||
- Some IDPs are not able to work with user-supplied client ids. They generate
|
||||
client ids automatically and do not allow to specify them manually.
|
||||
- To make features like automatic role assignment work, clients need to request
|
||||
specific scopes, depending on which exact IDP is used.
|
||||
|
||||
@@ -42,14 +42,14 @@ require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gookit/config/v2 v2.2.7
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0
|
||||
github.com/invopop/validation v0.8.0
|
||||
github.com/jellydator/ttlcache/v2 v2.11.1
|
||||
github.com/jellydator/ttlcache/v3 v3.4.0
|
||||
github.com/jinzhu/now v1.1.5
|
||||
github.com/justinas/alice v1.2.0
|
||||
github.com/kovidgoyal/imaging v1.8.20
|
||||
github.com/leonelquinteros/gotext v1.7.2
|
||||
github.com/leonelquinteros/gotext v1.7.3-0.20260422134830-b012b4ccae69
|
||||
github.com/libregraph/idm v0.5.0
|
||||
github.com/libregraph/lico v0.66.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
@@ -64,14 +64,14 @@ require (
|
||||
github.com/open-policy-agent/opa v1.15.2
|
||||
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d
|
||||
github.com/opencloud-eu/reva/v2 v2.46.0
|
||||
github.com/opencloud-eu/reva/v2 v2.46.2-0.20260602061404-0e975e5456eb
|
||||
github.com/opensearch-project/opensearch-go/v4 v4.6.0
|
||||
github.com/orcaman/concurrent-map v1.0.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pkg/xattr v0.4.12
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
github.com/r3labs/sse/v2 v2.10.0
|
||||
github.com/riandyrn/otelchi v0.12.2
|
||||
github.com/riandyrn/otelchi v0.12.3
|
||||
github.com/rogpeppe/go-internal v1.14.1
|
||||
github.com/rs/cors v1.11.1
|
||||
github.com/rs/zerolog v1.35.0
|
||||
@@ -94,24 +94,24 @@ require (
|
||||
github.com/xhit/go-simple-mail/v2 v2.16.0
|
||||
go-micro.dev/v4 v4.11.0
|
||||
go.etcd.io/bbolt v1.4.3
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0
|
||||
go.opentelemetry.io/contrib/zpages v0.68.0
|
||||
go.opentelemetry.io/otel v1.43.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0
|
||||
go.opentelemetry.io/otel v1.44.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0
|
||||
go.opentelemetry.io/otel/sdk v1.43.0
|
||||
go.opentelemetry.io/otel/trace v1.43.0
|
||||
golang.org/x/crypto v0.50.0
|
||||
go.opentelemetry.io/otel/sdk v1.44.0
|
||||
go.opentelemetry.io/otel/trace v1.44.0
|
||||
golang.org/x/crypto v0.51.0
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f
|
||||
golang.org/x/image v0.40.0
|
||||
golang.org/x/net v0.53.0
|
||||
golang.org/x/net v0.55.0
|
||||
golang.org/x/oauth2 v0.36.0
|
||||
golang.org/x/sync v0.20.0
|
||||
golang.org/x/term v0.42.0
|
||||
golang.org/x/term v0.43.0
|
||||
golang.org/x/text v0.37.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9
|
||||
google.golang.org/grpc v1.80.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa
|
||||
google.golang.org/grpc v1.81.1
|
||||
google.golang.org/protobuf v1.36.11
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
@@ -382,19 +382,19 @@ require (
|
||||
go.etcd.io/etcd/client/v3 v3.6.11 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.44.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/mod v0.35.0 // indirect
|
||||
golang.org/x/sys v0.43.0 // indirect
|
||||
golang.org/x/sys v0.45.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
golang.org/x/tools v0.44.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
|
||||
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
|
||||
@@ -613,8 +613,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vb
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
@@ -756,8 +756,8 @@ github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvf
|
||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/leonelquinteros/gotext v1.7.2 h1:bDPndU8nt+/kRo1m4l/1OXiiy2v7Z7dfPQ9+YP7G1Mc=
|
||||
github.com/leonelquinteros/gotext v1.7.2/go.mod h1:9/haCkm5P7Jay1sxKDGJ5WIg4zkz8oZKw4ekNpALob8=
|
||||
github.com/leonelquinteros/gotext v1.7.3-0.20260422134830-b012b4ccae69 h1:ZLo0bXX17iUZpb/Sp7N+1uK4Jb7dl0fm5x5ETfzYSvY=
|
||||
github.com/leonelquinteros/gotext v1.7.3-0.20260422134830-b012b4ccae69/go.mod h1:ksG5iXViKefoupjy+0qQjAVoaDnylnQ1ejWl9g14wh8=
|
||||
github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA=
|
||||
github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
|
||||
github.com/lestrrat-go/dsig v1.0.0 h1:OE09s2r9Z81kxzJYRn07TFM9XA4akrUdoMwr0L8xj38=
|
||||
@@ -952,8 +952,8 @@ github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9 h1:dIft
|
||||
github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9/go.mod h1:JWyDC6H+5oZRdUJUgKuaye+8Ph5hEs6HVzVoPKzWSGI=
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d h1:JcqGDiyrcaQwVyV861TUyQgO7uEmsjkhfm7aQd84dOw=
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q=
|
||||
github.com/opencloud-eu/reva/v2 v2.46.0 h1:wYUHIHOsLP9vad/K19/52RRfoooeHUGY4BSeowNrbpY=
|
||||
github.com/opencloud-eu/reva/v2 v2.46.0/go.mod h1:fWAzVpZlJQEY/qeIrd9d3U+LpqY9JGsjJ2dc0a1jCEs=
|
||||
github.com/opencloud-eu/reva/v2 v2.46.2-0.20260602061404-0e975e5456eb h1:7LXIqaeVQqOghfTw44kyLSak+cR0TIBl55M4+Tb08f8=
|
||||
github.com/opencloud-eu/reva/v2 v2.46.2-0.20260602061404-0e975e5456eb/go.mod h1:V1bwCQXM1pGmswtjdSJVIweA9Vv5EmtUZr/7Cm5086A=
|
||||
github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4 h1:l2oB/RctH+t8r7QBj5p8thfEHCM/jF35aAY3WQ3hADI=
|
||||
github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
@@ -1065,8 +1065,8 @@ github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKc
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/riandyrn/otelchi v0.12.2 h1:6QhGv0LVw/dwjtPd12mnNrl0oEQF4ZAlmHcnlTYbeAg=
|
||||
github.com/riandyrn/otelchi v0.12.2/go.mod h1:weZZeUJURvtCcbWsdb7Y6F8KFZGedJlSrgUjq9VirV8=
|
||||
github.com/riandyrn/otelchi v0.12.3 h1:KW9gA+97d6mExk8vbh0FRwb2biUvpyYlc8YuxP1Oap0=
|
||||
github.com/riandyrn/otelchi v0.12.3/go.mod h1:weZZeUJURvtCcbWsdb7Y6F8KFZGedJlSrgUjq9VirV8=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
@@ -1300,28 +1300,28 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0 h1:2yEATaop1/a1I4psnSLgWVPLWwCzkqWakgJy7xTDVy0=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0/go.mod h1:D7J12YRapIekYyPWgGPlA/23pRmpSEZC5xJC/TTLI9U=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
|
||||
go.opentelemetry.io/contrib/zpages v0.68.0 h1:H5yrUwxPrbvhzdBxjQD+VXMtPjIBfp8NWNVvQT8E30M=
|
||||
go.opentelemetry.io/contrib/zpages v0.68.0/go.mod h1:sZGctYYO4UOHItj9bx3F+t/s+u1Fv8CHCJ5s2eR2cjU=
|
||||
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk=
|
||||
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
||||
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 h1:4YsVu3B8+3qtWYYrsUYgn0OG78pN0rnNPRGX4SbokQI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0/go.mod h1:+wnlSn0mD1ADVMe3v9Z/WIaiz6q6gL2J/ejaAmdmv80=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 h1:qazEJlUOQzhCpzQpFETGby7EdqjI1wsd0W+6Gg1SCTU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0/go.mod h1:fOD2Yefuxixkx3ahVNf0O/PERb6r4OlbxfATVnYvzCo=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 h1:mS47AX77OtFfKG4vtp+84kuGSFZHTyxtXIN269vChY0=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0/go.mod h1:PJnsC41lAGncJlPUniSwM81gc80GkgWJWr3cu2nKEtU=
|
||||
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
|
||||
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
|
||||
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
|
||||
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
@@ -1362,8 +1362,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
|
||||
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -1453,8 +1453,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
|
||||
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
|
||||
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
|
||||
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1560,8 +1560,8 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -1570,8 +1570,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
|
||||
golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
|
||||
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1711,10 +1711,10 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM=
|
||||
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d h1:wT2n40TBqFY6wiwazVK9/iTWbsQrgk5ZfCSVFLO9LQA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
@@ -1730,8 +1730,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||
google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
|
||||
google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
|
||||
google.golang.org/grpc/examples v0.0.0-20211102180624-670c133e568e h1:m7aQHHqd0q89mRwhwS9Bx2rjyl/hsFAeta+uGrHsQaU=
|
||||
google.golang.org/grpc/examples v0.0.0-20211102180624-670c133e568e/go.mod h1:gID3PKrg7pWKntu9Ss6zTLJ0ttC0X9IHgREOCZwbCVU=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
|
||||
@@ -35,9 +35,10 @@ func InitCommand(_ *config.Config) *cobra.Command {
|
||||
}
|
||||
forceOverwriteFlag := viper.GetBool("force-overwrite")
|
||||
diffFlag, _ := cmd.Flags().GetBool("diff")
|
||||
quietFlag, _ := cmd.Flags().GetBool("quiet")
|
||||
configPathFlag := viper.GetString("config-path")
|
||||
adminPasswordFlag := viper.GetString("admin-password")
|
||||
err := ocinit.CreateConfig(insecure, forceOverwriteFlag, diffFlag, configPathFlag, adminPasswordFlag)
|
||||
err := ocinit.CreateConfig(insecure, forceOverwriteFlag, diffFlag, configPathFlag, adminPasswordFlag, quietFlag)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create config: %s", err)
|
||||
}
|
||||
@@ -49,6 +50,7 @@ func InitCommand(_ *config.Config) *cobra.Command {
|
||||
_ = viper.BindPFlag("insecure", initCmd.Flags().Lookup("insecure"))
|
||||
|
||||
initCmd.Flags().BoolP("diff", "d", false, "Show the difference between the current config and the new one")
|
||||
initCmd.Flags().BoolP("quiet", "q", false, "Work quietly. Surpresses and non-error message")
|
||||
|
||||
initCmd.Flags().BoolP("force-overwrite", "f", false, "Force overwrite existing config file")
|
||||
_ = viper.BindEnv("force-overwrite", "OC_FORCE_CONFIG_OVERWRITE")
|
||||
|
||||
@@ -10,8 +10,18 @@ import (
|
||||
|
||||
"github.com/opencloud-eu/opencloud/opencloud/pkg/register"
|
||||
"github.com/opencloud-eu/opencloud/pkg/config"
|
||||
"github.com/opencloud-eu/opencloud/pkg/config/configlog"
|
||||
"github.com/opencloud-eu/opencloud/pkg/config/parser"
|
||||
storageUsersParser "github.com/opencloud-eu/opencloud/services/storage-users/pkg/config/parser"
|
||||
"github.com/opencloud-eu/opencloud/services/storage-users/pkg/event"
|
||||
"github.com/opencloud-eu/opencloud/services/storage-users/pkg/revaconfig"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/events"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/ignore"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/options"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/registry"
|
||||
|
||||
"github.com/pkg/xattr"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/theckman/yacspin"
|
||||
"github.com/vmihailenco/msgpack/v5"
|
||||
@@ -28,8 +38,13 @@ const (
|
||||
var (
|
||||
spinner *yacspin.Spinner
|
||||
restartRequired = false
|
||||
ignorer *ignore.Ignorer
|
||||
)
|
||||
|
||||
type IDCacher interface {
|
||||
WarmupIDCache(root string, assimilate, onlyDirty bool) error
|
||||
}
|
||||
|
||||
// EntryInfo holds information about a directory entry.
|
||||
type EntryInfo struct {
|
||||
Path string
|
||||
@@ -46,6 +61,7 @@ func PosixfsCommand(cfg *config.Config) *cobra.Command {
|
||||
}
|
||||
|
||||
posixCmd.AddCommand(consistencyCmd(cfg))
|
||||
posixCmd.AddCommand(scanCmd(cfg))
|
||||
|
||||
return posixCmd
|
||||
}
|
||||
@@ -54,6 +70,73 @@ func init() {
|
||||
register.AddCommand(PosixfsCommand)
|
||||
}
|
||||
|
||||
// scanCmd performs a posixfs id cache warmup scan
|
||||
func scanCmd(ocCfg *config.Config) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "scan",
|
||||
Short: "Perform a filesystem scan and update the ID and filemetadata cache",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
if err := parser.ParseConfig(ocCfg, true); err != nil {
|
||||
return configlog.ReturnError(err)
|
||||
}
|
||||
|
||||
// Parse storage users config
|
||||
ocCfg.StorageUsers.Commons = ocCfg.Commons
|
||||
|
||||
return configlog.ReturnFatal(storageUsersParser.ParseConfig(ocCfg.StorageUsers))
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg := ocCfg.StorageUsers
|
||||
if cfg.Driver != "posix" {
|
||||
fmt.Fprintf(os.Stderr, "This command is only available when using the 'posix' driver. Current driver: '%s'\n", cfg.Driver)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// We want to initialize the driver but disable scanfs on boot, so we can trigger it manually afterwards
|
||||
drivers := revaconfig.StorageProviderDrivers(cfg)
|
||||
drivers["posix"] = revaconfig.Posix(cfg, false, false)
|
||||
|
||||
var fsStream events.Stream
|
||||
var err error
|
||||
fsStream, err = event.NewStream(cfg)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to create event stream for posix driver: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
f, ok := registry.NewFuncs["posix"]
|
||||
if !ok {
|
||||
fmt.Fprintf(os.Stderr, "posix driver not found in registry\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fs, err := f(drivers["posix"].(map[string]any), fsStream, nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to initialize filesystem driver '%s': %v\n", cfg.Driver, err)
|
||||
return err
|
||||
}
|
||||
|
||||
cacher, ok := fs.(IDCacher)
|
||||
if !ok {
|
||||
fmt.Fprintf(os.Stderr, "The posix driver does not expose WarmupIDCache.\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Println("Starting posixfs scan...")
|
||||
err = cacher.WarmupIDCache(cfg.Drivers.Posix.Root, true, false)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Scan failed: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Scan completed successfully.")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// consistencyCmd returns a command to check the consistency of the posixfs storage.
|
||||
func consistencyCmd(cfg *config.Config) *cobra.Command {
|
||||
consCmd := &cobra.Command{
|
||||
@@ -74,6 +157,12 @@ func checkPosixfsConsistency(cmd *cobra.Command, cfg *config.Config) error {
|
||||
rootPath, _ := cmd.Flags().GetString("root")
|
||||
indexesPath := filepath.Join(rootPath, "indexes")
|
||||
|
||||
opt, _ := options.New(map[string]interface{}{
|
||||
"root": rootPath,
|
||||
})
|
||||
log := zerolog.Nop()
|
||||
ignorer = ignore.NewIgnorer(opt, &log)
|
||||
|
||||
_, err := os.Stat(indexesPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
@@ -130,6 +219,7 @@ func checkSpaces(basePath string) {
|
||||
}
|
||||
|
||||
func checkSpace(spacePath string) {
|
||||
spinner.Message("")
|
||||
spinner.Suffix(fmt.Sprintf(" Checking space '%s'", spacePath))
|
||||
|
||||
info, err := os.Stat(spacePath)
|
||||
@@ -149,10 +239,11 @@ func checkSpace(spacePath string) {
|
||||
}
|
||||
|
||||
checkSpaceID(spacePath)
|
||||
checkNodeIDs(spacePath)
|
||||
}
|
||||
|
||||
func checkSpaceID(spacePath string) {
|
||||
spinner.Message("checking space ID uniqueness")
|
||||
spinner.Message(" - checking space ID uniqueness")
|
||||
|
||||
entries, uniqueIDs, oldestEntry, err := gatherAttributes(spacePath)
|
||||
if err != nil {
|
||||
@@ -161,7 +252,6 @@ func checkSpaceID(spacePath string) {
|
||||
}
|
||||
|
||||
if len(entries) == 0 {
|
||||
logSuccess("(empty space)")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -199,8 +289,65 @@ func checkSpaceID(spacePath string) {
|
||||
}
|
||||
fixSpaceID(spacePath, obsoleteIDs, targetID, entries)
|
||||
spinner.Unpause()
|
||||
} else {
|
||||
logSuccess("")
|
||||
}
|
||||
}
|
||||
|
||||
func walkParentIDs(dir string, parentID string) int {
|
||||
fixes := 0
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
logFailure("Error reading directory '%s': %v", dir, err)
|
||||
return 0
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
fullPath := filepath.Join(dir, entry.Name())
|
||||
|
||||
if ignorer.IsIgnored(fullPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
actualParentID, err := xattr.Get(fullPath, parentIDAttrName)
|
||||
if err != nil || string(actualParentID) != parentID {
|
||||
err = xattr.Set(fullPath, parentIDAttrName, []byte(parentID))
|
||||
if err != nil {
|
||||
logFailure("Failed to fix parent ID for '%s': %v", fullPath, err)
|
||||
} else {
|
||||
spinner.Pause()
|
||||
fmt.Printf("\n + Fixed parent ID for '%s'\n", fullPath)
|
||||
spinner.Unpause()
|
||||
fixes++
|
||||
restartRequired = true
|
||||
}
|
||||
}
|
||||
|
||||
if entry.IsDir() {
|
||||
nodeID, err := xattr.Get(fullPath, idAttrName)
|
||||
if err != nil || len(nodeID) == 0 {
|
||||
logFailure("Directory '%s' missing '%s', skipping its children", fullPath, idAttrName)
|
||||
continue
|
||||
}
|
||||
walkParentIDs(fullPath, string(nodeID))
|
||||
}
|
||||
}
|
||||
return fixes
|
||||
}
|
||||
|
||||
func checkNodeIDs(spacePath string) {
|
||||
spinner.Message(" - checking parent IDs")
|
||||
|
||||
rootID, err := xattr.Get(spacePath, idAttrName)
|
||||
if err != nil || len(rootID) == 0 {
|
||||
logFailure("Space root '%s' missing '%s' attribute", spacePath, idAttrName)
|
||||
return
|
||||
}
|
||||
|
||||
fixes := walkParentIDs(spacePath, string(rootID))
|
||||
|
||||
if fixes > 0 {
|
||||
spinner.Pause()
|
||||
fmt.Printf("\n ✓ Fixed %d incorrect parent IDs in %s\n", fixes, filepath.Base(spacePath))
|
||||
spinner.Unpause()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,6 +392,9 @@ func gatherAttributes(path string) ([]EntryInfo, map[string]struct{}, EntryInfo,
|
||||
|
||||
for _, entry := range dirEntries {
|
||||
fullPath := filepath.Join(path, entry.Name())
|
||||
if ignorer.IsIgnored(fullPath) {
|
||||
continue
|
||||
}
|
||||
info, err := os.Stat(fullPath)
|
||||
if err != nil {
|
||||
fmt.Printf(" - Warning: could not stat %s: %v\n", entry.Name(), err)
|
||||
@@ -349,7 +499,7 @@ func updateOwnerIndexFile(basePath string, obsoleteIDs []string) error {
|
||||
return fmt.Errorf("failed to write updated index file: %w", err)
|
||||
}
|
||||
|
||||
logSuccess("Successfully removed %d item(s) and saved index file.\n", itemsRemoved)
|
||||
fmt.Printf(" ✓ Successfully removed %d item(s) and saved index file.\n", itemsRemoved)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -368,13 +518,7 @@ func removeAttributes(path string) error {
|
||||
}
|
||||
|
||||
func logFailure(message string, args ...any) {
|
||||
spinner.StopFailMessage(fmt.Sprintf(message, args...))
|
||||
spinner.StopFailMessage(fmt.Sprintf("\n"+message, args...))
|
||||
spinner.StopFail()
|
||||
spinner.Start()
|
||||
}
|
||||
|
||||
func logSuccess(message string, args ...any) {
|
||||
spinner.StopMessage(fmt.Sprintf(message, args...))
|
||||
spinner.Stop()
|
||||
spinner.Start()
|
||||
}
|
||||
@@ -47,15 +47,22 @@ func backupOpenCloudConfigFile(configPath string) (string, error) {
|
||||
}
|
||||
|
||||
// printBanner prints the generated opencloud config banner.
|
||||
func printBanner(targetPath, ocAdminServicePassword, targetBackupConfig string) {
|
||||
func printBanner(targetPath, ocAdminServicePassword string, adminPWgenerated bool, targetBackupConfig string) {
|
||||
fmt.Printf(
|
||||
"\n=========================================\n"+
|
||||
" generated OpenCloud Config\n"+
|
||||
"=========================================\n"+
|
||||
" configpath : %s\n"+
|
||||
" user : admin\n"+
|
||||
" password : %s\n\n",
|
||||
targetPath, ocAdminServicePassword)
|
||||
" configpath : %s\n",
|
||||
targetPath,
|
||||
)
|
||||
if adminPWgenerated {
|
||||
fmt.Printf(" user : admin\n"+
|
||||
" password : %s\n",
|
||||
ocAdminServicePassword,
|
||||
)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
if targetBackupConfig != "" {
|
||||
fmt.Printf("\n=========================================\n"+
|
||||
"An older config file has been backed up to\n %s\n\n",
|
||||
@@ -64,13 +71,15 @@ func printBanner(targetPath, ocAdminServicePassword, targetBackupConfig string)
|
||||
}
|
||||
|
||||
// writeConfig writes the config to the target path and prints a banner
|
||||
func writeConfig(configPath, ocAdminServicePassword, targetBackupConfig string, yamlOutput []byte) error {
|
||||
func writeConfig(configPath, ocAdminServicePassword, targetBackupConfig string, yamlOutput []byte, adminPWgenerated, quiet bool) error {
|
||||
targetPath := path.Join(configPath, configFilename)
|
||||
err := os.WriteFile(targetPath, yamlOutput, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
printBanner(targetPath, ocAdminServicePassword, targetBackupConfig)
|
||||
if !quiet {
|
||||
printBanner(targetPath, ocAdminServicePassword, adminPWgenerated, targetBackupConfig)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ var (
|
||||
)
|
||||
|
||||
// CreateConfig creates a config file with random passwords at configPath
|
||||
func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword string) error {
|
||||
func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword string, quiet bool) error {
|
||||
if diff && forceOverwrite {
|
||||
return fmt.Errorf("diff and force-overwrite flags are mutually exclusive")
|
||||
}
|
||||
@@ -69,6 +69,7 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword
|
||||
idmServicePassword, idpServicePassword, ocAdminServicePassword, revaServicePassword string
|
||||
tokenManagerJwtSecret, collaborationWOPISecret, machineAuthAPIKey, systemUserAPIKey string
|
||||
revaTransferSecret, thumbnailsTransferSecret, serviceAccountSecret, urlSigningSecret string
|
||||
adminPasswdwordGenerated bool
|
||||
)
|
||||
|
||||
if diff {
|
||||
@@ -123,6 +124,7 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not generate random password for opencloud admin: %s", err)
|
||||
}
|
||||
adminPasswdwordGenerated = true
|
||||
}
|
||||
|
||||
revaServicePassword, err = generators.GenerateRandomPassword(passwordLength)
|
||||
@@ -311,5 +313,5 @@ func CreateConfig(insecure, forceOverwrite, diff bool, configPath, adminPassword
|
||||
if diff {
|
||||
return writePatch(configPath, yamlOutput)
|
||||
}
|
||||
return writeConfig(configPath, ocAdminServicePassword, targetBackupConfig, yamlOutput)
|
||||
return writeConfig(configPath, ocAdminServicePassword, targetBackupConfig, yamlOutput, adminPasswdwordGenerated, quiet)
|
||||
}
|
||||
+1
-1
@@ -54,7 +54,7 @@ func NewTranslatorFromCommonConfig(defaultLocale string, domain string, path str
|
||||
|
||||
// Translate translates a string to the locale
|
||||
func (t Translator) Translate(str, locale string) string {
|
||||
return t.Locale(locale).Get("%s", str)
|
||||
return t.Locale(locale).Get(str)
|
||||
}
|
||||
|
||||
// Locale returns the gotext.Locale, use `.Get` method to translate strings
|
||||
|
||||
@@ -34,7 +34,7 @@ var (
|
||||
// LatestTag is the latest released version plus the dev meta version.
|
||||
// Will be overwritten by the release pipeline
|
||||
// Needs a manual change for every tagged release
|
||||
LatestTag = "7.0.0+dev"
|
||||
LatestTag = "7.1.0+dev"
|
||||
|
||||
// Date indicates the build date.
|
||||
// This has been removed, it looks like you can only replace static strings with recent go versions
|
||||
|
||||
@@ -34,6 +34,9 @@ var _registeredEvents = []events.Unmarshaller{
|
||||
events.FileLocked{},
|
||||
events.FileUnlocked{},
|
||||
events.FileTouched{},
|
||||
events.SpaceCreated{},
|
||||
events.SpaceDeleted{},
|
||||
events.SpaceDisabled{},
|
||||
events.SpaceShared{},
|
||||
events.SpaceShareUpdated{},
|
||||
events.SpaceUnshared{},
|
||||
|
||||
@@ -131,6 +131,11 @@ func (cl *ClientlogService) processEvent(event events.Event) {
|
||||
users, data, err = processShareEvent(ctx, ref, gwc, event.InitiatorID, uid, gid)
|
||||
}
|
||||
|
||||
spaceEv := func(typ string, id *provider.StorageSpaceId, uids []string) {
|
||||
evType = typ
|
||||
users, data, err = processSpaceEvent(ctx, id, gwc, event.InitiatorID, uids)
|
||||
}
|
||||
|
||||
switch e := event.Event.(type) {
|
||||
default:
|
||||
err = errors.New("unhandled event")
|
||||
@@ -160,6 +165,16 @@ func (cl *ClientlogService) processEvent(event events.Event) {
|
||||
fileEv("file-unlocked", e.Ref)
|
||||
case events.FileTouched:
|
||||
fileEv("file-touched", e.Ref)
|
||||
case events.SpaceCreated:
|
||||
spaceEv("space-created", e.ID, []string{e.Executant.GetOpaqueId()})
|
||||
case events.SpaceDisabled:
|
||||
spaceEv("space-disabled", e.ID, []string{e.Executant.GetOpaqueId()})
|
||||
case events.SpaceDeleted:
|
||||
uids := []string{}
|
||||
for k := range e.FinalMembers {
|
||||
uids = append(uids, k)
|
||||
}
|
||||
spaceEv("space-deleted", e.ID, uids)
|
||||
case events.SpaceShared:
|
||||
r, _ := storagespace.ParseReference(e.ID.GetOpaqueId())
|
||||
shareEv("space-member-added", &r, e.GranteeUserID, e.GranteeGroupID)
|
||||
@@ -239,6 +254,14 @@ func processShareEvent(ctx context.Context, ref *provider.Reference, gwc gateway
|
||||
return addShareeData(ctx, gwc, data, users, shareeID, shareeGroupID)
|
||||
}
|
||||
|
||||
func processSpaceEvent(ctx context.Context, id *provider.StorageSpaceId, gwc gateway.GatewayAPIClient, initiatorid string, uids []string) ([]string, FileEvent, error) {
|
||||
data := FileEvent{
|
||||
SpaceID: id.GetOpaqueId(),
|
||||
InitiatorID: initiatorid,
|
||||
}
|
||||
return uids, data, nil
|
||||
}
|
||||
|
||||
// custom logic for item trashed event
|
||||
func processItemTrashedEvent(ctx context.Context, ref *provider.Reference, gwc gateway.GatewayAPIClient, initiatorid string, itemID *provider.ResourceId) ([]string, FileEvent, error) {
|
||||
data := FileEvent{
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
auto-install-peers=true
|
||||
public-hoist-pattern[]=*
|
||||
@@ -36,7 +36,7 @@
|
||||
"@types/react-redux": "^7.1.34",
|
||||
"@types/redux-logger": "^3.0.13",
|
||||
"axios": "^1.16.0",
|
||||
"i18next": "^26.1.0",
|
||||
"i18next": "^26.3.0",
|
||||
"i18next-browser-languagedetector": "^8.2.1",
|
||||
"i18next-resources-to-backend": "^1.2.1",
|
||||
"query-string": "^9.3.1",
|
||||
@@ -56,7 +56,7 @@
|
||||
"babel-plugin-named-asset-import": "^0.3.8",
|
||||
"babel-preset-react-app": "^10.1.0",
|
||||
"case-sensitive-paths-webpack-plugin": "2.4.0",
|
||||
"cldr": "^7.9.0",
|
||||
"cldr": "^8.0.0",
|
||||
"css-loader": "^7.1.4",
|
||||
"css-minimizer-webpack-plugin": "^8.0.0",
|
||||
"dotenv": "17.4.2",
|
||||
@@ -64,30 +64,23 @@
|
||||
"html-webpack-plugin": "^5.6.7",
|
||||
"i18next-conv": "^15.1.2",
|
||||
"i18next-parser": "^9.4.0",
|
||||
"license-checker-rseidelsohn": "4.4.2",
|
||||
"license-checker-rseidelsohn": "5.0.1",
|
||||
"mini-css-extract-plugin": "2.9.2",
|
||||
"pnp-webpack-plugin": "1.7.0",
|
||||
"postcss-flexbugs-fixes": "5.0.2",
|
||||
"postcss-loader": "4.3.0",
|
||||
"postcss-loader": "8.2.1",
|
||||
"postcss-normalize": "13.0.1",
|
||||
"postcss-preset-env": "11.2.0",
|
||||
"react-dev-utils": "^12.0.1",
|
||||
"resolve": "^1.22.12",
|
||||
"resolve-url-loader": "^5.0.0",
|
||||
"sass-loader": "^16.0.8",
|
||||
"sass-loader": "^17.0.0",
|
||||
"source-map-explorer": "^2.5.3",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript": "^6.0.3",
|
||||
"url-loader": "4.1.1",
|
||||
"webpack": "5.105.2",
|
||||
"webpack-manifest-plugin": "5.0.0",
|
||||
"workbox-webpack-plugin": "7.4.0"
|
||||
"workbox-webpack-plugin": "7.4.1"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"fast-uri": ">=3.1.2",
|
||||
"serialize-javascript@<7.0.3": ">=7.0.3",
|
||||
"@xmldom/xmldom": ">=0.8.13"
|
||||
}
|
||||
},
|
||||
"packageManager": "pnpm@9.15.4"
|
||||
"packageManager": "pnpm@11.1.3"
|
||||
}
|
||||
Generated
+1904
-1397
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,14 @@
|
||||
autoInstallPeers: true
|
||||
|
||||
publicHoistPattern:
|
||||
- "*"
|
||||
|
||||
overrides:
|
||||
fast-uri: ">=3.1.2"
|
||||
postcss: ">=8.5.10"
|
||||
"serialize-javascript@<7.0.3": ">=7.0.3"
|
||||
"@babel/plugin-transform-modules-systemjs": ">=7.29.4"
|
||||
"@xmldom/xmldom": ">=0.8.13"
|
||||
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
@@ -1,6 +1,6 @@
|
||||
SHELL := bash
|
||||
NAME := web
|
||||
WEB_ASSETS_VERSION = v7.0.1
|
||||
WEB_ASSETS_VERSION = v7.1.0
|
||||
WEB_ASSETS_BRANCH = main
|
||||
|
||||
ifneq (, $(shell command -v go 2> /dev/null)) # suppress `command not found warnings` for non go targets in CI
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -36,6 +37,8 @@ const (
|
||||
// TODO elementNameFilterFiles = "filter-files"
|
||||
)
|
||||
|
||||
var spacesSearchRegex = regexp.MustCompile(`^/(?:remote\.php/)?dav/spaces/([^/]+)`)
|
||||
|
||||
// Search is the endpoint for retrieving search results for REPORT requests
|
||||
func (g Webdav) Search(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.log.SubloggerWithRequestID(r.Context())
|
||||
@@ -76,9 +79,9 @@ func (g Webdav) Search(w http.ResponseWriter, r *http.Request) {
|
||||
PageSize: int32(rep.SearchFiles.Search.Limit),
|
||||
}
|
||||
|
||||
// Limit search to the according space when searching /dav/spaces/
|
||||
if strings.HasPrefix(r.URL.Path, "/dav/spaces") {
|
||||
space := strings.TrimPrefix(r.URL.Path, "/dav/spaces/")
|
||||
// Limit search to the according space when searching /dav/spaces/<spaceid>
|
||||
if matches := spacesSearchRegex.FindStringSubmatch(r.URL.Path); matches != nil {
|
||||
space := matches[1]
|
||||
rid, err := storagespace.ParseID(space)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Msg("error parsing the space id for filtering")
|
||||
@@ -92,6 +95,10 @@ func (g Webdav) Search(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
}
|
||||
davPrefix := ""
|
||||
if strings.HasPrefix(r.URL.Path, "/remote.php") {
|
||||
davPrefix = "/remote.php"
|
||||
}
|
||||
|
||||
rsp, err := g.searchClient.Search(ctx, req)
|
||||
if err != nil {
|
||||
@@ -105,12 +112,12 @@ func (g Webdav) Search(w http.ResponseWriter, r *http.Request) {
|
||||
logger.Error().Err(err).Msg("could not get search results")
|
||||
return
|
||||
}
|
||||
g.sendSearchResponse(rsp, w, r, user)
|
||||
g.sendSearchResponse(davPrefix, rsp, w, r, user)
|
||||
}
|
||||
|
||||
func (g Webdav) sendSearchResponse(rsp *searchsvc.SearchResponse, w http.ResponseWriter, r *http.Request, user *userv1beta1.User) {
|
||||
func (g Webdav) sendSearchResponse(davPrefix string, rsp *searchsvc.SearchResponse, w http.ResponseWriter, r *http.Request, user *userv1beta1.User) {
|
||||
logger := g.log.SubloggerWithRequestID(r.Context())
|
||||
responsesXML, err := multistatusResponse(r.Context(), g.config.OpenCloudPublicURL, rsp.Matches, user)
|
||||
responsesXML, err := multistatusResponse(r.Context(), davPrefix, g.config.OpenCloudPublicURL, rsp.Matches, user)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("error formatting propfind")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
@@ -128,10 +135,10 @@ func (g Webdav) sendSearchResponse(rsp *searchsvc.SearchResponse, w http.Respons
|
||||
}
|
||||
|
||||
// multistatusResponse converts a list of matches into a multistatus response string
|
||||
func multistatusResponse(ctx context.Context, publicURL string, matches []*searchmsg.Match, user *userv1beta1.User) ([]byte, error) {
|
||||
func multistatusResponse(ctx context.Context, davPrefix, publicURL string, matches []*searchmsg.Match, user *userv1beta1.User) ([]byte, error) {
|
||||
responses := make([]*propfind.ResponseXML, 0, len(matches))
|
||||
for i := range matches {
|
||||
res, err := matchToPropResponse(ctx, publicURL, matches[i], user)
|
||||
res, err := matchToPropResponse(ctx, davPrefix, publicURL, matches[i], user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -147,7 +154,7 @@ func multistatusResponse(ctx context.Context, publicURL string, matches []*searc
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func matchToPropResponse(ctx context.Context, publicURL string, match *searchmsg.Match, user *userv1beta1.User) (*propfind.ResponseXML, error) {
|
||||
func matchToPropResponse(ctx context.Context, davPrefix, publicURL string, match *searchmsg.Match, user *userv1beta1.User) (*propfind.ResponseXML, error) {
|
||||
// unfortunately, search uses own versions of ResourceId and Ref. So we need to assert them here
|
||||
var (
|
||||
ref string
|
||||
@@ -180,7 +187,7 @@ func matchToPropResponse(ctx context.Context, publicURL string, match *searchmsg
|
||||
return nil, err
|
||||
}
|
||||
response := propfind.ResponseXML{
|
||||
Href: net.EncodePath(path.Join("/remote.php/dav/spaces/", ref)),
|
||||
Href: net.EncodePath(path.Join(davPrefix, "/dav/spaces/", ref)),
|
||||
Propstat: []propfind.PropstatXML{},
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package svc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestSearch(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Search Suite")
|
||||
}
|
||||
|
||||
var _ = Describe("SpacesSearchRegex", func() {
|
||||
DescribeTable("path matching",
|
||||
func(path, expectedSpace string, shouldMatch bool) {
|
||||
matches := spacesSearchRegex.FindStringSubmatch(path)
|
||||
if shouldMatch {
|
||||
Expect(matches).ToNot(BeNil(), "Expected path %q to match", path)
|
||||
Expect(matches[1]).To(Equal(expectedSpace), "Expected space to be %q", expectedSpace)
|
||||
} else {
|
||||
Expect(matches).To(BeNil(), "Expected path %q not to match", path)
|
||||
}
|
||||
},
|
||||
Entry("standard dav spaces path", "/dav/spaces/12345", "12345", true),
|
||||
Entry("remote.php dav spaces path", "/remote.php/dav/spaces/12345", "12345", true),
|
||||
Entry("standard dav spaces path with subpaths", "/dav/spaces/12345/some/folder", "12345", true),
|
||||
Entry("remote.php dav spaces path with subpaths", "/remote.php/dav/spaces/12345/some/folder", "12345", true),
|
||||
Entry("standard dav spaces path without space", "/dav/spaces/", "", false),
|
||||
Entry("remote.php dav spaces path without space", "/remote.php/dav/spaces/", "", false),
|
||||
Entry("prefix match only", "/dav/spaces", "", false),
|
||||
Entry("unrelated path", "/dav/files/123", "", false),
|
||||
)
|
||||
})
|
||||
@@ -190,10 +190,6 @@
|
||||
|
||||
- [apiServiceAvailability/serviceAvailabilityCheck.feature:123](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature#L123)
|
||||
|
||||
#### [Skip tests for different languages](https://github.com/opencloud-eu/opencloud/issues/183)
|
||||
|
||||
- [apiActivities/activities.feature:2598](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiActivities/activities.feature#L2598)
|
||||
|
||||
#### [Missing properties in REPORT response](https://github.com/owncloud/ocis/issues/9780), [d:getetag property has empty value in REPORT response](https://github.com/owncloud/ocis/issues/9783)
|
||||
|
||||
- [apiSearch1/search.feature:437](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L437)
|
||||
|
||||
@@ -190,10 +190,6 @@
|
||||
|
||||
- [apiServiceAvailability/serviceAvailabilityCheck.feature:123](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiServiceAvailability/serviceAvailabilityCheck.feature#L123)
|
||||
|
||||
#### [Skip tests for different languages](https://github.com/opencloud-eu/opencloud/issues/183)
|
||||
|
||||
- [apiActivities/activities.feature:2598](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiActivities/activities.feature#L2598)
|
||||
|
||||
#### [Missing properties in REPORT response](https://github.com/owncloud/ocis/issues/9780), [d:getetag property has empty value in REPORT response](https://github.com/owncloud/ocis/issues/9783)
|
||||
|
||||
- [apiSearch1/search.feature:437](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L437)
|
||||
|
||||
@@ -1,298 +1,9 @@
|
||||
## Scenarios that are expected to fail when remote.php is not used
|
||||
|
||||
#### [REPORT request without remote.php returns empty result (only with dav/spaces path)](https://github.com/owncloud/ocis/issues/10329)
|
||||
|
||||
- [apiContract/sharesReport.feature:43](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/sharesReport.feature#L43)
|
||||
- [apiContract/sharesReport.feature:84](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/sharesReport.feature#L84)
|
||||
- [apiContract/sharesReport.feature:150](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/sharesReport.feature#L150)
|
||||
- [apiContract/sharesReport.feature:180](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/sharesReport.feature#L180)
|
||||
- [apiContract/spacesReport.feature:16](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/spacesReport.feature#L16)
|
||||
- [apiContract/spacesReport.feature:35](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/spacesReport.feature#L35)
|
||||
- [apiContract/spacesReport.feature:55](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/spacesReport.feature#L55)
|
||||
- [apiContract/spacesReport.feature:74](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/spacesReport.feature#L74)
|
||||
- [apiContract/spacesSharesReport.feature:46](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/spacesSharesReport.feature#L46)
|
||||
- [apiContract/spacesSharesReport.feature:77](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiContract/spacesSharesReport.feature#L77)
|
||||
- [apiSearch1/dateSearch.feature:19](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L19)
|
||||
- [apiSearch1/dateSearch.feature:39](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L39)
|
||||
- [apiSearch1/dateSearch.feature:40](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L40)
|
||||
- [apiSearch1/dateSearch.feature:41](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L41)
|
||||
- [apiSearch1/dateSearch.feature:42](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L42)
|
||||
- [apiSearch1/dateSearch.feature:43](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L43)
|
||||
- [apiSearch1/dateSearch.feature:44](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L44)
|
||||
- [apiSearch1/dateSearch.feature:45](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L45)
|
||||
- [apiSearch1/dateSearch.feature:46](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L46)
|
||||
- [apiSearch1/dateSearch.feature:47](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L47)
|
||||
- [apiSearch1/dateSearch.feature:48](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L48)
|
||||
- [apiSearch1/dateSearch.feature:50](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L50)
|
||||
- [apiSearch1/dateSearch.feature:53](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/dateSearch.feature#L53)
|
||||
- [apiSearch1/search.feature:113](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L113)
|
||||
- [apiSearch1/search.feature:176](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L176)
|
||||
- [apiSearch1/search.feature:280](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L280)
|
||||
- [apiSearch1/search.feature:304](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L304)
|
||||
- [apiSearch1/search.feature:317](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L317)
|
||||
- [apiSearch1/search.feature:318](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L318)
|
||||
- [apiSearch1/search.feature:319](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L319)
|
||||
- [apiSearch1/search.feature:320](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L320)
|
||||
- [apiSearch1/search.feature:321](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L321)
|
||||
- [apiSearch1/search.feature:324](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L324)
|
||||
- [apiSearch1/search.feature:356](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L356)
|
||||
- [apiSearch1/search.feature:369](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L369)
|
||||
- [apiSearch1/search.feature:396](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L396)
|
||||
- [apiSearch1/search.feature:410](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch1/search.feature#L410)
|
||||
- [apiSearch2/mediaTypeSearch.feature:31](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L31)
|
||||
- [apiSearch2/mediaTypeSearch.feature:32](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L32)
|
||||
- [apiSearch2/mediaTypeSearch.feature:33](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L33)
|
||||
- [apiSearch2/mediaTypeSearch.feature:34](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L34)
|
||||
- [apiSearch2/mediaTypeSearch.feature:35](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L35)
|
||||
- [apiSearch2/mediaTypeSearch.feature:36](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L36)
|
||||
- [apiSearch2/mediaTypeSearch.feature:37](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L37)
|
||||
- [apiSearch2/mediaTypeSearch.feature:38](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L38)
|
||||
- [apiSearch2/mediaTypeSearch.feature:39](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L39)
|
||||
- [apiSearch2/mediaTypeSearch.feature:60](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L60)
|
||||
- [apiSearch2/mediaTypeSearch.feature:61](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L61)
|
||||
- [apiSearch2/mediaTypeSearch.feature:62](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L62)
|
||||
- [apiSearch2/mediaTypeSearch.feature:63](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L63)
|
||||
- [apiSearch2/mediaTypeSearch.feature:64](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L64)
|
||||
- [apiSearch2/mediaTypeSearch.feature:65](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L65)
|
||||
- [apiSearch2/mediaTypeSearch.feature:66](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L66)
|
||||
- [apiSearch2/mediaTypeSearch.feature:67](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L67)
|
||||
- [apiSearch2/mediaTypeSearch.feature:68](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L68)
|
||||
- [apiSearch2/mediaTypeSearch.feature:90](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L90)
|
||||
- [apiSearch2/mediaTypeSearch.feature:91](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L91)
|
||||
- [apiSearch2/mediaTypeSearch.feature:92](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L92)
|
||||
- [apiSearch2/mediaTypeSearch.feature:93](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L93)
|
||||
- [apiSearch2/mediaTypeSearch.feature:94](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L94)
|
||||
- [apiSearch2/mediaTypeSearch.feature:95](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L95)
|
||||
- [apiSearch2/mediaTypeSearch.feature:96](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L96)
|
||||
- [apiSearch2/mediaTypeSearch.feature:97](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L97)
|
||||
- [apiSearch2/mediaTypeSearch.feature:98](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L98)
|
||||
- [apiSearch2/mediaTypeSearch.feature:126](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L126)
|
||||
- [apiSearch2/mediaTypeSearch.feature:127](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L127)
|
||||
- [apiSearch2/mediaTypeSearch.feature:128](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L128)
|
||||
- [apiSearch2/mediaTypeSearch.feature:129](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L129)
|
||||
- [apiSearch2/mediaTypeSearch.feature:130](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L130)
|
||||
- [apiSearch2/mediaTypeSearch.feature:131](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L131)
|
||||
- [apiSearch2/mediaTypeSearch.feature:132](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L132)
|
||||
- [apiSearch2/mediaTypeSearch.feature:133](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L133)
|
||||
- [apiSearch2/mediaTypeSearch.feature:134](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L134)
|
||||
- [apiSearch2/mediaTypeSearch.feature:161](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L161)
|
||||
- [apiSearch2/mediaTypeSearch.feature:162](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L162)
|
||||
- [apiSearch2/mediaTypeSearch.feature:163](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L163)
|
||||
- [apiSearch2/mediaTypeSearch.feature:164](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L164)
|
||||
- [apiSearch2/mediaTypeSearch.feature:165](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L165)
|
||||
- [apiSearch2/mediaTypeSearch.feature:166](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L166)
|
||||
- [apiSearch2/mediaTypeSearch.feature:167](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L167)
|
||||
- [apiSearch2/mediaTypeSearch.feature:168](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L168)
|
||||
- [apiSearch2/mediaTypeSearch.feature:169](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L169)
|
||||
- [apiSearch2/mediaTypeSearch.feature:172](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/mediaTypeSearch.feature#L172)
|
||||
- [apiSearch2/tagSearch.feature:36](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L36)
|
||||
- [apiSearch2/tagSearch.feature:64](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L64)
|
||||
- [apiSearch2/tagSearch.feature:85](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L85)
|
||||
- [apiSearch2/tagSearch.feature:108](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L108)
|
||||
- [apiSearch2/tagSearch.feature:138](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L138)
|
||||
- [apiSearch2/tagSearch.feature:172](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L172)
|
||||
- [apiSearch2/tagSearch.feature:207](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L207)
|
||||
- [apiSearch2/tagSearch.feature:226](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L226)
|
||||
- [apiSearch2/tagSearch.feature:254](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L254)
|
||||
- [apiSearch2/tagSearch.feature:255](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L255)
|
||||
- [apiSearch2/tagSearch.feature:256](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L256)
|
||||
- [apiSearch2/tagSearch.feature:257](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L257)
|
||||
- [apiSearch2/tagSearch.feature:258](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L258)
|
||||
- [apiSearch2/tagSearch.feature:259](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L259)
|
||||
- [apiSearch2/tagSearch.feature:260](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L260)
|
||||
- [apiSearch2/tagSearch.feature:261](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L261)
|
||||
- [apiSearch2/tagSearch.feature:262](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L262)
|
||||
- [apiSearch2/tagSearch.feature:263](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L263)
|
||||
- [apiSearch2/tagSearch.feature:265](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearch2/tagSearch.feature#L265)
|
||||
- [apiSearchContent/contentSearch.feature:27](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L27)
|
||||
- [apiSearchContent/contentSearch.feature:51](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L51)
|
||||
- [apiSearchContent/contentSearch.feature:84](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L84)
|
||||
- [apiSearchContent/contentSearch.feature:112](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L112)
|
||||
- [apiSearchContent/contentSearch.feature:131](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L131)
|
||||
- [apiSearchContent/contentSearch.feature:147](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L147)
|
||||
- [apiSearchContent/contentSearch.feature:163](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L163)
|
||||
- [apiSearchContent/contentSearch.feature:186](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L186)
|
||||
- [apiSearchContent/contentSearch.feature:215](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L215)
|
||||
- [apiSearchContent/contentSearch.feature:233](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L233)
|
||||
- [apiSearchContent/contentSearch.feature:234](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L234)
|
||||
- [apiSearchContent/contentSearch.feature:235](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L235)
|
||||
- [apiSearchContent/contentSearch.feature:236](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L236)
|
||||
- [apiSearchContent/contentSearch.feature:237](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L237)
|
||||
- [apiSearchContent/contentSearch.feature:238](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L238)
|
||||
- [apiSearchContent/contentSearch.feature:267](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSearchContent/contentSearch.feature#L267)
|
||||
- [cliCommands/searchReIndex.feature:15](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/cliCommands/searchReIndex.feature#L15)
|
||||
|
||||
#### [Downloading public files without remote.php returns 401 unauthorized error](https://github.com/owncloud/ocis/issues/9724)
|
||||
|
||||
- [apiSpaces/editPublicLinkOfSpace.feature:48](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/editPublicLinkOfSpace.feature#L48)
|
||||
- [apiSpaces/editPublicLinkOfSpace.feature:49](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/editPublicLinkOfSpace.feature#L49)
|
||||
- [apiSpacesShares/publicLinkDownload.feature:16](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/publicLinkDownload.feature#L16)
|
||||
- [apiSpacesShares/publicLinkDownload.feature:32](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/publicLinkDownload.feature#L32)
|
||||
- [apiSpacesShares/shareSpacesViaLink.feature:42](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/shareSpacesViaLink.feature#L42)
|
||||
- [apiSpacesShares/shareSpacesViaLink.feature:43](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/shareSpacesViaLink.feature#L43)
|
||||
- [apiSpacesShares/shareSpacesViaLink.feature:44](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/shareSpacesViaLink.feature#L44)
|
||||
- [apiSpacesShares/shareSubItemOfSpaceViaPublicLink.feature:146](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/shareSubItemOfSpaceViaPublicLink.feature#L146)
|
||||
- [apiSpacesShares/shareSubItemOfSpaceViaPublicLink.feature:147](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/shareSubItemOfSpaceViaPublicLink.feature#L147)
|
||||
- [apiSharingNgLinkSharePermission/createLinkShare.feature:473](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSharingNgLinkSharePermission/createLinkShare.feature#L473)
|
||||
- [apiSharingNgLinkSharePermission/createLinkShare.feature:1208](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSharingNgLinkSharePermission/createLinkShare.feature#L1208)
|
||||
- [apiSharingNgLinkSharePermission/updateLinkShare.feature:204](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSharingNgLinkSharePermission/updateLinkShare.feature#L204)
|
||||
- [apiSharingNgLinkSharePermission/updateLinkShare.feature:282](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSharingNgLinkSharePermission/updateLinkShare.feature#L282)
|
||||
- [apiSharingNgLinkShareRoot/updateLinkShare.feature:10](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSharingNgLinkShareRoot/updateLinkShare.feature#L10)
|
||||
- [apiSharingNgLinkShareRoot/updateLinkShare.feature:42](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSharingNgLinkShareRoot/updateLinkShare.feature#L42)
|
||||
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:220](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L220)
|
||||
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:221](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L221)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:59](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L59)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:60](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L60)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:88](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L88)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:89](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L89)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:122](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L122)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:123](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L123)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:209](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L209)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:210](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L210)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:229](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L229)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:230](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L230)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:257](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L257)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:258](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L258)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:273](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L273)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:274](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L274)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:277](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L277)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:291](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L291)
|
||||
- [coreApiSharePublicLink2/updatePublicLinkShare.feature:111](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/updatePublicLinkShare.feature#L111)
|
||||
- [coreApiSharePublicLink2/updatePublicLinkShare.feature:112](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/updatePublicLinkShare.feature#L112)
|
||||
- [coreApiSharePublicLink2/updatePublicLinkShare.feature:249](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/updatePublicLinkShare.feature#L249)
|
||||
- [coreApiSharePublicLink2/updatePublicLinkShare.feature:250](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/updatePublicLinkShare.feature#L250)
|
||||
- [coreApiSharePublicLink2/updatePublicLinkShare.feature:276](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/updatePublicLinkShare.feature#L276)
|
||||
- [coreApiSharePublicLink2/updatePublicLinkShare.feature:277](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/updatePublicLinkShare.feature#L277)
|
||||
|
||||
#### [Most (if not all) requests to public link share without remote.php returns 401 Unauthorized error](https://github.com/owncloud/ocis/issues/10331)
|
||||
|
||||
- [apiSpaces/publicLink.feature:18](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/publicLink.feature#L18)
|
||||
- [apiSpaces/publicLink.feature:23](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/publicLink.feature#L23)
|
||||
- [apiSpaces/publicLink.feature:28](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/publicLink.feature#L28)
|
||||
- [apiSpaces/publicLink.feature:34](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/publicLink.feature#L34)
|
||||
- [apiSpaces/publicLink.feature:40](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/publicLink.feature#L40)
|
||||
- [apiSpaces/uploadSpaces.feature:95](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/uploadSpaces.feature#L95)
|
||||
- [apiSpaces/uploadSpaces.feature:112](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/uploadSpaces.feature#L112)
|
||||
- [apiSpaces/uploadSpaces.feature:129](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpaces/uploadSpaces.feature#L129)
|
||||
- [apiSpacesShares/shareSpacesViaLink.feature:58](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiSpacesShares/shareSpacesViaLink.feature#L58)
|
||||
- [apiDepthInfinity/propfind.feature:74](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiDepthInfinity/propfind.feature#L74)
|
||||
- [apiDepthInfinity/propfind.feature:124](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiDepthInfinity/propfind.feature#L124)
|
||||
- [apiLocks/lockFiles.feature:490](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L490)
|
||||
- [apiLocks/lockFiles.feature:487](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L487)
|
||||
- [apiLocks/lockFiles.feature:488](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L488)
|
||||
- [apiLocks/lockFiles.feature:489](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L489)
|
||||
- [apiLocks/lockFiles.feature:513](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L513)
|
||||
- [apiLocks/lockFiles.feature:510](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L510)
|
||||
- [apiLocks/lockFiles.feature:511](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L511)
|
||||
- [apiLocks/lockFiles.feature:512](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/lockFiles.feature#L512)
|
||||
- [apiLocks/unlockFiles.feature:320](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L320)
|
||||
- [apiLocks/unlockFiles.feature:321](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L321)
|
||||
- [apiLocks/unlockFiles.feature:322](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L322)
|
||||
- [apiLocks/unlockFiles.feature:323](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiLocks/unlockFiles.feature#L323)
|
||||
- [apiAntivirus/antivirus.feature:114](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L114)
|
||||
- [apiAntivirus/antivirus.feature:115](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L115)
|
||||
- [apiAntivirus/antivirus.feature:116](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L116)
|
||||
- [apiAntivirus/antivirus.feature:117](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L117)
|
||||
- [apiAntivirus/antivirus.feature:118](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L118)
|
||||
- [apiAntivirus/antivirus.feature:119](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L119)
|
||||
- [apiAntivirus/antivirus.feature:140](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L140)
|
||||
- [apiAntivirus/antivirus.feature:141](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L141)
|
||||
- [apiAntivirus/antivirus.feature:142](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L142)
|
||||
- [apiAntivirus/antivirus.feature:143](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L143)
|
||||
- [apiAntivirus/antivirus.feature:144](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L144)
|
||||
- [apiAntivirus/antivirus.feature:145](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L145)
|
||||
- [apiAntivirus/antivirus.feature:357](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L357)
|
||||
- [apiAntivirus/antivirus.feature:358](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L358)
|
||||
- [apiAntivirus/antivirus.feature:359](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiAntivirus/antivirus.feature#L359)
|
||||
- [apiCollaboration/wopi.feature:956](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiCollaboration/wopi.feature#L956)
|
||||
- [apiCollaboration/wopi.feature:957](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiCollaboration/wopi.feature#L957)
|
||||
- [apiCollaboration/wopi.feature:958](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiCollaboration/wopi.feature#L958)
|
||||
- [apiCollaboration/wopi.feature:961](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiCollaboration/wopi.feature#L961)
|
||||
- [apiCollaboration/wopi.feature:1047](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiCollaboration/wopi.feature#L1047)
|
||||
- [apiCollaboration/wopi.feature:1048](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiCollaboration/wopi.feature#L1048)
|
||||
- [apiCollaboration/wopi.feature:1049](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiCollaboration/wopi.feature#L1049)
|
||||
- [apiCollaboration/wopi.feature:1052](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiCollaboration/wopi.feature#L1052)
|
||||
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:27](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L27)
|
||||
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:28](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L28)
|
||||
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:29](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L29)
|
||||
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:30](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L30)
|
||||
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:33](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L33)
|
||||
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:46](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L46)
|
||||
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:70](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L70)
|
||||
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:95](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L95)
|
||||
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:120](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L120)
|
||||
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:132](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L132)
|
||||
- [coreApiSharePublicLink1/changingPublicLinkShare.feature:145](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/changingPublicLinkShare.feature#L145)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:189](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L189)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:190](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L190)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:304](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L304)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:328](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L328)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:329](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L329)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:344](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L344)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:345](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L345)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:348](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L348)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:363](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L363)
|
||||
- [coreApiSharePublicLink1/createPublicLinkShare.feature:377](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink1/createPublicLinkShare.feature#L377)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:13](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L13)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:28](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L28)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:44](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L44)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:60](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L60)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:75](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L75)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:92](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L92)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:105](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L105)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:137](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L137)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:138](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L138)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:139](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L139)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:140](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L140)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:158](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L158)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:159](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L159)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:160](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L160)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:161](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L161)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:180](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L180)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:181](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L181)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:182](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L182)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:183](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L183)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:186](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L186)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:212](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L212)
|
||||
- [coreApiSharePublicLink2/copyFromPublicLink.feature:225](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/copyFromPublicLink.feature#L225)
|
||||
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:28](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L28)
|
||||
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:29](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L29)
|
||||
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:30](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L30)
|
||||
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:33](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L33)
|
||||
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:44](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L44)
|
||||
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:58](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L58)
|
||||
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:70](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L70)
|
||||
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:82](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L82)
|
||||
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:94](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L94)
|
||||
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:106](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L106)
|
||||
- [coreApiSharePublicLink2/uploadToPublicLinkShare.feature:118](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiSharePublicLink2/uploadToPublicLinkShare.feature#L118)
|
||||
- [coreApiWebdavEtagPropagation1/deleteFileFolder.feature:238](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/deleteFileFolder.feature#L238)
|
||||
- [coreApiWebdavEtagPropagation1/deleteFileFolder.feature:239](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/deleteFileFolder.feature#L239)
|
||||
- [coreApiWebdavEtagPropagation1/deleteFileFolder.feature:240](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/deleteFileFolder.feature#L240)
|
||||
- [coreApiWebdavEtagPropagation1/deleteFileFolder.feature:262](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/deleteFileFolder.feature#L262)
|
||||
- [coreApiWebdavEtagPropagation1/deleteFileFolder.feature:263](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/deleteFileFolder.feature#L263)
|
||||
- [coreApiWebdavEtagPropagation1/deleteFileFolder.feature:264](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/deleteFileFolder.feature#L264)
|
||||
- [coreApiWebdavEtagPropagation1/moveFileFolder.feature:308](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/moveFileFolder.feature#L308)
|
||||
- [coreApiWebdavEtagPropagation1/moveFileFolder.feature:309](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/moveFileFolder.feature#L309)
|
||||
- [coreApiWebdavEtagPropagation1/moveFileFolder.feature:310](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/moveFileFolder.feature#L310)
|
||||
- [coreApiWebdavEtagPropagation1/moveFileFolder.feature:333](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/moveFileFolder.feature#L333)
|
||||
- [coreApiWebdavEtagPropagation1/moveFileFolder.feature:334](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/moveFileFolder.feature#L334)
|
||||
- [coreApiWebdavEtagPropagation1/moveFileFolder.feature:335](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation1/moveFileFolder.feature#L335)
|
||||
- [coreApiWebdavEtagPropagation2/copyFileFolder.feature:135](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation2/copyFileFolder.feature#L135)
|
||||
- [coreApiWebdavEtagPropagation2/copyFileFolder.feature:136](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation2/copyFileFolder.feature#L136)
|
||||
- [coreApiWebdavEtagPropagation2/copyFileFolder.feature:137](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation2/copyFileFolder.feature#L137)
|
||||
- [coreApiWebdavEtagPropagation2/createFolder.feature:111](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation2/createFolder.feature#L111)
|
||||
- [coreApiWebdavEtagPropagation2/upload.feature:188](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation2/upload.feature#L188)
|
||||
- [coreApiWebdavEtagPropagation2/upload.feature:189](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation2/upload.feature#L189)
|
||||
- [coreApiWebdavEtagPropagation2/upload.feature:190](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavEtagPropagation2/upload.feature#L190)
|
||||
- [coreApiWebdavOperations/listFiles.feature:112](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/listFiles.feature#L112)
|
||||
- [coreApiWebdavOperations/listFiles.feature:140](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/listFiles.feature#L140)
|
||||
- [coreApiWebdavOperations/propfind.feature:40](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/propfind.feature#L40)
|
||||
- [coreApiWebdavOperations/propfind.feature:55](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/propfind.feature#L55)
|
||||
- [coreApiWebdavOperations/propfind.feature:69](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/propfind.feature#L69)
|
||||
- [coreApiWebdavUpload/uploadFile.feature:376](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUpload/uploadFile.feature#L376)
|
||||
- [apiActivities/shareActivities.feature:1956](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiActivities/shareActivities.feature#L1956)
|
||||
- [apiActivities/shareActivities.feature:2095](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/apiActivities/shareActivities.feature#L2095)
|
||||
|
||||
#### [Cannot create new TUS upload resource using /webdav without remote.php - returns html instead](https://github.com/owncloud/ocis/issues/10346)
|
||||
|
||||
@@ -339,8 +50,8 @@
|
||||
- [coreApiWebdavUploadTUS/uploadToShare.feature:69](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L69)
|
||||
- [coreApiWebdavUploadTUS/uploadToShare.feature:89](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L89)
|
||||
|
||||
#### [PROPFIND to /webdav root (old dav path) without remote.php returns html instead of xml](https://github.com/owncloud/ocis/issues/10334)
|
||||
|
||||
#### [PROPFIND to /webdav root (old dav path) without remote.php returns html instead of xml](https://github.com/owncloud/ocis/issues/10334)
|
||||
- [coreApiAuth/webDavAuth.feature:15](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavAuth.feature#L15)
|
||||
- [coreApiAuth/webDavAuth.feature:25](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiAuth/webDavAuth.feature#L25)
|
||||
- [coreApiShareManagementToShares/moveReceivedShare.feature:49](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiShareManagementToShares/moveReceivedShare.feature#L49)
|
||||
@@ -350,11 +61,6 @@
|
||||
- [coreApiWebdavOperations/propfind.feature:26](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/propfind.feature#L26)
|
||||
- [coreApiWebdavOperations/propfind.feature:36](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/propfind.feature#L36)
|
||||
|
||||
#### [Public cannot download file preview of unprotected (without password) link share without remote.php](https://github.com/owncloud/ocis/issues/10341)
|
||||
|
||||
- [coreApiWebdavPreviews/linkSharePreviews.feature:25](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/linkSharePreviews.feature#L25)
|
||||
- [coreApiWebdavPreviews/linkSharePreviews.feature:54](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/linkSharePreviews.feature#L54)
|
||||
|
||||
#### [Trying to create .. resource with /webdav root (old dav path) without remote.php returns html](https://github.com/owncloud/ocis/issues/10339)
|
||||
|
||||
- [coreApiWebdavCopyCreate/createFileFolder.feature:176](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavCopyCreate/createFileFolder.feature#L176)
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
load("@com_google_protobuf//bazel:proto_library.bzl", "proto_library")
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
|
||||
load("@rules_proto//proto:defs.bzl", "proto_library")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
+17
-1
@@ -71,6 +71,7 @@ type ServeMux struct {
|
||||
streamErrorHandler StreamErrorHandlerFunc
|
||||
routingErrorHandler RoutingErrorHandlerFunc
|
||||
disablePathLengthFallback bool
|
||||
disableHTTPMethodOverride bool
|
||||
unescapingMode UnescapingMode
|
||||
writeContentLength bool
|
||||
disableChunkedEncoding bool
|
||||
@@ -271,6 +272,19 @@ func WithDisablePathLengthFallback() ServeMuxOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithDisableHTTPMethodOverride returns a ServeMuxOption that disables the
|
||||
// X-HTTP-Method-Override header handling.
|
||||
//
|
||||
// When this option is used, the mux will no longer allow POST requests with
|
||||
// the X-HTTP-Method-Override header to override the HTTP method. The path
|
||||
// length fallback (POST with application/x-www-form-urlencoded falling back
|
||||
// to a matching GET handler) is not affected by this option.
|
||||
func WithDisableHTTPMethodOverride() ServeMuxOption {
|
||||
return func(serveMux *ServeMux) {
|
||||
serveMux.disableHTTPMethodOverride = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithWriteContentLength returns a ServeMuxOption to enable writing content length on non-streaming responses
|
||||
func WithWriteContentLength() ServeMuxOption {
|
||||
return func(serveMux *ServeMux) {
|
||||
@@ -405,7 +419,7 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
path = r.URL.RawPath
|
||||
}
|
||||
|
||||
if override := r.Header.Get("X-HTTP-Method-Override"); override != "" && s.isPathLengthFallback(r) {
|
||||
if override := r.Header.Get("X-HTTP-Method-Override"); override != "" && !s.disableHTTPMethodOverride && s.isPathLengthFallback(r) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
_, outboundMarshaler := MarshalerForRequest(s, r)
|
||||
sterr := status.Error(codes.InvalidArgument, err.Error())
|
||||
@@ -467,6 +481,7 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
Err: mse,
|
||||
})
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -509,6 +524,7 @@ func (s *ServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
HTTPStatus: http.StatusBadRequest,
|
||||
Err: mse,
|
||||
})
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
# GEMINI.md - gotext Project Context & Guidelines
|
||||
|
||||
This document serves as the primary source of truth for the `gotext` project architecture, conventions, and workflows. Adherence to these guidelines is mandatory for all development tasks.
|
||||
|
||||
## Project Overview
|
||||
`gotext` is a GNU Gettext implementation for Go (Golang). it provides a thread-safe, hierarchical translation system that supports PO/MO files, plural forms, and context-aware translations.
|
||||
|
||||
## Core Architecture
|
||||
The project follows a hierarchical structure for managing translations:
|
||||
1. **Global Package API (`gotext.go`)**: Provides convenient, global functions (e.g., `Get`, `GetD`, `GetN`) that rely on a shared `globalConfig`.
|
||||
2. **Locales (`locale.go`)**: Represent a specific language (e.g., "en_US", "es_ES"). A `Locale` container manages multiple `Domains`.
|
||||
3. **Domains (`domain.go`)**: The core translation engine for a specific set of messages. It handles:
|
||||
- Message retrieval (with support for plural forms and `msgctxt`).
|
||||
- Header parsing (including plural-form rules).
|
||||
- Thread-safety via `sync.RWMutex`.
|
||||
4. **Translators (`translator.go`)**: An interface for translation sources.
|
||||
- **PO (`po.go`)**: Parsers for human-readable Gettext Portable Object files.
|
||||
- **MO (`mo.go`)**: Parsers for binary Gettext Machine Object files.
|
||||
5. **Plural Forms (`plurals/`)**: A dedicated package for compiling and evaluating GNU Gettext plural form expressions.
|
||||
|
||||
## CLI Tools
|
||||
- **xgotext (`cli/xgotext/`)**: A tool to scan Go source files, extract translatable strings (matching `Get`, `GetD`, etc.), and generate/update PO files.
|
||||
|
||||
## Development Workflows
|
||||
### Branching & PRs
|
||||
- **Feature Branches**: All development MUST happen in dedicated feature branches (e.g., `feature/description-of-change`).
|
||||
- **Target Branch**: All Pull Requests MUST be made against the `master` branch.
|
||||
- **Review Requirement**: No direct commits to `master` are allowed. All changes must be reviewed via a PR.
|
||||
- **GitHub Integration**: If the `gh` CLI tool is installed and authenticated, it should be used for managing Pull Requests, Issues, and Reviews. Otherwise, these tasks must be performed manually through the GitHub web interface.
|
||||
|
||||
### Coding Standards
|
||||
- **Idiomatic Go**: Follow standard Go conventions (`gofmt`, `go vet`).
|
||||
- **Thread Safety**: Ensure all shared state (like in `Domain`) is protected by appropriate synchronization primitives.
|
||||
- **Testing**:
|
||||
- Every new feature or bug fix must include corresponding tests.
|
||||
- Use the `fixtures/` directory for sample PO/MO files.
|
||||
- Run `go test ./...` to ensure no regressions.
|
||||
|
||||
## Key Files & Symbols
|
||||
- `gotext.go`: `Configure`, `Get`, `GetD`, `GetN`.
|
||||
- `locale.go`: `Locale`, `NewLocale`, `AddDomain`.
|
||||
- `domain.go`: `Domain`, `Get`, `GetN`, `parseHeaders`.
|
||||
- `translator.go`: `Translator` interface.
|
||||
- `plurals/compiler.go`: `Compile` function for plural expressions.
|
||||
|
||||
## Instructions for Gemini CLI
|
||||
- Always work in a feature branch.
|
||||
- Before submitting, verify changes with existing and new tests.
|
||||
- Propose a PR to `master` after confirmation.
|
||||
+94
-210
@@ -9,258 +9,142 @@
|
||||
|
||||
[GNU gettext utilities](https://www.gnu.org/software/gettext) for Go.
|
||||
|
||||
`gotext` is a native Go implementation of the GNU Gettext utilities. It provides a thread-safe, flexible, and powerful way to handle internationalization (i18n) and localization (l10n) in your Go applications.
|
||||
|
||||
# Features
|
||||
---
|
||||
|
||||
- Implements GNU gettext support in native Go.
|
||||
- Complete support for [PO files](https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html) including:
|
||||
- Support for multiline strings and headers.
|
||||
- Support for variables inside translation strings using Go's [fmt syntax](https://golang.org/pkg/fmt/).
|
||||
- Support for [pluralization rules](https://www.gnu.org/software/gettext/manual/html_node/Translating-plural-forms.html).
|
||||
- Support for [message contexts](https://www.gnu.org/software/gettext/manual/html_node/Contexts.html).
|
||||
- Support for MO files.
|
||||
- Thread-safe: This package is safe for concurrent use across multiple goroutines.
|
||||
- It works with UTF-8 encoding as it's the default for Go language.
|
||||
- Unit tests available.
|
||||
- Language codes are automatically simplified from the form `en_UK` to `en` if the first isn't available.
|
||||
- Ready to use inside Go templates.
|
||||
- Objects are serializable to []byte to store them in cache.
|
||||
- Support for Go Modules.
|
||||
**📖 [Read the Full Documentation](https://leonelquinteros.github.io/gotext/)**
|
||||
|
||||
---
|
||||
|
||||
# License
|
||||
### Table of Contents
|
||||
- [Features](#features)
|
||||
- [Installation](#installation)
|
||||
- [Getting Started](#getting-started)
|
||||
- [CLI Tool (xgotext)](#cli-tool-xgotext)
|
||||
- [Advanced Usage](#advanced-usage)
|
||||
- [Locale Object](#using-locale-object)
|
||||
- [Plural Forms](#use-plural-forms-of-translations)
|
||||
- [Dynamic Variables](#using-dynamic-variables-on-translations)
|
||||
- [Directory Structure](#locales-directories-structure)
|
||||
- [Contributing](#contributing)
|
||||
- [License](#license)
|
||||
|
||||
[MIT license](LICENSE)
|
||||
---
|
||||
|
||||
## Features
|
||||
- Native Go implementation of Gettext (no external dependencies).
|
||||
- Full support for **PO and MO files**.
|
||||
- **Pluralization rules** support via GNU Gettext plural-form expressions.
|
||||
- **Message context** support (`msgctxt`).
|
||||
- Thread-safe for concurrent use.
|
||||
- Works with UTF-8 by default.
|
||||
- Integrated CLI tool (`xgotext`) for string extraction.
|
||||
- Serializable objects for caching.
|
||||
- Seamless integration with Go's `text/template` and `html/template`.
|
||||
|
||||
# Documentation
|
||||
---
|
||||
|
||||
Refer to package documentation at (https://pkg.go.dev/github.com/leonelquinteros/gotext)
|
||||
|
||||
|
||||
# Installation
|
||||
|
||||
```
|
||||
## Installation
|
||||
```bash
|
||||
go get github.com/leonelquinteros/gotext
|
||||
```
|
||||
|
||||
- There are no requirements or dependencies to use this package.
|
||||
- No need to install GNU gettext utilities (unless specific needs of CLI tools).
|
||||
- No need for environment variables. Some naming conventions are applied but not needed.
|
||||
---
|
||||
|
||||
|
||||
# Usage examples
|
||||
|
||||
## Using package for single language/domain settings
|
||||
|
||||
For quick/simple translations you can use the package level functions directly.
|
||||
## Getting Started
|
||||
For a quick start, use the package-level API:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Configure package
|
||||
gotext.Configure("/path/to/locales/root/dir", "en_UK", "domain-name")
|
||||
// Configure package: locales path, language, and domain
|
||||
gotext.Configure("/path/to/locales", "en_US", "default")
|
||||
|
||||
// Translate text from default domain
|
||||
fmt.Println(gotext.Get("My text on 'domain-name' domain"))
|
||||
// Simple translation
|
||||
fmt.Println(gotext.Get("Hello, world!"))
|
||||
|
||||
// Translate text from a different domain without reconfigure
|
||||
fmt.Println(gotext.GetD("domain2", "Another text on a different domain"))
|
||||
// Translation with variables
|
||||
fmt.Println(gotext.Get("Hello, %s!", "Gopher"))
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Using dynamic variables on translations
|
||||
For more details, see the [Getting Started Guide](docs/GETTING_STARTED.md).
|
||||
|
||||
All translation strings support dynamic variables to be inserted without translate.
|
||||
Use the fmt.Printf syntax (from Go's "fmt" package) to specify how to print the non-translated variable inside the translation string.
|
||||
---
|
||||
|
||||
## CLI Tool (xgotext)
|
||||
`gotext` includes a command-line tool to extract translatable strings from your Go source code.
|
||||
|
||||
**Install xgotext:**
|
||||
```bash
|
||||
go install github.com/leonelquinteros/gotext/cli/xgotext@latest
|
||||
```
|
||||
|
||||
**Extract strings:**
|
||||
```bash
|
||||
xgotext -p . -o locales/en_US/default.po
|
||||
```
|
||||
|
||||
See the [xgotext Documentation](docs/xgotext.md) for full usage details.
|
||||
|
||||
---
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Using Locale object
|
||||
For managing multiple languages or domains independently:
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Configure package
|
||||
gotext.Configure("/path/to/locales/root/dir", "en_UK", "domain-name")
|
||||
|
||||
// Set variables
|
||||
name := "John"
|
||||
|
||||
// Translate text with variables
|
||||
fmt.Println(gotext.Get("Hi, my name is %s", name))
|
||||
}
|
||||
|
||||
l := gotext.NewLocale("/path/to/locales", "es_UY")
|
||||
l.AddDomain("default")
|
||||
fmt.Println(l.Get("Translate this"))
|
||||
```
|
||||
|
||||
|
||||
## Using Locale object
|
||||
|
||||
When having multiple languages/domains/libraries at the same time, you can create Locale objects for each variation
|
||||
so you can handle each settings on their own.
|
||||
### Use plural forms of translations
|
||||
`gotext` handles complex pluralization rules defined in PO headers:
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Create Locale with library path and language code
|
||||
l := gotext.NewLocale("/path/to/locales/root/dir", "es_UY")
|
||||
|
||||
// Load domain '/path/to/locales/root/dir/es_UY/default.po'
|
||||
l.AddDomain("default")
|
||||
|
||||
// Translate text from default domain
|
||||
fmt.Println(l.Get("Translate this"))
|
||||
|
||||
// Load different domain
|
||||
l.AddDomain("translations")
|
||||
|
||||
// Translate text from domain
|
||||
fmt.Println(l.GetD("translations", "Translate this"))
|
||||
}
|
||||
// GetN(singular, plural, quantity, args...)
|
||||
fmt.Println(gotext.GetN("I have one apple.", "I have %d apples.", 5, 5))
|
||||
```
|
||||
See the [Plural Forms Guide](docs/PLURALS.md) for more examples.
|
||||
|
||||
This is also helpful for using inside templates (from the "text/template" package), where you can pass the Locale object to the template.
|
||||
If you set the Locale object as "Loc" in the template, then the template code would look like:
|
||||
|
||||
```
|
||||
{{ .Loc.Get "Translate this" }}
|
||||
```
|
||||
|
||||
|
||||
## Using the Po object to handle .po files and PO-formatted strings
|
||||
|
||||
For when you need to work with PO files and strings,
|
||||
you can directly use the Po object to parse it and access the translations in there in the same way.
|
||||
|
||||
### Using dynamic variables on translations
|
||||
Supports standard `fmt` package syntax:
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Set PO content
|
||||
str := `
|
||||
msgid "Translate this"
|
||||
msgstr "Translated text"
|
||||
|
||||
msgid "Another string"
|
||||
msgstr ""
|
||||
|
||||
msgid "One with var: %s"
|
||||
msgstr "This one sets the var: %s"
|
||||
`
|
||||
|
||||
// Create Po object
|
||||
po := gotext.NewPo()
|
||||
po.Parse(str)
|
||||
|
||||
fmt.Println(po.Get("Translate this"))
|
||||
}
|
||||
name := "John"
|
||||
fmt.Println(gotext.Get("Hi, my name is %s", name))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Use plural forms of translations
|
||||
|
||||
PO format supports defining one or more plural forms for the same translation.
|
||||
Relying on the PO file headers, a Plural-Forms formula can be set on the translation file
|
||||
as defined in (https://www.gnu.org/savannah-checkouts/gnu/gettext/manual/html_node/Plural-forms.html)
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/leonelquinteros/gotext"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Set PO content
|
||||
str := `
|
||||
msgid ""
|
||||
msgstr ""
|
||||
|
||||
# Header below
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Translate this"
|
||||
msgstr "Translated text"
|
||||
|
||||
msgid "Another string"
|
||||
msgstr ""
|
||||
|
||||
msgid "One with var: %s"
|
||||
msgid_plural "Several with vars: %s"
|
||||
msgstr[0] "This one is the singular: %s"
|
||||
msgstr[1] "This one is the plural: %s"
|
||||
`
|
||||
|
||||
// Create Po object
|
||||
po := new(gotext.Po)
|
||||
po.Parse(str)
|
||||
|
||||
fmt.Println(po.GetN("One with var: %s", "Several with vars: %s", 54, v))
|
||||
// "This one is the plural: Variable"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
# Locales directories structure
|
||||
|
||||
The package will assume a directories structure starting with a base path that will be provided to the package configuration
|
||||
or to object constructors depending on the use, but either will use the same convention to lookup inside the base path.
|
||||
|
||||
Inside the base directory where will be the language directories named using the language and country 2-letter codes (en_US, es_AR, ...).
|
||||
All package functions can lookup after the simplified version for each language in case the full code isn't present but the more general language code exists.
|
||||
So if the language set is `en_UK`, but there is no directory named after that code and there is a directory named `en`,
|
||||
all package functions will be able to resolve this generalization and provide translations for the more general library.
|
||||
|
||||
The language codes are assumed to be [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) codes (2-letter codes).
|
||||
That said, most functions will work with any coding standard as long the directory name matches the language code set on the configuration.
|
||||
|
||||
Then, there can be a `LC_MESSAGES` containing all PO files or the PO files themselves.
|
||||
A library directory structure can look like:
|
||||
|
||||
## Locales directories structure
|
||||
The package expects a standard Gettext directory structure:
|
||||
```
|
||||
/path/to/locales
|
||||
/path/to/locales/en_US
|
||||
/path/to/locales/en_US/LC_MESSAGES
|
||||
/path/to/locales/en_US/LC_MESSAGES/default.po
|
||||
/path/to/locales/en_US/LC_MESSAGES/extras.po
|
||||
/path/to/locales/en_UK
|
||||
/path/to/locales/en_UK/LC_MESSAGES
|
||||
/path/to/locales/en_UK/LC_MESSAGES/default.po
|
||||
/path/to/locales/en_UK/LC_MESSAGES/extras.po
|
||||
/path/to/locales/en_AU
|
||||
/path/to/locales/en_AU/LC_MESSAGES
|
||||
/path/to/locales/en_AU/LC_MESSAGES/default.po
|
||||
/path/to/locales/en_AU/LC_MESSAGES/extras.po
|
||||
/path/to/locales/es
|
||||
/path/to/locales/es/default.po
|
||||
/path/to/locales/es/extras.po
|
||||
/path/to/locales/es_ES
|
||||
/path/to/locales/es_ES/default.po
|
||||
/path/to/locales/es_ES/extras.po
|
||||
/path/to/locales/fr
|
||||
/path/to/locales/fr/default.po
|
||||
/path/to/locales/fr/extras.po
|
||||
/en_US
|
||||
/LC_MESSAGES
|
||||
default.po
|
||||
/es_ES
|
||||
default.po
|
||||
```
|
||||
It supports automatic language simplification (e.g., falling back from `en_UK` to `en`).
|
||||
|
||||
And so on...
|
||||
---
|
||||
|
||||
## Contributing
|
||||
We welcome contributions of all kinds!
|
||||
- [Contributing Guidelines](CONTRIBUTING.md)
|
||||
- [Code of Conduct](CODE_OF_CONDUCT.md)
|
||||
|
||||
# Contribute
|
||||
|
||||
- Please, contribute.
|
||||
- Use the package on your projects.
|
||||
- Report issues on Github.
|
||||
- Send pull requests for bugfixes and improvements.
|
||||
- Send proposals on Github issues.
|
||||
---
|
||||
|
||||
## License
|
||||
`gotext` is released under the [MIT License](LICENSE).
|
||||
+1
-1
@@ -55,7 +55,7 @@ func init() {
|
||||
domain: "default",
|
||||
languages: []string{FallbackLocale},
|
||||
library: "/usr/local/share/locale",
|
||||
locales: nil,
|
||||
locales: make([]*Locale, 0),
|
||||
}
|
||||
|
||||
// Register Translator types for gob encoding
|
||||
|
||||
+6
-1
@@ -31,12 +31,17 @@ func SimplifiedLocale(lang string) string {
|
||||
// FormatString applies text formatting only when needed to parse variables.
|
||||
func FormatString(str string, vars ...interface{}) string {
|
||||
if len(vars) > 0 {
|
||||
return fmt.Sprintf(str, vars...)
|
||||
return FormatStringWithArgs(str, vars)
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
// FormatStringWithArgs applies text formatting as a proxy for fmt.Sprintf.
|
||||
func FormatStringWithArgs(str string, vars []interface{}) string {
|
||||
return fmt.Sprintf(str, vars...)
|
||||
}
|
||||
|
||||
// Appendf applies text formatting only when needed to parse variables.
|
||||
func Appendf(b []byte, str string, vars ...interface{}) []byte {
|
||||
if len(vars) > 0 {
|
||||
|
||||
+4
-4
@@ -357,7 +357,7 @@ func (l *Locale) GetTranslations() map[string]*Translation {
|
||||
|
||||
// IsTranslated reports whether a string is translated
|
||||
func (l *Locale) IsTranslated(str string) bool {
|
||||
return l.IsTranslatedND(l.GetDomain(), str, 0)
|
||||
return l.IsTranslatedND(l.GetDomain(), str, 1)
|
||||
}
|
||||
|
||||
// IsTranslatedN reports whether a plural string is translated
|
||||
@@ -367,7 +367,7 @@ func (l *Locale) IsTranslatedN(str string, n int) bool {
|
||||
|
||||
// IsTranslatedD reports whether a domain string is translated
|
||||
func (l *Locale) IsTranslatedD(dom, str string) bool {
|
||||
return l.IsTranslatedND(dom, str, 0)
|
||||
return l.IsTranslatedND(dom, str, 1)
|
||||
}
|
||||
|
||||
// IsTranslatedND reports whether a plural domain string is translated
|
||||
@@ -387,7 +387,7 @@ func (l *Locale) IsTranslatedND(dom, str string, n int) bool {
|
||||
|
||||
// IsTranslatedC reports whether a context string is translated
|
||||
func (l *Locale) IsTranslatedC(str, ctx string) bool {
|
||||
return l.IsTranslatedNDC(l.GetDomain(), str, 0, ctx)
|
||||
return l.IsTranslatedNDC(l.GetDomain(), str, 1, ctx)
|
||||
}
|
||||
|
||||
// IsTranslatedNC reports whether a plural context string is translated
|
||||
@@ -397,7 +397,7 @@ func (l *Locale) IsTranslatedNC(str string, n int, ctx string) bool {
|
||||
|
||||
// IsTranslatedDC reports whether a domain context string is translated
|
||||
func (l *Locale) IsTranslatedDC(dom, str, ctx string) bool {
|
||||
return l.IsTranslatedNDC(dom, str, 0, ctx)
|
||||
return l.IsTranslatedNDC(dom, str, 1, ctx)
|
||||
}
|
||||
|
||||
// IsTranslatedNDC reports whether a plural domain context string is translated
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
site_name: gotext
|
||||
site_description: GNU gettext utilities for Go
|
||||
site_author: Leonel Quinteros
|
||||
repo_url: https://github.com/leonelquinteros/gotext
|
||||
edit_uri: edit/master/docs/
|
||||
|
||||
theme:
|
||||
name: material
|
||||
palette:
|
||||
primary: indigo
|
||||
accent: indigo
|
||||
features:
|
||||
- navigation.tabs
|
||||
- navigation.sections
|
||||
- navigation.top
|
||||
- search.suggest
|
||||
- search.highlight
|
||||
|
||||
nav:
|
||||
- Home: index.md
|
||||
- Getting Started: GETTING_STARTED.md
|
||||
- User Guides:
|
||||
- Plural Forms: PLURALS.md
|
||||
- xgotext CLI: xgotext.md
|
||||
- Character Encoding: ENCODING.md
|
||||
- Best Practices: BEST_PRACTICES.md
|
||||
- API Reference: https://pkg.go.dev/github.com/leonelquinteros/gotext
|
||||
- Contributing:
|
||||
- Guidelines: CONTRIBUTING.md
|
||||
- Code of Conduct: CODE_OF_CONDUCT.md
|
||||
- License: LICENSE.md
|
||||
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
- pymdownx.highlight:
|
||||
anchor_linenums: true
|
||||
- pymdownx.inlinehilite
|
||||
- pymdownx.snippets
|
||||
- pymdownx.superfences
|
||||
+43
-18
@@ -22,7 +22,7 @@ type match struct {
|
||||
closePos int
|
||||
}
|
||||
|
||||
var pat = regexp.MustCompile(`(\?|:|\|\||&&|==|!=|>=|>|<=|<|%|\d+|n)`)
|
||||
var pat = regexp.MustCompile(`(\?|:|\|\||&&|==|!=|>=|>|<=|<|%|\d+|n|\(|\))`)
|
||||
|
||||
type testToken interface {
|
||||
compile(tokens []string) (test test, err error)
|
||||
@@ -161,29 +161,28 @@ func compileEquality(tokens []string, sep string, builder cmpTestBuilder) (test
|
||||
if err != nil {
|
||||
return test, err
|
||||
}
|
||||
if len(split.Left) == 1 && split.Left[0] == "n" {
|
||||
if len(split.Right) != 1 {
|
||||
return test, errors.New("test can only compare n to integers")
|
||||
}
|
||||
i, err := parseUint32(split.Right[0])
|
||||
if contains(split.Left, "n") && !contains(split.Left, "%") && len(split.Left) == 1 {
|
||||
i, err := parseUint32(strings.Join(split.Right, ""))
|
||||
if err != nil {
|
||||
// Try to compile it as a full expression if it's not a simple integer
|
||||
// but for now we follow the existing pattern of expecting integers here
|
||||
// unless it's a mod operation.
|
||||
return test, err
|
||||
}
|
||||
return builder(i, false), nil
|
||||
} else if len(split.Right) == 1 && split.Right[0] == "n" {
|
||||
if len(split.Left) != 1 {
|
||||
return test, errors.New("test can only compare n to integers")
|
||||
}
|
||||
i, err := parseUint32(split.Left[0])
|
||||
} else if contains(split.Right, "n") && !contains(split.Right, "%") && len(split.Right) == 1 {
|
||||
i, err := parseUint32(strings.Join(split.Left, ""))
|
||||
if err != nil {
|
||||
return test, err
|
||||
}
|
||||
return builder(i, true), nil
|
||||
} else if contains(split.Left, "n") && contains(split.Left, "%") {
|
||||
return subPipe(split.Left, split.Right, builder, false)
|
||||
} else if contains(split.Right, "n") && contains(split.Right, "%") {
|
||||
return subPipe(split.Right, split.Left, builder, true)
|
||||
}
|
||||
return test, errors.New("equality test must have 'n' as one of the two tests")
|
||||
|
||||
return test, errors.New("equality test must have 'n' as one of the two tests")
|
||||
}
|
||||
|
||||
var eqToken eqStruct
|
||||
@@ -353,7 +352,7 @@ func split(s string) <-chan string {
|
||||
|
||||
// Tokenizes a string into a list of strings, tokens grouped by parenthesis are
|
||||
// not split! If the string starts with ( and ends in ), those are stripped.
|
||||
func tokenize(s string) []string {
|
||||
func tokenize(s string) ([]string, error) {
|
||||
/*
|
||||
TODO: Properly detect if the string starts with a ( and ends with a )
|
||||
and that those two form a matching pair.
|
||||
@@ -361,7 +360,7 @@ func tokenize(s string) []string {
|
||||
Eg: (foo) -> true; (foo)(bar) -> false;
|
||||
*/
|
||||
if len(s) == 0 {
|
||||
return []string{}
|
||||
return []string{}, nil
|
||||
}
|
||||
if s[0] == '(' && s[len(s)-1] == ')' {
|
||||
s = s[1 : len(s)-1]
|
||||
@@ -372,15 +371,22 @@ func tokenize(s string) []string {
|
||||
if chunk[0] == '(' && chunk[len(chunk)-1] == ')' {
|
||||
ret = append(ret, chunk)
|
||||
} else {
|
||||
for _, token := range pat.FindAllStringSubmatch(chunk, -1) {
|
||||
// Verify all characters in chunk are matched by our pattern
|
||||
matches := pat.FindAllStringSubmatch(chunk, -1)
|
||||
matchedLen := 0
|
||||
for _, token := range matches {
|
||||
ret = append(ret, token[0])
|
||||
matchedLen += len(token[0])
|
||||
}
|
||||
if matchedLen != len(chunk) {
|
||||
return nil, fmt.Errorf("invalid character in expression chunk: %s", chunk)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Empty chunk in string '%s'\n", s)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Compile a string containing a plural form expression to a Expression object.
|
||||
@@ -406,7 +412,10 @@ func contains(haystack []string, needle string) bool {
|
||||
|
||||
// Compiles an expression (ternary or constant)
|
||||
func compileExpression(s string) (expr Expression, err error) {
|
||||
tokens := tokenize(s)
|
||||
tokens, err := tokenize(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if contains(tokens, "?") {
|
||||
return ternaryToken.compile(tokens)
|
||||
}
|
||||
@@ -415,12 +424,28 @@ func compileExpression(s string) (expr Expression, err error) {
|
||||
|
||||
// Compiles a test (comparison)
|
||||
func compileTest(s string) (test test, err error) {
|
||||
tokens := tokenize(s)
|
||||
tokens, err := tokenize(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, tokenDef := range precedence {
|
||||
if contains(tokens, tokenDef.op) {
|
||||
return tokenDef.token.compile(tokens)
|
||||
}
|
||||
}
|
||||
if contains(tokens, "%") {
|
||||
m, err := compileMod(tokens)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pipe{
|
||||
modifier: m,
|
||||
action: equal{value: 0}, // default to testing for 0
|
||||
}, nil
|
||||
}
|
||||
if len(tokens) == 1 && tokens[0] == "n" {
|
||||
return equal{value: 1}, nil
|
||||
}
|
||||
return test, errors.New("cannot compile")
|
||||
}
|
||||
|
||||
|
||||
Generated
Vendored
+17
-13
@@ -28,12 +28,14 @@ import (
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
|
||||
"github.com/opencloud-eu/reva/v2/pkg/appctx"
|
||||
ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/status"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/sharedconf"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/protobuf/proto"
|
||||
@@ -123,20 +125,22 @@ func (s *svc) Authenticate(ctx context.Context, req *gateway.AuthenticateRequest
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, token) // TODO(jfd): hardcoded metadata key. use PerRPCCredentials?
|
||||
|
||||
// create home directory
|
||||
createHomeRes, err := s.CreateHome(ctx, &storageprovider.CreateHomeRequest{})
|
||||
if err != nil {
|
||||
log.Err(err).Msg("error calling CreateHome")
|
||||
return &gateway.AuthenticateResponse{
|
||||
Status: status.NewInternal(ctx, "error creating user home"),
|
||||
}, nil
|
||||
}
|
||||
if res.User.GetId().GetType() != userpb.UserType_USER_TYPE_SERVICE && res.User.GetId().GetType() != userpb.UserType_USER_TYPE_LIGHTWEIGHT {
|
||||
createHomeRes, err := s.CreateHome(ctx, &storageprovider.CreateHomeRequest{})
|
||||
if err != nil {
|
||||
log.Err(err).Msg("error calling CreateHome")
|
||||
return &gateway.AuthenticateResponse{
|
||||
Status: status.NewInternal(ctx, "error creating user home"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if createHomeRes.Status.Code != rpc.Code_CODE_OK && createHomeRes.Status.Code != rpc.Code_CODE_ALREADY_EXISTS {
|
||||
err := status.NewErrorFromCode(createHomeRes.Status.Code, "gateway")
|
||||
log.Err(err).Msg("error calling Createhome")
|
||||
return &gateway.AuthenticateResponse{
|
||||
Status: status.NewInternal(ctx, "error creating user home"),
|
||||
}, nil
|
||||
if createHomeRes.Status.Code != rpc.Code_CODE_OK && createHomeRes.Status.Code != rpc.Code_CODE_ALREADY_EXISTS {
|
||||
err := status.NewErrorFromCode(createHomeRes.Status.Code, "gateway")
|
||||
log.Err(err).Msg("error calling Createhome")
|
||||
return &gateway.AuthenticateResponse{
|
||||
Status: status.NewInternal(ctx, "error creating user home"),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
gwRes := &gateway.AuthenticateResponse{
|
||||
|
||||
Generated
Vendored
+20
-5
@@ -28,20 +28,17 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
collaborationv1beta1 "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
linkv1beta1 "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
registry "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1"
|
||||
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/conversions"
|
||||
"github.com/rs/zerolog/log"
|
||||
"google.golang.org/grpc/codes"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/appctx"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/conversions"
|
||||
ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/publicshare"
|
||||
@@ -51,7 +48,12 @@ import (
|
||||
"github.com/opencloud-eu/reva/v2/pkg/share"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storagespace"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/utils"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
"google.golang.org/grpc/codes"
|
||||
gstatus "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
@@ -114,6 +116,19 @@ func (s *svc) CreateHome(ctx context.Context, req *provider.CreateHomeRequest) (
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
if u.GetId().GetType() == userpb.UserType_USER_TYPE_SERVICE {
|
||||
return &provider.CreateHomeResponse{
|
||||
Status: status.NewInvalid(ctx, "gateway: refusing to create home for service user"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if u.GetId().GetType() == userpb.UserType_USER_TYPE_LIGHTWEIGHT {
|
||||
return &provider.CreateHomeResponse{
|
||||
Status: status.NewInvalid(ctx, "gateway: refusing to create home for lightweight user"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
quotaStr := utils.ReadPlainFromOpaque(req.Opaque, "quota")
|
||||
var quota *provider.Quota
|
||||
if quotaStr != "" {
|
||||
|
||||
Generated
Vendored
+10
-1
@@ -1045,7 +1045,16 @@ func (s *Service) PurgeRecycle(ctx context.Context, req *provider.PurgeRecycleRe
|
||||
}
|
||||
|
||||
func (s *Service) ListGrants(ctx context.Context, req *provider.ListGrantsRequest) (*provider.ListGrantsResponse, error) {
|
||||
grants, err := s.Storage.ListGrants(ctx, req.Ref)
|
||||
fs := s.Storage
|
||||
if req.Opaque != nil && req.Opaque.Map != nil {
|
||||
if _, ok := req.Opaque.Map["with_disabled_spaces"]; ok {
|
||||
if dsfs, ok := fs.(storage.SpaceDisabledListableFS); ok {
|
||||
fs = dsfs.WithDisabledSpaces()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grants, err := fs.ListGrants(ctx, req.Ref)
|
||||
if err != nil {
|
||||
var st *rpc.Status
|
||||
switch err.(type) {
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -157,7 +157,7 @@ func (s *svc) Close() error {
|
||||
}
|
||||
|
||||
func (s *svc) Unprotected() []string {
|
||||
return []string{"/status.php", "/status", "/remote.php/dav/public-files/", "/apps/files/", "/index.php/f/", "/index.php/s/", "/remote.php/dav/ocm/", "/dav/ocm/"}
|
||||
return []string{"/status.php", "/status", "/remote.php/dav/public-files/", "/dav/public-files/", "/apps/files/", "/index.php/f/", "/index.php/s/", "/remote.php/dav/ocm/", "/dav/ocm/"}
|
||||
}
|
||||
|
||||
func (s *svc) Handler() http.Handler {
|
||||
|
||||
+5
-2
@@ -79,7 +79,7 @@ func (m *ImportSpaceMembersMigration) Name() string {
|
||||
}
|
||||
|
||||
func (m *ImportSpaceMembersMigration) Version() int {
|
||||
return 1
|
||||
return 2
|
||||
}
|
||||
|
||||
func (m *ImportSpaceMembersMigration) Migrate() error {
|
||||
@@ -162,7 +162,10 @@ func (m *ImportSpaceMembersMigration) migrateSpace(ctx context.Context, space *p
|
||||
}
|
||||
|
||||
ref := &provider.Reference{ResourceId: space.GetRoot()}
|
||||
grantsRes, err := spClient.ListGrants(ctx, &provider.ListGrantsRequest{Ref: ref})
|
||||
grantsRes, err := spClient.ListGrants(ctx, &provider.ListGrantsRequest{
|
||||
Ref: ref,
|
||||
Opaque: utils.AppendPlainToOpaque(nil, "with_disabled_spaces", "true"),
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
package ignore
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/blobstore"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/lookup"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/options"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/utils/templates"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
// Ignorer handles checking if paths should be ignored in posix operations
|
||||
type Ignorer struct {
|
||||
options *options.Options
|
||||
log *zerolog.Logger
|
||||
personalSpacesRoot string
|
||||
projectSpacesRoot string
|
||||
}
|
||||
|
||||
// NewIgnorer creates a new Ignorer given the posix options and logger
|
||||
func NewIgnorer(options *options.Options, log *zerolog.Logger) *Ignorer {
|
||||
return &Ignorer{
|
||||
options: options,
|
||||
log: log,
|
||||
personalSpacesRoot: filepath.Clean(filepath.Join(options.Root, templates.Base(options.PersonalSpacePathTemplate))),
|
||||
projectSpacesRoot: filepath.Clean(filepath.Join(options.Root, templates.Base(options.GeneralSpacePathTemplate))),
|
||||
}
|
||||
}
|
||||
|
||||
// IsIgnored checking if paths should be ignored in posix operations
|
||||
func (i *Ignorer) IsIgnored(path string) bool {
|
||||
return IsLockFile(path) || IsTrash(path) || i.IsUpload(path) || i.IsInternal(path) || i.IsRootPath(path) || i.IsSpaceRoot(path)
|
||||
}
|
||||
|
||||
func (i *Ignorer) IsUpload(path string) bool {
|
||||
return strings.HasPrefix(path, i.options.UploadDirectory)
|
||||
}
|
||||
|
||||
func (i *Ignorer) IsIndex(path string) bool {
|
||||
return strings.HasPrefix(path, filepath.Join(i.options.Root, "indexes"))
|
||||
}
|
||||
|
||||
func (i *Ignorer) IsTemporary(path string) bool {
|
||||
if filepath.IsAbs(path) {
|
||||
tmpDirPattern := filepath.Join(i.options.Root, "*", "*", blobstore.TMPDir)
|
||||
isTempDir, err := filepath.Match(tmpDirPattern, path)
|
||||
if err != nil {
|
||||
i.log.Error().Err(err).Str("pattern", tmpDirPattern).Str("path", path).Msg("error matching temporary path")
|
||||
return false
|
||||
}
|
||||
isTempParentDir, err := filepath.Match(tmpDirPattern, filepath.Dir(path))
|
||||
if err != nil {
|
||||
i.log.Error().Err(err).Str("pattern", tmpDirPattern).Str("path", filepath.Dir(path)).Msg("error matching temporary path")
|
||||
return false
|
||||
}
|
||||
return isTempDir || isTempParentDir
|
||||
}
|
||||
return path == blobstore.TMPDir || filepath.Dir(path) == blobstore.TMPDir
|
||||
}
|
||||
|
||||
func (i *Ignorer) IsRootPath(path string) bool {
|
||||
return path == i.options.Root ||
|
||||
path == i.personalSpacesRoot ||
|
||||
path == i.projectSpacesRoot
|
||||
}
|
||||
|
||||
func (i *Ignorer) IsSpaceRoot(path string) bool {
|
||||
parent := filepath.Dir(path)
|
||||
return parent == i.personalSpacesRoot || parent == i.projectSpacesRoot
|
||||
}
|
||||
|
||||
func (i *Ignorer) IsInternal(path string) bool {
|
||||
return i.IsIndex(path) || strings.Contains(path, lookup.MetadataDir) || i.IsTemporary(path)
|
||||
}
|
||||
|
||||
func IsLockFile(path string) bool {
|
||||
return strings.HasSuffix(path, ".flock") || strings.HasSuffix(path, ".mlock")
|
||||
}
|
||||
|
||||
func IsTrash(path string) bool {
|
||||
return strings.HasSuffix(path, ".trashinfo") || strings.HasSuffix(path, ".trashitem") || strings.Contains(path, ".Trash")
|
||||
}
|
||||
+11
-2
@@ -81,6 +81,8 @@ type Lookup struct {
|
||||
userMapper usermapper.Mapper
|
||||
tm node.TimeManager
|
||||
log *zerolog.Logger
|
||||
|
||||
listDisabledSpaces bool
|
||||
}
|
||||
|
||||
// New returns a new Lookup instance
|
||||
@@ -186,6 +188,13 @@ func (lu *Lookup) NodeFromResource(ctx context.Context, ref *provider.Reference)
|
||||
return nil, fmt.Errorf("invalid reference %+v. resource_id must be set", ref)
|
||||
}
|
||||
|
||||
// WithDisabledSpaces returns a lookup that can list disabled spaces
|
||||
func (lu *Lookup) WithDisabledSpaces() node.PathLookup {
|
||||
l := *lu
|
||||
l.listDisabledSpaces = true
|
||||
return &l
|
||||
}
|
||||
|
||||
// NodeFromID returns the internal path for the id
|
||||
func (lu *Lookup) NodeFromID(ctx context.Context, id *provider.ResourceId) (n *node.Node, err error) {
|
||||
ctx, span := tracer.Start(ctx, "NodeFromID")
|
||||
@@ -197,7 +206,7 @@ func (lu *Lookup) NodeFromID(ctx context.Context, id *provider.ResourceId) (n *n
|
||||
// The Resource references the root of a space
|
||||
return lu.NodeFromSpaceID(ctx, id.SpaceId)
|
||||
}
|
||||
return node.ReadNode(ctx, lu, id.SpaceId, id.OpaqueId, "", false, nil, false)
|
||||
return node.ReadNode(ctx, lu, id.SpaceId, id.OpaqueId, "", lu.listDisabledSpaces, nil, false)
|
||||
}
|
||||
|
||||
// Pathify segments the beginning of a string into depth segments of width length
|
||||
@@ -218,7 +227,7 @@ func Pathify(id string, depth, width int) string {
|
||||
|
||||
// NodeFromSpaceID converts a resource id into a Node
|
||||
func (lu *Lookup) NodeFromSpaceID(ctx context.Context, spaceID string) (n *node.Node, err error) {
|
||||
node, err := node.ReadNode(ctx, lu, spaceID, spaceID, "", false, nil, false)
|
||||
node, err := node.ReadNode(ctx, lu, spaceID, spaceID, "", lu.listDisabledSpaces, nil, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+6
@@ -225,6 +225,12 @@ func New(o *options.Options, stream events.Stream, cache, historyCache *idcache.
|
||||
return fs, nil
|
||||
}
|
||||
|
||||
func (fs *posixFS) WithDisabledSpaces() storage.FS {
|
||||
f := *fs
|
||||
f.FS = fs.FS.(storage.SpaceDisabledListableFS).WithDisabledSpaces()
|
||||
return &f
|
||||
}
|
||||
|
||||
// WarmupIDCache allows triggering a posix fs scan and id cache warmup manually.
|
||||
func (fs *posixFS) WarmupIDCache(root string, assimilate, onlyDirty bool) error {
|
||||
return fs.tree.WarmupIDCache(root, assimilate, onlyDirty)
|
||||
|
||||
+10
-9
@@ -41,6 +41,7 @@ import (
|
||||
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/events"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/ignore"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/watcher"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/prefixes"
|
||||
@@ -368,7 +369,7 @@ func (t *Tree) findSpaceId(path string) (string, error) {
|
||||
spaceCandidate := path
|
||||
for strings.HasPrefix(spaceCandidate, t.options.Root) {
|
||||
// jail at root
|
||||
if t.isRootPath(spaceCandidate) {
|
||||
if t.Ignorer.IsRootPath(spaceCandidate) {
|
||||
return "", ErrRootReached
|
||||
}
|
||||
|
||||
@@ -709,7 +710,7 @@ assimilate:
|
||||
// The Space's name attribute might not match the directory name. Use the name as
|
||||
// it was set before. Also the space root doesn't have a 'type' attribute
|
||||
// currently so only set it for normal directories.
|
||||
if t.isSpaceRoot(path) {
|
||||
if t.Ignorer.IsSpaceRoot(path) {
|
||||
if previousAttribs != nil && previousAttribs[prefixes.NameAttr] != nil {
|
||||
attributes[prefixes.NameAttr] = previousAttribs[prefixes.NameAttr]
|
||||
}
|
||||
@@ -866,17 +867,17 @@ func (t *Tree) WarmupIDCache(root string, assimilate, onlyDirty bool) error {
|
||||
}
|
||||
|
||||
// skip irrelevant files
|
||||
if t.isInternal(path) ||
|
||||
isLockFile(path) ||
|
||||
isTrash(path) ||
|
||||
t.isUpload(path) ||
|
||||
t.isIndex(path) {
|
||||
if t.Ignorer.IsInternal(path) ||
|
||||
ignore.IsLockFile(path) ||
|
||||
ignore.IsTrash(path) ||
|
||||
t.Ignorer.IsUpload(path) ||
|
||||
t.Ignorer.IsIndex(path) {
|
||||
if info.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if t.isRootPath(path) {
|
||||
if t.Ignorer.IsRootPath(path) {
|
||||
return nil // ignore the root paths
|
||||
}
|
||||
|
||||
@@ -982,7 +983,7 @@ func (t *Tree) WarmupIDCache(root string, assimilate, onlyDirty bool) error {
|
||||
})
|
||||
|
||||
for dir, size := range sizes {
|
||||
if t.isRootPath(dir) {
|
||||
if t.Ignorer.IsRootPath(dir) {
|
||||
continue
|
||||
}
|
||||
spaceID, id, err := t.lookup.IDsForPath(context.Background(), dir)
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -84,7 +84,7 @@ func (w *CephFSWatcher) Watch(topic string) {
|
||||
continue
|
||||
}
|
||||
|
||||
if w.tree.isIgnored(ev.Path) {
|
||||
if w.tree.Ignorer.IsIgnored(ev.Path) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -83,7 +83,7 @@ start:
|
||||
w.log.Error().Err(err).Str("line", line).Msg("error unmarshalling line")
|
||||
continue
|
||||
}
|
||||
if w.tree.isIgnored(ev.Path) {
|
||||
if w.tree.Ignorer.IsIgnored(ev.Path) {
|
||||
continue
|
||||
}
|
||||
go func() {
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -66,7 +66,7 @@ func (w *GpfsWatchFolderWatcher) Watch(topic string) {
|
||||
continue
|
||||
}
|
||||
|
||||
if w.tree.isIgnored(lwev.Path) {
|
||||
if w.tree.Ignorer.IsIgnored(lwev.Path) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
+7
-63
@@ -41,8 +41,8 @@ import (
|
||||
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/events"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/blobstore"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/idcache"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/ignore"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/lookup"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/options"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/trashbin"
|
||||
@@ -54,7 +54,6 @@ import (
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/permissions"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/tree/propagator"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/usermapper"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/utils/templates"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/utils"
|
||||
)
|
||||
|
||||
@@ -93,9 +92,8 @@ type Tree struct {
|
||||
idResolver IDResolver // points at the lookup but can be overridden for testing
|
||||
assimilateFunc func(item scanItem) error // function to call to assimilate a node, can be overridden for testing
|
||||
|
||||
options *options.Options
|
||||
personalSpacesRoot string
|
||||
projectSpacesRoot string
|
||||
options *options.Options
|
||||
Ignorer *ignore.Ignorer
|
||||
|
||||
userMapper usermapper.Mapper
|
||||
idCache *idcache.IDCache
|
||||
@@ -128,10 +126,9 @@ func New(lu node.PathLookup, bs node.Blobstore, um usermapper.Mapper, trashbin *
|
||||
scanDebouncer: NewScanDebouncer(o.ScanDebounceDelay, func(item scanItem) {
|
||||
scanQueue <- item
|
||||
}),
|
||||
es: es,
|
||||
log: log,
|
||||
personalSpacesRoot: filepath.Clean(filepath.Join(o.Root, templates.Base(o.PersonalSpacePathTemplate))),
|
||||
projectSpacesRoot: filepath.Clean(filepath.Join(o.Root, templates.Base(o.GeneralSpacePathTemplate))),
|
||||
es: es,
|
||||
log: log,
|
||||
Ignorer: ignore.NewIgnorer(o, log),
|
||||
}
|
||||
t.idResolver = t.lookup
|
||||
t.assimilateFunc = t.assimilate
|
||||
@@ -499,7 +496,7 @@ func (t *Tree) ListFolder(ctx context.Context, n *node.Node) ([]*node.Node, erro
|
||||
g.Go(func() error {
|
||||
defer close(work)
|
||||
for _, name := range names {
|
||||
if t.isInternal(name) || isLockFile(name) || isTrash(name) {
|
||||
if t.Ignorer.IsInternal(name) || ignore.IsLockFile(name) || ignore.IsTrash(name) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -776,56 +773,3 @@ func (t *Tree) createDirNode(ctx context.Context, n *node.Node) (err error) {
|
||||
}
|
||||
return n.SetXattrsWithContext(ctx, attributes, false)
|
||||
}
|
||||
|
||||
func (t *Tree) isIgnored(path string) bool {
|
||||
return isLockFile(path) || isTrash(path) || t.isUpload(path) || t.isInternal(path) || t.isRootPath(path) || t.isSpaceRoot(path)
|
||||
}
|
||||
|
||||
func (t *Tree) isUpload(path string) bool {
|
||||
return strings.HasPrefix(path, t.options.UploadDirectory)
|
||||
}
|
||||
|
||||
func (t *Tree) isIndex(path string) bool {
|
||||
return strings.HasPrefix(path, filepath.Join(t.options.Root, "indexes"))
|
||||
}
|
||||
|
||||
func (t *Tree) isTemporary(path string) bool {
|
||||
if filepath.IsAbs(path) {
|
||||
tmpDirPattern := filepath.Join(t.options.Root, "*", "*", blobstore.TMPDir)
|
||||
isTempDir, err := filepath.Match(tmpDirPattern, path)
|
||||
if err != nil {
|
||||
t.log.Error().Err(err).Str("pattern", tmpDirPattern).Str("path", path).Msg("error matching temporary path")
|
||||
return false
|
||||
}
|
||||
isTempParentDir, err := filepath.Match(tmpDirPattern, filepath.Dir(path))
|
||||
if err != nil {
|
||||
t.log.Error().Err(err).Str("pattern", tmpDirPattern).Str("path", filepath.Dir(path)).Msg("error matching temporary path")
|
||||
return false
|
||||
}
|
||||
return isTempDir || isTempParentDir
|
||||
}
|
||||
return path == blobstore.TMPDir || filepath.Dir(path) == blobstore.TMPDir
|
||||
}
|
||||
|
||||
func (t *Tree) isRootPath(path string) bool {
|
||||
return path == t.options.Root ||
|
||||
path == t.personalSpacesRoot ||
|
||||
path == t.projectSpacesRoot
|
||||
}
|
||||
|
||||
func (t *Tree) isSpaceRoot(path string) bool {
|
||||
parent := filepath.Dir(path)
|
||||
return parent == t.personalSpacesRoot || parent == t.projectSpacesRoot
|
||||
}
|
||||
|
||||
func (t *Tree) isInternal(path string) bool {
|
||||
return t.isIndex(path) || strings.Contains(path, lookup.MetadataDir) || t.isTemporary(path)
|
||||
}
|
||||
|
||||
func isLockFile(path string) bool {
|
||||
return strings.HasSuffix(path, ".flock") || strings.HasSuffix(path, ".mlock")
|
||||
}
|
||||
|
||||
func isTrash(path string) bool {
|
||||
return strings.HasSuffix(path, ".trashinfo") || strings.HasSuffix(path, ".trashitem") || strings.Contains(path, ".Trash")
|
||||
}
|
||||
Generated
Vendored
+3
-2
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/ignore"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/options"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/watcher"
|
||||
)
|
||||
@@ -212,8 +213,8 @@ func isSubpath(root, p string) bool {
|
||||
// isIgnored checks if the path is ignored by its tree.
|
||||
func isPathIgnored(tree *Tree, path string) bool {
|
||||
|
||||
isLockFile := isLockFile(path)
|
||||
isTrash := isTrash(path)
|
||||
isLockFile := ignore.IsLockFile(path)
|
||||
isTrash := ignore.IsTrash(path)
|
||||
isUpload := tree.isUpload(path)
|
||||
isInternal := tree.isInternal(path)
|
||||
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -90,7 +90,7 @@ func (iw *InotifyWatcher) Watch(path string) {
|
||||
for {
|
||||
select {
|
||||
case event := <-events:
|
||||
if iw.tree.isIgnored(event.Filename) {
|
||||
if iw.tree.Ignorer.IsIgnored(event.Filename) {
|
||||
continue
|
||||
}
|
||||
for _, e := range event.Events {
|
||||
|
||||
Generated
Vendored
+7
@@ -568,6 +568,13 @@ func (fs *Decomposedfs) Postprocessing(ch <-chan events.Event) {
|
||||
}
|
||||
}
|
||||
|
||||
// WithDisabledSpaces returns a filesystem that can list disabled spaces
|
||||
func (fs *Decomposedfs) WithDisabledSpaces() storage.FS {
|
||||
f := *fs
|
||||
f.lu = f.lu.WithDisabledSpaces()
|
||||
return &f
|
||||
}
|
||||
|
||||
// Shutdown shuts down the storage
|
||||
func (fs *Decomposedfs) Shutdown(ctx context.Context) error {
|
||||
return nil
|
||||
|
||||
Generated
Vendored
+11
-2
@@ -57,6 +57,8 @@ type Lookup struct {
|
||||
|
||||
metadataBackend metadata.Backend
|
||||
tm node.TimeManager
|
||||
|
||||
listDisabledSpaces bool
|
||||
}
|
||||
|
||||
// New returns a new Lookup instance
|
||||
@@ -140,6 +142,13 @@ func (lu *Lookup) NodeFromResource(ctx context.Context, ref *provider.Reference)
|
||||
return nil, fmt.Errorf("invalid reference %+v. resource_id must be set", ref)
|
||||
}
|
||||
|
||||
// WithDisabledSpaces returns a lookup that can list disabled spaces
|
||||
func (lu *Lookup) WithDisabledSpaces() node.PathLookup {
|
||||
l := *lu
|
||||
l.listDisabledSpaces = true
|
||||
return &l
|
||||
}
|
||||
|
||||
// NodeFromID returns the internal path for the id
|
||||
func (lu *Lookup) NodeFromID(ctx context.Context, id *provider.ResourceId) (n *node.Node, err error) {
|
||||
ctx, span := tracer.Start(ctx, "NodeFromID")
|
||||
@@ -151,7 +160,7 @@ func (lu *Lookup) NodeFromID(ctx context.Context, id *provider.ResourceId) (n *n
|
||||
// The Resource references the root of a space
|
||||
return lu.NodeFromSpaceID(ctx, id.SpaceId)
|
||||
}
|
||||
return node.ReadNode(ctx, lu, id.SpaceId, id.OpaqueId, "", false, nil, false)
|
||||
return node.ReadNode(ctx, lu, id.SpaceId, id.OpaqueId, "", lu.listDisabledSpaces, nil, false)
|
||||
}
|
||||
|
||||
// Pathify segments the beginning of a string into depth segments of width length
|
||||
@@ -172,7 +181,7 @@ func Pathify(id string, depth, width int) string {
|
||||
|
||||
// NodeFromSpaceID converts a resource id into a Node
|
||||
func (lu *Lookup) NodeFromSpaceID(ctx context.Context, spaceID string) (n *node.Node, err error) {
|
||||
node, err := node.ReadNode(ctx, lu, spaceID, spaceID, "", false, nil, false)
|
||||
node, err := node.ReadNode(ctx, lu, spaceID, spaceID, "", lu.listDisabledSpaces, nil, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+1
@@ -148,6 +148,7 @@ type Tree interface {
|
||||
|
||||
// PathLookup defines the interface for the lookup component
|
||||
type PathLookup interface {
|
||||
WithDisabledSpaces() PathLookup
|
||||
NodeFromSpaceID(ctx context.Context, spaceID string) (n *Node, err error)
|
||||
NodeFromResource(ctx context.Context, ref *provider.Reference) (*Node, error)
|
||||
NodeFromID(ctx context.Context, id *provider.ResourceId) (n *Node, err error)
|
||||
|
||||
+6
@@ -152,6 +152,12 @@ type FS interface {
|
||||
GetHome(ctx context.Context) (string, error)
|
||||
}
|
||||
|
||||
// SpaceDisabledListableFS is the interface to implement for storage drivers that
|
||||
// can list resources in disabled spaces.
|
||||
type SpaceDisabledListableFS interface {
|
||||
WithDisabledSpaces() FS
|
||||
}
|
||||
|
||||
// UnscopeFunc is a function that unscopes a user
|
||||
type UnscopeFunc func()
|
||||
|
||||
|
||||
Generated
Vendored
+7
@@ -51,6 +51,13 @@ func NewFS(next storage.FS, hooks ...Hook) *FS {
|
||||
}
|
||||
}
|
||||
|
||||
// WithDisabledSpaces returns a filesystem that can list disabled spaces
|
||||
func (fs *FS) WithDisabledSpaces() storage.FS {
|
||||
f := *fs
|
||||
f.next = fs.next.(storage.SpaceDisabledListableFS).WithDisabledSpaces()
|
||||
return &f
|
||||
}
|
||||
|
||||
// ListUploadSessions returns the upload sessions matching the given filter
|
||||
func (f *FS) ListUploadSessions(ctx context.Context, filter storage.UploadSessionFilter) ([]storage.UploadSession, error) {
|
||||
return f.next.(storage.UploadSessionLister).ListUploadSessions(ctx, filter)
|
||||
|
||||
+12
-1
@@ -8,6 +8,16 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.12.3] - 2026-05-03
|
||||
|
||||
### Added
|
||||
|
||||
- Add OpenTelemetry semantic-convention compliant HTTP server metric middleware for `http.server.request.duration`, `http.server.active_requests`, `http.server.request.body.size`, and `http.server.response.body.size`.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Deprecate legacy metric middleware for `request_duration_millis`, `requests_inflight`, and `response_size_bytes`. These remain available for backward compatibility.
|
||||
|
||||
## [0.12.2] - 2025-09-02
|
||||
|
||||
### Fixed
|
||||
@@ -269,7 +279,8 @@ It contains instrumentation for trace and depends on:
|
||||
[#2]: https://github.com/riandyrn/otelchi/pull/2
|
||||
[#1]: https://github.com/riandyrn/otelchi/pull/1
|
||||
|
||||
[Unreleased]: https://github.com/riandyrn/otelchi/compare/v0.12.2...HEAD
|
||||
[Unreleased]: https://github.com/riandyrn/otelchi/compare/v0.12.3...HEAD
|
||||
[0.12.3]: https://github.com/riandyrn/otelchi/releases/tag/v0.12.3
|
||||
[0.12.2]: https://github.com/riandyrn/otelchi/releases/tag/v0.12.2
|
||||
[0.12.1]: https://github.com/riandyrn/otelchi/releases/tag/v0.12.1
|
||||
[0.12.0]: https://github.com/riandyrn/otelchi/releases/tag/v0.12.0
|
||||
|
||||
+11
@@ -22,6 +22,17 @@ $ go get github.com/riandyrn/otelchi
|
||||
|
||||
See [examples](./examples) for details.
|
||||
|
||||
## Metrics
|
||||
|
||||
The `metric` package provides OpenTelemetry semantic-convention compliant HTTP server metric middleware:
|
||||
|
||||
- `http.server.request.duration`
|
||||
- `http.server.active_requests`
|
||||
- `http.server.request.body.size`
|
||||
- `http.server.response.body.size`
|
||||
|
||||
Legacy metric middleware for `request_duration_millis`, `requests_inflight`, and `response_size_bytes` is still available but deprecated.
|
||||
|
||||
## Why Port This?
|
||||
|
||||
I was planning to make this project as part of the Open Telemetry Go instrumentation project. However, based on [this comment](https://github.com/open-telemetry/opentelemetry-go-contrib/pull/986#issuecomment-941280855) they no longer accept new instrumentation. This is why I maintain this project here.
|
||||
+1
-1
@@ -2,5 +2,5 @@ package version
|
||||
|
||||
// Version is the current release version of otelchi in use.
|
||||
func Version() string {
|
||||
return "0.12.2"
|
||||
return "0.12.3"
|
||||
}
|
||||
Generated
Vendored
-11
@@ -47,7 +47,6 @@ type config struct {
|
||||
TracerProvider trace.TracerProvider
|
||||
MeterProvider metric.MeterProvider
|
||||
SpanKind trace.SpanKind
|
||||
SpanStartOptions []trace.SpanStartOption
|
||||
SpanAttributes []attribute.KeyValue
|
||||
MetricAttributes []attribute.KeyValue
|
||||
MetricAttributesFn func(ctx context.Context) []attribute.KeyValue
|
||||
@@ -204,16 +203,6 @@ func WithMessageEvents(events ...Event) Option {
|
||||
})
|
||||
}
|
||||
|
||||
// WithSpanOptions configures an additional set of
|
||||
// trace.SpanOptions, which are applied to each new span.
|
||||
//
|
||||
// Deprecated: It is only used by the deprecated interceptor, and is unused by [NewClientHandler] and [NewServerHandler].
|
||||
func WithSpanOptions(opts ...trace.SpanStartOption) Option {
|
||||
return optionFunc(func(c *config) {
|
||||
c.SpanStartOptions = append(c.SpanStartOptions, opts...)
|
||||
})
|
||||
}
|
||||
|
||||
// WithSpanKind returns an Option to set the span kind for spans created by
|
||||
// the handler.
|
||||
//
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.41.0"
|
||||
grpc_codes "google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal/parse.go
Generated
Vendored
+1
-1
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
oldsemconv "go.opentelemetry.io/otel/semconv/v1.37.0" //nolint:depguard // Use of v1.37.0 is required for backward compatibility stability opt-in.
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.41.0"
|
||||
)
|
||||
|
||||
// ParseFullMethod returns a span name following the OpenTelemetry semantic
|
||||
|
||||
Generated
Vendored
+31
-19
@@ -13,8 +13,8 @@ import (
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
oldrpcconv "go.opentelemetry.io/otel/semconv/v1.37.0/rpcconv" //nolint:depguard // Use of v1.37.0 is required for backward compatibility stability opt-in.
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0/rpcconv"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.41.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.41.0/rpcconv"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
grpc_codes "google.golang.org/grpc/codes"
|
||||
@@ -367,27 +367,39 @@ func (*config) handleRPC(
|
||||
span.End()
|
||||
}
|
||||
|
||||
var metricAttrs []attribute.KeyValue
|
||||
if gctx != nil {
|
||||
// Don't use gctx.metricAttrSet here, because it requires passing
|
||||
// multiple RecordOptions, which would call metric.mergeSets and
|
||||
// allocate a new set for each Record call.
|
||||
metricAttrs = make([]attribute.KeyValue, 0, len(gctx.metricAttrs)+1)
|
||||
metricAttrs = append(metricAttrs, gctx.metricAttrs...)
|
||||
}
|
||||
metricAttrs = append(metricAttrs, rpcStatusAttr)
|
||||
// Allocate vararg slice once.
|
||||
recordOpts := []metric.RecordOption{metric.WithAttributeSet(attribute.NewSet(metricAttrs...))}
|
||||
|
||||
// Use floating point division here for higher precision (instead of Millisecond method).
|
||||
// Measure right before calling Record() to capture as much elapsed time as possible.
|
||||
elapsedTime := float64(rs.EndTime.Sub(rs.BeginTime)) / float64(time.Second)
|
||||
var durationEnabled bool
|
||||
var oldDurationEnabled bool
|
||||
|
||||
if duration != nil {
|
||||
duration.Record(ctx, elapsedTime, recordOpts...)
|
||||
durationEnabled = duration.Enabled(ctx)
|
||||
}
|
||||
if oldDuration != nil {
|
||||
oldDuration.Record(ctx, elapsedTime*1000.0, recordOpts...)
|
||||
oldDurationEnabled = oldDuration.Enabled(ctx)
|
||||
}
|
||||
|
||||
if durationEnabled || oldDurationEnabled {
|
||||
var metricAttrs []attribute.KeyValue
|
||||
if gctx != nil {
|
||||
// Don't use gctx.metricAttrSet here, because it requires passing
|
||||
// multiple RecordOptions, which would call metric.mergeSets and
|
||||
// allocate a new set for each Record call.
|
||||
metricAttrs = make([]attribute.KeyValue, 0, len(gctx.metricAttrs)+1)
|
||||
metricAttrs = append(metricAttrs, gctx.metricAttrs...)
|
||||
}
|
||||
metricAttrs = append(metricAttrs, rpcStatusAttr)
|
||||
// Allocate vararg slice once.
|
||||
recordOpts := []metric.RecordOption{metric.WithAttributeSet(attribute.NewSet(metricAttrs...))}
|
||||
|
||||
// Use floating point division here for higher precision (instead of Millisecond method).
|
||||
// Measure right before calling Record() to capture as much elapsed time as possible.
|
||||
elapsedTime := float64(rs.EndTime.Sub(rs.BeginTime)) / float64(time.Second)
|
||||
|
||||
if durationEnabled {
|
||||
duration.Record(ctx, elapsedTime, recordOpts...)
|
||||
}
|
||||
if oldDurationEnabled {
|
||||
oldDuration.Record(ctx, elapsedTime*1000.0, recordOpts...)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -4,4 +4,4 @@
|
||||
package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
|
||||
|
||||
// Version is the current release version of the gRPC instrumentation.
|
||||
const Version = "0.68.0"
|
||||
const Version = "0.69.0"
|
||||
+11
-7
@@ -96,9 +96,9 @@ linters:
|
||||
- "!**/exporters/zipkin/**"
|
||||
deny:
|
||||
- pkg: go.opentelemetry.io/otel/semconv
|
||||
desc: "Use go.opentelemetry.io/otel/semconv/v1.40.0 instead. If a newer semconv version has been released, update the depguard rule."
|
||||
desc: "Use go.opentelemetry.io/otel/semconv/v1.41.0 instead. If a newer semconv version has been released, update the depguard rule."
|
||||
allow:
|
||||
- go.opentelemetry.io/otel/semconv/v1.40.0
|
||||
- go.opentelemetry.io/otel/semconv/v1.41.0
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
- appendAssign
|
||||
@@ -134,13 +134,16 @@ linters:
|
||||
strconcat: true
|
||||
revive:
|
||||
confidence: 0.01
|
||||
enable-all-rules: false
|
||||
enable-default-rules: true
|
||||
max-open-files: 2048
|
||||
rules:
|
||||
- name: blank-imports
|
||||
- name: bool-literal-in-expr
|
||||
- name: constant-logical-expr
|
||||
- name: context-as-argument
|
||||
arguments:
|
||||
- allowTypesBefore: '*testing.T'
|
||||
- allow-types-before: '*testing.T'
|
||||
disabled: true
|
||||
- name: context-keys-type
|
||||
- name: deep-exit
|
||||
@@ -152,7 +155,7 @@ linters:
|
||||
- name: duplicated-imports
|
||||
- name: early-return
|
||||
arguments:
|
||||
- preserveScope
|
||||
- preserve-scope
|
||||
- name: empty-block
|
||||
- name: empty-lines
|
||||
- name: error-naming
|
||||
@@ -161,7 +164,7 @@ linters:
|
||||
- name: errorf
|
||||
- name: exported
|
||||
arguments:
|
||||
- sayRepetitiveInsteadOfStutters
|
||||
- say-repetitive-instead-of-stutters
|
||||
- name: flag-parameter
|
||||
- name: identical-branches
|
||||
- name: if-return
|
||||
@@ -169,11 +172,12 @@ linters:
|
||||
- name: increment-decrement
|
||||
- name: indent-error-flow
|
||||
arguments:
|
||||
- preserveScope
|
||||
- preserve-scope
|
||||
- name: package-comments
|
||||
- name: range
|
||||
- name: range-val-in-closure
|
||||
- name: range-val-address
|
||||
- name: receiver-naming
|
||||
- name: redefines-builtin-id
|
||||
- name: string-format
|
||||
arguments:
|
||||
@@ -183,7 +187,7 @@ linters:
|
||||
- name: struct-tag
|
||||
- name: superfluous-else
|
||||
arguments:
|
||||
- preserveScope
|
||||
- preserve-scope
|
||||
- name: time-equal
|
||||
- name: unconditional-recursion
|
||||
- name: unexported-return
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
# Agent Guide for opentelemetry-go
|
||||
|
||||
This file contains active, task-oriented instructions for autonomous and semi-autonomous coding agents working in this repository.
|
||||
|
||||
Before starting any task, read `.github/copilot-instructions.md`, `CONTRIBUTING.md`, and this file.
|
||||
Treat `.github/copilot-instructions.md` as global passive guidance for every task, including docs-only and review-only work.
|
||||
|
||||
## Core expectations
|
||||
|
||||
- Preserve OpenTelemetry specification compliance, API stability, and idiomatic Go.
|
||||
- Prefer minimal, surgical changes over broad refactors or speculative cleanup.
|
||||
- Read the package you are editing and match its existing naming, option types, error handling, comments, tests, and concurrency patterns.
|
||||
- Keep public APIs backward compatible unless the task explicitly requires a breaking change.
|
||||
- Keep telemetry resilient and loosely coupled. Do not introduce behavior that can unexpectedly interfere with host applications.
|
||||
- Inspect boundaries carefully: input validation, resource limits, cancellation, shutdown, error propagation, concurrency, and memory growth.
|
||||
- Prefer fail-safe behavior and explicit invariants over implicit assumptions.
|
||||
- Keep dependencies minimal and justified.
|
||||
- Preserve host-application safety: telemetry should not panic, block indefinitely, or amplify attacker-controlled input.
|
||||
- Be conservative on hot paths. Avoid unnecessary allocations, reflection, interface churn, blocking, global state, and high-cardinality telemetry.
|
||||
- Write comments only for intent, invariants, and non-obvious constraints. Do not add comments that restate the code.
|
||||
|
||||
## Default workflow
|
||||
|
||||
For new features and behavior changes, use this order unless the task explicitly says otherwise:
|
||||
|
||||
1. Read the relevant package, its tests, and any package docs or `README.md`.
|
||||
2. Add or update a failing unit test that captures the required behavior or regression.
|
||||
3. Implement the smallest change that makes the test pass.
|
||||
4. Refactor only after the behavior is locked in, and only if the refactor keeps the diff focused.
|
||||
5. If the changed code is on a hot path or performance-sensitive, inspect existing benchmarks and run them. Add a benchmark if coverage is missing.
|
||||
6. Update documentation artifacts as needed while the context is fresh. Follow the documentation and changelog conventions below for the specific updates required.
|
||||
7. Run `make precommit` each time before considering the work complete.
|
||||
|
||||
For docs-only, test-only, or review-only tasks, still start with the required repository guidance above, then skip the workflow steps that do not apply while keeping the same discipline around scope, verification, and repository conventions.
|
||||
|
||||
## Verification
|
||||
|
||||
- Use `make` as the canonical repository verification command. The default target is `precommit`.
|
||||
- `make precommit` is the expected final verification step for linting, generation, README checks, module checks, and tests.
|
||||
- During iteration, targeted commands are fine for fast feedback, but do not stop there if the task changes code.
|
||||
- If you touch performance-sensitive code, run focused benchmarks and compare the results using `benchstat` in addition to `make`.
|
||||
|
||||
## Documentation and changelog
|
||||
|
||||
- Non-internal, non-test packages should have Go doc comments, usually in `doc.go`.
|
||||
- Non-internal, non-test, non-documentation packages should also have a `README.md` with at least a title and a `pkg.go.dev` badge.
|
||||
- Prefer examples over long code snippets in GoDoc when practical.
|
||||
- Keep docs aligned with actual behavior. Do not leave stale comments, stale examples, or stale package documentation behind.
|
||||
- For user-visible changes, update `CHANGELOG.md` under the appropriate `Added`, `Changed`, `Deprecated`, `Fixed`, or `Removed` section within `## [Unreleased]`.
|
||||
|
||||
## Repository habits
|
||||
|
||||
- Prefer focused diffs. Avoid drive-by cleanup.
|
||||
- Follow existing option patterns and exported API conventions instead of inventing new abstractions.
|
||||
- Generated files are checked in. If your change affects generation, keep generated output up to date.
|
||||
- Prefer fast local search tools such as `rg` when exploring the repository.
|
||||
- When changing behavior, make the invariants explicit in tests.
|
||||
|
||||
## Personas
|
||||
|
||||
### Feature Agent
|
||||
|
||||
Use this persona for new behavior, new API surface, or spec-driven feature work.
|
||||
|
||||
- Start with a failing unit test.
|
||||
- Confirm the expected behavior against the spec, existing package behavior, and public API compatibility.
|
||||
- Implement the smallest viable change.
|
||||
- Update GoDoc, examples, `README.md`, and `CHANGELOG.md` when the change is user-visible.
|
||||
- If the feature touches a hot path, check benchmarks and add one if the coverage is missing.
|
||||
|
||||
### Refactoring Agent
|
||||
|
||||
Use this persona when improving structure without intentionally changing behavior.
|
||||
|
||||
- Treat behavior preservation as the default contract.
|
||||
- Add or tighten tests before moving code if current behavior is not already pinned down.
|
||||
- Avoid broad rewrites, clever abstractions, or package-wide cleanup unless explicitly requested.
|
||||
- If a refactor touches a hot path, benchmark before and after.
|
||||
- Keep API shape, semantics, concurrency guarantees, and failure modes unchanged unless the task says otherwise.
|
||||
|
||||
### Test Agent
|
||||
|
||||
Use this persona when adding missing coverage, reproducing bugs, or hardening regressions.
|
||||
|
||||
- Reproduce the bug or missing behavior with the smallest failing test you can.
|
||||
- Prefer testing public behavior and externally visible invariants.
|
||||
- Add targeted regression tests before changing production code.
|
||||
- Only change production code when it is required to make the tested behavior correct or testable.
|
||||
- Keep tests deterministic, readable, and aligned with package patterns.
|
||||
|
||||
### Performance Agent
|
||||
|
||||
Use this persona for hot-path work, allocation reduction, or throughput and latency improvements.
|
||||
|
||||
- Benchmark first to establish a baseline.
|
||||
- Prefer changes that reduce allocations, copying, interface churn, and unnecessary synchronization.
|
||||
- Do not trade away correctness, spec compliance, or API stability for micro-optimizations.
|
||||
- Add or update benchmarks when performance-sensitive coverage is missing.
|
||||
- If you materially change a hot path, capture before-and-after results, preferably with `benchstat`.
|
||||
|
||||
### Review Agent
|
||||
|
||||
Use this persona when asked to review code, patches, or pull requests.
|
||||
|
||||
- Lead with findings, not summaries.
|
||||
- Order findings by severity and include precise file and line references when available.
|
||||
- Focus on correctness, spec compliance, API compatibility, concurrency safety, resilience, performance regressions, missing tests, missing benchmarks, documentation gaps, and changelog gaps.
|
||||
- Call out when a diff is broader than necessary.
|
||||
- If you find no issues, say that explicitly and note any residual risks or verification gaps.
|
||||
+96
-1
@@ -11,6 +11,100 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
||||
<!-- Released section -->
|
||||
<!-- Don't change this section unless doing release -->
|
||||
|
||||
## [1.44.0/0.66.0/0.20.0/0.0.17] 2026-05-27
|
||||
|
||||
### Added
|
||||
|
||||
- Add `ByteSlice` and `ByteSliceValue` functions for new `BYTESLICE` attribute type in `go.opentelemetry.io/otel/attribute`. (#7948)
|
||||
- Apply attribute value limit to the `KindBytes` attribute type in `go.opentelemetry.io/otel/sdk/log`. (#7990)
|
||||
- Apply attribute value limit to the `BYTESLICE` attribute type in `go.opentelemetry.io/otel/sdk/trace`. (#7990)
|
||||
- Support `BYTESLICE` attributes in `go.opentelemetry.io/otel/trace`. (#8153)
|
||||
- Support `BYTESLICE` attributes in `go.opentelemetry.io/otel/exporters/otlp/otlptrace`. (#8153)
|
||||
- Support `BYTESLICE` attributes in `go.opentelemetry.io/otel/exporters/otlp/otlplog`. (#8153)
|
||||
- Support `BYTESLICE` attributes in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric`. (#8153)
|
||||
- Support `BYTESLICE` attributes in `go.opentelemetry.io/otel/exporters/zipkin`. (#8153)
|
||||
- Add `String` method for `Value` type in `go.opentelemetry.io/otel/attribute`. (#8142)
|
||||
- Add `Slice` and `SliceValue` functions for new `SLICE` attribute type in `go.opentelemetry.io/otel/attribute`. (#8166)
|
||||
- Support `SLICE` attributes in `go.opentelemetry.io/otel/exporters/otlp/otlptrace`. (#8216)
|
||||
- Support `SLICE` attributes in `go.opentelemetry.io/otel/exporters/otlp/otlplog`. (#8216)
|
||||
- Support `SLICE` attributes in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric`. (#8216)
|
||||
- Support `SLICE` attributes in `go.opentelemetry.io/otel/exporters/zipkin`. (#8216)
|
||||
- Apply `AttributeValueLengthLimit` to `attribute.SLICE` type attribute values in `go.opentelemetry.io/otel/sdk/trace`, recursively truncating contained string values. (#8217)
|
||||
- Add `Error` field on `Record` type in `go.opentelemetry.io/otel/log/logtest`. (#8148)
|
||||
- Add `WithMaxRequestSize` option in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`. (#8157)
|
||||
- Add `WithMaxRequestSize` option in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#8157)
|
||||
- Add `WithMaxRequestSize` option in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`. (#8157)
|
||||
- Add `WithMaxRequestSize` option in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#8157)
|
||||
- Add `WithMaxRequestSize` option in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`. (#8157)
|
||||
- Add `WithMaxRequestSize` option in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#8157)
|
||||
- Add `Settable` to `go.opentelemetry.io/otel/metric/x` to allow reusing attribute options. (#8178)
|
||||
- Add experimental support for splitting metric data across multiple batches in `go.opentelemetry.io/otel/sdk/metric`.
|
||||
Set `OTEL_GO_X_METRIC_EXPORT_BATCH_SIZE=<max_size>` to enable for all periodic readers.
|
||||
See `go.opentelemetry.io/otel/sdk/metric/internal/x` for feature documentation. (#8071)
|
||||
- Add experimental self-observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`.
|
||||
Enable with `OTEL_GO_X_SELF_OBSERVABILITY=true` environment variable.
|
||||
See `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc/internal/x` for feature documentation. (#8192)
|
||||
- Add experimental self-observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`.
|
||||
Enable with `OTEL_GO_X_SELF_OBSERVABILITY=true` environment variable.
|
||||
See `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp/internal/x` for feature documentation. (#8194)
|
||||
- Add experimental self-observability metrics in `go.opentelemetry.io/otel/exporters/stdout/stdoutlog`.
|
||||
Enable with `OTEL_GO_X_SELF_OBSERVABILITY=true` environment variable.
|
||||
See `go.opentelemetry.io/otel/stdout/stdoutlog/internal/x` for feature documentation. (#8263)
|
||||
- Add `WithDefaultAttributes` to `go.opentelemetry.io/otel/metric/x` to support setting default attributes on instruments. (#8135)
|
||||
- Add `go.opentelemetry.io/otel/semconv/v1.41.0` package.
|
||||
The package contains semantic conventions from the `v1.41.0` version of the OpenTelemetry Semantic Conventions.
|
||||
See the [migration documentation](./semconv/v1.41.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.40.0`. (#8324)
|
||||
- Add Observable variants of instruments to `go.opentelemetry.io/otel/semconv/v1.41.0` package. (#8350)
|
||||
- Generate explicit histogram bucket boundaries from weaver configuration for HTTP and RPC duration instruments in `go.opentelemetry.io/otel/semconv/v1.41.0`. (#8002)
|
||||
|
||||
### Changed
|
||||
|
||||
- ⚠️ **Breaking Change:** `go.opentelemetry.io/otel/sdk/metric` now applies a default cardinality limit of 2000 to comply with the Metrics SDK specification recommendation.
|
||||
New attribute sets are dropped when the cardinality limit is reached. The measurement of these sets are aggregated into a special attribute set containing `attribute.Bool("otel.metric.overflow", true)`.
|
||||
This can break users who relied on the previous unlimited default.
|
||||
Set `WithCardinalityLimit(0)` or the deprecated `OTEL_GO_X_CARDINALITY_LIMIT=0` environment variable to preserve unlimited cardinality.
|
||||
Note that support for `OTEL_GO_X_CARDINALITY_LIMIT` may be removed in a future release. (#8247)
|
||||
- `ErrorType` in `go.opentelemetry.io/otel/semconv` now unwraps errors created with `fmt.Errorf` when deriving the `error.type` attribute. (#8133)
|
||||
- `go.opentelemetry.io/otel/sdk/log` now unwraps error chains created with `fmt.Errorf` when deriving the `error.type` attribute from errors on log records. (#8133)
|
||||
- `Set.MarshalLog` method in `go.opentelemetry.io/otel/attribute` now uses `Value.String` formatting following the [OpenTelemetry AnyValue representation for non-OTLP protocols](https://opentelemetry.io/docs/specs/otel/common/#anyvalue). (#8169)
|
||||
- Optimize `go.opentelemetry.io/otel/sdk/metric` to return a drop reservoir and short-circuit `Offer` calls to the exemplar reservoir when `exemplar.AlwaysOffFilter` is configured. (#8211) (#8267)
|
||||
- Optimize `go.opentelemetry.io/otel/sdk/metric` to return a drop reservoir for asynchronous instruments when `exemplar.TraceBasedFilter` is configured. (#8286)
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Deprecate `Value.Emit` method in `go.opentelemetry.io/otel/attribute`.
|
||||
Use `Value.String` instead. (#8176)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Limit OTLP request size to 64 MiB by default in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`.
|
||||
The limit applies before compression, oversized requests are treated as non-retryable errors, and the limit can be configured with the new `WithMaxRequestSize` option. (#8157, #8365)
|
||||
- Limit OTLP request size to 64 MiB by default in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`.
|
||||
The limit applies before compression, oversized requests are treated as non-retryable errors, and the limit can be configured with the new `WithMaxRequestSize` option. (#8157, #8365)
|
||||
- Limit OTLP request size to 64 MiB by default in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`.
|
||||
The limit applies before compression, oversized requests are treated as non-retryable errors, and the limit can be configured with the new `WithMaxRequestSize` option. (#8157, #8365)
|
||||
- Limit OTLP request size to 64 MiB by default in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`.
|
||||
The limit applies before compression, oversized requests are treated as non-retryable errors, and the limit can be configured with the new `WithMaxRequestSize` option. (#8157, #8365)
|
||||
- Limit OTLP request size to 64 MiB by default in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`.
|
||||
The limit applies before compression, oversized requests are treated as non-retryable errors, and the limit can be configured with the new `WithMaxRequestSize` option. (#8157, #8365)
|
||||
- Limit OTLP request size to 64 MiB by default in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`.
|
||||
The limit applies before compression, oversized requests are treated as non-retryable errors, and the limit can be configured with the new `WithMaxRequestSize` option. (#8157, #8365)
|
||||
- Fix gzipped request body replay on redirect in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#8135)
|
||||
- Fix gzipped request body replay on redirect in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#8152)
|
||||
- `go.opentelemetry.io/otel/exporters/prometheus` now uses `Value.String` formatting for label values following the [OpenTelemetry AnyValue representation for non-OTLP protocols](https://opentelemetry.io/docs/specs/otel/common/#anyvalue). (#8170)
|
||||
- Propagate errors from the exporter when calling `Shutdown` on `BatchSpanProcessor` in `go.opentelemetry.io/otel/sdk/trace`. (#8197)
|
||||
- Fix stale status code reporting on self-observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` and `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#8226)
|
||||
- Fix a concurrent `Collect` data race and potential panic in `go.opentelemetry.io/otel/exporters/prometheus` when `WithResourceAsConstantLabels` option is used. (#8227)
|
||||
- Fix race condition in `FixedSizeReservoir` in `go.opentelemetry.io/otel/sdk/metric/exemplar` by reverting #7447. (#8249)
|
||||
- Fix `FixedSizeReservoir` in `go.opentelemetry.io/otel/sdk/metric/exemplar` to safely handle zero size.
|
||||
A capacity check in the constructor initializes the reservoir safely and skips initialization for zero-cap; early returns in `Offer()` and `Collect()` ensure no-op behavior. (#8295)
|
||||
- Fix counting of spans and logs in self-observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`, `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`, `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`, and `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#8254)
|
||||
- Drop conflicting scope attributes named `name`, `version`, or `schema_url` from metric labels in `go.opentelemetry.io/otel/exporters/prometheus`, preserving the dedicated `otel_scope_name`, `otel_scope_version`, and `otel_scope_schema_url` labels. (#8264)
|
||||
- Close schema files opened by `ParseFile` in `go.opentelemetry.io/otel/schema/v1.0` and `go.opentelemetry.io/otel/schema/v1.1`. ([GHSA-995v-fvrw-c78m](https://github.com/open-telemetry/opentelemetry-go/security/advisories/GHSA-995v-fvrw-c78m))
|
||||
- Enforce the 8192-byte baggage size limit during extraction/parsing, changing behavior when the limit is exceeded in `go.opentelemetry.io/otel/baggage` and `go.opentelemetry.io/otel/propagation`. (#8222)
|
||||
- Fix `go.opentelemetry.io/otel/semconv/v1.41.0` to include `Attr*` helper methods for required attributes on observable instruments. (#8361)
|
||||
- Limit baggage extraction error reporting in `go.opentelemetry.io/otel/propagation` to prevent malformed or oversized baggage headers from flooding logs. ([GHSA-5wrp-cwcj-q835](https://github.com/open-telemetry/opentelemetry-go/security/advisories/GHSA-5wrp-cwcj-q835))
|
||||
|
||||
## [1.43.0/0.65.0/0.19.0] 2026-04-02
|
||||
|
||||
### Added
|
||||
@@ -3619,7 +3713,8 @@ It contains api and sdk for trace and meter.
|
||||
- CircleCI build CI manifest files.
|
||||
- CODEOWNERS file to track owners of this project.
|
||||
|
||||
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.43.0...HEAD
|
||||
[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.44.0...HEAD
|
||||
[1.44.0/0.66.0/0.20.0/0.0.17]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.44.0
|
||||
[1.43.0/0.65.0/0.19.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.43.0
|
||||
[1.42.0/0.64.0/0.18.0/0.0.16]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.42.0
|
||||
[1.41.0/0.63.0/0.17.0/0.0.15]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.41.0
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
# Instructions for Claude Code
|
||||
|
||||
@AGENTS.md
|
||||
+89
-12
@@ -11,6 +11,12 @@ for a summary description of past meetings. To request edit access,
|
||||
join the meeting or get in touch on
|
||||
[Slack](https://cloud-native.slack.com/archives/C01NPAXACKT).
|
||||
|
||||
The meeting is open for all to join. We invite everyone to join our
|
||||
meeting, regardless of your experience level. Whether you're a
|
||||
seasoned OpenTelemetry developer, just starting your journey, or
|
||||
simply curious about the work we do, you're more than welcome to
|
||||
participate!
|
||||
|
||||
## Development
|
||||
|
||||
You can view and edit the source code by cloning this repository:
|
||||
@@ -746,8 +752,8 @@ Encapsulate setup in constructor functions, ensuring clear ownership and scope:
|
||||
import (
|
||||
"errors"
|
||||
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0/otelconv"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.41.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.41.0/otelconv"
|
||||
)
|
||||
|
||||
type SDKComponent struct {
|
||||
@@ -808,11 +814,11 @@ func (c *Component) initObservability() {
|
||||
|
||||
#### Performance
|
||||
|
||||
When observability is disabled there should be little to no overhead.
|
||||
When observability is disabled or the instrument is not `Enabled`, there should be little to no overhead.
|
||||
|
||||
```go
|
||||
func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error {
|
||||
if e.inst != nil {
|
||||
if e.inst != nil && e.inst.Enabled(ctx) {
|
||||
attrs := expensiveOperation()
|
||||
e.inst.recordSpanInflight(ctx, int64(len(spans)), attrs...)
|
||||
}
|
||||
@@ -829,7 +835,7 @@ func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan)
|
||||
}
|
||||
|
||||
func (i *instrumentation) recordSpanInflight(ctx context.Context, count int64, attrs ...attribute.KeyValue) {
|
||||
if i == nil || i.inflight == nil {
|
||||
if i == nil || i.inflight == nil || !i.inflight.Enabled(ctx) {
|
||||
return
|
||||
}
|
||||
i.inflight.Add(ctx, count, metric.WithAttributes(attrs...))
|
||||
@@ -865,8 +871,12 @@ var (
|
||||
)
|
||||
|
||||
func (i *instrumentation) record(ctx context.Context, value int64, baseAttrs ...attribute.KeyValue) {
|
||||
if !i.counter.Enabled(ctx) {
|
||||
return
|
||||
}
|
||||
attrs := attrPool.Get().(*[]attribute.KeyValue)
|
||||
defer func() {
|
||||
clear(*attrs) // Clear references to strings/etc to let GC collect them.
|
||||
*attrs = (*attrs)[:0] // Reset.
|
||||
attrPool.Put(attrs)
|
||||
}()
|
||||
@@ -877,6 +887,7 @@ func (i *instrumentation) record(ctx context.Context, value int64, baseAttrs ...
|
||||
|
||||
addOpt := addOptPool.Get().(*[]metric.AddOption)
|
||||
defer func() {
|
||||
clear(*addOpt)
|
||||
*addOpt = (*addOpt)[:0]
|
||||
addOptPool.Put(addOpt)
|
||||
}()
|
||||
@@ -1007,16 +1018,20 @@ Ensure observability measurements receive the correct context, especially for tr
|
||||
```go
|
||||
func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan) error {
|
||||
// Use the provided context for observability measurements
|
||||
e.inst.recordSpanExportStarted(ctx, len(spans))
|
||||
if e.inst.Enabled(ctx) {
|
||||
e.inst.recordSpanExportStarted(ctx, len(spans))
|
||||
}
|
||||
|
||||
err := e.doExport(ctx, spans)
|
||||
|
||||
if err != nil {
|
||||
e.inst.recordSpanExportFailed(ctx, len(spans), err)
|
||||
} else {
|
||||
e.inst.recordSpanExportSucceeded(ctx, len(spans))
|
||||
if e.inst.Enabled(ctx) {
|
||||
if err != nil {
|
||||
e.inst.recordSpanExportFailed(ctx, len(spans), err)
|
||||
} else {
|
||||
e.inst.recordSpanExportSucceeded(ctx, len(spans))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return err
|
||||
}
|
||||
```
|
||||
@@ -1039,7 +1054,7 @@ func (e *Exporter) ExportSpans(ctx context.Context, spans []trace.ReadOnlySpan)
|
||||
|
||||
All observability metrics should follow the [OpenTelemetry Semantic Conventions for SDK metrics](https://github.com/open-telemetry/semantic-conventions/blob/1cf2476ae5e518225a766990a28a6d5602bd5a30/docs/otel/sdk-metrics.md).
|
||||
|
||||
Use the metric semantic conventions convenience package [otelconv](./semconv/v1.40.0/otelconv/metric.go).
|
||||
Use the metric semantic conventions convenience package [otelconv](./semconv/v1.41.0/otelconv/metric.go).
|
||||
|
||||
##### Component Identification
|
||||
|
||||
@@ -1109,6 +1124,68 @@ func TestObservability(t *testing.T) {
|
||||
Test order should not affect results.
|
||||
Ensure that any global state (e.g. component ID counters) is reset between tests.
|
||||
|
||||
### Experimental Features
|
||||
|
||||
To support the development of new features in the specification, we use the following patterns to implement in-development features without adding new public artifacts in stable modules.
|
||||
|
||||
#### Experimental behavior with no API artifacts
|
||||
|
||||
Features that change behavior without changing the API (e.g., exemplar collection, auto-generation of identifiers) are implemented behind a feature gate.
|
||||
The implementation resides in an `/internal/x` package and is activated through environment variables with the `OTEL_GO_X_` prefix (e.g., `OTEL_GO_X_OBSERVABILITY`).
|
||||
The feature must be documented in a `README.md` file in the `/internal/x` package.
|
||||
|
||||
#### Experimental methods on SDK-only interfaces
|
||||
|
||||
Features that require new methods on SDK interfaces are defined as a new interface in an experimental module (e.g., `go.opentelemetry.io/otel/sdk/x`).
|
||||
The SDK uses type assertions (without importing the unstable package) to check if passing types implement these experimental interfaces.
|
||||
The SDK must not depend on the experimental module.
|
||||
|
||||
#### Experimental structs, functions, or interfaces
|
||||
|
||||
Features that don't need any changes to the existing stable package are implemented in an experimental module (e.g., `go.opentelemetry.io/otel/sdk/x`).
|
||||
|
||||
#### Experimental signals and components
|
||||
|
||||
New telemetry signals (e.g., Logs before stabilization) and components (e.g. bridges) are hosted in new, unstable modules (e.g., `go.opentelemetry.io/otel/log` before 1.0.0).
|
||||
The package should have the final name it will use once stabilized (i.e. not `/x`), and is released at a v0.x.y version to indicate it is not stable.
|
||||
Most new components are hosted in [opentelemetry-go-contrib](https://github.com/open-telemetry/opentelemetry-go-contrib).
|
||||
|
||||
#### Experimental options for API or SDK functions
|
||||
|
||||
Experimental Options functions are implemented in an experimental module (e.g., `go.opentelemetry.io/otel/sdk/x`).
|
||||
The return type of the Option function must embed the option's type (e.g. `metric.InstrumentOption`), and have an `Experimental()` method to prevent the API from panicking when the option is used.
|
||||
The SDK uses type assertions (without importing the unstable package) to check if passing types implement these experimental interfaces.
|
||||
The SDK must not depend on the experimental module.
|
||||
|
||||
For example:
|
||||
|
||||
```go
|
||||
type myOption struct {
|
||||
// Embed the stable option type.
|
||||
metric.InstrumentOption
|
||||
value string
|
||||
}
|
||||
|
||||
// Experimental prevents the API from panicking when the option is used.
|
||||
func (o myOption) Experimental() {}
|
||||
|
||||
// The SDK can use type assertions to use this function.
|
||||
func (o myOption) Value() string { return o.value }
|
||||
|
||||
func WithMyOption(value string) metric.InstrumentOption {
|
||||
return myOption{value: value}
|
||||
}
|
||||
```
|
||||
|
||||
#### Not Supported
|
||||
|
||||
The following kinds of experimental features are **not currently supported** on stable interfaces:
|
||||
|
||||
- Experimental methods on API interfaces
|
||||
- Experimental fields for API or SDK exported structs
|
||||
|
||||
In some cases forks or long-lived branches may be used for prototyping these features.
|
||||
|
||||
## Approvers and Maintainers
|
||||
|
||||
### Maintainers
|
||||
|
||||
+9
-1
@@ -191,8 +191,16 @@ benchmark: $(OTEL_GO_MOD_DIRS:%=benchmark/%)
|
||||
benchmark/%:
|
||||
cd $* && $(GO) test -run='^$$' -bench=. $(ARGS) ./...
|
||||
|
||||
# sdk/metric is split into two shards to work around CodSpeed limitations.
|
||||
# See https://github.com/CodSpeedHQ/codspeed-go/issues/56
|
||||
BENCHMARK_SHARDS := $(filter-out ./sdk/metric,$(OTEL_GO_MOD_DIRS)) ./sdk/metric/root ./sdk/metric/internal
|
||||
benchmark/./sdk/metric/root:
|
||||
cd ./sdk/metric && $(GO) test -run='^$$' -bench=. $(ARGS) . ./exemplar/...
|
||||
benchmark/./sdk/metric/internal:
|
||||
cd ./sdk/metric && $(GO) test -run='^$$' -bench=. $(ARGS) ./internal/...
|
||||
|
||||
print-sharded-benchmarks:
|
||||
@echo $(OTEL_GO_MOD_DIRS) | jq -cR 'split(" ")'
|
||||
@echo $(BENCHMARK_SHARDS) | jq -cR 'split(" ")'
|
||||
|
||||
.PHONY: golangci-lint golangci-lint-fix
|
||||
golangci-lint-fix: ARGS=--fix
|
||||
|
||||
+3
-1
@@ -105,7 +105,9 @@ func (d *defaultAttrEncoder) Encode(iter Iterator) string {
|
||||
if keyValue.Value.Type() == STRING {
|
||||
copyAndEscape(buf, keyValue.Value.AsString())
|
||||
} else {
|
||||
_, _ = buf.WriteString(keyValue.Value.Emit())
|
||||
_, _ = buf.WriteString(
|
||||
keyValue.Value.Emit(),
|
||||
) //nolint:staticcheck // Preserve the existing default encoder output.
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
|
||||
+47
-11
@@ -27,6 +27,8 @@ const (
|
||||
int64SliceID uint64 = 3762322556277578591 // "_[]int64" (little endian)
|
||||
float64SliceID uint64 = 7308324551835016539 // "[]double" (little endian)
|
||||
stringSliceID uint64 = 7453010373645655387 // "[]string" (little endian)
|
||||
byteSliceID uint64 = 6874028470941080415 // "_[]byte_" (little endian)
|
||||
sliceID uint64 = 7883494272577650031 // "__slice_" (little endian)
|
||||
emptyID uint64 = 7305809155345288421 // "__empty_" (little endian)
|
||||
)
|
||||
|
||||
@@ -42,53 +44,87 @@ func hashKVs(kvs []KeyValue) uint64 {
|
||||
// hashKV returns the xxHash64 hash of kv with h as the base.
|
||||
func hashKV(h xxhash.Hash, kv KeyValue) xxhash.Hash {
|
||||
h = h.String(string(kv.Key))
|
||||
return hashValue(h, kv.Value)
|
||||
}
|
||||
|
||||
switch kv.Value.Type() {
|
||||
func hashValue(h xxhash.Hash, v Value) xxhash.Hash {
|
||||
switch v.Type() {
|
||||
case BOOL:
|
||||
h = h.Uint64(boolID)
|
||||
h = h.Uint64(kv.Value.numeric)
|
||||
h = h.Uint64(v.numeric)
|
||||
case INT64:
|
||||
h = h.Uint64(int64ID)
|
||||
h = h.Uint64(kv.Value.numeric)
|
||||
h = h.Uint64(v.numeric)
|
||||
case FLOAT64:
|
||||
h = h.Uint64(float64ID)
|
||||
// Assumes numeric stored with math.Float64bits.
|
||||
h = h.Uint64(kv.Value.numeric)
|
||||
h = h.Uint64(v.numeric)
|
||||
case STRING:
|
||||
h = h.Uint64(stringID)
|
||||
h = h.String(kv.Value.stringly)
|
||||
h = h.String(v.stringly)
|
||||
case BOOLSLICE:
|
||||
h = h.Uint64(boolSliceID)
|
||||
rv := reflect.ValueOf(kv.Value.slice)
|
||||
rv := reflect.ValueOf(v.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
h = h.Bool(rv.Index(i).Bool())
|
||||
}
|
||||
case INT64SLICE:
|
||||
h = h.Uint64(int64SliceID)
|
||||
rv := reflect.ValueOf(kv.Value.slice)
|
||||
rv := reflect.ValueOf(v.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
h = h.Int64(rv.Index(i).Int())
|
||||
}
|
||||
case FLOAT64SLICE:
|
||||
h = h.Uint64(float64SliceID)
|
||||
rv := reflect.ValueOf(kv.Value.slice)
|
||||
rv := reflect.ValueOf(v.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
h = h.Float64(rv.Index(i).Float())
|
||||
}
|
||||
case STRINGSLICE:
|
||||
h = h.Uint64(stringSliceID)
|
||||
rv := reflect.ValueOf(kv.Value.slice)
|
||||
rv := reflect.ValueOf(v.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
h = h.String(rv.Index(i).String())
|
||||
}
|
||||
case BYTESLICE:
|
||||
h = h.Uint64(byteSliceID)
|
||||
h = h.String(v.stringly)
|
||||
case SLICE:
|
||||
h = h.Uint64(sliceID)
|
||||
switch vals := v.slice.(type) {
|
||||
case [0]Value:
|
||||
// No values to hash, but the type identifier is still hashed above.
|
||||
case [1]Value:
|
||||
h = hashValueSlice(h, vals[:])
|
||||
case [2]Value:
|
||||
h = hashValueSlice(h, vals[:])
|
||||
case [3]Value:
|
||||
h = hashValueSlice(h, vals[:])
|
||||
case [4]Value:
|
||||
h = hashValueSlice(h, vals[:])
|
||||
case [5]Value:
|
||||
h = hashValueSlice(h, vals[:])
|
||||
default:
|
||||
rv := reflect.ValueOf(v.slice)
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
h = hashValue(h, rv.Index(i).Interface().(Value))
|
||||
}
|
||||
}
|
||||
case EMPTY:
|
||||
h = h.Uint64(emptyID)
|
||||
default:
|
||||
// Logging is an alternative, but using the internal logger here
|
||||
// causes an import cycle so it is not done.
|
||||
v := kv.Value.AsInterface()
|
||||
msg := fmt.Sprintf("unknown value type: %[1]v (%[1]T)", v)
|
||||
val := v.AsInterface()
|
||||
msg := fmt.Sprintf("unknown value type: %[1]v (%[1]T)", val)
|
||||
panic(msg)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
func hashValueSlice(h xxhash.Hash, vals []Value) xxhash.Hash {
|
||||
for _, v := range vals {
|
||||
h = hashValue(h, v)
|
||||
}
|
||||
return h
|
||||
}
|
||||
+22
@@ -117,6 +117,28 @@ func (k Key) StringSlice(v []string) KeyValue {
|
||||
}
|
||||
}
|
||||
|
||||
// ByteSlice creates a KeyValue instance with a BYTESLICE Value.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the provided
|
||||
// convenience function instead -- ByteSlice(name, value).
|
||||
func (k Key) ByteSlice(v []byte) KeyValue {
|
||||
return KeyValue{
|
||||
Key: k,
|
||||
Value: ByteSliceValue(v),
|
||||
}
|
||||
}
|
||||
|
||||
// Slice creates a KeyValue instance with a SLICE Value.
|
||||
//
|
||||
// If creating both a key and value at the same time, use the provided
|
||||
// convenience function instead -- Slice(name, values...).
|
||||
func (k Key) Slice(v ...Value) KeyValue {
|
||||
return KeyValue{
|
||||
Key: k,
|
||||
Value: SliceValue(v...),
|
||||
}
|
||||
}
|
||||
|
||||
// Defined reports whether the key is not empty.
|
||||
func (k Key) Defined() bool {
|
||||
return len(k) != 0
|
||||
|
||||
+10
@@ -68,6 +68,16 @@ func StringSlice(k string, v []string) KeyValue {
|
||||
return Key(k).StringSlice(v)
|
||||
}
|
||||
|
||||
// ByteSlice creates a KeyValue with a BYTESLICE Value type.
|
||||
func ByteSlice(k string, v []byte) KeyValue {
|
||||
return Key(k).ByteSlice(v)
|
||||
}
|
||||
|
||||
// Slice creates a KeyValue with a SLICE Value type.
|
||||
func Slice(k string, v ...Value) KeyValue {
|
||||
return Key(k).Slice(v...)
|
||||
}
|
||||
|
||||
// Stringer creates a new key-value pair with a passed name and a string
|
||||
// value generated by the passed Stringer interface.
|
||||
func Stringer(k string, v fmt.Stringer) KeyValue {
|
||||
|
||||
+2
-2
@@ -401,7 +401,7 @@ func computeDataFixed(kvs []KeyValue) any {
|
||||
func computeDataReflect(kvs []KeyValue) any {
|
||||
at := reflect.New(reflect.ArrayOf(len(kvs), keyValueType)).Elem()
|
||||
for i, keyValue := range kvs {
|
||||
*(at.Index(i).Addr().Interface().(*KeyValue)) = keyValue
|
||||
*at.Index(i).Addr().Interface().(*KeyValue) = keyValue
|
||||
}
|
||||
return at.Interface()
|
||||
}
|
||||
@@ -415,7 +415,7 @@ func (l *Set) MarshalJSON() ([]byte, error) {
|
||||
func (l Set) MarshalLog() any {
|
||||
kvs := make(map[string]string)
|
||||
for _, kv := range l.ToSlice() {
|
||||
kvs[string(kv.Key)] = kv.Value.Emit()
|
||||
kvs[string(kv.Key)] = kv.Value.String()
|
||||
}
|
||||
return kvs
|
||||
}
|
||||
|
||||
+4
-2
@@ -17,11 +17,13 @@ func _() {
|
||||
_ = x[INT64SLICE-6]
|
||||
_ = x[FLOAT64SLICE-7]
|
||||
_ = x[STRINGSLICE-8]
|
||||
_ = x[BYTESLICE-9]
|
||||
_ = x[SLICE-10]
|
||||
}
|
||||
|
||||
const _Type_name = "EMPTYBOOLINT64FLOAT64STRINGBOOLSLICEINT64SLICEFLOAT64SLICESTRINGSLICE"
|
||||
const _Type_name = "EMPTYBOOLINT64FLOAT64STRINGBOOLSLICEINT64SLICEFLOAT64SLICESTRINGSLICEBYTESLICESLICE"
|
||||
|
||||
var _Type_index = [...]uint8{0, 5, 9, 14, 21, 27, 36, 46, 58, 69}
|
||||
var _Type_index = [...]uint8{0, 5, 9, 14, 21, 27, 36, 46, 58, 69, 78, 83}
|
||||
|
||||
func (i Type) String() string {
|
||||
idx := int(i) - 0
|
||||
|
||||
+742
@@ -4,9 +4,14 @@
|
||||
package attribute // import "go.opentelemetry.io/otel/attribute"
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
attribute "go.opentelemetry.io/otel/attribute/internal"
|
||||
)
|
||||
@@ -45,6 +50,10 @@ const (
|
||||
FLOAT64SLICE
|
||||
// STRINGSLICE is a slice of strings Type Value.
|
||||
STRINGSLICE
|
||||
// BYTESLICE is a slice of bytes Type Value.
|
||||
BYTESLICE
|
||||
// SLICE is a slice of Value Type values.
|
||||
SLICE
|
||||
// INVALID is used for a Value with no value set.
|
||||
//
|
||||
// Deprecated: Use EMPTY instead as an empty value is a valid value.
|
||||
@@ -134,6 +143,19 @@ func StringSliceValue(v []string) Value {
|
||||
return Value{vtype: STRINGSLICE, slice: attribute.SliceValue(v)}
|
||||
}
|
||||
|
||||
// ByteSliceValue creates a BYTESLICE Value.
|
||||
func ByteSliceValue(v []byte) Value {
|
||||
return Value{
|
||||
vtype: BYTESLICE,
|
||||
stringly: string(v),
|
||||
}
|
||||
}
|
||||
|
||||
// SliceValue creates a SLICE Value.
|
||||
func SliceValue(v ...Value) Value {
|
||||
return Value{vtype: SLICE, slice: sliceValue(v)}
|
||||
}
|
||||
|
||||
// Type returns a type of the Value.
|
||||
func (v Value) Type() Type {
|
||||
return v.vtype
|
||||
@@ -215,6 +237,59 @@ func (v Value) asStringSlice() []string {
|
||||
return attribute.AsSlice[string](v.slice)
|
||||
}
|
||||
|
||||
// AsSlice returns the []Value value. Make sure that the Value's type is
|
||||
// SLICE.
|
||||
func (v Value) AsSlice() []Value {
|
||||
if v.vtype != SLICE {
|
||||
return nil
|
||||
}
|
||||
return v.asSlice()
|
||||
}
|
||||
|
||||
func (v Value) asSlice() []Value {
|
||||
switch vals := v.slice.(type) {
|
||||
case [0]Value:
|
||||
return []Value{}
|
||||
case [1]Value:
|
||||
return []Value{vals[0]}
|
||||
case [2]Value:
|
||||
return []Value{vals[0], vals[1]}
|
||||
case [3]Value:
|
||||
return []Value{vals[0], vals[1], vals[2]}
|
||||
case [4]Value:
|
||||
return []Value{vals[0], vals[1], vals[2], vals[3]}
|
||||
case [5]Value:
|
||||
return []Value{vals[0], vals[1], vals[2], vals[3], vals[4]}
|
||||
default:
|
||||
return asValueSliceReflect(v.slice)
|
||||
}
|
||||
}
|
||||
|
||||
func asValueSliceReflect(v any) []Value {
|
||||
rv := reflect.ValueOf(v)
|
||||
if !rv.IsValid() || rv.Kind() != reflect.Array || rv.Type().Elem() != reflect.TypeFor[Value]() {
|
||||
return nil
|
||||
}
|
||||
cpy := make([]Value, rv.Len())
|
||||
if len(cpy) > 0 {
|
||||
_ = reflect.Copy(reflect.ValueOf(cpy), rv)
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
// AsByteSlice returns the bytes value. Make sure that the Value's type
|
||||
// is BYTESLICE.
|
||||
func (v Value) AsByteSlice() []byte {
|
||||
if v.vtype != BYTESLICE {
|
||||
return nil
|
||||
}
|
||||
return v.asByteSlice()
|
||||
}
|
||||
|
||||
func (v Value) asByteSlice() []byte {
|
||||
return []byte(v.stringly)
|
||||
}
|
||||
|
||||
type unknownValueType struct{}
|
||||
|
||||
// AsInterface returns Value's data as any.
|
||||
@@ -236,13 +311,60 @@ func (v Value) AsInterface() any {
|
||||
return v.stringly
|
||||
case STRINGSLICE:
|
||||
return v.asStringSlice()
|
||||
case BYTESLICE:
|
||||
return v.asByteSlice()
|
||||
case SLICE:
|
||||
return v.asSlice()
|
||||
case EMPTY:
|
||||
return nil
|
||||
}
|
||||
return unknownValueType{}
|
||||
}
|
||||
|
||||
// String returns a string representation of Value using the
|
||||
// [OpenTelemetry AnyValue representation for non-OTLP protocols] rules.
|
||||
//
|
||||
// Strings are returned as-is without JSON quoting, booleans and integers use
|
||||
// JSON literals, floating-point values use JSON numbers except that NaN and
|
||||
// ±Inf are rendered as NaN, Infinity, and -Infinity, byte slices are
|
||||
// base64-encoded, empty values are the empty string, and slices are encoded as
|
||||
// JSON arrays. String, byte, and special floating-point values inside arrays
|
||||
// are encoded as JSON strings, and empty values inside arrays are encoded as
|
||||
// null.
|
||||
//
|
||||
// [OpenTelemetry AnyValue representation for non-OTLP protocols]: https://opentelemetry.io/docs/specs/otel/common/#anyvalue-representation-for-non-otlp-protocols
|
||||
func (v Value) String() string {
|
||||
switch v.Type() {
|
||||
case BOOL:
|
||||
return strconv.FormatBool(v.AsBool())
|
||||
case BOOLSLICE:
|
||||
return formatBoolSliceValue(v.slice)
|
||||
case INT64:
|
||||
return strconv.FormatInt(v.AsInt64(), 10)
|
||||
case INT64SLICE:
|
||||
return formatInt64SliceValue(v.slice)
|
||||
case FLOAT64:
|
||||
return formatFloat64(v.AsFloat64())
|
||||
case FLOAT64SLICE:
|
||||
return formatFloat64SliceValue(v.slice)
|
||||
case STRING:
|
||||
return v.stringly
|
||||
case STRINGSLICE:
|
||||
return formatStringSliceValue(v.slice)
|
||||
case BYTESLICE:
|
||||
return formatByteSlice(v.stringly)
|
||||
case SLICE:
|
||||
return formatValueSliceValue(v.slice)
|
||||
case EMPTY:
|
||||
return ""
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// Emit returns a string representation of Value's data.
|
||||
//
|
||||
// Deprecated: Use [Value.String] instead.
|
||||
func (v Value) Emit() string {
|
||||
switch v.Type() {
|
||||
case BOOLSLICE:
|
||||
@@ -273,6 +395,10 @@ func (v Value) Emit() string {
|
||||
return string(j)
|
||||
case STRING:
|
||||
return v.stringly
|
||||
case BYTESLICE:
|
||||
return formatByteSlice(v.stringly)
|
||||
case SLICE:
|
||||
return formatValueSliceValue(v.slice)
|
||||
case EMPTY:
|
||||
return ""
|
||||
default:
|
||||
@@ -280,6 +406,622 @@ func (v Value) Emit() string {
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
jsonArrayBracketsLen = len("[]")
|
||||
boolArrayElemMaxLen = len("false")
|
||||
int64ArrayElemMaxLen = len("-9223372036854775808")
|
||||
float64ArrayElemMaxLen = len("-1.7976931348623157e+308")
|
||||
commaLen = len(",")
|
||||
)
|
||||
|
||||
func sliceValue(v []Value) any {
|
||||
switch len(v) {
|
||||
case 0:
|
||||
return [0]Value{}
|
||||
case 1:
|
||||
return [1]Value{v[0]}
|
||||
case 2:
|
||||
return [2]Value{v[0], v[1]}
|
||||
case 3:
|
||||
return [3]Value{v[0], v[1], v[2]}
|
||||
case 4:
|
||||
return [4]Value{v[0], v[1], v[2], v[3]}
|
||||
case 5:
|
||||
return [5]Value{v[0], v[1], v[2], v[3], v[4]}
|
||||
default:
|
||||
return sliceValueReflect(v)
|
||||
}
|
||||
}
|
||||
|
||||
func sliceValueReflect(v []Value) any {
|
||||
cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[Value]())).Elem()
|
||||
reflect.Copy(cp, reflect.ValueOf(v))
|
||||
return cp.Interface()
|
||||
}
|
||||
|
||||
func formatBoolSliceValue(v any) string {
|
||||
switch vals := v.(type) {
|
||||
case [0]bool:
|
||||
return "[]"
|
||||
case [1]bool:
|
||||
return formatBoolSlice(vals[:])
|
||||
case [2]bool:
|
||||
return formatBoolSlice(vals[:])
|
||||
case [3]bool:
|
||||
return formatBoolSlice(vals[:])
|
||||
default:
|
||||
return formatBoolSliceReflect(v)
|
||||
}
|
||||
}
|
||||
|
||||
func formatBoolSlice(vals []bool) string {
|
||||
var b strings.Builder
|
||||
appendBoolSlice(&b, vals)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func formatBoolSliceReflect(v any) string {
|
||||
var b strings.Builder
|
||||
appendBoolSliceReflect(&b, reflect.ValueOf(v))
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func appendBoolSliceValue(dst *strings.Builder, v any) {
|
||||
switch vals := v.(type) {
|
||||
case [0]bool:
|
||||
_, _ = dst.WriteString("[]")
|
||||
case [1]bool:
|
||||
appendBoolSlice(dst, vals[:])
|
||||
case [2]bool:
|
||||
appendBoolSlice(dst, vals[:])
|
||||
case [3]bool:
|
||||
appendBoolSlice(dst, vals[:])
|
||||
default:
|
||||
appendBoolSliceReflect(dst, reflect.ValueOf(v))
|
||||
}
|
||||
}
|
||||
|
||||
func appendBoolSlice(dst *strings.Builder, vals []bool) {
|
||||
dst.Grow(jsonArrayBracketsLen + len(vals)*(boolArrayElemMaxLen+commaLen))
|
||||
_ = dst.WriteByte('[')
|
||||
for i, val := range vals {
|
||||
if i > 0 {
|
||||
_ = dst.WriteByte(',')
|
||||
}
|
||||
if val {
|
||||
_, _ = dst.WriteString("true")
|
||||
} else {
|
||||
_, _ = dst.WriteString("false")
|
||||
}
|
||||
}
|
||||
_ = dst.WriteByte(']')
|
||||
}
|
||||
|
||||
func appendBoolSliceReflect(dst *strings.Builder, rv reflect.Value) {
|
||||
dst.Grow(jsonArrayBracketsLen + rv.Len()*(boolArrayElemMaxLen+commaLen))
|
||||
_ = dst.WriteByte('[')
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
if i > 0 {
|
||||
_ = dst.WriteByte(',')
|
||||
}
|
||||
if rv.Index(i).Bool() {
|
||||
_, _ = dst.WriteString("true")
|
||||
} else {
|
||||
_, _ = dst.WriteString("false")
|
||||
}
|
||||
}
|
||||
_ = dst.WriteByte(']')
|
||||
}
|
||||
|
||||
func formatInt64SliceValue(v any) string {
|
||||
switch vals := v.(type) {
|
||||
case [0]int64:
|
||||
return "[]"
|
||||
case [1]int64:
|
||||
return formatInt64Slice(vals[:])
|
||||
case [2]int64:
|
||||
return formatInt64Slice(vals[:])
|
||||
case [3]int64:
|
||||
return formatInt64Slice(vals[:])
|
||||
default:
|
||||
return formatInt64SliceReflect(v)
|
||||
}
|
||||
}
|
||||
|
||||
func formatInt64Slice(vals []int64) string {
|
||||
var b strings.Builder
|
||||
appendInt64Slice(&b, vals)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func formatInt64SliceReflect(v any) string {
|
||||
var b strings.Builder
|
||||
appendInt64SliceReflect(&b, reflect.ValueOf(v))
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func appendInt64SliceValue(dst *strings.Builder, v any) {
|
||||
switch vals := v.(type) {
|
||||
case [0]int64:
|
||||
_, _ = dst.WriteString("[]")
|
||||
case [1]int64:
|
||||
appendInt64Slice(dst, vals[:])
|
||||
case [2]int64:
|
||||
appendInt64Slice(dst, vals[:])
|
||||
case [3]int64:
|
||||
appendInt64Slice(dst, vals[:])
|
||||
default:
|
||||
appendInt64SliceReflect(dst, reflect.ValueOf(v))
|
||||
}
|
||||
}
|
||||
|
||||
func appendInt64Slice(dst *strings.Builder, vals []int64) {
|
||||
dst.Grow(jsonArrayBracketsLen + len(vals)*(int64ArrayElemMaxLen+commaLen))
|
||||
_ = dst.WriteByte('[')
|
||||
|
||||
var buf [int64ArrayElemMaxLen]byte
|
||||
for i, val := range vals {
|
||||
if i > 0 {
|
||||
_ = dst.WriteByte(',')
|
||||
}
|
||||
out := strconv.AppendInt(buf[:0], val, 10)
|
||||
_, _ = dst.Write(out)
|
||||
}
|
||||
|
||||
_ = dst.WriteByte(']')
|
||||
}
|
||||
|
||||
func appendInt64SliceReflect(dst *strings.Builder, rv reflect.Value) {
|
||||
dst.Grow(jsonArrayBracketsLen + rv.Len()*(int64ArrayElemMaxLen+commaLen))
|
||||
_ = dst.WriteByte('[')
|
||||
|
||||
var scratch [int64ArrayElemMaxLen]byte
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
if i > 0 {
|
||||
_ = dst.WriteByte(',')
|
||||
}
|
||||
out := strconv.AppendInt(scratch[:0], rv.Index(i).Int(), 10)
|
||||
_, _ = dst.Write(out)
|
||||
}
|
||||
|
||||
_ = dst.WriteByte(']')
|
||||
}
|
||||
|
||||
func formatFloat64(v float64) string {
|
||||
switch {
|
||||
case math.IsNaN(v):
|
||||
return "NaN"
|
||||
case math.IsInf(v, 1):
|
||||
return "Infinity"
|
||||
case math.IsInf(v, -1):
|
||||
return "-Infinity"
|
||||
default:
|
||||
return strconv.FormatFloat(v, 'g', -1, 64)
|
||||
}
|
||||
}
|
||||
|
||||
func formatFloat64SliceValue(v any) string {
|
||||
switch vals := v.(type) {
|
||||
case [0]float64:
|
||||
return "[]"
|
||||
case [1]float64:
|
||||
return formatFloat64Slice(vals[:])
|
||||
case [2]float64:
|
||||
return formatFloat64Slice(vals[:])
|
||||
case [3]float64:
|
||||
return formatFloat64Slice(vals[:])
|
||||
default:
|
||||
return formatFloat64SliceReflect(v)
|
||||
}
|
||||
}
|
||||
|
||||
func formatFloat64Slice(vals []float64) string {
|
||||
var b strings.Builder
|
||||
appendFloat64Slice(&b, vals)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func formatFloat64SliceReflect(v any) string {
|
||||
var b strings.Builder
|
||||
appendFloat64SliceReflect(&b, reflect.ValueOf(v))
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func appendFloat64SliceValue(dst *strings.Builder, v any) {
|
||||
switch vals := v.(type) {
|
||||
case [0]float64:
|
||||
_, _ = dst.WriteString("[]")
|
||||
case [1]float64:
|
||||
appendFloat64Slice(dst, vals[:])
|
||||
case [2]float64:
|
||||
appendFloat64Slice(dst, vals[:])
|
||||
case [3]float64:
|
||||
appendFloat64Slice(dst, vals[:])
|
||||
default:
|
||||
appendFloat64SliceReflect(dst, reflect.ValueOf(v))
|
||||
}
|
||||
}
|
||||
|
||||
func appendFloat64Slice(dst *strings.Builder, vals []float64) {
|
||||
dst.Grow(jsonArrayBracketsLen + len(vals)*(float64ArrayElemMaxLen+commaLen))
|
||||
_ = dst.WriteByte('[')
|
||||
|
||||
var buf [float64ArrayElemMaxLen]byte
|
||||
for i, val := range vals {
|
||||
if i > 0 {
|
||||
_ = dst.WriteByte(',')
|
||||
}
|
||||
|
||||
switch {
|
||||
case math.IsNaN(val):
|
||||
_, _ = dst.WriteString(`"NaN"`)
|
||||
case math.IsInf(val, 1):
|
||||
_, _ = dst.WriteString(`"Infinity"`)
|
||||
case math.IsInf(val, -1):
|
||||
_, _ = dst.WriteString(`"-Infinity"`)
|
||||
default:
|
||||
out := strconv.AppendFloat(buf[:0], val, 'g', -1, 64)
|
||||
_, _ = dst.Write(out)
|
||||
}
|
||||
}
|
||||
|
||||
_ = dst.WriteByte(']')
|
||||
}
|
||||
|
||||
func appendFloat64SliceReflect(dst *strings.Builder, rv reflect.Value) {
|
||||
dst.Grow(jsonArrayBracketsLen + rv.Len()*(float64ArrayElemMaxLen+commaLen))
|
||||
_ = dst.WriteByte('[')
|
||||
|
||||
var scratch [float64ArrayElemMaxLen]byte
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
if i > 0 {
|
||||
_ = dst.WriteByte(',')
|
||||
}
|
||||
val := rv.Index(i).Float()
|
||||
switch {
|
||||
case math.IsNaN(val):
|
||||
_, _ = dst.WriteString(`"NaN"`)
|
||||
case math.IsInf(val, 1):
|
||||
_, _ = dst.WriteString(`"Infinity"`)
|
||||
case math.IsInf(val, -1):
|
||||
_, _ = dst.WriteString(`"-Infinity"`)
|
||||
default:
|
||||
out := strconv.AppendFloat(scratch[:0], val, 'g', -1, 64)
|
||||
_, _ = dst.Write(out)
|
||||
}
|
||||
}
|
||||
|
||||
_ = dst.WriteByte(']')
|
||||
}
|
||||
|
||||
func formatStringSliceValue(v any) string {
|
||||
switch vals := v.(type) {
|
||||
case [0]string:
|
||||
return "[]"
|
||||
case [1]string:
|
||||
return formatStringSlice(vals[:])
|
||||
case [2]string:
|
||||
return formatStringSlice(vals[:])
|
||||
case [3]string:
|
||||
return formatStringSlice(vals[:])
|
||||
default:
|
||||
return formatStringSliceReflect(v)
|
||||
}
|
||||
}
|
||||
|
||||
func formatStringSlice(vals []string) string {
|
||||
var b strings.Builder
|
||||
appendStringSlice(&b, vals)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func formatStringSliceReflect(v any) string {
|
||||
var b strings.Builder
|
||||
appendStringSliceReflect(&b, reflect.ValueOf(v))
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func appendStringSliceValue(dst *strings.Builder, v any) {
|
||||
switch vals := v.(type) {
|
||||
case [0]string:
|
||||
_, _ = dst.WriteString("[]")
|
||||
case [1]string:
|
||||
appendStringSlice(dst, vals[:])
|
||||
case [2]string:
|
||||
appendStringSlice(dst, vals[:])
|
||||
case [3]string:
|
||||
appendStringSlice(dst, vals[:])
|
||||
default:
|
||||
appendStringSliceReflect(dst, reflect.ValueOf(v))
|
||||
}
|
||||
}
|
||||
|
||||
func appendStringSlice(dst *strings.Builder, vals []string) {
|
||||
size := jsonArrayBracketsLen
|
||||
for _, val := range vals {
|
||||
size += len(val) + commaLen + 2 // Account for JSON string quotes and comma.
|
||||
}
|
||||
|
||||
dst.Grow(size)
|
||||
_ = dst.WriteByte('[')
|
||||
for i, val := range vals {
|
||||
if i > 0 {
|
||||
_ = dst.WriteByte(',')
|
||||
}
|
||||
appendJSONString(dst, val)
|
||||
}
|
||||
_ = dst.WriteByte(']')
|
||||
}
|
||||
|
||||
func appendStringSliceReflect(dst *strings.Builder, rv reflect.Value) {
|
||||
size := jsonArrayBracketsLen
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
size += len(rv.Index(i).String()) + commaLen + 2 // Account for JSON string quotes and comma.
|
||||
}
|
||||
|
||||
dst.Grow(size)
|
||||
_ = dst.WriteByte('[')
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
if i > 0 {
|
||||
_ = dst.WriteByte(',')
|
||||
}
|
||||
appendJSONString(dst, rv.Index(i).String())
|
||||
}
|
||||
_ = dst.WriteByte(']')
|
||||
}
|
||||
|
||||
func formatByteSlice(v string) string {
|
||||
var b strings.Builder
|
||||
appendBase64(&b, v)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func formatValueSliceValue(v any) string {
|
||||
switch vals := v.(type) {
|
||||
case [0]Value:
|
||||
return "[]"
|
||||
case [1]Value:
|
||||
return formatValueSlice(vals[:])
|
||||
case [2]Value:
|
||||
return formatValueSlice(vals[:])
|
||||
case [3]Value:
|
||||
return formatValueSlice(vals[:])
|
||||
case [4]Value:
|
||||
return formatValueSlice(vals[:])
|
||||
case [5]Value:
|
||||
return formatValueSlice(vals[:])
|
||||
default:
|
||||
return formatValueSliceReflect(v)
|
||||
}
|
||||
}
|
||||
|
||||
func formatValueSlice(vals []Value) string {
|
||||
var b strings.Builder
|
||||
appendValueSlice(&b, vals)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func formatValueSliceReflect(v any) string {
|
||||
var b strings.Builder
|
||||
appendValueSliceReflect(&b, reflect.ValueOf(v))
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func appendValueSliceValue(dst *strings.Builder, v any) {
|
||||
switch vals := v.(type) {
|
||||
case [0]Value:
|
||||
_, _ = dst.WriteString("[]")
|
||||
case [1]Value:
|
||||
appendValueSlice(dst, vals[:])
|
||||
case [2]Value:
|
||||
appendValueSlice(dst, vals[:])
|
||||
case [3]Value:
|
||||
appendValueSlice(dst, vals[:])
|
||||
case [4]Value:
|
||||
appendValueSlice(dst, vals[:])
|
||||
case [5]Value:
|
||||
appendValueSlice(dst, vals[:])
|
||||
default:
|
||||
appendValueSliceReflect(dst, reflect.ValueOf(v))
|
||||
}
|
||||
}
|
||||
|
||||
func appendValueSlice(dst *strings.Builder, vals []Value) {
|
||||
// Estimate 10 bytes per value for small values and commas.
|
||||
dst.Grow(jsonArrayBracketsLen + len(vals)*commaLen + len(vals)*10)
|
||||
_ = dst.WriteByte('[')
|
||||
for i, val := range vals {
|
||||
if i > 0 {
|
||||
_ = dst.WriteByte(',')
|
||||
}
|
||||
appendJSONValue(dst, val)
|
||||
}
|
||||
_ = dst.WriteByte(']')
|
||||
}
|
||||
|
||||
func appendValueSliceReflect(dst *strings.Builder, rv reflect.Value) {
|
||||
// Estimate 10 bytes per value for small values and commas.
|
||||
dst.Grow(jsonArrayBracketsLen + rv.Len()*commaLen + rv.Len()*10)
|
||||
_ = dst.WriteByte('[')
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
if i > 0 {
|
||||
_ = dst.WriteByte(',')
|
||||
}
|
||||
appendJSONValue(dst, rv.Index(i).Interface().(Value))
|
||||
}
|
||||
_ = dst.WriteByte(']')
|
||||
}
|
||||
|
||||
func appendJSONValue(dst *strings.Builder, v Value) {
|
||||
switch v.Type() {
|
||||
case BOOL:
|
||||
if v.AsBool() {
|
||||
_, _ = dst.WriteString("true")
|
||||
} else {
|
||||
_, _ = dst.WriteString("false")
|
||||
}
|
||||
case BOOLSLICE:
|
||||
appendBoolSliceValue(dst, v.slice)
|
||||
case INT64:
|
||||
var buf [int64ArrayElemMaxLen]byte
|
||||
out := strconv.AppendInt(buf[:0], v.AsInt64(), 10)
|
||||
_, _ = dst.Write(out)
|
||||
case INT64SLICE:
|
||||
appendInt64SliceValue(dst, v.slice)
|
||||
case FLOAT64:
|
||||
val := v.AsFloat64()
|
||||
switch {
|
||||
case math.IsNaN(val):
|
||||
appendJSONString(dst, "NaN")
|
||||
case math.IsInf(val, 1):
|
||||
appendJSONString(dst, "Infinity")
|
||||
case math.IsInf(val, -1):
|
||||
appendJSONString(dst, "-Infinity")
|
||||
default:
|
||||
var buf [float64ArrayElemMaxLen]byte
|
||||
out := strconv.AppendFloat(buf[:0], val, 'g', -1, 64)
|
||||
_, _ = dst.Write(out)
|
||||
}
|
||||
case FLOAT64SLICE:
|
||||
appendFloat64SliceValue(dst, v.slice)
|
||||
case STRING:
|
||||
appendJSONString(dst, v.stringly)
|
||||
case STRINGSLICE:
|
||||
appendStringSliceValue(dst, v.slice)
|
||||
case BYTESLICE:
|
||||
_ = dst.WriteByte('"')
|
||||
appendBase64(dst, v.stringly)
|
||||
_ = dst.WriteByte('"')
|
||||
case SLICE:
|
||||
appendValueSliceValue(dst, v.slice)
|
||||
case EMPTY:
|
||||
_, _ = dst.WriteString("null")
|
||||
default:
|
||||
appendJSONString(dst, "unknown")
|
||||
}
|
||||
}
|
||||
|
||||
// appendJSONString appends s to dst as a JSON string literal.
|
||||
//
|
||||
// This is adapted from the Go standard library's encoding/json
|
||||
// [appendString implementation]. It keeps the same escaping behavior we need
|
||||
// here, but writes directly into a strings.Builder and intentionally does not
|
||||
// apply HTML escaping because the OpenTelemetry non-OTLP AnyValue representation
|
||||
// only requires JSON array string encoding. We inline this instead of using
|
||||
// encoding/json so slice formatting avoids allocations and reflection.
|
||||
//
|
||||
// [appendString implementation]: https://github.com/golang/go/blob/3b5954c6349d31465dca409b45ab6597e0942d9f/src/encoding/json/encode.go#L998-L1064
|
||||
func appendJSONString(dst *strings.Builder, s string) {
|
||||
const hex = "0123456789abcdef" // For escaping bytes to hex.
|
||||
|
||||
_ = dst.WriteByte('"')
|
||||
start := 0
|
||||
|
||||
for i := 0; i < len(s); {
|
||||
if c := s[i]; c < utf8.RuneSelf {
|
||||
if c >= 0x20 && c != '\\' && c != '"' {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if start < i {
|
||||
_, _ = dst.WriteString(s[start:i])
|
||||
}
|
||||
|
||||
switch c {
|
||||
case '\\', '"':
|
||||
_ = dst.WriteByte('\\')
|
||||
_ = dst.WriteByte(c)
|
||||
case '\b':
|
||||
_, _ = dst.WriteString(`\b`)
|
||||
case '\f':
|
||||
_, _ = dst.WriteString(`\f`)
|
||||
case '\n':
|
||||
_, _ = dst.WriteString(`\n`)
|
||||
case '\r':
|
||||
_, _ = dst.WriteString(`\r`)
|
||||
case '\t':
|
||||
_, _ = dst.WriteString(`\t`)
|
||||
default:
|
||||
_, _ = dst.WriteString(`\u00`)
|
||||
_ = dst.WriteByte(hex[c>>4])
|
||||
_ = dst.WriteByte(hex[c&0x0f])
|
||||
}
|
||||
|
||||
i++
|
||||
start = i
|
||||
continue
|
||||
}
|
||||
|
||||
r, size := utf8.DecodeRuneInString(s[i:])
|
||||
if r == utf8.RuneError && size == 1 {
|
||||
if start < i {
|
||||
_, _ = dst.WriteString(s[start:i])
|
||||
}
|
||||
// Match encoding/json by replacing invalid UTF-8 with U+FFFD.
|
||||
_, _ = dst.WriteString(`\ufffd`)
|
||||
i++
|
||||
start = i
|
||||
continue
|
||||
}
|
||||
|
||||
if r == '\u2028' || r == '\u2029' {
|
||||
if start < i {
|
||||
_, _ = dst.WriteString(s[start:i])
|
||||
}
|
||||
// Escape JSONP-sensitive separators unconditionally, like encoding/json.
|
||||
_, _ = dst.WriteString(`\u202`)
|
||||
_ = dst.WriteByte(hex[r&0x0f])
|
||||
i += size
|
||||
start = i
|
||||
continue
|
||||
}
|
||||
|
||||
i += size
|
||||
}
|
||||
|
||||
if start < len(s) {
|
||||
_, _ = dst.WriteString(s[start:])
|
||||
}
|
||||
_ = dst.WriteByte('"')
|
||||
}
|
||||
|
||||
// This is adapted from the Go standard library's encoding/base64
|
||||
// [Encoding.Encode implementation]. It keeps the same encoding behavior we need
|
||||
// here, but writes directly into a strings.Builder. We inline this instead of using
|
||||
// encoding/base64 to avoid allocations.
|
||||
//
|
||||
// [Encoding.Encode implementation]: https://github.com/golang/go/blob/3b5954c6349d31465dca409b45ab6597e0942d9f/src/encoding/base64/base64.go#L139-L189
|
||||
func appendBase64(dst *strings.Builder, s string) {
|
||||
const encode = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
|
||||
dst.Grow(base64.StdEncoding.EncodedLen(len(s)))
|
||||
|
||||
i := 0
|
||||
for ; i+2 < len(s); i += 3 {
|
||||
n := uint32(s[i])<<16 | uint32(s[i+1])<<8 | uint32(s[i+2])
|
||||
_ = dst.WriteByte(encode[n>>18&0x3f])
|
||||
_ = dst.WriteByte(encode[n>>12&0x3f])
|
||||
_ = dst.WriteByte(encode[n>>6&0x3f])
|
||||
_ = dst.WriteByte(encode[n&0x3f])
|
||||
}
|
||||
|
||||
switch len(s) - i {
|
||||
case 1:
|
||||
n := uint32(s[i]) << 16
|
||||
_ = dst.WriteByte(encode[n>>18&0x3f])
|
||||
_ = dst.WriteByte(encode[n>>12&0x3f])
|
||||
_ = dst.WriteByte('=')
|
||||
_ = dst.WriteByte('=')
|
||||
case 2:
|
||||
n := uint32(s[i])<<16 | uint32(s[i+1])<<8
|
||||
_ = dst.WriteByte(encode[n>>18&0x3f])
|
||||
_ = dst.WriteByte(encode[n>>12&0x3f])
|
||||
_ = dst.WriteByte(encode[n>>6&0x3f])
|
||||
_ = dst.WriteByte('=')
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON encoding of the Value.
|
||||
func (v Value) MarshalJSON() ([]byte, error) {
|
||||
var jsonVal struct {
|
||||
|
||||
+26
-4
@@ -14,6 +14,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
maxParseErrors = 5
|
||||
|
||||
// W3C Baggage specification limits.
|
||||
// https://www.w3.org/TR/baggage/#limits
|
||||
maxMembers = 64
|
||||
maxBytesPerBaggageString = 8192
|
||||
|
||||
@@ -493,9 +497,15 @@ func New(members ...Member) (Baggage, error) {
|
||||
// from the W3C Baggage specification which allows duplicate list-members, but
|
||||
// conforms to the OpenTelemetry Baggage specification.
|
||||
//
|
||||
// If the baggage-string exceeds the maximum allowed members (64) or bytes
|
||||
// (8192), members are dropped until the limits are satisfied and an error is
|
||||
// returned along with the partial result.
|
||||
// If the raw baggage-string exceeds the maximum allowed bytes (8192), an
|
||||
// empty Baggage and an error are returned.
|
||||
//
|
||||
// Otherwise, members are parsed left-to-right and accumulated until one of
|
||||
// the following conditions is reached, at which point parsing stops and an
|
||||
// error is returned alongside the partial result:
|
||||
// - accepting the next member would cause the encoded baggage to exceed
|
||||
// 8192 bytes, or
|
||||
// - the baggage already contains 64 distinct keys.
|
||||
//
|
||||
// Invalid members are skipped and the error is returned along with the
|
||||
// partial result containing the valid members.
|
||||
@@ -504,9 +514,14 @@ func Parse(bStr string) (Baggage, error) {
|
||||
return Baggage{}, nil
|
||||
}
|
||||
|
||||
if n := len(bStr); n > maxBytesPerBaggageString {
|
||||
return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n)
|
||||
}
|
||||
|
||||
b := make(baggage.List)
|
||||
sizes := make(map[string]int) // Track per-key byte sizes
|
||||
var totalBytes int
|
||||
var parseErrors int
|
||||
var truncateErr error
|
||||
for memberStr := range strings.SplitSeq(bStr, listDelimiter) {
|
||||
// Check member count limit.
|
||||
@@ -517,7 +532,10 @@ func Parse(bStr string) (Baggage, error) {
|
||||
|
||||
m, err := parseMember(memberStr)
|
||||
if err != nil {
|
||||
truncateErr = errors.Join(truncateErr, err)
|
||||
parseErrors++
|
||||
if parseErrors <= maxParseErrors {
|
||||
truncateErr = errors.Join(truncateErr, err)
|
||||
}
|
||||
continue // skip invalid member, keep processing
|
||||
}
|
||||
|
||||
@@ -553,6 +571,10 @@ func Parse(bStr string) (Baggage, error) {
|
||||
totalBytes = newTotalBytes
|
||||
}
|
||||
|
||||
if dropped := parseErrors - maxParseErrors; dropped > 0 {
|
||||
truncateErr = errors.Join(truncateErr, fmt.Errorf("and %d more invalid member(s)", dropped))
|
||||
}
|
||||
|
||||
if len(b) == 0 {
|
||||
return Baggage{}, truncateErr
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# This is a renovate-friendly source of Docker images.
|
||||
FROM python:3.13.6-slim-bullseye@sha256:e98b521460ee75bca92175c16247bdf7275637a8faaeb2bcfa19d879ae5c4b9a AS python
|
||||
FROM otel/weaver:v0.22.1@sha256:33ae522ae4b71c1c562563c1d81f46aa0f79f088a0873199143a1f11ac30e5c9 AS weaver
|
||||
FROM otel/weaver:v0.23.0@sha256:7984ecb55b859eb3034ae9d836c4eeda137e2bdd0873b7ba2bb6c3d24d6ff457 AS weaver
|
||||
FROM avtodev/markdown-lint:v1@sha256:6aeedc2f49138ce7a1cd0adffc1b1c0321b841dc2102408967d9301c031949ee AS markdown
|
||||
Generated
Vendored
+18
@@ -87,6 +87,16 @@ func Value(v attribute.Value) *commonpb.AnyValue {
|
||||
av.Value = &commonpb.AnyValue_StringValue{
|
||||
StringValue: v.AsString(),
|
||||
}
|
||||
case attribute.BYTESLICE:
|
||||
av.Value = &commonpb.AnyValue_BytesValue{
|
||||
BytesValue: v.AsByteSlice(),
|
||||
}
|
||||
case attribute.SLICE:
|
||||
av.Value = &commonpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &commonpb.ArrayValue{
|
||||
Values: values(v.AsSlice()),
|
||||
},
|
||||
}
|
||||
case attribute.STRINGSLICE:
|
||||
av.Value = &commonpb.AnyValue_ArrayValue{
|
||||
ArrayValue: &commonpb.ArrayValue{
|
||||
@@ -149,3 +159,11 @@ func stringSliceValues(vals []string) []*commonpb.AnyValue {
|
||||
}
|
||||
return converted
|
||||
}
|
||||
|
||||
func values(vals []attribute.Value) []*commonpb.AnyValue {
|
||||
converted := make([]*commonpb.AnyValue, len(vals))
|
||||
for i, v := range vals {
|
||||
converted[i] = Value(v)
|
||||
}
|
||||
return converted
|
||||
}
|
||||
+34
-18
@@ -6,6 +6,7 @@ package otlptracegrpc // import "go.opentelemetry.io/otel/exporters/otlp/otlptra
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -16,6 +17,7 @@ import (
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal"
|
||||
@@ -26,11 +28,12 @@ import (
|
||||
)
|
||||
|
||||
type client struct {
|
||||
endpoint string
|
||||
dialOpts []grpc.DialOption
|
||||
metadata metadata.MD
|
||||
exportTimeout time.Duration
|
||||
requestFunc retry.RequestFunc
|
||||
endpoint string
|
||||
dialOpts []grpc.DialOption
|
||||
metadata metadata.MD
|
||||
exportTimeout time.Duration
|
||||
maxRequestSize int
|
||||
requestFunc retry.RequestFunc
|
||||
|
||||
// stopCtx is used as a parent context for all exports. Therefore, when it
|
||||
// is canceled with the stopFunc all exports are canceled.
|
||||
@@ -65,14 +68,15 @@ func newClient(opts ...Option) *client {
|
||||
ctx, cancel := context.WithCancel(context.Background()) //nolint:gosec // cancel called in client shutdown.
|
||||
|
||||
c := &client{
|
||||
endpoint: cfg.Traces.Endpoint,
|
||||
exportTimeout: cfg.Traces.Timeout,
|
||||
requestFunc: cfg.RetryConfig.RequestFunc(retryable),
|
||||
dialOpts: cfg.DialOptions,
|
||||
stopCtx: ctx,
|
||||
stopFunc: cancel,
|
||||
conn: cfg.GRPCConn,
|
||||
instID: counter.NextExporterID(),
|
||||
endpoint: cfg.Traces.Endpoint,
|
||||
exportTimeout: cfg.Traces.Timeout,
|
||||
maxRequestSize: cfg.Traces.MaxRequestSize,
|
||||
requestFunc: cfg.RetryConfig.RequestFunc(retryable),
|
||||
dialOpts: cfg.DialOptions,
|
||||
stopCtx: ctx,
|
||||
stopFunc: cancel,
|
||||
conn: cfg.GRPCConn,
|
||||
instID: counter.NextExporterID(),
|
||||
}
|
||||
|
||||
if len(cfg.Traces.Headers) > 0 {
|
||||
@@ -205,16 +209,28 @@ func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc
|
||||
ctx, cancel := c.exportContext(ctx)
|
||||
defer cancel()
|
||||
|
||||
var code codes.Code
|
||||
pbRequest := &coltracepb.ExportTraceServiceRequest{
|
||||
ResourceSpans: protoSpans,
|
||||
}
|
||||
|
||||
code := codes.Unknown
|
||||
if c.inst != nil {
|
||||
op := c.inst.ExportSpans(ctx, len(protoSpans))
|
||||
var spanCount int
|
||||
for _, rs := range protoSpans {
|
||||
for _, ss := range rs.ScopeSpans {
|
||||
spanCount += len(ss.Spans)
|
||||
}
|
||||
}
|
||||
op := c.inst.ExportSpans(ctx, spanCount)
|
||||
defer func() { op.End(uploadErr, code) }()
|
||||
}
|
||||
|
||||
if maxSize := c.maxRequestSize; maxSize > 0 && proto.Size(pbRequest) > maxSize {
|
||||
return fmt.Errorf("request message too large: exceeded %d bytes", maxSize)
|
||||
}
|
||||
|
||||
return c.requestFunc(ctx, func(iCtx context.Context) error {
|
||||
resp, err := c.tsc.Export(iCtx, &coltracepb.ExportTraceServiceRequest{
|
||||
ResourceSpans: protoSpans,
|
||||
})
|
||||
resp, err := c.tsc.Export(iCtx, pbRequest)
|
||||
if resp != nil && resp.PartialSuccess != nil {
|
||||
msg := resp.PartialSuccess.GetErrorMessage()
|
||||
n := resp.PartialSuccess.GetRejectedSpans()
|
||||
|
||||
Generated
Vendored
+7
-3
@@ -18,8 +18,8 @@ import (
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/internal/x"
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.40.0/otelconv"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.41.0"
|
||||
"go.opentelemetry.io/otel/semconv/v1.41.0/otelconv"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -72,6 +72,7 @@ var (
|
||||
func get[T any](p *sync.Pool) *[]T { return p.Get().(*[]T) }
|
||||
|
||||
func put[T any](p *sync.Pool, s *[]T) {
|
||||
clear(*s) // erase elements to allow GC to collect what they refer to.
|
||||
*s = (*s)[:0] // Reset.
|
||||
p.Put(s)
|
||||
}
|
||||
@@ -339,7 +340,10 @@ var errPartialPool = &sync.Pool{
|
||||
// the provided non-nil err.
|
||||
func rejected(n int64, err error) int64 {
|
||||
ps := errPartialPool.Get().(*internal.PartialSuccess)
|
||||
defer errPartialPool.Put(ps)
|
||||
defer func() {
|
||||
*ps = internal.PartialSuccess{} // erase fields to allow GC to collect them.
|
||||
errPartialPool.Put(ps)
|
||||
}()
|
||||
// Check for partial success.
|
||||
if errors.As(err, ps) {
|
||||
// Bound RejectedItems to [0, n]. This should not be needed,
|
||||
|
||||
Generated
Vendored
+28
-15
@@ -31,6 +31,9 @@ const (
|
||||
// DefaultTracesPath is a default URL path for endpoint that
|
||||
// receives spans.
|
||||
DefaultTracesPath string = "/v1/traces"
|
||||
// DefaultMaxRequestSize is the default maximum size of a serialized export
|
||||
// request, before compression.
|
||||
DefaultMaxRequestSize int = 64 * 1024 * 1024
|
||||
// DefaultTimeout is a default max waiting time for the backend to process
|
||||
// each span batch.
|
||||
DefaultTimeout time.Duration = 10 * time.Second
|
||||
@@ -42,13 +45,14 @@ type (
|
||||
HTTPTransportProxyFunc func(*http.Request) (*url.URL, error)
|
||||
|
||||
SignalConfig struct {
|
||||
Endpoint string
|
||||
Insecure bool
|
||||
TLSCfg *tls.Config
|
||||
Headers map[string]string
|
||||
Compression Compression
|
||||
Timeout time.Duration
|
||||
URLPath string
|
||||
Endpoint string
|
||||
Insecure bool
|
||||
TLSCfg *tls.Config
|
||||
Headers map[string]string
|
||||
Compression Compression
|
||||
MaxRequestSize int
|
||||
Timeout time.Duration
|
||||
URLPath string
|
||||
|
||||
// gRPC configurations
|
||||
GRPCCredentials credentials.TransportCredentials
|
||||
@@ -77,10 +81,11 @@ type (
|
||||
func NewHTTPConfig(opts ...HTTPOption) Config {
|
||||
cfg := Config{
|
||||
Traces: SignalConfig{
|
||||
Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorHTTPPort),
|
||||
URLPath: DefaultTracesPath,
|
||||
Compression: NoCompression,
|
||||
Timeout: DefaultTimeout,
|
||||
Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorHTTPPort),
|
||||
URLPath: DefaultTracesPath,
|
||||
Compression: NoCompression,
|
||||
MaxRequestSize: DefaultMaxRequestSize,
|
||||
Timeout: DefaultTimeout,
|
||||
},
|
||||
RetryConfig: retry.DefaultConfig,
|
||||
}
|
||||
@@ -111,10 +116,11 @@ func NewGRPCConfig(opts ...GRPCOption) Config {
|
||||
userAgent := "OTel OTLP Exporter Go/" + otlptrace.Version()
|
||||
cfg := Config{
|
||||
Traces: SignalConfig{
|
||||
Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorGRPCPort),
|
||||
URLPath: DefaultTracesPath,
|
||||
Compression: NoCompression,
|
||||
Timeout: DefaultTimeout,
|
||||
Endpoint: fmt.Sprintf("%s:%d", DefaultCollectorHost, DefaultCollectorGRPCPort),
|
||||
URLPath: DefaultTracesPath,
|
||||
Compression: NoCompression,
|
||||
MaxRequestSize: DefaultMaxRequestSize,
|
||||
Timeout: DefaultTimeout,
|
||||
},
|
||||
RetryConfig: retry.DefaultConfig,
|
||||
DialOptions: []grpc.DialOption{grpc.WithUserAgent(userAgent)},
|
||||
@@ -345,6 +351,13 @@ func WithTimeout(duration time.Duration) GenericOption {
|
||||
})
|
||||
}
|
||||
|
||||
func WithMaxRequestSize(size int) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Traces.MaxRequestSize = size
|
||||
return cfg
|
||||
})
|
||||
}
|
||||
|
||||
func WithProxy(pf HTTPTransportProxyFunc) GenericOption {
|
||||
return newGenericOption(func(cfg Config) Config {
|
||||
cfg.Traces.Proxy = pf
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -5,4 +5,4 @@ package internal // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace/ot
|
||||
|
||||
// Version is the current release version of the OpenTelemetry OTLP gRPC trace
|
||||
// exporter in use.
|
||||
const Version = "1.43.0"
|
||||
const Version = "1.44.0"
|
||||
Generated
Vendored
+10
@@ -192,6 +192,16 @@ func WithTimeout(duration time.Duration) Option {
|
||||
return wrappedOption{otlpconfig.WithTimeout(duration)}
|
||||
}
|
||||
|
||||
// WithMaxRequestSize sets the maximum size, in bytes, of a serialized export
|
||||
// request, before compression, that the exporter will send.
|
||||
//
|
||||
// If size is less than or equal to zero, no request-size limit is applied.
|
||||
// Disabling the limit is not recommended because it can lead to excessive
|
||||
// resource consumption or abuse.
|
||||
func WithMaxRequestSize(size int) Option {
|
||||
return wrappedOption{otlpconfig.WithMaxRequestSize(size)}
|
||||
}
|
||||
|
||||
// WithRetry sets the retry policy for transient retryable errors that may be
|
||||
// returned by the target endpoint when exporting a batch of spans.
|
||||
//
|
||||
|
||||
+1
-1
@@ -5,5 +5,5 @@ package otlptrace // import "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
|
||||
|
||||
// Version is the current release version of the OpenTelemetry OTLP trace exporter in use.
|
||||
func Version() string {
|
||||
return "1.43.0"
|
||||
return "1.44.0"
|
||||
}
|
||||
+9
@@ -51,6 +51,9 @@ type Float64ObservableCounterConfig struct {
|
||||
func NewFloat64ObservableCounterConfig(opts ...Float64ObservableCounterOption) Float64ObservableCounterConfig {
|
||||
var config Float64ObservableCounterConfig
|
||||
for _, o := range opts {
|
||||
if _, ok := o.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
config = o.applyFloat64ObservableCounter(config)
|
||||
}
|
||||
return config
|
||||
@@ -111,6 +114,9 @@ func NewFloat64ObservableUpDownCounterConfig(
|
||||
) Float64ObservableUpDownCounterConfig {
|
||||
var config Float64ObservableUpDownCounterConfig
|
||||
for _, o := range opts {
|
||||
if _, ok := o.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
config = o.applyFloat64ObservableUpDownCounter(config)
|
||||
}
|
||||
return config
|
||||
@@ -168,6 +174,9 @@ type Float64ObservableGaugeConfig struct {
|
||||
func NewFloat64ObservableGaugeConfig(opts ...Float64ObservableGaugeOption) Float64ObservableGaugeConfig {
|
||||
var config Float64ObservableGaugeConfig
|
||||
for _, o := range opts {
|
||||
if _, ok := o.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
config = o.applyFloat64ObservableGauge(config)
|
||||
}
|
||||
return config
|
||||
|
||||
+9
@@ -50,6 +50,9 @@ type Int64ObservableCounterConfig struct {
|
||||
func NewInt64ObservableCounterConfig(opts ...Int64ObservableCounterOption) Int64ObservableCounterConfig {
|
||||
var config Int64ObservableCounterConfig
|
||||
for _, o := range opts {
|
||||
if _, ok := o.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
config = o.applyInt64ObservableCounter(config)
|
||||
}
|
||||
return config
|
||||
@@ -110,6 +113,9 @@ func NewInt64ObservableUpDownCounterConfig(
|
||||
) Int64ObservableUpDownCounterConfig {
|
||||
var config Int64ObservableUpDownCounterConfig
|
||||
for _, o := range opts {
|
||||
if _, ok := o.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
config = o.applyInt64ObservableUpDownCounter(config)
|
||||
}
|
||||
return config
|
||||
@@ -167,6 +173,9 @@ type Int64ObservableGaugeConfig struct {
|
||||
func NewInt64ObservableGaugeConfig(opts ...Int64ObservableGaugeOption) Int64ObservableGaugeConfig {
|
||||
var config Int64ObservableGaugeConfig
|
||||
for _, o := range opts {
|
||||
if _, ok := o.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
config = o.applyInt64ObservableGauge(config)
|
||||
}
|
||||
return config
|
||||
|
||||
+7
@@ -42,11 +42,18 @@ type MeterOption interface {
|
||||
applyMeter(MeterConfig) MeterConfig
|
||||
}
|
||||
|
||||
type experimentalOption interface {
|
||||
Experimental()
|
||||
}
|
||||
|
||||
// NewMeterConfig creates a new MeterConfig and applies
|
||||
// all the given options.
|
||||
func NewMeterConfig(opts ...MeterOption) MeterConfig {
|
||||
var config MeterConfig
|
||||
for _, o := range opts {
|
||||
if _, ok := o.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
config = o.applyMeter(config)
|
||||
}
|
||||
return config
|
||||
|
||||
+39
-12
@@ -24,10 +24,10 @@ all instruments fall into two overlapping logical categories: asynchronous or
|
||||
synchronous, and int64 or float64.
|
||||
|
||||
All synchronous instruments ([Int64Counter], [Int64UpDownCounter],
|
||||
[Int64Histogram], [Float64Counter], [Float64UpDownCounter], and
|
||||
[Float64Histogram]) are used to measure the operation and performance of source
|
||||
code during the source code execution. These instruments only make measurements
|
||||
when the source code they instrument is run.
|
||||
[Int64Histogram], [Int64Gauge], [Float64Counter], [Float64UpDownCounter],
|
||||
[Float64Histogram], and [Float64Gauge]) are used to measure the operation and
|
||||
performance of source code during the source code execution. These instruments
|
||||
only make measurements when the source code they instrument is run.
|
||||
|
||||
All asynchronous instruments ([Int64ObservableCounter],
|
||||
[Int64ObservableUpDownCounter], [Int64ObservableGauge],
|
||||
@@ -50,9 +50,11 @@ incrementally increase in value. UpDownCounters ([Int64UpDownCounter],
|
||||
values that can increase and decrease. When more information needs to be
|
||||
conveyed about all the synchronous measurements made during a collection cycle,
|
||||
a Histogram ([Int64Histogram] and [Float64Histogram]) should be used. Finally,
|
||||
when just the most recent measurement needs to be conveyed about an
|
||||
asynchronous measurement, a Gauge ([Int64ObservableGauge] and
|
||||
[Float64ObservableGauge]) should be used.
|
||||
when just the most recent measurement needs to be conveyed, a Gauge
|
||||
([Int64Gauge], [Float64Gauge], [Int64ObservableGauge], and
|
||||
[Float64ObservableGauge]) should be used: the synchronous variants record an
|
||||
instantaneous value at a specific point in code, while the observable variants
|
||||
sample the value via a callback once per collection cycle.
|
||||
|
||||
See the [OpenTelemetry documentation] for more information about instruments
|
||||
and their intended use.
|
||||
@@ -80,11 +82,11 @@ Measurements are made by recording values and information about the values with
|
||||
an instrument. How these measurements are recorded depends on the instrument.
|
||||
|
||||
Measurements for synchronous instruments ([Int64Counter], [Int64UpDownCounter],
|
||||
[Int64Histogram], [Float64Counter], [Float64UpDownCounter], and
|
||||
[Float64Histogram]) are recorded using the instrument methods directly. All
|
||||
counter instruments have an Add method that is used to measure an increment
|
||||
value, and all histogram instruments have a Record method to measure a data
|
||||
point.
|
||||
[Int64Histogram], [Int64Gauge], [Float64Counter], [Float64UpDownCounter],
|
||||
[Float64Histogram], and [Float64Gauge]) are recorded using the instrument
|
||||
methods directly. All counter instruments have an Add method that is used to
|
||||
measure an increment value, and all histogram and synchronous gauge
|
||||
instruments have a Record method to measure a data point.
|
||||
|
||||
Asynchronous instruments ([Int64ObservableCounter],
|
||||
[Int64ObservableUpDownCounter], [Int64ObservableGauge],
|
||||
@@ -107,6 +109,31 @@ respectively):
|
||||
If the criteria are not met, use the RegisterCallback method of the [Meter] that
|
||||
created the instrument to register a [Callback].
|
||||
|
||||
# Avoiding Expensive Computations
|
||||
|
||||
All synchronous instruments provide an Enabled method that reports whether the
|
||||
instrument will process measurements for the given context. When no SDK is
|
||||
registered or the instrument is otherwise disabled, Enabled returns false. This
|
||||
can be used to avoid expensive measurement work when a measurement will not be
|
||||
recorded:
|
||||
|
||||
if counter.Enabled(ctx) {
|
||||
counter.Add(ctx, 1, metric.WithAttributes(expensiveAttributes()...))
|
||||
}
|
||||
|
||||
This is especially valuable when computing attributes is expensive.
|
||||
[WithAttributes] performs non-trivial work on every call to build an
|
||||
[attribute.Set] from the provided attributes, and that work is wasted if the
|
||||
measurement is not recorded.
|
||||
|
||||
For performance sensitive code where the same attribute set is used repeatedly,
|
||||
prefer [WithAttributeSet]. It accepts a pre-built [attribute.Set], letting you
|
||||
pay the construction cost once and reuse it across many measurements:
|
||||
|
||||
attrs := attribute.NewSet(attribute.String("key", "val"))
|
||||
// ... later, on each call:
|
||||
counter.Add(ctx, 1, metric.WithAttributeSet(attrs))
|
||||
|
||||
# API Implementations
|
||||
|
||||
This package does not conform to the standard Go versioning policy, all of its
|
||||
|
||||
+33
-6
@@ -3,7 +3,9 @@
|
||||
|
||||
package metric // import "go.opentelemetry.io/otel/metric"
|
||||
|
||||
import "go.opentelemetry.io/otel/attribute"
|
||||
import (
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
)
|
||||
|
||||
// Observable is used as a grouping mechanism for all instruments that are
|
||||
// updated within a Callback.
|
||||
@@ -228,6 +230,9 @@ type AddConfig struct {
|
||||
func NewAddConfig(opts []AddOption) AddConfig {
|
||||
config := AddConfig{attrs: *attribute.EmptySet()}
|
||||
for _, o := range opts {
|
||||
if _, ok := o.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
config = o.applyAdd(config)
|
||||
}
|
||||
return config
|
||||
@@ -253,6 +258,9 @@ type RecordConfig struct {
|
||||
func NewRecordConfig(opts []RecordOption) RecordConfig {
|
||||
config := RecordConfig{attrs: *attribute.EmptySet()}
|
||||
for _, o := range opts {
|
||||
if _, ok := o.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
config = o.applyRecord(config)
|
||||
}
|
||||
return config
|
||||
@@ -278,6 +286,9 @@ type ObserveConfig struct {
|
||||
func NewObserveConfig(opts []ObserveOption) ObserveConfig {
|
||||
config := ObserveConfig{attrs: *attribute.EmptySet()}
|
||||
for _, o := range opts {
|
||||
if _, ok := o.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
config = o.applyObserve(config)
|
||||
}
|
||||
return config
|
||||
@@ -299,6 +310,10 @@ type attrOpt struct {
|
||||
set attribute.Set
|
||||
}
|
||||
|
||||
func (o *attrOpt) Set(set attribute.Set) {
|
||||
o.set = set
|
||||
}
|
||||
|
||||
// mergeSets returns the union of keys between a and b. Any duplicate keys will
|
||||
// use the value associated with b.
|
||||
func mergeSets(a, b attribute.Set) attribute.Set {
|
||||
@@ -311,7 +326,7 @@ func mergeSets(a, b attribute.Set) attribute.Set {
|
||||
return attribute.NewSet(merged...)
|
||||
}
|
||||
|
||||
func (o attrOpt) applyAdd(c AddConfig) AddConfig {
|
||||
func (o *attrOpt) applyAdd(c AddConfig) AddConfig {
|
||||
switch {
|
||||
case o.set.Len() == 0:
|
||||
case c.attrs.Len() == 0:
|
||||
@@ -322,7 +337,7 @@ func (o attrOpt) applyAdd(c AddConfig) AddConfig {
|
||||
return c
|
||||
}
|
||||
|
||||
func (o attrOpt) applyRecord(c RecordConfig) RecordConfig {
|
||||
func (o *attrOpt) applyRecord(c RecordConfig) RecordConfig {
|
||||
switch {
|
||||
case o.set.Len() == 0:
|
||||
case c.attrs.Len() == 0:
|
||||
@@ -333,7 +348,7 @@ func (o attrOpt) applyRecord(c RecordConfig) RecordConfig {
|
||||
return c
|
||||
}
|
||||
|
||||
func (o attrOpt) applyObserve(c ObserveConfig) ObserveConfig {
|
||||
func (o *attrOpt) applyObserve(c ObserveConfig) ObserveConfig {
|
||||
switch {
|
||||
case o.set.Len() == 0:
|
||||
case c.attrs.Len() == 0:
|
||||
@@ -350,8 +365,14 @@ func (o attrOpt) applyObserve(c ObserveConfig) ObserveConfig {
|
||||
// If multiple WithAttributeSet or WithAttributes options are passed the
|
||||
// attributes will be merged together in the order they are passed. Attributes
|
||||
// with duplicate keys will use the last value passed.
|
||||
//
|
||||
// Experimental: The returned option may implement
|
||||
// [go.opentelemetry.io/otel/metric/x.Settable][attribute.Set], which can be
|
||||
// used to replace the option's attribute set and reuse the option without
|
||||
// additional allocations. This behavior is experimental and may be changed or
|
||||
// removed in a future release without notice.
|
||||
func WithAttributeSet(attributes attribute.Set) MeasurementOption {
|
||||
return attrOpt{set: attributes}
|
||||
return &attrOpt{set: attributes}
|
||||
}
|
||||
|
||||
// WithAttributes converts attributes into an attribute Set and sets the Set to
|
||||
@@ -369,8 +390,14 @@ func WithAttributeSet(attributes attribute.Set) MeasurementOption {
|
||||
//
|
||||
// See [WithAttributeSet] for information about how multiple WithAttributes are
|
||||
// merged.
|
||||
//
|
||||
// Experimental: The returned option may implement
|
||||
// [go.opentelemetry.io/otel/metric/x.Settable][[]attribute.KeyValue], which can be
|
||||
// used to replace the option's attributes and reuse the option without
|
||||
// additional allocations. This behavior is experimental and may be changed or
|
||||
// removed in a future release without notice.
|
||||
func WithAttributes(attributes ...attribute.KeyValue) MeasurementOption {
|
||||
cp := make([]attribute.KeyValue, len(attributes))
|
||||
copy(cp, attributes)
|
||||
return attrOpt{set: attribute.NewSet(cp...)}
|
||||
return &attrOpt{set: attribute.NewSet(cp...)}
|
||||
}
|
||||
+12
@@ -51,6 +51,9 @@ type Float64CounterConfig struct {
|
||||
func NewFloat64CounterConfig(opts ...Float64CounterOption) Float64CounterConfig {
|
||||
var config Float64CounterConfig
|
||||
for _, o := range opts {
|
||||
if _, ok := o.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
config = o.applyFloat64Counter(config)
|
||||
}
|
||||
return config
|
||||
@@ -116,6 +119,9 @@ type Float64UpDownCounterConfig struct {
|
||||
func NewFloat64UpDownCounterConfig(opts ...Float64UpDownCounterOption) Float64UpDownCounterConfig {
|
||||
var config Float64UpDownCounterConfig
|
||||
for _, o := range opts {
|
||||
if _, ok := o.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
config = o.applyFloat64UpDownCounter(config)
|
||||
}
|
||||
return config
|
||||
@@ -182,6 +188,9 @@ type Float64HistogramConfig struct {
|
||||
func NewFloat64HistogramConfig(opts ...Float64HistogramOption) Float64HistogramConfig {
|
||||
var config Float64HistogramConfig
|
||||
for _, o := range opts {
|
||||
if _, ok := o.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
config = o.applyFloat64Histogram(config)
|
||||
}
|
||||
return config
|
||||
@@ -251,6 +260,9 @@ type Float64GaugeConfig struct {
|
||||
func NewFloat64GaugeConfig(opts ...Float64GaugeOption) Float64GaugeConfig {
|
||||
var config Float64GaugeConfig
|
||||
for _, o := range opts {
|
||||
if _, ok := o.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
config = o.applyFloat64Gauge(config)
|
||||
}
|
||||
return config
|
||||
|
||||
+12
@@ -51,6 +51,9 @@ type Int64CounterConfig struct {
|
||||
func NewInt64CounterConfig(opts ...Int64CounterOption) Int64CounterConfig {
|
||||
var config Int64CounterConfig
|
||||
for _, o := range opts {
|
||||
if _, ok := o.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
config = o.applyInt64Counter(config)
|
||||
}
|
||||
return config
|
||||
@@ -116,6 +119,9 @@ type Int64UpDownCounterConfig struct {
|
||||
func NewInt64UpDownCounterConfig(opts ...Int64UpDownCounterOption) Int64UpDownCounterConfig {
|
||||
var config Int64UpDownCounterConfig
|
||||
for _, o := range opts {
|
||||
if _, ok := o.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
config = o.applyInt64UpDownCounter(config)
|
||||
}
|
||||
return config
|
||||
@@ -182,6 +188,9 @@ type Int64HistogramConfig struct {
|
||||
func NewInt64HistogramConfig(opts ...Int64HistogramOption) Int64HistogramConfig {
|
||||
var config Int64HistogramConfig
|
||||
for _, o := range opts {
|
||||
if _, ok := o.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
config = o.applyInt64Histogram(config)
|
||||
}
|
||||
return config
|
||||
@@ -251,6 +260,9 @@ type Int64GaugeConfig struct {
|
||||
func NewInt64GaugeConfig(opts ...Int64GaugeOption) Int64GaugeConfig {
|
||||
var config Int64GaugeConfig
|
||||
for _, o := range opts {
|
||||
if _, ok := o.(experimentalOption); ok {
|
||||
continue
|
||||
}
|
||||
config = o.applyInt64Gauge(config)
|
||||
}
|
||||
return config
|
||||
|
||||
+60
-12
@@ -5,6 +5,9 @@ package propagation // import "go.opentelemetry.io/otel/propagation"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"go.opentelemetry.io/otel/baggage"
|
||||
"go.opentelemetry.io/otel/internal/errorhandler"
|
||||
@@ -13,11 +16,18 @@ import (
|
||||
const (
|
||||
baggageHeader = "baggage"
|
||||
|
||||
maxParseErrors = 5
|
||||
|
||||
// W3C Baggage specification limits.
|
||||
// https://www.w3.org/TR/baggage/#limits
|
||||
maxMembers = 64
|
||||
maxMembers = 64
|
||||
maxBytesPerBaggageString = 8192
|
||||
)
|
||||
|
||||
// handleExtractErrOnce limits error reporting for attacker-controlled baggage headers
|
||||
// to one process-wide emission, preventing repeated extraction from flooding logs.
|
||||
var handleExtractErrOnce sync.Once
|
||||
|
||||
// Baggage is a propagator that supports the W3C Baggage format.
|
||||
//
|
||||
// This propagates user-defined baggage associated with a trace. The complete
|
||||
@@ -57,7 +67,9 @@ func extractSingleBaggage(parent context.Context, carrier TextMapCarrier) contex
|
||||
|
||||
bag, err := baggage.Parse(bStr)
|
||||
if err != nil {
|
||||
errorhandler.GetErrorHandler().Handle(err)
|
||||
handleExtractErrOnce.Do(func() {
|
||||
errorhandler.GetErrorHandler().Handle(err)
|
||||
})
|
||||
}
|
||||
if bag.Len() == 0 {
|
||||
return parent
|
||||
@@ -72,24 +84,60 @@ func extractMultiBaggage(parent context.Context, carrier ValuesGetter) context.C
|
||||
}
|
||||
|
||||
var members []baggage.Member
|
||||
for _, bStr := range bVals {
|
||||
currBag, err := baggage.Parse(bStr)
|
||||
if err != nil {
|
||||
errorhandler.GetErrorHandler().Handle(err)
|
||||
var totalBytes int
|
||||
var parseErrors int
|
||||
var truncateErr error
|
||||
for i, bStr := range bVals {
|
||||
if i > 0 {
|
||||
totalBytes++ // comma separator between combined header values
|
||||
}
|
||||
if currBag.Len() == 0 {
|
||||
continue
|
||||
totalBytes += len(bStr)
|
||||
if totalBytes > maxBytesPerBaggageString {
|
||||
// Per the W3C Baggage spec, the byte limit applies to the
|
||||
// combination of all baggage headers, not each header
|
||||
// individually. Mirror the single-header behavior of
|
||||
// reporting the error and returning the parent context
|
||||
// with no baggage attached.
|
||||
handleExtractErrOnce.Do(func() {
|
||||
errorhandler.GetErrorHandler().Handle(fmt.Errorf(
|
||||
"baggage: aggregate header size %d exceeds %d byte limit",
|
||||
totalBytes,
|
||||
maxBytesPerBaggageString,
|
||||
))
|
||||
})
|
||||
return parent
|
||||
}
|
||||
members = append(members, currBag.Members()...)
|
||||
if len(members) >= maxMembers {
|
||||
break
|
||||
|
||||
// If members exceed the limit, stop parsing baggage.
|
||||
if len(members) <= maxMembers {
|
||||
currBag, err := baggage.Parse(bStr)
|
||||
if err != nil {
|
||||
parseErrors++
|
||||
if parseErrors <= maxParseErrors {
|
||||
truncateErr = errors.Join(truncateErr, err)
|
||||
}
|
||||
}
|
||||
if currBag.Len() == 0 {
|
||||
continue
|
||||
}
|
||||
members = append(members, currBag.Members()...)
|
||||
}
|
||||
}
|
||||
|
||||
if dropped := parseErrors - maxParseErrors; dropped > 0 {
|
||||
truncateErr = errors.Join(truncateErr, fmt.Errorf("and %d more error(s)", dropped))
|
||||
}
|
||||
|
||||
b, err := baggage.New(members...)
|
||||
if err != nil {
|
||||
errorhandler.GetErrorHandler().Handle(err)
|
||||
truncateErr = errors.Join(truncateErr, err)
|
||||
}
|
||||
if truncateErr != nil {
|
||||
handleExtractErrOnce.Do(func() {
|
||||
errorhandler.GetErrorHandler().Handle(truncateErr)
|
||||
})
|
||||
}
|
||||
|
||||
if b.Len() == 0 {
|
||||
return parent
|
||||
}
|
||||
|
||||
+2
-2
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/sdk"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.41.0"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -79,7 +79,7 @@ func (sd stringDetector) Detect(context.Context) (*Resource, error) {
|
||||
}
|
||||
a := sd.K.String(value)
|
||||
if !a.Valid() {
|
||||
return nil, fmt.Errorf("invalid attribute: %q -> %q", a.Key, a.Value.Emit())
|
||||
return nil, fmt.Errorf("invalid attribute: %q -> %q", a.Key, a.Value.String())
|
||||
}
|
||||
return NewWithAttributes(sd.schemaURL, sd.K.String(value)), nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ import (
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.41.0"
|
||||
)
|
||||
|
||||
type containerIDProvider func() (string, error)
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.41.0"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.41.0"
|
||||
)
|
||||
|
||||
type hostIDProvider func() (string, error)
|
||||
|
||||
+5
-2
@@ -5,10 +5,13 @@
|
||||
|
||||
package resource // import "go.opentelemetry.io/otel/sdk/resource"
|
||||
|
||||
import "os/exec"
|
||||
import (
|
||||
"context"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func execCommand(name string, arg ...string) (string, error) {
|
||||
cmd := exec.Command(name, arg...)
|
||||
cmd := exec.CommandContext(context.Background(), name, arg...)
|
||||
b, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
Loaded 100 of 273 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user