mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-09-08 19:58:46 -04:00
Compare commits
107
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41a9ad809d | ||
|
|
0f97fc5e60 | ||
|
|
f060ca4660 | ||
|
|
3112d136ff | ||
|
|
4414bd0d08 | ||
|
|
8593a1409a | ||
|
|
6d9d2baa80 | ||
|
|
d6c6340705 | ||
|
|
9915fc8ea7 | ||
|
|
6dff206e8a | ||
|
|
a478f59c50 | ||
|
|
48a9448ba7 | ||
|
|
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.
|
||||
|
||||
@@ -33,8 +33,9 @@ require (
|
||||
github.com/go-micro/plugins/v4/store/nats-js-kv v0.0.0-20240726082623-6831adfdcdc4
|
||||
github.com/go-micro/plugins/v4/wrapper/monitoring/prometheus v1.2.0
|
||||
github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry v1.2.0
|
||||
github.com/go-playground/validator/v10 v10.30.2
|
||||
github.com/go-playground/validator/v10 v10.30.3
|
||||
github.com/go-resty/resty/v2 v2.17.2
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1
|
||||
github.com/golang/protobuf v1.5.4
|
||||
github.com/google/go-cmp v0.7.0
|
||||
@@ -42,17 +43,16 @@ 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
|
||||
github.com/mna/pigeon v1.3.0
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
|
||||
github.com/nats-io/nats-server/v2 v2.14.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
|
||||
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.52.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
|
||||
@@ -223,7 +223,6 @@ require (
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/go-test/deep v1.1.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
@@ -315,7 +314,7 @@ require (
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
|
||||
github.com/pablodz/inotifywaitgo v0.0.9 // indirect
|
||||
github.com/pablodz/inotifywaitgo v0.0.12 // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
@@ -382,19 +381,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
|
||||
@@ -413,5 +412,4 @@ exclude github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||
|
||||
replace github.com/go-micro/plugins/v4/store/nats-js-kv => github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-20250512152754-23325793059a
|
||||
|
||||
// to get the logger injection (https://github.com/pablodz/inotifywaitgo/pull/11)
|
||||
replace github.com/pablodz/inotifywaitgo v0.0.9 => github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9
|
||||
replace github.com/opencloud-eu/reva/v2 => github.com/opencloud-eu/reva/v2 v2.46.3-0.20260609111154-079ecc53abaa
|
||||
@@ -449,8 +449,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.30.2 h1:JiFIMtSSHb2/XBUbWM4i/MpeQm9ZK2xqPNk8vgvu5JQ=
|
||||
github.com/go-playground/validator/v10 v10.30.2/go.mod h1:mAf2pIOVXjTEBrwUMGKkCWKKPs9NheYGabeB04txQSc=
|
||||
github.com/go-playground/validator/v10 v10.30.3 h1:4MU6YkEwx7GbcPJOZxrtbu+QfF3pJLJuaYTeAH0DYy8=
|
||||
github.com/go-playground/validator/v10 v10.30.3/go.mod h1:4Axh7oCNGcoGkqLoE4YWt6n20mcEIsPRlB7vPk3lpyc=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
|
||||
@@ -466,8 +466,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
|
||||
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
@@ -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=
|
||||
@@ -857,8 +857,6 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mna/pigeon v1.3.0 h1:/3fzVrl1C2RK3x04tyL+ribn+3S3VSEFFbCFLmRPAoc=
|
||||
@@ -948,12 +946,10 @@ github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-202505121527
|
||||
github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-20250512152754-23325793059a/go.mod h1:pjcozWijkNPbEtX5SIQaxEW/h8VAVZYTLx+70bmB3LY=
|
||||
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 h1:W1ms+lP5lUUIzjRGDg93WrQfZJZCaV1ZP3KeyXi8bzY=
|
||||
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89/go.mod h1:vigJkNss1N2QEceCuNw/ullDehncuJNFB6mEnzfq9UI=
|
||||
github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9 h1:dIftlX03Bzfbujhp9B54FbgER0VBDWJi/w8RBxJlzxU=
|
||||
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.3-0.20260609111154-079ecc53abaa h1:MSS7EwbPh74IpH0Tidn6JoeMNF1D1A66MaWk0Ugqnv0=
|
||||
github.com/opencloud-eu/reva/v2 v2.46.3-0.20260609111154-079ecc53abaa/go.mod h1:RoFQt+u7edxwzHr1IZ2Y6VaDinMiRPQupAvMBy3WVmE=
|
||||
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=
|
||||
@@ -972,6 +968,8 @@ github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CF
|
||||
github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||
github.com/pablodz/inotifywaitgo v0.0.12 h1:DVJJTFMDlSAdO46yisPF2vzozs/r+4k6ih8MVghq78M=
|
||||
github.com/pablodz/inotifywaitgo v0.0.12/go.mod h1:JWyDC6H+5oZRdUJUgKuaye+8Ph5hEs6HVzVoPKzWSGI=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
@@ -1065,8 +1063,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 +1298,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 +1360,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.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
||||
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 +1451,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 +1558,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 +1568,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 +1709,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 +1728,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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package revaconfig
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/opencloud/pkg/log"
|
||||
"github.com/opencloud-eu/opencloud/services/app-registry/pkg/config"
|
||||
)
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package defaults
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/config/defaults"
|
||||
"github.com/opencloud-eu/opencloud/pkg/shared"
|
||||
"github.com/opencloud-eu/opencloud/pkg/structs"
|
||||
"github.com/opencloud-eu/opencloud/services/auth-basic/pkg/config"
|
||||
@@ -38,8 +35,7 @@ func DefaultConfig() *config.Config {
|
||||
AuthProvider: "ldap",
|
||||
AuthProviders: config.AuthProviders{
|
||||
LDAP: config.LDAPProvider{
|
||||
URI: "ldaps://localhost:9235",
|
||||
CACert: filepath.Join(defaults.BaseDataPath(), "idm", "ldap.crt"),
|
||||
URI: "ldap://localhost:9236",
|
||||
Insecure: false,
|
||||
UserBaseDN: "ou=users,o=libregraph-idm",
|
||||
GroupBaseDN: "ou=groups,o=libregraph-idm",
|
||||
|
||||
@@ -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,11 +1,9 @@
|
||||
package defaults
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/config/defaults"
|
||||
"github.com/opencloud-eu/opencloud/pkg/shared"
|
||||
"github.com/opencloud-eu/opencloud/pkg/structs"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/config"
|
||||
@@ -79,9 +77,8 @@ func DefaultConfig() *config.Config {
|
||||
Identity: config.Identity{
|
||||
Backend: "ldap",
|
||||
LDAP: config.LDAP{
|
||||
URI: "ldaps://localhost:9235",
|
||||
URI: "ldap://localhost:9236",
|
||||
Insecure: false,
|
||||
CACert: path.Join(defaults.BaseDataPath(), "idm", "ldap.crt"),
|
||||
BindDN: "uid=libregraph,ou=sysusers,o=libregraph-idm",
|
||||
UseServerUUID: false,
|
||||
UsePasswordModExOp: true,
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package defaults
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/config/defaults"
|
||||
"github.com/opencloud-eu/opencloud/pkg/shared"
|
||||
"github.com/opencloud-eu/opencloud/pkg/structs"
|
||||
"github.com/opencloud-eu/opencloud/services/groups/pkg/config"
|
||||
@@ -38,8 +35,7 @@ func DefaultConfig() *config.Config {
|
||||
Driver: "ldap",
|
||||
Drivers: config.Drivers{
|
||||
LDAP: config.LDAPDriver{
|
||||
URI: "ldaps://localhost:9235",
|
||||
CACert: filepath.Join(defaults.BaseDataPath(), "idm", "ldap.crt"),
|
||||
URI: "ldap://localhost:9236",
|
||||
Insecure: false,
|
||||
UserBaseDN: "ou=users,o=libregraph-idm",
|
||||
GroupBaseDN: "ou=groups,o=libregraph-idm",
|
||||
|
||||
@@ -8,10 +8,11 @@ import (
|
||||
"html/template"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/config/configlog"
|
||||
pkgcrypto "github.com/opencloud-eu/opencloud/pkg/crypto"
|
||||
"github.com/opencloud-eu/opencloud/pkg/config/defaults"
|
||||
"github.com/opencloud-eu/opencloud/pkg/log"
|
||||
"github.com/opencloud-eu/opencloud/pkg/runner"
|
||||
"github.com/opencloud-eu/opencloud/services/idm"
|
||||
@@ -47,23 +48,25 @@ func Server(cfg *config.Config) *cobra.Command {
|
||||
gr := runner.NewGroup()
|
||||
{
|
||||
servercfg := server.Config{
|
||||
Logger: log.LogrusWrap(logger.Logger),
|
||||
LDAPHandler: "boltdb",
|
||||
LDAPSListenAddr: cfg.IDM.LDAPSAddr,
|
||||
TLSCertFile: cfg.IDM.Cert,
|
||||
TLSKeyFile: cfg.IDM.Key,
|
||||
LDAPBaseDN: "o=libregraph-idm",
|
||||
LDAPAdminDN: "uid=libregraph,ou=sysusers,o=libregraph-idm",
|
||||
Logger: log.LogrusWrap(logger.Logger),
|
||||
LDAPHandler: "boltdb",
|
||||
LDAPListenAddr: cfg.IDM.LDAPAddr,
|
||||
LDAPBaseDN: "o=libregraph-idm",
|
||||
LDAPAdminDN: "uid=libregraph,ou=sysusers,o=libregraph-idm",
|
||||
|
||||
BoltDBFile: cfg.IDM.DatabasePath,
|
||||
}
|
||||
|
||||
if cfg.IDM.LDAPSAddr != "" {
|
||||
// Generate a self-signing cert if no certificate is present
|
||||
if err := pkgcrypto.GenCert(cfg.IDM.Cert, cfg.IDM.Key, logger); err != nil {
|
||||
logger.Fatal().Err(err).Msgf("Could not generate test-certificate")
|
||||
}
|
||||
servercfg.LDAPSListenAddr = cfg.IDM.LDAPSAddr
|
||||
servercfg.TLSCertFile = cfg.IDM.Cert
|
||||
servercfg.TLSKeyFile = cfg.IDM.Key
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(path.Join(defaults.BaseDataPath(), "idm"), 0700); err != nil {
|
||||
logger.Fatal().Err(err).Msgf("Could not create data directory for idm")
|
||||
}
|
||||
|
||||
if _, err := os.Stat(servercfg.BoltDBFile); errors.Is(err, os.ErrNotExist) {
|
||||
logger.Debug().Msg("Bootstrapping IDM database")
|
||||
if err = bootstrap(logger, cfg, servercfg); err != nil {
|
||||
|
||||
@@ -27,6 +27,7 @@ type Config struct {
|
||||
|
||||
type Settings struct {
|
||||
LDAPSAddr string `yaml:"ldaps_addr" env:"IDM_LDAPS_ADDR" desc:"Listen address for the LDAPS listener (ip-addr:port)." introductionVersion:"1.0.0"`
|
||||
LDAPAddr string
|
||||
Cert string `yaml:"cert" env:"IDM_LDAPS_CERT" desc:"File name of the TLS server certificate for the LDAPS listener. If not defined, the root directory derives from $OC_BASE_DATA_PATH/idm." introductionVersion:"1.0.0"`
|
||||
Key string `yaml:"key" env:"IDM_LDAPS_KEY" desc:"File name for the TLS certificate key for the server certificate. If not defined, the root directory derives from $OC_BASE_DATA_PATH/idm." introductionVersion:"1.0.0"`
|
||||
DatabasePath string `yaml:"database" env:"IDM_DATABASE_PATH" desc:"Full path to the IDM backend database. If not defined, the root directory derives from $OC_BASE_DATA_PATH/idm." introductionVersion:"1.0.0"`
|
||||
|
||||
@@ -30,9 +30,7 @@ func DefaultConfig() *config.Config {
|
||||
CreateDemoUsers: false,
|
||||
DemoUsersIssuerUrl: "https://localhost:9200",
|
||||
IDM: config.Settings{
|
||||
LDAPSAddr: "127.0.0.1:9235",
|
||||
Cert: path.Join(defaults.BaseDataPath(), "idm", "ldap.crt"),
|
||||
Key: path.Join(defaults.BaseDataPath(), "idm", "ldap.key"),
|
||||
LDAPAddr: "127.0.0.1:9236",
|
||||
DatabasePath: path.Join(defaults.BaseDataPath(), "idm", "idm.boltdb"),
|
||||
},
|
||||
}
|
||||
@@ -51,5 +49,5 @@ func EnsureDefaults(cfg *config.Config) {
|
||||
|
||||
// Sanitize sanitizes the configuration
|
||||
func Sanitize(cfg *config.Config) {
|
||||
// nothing to sanitize here
|
||||
// nothing to do yet
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package parser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
occfg "github.com/opencloud-eu/opencloud/pkg/config"
|
||||
"github.com/opencloud-eu/opencloud/pkg/shared"
|
||||
@@ -53,5 +54,25 @@ func Validate(cfg *config.Config) error {
|
||||
return shared.MissingServiceUserPassword(cfg.Service.Name, "REVA")
|
||||
}
|
||||
|
||||
ip, err := net.ResolveTCPAddr("tcp", cfg.IDM.LDAPAddr) // validate the LDAP address if set
|
||||
|
||||
if err != nil {
|
||||
return errors.New("invalid configuration: 'ldap_addr' is not a valid address")
|
||||
}
|
||||
|
||||
if !ip.IP.IsLoopback() {
|
||||
// loopback addresses are allowed to be used with ldap_addr, but not with ldaps_addr, for security reasons
|
||||
return errors.New("invalid configuration: 'ldap_addr' is set but 'ldaps_addr' is not set. For security reasons, the 'ldap_addr' setting is only allowed to be used with loopback addresses. Please set 'ldaps_addr' to a valid address and port to listen for LDAPS connections")
|
||||
}
|
||||
|
||||
if cfg.IDM.LDAPSAddr != "" {
|
||||
if cfg.IDM.Cert == "" {
|
||||
return errors.New("invalid configuration: 'ldaps_addr' is set but 'cert' is not set. Please set 'cert' to a valid path to a TLS certificate")
|
||||
}
|
||||
if cfg.IDM.Key == "" {
|
||||
return errors.New("invalid configuration: 'ldaps_addr' is set but 'key' is not set. Please set 'key' to a valid path to a TLS certificate key")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
auto-install-peers=true
|
||||
public-hoist-pattern[]=*
|
||||
@@ -35,8 +35,8 @@
|
||||
"@types/react-dom": "^17.0.26",
|
||||
"@types/react-redux": "^7.1.34",
|
||||
"@types/redux-logger": "^3.0.13",
|
||||
"axios": "^1.16.0",
|
||||
"i18next": "^26.1.0",
|
||||
"axios": "^1.16.1",
|
||||
"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"
|
||||
}
|
||||
@@ -119,8 +119,7 @@ func DefaultConfig() *config.Config {
|
||||
},
|
||||
},
|
||||
Ldap: config.Ldap{
|
||||
URI: "ldaps://localhost:9235",
|
||||
TLSCACert: filepath.Join(defaults.BaseDataPath(), "idm", "ldap.crt"),
|
||||
URI: "ldap://localhost:9236",
|
||||
BindDN: "uid=idp,ou=sysusers,o=libregraph-idm",
|
||||
BaseDN: "ou=users,o=libregraph-idm",
|
||||
Scope: "sub",
|
||||
|
||||
Generated
+1954
-1416
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,30 @@
|
||||
autoInstallPeers: true
|
||||
|
||||
publicHoistPattern:
|
||||
- "*"
|
||||
|
||||
# Ensure platform-specific optional dependencies (e.g. Rollup's native
|
||||
# binaries pulled in via workbox) are fetched for every architecture we
|
||||
# build/package for, not just the host that runs `pnpm install`. Without
|
||||
# this, cross-arch RPM/OBS builds fail with "Cannot find module
|
||||
# @rollup/rollup-linux-arm64-gnu" on aarch64.
|
||||
supportedArchitectures:
|
||||
os:
|
||||
- current
|
||||
- linux
|
||||
cpu:
|
||||
- x64
|
||||
- arm64
|
||||
libc:
|
||||
- glibc
|
||||
- musl
|
||||
|
||||
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,9 +1,6 @@
|
||||
package defaults
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/pkg/config/defaults"
|
||||
"github.com/opencloud-eu/opencloud/pkg/shared"
|
||||
"github.com/opencloud-eu/opencloud/pkg/structs"
|
||||
"github.com/opencloud-eu/opencloud/services/users/pkg/config"
|
||||
@@ -38,8 +35,7 @@ func DefaultConfig() *config.Config {
|
||||
Driver: "ldap",
|
||||
Drivers: config.Drivers{
|
||||
LDAP: config.LDAPDriver{
|
||||
URI: "ldaps://localhost:9235",
|
||||
CACert: filepath.Join(defaults.BaseDataPath(), "idm", "ldap.crt"),
|
||||
URI: "ldap://localhost:9236",
|
||||
Insecure: false,
|
||||
UserBaseDN: "ou=users,o=libregraph-idm",
|
||||
GroupBaseDN: "ou=groups,o=libregraph-idm",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -30,3 +30,4 @@ _testmain.go
|
||||
cover.html
|
||||
README.html
|
||||
.idea
|
||||
.claude/
|
||||
+5
@@ -53,3 +53,8 @@ linters:
|
||||
- varnamelen
|
||||
- wrapcheck
|
||||
- wsl
|
||||
- gomodguard
|
||||
settings:
|
||||
govet:
|
||||
disable:
|
||||
- inline
|
||||
+7
-2
@@ -51,7 +51,8 @@ Validator returns only InvalidValidationError for bad validation input, nil or V
|
||||
|
||||
```go
|
||||
err := validate.Struct(mystruct)
|
||||
validationErrors := err.(validator.ValidationErrors)
|
||||
var validationErrors validator.ValidationErrors
|
||||
errors.As(err, &validationErrors)
|
||||
```
|
||||
|
||||
Usage and documentation
|
||||
@@ -128,6 +129,7 @@ validate := validator.New(validator.WithRequiredStructEnabled())
|
||||
| url | URL String |
|
||||
| http_url | HTTP(s) URL String |
|
||||
| https_url | HTTPS-only URL String |
|
||||
| origin | Web origin (URL with HTTP(S) scheme and host, but no path/query/fragment) |
|
||||
| url_encoded | URL Encoded |
|
||||
| urn_rfc2141 | Urn RFC 2141 String |
|
||||
|
||||
@@ -169,6 +171,7 @@ validate := validator.New(validator.WithRequiredStructEnabled())
|
||||
| bic_iso_9362_2014 | Business Identifier Code (ISO 9362:2014) |
|
||||
| bic | Business Identifier Code (ISO 9362:2022) |
|
||||
| bcp47_language_tag | Language tag (BCP 47) |
|
||||
| bcp47_strict_language_tag | Language tag (BCP 47), strictly following RFC 5646 |
|
||||
| btc_addr | Bitcoin Address |
|
||||
| btc_addr_bech32 | Bitcoin Bech32 Address (segwit) |
|
||||
| credit_card | Credit Card Number |
|
||||
@@ -222,7 +225,7 @@ validate := validator.New(validator.WithRequiredStructEnabled())
|
||||
| sha384 | SHA384 hash |
|
||||
| sha512 | SHA512 hash |
|
||||
| ripemd128 | RIPEMD-128 hash |
|
||||
| ripemd128 | RIPEMD-160 hash |
|
||||
| ripemd160 | RIPEMD-160 hash |
|
||||
| tiger128 | TIGER128 hash |
|
||||
| tiger160 | TIGER160 hash |
|
||||
| tiger192 | TIGER192 hash |
|
||||
@@ -250,11 +253,13 @@ validate := validator.New(validator.WithRequiredStructEnabled())
|
||||
| file | Existing File |
|
||||
| filepath | File Path |
|
||||
| image | Image |
|
||||
| mimetype | MIME Type |
|
||||
| isdefault | Is Default |
|
||||
| len | Length |
|
||||
| max | Maximum |
|
||||
| min | Minimum |
|
||||
| oneof | One Of |
|
||||
| noneof | None Of |
|
||||
| required | Required |
|
||||
| required_if | Required If |
|
||||
| required_unless | Required Unless |
|
||||
|
||||
+314
-94
@@ -3,7 +3,6 @@ package validator
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"cmp"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
@@ -16,7 +15,9 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -141,6 +142,7 @@ var (
|
||||
"http_url": isHttpURL,
|
||||
"https_url": isHttpsURL,
|
||||
"uri": isURI,
|
||||
"origin": isOrigin,
|
||||
"urn_rfc2141": isUrnRFC2141, // RFC 2141
|
||||
"file": isFile,
|
||||
"filepath": isFilePath,
|
||||
@@ -159,6 +161,7 @@ var (
|
||||
"startsnotwith": startsNotWith,
|
||||
"endsnotwith": endsNotWith,
|
||||
"image": isImage,
|
||||
"mimetype": isMIMEType,
|
||||
"isbn": isISBN,
|
||||
"isbn10": isISBN10,
|
||||
"isbn13": isISBN13,
|
||||
@@ -217,6 +220,8 @@ var (
|
||||
"unique": isUnique,
|
||||
"oneof": isOneOf,
|
||||
"oneofci": isOneOfCI,
|
||||
"noneof": isNoneOf,
|
||||
"noneofci": isNoneOfCI,
|
||||
"html": isHTML,
|
||||
"html_encoded": isHTMLEncoded,
|
||||
"url_encoded": isURLEncoded,
|
||||
@@ -240,6 +245,7 @@ var (
|
||||
"iso4217": isIso4217,
|
||||
"iso4217_numeric": isIso4217Numeric,
|
||||
"bcp47_language_tag": isBCP47LanguageTag,
|
||||
"bcp47_strict_language_tag": isBCP47StrictLanguageTag,
|
||||
"postcode_iso3166_alpha2": isPostcodeByIso3166Alpha2,
|
||||
"postcode_iso3166_alpha2_field": isPostcodeByIso3166Alpha2Field,
|
||||
"bic_iso_9362_2014": isIsoBic2014Format,
|
||||
@@ -261,6 +267,33 @@ var (
|
||||
var (
|
||||
oneofValsCache = map[string][]string{}
|
||||
oneofValsCacheRWLock = sync.RWMutex{}
|
||||
|
||||
// BCP47 language tag
|
||||
// according to https://www.rfc-editor.org/rfc/bcp/bcp47.txt
|
||||
bcp47LanguageTagRe = regexp.MustCompile(strings.Join([]string{
|
||||
// group 1:
|
||||
`^(`,
|
||||
// irregular
|
||||
`en-gb-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|`,
|
||||
`sgn-be-fr|sgn-be-nl|sgn-ch-de|`,
|
||||
// regular
|
||||
`art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang|`,
|
||||
// privateuse
|
||||
`x-[a-z0-9]{1,8}`,
|
||||
`)$`,
|
||||
|
||||
`|`,
|
||||
|
||||
// langtag
|
||||
`^`,
|
||||
`((?:[a-z]{2,3}(?:-[a-z]{3}){0,3})|[a-z]{4}|[a-z]{5,8})`, // group 2: language
|
||||
`(?:-([a-z]{4}))?`, // group 3: script
|
||||
`(?:-([a-z]{2}|[0-9]{3}))?`, // group 4: region
|
||||
`(?:-((?:[a-z0-9]{5,8}|[0-9][a-z0-9]{3})(?:-(?:[a-z0-9]{5,8}|[0-9][a-z0-9]{3}))*))?`, // group 5: variant
|
||||
`(?:-((?:[a-wyz0-9](?:-[a-z0-9]{2,8})+)(?:-(?:[a-wyz0-9](?:-[a-z0-9]{2,8})+))*))?`, // group 6: extension
|
||||
`(?:-x(?:-[a-z0-9]{1,8})+)?`,
|
||||
`$`,
|
||||
}, ""))
|
||||
)
|
||||
|
||||
func parseOneOfParam2(s string) []string {
|
||||
@@ -307,12 +340,8 @@ func isOneOf(fl FieldLevel) bool {
|
||||
default:
|
||||
panic(fmt.Sprintf("Bad field type %s", field.Type()))
|
||||
}
|
||||
for i := 0; i < len(vals); i++ {
|
||||
if vals[i] == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
return slices.Contains(vals, v)
|
||||
}
|
||||
|
||||
// isOneOfCI is the validation function for validating if the current field's value is one of the provided string values (case insensitive).
|
||||
@@ -323,13 +352,20 @@ func isOneOfCI(fl FieldLevel) bool {
|
||||
if field.Kind() != reflect.String {
|
||||
panic(fmt.Sprintf("Bad field type %s", field.Type()))
|
||||
}
|
||||
v := field.String()
|
||||
for _, val := range vals {
|
||||
if strings.EqualFold(val, v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
return slices.ContainsFunc(vals, func(val string) bool {
|
||||
return strings.EqualFold(val, field.String())
|
||||
})
|
||||
}
|
||||
|
||||
// isNoneOf validates that the current field's value is not one of the provided string or integer values
|
||||
func isNoneOf(fl FieldLevel) bool {
|
||||
return !isOneOf(fl)
|
||||
}
|
||||
|
||||
// isNoneOfCI validates that the current field's value is not one of the provided string values (case insensitive)
|
||||
func isNoneOfCI(fl FieldLevel) bool {
|
||||
return !isOneOfCI(fl)
|
||||
}
|
||||
|
||||
// isUnique is the validation function for validating if each array|slice|map value is unique
|
||||
@@ -1526,6 +1562,64 @@ func isURI(fl FieldLevel) bool {
|
||||
panic(fmt.Sprintf("Bad field type %s", field.Type()))
|
||||
}
|
||||
|
||||
// isOrigin checks if a field value is a valid web origin URL with HTTP(S) scheme and host defined, but no path, query, or fragment.
|
||||
func isOrigin(fl FieldLevel) bool {
|
||||
field := fl.Field()
|
||||
|
||||
if field.Kind() == reflect.String {
|
||||
s := field.String()
|
||||
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Fragments with empty content ("#") are not detectable after parse and
|
||||
// u.Fragment will be empty even if # is present in the URL.
|
||||
if strings.Contains(s, "#") {
|
||||
return false
|
||||
}
|
||||
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if u.Scheme != "http" && u.Scheme != "https" {
|
||||
return false
|
||||
}
|
||||
|
||||
if u.Path != "" || u.RawQuery != "" || u.ForceQuery || u.Fragment != "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if u.User != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
hostname := u.Hostname()
|
||||
if hostname == "" {
|
||||
return false
|
||||
}
|
||||
if net.ParseIP(hostname) == nil && !hostnameRegexRFC1123().MatchString(hostname) {
|
||||
return false
|
||||
}
|
||||
|
||||
portStr := u.Port()
|
||||
if portStr != "" {
|
||||
// Port 0 is reserved (RFC 6335) and has no valid use as an origin port.
|
||||
// https://www.rfc-editor.org/rfc/rfc6335.html#section-6
|
||||
port, portErr := strconv.ParseUint(portStr, 10, 16)
|
||||
if portErr != nil || port == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("Bad field type %s", field.Type()))
|
||||
}
|
||||
|
||||
// isURL is the validation function for validating if the current field's value is a valid URL.
|
||||
func isURL(fl FieldLevel) bool {
|
||||
field := fl.Field()
|
||||
@@ -1635,6 +1729,63 @@ func isFile(fl FieldLevel) bool {
|
||||
panic(fmt.Sprintf("Bad field type %s", field.Type()))
|
||||
}
|
||||
|
||||
func detectFileMIMEType(field reflect.Value) (string, bool) {
|
||||
switch field.Kind() {
|
||||
case reflect.String:
|
||||
filePath := field.String()
|
||||
fileInfo, err := os.Stat(filePath)
|
||||
if err != nil || fileInfo.IsDir() {
|
||||
return "", false
|
||||
}
|
||||
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
|
||||
mime, err := mimetype.DetectReader(file)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return mime.String(), true
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("Bad field type %s", field.Type()))
|
||||
}
|
||||
|
||||
func matchesMIMEType(mime, expected string) bool {
|
||||
expectedType, expectedSubtype, ok := strings.Cut(strings.TrimSpace(expected), "/")
|
||||
if !ok || expectedType == "" || expectedSubtype == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
mimeType, mimeSubtype, ok := strings.Cut(mime, "/")
|
||||
if !ok || mimeType == "" || mimeSubtype == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if expectedSubtype == "*" {
|
||||
return mimeType == expectedType
|
||||
}
|
||||
|
||||
return mimeType == expectedType && mimeSubtype == expectedSubtype
|
||||
}
|
||||
|
||||
// isMIMEType is the validation function for validating if the current field's value contains the path to a file
|
||||
// whose detected MIME type matches the provided validator param in the form type/subtype or type/*.
|
||||
func isMIMEType(fl FieldLevel) bool {
|
||||
mime, ok := detectFileMIMEType(fl.Field())
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return matchesMIMEType(mime, fl.Param())
|
||||
}
|
||||
|
||||
// isImage is the validation function for validating if the current field's value contains the path to a valid image file
|
||||
func isImage(fl FieldLevel) bool {
|
||||
mimetypes := map[string]bool{
|
||||
@@ -1663,39 +1814,14 @@ func isImage(fl FieldLevel) bool {
|
||||
"image/x-xpixmap": true,
|
||||
"image/x-xwindowdump": true,
|
||||
}
|
||||
field := fl.Field()
|
||||
|
||||
switch field.Kind() {
|
||||
case reflect.String:
|
||||
filePath := field.String()
|
||||
fileInfo, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if fileInfo.IsDir() {
|
||||
return false
|
||||
}
|
||||
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
|
||||
mime, err := mimetype.DetectReader(file)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if _, ok := mimetypes[mime.String()]; ok {
|
||||
return true
|
||||
}
|
||||
mime, ok := detectFileMIMEType(fl.Field())
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("Bad field type %s", field.Type()))
|
||||
_, ok = mimetypes[mime]
|
||||
return ok
|
||||
}
|
||||
|
||||
// isFilePath is the validation function for validating if the current field's value is a valid file path.
|
||||
@@ -3081,6 +3207,150 @@ func isBCP47LanguageTag(fl FieldLevel) bool {
|
||||
panic(fmt.Sprintf("Bad field type %s", field.Type()))
|
||||
}
|
||||
|
||||
// isBCP47StrictLanguageTag is the validation function for validating if the current field's value is a valid BCP 47 language tag
|
||||
// according to https://www.rfc-editor.org/rfc/bcp/bcp47.txt
|
||||
func isBCP47StrictLanguageTag(fl FieldLevel) bool {
|
||||
field := fl.Field()
|
||||
|
||||
if field.Kind() != reflect.String {
|
||||
panic(fmt.Sprintf("Bad field type %s", field.Type()))
|
||||
}
|
||||
|
||||
lower := strings.ToLower(field.String())
|
||||
lowerTagDash := lower + "-"
|
||||
|
||||
m := bcp47LanguageTagRe.FindStringSubmatch(lower)
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
grandfatheredOrPrivateuse := m[1]
|
||||
lang := m[2]
|
||||
script := m[3]
|
||||
region := m[4]
|
||||
variant := m[5]
|
||||
extension := m[6]
|
||||
|
||||
if grandfatheredOrPrivateuse != "" {
|
||||
return true
|
||||
}
|
||||
|
||||
// language = 2*3ALPHA ; shortest ISO 639 code
|
||||
// ["-" extlang] ; sometimes followed by
|
||||
// ; extended language subtags
|
||||
// / 4ALPHA ; or reserved for future use
|
||||
// / 5*8ALPHA ; or registered language subtag
|
||||
switch n := len(lang); {
|
||||
// 2*3ALPHA "-" extlang
|
||||
case strings.Contains(lang, "-"):
|
||||
parts := strings.Split(lang, "-")
|
||||
|
||||
baseLang := parts[0]
|
||||
base, err := language.ParseBase(baseLang)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
// base.String() normalizes the base to the shortest code
|
||||
// for the language
|
||||
if base.String() != baseLang {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, e := range parts[1:] {
|
||||
prefixes, ok := iana_subtag_registry_extlangs[e]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(prefixes) > 0 {
|
||||
found := false
|
||||
for _, p := range prefixes {
|
||||
if strings.HasPrefix(lowerTagDash, p) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2*3ALPHA ; shortest ISO 639 code
|
||||
case n <= 3:
|
||||
base, err := language.ParseBase(lang)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// base.String() normalizes the base to the shortest code
|
||||
// for the language
|
||||
if base.String() != lang {
|
||||
return false
|
||||
}
|
||||
// 4ALPHA ; or reserved for future use
|
||||
case n == 4:
|
||||
return false
|
||||
// 5*8ALPHA ; or registered language subtag
|
||||
default:
|
||||
// registered language subtag with 5+ characters.
|
||||
// As of today there aren't any.
|
||||
// https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
|
||||
return false
|
||||
}
|
||||
|
||||
// script = 4ALPHA ; ISO 15924 code
|
||||
if script != "" {
|
||||
_, err := language.ParseScript(script)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// region = 2ALPHA ; ISO 3166-1 code
|
||||
// 3DIGIT ; UN M.49 code
|
||||
if region != "" {
|
||||
if len(region) == 2 {
|
||||
_, err := language.ParseRegion(region)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
// Can't use language.ParseRegion() here because not all
|
||||
// UN M.49 region codes are allowed, just the subset present
|
||||
// in the IANA subtag registry.
|
||||
_, ok := iana_subtag_registry_m49_codes[region]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// variant = 5*8alphanum ; registered variants
|
||||
// / (DIGIT 3alphanum)
|
||||
if variant != "" {
|
||||
for v := range strings.SplitSeq(variant, "-") {
|
||||
_, err := language.ParseVariant(v)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok := iana_subtag_registry_variants[v]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if extension != "" {
|
||||
_, err := language.ParseExtension(extension)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// isIsoBic2014Format is the validation function for validating if the current field's value is a valid Business Identifier Code (SWIFT code), defined in ISO 9362 2014
|
||||
func isIsoBic2014Format(fl FieldLevel) bool {
|
||||
bicString := fl.Field().String()
|
||||
@@ -3235,59 +3505,9 @@ func isEIN(fl FieldLevel) bool {
|
||||
return einRegex().MatchString(field.String())
|
||||
}
|
||||
|
||||
func isValidateFn(fl FieldLevel) bool {
|
||||
const defaultParam = `Validate`
|
||||
|
||||
field := fl.Field()
|
||||
validateFn := cmp.Or(fl.Param(), defaultParam)
|
||||
|
||||
ok, err := tryCallValidateFn(field, validateFn)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
var (
|
||||
errMethodNotFound = errors.New(`method not found`)
|
||||
errMethodReturnNoValues = errors.New(`method return o values (void)`)
|
||||
errMethodReturnInvalidType = errors.New(`method should return invalid type`)
|
||||
)
|
||||
|
||||
func tryCallValidateFn(field reflect.Value, validateFn string) (bool, error) {
|
||||
method := field.MethodByName(validateFn)
|
||||
if field.CanAddr() && !method.IsValid() {
|
||||
method = field.Addr().MethodByName(validateFn)
|
||||
}
|
||||
|
||||
if !method.IsValid() {
|
||||
return false, fmt.Errorf("unable to call %q on type %q: %w",
|
||||
validateFn, field.Type().String(), errMethodNotFound)
|
||||
}
|
||||
|
||||
returnValues := method.Call([]reflect.Value{})
|
||||
if len(returnValues) == 0 {
|
||||
return false, fmt.Errorf("unable to use result of method %q on type %q: %w",
|
||||
validateFn, field.Type().String(), errMethodReturnNoValues)
|
||||
}
|
||||
|
||||
firstReturnValue := returnValues[0]
|
||||
|
||||
switch firstReturnValue.Kind() {
|
||||
case reflect.Bool:
|
||||
return firstReturnValue.Bool(), nil
|
||||
case reflect.Interface:
|
||||
errorType := reflect.TypeOf((*error)(nil)).Elem()
|
||||
|
||||
if firstReturnValue.Type().Implements(errorType) {
|
||||
return firstReturnValue.IsNil(), nil
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("unable to use result of method %q on type %q: %w (got interface %v expect error)",
|
||||
validateFn, field.Type().String(), errMethodReturnInvalidType, firstReturnValue.Type().String())
|
||||
default:
|
||||
return false, fmt.Errorf("unable to use result of method %q on type %q: %w (got %v expect error or bool)",
|
||||
validateFn, field.Type().String(), errMethodReturnInvalidType, firstReturnValue.Type().String())
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -144,7 +144,7 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
|
||||
|
||||
if v.hasTagNameFunc {
|
||||
name := v.tagNameFunc(fld)
|
||||
if len(name) > 0 {
|
||||
if len(name) > 0 || v.omitBlankFieldNames {
|
||||
customName = name
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -1175,3 +1175,15 @@ var iso3166_2 = map[string]struct{}{
|
||||
"ZW-BU": {}, "ZW-HA": {}, "ZW-MA": {}, "ZW-MC": {}, "ZW-ME": {},
|
||||
"ZW-MI": {}, "ZW-MN": {}, "ZW-MS": {}, "ZW-MV": {}, "ZW-MW": {},
|
||||
}
|
||||
|
||||
// Subset of UN M.49 region codes present in the IANA Language Subtag Registry:
|
||||
// https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
|
||||
var iana_subtag_registry_m49_codes = map[string]struct{}{
|
||||
"001": {}, "002": {}, "003": {}, "005": {}, "009": {},
|
||||
"011": {}, "013": {}, "014": {}, "015": {}, "017": {},
|
||||
"018": {}, "019": {}, "021": {}, "029": {}, "030": {},
|
||||
"034": {}, "035": {}, "039": {}, "053": {}, "054": {},
|
||||
"057": {}, "061": {}, "142": {}, "143": {}, "145": {},
|
||||
"150": {}, "151": {}, "154": {}, "155": {}, "202": {},
|
||||
"419": {},
|
||||
}
|
||||
+42
@@ -184,6 +184,12 @@ so the above will become excludesall=0x7C
|
||||
Field `validate:"excludesall=0x7C"` // GOOD! Use the UTF-8 hex representation.
|
||||
}
|
||||
|
||||
# Build tags
|
||||
|
||||
The library provides a build tag for build size optimizations. If you are not using
|
||||
`validateFn` you can add the `validator_novalidatefn` build tag to enabled better dead
|
||||
code elimination. With this build tag, any usage of `validateFn` tags will panic.
|
||||
|
||||
# Baked In Validators and Tags
|
||||
|
||||
Here is a list of the current built in validators:
|
||||
@@ -546,6 +552,24 @@ Works the same as oneof but is case insensitive and therefore only accepts strin
|
||||
Usage: oneofci=red green
|
||||
oneofci='red green' 'blue yellow'
|
||||
|
||||
# None Of
|
||||
|
||||
For strings, ints, and uints, noneof will ensure that the value is not one of
|
||||
the values in the parameter. The parameter should be a list of values separated by whitespace.
|
||||
Values may be strings or numbers. To inversely match strings with spaces in them, include the target string between single quotes.
|
||||
Kind of like an 'enum'.
|
||||
|
||||
Usage: noneof=red green
|
||||
noneof='red green' 'blue yellow'
|
||||
noneof=5 7 9
|
||||
|
||||
|
||||
# None Of Case Insensitive
|
||||
Works the same as noneof but is case insensitive and therefore only accepts strings.
|
||||
|
||||
Usage: noneofci=red green
|
||||
noneofci='red green' 'blue yellow'
|
||||
|
||||
# Greater Than
|
||||
|
||||
For numbers, this will ensure that the value is greater than the
|
||||
@@ -969,6 +993,16 @@ This is done using os.Stat and github.com/gabriel-vasile/mimetype
|
||||
|
||||
Usage: image
|
||||
|
||||
# MIME type path
|
||||
|
||||
This validates that a string value contains a valid file path and that
|
||||
the file exists on the machine and matches the provided MIME type in the
|
||||
form type/subtype or type/*.
|
||||
This is done using os.Stat and github.com/gabriel-vasile/mimetype
|
||||
|
||||
Usage: mimetype=image/png
|
||||
Usage: mimetype=image/*
|
||||
|
||||
# File Path
|
||||
|
||||
This validates that a string value contains a valid file path but does not
|
||||
@@ -1442,6 +1476,14 @@ More information on https://pkg.go.dev/golang.org/x/text/language
|
||||
|
||||
Usage: bcp47_language_tag
|
||||
|
||||
# BCP 47 Strict Language Tag
|
||||
|
||||
This validates that a string value is a valid BCP 47 language tag strictly following RFC 5646 rules,
|
||||
unlike language.Parse which also accepts Unicode extensions.
|
||||
see https://www.rfc-editor.org/rfc/bcp/bcp47.txt
|
||||
|
||||
Usage: bcp47_strict_language_tag
|
||||
|
||||
BIC (SWIFT code - 2022 standard)
|
||||
|
||||
This validates that a string value is a valid Business Identifier Code (SWIFT code), defined in ISO 9362:2022.
|
||||
|
||||
+14
@@ -24,3 +24,17 @@ func WithPrivateFieldValidation() Option {
|
||||
v.privateFieldValidation = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithTagNameFuncBlankOmit makes a blank return from a RegisterTagNameFunc omit
|
||||
// the field from the error namespace instead of silently falling back to the
|
||||
// struct field name.
|
||||
//
|
||||
// This was made opt-in behaviour to maintain backward compatibility with
|
||||
// existing callers that rely on the fallback for error namespaces.
|
||||
//
|
||||
// It is recommended you enable this as it will be the default behaviour in v11+.
|
||||
func WithTagNameFuncBlankOmit() Option {
|
||||
return func(v *Validate) {
|
||||
v.omitBlankFieldNames = true
|
||||
}
|
||||
}
|
||||
+7
-7
@@ -33,7 +33,7 @@ const (
|
||||
uUID3RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
||||
uUID4RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||
uUID5RegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||
uUIDRegexString = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
||||
uUIDRegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
|
||||
uUID3RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-3[0-9a-fA-F]{3}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
|
||||
uUID4RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
|
||||
uUID5RFC4122RegexString = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-5[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$"
|
||||
@@ -56,12 +56,12 @@ const (
|
||||
latitudeRegexString = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
|
||||
longitudeRegexString = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
|
||||
sSNRegexString = `^[0-9]{3}[ -]?(0[1-9]|[1-9][0-9])[ -]?([1-9][0-9]{3}|[0-9][1-9][0-9]{2}|[0-9]{2}[1-9][0-9]|[0-9]{3}[1-9])$`
|
||||
hostnameRegexStringRFC952 = `^[a-zA-Z]([a-zA-Z0-9\-]+[\.]?)*[a-zA-Z0-9]$` // https://tools.ietf.org/html/rfc952
|
||||
hostnameRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62}){1}(\.[a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})*?$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123
|
||||
hostnameRegexStringRFC952 = `^[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$` // https://tools.ietf.org/html/rfc952
|
||||
hostnameRegexStringRFC1123 = `^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$` // accepts hostname starting with a digit https://tools.ietf.org/html/rfc1123
|
||||
fqdnRegexStringRFC1123 = `^([a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})(\.[a-zA-Z0-9]{1}[a-zA-Z0-9-]{0,62})*?(\.[a-zA-Z]{1}[a-zA-Z0-9-]{0,62})\.?$` // same as hostnameRegexStringRFC1123 but must contain a non numerical TLD (possibly ending with '.')
|
||||
btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address
|
||||
btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
|
||||
btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
|
||||
btcAddressRegexString = `^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$` // bitcoin address
|
||||
btcAddressUpperRegexStringBech32 = `^BC1[02-9AC-HJ-NP-Z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
|
||||
btcAddressLowerRegexStringBech32 = `^bc1[02-9ac-hj-np-z]{7,76}$` // bitcoin bech32 address https://en.bitcoin.it/wiki/Bech32
|
||||
ethAddressRegexString = `^0x[0-9a-fA-F]{40}$`
|
||||
ethAddressUpperRegexString = `^0x[0-9A-F]{40}$`
|
||||
ethAddressLowerRegexString = `^0x[0-9a-f]{40}$`
|
||||
@@ -77,7 +77,7 @@ const (
|
||||
cveRegexString = `^CVE-(1999|2\d{3})-(0[^0]\d{2}|0\d[^0]\d{1}|0\d{2}[^0]|[1-9]{1}\d{3,})$` // CVE Format Id https://cve.mitre.org/cve/identifiers/syntaxchange.html
|
||||
mongodbIdRegexString = "^[a-f\\d]{24}$"
|
||||
mongodbConnStringRegexString = "^mongodb(\\+srv)?:\\/\\/(([a-zA-Z\\d]+):([a-zA-Z\\d$:\\/?#\\[\\]@]+)@)?(([a-z\\d.-]+)(:[\\d]+)?)((,(([a-z\\d.-]+)(:(\\d+))?))*)?(\\/[a-zA-Z-_]{1,64})?(\\?(([a-zA-Z]+)=([a-zA-Z\\d]+))(&(([a-zA-Z\\d]+)=([a-zA-Z\\d]+))?)*)?$"
|
||||
cronRegexString = `(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|((((\d+,)+\d+|((\*|\d+)(\/|-)\d+)|\d+|\*) ?){5,7})`
|
||||
cronRegexString = `^((@(annually|yearly|monthly|weekly|daily|hourly|reboot))|(@every (\d+(ns|us|µs|ms|s|m|h))+)|(([A-Za-z0-9*?][A-Za-z0-9*?/,#L-]+|[*?0-9])( +([A-Za-z0-9*?][A-Za-z0-9*?/,#L-]+|[*?0-9])){4,6}))$`
|
||||
spicedbIDRegexString = `^(([a-zA-Z0-9/_|\-=+]{1,})|\*)$`
|
||||
spicedbPermissionRegexString = "^([a-z][a-z0-9_]{1,62}[a-z0-9])?$"
|
||||
spicedbTypeRegexString = "^([a-z][a-z0-9_]{1,61}[a-z0-9]/)?[a-z][a-z0-9_]{1,62}[a-z0-9]$"
|
||||
|
||||
+15
@@ -1466,6 +1466,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
|
||||
return t
|
||||
},
|
||||
},
|
||||
{
|
||||
tag: "timezone",
|
||||
translation: "{0} must be a valid time zone",
|
||||
override: false,
|
||||
},
|
||||
{
|
||||
tag: "postcode_iso3166_alpha2",
|
||||
translation: "{0} does not match postcode format of {1} country",
|
||||
@@ -1504,11 +1509,21 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
|
||||
translation: "{0} must be a valid image",
|
||||
override: false,
|
||||
},
|
||||
{
|
||||
tag: "mimetype",
|
||||
translation: "{0} must be a valid MIME type",
|
||||
override: false,
|
||||
},
|
||||
{
|
||||
tag: "cve",
|
||||
translation: "{0} must be a valid cve identifier",
|
||||
override: false,
|
||||
},
|
||||
{
|
||||
tag: "bcp47_strict_language_tag",
|
||||
translation: "{0} must be a valid BCP 47 language tag",
|
||||
override: false,
|
||||
},
|
||||
{
|
||||
tag: "validateFn",
|
||||
translation: "{0} must be a valid object",
|
||||
|
||||
+20
-6
@@ -116,7 +116,7 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
|
||||
|
||||
if ct.hasTag {
|
||||
if kind == reflect.Invalid {
|
||||
v.str1 = string(append(ns, cf.altName...))
|
||||
v.str1 = appendAltName(ns, cf.altName)
|
||||
if v.v.hasTagNameFunc {
|
||||
v.str2 = string(append(structNs, cf.name...))
|
||||
} else {
|
||||
@@ -138,7 +138,7 @@ func (v *validate) traverseField(ctx context.Context, parent reflect.Value, curr
|
||||
return
|
||||
}
|
||||
|
||||
v.str1 = string(append(ns, cf.altName...))
|
||||
v.str1 = appendAltName(ns, cf.altName)
|
||||
if v.v.hasTagNameFunc {
|
||||
v.str2 = string(append(structNs, cf.name...))
|
||||
} else {
|
||||
@@ -192,7 +192,9 @@ OUTER:
|
||||
// VarWithField - this allows for validating against each field within the struct against a specific value
|
||||
// pretty handy in certain situations
|
||||
if len(cf.name) > 0 {
|
||||
ns = append(append(ns, cf.altName...), '.')
|
||||
if len(cf.altName) > 0 {
|
||||
ns = append(append(ns, cf.altName...), '.')
|
||||
}
|
||||
structNs = append(append(structNs, cf.name...), '.')
|
||||
}
|
||||
|
||||
@@ -212,7 +214,9 @@ OUTER:
|
||||
// VarWithField - this allows for validating against each field within the struct against a specific value
|
||||
// pretty handy in certain situations
|
||||
if len(cf.name) > 0 {
|
||||
ns = append(append(ns, cf.altName...), '.')
|
||||
if len(cf.altName) > 0 {
|
||||
ns = append(append(ns, cf.altName...), '.')
|
||||
}
|
||||
structNs = append(append(structNs, cf.name...), '.')
|
||||
}
|
||||
|
||||
@@ -409,7 +413,7 @@ OUTER:
|
||||
|
||||
if ct.isBlockEnd || ct.next == nil {
|
||||
// if we get here, no valid 'or' value and no more tags
|
||||
v.str1 = string(append(ns, cf.altName...))
|
||||
v.str1 = appendAltName(ns, cf.altName)
|
||||
|
||||
if v.v.hasTagNameFunc {
|
||||
v.str2 = string(append(structNs, cf.name...))
|
||||
@@ -468,7 +472,7 @@ OUTER:
|
||||
v.ct = ct
|
||||
|
||||
if !ct.fn(ctx, v) {
|
||||
v.str1 = string(append(ns, cf.altName...))
|
||||
v.str1 = appendAltName(ns, cf.altName)
|
||||
|
||||
if v.v.hasTagNameFunc {
|
||||
v.str2 = string(append(structNs, cf.name...))
|
||||
@@ -499,6 +503,16 @@ OUTER:
|
||||
}
|
||||
}
|
||||
|
||||
func appendAltName(ns []byte, altName string) string {
|
||||
if len(altName) > 0 {
|
||||
return string(append(ns, altName...))
|
||||
}
|
||||
if n := len(ns); n > 0 && ns[n-1] == '.' {
|
||||
return string(ns[:n-1])
|
||||
}
|
||||
return string(ns)
|
||||
}
|
||||
|
||||
func getValue(val reflect.Value) interface{} {
|
||||
if val.CanInterface() {
|
||||
return val.Interface()
|
||||
|
||||
+6
-5
@@ -68,7 +68,7 @@ type FilterFunc func(ns []byte) bool
|
||||
|
||||
// CustomTypeFunc allows for overriding or adding custom field type handler functions
|
||||
// field = field value of the type to return a value to be validated
|
||||
// example Valuer from sql drive see https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29
|
||||
// example Valuer from sql driver see https://golang.org/src/database/sql/driver/types.go?s=1210:1293#L29
|
||||
type CustomTypeFunc func(field reflect.Value) interface{}
|
||||
|
||||
// TagNameFunc allows for adding of a custom tag name parser
|
||||
@@ -96,6 +96,7 @@ type Validate struct {
|
||||
hasTagNameFunc bool
|
||||
requiredStructEnabled bool
|
||||
privateFieldValidation bool
|
||||
omitBlankFieldNames bool
|
||||
}
|
||||
|
||||
// New returns a new instance of 'validate' with sane defaults.
|
||||
@@ -428,7 +429,7 @@ func (v *Validate) StructFilteredCtx(ctx context.Context, s interface{}, fn Filt
|
||||
}
|
||||
|
||||
// StructPartial validates the fields passed in only, ignoring all others.
|
||||
// Fields may be provided in a namespaced fashion relative to the struct provided
|
||||
// Fields may be provided in a namespaced fashion relative to the struct provided
|
||||
// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
|
||||
//
|
||||
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
|
||||
@@ -439,7 +440,7 @@ func (v *Validate) StructPartial(s interface{}, fields ...string) error {
|
||||
|
||||
// StructPartialCtx validates the fields passed in only, ignoring all others and allows passing of contextual
|
||||
// validation information via context.Context
|
||||
// Fields may be provided in a namespaced fashion relative to the struct provided
|
||||
// Fields may be provided in a namespaced fashion relative to the struct provided
|
||||
// eg. NestedStruct.Field or NestedArrayField[0].Struct.Name
|
||||
//
|
||||
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
|
||||
@@ -514,7 +515,7 @@ func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields .
|
||||
}
|
||||
|
||||
// StructExcept validates all fields except the ones passed in.
|
||||
// Fields may be provided in a namespaced fashion relative to the struct provided
|
||||
// Fields may be provided in a namespaced fashion relative to the struct provided
|
||||
// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name
|
||||
//
|
||||
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
|
||||
@@ -525,7 +526,7 @@ func (v *Validate) StructExcept(s interface{}, fields ...string) error {
|
||||
|
||||
// StructExceptCtx validates all fields except the ones passed in and allows passing of contextual
|
||||
// validation information via context.Context
|
||||
// Fields may be provided in a namespaced fashion relative to the struct provided
|
||||
// Fields may be provided in a namespaced fashion relative to the struct provided
|
||||
// i.e. NestedStruct.Field or NestedArrayField[0].Struct.Name
|
||||
//
|
||||
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
|
||||
|
||||
+3
@@ -19,3 +19,6 @@ indent_size = 2
|
||||
|
||||
[.golangci.yaml]
|
||||
indent_size = 2
|
||||
|
||||
[devenv.yaml]
|
||||
indent_size = 2
|
||||
+7
-3
@@ -1,6 +1,10 @@
|
||||
/.devenv/
|
||||
/.direnv/
|
||||
/.pre-commit-config.yaml
|
||||
/bin/
|
||||
/build/
|
||||
/var/
|
||||
|
||||
# Devenv
|
||||
.devenv*
|
||||
devenv.local.nix
|
||||
devenv.local.yaml
|
||||
.direnv
|
||||
.pre-commit-config.yaml
|
||||
-294
@@ -1,294 +0,0 @@
|
||||
{
|
||||
"nodes": {
|
||||
"cachix": {
|
||||
"inputs": {
|
||||
"devenv": [
|
||||
"devenv"
|
||||
],
|
||||
"flake-compat": [
|
||||
"devenv"
|
||||
],
|
||||
"git-hooks": [
|
||||
"devenv"
|
||||
],
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1742042642,
|
||||
"narHash": "sha256-D0gP8srrX0qj+wNYNPdtVJsQuFzIng3q43thnHXQ/es=",
|
||||
"owner": "cachix",
|
||||
"repo": "cachix",
|
||||
"rev": "a624d3eaf4b1d225f918de8543ed739f2f574203",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"ref": "latest",
|
||||
"repo": "cachix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"devenv": {
|
||||
"inputs": {
|
||||
"cachix": "cachix",
|
||||
"flake-compat": "flake-compat",
|
||||
"git-hooks": "git-hooks",
|
||||
"nix": "nix",
|
||||
"nixpkgs": "nixpkgs_3"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1744876578,
|
||||
"narHash": "sha256-8MTBj2REB8t29sIBLpxbR0+AEGJ7f+RkzZPAGsFd40c=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "7ff7c351bba20d0615be25ecdcbcf79b57b85fe1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1733328505,
|
||||
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": [
|
||||
"devenv",
|
||||
"nix",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1712014858,
|
||||
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts_2": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1743550720,
|
||||
"narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "c621e8422220273271f52058f618c94e405bb0f5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"git-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"devenv"
|
||||
],
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1742649964,
|
||||
"narHash": "sha256-DwOTp7nvfi8mRfuL1escHDXabVXFGT1VlPD1JHrtrco=",
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "dcf5072734cb576d2b0c59b2ac44f5050b5eac82",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"git-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1709087332,
|
||||
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"libgit2": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1697646580,
|
||||
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
|
||||
"owner": "libgit2",
|
||||
"repo": "libgit2",
|
||||
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "libgit2",
|
||||
"repo": "libgit2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"devenv"
|
||||
],
|
||||
"flake-parts": "flake-parts",
|
||||
"libgit2": "libgit2",
|
||||
"nixpkgs": "nixpkgs_2",
|
||||
"nixpkgs-23-11": [
|
||||
"devenv"
|
||||
],
|
||||
"nixpkgs-regression": [
|
||||
"devenv"
|
||||
],
|
||||
"pre-commit-hooks": [
|
||||
"devenv"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1741798497,
|
||||
"narHash": "sha256-E3j+3MoY8Y96mG1dUIiLFm2tZmNbRvSiyN7CrSKuAVg=",
|
||||
"owner": "domenkozar",
|
||||
"repo": "nix",
|
||||
"rev": "f3f44b2baaf6c4c6e179de8cbb1cc6db031083cd",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "domenkozar",
|
||||
"ref": "devenv-2.24",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1733212471,
|
||||
"narHash": "sha256-M1+uCoV5igihRfcUKrr1riygbe73/dzNnzPsmaLCmpo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "55d15ad12a74eb7d4646254e13638ad0c4128776",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1743296961,
|
||||
"narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nixpkgs.lib",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1717432640,
|
||||
"narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "88269ab3044128b7c2f4c7d68448b2fb50456870",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "release-24.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1733477122,
|
||||
"narHash": "sha256-qamMCz5mNpQmgBwc8SB5tVMlD5sbwVIToVZtSxMph9s=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"rev": "7bd9e84d0452f6d2e63b6e6da29fe73fac951857",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"ref": "rolling",
|
||||
"repo": "devenv-nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_4": {
|
||||
"locked": {
|
||||
"lastModified": 1744536153,
|
||||
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"devenv": "devenv",
|
||||
"flake-parts": "flake-parts_2",
|
||||
"nixpkgs": "nixpkgs_4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
devenv.url = "github:cachix/devenv";
|
||||
};
|
||||
|
||||
outputs =
|
||||
inputs@{ flake-parts, ... }:
|
||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
imports = [
|
||||
inputs.devenv.flakeModule
|
||||
];
|
||||
|
||||
systems = [
|
||||
"x86_64-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
];
|
||||
|
||||
perSystem =
|
||||
{ pkgs, ... }:
|
||||
rec {
|
||||
devenv.shells = {
|
||||
default = {
|
||||
languages = {
|
||||
go.enable = true;
|
||||
};
|
||||
|
||||
pre-commit.hooks = {
|
||||
nixpkgs-fmt.enable = true;
|
||||
};
|
||||
|
||||
packages = with pkgs; [
|
||||
golangci-lint
|
||||
];
|
||||
|
||||
# https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
|
||||
containers = pkgs.lib.mkForce { };
|
||||
};
|
||||
|
||||
ci = devenv.shells.default;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
+284
-48
@@ -173,6 +173,25 @@
|
||||
// Public: "I made it through!"
|
||||
// }
|
||||
//
|
||||
// # Custom Decoding with Unmarshaler
|
||||
//
|
||||
// Types can implement the Unmarshaler interface to control their own decoding. The interface
|
||||
// behaves similarly to how UnmarshalJSON does in the standard library. It can be used as an
|
||||
// alternative or companion to a DecodeHook.
|
||||
//
|
||||
// type TrimmedString string
|
||||
//
|
||||
// func (t *TrimmedString) UnmarshalMapstructure(input any) error {
|
||||
// str, ok := input.(string)
|
||||
// if !ok {
|
||||
// return fmt.Errorf("expected string, got %T", input)
|
||||
// }
|
||||
// *t = TrimmedString(strings.TrimSpace(str))
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// See the Unmarshaler interface documentation for more details.
|
||||
//
|
||||
// # Other Configuration
|
||||
//
|
||||
// mapstructure is highly configurable. See the DecoderConfig struct
|
||||
@@ -218,6 +237,17 @@ type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, any) (any, error)
|
||||
// values.
|
||||
type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (any, error)
|
||||
|
||||
// Unmarshaler is the interface implemented by types that can unmarshal
|
||||
// themselves. UnmarshalMapstructure receives the input data (potentially
|
||||
// transformed by DecodeHook) and should populate the receiver with the
|
||||
// decoded values.
|
||||
//
|
||||
// The Unmarshaler interface takes precedence over the default decoding
|
||||
// logic for any type (structs, slices, maps, primitives, etc.).
|
||||
type Unmarshaler interface {
|
||||
UnmarshalMapstructure(any) error
|
||||
}
|
||||
|
||||
// DecoderConfig is the configuration that is used to create a new decoder
|
||||
// and allows customization of various aspects of decoding.
|
||||
type DecoderConfig struct {
|
||||
@@ -281,6 +311,13 @@ type DecoderConfig struct {
|
||||
// }
|
||||
Squash bool
|
||||
|
||||
// Deep will map structures in slices instead of copying them
|
||||
//
|
||||
// type Parent struct {
|
||||
// Children []Child `mapstructure:",deep"`
|
||||
// }
|
||||
Deep bool
|
||||
|
||||
// Metadata is the struct that will contain extra metadata about
|
||||
// the decoding. If this is nil, then no metadata will be tracked.
|
||||
Metadata *Metadata
|
||||
@@ -290,9 +327,15 @@ type DecoderConfig struct {
|
||||
Result any
|
||||
|
||||
// The tag name that mapstructure reads for field names. This
|
||||
// defaults to "mapstructure"
|
||||
// defaults to "mapstructure". Multiple tag names can be specified
|
||||
// as a comma-separated list (e.g., "yaml,json"), and the first
|
||||
// matching non-empty tag will be used.
|
||||
TagName string
|
||||
|
||||
// RootName specifies the name to use for the root element in error messages. For example:
|
||||
// '<rootName>' has unset fields: <fieldName>
|
||||
RootName string
|
||||
|
||||
// The option of the value in the tag that indicates a field should
|
||||
// be squashed. This defaults to "squash".
|
||||
SquashTagOption string
|
||||
@@ -304,11 +347,34 @@ type DecoderConfig struct {
|
||||
// MatchName is the function used to match the map key to the struct
|
||||
// field name or tag. Defaults to `strings.EqualFold`. This can be used
|
||||
// to implement case-sensitive tag values, support snake casing, etc.
|
||||
//
|
||||
// MatchName is used as a fallback comparison when the direct key lookup fails.
|
||||
// See also MapFieldName for transforming field names before lookup.
|
||||
MatchName func(mapKey, fieldName string) bool
|
||||
|
||||
// DecodeNil, if set to true, will cause the DecodeHook (if present) to run
|
||||
// even if the input is nil. This can be used to provide default values.
|
||||
DecodeNil bool
|
||||
|
||||
// MapFieldName is the function used to convert the struct field name to the map's key name.
|
||||
//
|
||||
// This is useful for automatically converting between naming conventions without
|
||||
// explicitly tagging each field. For example, to convert Go's PascalCase field names
|
||||
// to snake_case map keys:
|
||||
//
|
||||
// MapFieldName: func(s string) string {
|
||||
// return strcase.ToSnake(s)
|
||||
// }
|
||||
//
|
||||
// When decoding from a map to a struct, the transformed field name is used for
|
||||
// the initial lookup. If not found, MatchName is used as a fallback comparison.
|
||||
// Explicit struct tags always take precedence over MapFieldName.
|
||||
MapFieldName func(string) string
|
||||
|
||||
// DisableUnmarshaler, if set to true, disables the use of the Unmarshaler
|
||||
// interface. Types implementing Unmarshaler will be decoded using the
|
||||
// standard struct decoding logic instead.
|
||||
DisableUnmarshaler bool
|
||||
}
|
||||
|
||||
// A Decoder takes a raw interface value and turns it into structured
|
||||
@@ -445,6 +511,12 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
|
||||
config.MatchName = strings.EqualFold
|
||||
}
|
||||
|
||||
if config.MapFieldName == nil {
|
||||
config.MapFieldName = func(s string) string {
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
result := &Decoder{
|
||||
config: config,
|
||||
}
|
||||
@@ -458,7 +530,7 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) {
|
||||
// Decode decodes the given raw interface to the target pointer specified
|
||||
// by the configuration.
|
||||
func (d *Decoder) Decode(input any) error {
|
||||
err := d.decode("", input, reflect.ValueOf(d.config.Result).Elem())
|
||||
err := d.decode(d.config.RootName, input, reflect.ValueOf(d.config.Result).Elem())
|
||||
|
||||
// Retain some of the original behavior when multiple errors ocurr
|
||||
var joinedErr interface{ Unwrap() []error }
|
||||
@@ -540,36 +612,50 @@ func (d *Decoder) decode(name string, input any, outVal reflect.Value) error {
|
||||
|
||||
var err error
|
||||
addMetaKey := true
|
||||
switch outputKind {
|
||||
case reflect.Bool:
|
||||
err = d.decodeBool(name, input, outVal)
|
||||
case reflect.Interface:
|
||||
err = d.decodeBasic(name, input, outVal)
|
||||
case reflect.String:
|
||||
err = d.decodeString(name, input, outVal)
|
||||
case reflect.Int:
|
||||
err = d.decodeInt(name, input, outVal)
|
||||
case reflect.Uint:
|
||||
err = d.decodeUint(name, input, outVal)
|
||||
case reflect.Float32:
|
||||
err = d.decodeFloat(name, input, outVal)
|
||||
case reflect.Complex64:
|
||||
err = d.decodeComplex(name, input, outVal)
|
||||
case reflect.Struct:
|
||||
err = d.decodeStruct(name, input, outVal)
|
||||
case reflect.Map:
|
||||
err = d.decodeMap(name, input, outVal)
|
||||
case reflect.Ptr:
|
||||
addMetaKey, err = d.decodePtr(name, input, outVal)
|
||||
case reflect.Slice:
|
||||
err = d.decodeSlice(name, input, outVal)
|
||||
case reflect.Array:
|
||||
err = d.decodeArray(name, input, outVal)
|
||||
case reflect.Func:
|
||||
err = d.decodeFunc(name, input, outVal)
|
||||
default:
|
||||
// If we reached this point then we weren't able to decode it
|
||||
return newDecodeError(name, fmt.Errorf("unsupported type: %s", outputKind))
|
||||
|
||||
// Check if the target implements Unmarshaler and use it if not disabled
|
||||
unmarshaled := false
|
||||
if !d.config.DisableUnmarshaler {
|
||||
if unmarshaler, ok := getUnmarshaler(outVal); ok {
|
||||
if err = unmarshaler.UnmarshalMapstructure(input); err != nil {
|
||||
err = newDecodeError(name, err)
|
||||
}
|
||||
unmarshaled = true
|
||||
}
|
||||
}
|
||||
|
||||
if !unmarshaled {
|
||||
switch outputKind {
|
||||
case reflect.Bool:
|
||||
err = d.decodeBool(name, input, outVal)
|
||||
case reflect.Interface:
|
||||
err = d.decodeBasic(name, input, outVal)
|
||||
case reflect.String:
|
||||
err = d.decodeString(name, input, outVal)
|
||||
case reflect.Int:
|
||||
err = d.decodeInt(name, input, outVal)
|
||||
case reflect.Uint:
|
||||
err = d.decodeUint(name, input, outVal)
|
||||
case reflect.Float32:
|
||||
err = d.decodeFloat(name, input, outVal)
|
||||
case reflect.Complex64:
|
||||
err = d.decodeComplex(name, input, outVal)
|
||||
case reflect.Struct:
|
||||
err = d.decodeStruct(name, input, outVal)
|
||||
case reflect.Map:
|
||||
err = d.decodeMap(name, input, outVal)
|
||||
case reflect.Ptr:
|
||||
addMetaKey, err = d.decodePtr(name, input, outVal)
|
||||
case reflect.Slice:
|
||||
err = d.decodeSlice(name, input, outVal)
|
||||
case reflect.Array:
|
||||
err = d.decodeArray(name, input, outVal)
|
||||
case reflect.Func:
|
||||
err = d.decodeFunc(name, input, outVal)
|
||||
default:
|
||||
// If we reached this point then we weren't able to decode it
|
||||
return newDecodeError(name, fmt.Errorf("unsupported type: %s", outputKind))
|
||||
}
|
||||
}
|
||||
|
||||
// If we reached here, then we successfully decoded SOMETHING, so
|
||||
@@ -668,7 +754,7 @@ func (d *Decoder) decodeString(name string, data any, val reflect.Value) error {
|
||||
case reflect.Uint8:
|
||||
var uints []uint8
|
||||
if dataKind == reflect.Array {
|
||||
uints = make([]uint8, dataVal.Len(), dataVal.Len())
|
||||
uints = make([]uint8, dataVal.Len())
|
||||
for i := range uints {
|
||||
uints[i] = dataVal.Index(i).Interface().(uint8)
|
||||
}
|
||||
@@ -1060,8 +1146,8 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
|
||||
)
|
||||
}
|
||||
|
||||
tagValue := f.Tag.Get(d.config.TagName)
|
||||
keyName := f.Name
|
||||
tagValue, _ := getTagValue(f, d.config.TagName)
|
||||
keyName := d.config.MapFieldName(f.Name)
|
||||
|
||||
if tagValue == "" && d.config.IgnoreUntaggedFields {
|
||||
continue
|
||||
@@ -1070,6 +1156,9 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
|
||||
// If Squash is set in the config, we squash the field down.
|
||||
squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous
|
||||
|
||||
// If Deep is set in the config, set as default value.
|
||||
deep := d.config.Deep
|
||||
|
||||
v = dereferencePtrToStructIfNeeded(v, d.config.TagName)
|
||||
|
||||
// Determine the name of the key in the map
|
||||
@@ -1078,12 +1167,12 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
|
||||
continue
|
||||
}
|
||||
// If "omitempty" is specified in the tag, it ignores empty values.
|
||||
if strings.Index(tagValue[index+1:], "omitempty") != -1 && isEmptyValue(v) {
|
||||
if strings.Contains(tagValue[index+1:], "omitempty") && isEmptyValue(v) {
|
||||
continue
|
||||
}
|
||||
|
||||
// If "omitzero" is specified in the tag, it ignores zero values.
|
||||
if strings.Index(tagValue[index+1:], "omitzero") != -1 && v.IsZero() {
|
||||
if strings.Contains(tagValue[index+1:], "omitzero") && v.IsZero() {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1103,7 +1192,7 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if strings.Index(tagValue[index+1:], "remain") != -1 {
|
||||
if strings.Contains(tagValue[index+1:], "remain") {
|
||||
if v.Kind() != reflect.Map {
|
||||
return newDecodeError(
|
||||
name+"."+f.Name,
|
||||
@@ -1118,6 +1207,9 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
deep = deep || strings.Contains(tagValue[index+1:], "deep")
|
||||
|
||||
if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" {
|
||||
keyName = keyNameTagValue
|
||||
}
|
||||
@@ -1164,6 +1256,41 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re
|
||||
valMap.SetMapIndex(reflect.ValueOf(keyName), vMap)
|
||||
}
|
||||
|
||||
case reflect.Slice:
|
||||
if deep {
|
||||
var childType reflect.Type
|
||||
switch v.Type().Elem().Kind() {
|
||||
case reflect.Struct:
|
||||
childType = reflect.TypeOf(map[string]any{})
|
||||
default:
|
||||
childType = v.Type().Elem()
|
||||
}
|
||||
|
||||
sType := reflect.SliceOf(childType)
|
||||
|
||||
addrVal := reflect.New(sType)
|
||||
|
||||
vSlice := reflect.MakeSlice(sType, v.Len(), v.Cap())
|
||||
|
||||
if v.Len() > 0 {
|
||||
reflect.Indirect(addrVal).Set(vSlice)
|
||||
|
||||
err := d.decode(keyName, v.Interface(), reflect.Indirect(addrVal))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
vSlice = reflect.Indirect(addrVal)
|
||||
|
||||
valMap.SetMapIndex(reflect.ValueOf(keyName), vSlice)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
// When deep mapping is not needed, fallthrough to normal copy
|
||||
fallthrough
|
||||
|
||||
default:
|
||||
valMap.SetMapIndex(reflect.ValueOf(keyName), v)
|
||||
}
|
||||
@@ -1471,7 +1598,10 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
||||
remain := false
|
||||
|
||||
// We always parse the tags cause we're looking for other tags too
|
||||
tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",")
|
||||
tagParts := getTagParts(fieldType, d.config.TagName)
|
||||
if len(tagParts) == 0 {
|
||||
tagParts = []string{""}
|
||||
}
|
||||
for _, tag := range tagParts[1:] {
|
||||
if tag == d.config.SquashTagOption {
|
||||
squash = true
|
||||
@@ -1492,6 +1622,18 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
||||
if !fieldVal.IsNil() {
|
||||
structs = append(structs, fieldVal.Elem().Elem())
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if fieldVal.Type().Elem().Kind() == reflect.Struct {
|
||||
if fieldVal.IsNil() {
|
||||
fieldVal.Set(reflect.New(fieldVal.Type().Elem()))
|
||||
}
|
||||
structs = append(structs, fieldVal.Elem())
|
||||
} else {
|
||||
errs = append(errs, newDecodeError(
|
||||
name+"."+fieldType.Name,
|
||||
fmt.Errorf("unsupported type for squashed pointer: %s", fieldVal.Type().Elem().Kind()),
|
||||
))
|
||||
}
|
||||
default:
|
||||
errs = append(errs, newDecodeError(
|
||||
name+"."+fieldType.Name,
|
||||
@@ -1516,13 +1658,15 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
||||
field, fieldValue := f.field, f.val
|
||||
fieldName := field.Name
|
||||
|
||||
tagValue := field.Tag.Get(d.config.TagName)
|
||||
tagValue, _ := getTagValue(field, d.config.TagName)
|
||||
if tagValue == "" && d.config.IgnoreUntaggedFields {
|
||||
continue
|
||||
}
|
||||
tagValue = strings.SplitN(tagValue, ",", 2)[0]
|
||||
if tagValue != "" {
|
||||
fieldName = tagValue
|
||||
} else {
|
||||
fieldName = d.config.MapFieldName(fieldName)
|
||||
}
|
||||
|
||||
rawMapKey := reflect.ValueOf(fieldName)
|
||||
@@ -1605,8 +1749,14 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
// Improve error message when name is empty by showing the target struct type
|
||||
// in the case where it is empty for embedded structs.
|
||||
errorName := name
|
||||
if errorName == "" {
|
||||
errorName = val.Type().String()
|
||||
}
|
||||
errs = append(errs, newDecodeError(
|
||||
name,
|
||||
errorName,
|
||||
fmt.Errorf("has invalid keys: %s", strings.Join(keys, ", ")),
|
||||
))
|
||||
}
|
||||
@@ -1692,7 +1842,7 @@ func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool,
|
||||
if f.PkgPath == "" && !checkMapstructureTags { // check for unexported fields
|
||||
return true
|
||||
}
|
||||
if checkMapstructureTags && f.Tag.Get(tagName) != "" { // check for mapstructure tags inside
|
||||
if checkMapstructureTags && hasAnyTag(f, tagName) { // check for mapstructure tags inside
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1700,13 +1850,99 @@ func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool,
|
||||
}
|
||||
|
||||
func dereferencePtrToStructIfNeeded(v reflect.Value, tagName string) reflect.Value {
|
||||
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
|
||||
if v.Kind() != reflect.Ptr {
|
||||
return v
|
||||
}
|
||||
deref := v.Elem()
|
||||
derefT := deref.Type()
|
||||
if isStructTypeConvertibleToMap(derefT, true, tagName) {
|
||||
return deref
|
||||
|
||||
switch v.Elem().Kind() {
|
||||
case reflect.Slice:
|
||||
return v.Elem()
|
||||
|
||||
case reflect.Struct:
|
||||
deref := v.Elem()
|
||||
derefT := deref.Type()
|
||||
if isStructTypeConvertibleToMap(derefT, true, tagName) {
|
||||
return deref
|
||||
}
|
||||
return v
|
||||
|
||||
default:
|
||||
return v
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func hasAnyTag(field reflect.StructField, tagName string) bool {
|
||||
_, ok := getTagValue(field, tagName)
|
||||
return ok
|
||||
}
|
||||
|
||||
func getTagParts(field reflect.StructField, tagName string) []string {
|
||||
tagValue, ok := getTagValue(field, tagName)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return strings.Split(tagValue, ",")
|
||||
}
|
||||
|
||||
func getTagValue(field reflect.StructField, tagName string) (string, bool) {
|
||||
for _, name := range splitTagNames(tagName) {
|
||||
if tag := field.Tag.Get(name); tag != "" {
|
||||
return tag, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func splitTagNames(tagName string) []string {
|
||||
if tagName == "" {
|
||||
return []string{"mapstructure"}
|
||||
}
|
||||
parts := strings.Split(tagName, ",")
|
||||
result := make([]string, 0, len(parts))
|
||||
|
||||
for _, name := range parts {
|
||||
name = strings.TrimSpace(name)
|
||||
if name != "" {
|
||||
result = append(result, name)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// unmarshalerType is cached for performance
|
||||
var unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
|
||||
|
||||
// getUnmarshaler checks if the value implements Unmarshaler and returns
|
||||
// the Unmarshaler and a boolean indicating if it was found. It handles both
|
||||
// pointer and value receivers.
|
||||
func getUnmarshaler(val reflect.Value) (Unmarshaler, bool) {
|
||||
// Skip invalid or nil values
|
||||
if !val.IsValid() {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
switch val.Kind() {
|
||||
case reflect.Pointer, reflect.Interface:
|
||||
if val.IsNil() {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
// Check pointer receiver first (most common case)
|
||||
if val.CanAddr() {
|
||||
ptrVal := val.Addr()
|
||||
// Quick check: if no methods, can't implement any interface
|
||||
if ptrVal.Type().NumMethod() > 0 && ptrVal.Type().Implements(unmarshalerType) {
|
||||
return ptrVal.Interface().(Unmarshaler), true
|
||||
}
|
||||
}
|
||||
|
||||
// Check value receiver
|
||||
// Quick check: if no methods, can't implement any interface
|
||||
if val.Type().NumMethod() > 0 && val.CanInterface() && val.Type().Implements(unmarshalerType) {
|
||||
return val.Interface().(Unmarshaler), true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
||||
|
||||
-96
@@ -1,96 +0,0 @@
|
||||
## 1.5.0
|
||||
|
||||
* New option `IgnoreUntaggedFields` to ignore decoding to any fields
|
||||
without `mapstructure` (or the configured tag name) set [GH-277]
|
||||
* New option `ErrorUnset` which makes it an error if any fields
|
||||
in a target struct are not set by the decoding process. [GH-225]
|
||||
* New function `OrComposeDecodeHookFunc` to help compose decode hooks. [GH-240]
|
||||
* Decoding to slice from array no longer crashes [GH-265]
|
||||
* Decode nested struct pointers to map [GH-271]
|
||||
* Fix issue where `,squash` was ignored if `Squash` option was set. [GH-280]
|
||||
* Fix issue where fields with `,omitempty` would sometimes decode
|
||||
into a map with an empty string key [GH-281]
|
||||
|
||||
## 1.4.3
|
||||
|
||||
* Fix cases where `json.Number` didn't decode properly [GH-261]
|
||||
|
||||
## 1.4.2
|
||||
|
||||
* Custom name matchers to support any sort of casing, formatting, etc. for
|
||||
field names. [GH-250]
|
||||
* Fix possible panic in ComposeDecodeHookFunc [GH-251]
|
||||
|
||||
## 1.4.1
|
||||
|
||||
* Fix regression where `*time.Time` value would be set to empty and not be sent
|
||||
to decode hooks properly [GH-232]
|
||||
|
||||
## 1.4.0
|
||||
|
||||
* A new decode hook type `DecodeHookFuncValue` has been added that has
|
||||
access to the full values. [GH-183]
|
||||
* Squash is now supported with embedded fields that are struct pointers [GH-205]
|
||||
* Empty strings will convert to 0 for all numeric types when weakly decoding [GH-206]
|
||||
|
||||
## 1.3.3
|
||||
|
||||
* Decoding maps from maps creates a settable value for decode hooks [GH-203]
|
||||
|
||||
## 1.3.2
|
||||
|
||||
* Decode into interface type with a struct value is supported [GH-187]
|
||||
|
||||
## 1.3.1
|
||||
|
||||
* Squash should only squash embedded structs. [GH-194]
|
||||
|
||||
## 1.3.0
|
||||
|
||||
* Added `",omitempty"` support. This will ignore zero values in the source
|
||||
structure when encoding. [GH-145]
|
||||
|
||||
## 1.2.3
|
||||
|
||||
* Fix duplicate entries in Keys list with pointer values. [GH-185]
|
||||
|
||||
## 1.2.2
|
||||
|
||||
* Do not add unsettable (unexported) values to the unused metadata key
|
||||
or "remain" value. [GH-150]
|
||||
|
||||
## 1.2.1
|
||||
|
||||
* Go modules checksum mismatch fix
|
||||
|
||||
## 1.2.0
|
||||
|
||||
* Added support to capture unused values in a field using the `",remain"` value
|
||||
in the mapstructure tag. There is an example to showcase usage.
|
||||
* Added `DecoderConfig` option to always squash embedded structs
|
||||
* `json.Number` can decode into `uint` types
|
||||
* Empty slices are preserved and not replaced with nil slices
|
||||
* Fix panic that can occur in when decoding a map into a nil slice of structs
|
||||
* Improved package documentation for godoc
|
||||
|
||||
## 1.1.2
|
||||
|
||||
* Fix error when decode hook decodes interface implementation into interface
|
||||
type. [GH-140]
|
||||
|
||||
## 1.1.1
|
||||
|
||||
* Fix panic that can happen in `decodePtr`
|
||||
|
||||
## 1.1.0
|
||||
|
||||
* Added `StringToIPHookFunc` to convert `string` to `net.IP` and `net.IPNet` [GH-133]
|
||||
* Support struct to struct decoding [GH-137]
|
||||
* If source map value is nil, then destination map value is nil (instead of empty)
|
||||
* If source slice value is nil, then destination slice value is nil (instead of empty)
|
||||
* If source pointer is nil, then destination pointer is set to nil (instead of
|
||||
allocated zero value of type)
|
||||
|
||||
## 1.0.0
|
||||
|
||||
* Initial tagged stable release.
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Mitchell Hashimoto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
# mapstructure [](https://godoc.org/github.com/mitchellh/mapstructure)
|
||||
|
||||
mapstructure is a Go library for decoding generic map values to structures
|
||||
and vice versa, while providing helpful error handling.
|
||||
|
||||
This library is most useful when decoding values from some data stream (JSON,
|
||||
Gob, etc.) where you don't _quite_ know the structure of the underlying data
|
||||
until you read a part of it. You can therefore read a `map[string]interface{}`
|
||||
and use this library to decode it into the proper underlying native Go
|
||||
structure.
|
||||
|
||||
## Installation
|
||||
|
||||
Standard `go get`:
|
||||
|
||||
```
|
||||
$ go get github.com/mitchellh/mapstructure
|
||||
```
|
||||
|
||||
## Usage & Example
|
||||
|
||||
For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/mapstructure).
|
||||
|
||||
The `Decode` function has examples associated with it there.
|
||||
|
||||
## But Why?!
|
||||
|
||||
Go offers fantastic standard libraries for decoding formats such as JSON.
|
||||
The standard method is to have a struct pre-created, and populate that struct
|
||||
from the bytes of the encoded format. This is great, but the problem is if
|
||||
you have configuration or an encoding that changes slightly depending on
|
||||
specific fields. For example, consider this JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "person",
|
||||
"name": "Mitchell"
|
||||
}
|
||||
```
|
||||
|
||||
Perhaps we can't populate a specific structure without first reading
|
||||
the "type" field from the JSON. We could always do two passes over the
|
||||
decoding of the JSON (reading the "type" first, and the rest later).
|
||||
However, it is much simpler to just decode this into a `map[string]interface{}`
|
||||
structure, read the "type" key, then use something like this library
|
||||
to decode it into the proper structure.
|
||||
-279
@@ -1,279 +0,0 @@
|
||||
package mapstructure
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns
|
||||
// it into the proper DecodeHookFunc type, such as DecodeHookFuncType.
|
||||
func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc {
|
||||
// Create variables here so we can reference them with the reflect pkg
|
||||
var f1 DecodeHookFuncType
|
||||
var f2 DecodeHookFuncKind
|
||||
var f3 DecodeHookFuncValue
|
||||
|
||||
// Fill in the variables into this interface and the rest is done
|
||||
// automatically using the reflect package.
|
||||
potential := []interface{}{f1, f2, f3}
|
||||
|
||||
v := reflect.ValueOf(h)
|
||||
vt := v.Type()
|
||||
for _, raw := range potential {
|
||||
pt := reflect.ValueOf(raw).Type()
|
||||
if vt.ConvertibleTo(pt) {
|
||||
return v.Convert(pt).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodeHookExec executes the given decode hook. This should be used
|
||||
// since it'll naturally degrade to the older backwards compatible DecodeHookFunc
|
||||
// that took reflect.Kind instead of reflect.Type.
|
||||
func DecodeHookExec(
|
||||
raw DecodeHookFunc,
|
||||
from reflect.Value, to reflect.Value) (interface{}, error) {
|
||||
|
||||
switch f := typedDecodeHook(raw).(type) {
|
||||
case DecodeHookFuncType:
|
||||
return f(from.Type(), to.Type(), from.Interface())
|
||||
case DecodeHookFuncKind:
|
||||
return f(from.Kind(), to.Kind(), from.Interface())
|
||||
case DecodeHookFuncValue:
|
||||
return f(from, to)
|
||||
default:
|
||||
return nil, errors.New("invalid decode hook signature")
|
||||
}
|
||||
}
|
||||
|
||||
// ComposeDecodeHookFunc creates a single DecodeHookFunc that
|
||||
// automatically composes multiple DecodeHookFuncs.
|
||||
//
|
||||
// The composed funcs are called in order, with the result of the
|
||||
// previous transformation.
|
||||
func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc {
|
||||
return func(f reflect.Value, t reflect.Value) (interface{}, error) {
|
||||
var err error
|
||||
data := f.Interface()
|
||||
|
||||
newFrom := f
|
||||
for _, f1 := range fs {
|
||||
data, err = DecodeHookExec(f1, newFrom, t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newFrom = reflect.ValueOf(data)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
}
|
||||
|
||||
// OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned.
|
||||
// If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages.
|
||||
func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc {
|
||||
return func(a, b reflect.Value) (interface{}, error) {
|
||||
var allErrs string
|
||||
var out interface{}
|
||||
var err error
|
||||
|
||||
for _, f := range ff {
|
||||
out, err = DecodeHookExec(f, a, b)
|
||||
if err != nil {
|
||||
allErrs += err.Error() + "\n"
|
||||
continue
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
return nil, errors.New(allErrs)
|
||||
}
|
||||
}
|
||||
|
||||
// StringToSliceHookFunc returns a DecodeHookFunc that converts
|
||||
// string to []string by splitting on the given sep.
|
||||
func StringToSliceHookFunc(sep string) DecodeHookFunc {
|
||||
return func(
|
||||
f reflect.Kind,
|
||||
t reflect.Kind,
|
||||
data interface{}) (interface{}, error) {
|
||||
if f != reflect.String || t != reflect.Slice {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
raw := data.(string)
|
||||
if raw == "" {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
return strings.Split(raw, sep), nil
|
||||
}
|
||||
}
|
||||
|
||||
// StringToTimeDurationHookFunc returns a DecodeHookFunc that converts
|
||||
// strings to time.Duration.
|
||||
func StringToTimeDurationHookFunc() DecodeHookFunc {
|
||||
return func(
|
||||
f reflect.Type,
|
||||
t reflect.Type,
|
||||
data interface{}) (interface{}, error) {
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
}
|
||||
if t != reflect.TypeOf(time.Duration(5)) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Convert it by parsing
|
||||
return time.ParseDuration(data.(string))
|
||||
}
|
||||
}
|
||||
|
||||
// StringToIPHookFunc returns a DecodeHookFunc that converts
|
||||
// strings to net.IP
|
||||
func StringToIPHookFunc() DecodeHookFunc {
|
||||
return func(
|
||||
f reflect.Type,
|
||||
t reflect.Type,
|
||||
data interface{}) (interface{}, error) {
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
}
|
||||
if t != reflect.TypeOf(net.IP{}) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Convert it by parsing
|
||||
ip := net.ParseIP(data.(string))
|
||||
if ip == nil {
|
||||
return net.IP{}, fmt.Errorf("failed parsing ip %v", data)
|
||||
}
|
||||
|
||||
return ip, nil
|
||||
}
|
||||
}
|
||||
|
||||
// StringToIPNetHookFunc returns a DecodeHookFunc that converts
|
||||
// strings to net.IPNet
|
||||
func StringToIPNetHookFunc() DecodeHookFunc {
|
||||
return func(
|
||||
f reflect.Type,
|
||||
t reflect.Type,
|
||||
data interface{}) (interface{}, error) {
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
}
|
||||
if t != reflect.TypeOf(net.IPNet{}) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Convert it by parsing
|
||||
_, net, err := net.ParseCIDR(data.(string))
|
||||
return net, err
|
||||
}
|
||||
}
|
||||
|
||||
// StringToTimeHookFunc returns a DecodeHookFunc that converts
|
||||
// strings to time.Time.
|
||||
func StringToTimeHookFunc(layout string) DecodeHookFunc {
|
||||
return func(
|
||||
f reflect.Type,
|
||||
t reflect.Type,
|
||||
data interface{}) (interface{}, error) {
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
}
|
||||
if t != reflect.TypeOf(time.Time{}) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Convert it by parsing
|
||||
return time.Parse(layout, data.(string))
|
||||
}
|
||||
}
|
||||
|
||||
// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to
|
||||
// the decoder.
|
||||
//
|
||||
// Note that this is significantly different from the WeaklyTypedInput option
|
||||
// of the DecoderConfig.
|
||||
func WeaklyTypedHook(
|
||||
f reflect.Kind,
|
||||
t reflect.Kind,
|
||||
data interface{}) (interface{}, error) {
|
||||
dataVal := reflect.ValueOf(data)
|
||||
switch t {
|
||||
case reflect.String:
|
||||
switch f {
|
||||
case reflect.Bool:
|
||||
if dataVal.Bool() {
|
||||
return "1", nil
|
||||
}
|
||||
return "0", nil
|
||||
case reflect.Float32:
|
||||
return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil
|
||||
case reflect.Int:
|
||||
return strconv.FormatInt(dataVal.Int(), 10), nil
|
||||
case reflect.Slice:
|
||||
dataType := dataVal.Type()
|
||||
elemKind := dataType.Elem().Kind()
|
||||
if elemKind == reflect.Uint8 {
|
||||
return string(dataVal.Interface().([]uint8)), nil
|
||||
}
|
||||
case reflect.Uint:
|
||||
return strconv.FormatUint(dataVal.Uint(), 10), nil
|
||||
}
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func RecursiveStructToMapHookFunc() DecodeHookFunc {
|
||||
return func(f reflect.Value, t reflect.Value) (interface{}, error) {
|
||||
if f.Kind() != reflect.Struct {
|
||||
return f.Interface(), nil
|
||||
}
|
||||
|
||||
var i interface{} = struct{}{}
|
||||
if t.Type() != reflect.TypeOf(&i).Elem() {
|
||||
return f.Interface(), nil
|
||||
}
|
||||
|
||||
m := make(map[string]interface{})
|
||||
t.Set(reflect.ValueOf(m))
|
||||
|
||||
return f.Interface(), nil
|
||||
}
|
||||
}
|
||||
|
||||
// TextUnmarshallerHookFunc returns a DecodeHookFunc that applies
|
||||
// strings to the UnmarshalText function, when the target type
|
||||
// implements the encoding.TextUnmarshaler interface
|
||||
func TextUnmarshallerHookFunc() DecodeHookFuncType {
|
||||
return func(
|
||||
f reflect.Type,
|
||||
t reflect.Type,
|
||||
data interface{}) (interface{}, error) {
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
}
|
||||
result := reflect.New(t).Interface()
|
||||
unmarshaller, ok := result.(encoding.TextUnmarshaler)
|
||||
if !ok {
|
||||
return data, nil
|
||||
}
|
||||
if err := unmarshaller.UnmarshalText([]byte(data.(string))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
package mapstructure
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Error implements the error interface and can represents multiple
|
||||
// errors that occur in the course of a single decode.
|
||||
type Error struct {
|
||||
Errors []string
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
points := make([]string, len(e.Errors))
|
||||
for i, err := range e.Errors {
|
||||
points[i] = fmt.Sprintf("* %s", err)
|
||||
}
|
||||
|
||||
sort.Strings(points)
|
||||
return fmt.Sprintf(
|
||||
"%d error(s) decoding:\n\n%s",
|
||||
len(e.Errors), strings.Join(points, "\n"))
|
||||
}
|
||||
|
||||
// WrappedErrors implements the errwrap.Wrapper interface to make this
|
||||
// return value more useful with the errwrap and go-multierror libraries.
|
||||
func (e *Error) WrappedErrors() []error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := make([]error, len(e.Errors))
|
||||
for i, e := range e.Errors {
|
||||
result[i] = errors.New(e)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func appendErrors(errors []string, err error) []string {
|
||||
switch e := err.(type) {
|
||||
case *Error:
|
||||
return append(errors, e.Errors...)
|
||||
default:
|
||||
return append(errors, e.Error())
|
||||
}
|
||||
}
|
||||
-1540
File diff suppressed because it is too large.
Load diff
+1
@@ -17,3 +17,4 @@
|
||||
- Thomas Boerger <thomas@webhippie.de>
|
||||
- Miroslav Bauer <bauer@cesnet.cz>
|
||||
- Daniel Mueller <daniel.mueller@uni-muenster.de>
|
||||
- Michael Stingl <mail@michaelstingl.com>
|
||||
+1
-1
@@ -27,7 +27,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/logger"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/registry"
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ import (
|
||||
authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
|
||||
gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/appctx"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/auth/scope"
|
||||
ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -28,7 +28,7 @@ import (
|
||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/appctx"
|
||||
revactx "github.com/opencloud-eu/reva/v2/pkg/ctx"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/events"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -22,7 +22,7 @@ import (
|
||||
"context"
|
||||
|
||||
appauthpb "github.com/cs3org/go-cs3apis/cs3/auth/applications/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/appauth"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/appauth/manager/registry"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/appctx"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -28,8 +28,8 @@ import (
|
||||
registrypb "github.com/cs3org/go-cs3apis/cs3/app/registry/v1beta1"
|
||||
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/juliangruber/go-intersect"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/app"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/app/provider/registry"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -24,7 +24,7 @@ import (
|
||||
"google.golang.org/grpc"
|
||||
|
||||
registrypb "github.com/cs3org/go-cs3apis/cs3/app/registry/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/app"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/app/registry/registry"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -24,7 +24,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/appctx"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/auth"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/auth/manager/registry"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -22,7 +22,7 @@ import (
|
||||
"context"
|
||||
|
||||
registrypb "github.com/cs3org/go-cs3apis/cs3/auth/registry/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/auth"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/auth/registry/registry"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ import (
|
||||
ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1"
|
||||
datatx "github.com/cs3org/go-cs3apis/cs3/tx/v1beta1"
|
||||
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
txdriver "github.com/opencloud-eu/reva/v2/pkg/datatx"
|
||||
txregistry "github.com/opencloud-eu/reva/v2/pkg/datatx/manager/registry"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
|
||||
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{
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ import (
|
||||
"strings"
|
||||
|
||||
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/sharedconf"
|
||||
|
||||
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
+1
-1
@@ -24,7 +24,7 @@ import (
|
||||
"sort"
|
||||
|
||||
grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/group"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/group/manager/registry"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -22,7 +22,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/internal/grpc/services/helloworld/proto"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -22,7 +22,7 @@ import (
|
||||
"context"
|
||||
|
||||
ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/ocm/provider"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/ocm/provider/authorizer/registry"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -24,7 +24,7 @@ import (
|
||||
|
||||
permissions "github.com/cs3org/go-cs3apis/cs3/permissions/v1beta1"
|
||||
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/permission"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/permission/manager/registry"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -24,7 +24,7 @@ import (
|
||||
"google.golang.org/grpc"
|
||||
|
||||
preferencespb "github.com/cs3org/go-cs3apis/cs3/preferences/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/preferences"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/preferences/registry"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -30,7 +30,7 @@ import (
|
||||
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/password"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/permission"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -33,7 +33,7 @@ import (
|
||||
ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"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"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -39,7 +39,7 @@ import (
|
||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"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"
|
||||
|
||||
Generated
Vendored
+11
-2
@@ -33,7 +33,7 @@ import (
|
||||
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"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"
|
||||
@@ -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
@@ -28,7 +28,7 @@ import (
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
registrypb "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1"
|
||||
typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/status"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -24,7 +24,7 @@ import (
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/usershareprovider/usershareprovider.go
Generated
Vendored
+1
-1
@@ -33,7 +33,7 @@ import (
|
||||
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ import (
|
||||
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"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/internal/http/interceptors/auth/credential/registry"
|
||||
tokenregistry "github.com/opencloud-eu/reva/v2/internal/http/interceptors/auth/token/registry"
|
||||
tokenwriterregistry "github.com/opencloud-eu/reva/v2/internal/http/interceptors/auth/tokenwriter/registry"
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
package cors
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rhttp/global"
|
||||
"github.com/rs/cors"
|
||||
)
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -25,7 +25,7 @@ import (
|
||||
"strings"
|
||||
|
||||
ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/appctx"
|
||||
ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/ocm/provider"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -33,8 +33,8 @@ import (
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
iso6391 "github.com/emvi/iso-639-1"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
ua "github.com/mileusna/useragent"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/status"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -32,7 +32,7 @@ import (
|
||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||
|
||||
"github.com/gdexlab/go-render/render"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
"github.com/opencloud-eu/reva/v2/internal/http/services/archiver/manager"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
|
||||
|
||||
Loaded 100 of 411 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user