From 81e000b987eeb90f070ad4256b61997f9ed5f82b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 06:31:49 +0000 Subject: [PATCH 01/15] build(deps): bump github.com/gookit/config/v2 from 2.2.4 to 2.2.5 Bumps [github.com/gookit/config/v2](https://github.com/gookit/config) from 2.2.4 to 2.2.5. - [Release notes](https://github.com/gookit/config/releases) - [Commits](https://github.com/gookit/config/compare/v2.2.4...v2.2.5) --- updated-dependencies: - dependency-name: github.com/gookit/config/v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 4 +- go.sum | 12 +- vendor/github.com/gookit/config/v2/README.md | 16 +- .../gookit/config/v2/README.zh-CN.md | 29 +- vendor/github.com/gookit/config/v2/config.go | 13 +- vendor/github.com/gookit/config/v2/export.go | 4 +- vendor/github.com/gookit/config/v2/load.go | 86 ++- vendor/github.com/gookit/config/v2/options.go | 32 +- vendor/github.com/gookit/goutil/README.md | 71 +- .../github.com/gookit/goutil/README.zh-CN.md | 71 +- .../github.com/gookit/goutil/comdef/comdef.go | 3 + .../github.com/gookit/goutil/comdef/errors.go | 34 +- .../gookit/goutil/comdef/interface.go | 5 + vendor/github.com/gookit/goutil/conv.go | 31 +- .../gookit/goutil/encodes/encodes.go | 1 + .../gookit/goutil/fsutil/opwrite.go | 5 +- vendor/github.com/gookit/goutil/func.go | 2 +- .../gookit/goutil/internal/checkfn/check.go | 48 +- .../gookit/goutil/internal/comfunc/comfunc.go | 1 - .../gookit/goutil/internal/comfunc/convert.go | 118 ++++ .../gookit/goutil/internal/varexpr/varexpr.go | 46 +- .../github.com/gookit/goutil/maputil/data.go | 24 +- .../gookit/goutil/maputil/maputil.go | 24 +- .../github.com/gookit/goutil/maputil/smap.go | 14 + .../gookit/goutil/mathutil/convert.go | 641 +++++++++++------- .../gookit/goutil/mathutil/mathutil.go | 36 + .../gookit/goutil/reflects/check.go | 11 +- .../github.com/gookit/goutil/reflects/conv.go | 12 +- .../gookit/goutil/reflects/reflects.go | 3 +- .../gookit/goutil/structs/writer.go | 43 +- .../gookit/goutil/strutil/convert.go | 145 +--- .../gookit/goutil/strutil/strutil.go | 65 -- vendor/modules.txt | 4 +- 33 files changed, 1028 insertions(+), 626 deletions(-) diff --git a/go.mod b/go.mod index 2d458e658a..0b3b003bf4 100644 --- a/go.mod +++ b/go.mod @@ -47,7 +47,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/google/go-tika v0.3.0 github.com/google/uuid v1.5.0 - github.com/gookit/config/v2 v2.2.4 + github.com/gookit/config/v2 v2.2.5 github.com/gorilla/mux v1.8.1 github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 github.com/jellydator/ttlcache/v2 v2.11.1 @@ -223,7 +223,7 @@ require ( github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/google/renameio/v2 v2.0.0 // indirect github.com/gookit/color v1.5.4 // indirect - github.com/gookit/goutil v0.6.14 // indirect + github.com/gookit/goutil v0.6.15 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/schema v1.2.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect diff --git a/go.sum b/go.sum index a2d73cb7d0..2a8e210ab5 100644 --- a/go.sum +++ b/go.sum @@ -1423,12 +1423,12 @@ github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+ github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= -github.com/gookit/config/v2 v2.2.4 h1:uLHNzFzREe5gDBP4Gb1+WOC9LB6vauPvq4eolp32Dcg= -github.com/gookit/config/v2 v2.2.4/go.mod h1:k1ofSAuJnW6n1kTriFMSzFDC8ZT20tAPQ+1iGI3QOrU= -github.com/gookit/goutil v0.6.14 h1:96elyOG4BvVoDaiT7vx1vHPrVyEtFfYlPPBODR0/FGQ= -github.com/gookit/goutil v0.6.14/go.mod h1:YyDBddefmjS+mU2PDPgCcjVzTDM5WgExiDv5ZA/b8I8= -github.com/gookit/ini/v2 v2.2.2 h1:3B8abZJrVH1vi/7TU4STuTBxdhiAq1ORSt6NJZCahaI= -github.com/gookit/ini/v2 v2.2.2/go.mod h1:wGEfnBxv+7nVXytWM44tiqczv5hLKJ+m9MaA2uJg3iM= +github.com/gookit/config/v2 v2.2.5 h1:RECbYYbtherywmzn3LNeu9NA5ZqhD7MSKEMsJ7l+MpU= +github.com/gookit/config/v2 v2.2.5/go.mod h1:NeX+yiNYn6Ei10eJvCQFXuHEPIE/IPS8bqaFIsszzaM= +github.com/gookit/goutil v0.6.15 h1:mMQ0ElojNZoyPD0eVROk5QXJPh2uKR4g06slgPDF5Jo= +github.com/gookit/goutil v0.6.15/go.mod h1:qdKdYEHQdEtyH+4fNdQNZfJHhI0jUZzHxQVAV3DaMDY= +github.com/gookit/ini/v2 v2.2.3 h1:nSbN+x9OfQPcMObTFP+XuHt8ev6ndv/fWWqxFhPMu2E= +github.com/gookit/ini/v2 v2.2.3/go.mod h1:Vu6p7P7xcfmb8KYu3L0ek8bqu/Im63N81q208SCCZY4= github.com/gophercloud/gophercloud v0.15.1-0.20210202035223-633d73521055/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= github.com/gophercloud/gophercloud v0.16.0/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4= github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae/go.mod h1:wx8HMD8oQD0Ryhz6+6ykq75PJ79iPyEqYHfwZ4l7OsA= diff --git a/vendor/github.com/gookit/config/v2/README.md b/vendor/github.com/gookit/config/v2/README.md index 908385d625..70e9bbc261 100644 --- a/vendor/github.com/gookit/config/v2/README.md +++ b/vendor/github.com/gookit/config/v2/README.md @@ -89,6 +89,7 @@ import ( // go run ./examples/yaml.go func main() { + // config.ParseEnv: will parse env var in string value. eg: shell: ${SHELL} config.WithOptions(config.ParseEnv) // add driver for support yaml content @@ -350,7 +351,7 @@ ioutil.WriteFile("my-config.yaml", buf.Bytes(), 0755) ```go // Options config options type Options struct { - // parse env value. like: "${EnvName}" "${EnvName|default}" + // parse env in string value. like: "${EnvName}" "${EnvName|default}" ParseEnv bool // ParseTime parses a duration string to time.Duration // eg: 10s, 2m @@ -361,10 +362,6 @@ type Options struct { EnableCache bool // parse key, allow find value by key path. default is True eg: 'key.sub' will find `map[key]sub` ParseKey bool - // tag name for binding data to struct - // Deprecated - // please set tag name by DecoderConfig - TagName string // the delimiter char for split key path, if `FindByPath=true`. default is '.' Delimiter byte // default write format @@ -380,6 +377,15 @@ type Options struct { } ``` +Examples for set options: + +```go +config.WithOptions(config.WithTagName("mytag")) +config.WithOptions(func(opt *Options) { + opt.SetTagNames("config") +}) +``` + ### Options: Parse default Support parse default value by struct tag `default` diff --git a/vendor/github.com/gookit/config/v2/README.zh-CN.md b/vendor/github.com/gookit/config/v2/README.zh-CN.md index 077e8046d7..7b161dda40 100644 --- a/vendor/github.com/gookit/config/v2/README.zh-CN.md +++ b/vendor/github.com/gookit/config/v2/README.zh-CN.md @@ -21,14 +21,20 @@ - 支持从 OS ENV 变量数据加载配置 - 支持从远程 URL 加载配置数据 - 支持从命令行参数(`flags`)设置配置数据 -- 支持在配置数据更改时触发事件 - - 可用事件: `set.value`, `set.data`, `load.data`, `clean.data`, `reload.data` -- 支持数据覆盖合并,加载多份数据时将按key自动合并 +- 数据自动覆盖合并,加载多份数据时将按`key`自动合并 +- 支持丰富的自定义选项设置 + - `Readonly` 支持设置配置数据只读 + - `EnableCache` 支持设置配置数据缓存 + - `ParseEnv` 支持获取时自动解析string值里的ENV变量(`shell: ${SHELL}` -> `shell: /bin/zsh`) + - `ParseDefault` 支持在绑定数据到结构体时解析默认值 (tag: `default:"def_value"`, 配合ParseEnv也支持ENV变量) + - `ParseTime` 支持绑定数据到struct时自动转换 `10s`,`2m` 为 `time.Duration` + - 完整选项设置请查看 `config.Options` - 支持将全部或部分配置数据绑定到结构体 `config.BindStruct("key", &s)` - 支持通过结构体标签 `default` 解析并设置默认值. eg: `default:"def_value"` - 支持从 ENV 初始化设置字段值 `default:"${APP_ENV | dev}"` - 支持通过 `.` 分隔符来按路径获取子级值,也支持自定义分隔符。 e.g `map.key` `arr.2` -- 支持解析ENV变量名称。 like `shell: ${SHELL}` -> `shell: /bin/zsh` +- 支持在配置数据更改时触发事件 + - 可用事件: `set.value`, `set.data`, `load.data`, `clean.data`, `reload.data` - 简洁的使用API `Get` `Int` `Uint` `Int64` `String` `Bool` `Ints` `IntMap` `Strings` `StringMap` ... - 完善的单元测试(code coverage > 95%) @@ -88,7 +94,7 @@ import ( // go run ./examples/yaml.go func main() { - // 设置选项支持 ENV 解析 + // 设置选项支持ENV变量解析:当获取的值为string类型时,会尝试解析其中的ENV变量 config.WithOptions(config.ParseEnv) // 添加驱动程序以支持yaml内容解析(除了JSON是默认支持,其他的则是按需使用) @@ -334,7 +340,7 @@ ioutil.WriteFile("my-config.yaml", buf.Bytes(), 0755) ```go // Options config options type Options struct { - // parse env value. like: "${EnvName}" "${EnvName|default}" + // parse env in string value. like: "${EnvName}" "${EnvName|default}" ParseEnv bool // ParseTime parses a duration string to time.Duration // eg: 10s, 2m @@ -345,8 +351,6 @@ type Options struct { EnableCache bool // parse key, allow find value by key path. default is True eg: 'key.sub' will find `map[key]sub` ParseKey bool - // tag name for binding data to struct - TagName string // the delimiter char for split key, when `FindByPath=true`. default is '.' Delimiter byte // default write format. default is JSON @@ -362,6 +366,15 @@ type Options struct { } ``` +Examples for set options: + +```go +config.WithOptions(config.WithTagName("mytag")) +config.WithOptions(func(opt *Options) { + opt.SetTagNames("config") +}) +``` + ### 选项: 解析默认值 NEW: 支持通过结构标签 `default` 解析并设置默认值 diff --git a/vendor/github.com/gookit/config/v2/config.go b/vendor/github.com/gookit/config/v2/config.go index 4c708c93a3..d52515bc4b 100644 --- a/vendor/github.com/gookit/config/v2/config.go +++ b/vendor/github.com/gookit/config/v2/config.go @@ -111,23 +111,24 @@ type Config struct { sMapCache map[string]strMap } -// New config instance, default add JSON driver +// New config instance with custom options, default with JSON driver func New(name string, opts ...OptionFn) *Config { - return NewEmpty(name).WithDriver(JSONDriver).WithOptions(opts...) + return NewEmpty(name, opts...).WithDriver(JSONDriver) } -// NewEmpty config instance -func NewEmpty(name string) *Config { - return &Config{ +// NewEmpty create config instance with custom options +func NewEmpty(name string, opts ...OptionFn) *Config { + c := &Config{ name: name, opts: newDefaultOption(), data: make(map[string]any), - // don't add any drivers encoders: map[string]Encoder{}, decoders: map[string]Decoder{}, aliasMap: make(map[string]string), } + + return c.WithOptions(opts...) } // NewWith create config instance, and you can call some init func diff --git a/vendor/github.com/gookit/config/v2/export.go b/vendor/github.com/gookit/config/v2/export.go index d6e7c573cb..7f20b61794 100644 --- a/vendor/github.com/gookit/config/v2/export.go +++ b/vendor/github.com/gookit/config/v2/export.go @@ -121,7 +121,7 @@ func (c *Config) Structure(key string, dst any) (err error) { return err } -// ToJSON string +// ToJSON string, will ignore error func (c *Config) ToJSON() string { buf := &bytes.Buffer{} @@ -129,7 +129,6 @@ func (c *Config) ToJSON() string { if err != nil { return "" } - return buf.String() } @@ -168,7 +167,6 @@ func (c *Config) DumpTo(out io.Writer, format string) (n int64, err error) { // write content to out num, _ := fmt.Fprintln(out, string(encoded)) - return int64(num), nil } diff --git a/vendor/github.com/gookit/config/v2/load.go b/vendor/github.com/gookit/config/v2/load.go index 38becdf8c2..e90f634e8d 100644 --- a/vendor/github.com/gookit/config/v2/load.go +++ b/vendor/github.com/gookit/config/v2/load.go @@ -12,9 +12,9 @@ import ( "strings" "time" + "dario.cat/mergo" "github.com/gookit/goutil/errorx" "github.com/gookit/goutil/fsutil" - "github.com/imdario/mergo" ) // LoadFiles load one or multi files, will fire OnLoadData event @@ -103,10 +103,8 @@ func (c *Config) LoadOSEnv(keys []string, keyToLower bool) { if keyToLower { key = strings.ToLower(key) } - _ = c.Set(key, val) } - c.fireHook(OnLoadData) } @@ -195,7 +193,7 @@ func LoadData(dataSource ...any) error { return dc.LoadData(dataSource...) } // LoadData load data from map OR struct // -// The dataSources can be: +// The dataSources type allow: // - map[string]any // - map[string]string func (c *Config) LoadData(dataSources ...any) (err error) { @@ -203,19 +201,24 @@ func (c *Config) LoadData(dataSources ...any) (err error) { c.opts.Delimiter = defaultDelimiter } + var loaded bool for _, ds := range dataSources { if smp, ok := ds.(map[string]string); ok { + loaded = true c.LoadSMap(smp) continue } - err = mergo.Merge(&c.data, ds, mergo.WithOverride) + err = mergo.Merge(&c.data, ds, c.opts.MergeOptions...) if err != nil { return errorx.WithStack(err) } + loaded = true } - c.fireHook(OnLoadData) + if loaded { + c.fireHook(OnLoadData) + } return } @@ -237,8 +240,8 @@ func LoadSources(format string, src []byte, more ...[]byte) error { // Usage: // // config.LoadSources(config.Yaml, []byte(` -// name: blog -// arr: +// name: blog +// arr: // key: val // // `)) @@ -308,6 +311,24 @@ func (c *Config) LoadExistsByFormat(format string, configFiles ...string) (err e return } +// LoadOptions for load config from dir. +type LoadOptions struct { + // DataKey use for load config from dir. + // see https://github.com/gookit/config/issues/173 + DataKey string +} + +// LoadOptFn type func +type LoadOptFn func(lo *LoadOptions) + +func newLoadOptions(loFns []LoadOptFn) *LoadOptions { + lo := &LoadOptions{} + for _, fn := range loFns { + fn(lo) + } + return lo +} + // LoadFromDir Load custom format files from the given directory, the file name will be used as the key. // // Example: @@ -317,24 +338,30 @@ func (c *Config) LoadExistsByFormat(format string, configFiles ...string) (err e // // // after load // Config.data = map[string]any{"task": file data} -func LoadFromDir(dirPath, format string) error { - return dc.LoadFromDir(dirPath, format) +func LoadFromDir(dirPath, format string, loFns ...LoadOptFn) error { + return dc.LoadFromDir(dirPath, format, loFns...) } // LoadFromDir Load custom format files from the given directory, the file name will be used as the key. // +// NOTE: will not be reloaded on call ReloadFiles(), if data loaded by the method. +// // Example: // -// // file: /somedir/task.json +// // file: /somedir/task.json , will use filename 'task' as key // Config.LoadFromDir("/somedir", "json") // -// // after load -// Config.data = map[string]any{"task": file data} -func (c *Config) LoadFromDir(dirPath, format string) (err error) { +// // after load, the data will be: +// Config.data = map[string]any{"task": {file data}} +func (c *Config) LoadFromDir(dirPath, format string, loFns ...LoadOptFn) (err error) { extName := "." + format extLen := len(extName) - return fsutil.FindInDir(dirPath, func(fPath string, ent fs.DirEntry) error { + lo := newLoadOptions(loFns) + dirData := make(map[string]any) + dataList := make([]map[string]any, 0, 8) + + err = fsutil.FindInDir(dirPath, func(fPath string, ent fs.DirEntry) error { baseName := ent.Name() if strings.HasSuffix(baseName, extName) { data, err := c.parseSourceToMap(format, fsutil.MustReadFile(fPath)) @@ -342,18 +369,31 @@ func (c *Config) LoadFromDir(dirPath, format string) (err error) { return err } + // filename without ext. onlyName := baseName[:len(baseName)-extLen] - err = c.loadDataMap(map[string]any{onlyName: data}) + if lo.DataKey != "" { + dataList = append(dataList, data) + } else { + dirData[onlyName] = data + } - // use file name as key, it cannot be reloaded. SO, cannot append to loadedFiles - // if err == nil { - // c.loadedFiles = append(c.loadedFiles, fPath) - // } - - return err + // TODO use file name as key, it cannot be reloaded. So, cannot append to loadedFiles + // c.loadedFiles = append(c.loadedFiles, fPath) } return nil }) + + if err != nil { + return err + } + if lo.DataKey != "" { + dirData[lo.DataKey] = dataList + } + + if len(dirData) == 0 { + return nil + } + return c.loadDataMap(dirData) } // ReloadFiles reload config data use loaded files @@ -440,7 +480,7 @@ func (c *Config) loadDataMap(data map[string]any) (err error) { c.data = data } else { // again ... will merge data - err = mergo.Merge(&c.data, data, mergo.WithOverride, mergo.WithTypeCheck) + err = mergo.Merge(&c.data, data, c.opts.MergeOptions...) } if !c.reloading && err == nil { diff --git a/vendor/github.com/gookit/config/v2/options.go b/vendor/github.com/gookit/config/v2/options.go index 595468f854..f58e83668c 100644 --- a/vendor/github.com/gookit/config/v2/options.go +++ b/vendor/github.com/gookit/config/v2/options.go @@ -3,6 +3,8 @@ package config import ( "strings" + "dario.cat/mergo" + "github.com/gookit/goutil" "github.com/mitchellh/mapstructure" ) @@ -20,20 +22,22 @@ type HookFunc func(event string, c *Config) // Options config options type Options struct { - // ParseEnv parse env value. like: "${EnvName}" "${EnvName|default}" + // ParseEnv parse env in string value and default value. like: "${EnvName}" "${EnvName|default}" ParseEnv bool // ParseTime parses a duration string to time.Duration // eg: 10s, 2m ParseTime bool // Readonly config is readonly Readonly bool + // ParseDefault tag on binding data to struct. tag: default + ParseDefault bool // EnableCache enable config data cache EnableCache bool // ParseKey parse key path, allow find value by key path. eg: 'key.sub' will find `map[key]sub` ParseKey bool // TagName tag name for binding data to struct // - // Tips: please set tag name by DecoderConfig + // Deprecated: please set tag name by DecoderConfig, or use SetTagName() TagName string // Delimiter the delimiter char for split key path, if `FindByPath=true`. default is '.' Delimiter byte @@ -45,8 +49,8 @@ type Options struct { DecoderConfig *mapstructure.DecoderConfig // HookFunc on data changed. you can do something... HookFunc HookFunc - // ParseDefault tag on binding data to struct. tag: default - ParseDefault bool + // MergeOptions settings for merge two data + MergeOptions []func(*mergo.Config) // WatchChange bool } @@ -63,6 +67,10 @@ func newDefaultOption() *Options { ReadFormat: JSON, // struct decoder config DecoderConfig: newDefaultDecoderConfig(""), + MergeOptions: []func(*mergo.Config){ + mergo.WithOverride, + mergo.WithTypeCheck, + }, } } @@ -79,6 +87,12 @@ func newDefaultDecoderConfig(tagName string) *mapstructure.DecoderConfig { } } +// SetTagName for mapping data to struct +func (o *Options) SetTagName(tagName string) { + o.TagName = tagName + o.DecoderConfig.TagName = tagName +} + func (o *Options) shouldAddHookFunc() bool { return o.ParseTime || o.ParseEnv } @@ -113,8 +127,7 @@ func (o *Options) makeDecoderConfig() *mapstructure.DecoderConfig { // WithTagName set tag name for export to struct func WithTagName(tagName string) func(*Options) { return func(opts *Options) { - opts.TagName = tagName - opts.DecoderConfig.TagName = tagName + opts.SetTagName(tagName) } } @@ -137,15 +150,12 @@ func Delimiter(sep byte) func(*Options) { } } -// SaveFileOnSet set hook func +// SaveFileOnSet set hook func, will panic on save error func SaveFileOnSet(fileName string, format string) func(options *Options) { return func(opts *Options) { opts.HookFunc = func(event string, c *Config) { if strings.HasPrefix(event, "set.") { - err := c.DumpToFile(fileName, format) - if err != nil { - panic(err) - } + goutil.PanicErr(c.DumpToFile(fileName, format)) } } } diff --git a/vendor/github.com/gookit/goutil/README.md b/vendor/github.com/gookit/goutil/README.md index b9747fa47a..22cd6255ef 100644 --- a/vendor/github.com/gookit/goutil/README.md +++ b/vendor/github.com/gookit/goutil/README.md @@ -664,7 +664,6 @@ func SizeToString(size uint64) string func StringToByte(sizeStr string) uint64 func ParseByte(sizeStr string) uint64 func PrettyJSON(v any) (string, error) -func StringsToInts(ss []string) (ints []int, err error) func ArgsWithSpaces(vs []any) (message string) // source at fmtutil/time.go func HowLongAgo(sec int64) string @@ -909,6 +908,8 @@ func SimpleMerge(src, dst map[string]any) map[string]any func DeepMerge(src, dst map[string]any, deep int) map[string]any func MergeSMap(src, dst map[string]string, ignoreCase bool) map[string]string func MergeStringMap(src, dst map[string]string, ignoreCase bool) map[string]string +func MergeMultiSMap(mps ...map[string]string) map[string]string +func FilterSMap(sm map[string]string) map[string]string func MakeByPath(path string, val any) (mp map[string]any) func MakeByKeys(keys []string, val any) (mp map[string]any) // source at maputil/setval.go @@ -942,36 +943,49 @@ func MaxI64(x, y int64) int64 func SwapMaxI64(x, y int64) (int64, int64) func MaxFloat(x, y float64) float64 // source at mathutil/convert.go +func NewConvOption[T any](optFns ...ConvOptionFn[T]) *ConvOption[T] +func WithNilAsFail[T any](opt *ConvOption[T]) +func WithHandlePtr[T any](opt *ConvOption[T]) +func WithUserConvFn[T any](fn ToTypeFunc[T]) ConvOptionFn[T] func Int(in any) (int, error) func SafeInt(in any) int func QuietInt(in any) int -func MustInt(in any) int func IntOrPanic(in any) int +func MustInt(in any) int func IntOrDefault(in any, defVal int) int func IntOr(in any, defVal int) int -func IntOrErr(in any) (iVal int, err error) -func ToInt(in any) (iVal int, err error) -func ToIntWithFunc(in any, usrFn ToIntFunc) (iVal int, err error) +func IntOrErr(in any) (int, error) +func ToInt(in any) (int, error) +func ToIntWith(in any, optFns ...ConvOptionFn[int]) (iVal int, err error) func StrInt(s string) int func StrIntOr(s string, defVal int) int -func Uint(in any) (uint64, error) -func SafeUint(in any) uint64 -func QuietUint(in any) uint64 -func MustUint(in any) uint64 -func UintOrDefault(in any, defVal uint64) uint64 -func UintOr(in any, defVal uint64) uint64 -func UintOrErr(in any) (uint64, error) -func ToUint(in any) (u64 uint64, err error) -func ToUintWithFunc(in any, usrFn ToUintFunc) (u64 uint64, err error) func Int64(in any) (int64, error) func SafeInt64(in any) int64 func QuietInt64(in any) int64 func MustInt64(in any) int64 func Int64OrDefault(in any, defVal int64) int64 func Int64Or(in any, defVal int64) int64 +func ToInt64(in any) (int64, error) func Int64OrErr(in any) (int64, error) -func ToInt64(in any) (i64 int64, err error) -func ToInt64WithFunc(in any, usrFn ToInt64Func) (i64 int64, err error) +func ToInt64With(in any, optFns ...ConvOptionFn[int64]) (i64 int64, err error) +func Uint(in any) (uint, error) +func SafeUint(in any) uint +func QuietUint(in any) uint +func MustUint(in any) uint +func UintOrDefault(in any, defVal uint) uint +func UintOr(in any, defVal uint) uint +func UintOrErr(in any) (uint, error) +func ToUint(in any) (u64 uint, err error) +func ToUintWith(in any, optFns ...ConvOptionFn[uint]) (uVal uint, err error) +func Uint64(in any) (uint64, error) +func QuietUint64(in any) uint64 +func SafeUint64(in any) uint64 +func MustUint64(in any) uint64 +func Uint64OrDefault(in any, defVal uint64) uint64 +func Uint64Or(in any, defVal uint64) uint64 +func Uint64OrErr(in any) (uint64, error) +func ToUint64(in any) (uint64, error) +func ToUint64With(in any, optFns ...ConvOptionFn[uint64]) (u64 uint64, err error) func QuietFloat(in any) float64 func SafeFloat(in any) float64 func FloatOrPanic(in any) float64 @@ -980,8 +994,8 @@ func FloatOrDefault(in any, defVal float64) float64 func FloatOr(in any, defVal float64) float64 func Float(in any) (float64, error) func FloatOrErr(in any) (float64, error) -func ToFloat(in any) (f64 float64, err error) -func ToFloatWithFunc(in any, usrFn ToFloatFunc) (f64 float64, err error) +func ToFloat(in any) (float64, error) +func ToFloatWith(in any, optFns ...ConvOptionFn[float64]) (f64 float64, err error) func MustString(val any) string func StringOrPanic(val any) string func StringOrDefault(val any, defVal string) string @@ -991,10 +1005,8 @@ func StringOrErr(val any) (string, error) func QuietString(val any) string func String(val any) string func SafeString(val any) string -func TryToString(val any, defaultAsErr bool) (str string, err error) -func ToStringWithFunc(val any, usrFn comdef.ToStringFunc) (str string, err error) -func Percent(val, total int) float64 -func ElapsedTime(startTime time.Time) string +func TryToString(val any, defaultAsErr bool) (string, error) +func ToStringWith(in any, optFns ...comfunc.ConvOptionFn) (string, error) // source at mathutil/format.go func DataSize(size uint64) string func HowLongAgo(sec int64) string @@ -1005,11 +1017,19 @@ func LessOr[T comdef.XintOrFloat](val, max, devVal T) T func LteOr[T comdef.XintOrFloat](val, max, devVal T) T func GreaterOr[T comdef.XintOrFloat](val, min, defVal T) T func GteOr[T comdef.XintOrFloat](val, min, defVal T) T +func Mul[T1, T2 comdef.XintOrFloat](a T1, b T2) float64 +func MulF2i(a, b float64) int +func Div[T1, T2 comdef.XintOrFloat](a T1, b T2) float64 +func DivInt[T comdef.Integer](a, b T) int +func DivF2i(a, b float64) int +func Percent(val, total int) float64 // source at mathutil/random.go func RandomInt(min, max int) int func RandInt(min, max int) int func RandIntWithSeed(min, max int, seed int64) int func RandomIntWithSeed(min, max int, seed int64) int +// source at mathutil/value.go +func New[T comdef.IntOrFloat](v T) *Num[T] ``` ### Reflects @@ -1223,7 +1243,7 @@ func MustString(val any) string func StringOrDefault(val any, defVal string) string func StringOr(val any, defVal string) string func AnyToString(val any, defaultAsErr bool) (s string, err error) -func ToStringWithFunc(val any, fbFn comdef.ToStringFunc) (str string, err error) +func ToStringWith(in any, optFns ...comfunc.ConvOptionFn) (string, error) func ToBool(s string) (bool, error) func QuietBool(s string) bool func SafeBool(s string) bool @@ -1238,8 +1258,8 @@ func QuietInt(s string) int func MustInt(s string) int func IntOrPanic(s string) int func Int64(s string) int64 -func SafeInt64(s string) int64 func QuietInt64(s string) int64 +func SafeInt64(s string) int64 func ToInt64(s string) (int64, error) func Int64OrDefault(s string, defVal int64) int64 func Int64Or(s string, defVal int64) int64 @@ -1401,9 +1421,6 @@ func OrHandle(s string, fn comdef.StringHandleFunc) string func Valid(ss ...string) string func Replaces(str string, pairs map[string]string) string func NewReplacer(pairs map[string]string) *strings.Replacer -func PrettyJSON(v any) (string, error) -func RenderTemplate(input string, data any, fns template.FuncMap, isFile ...bool) string -func RenderText(input string, data any, fns template.FuncMap, isFile ...bool) string func WrapTag(s, tag string) string func SubstrCount(s, substr string, params ...uint64) (int, error) ``` diff --git a/vendor/github.com/gookit/goutil/README.zh-CN.md b/vendor/github.com/gookit/goutil/README.zh-CN.md index 318d4976d6..aca933bed0 100644 --- a/vendor/github.com/gookit/goutil/README.zh-CN.md +++ b/vendor/github.com/gookit/goutil/README.zh-CN.md @@ -665,7 +665,6 @@ func SizeToString(size uint64) string func StringToByte(sizeStr string) uint64 func ParseByte(sizeStr string) uint64 func PrettyJSON(v any) (string, error) -func StringsToInts(ss []string) (ints []int, err error) func ArgsWithSpaces(vs []any) (message string) // source at fmtutil/time.go func HowLongAgo(sec int64) string @@ -910,6 +909,8 @@ func SimpleMerge(src, dst map[string]any) map[string]any func DeepMerge(src, dst map[string]any, deep int) map[string]any func MergeSMap(src, dst map[string]string, ignoreCase bool) map[string]string func MergeStringMap(src, dst map[string]string, ignoreCase bool) map[string]string +func MergeMultiSMap(mps ...map[string]string) map[string]string +func FilterSMap(sm map[string]string) map[string]string func MakeByPath(path string, val any) (mp map[string]any) func MakeByKeys(keys []string, val any) (mp map[string]any) // source at maputil/setval.go @@ -943,36 +944,49 @@ func MaxI64(x, y int64) int64 func SwapMaxI64(x, y int64) (int64, int64) func MaxFloat(x, y float64) float64 // source at mathutil/convert.go +func NewConvOption[T any](optFns ...ConvOptionFn[T]) *ConvOption[T] +func WithNilAsFail[T any](opt *ConvOption[T]) +func WithHandlePtr[T any](opt *ConvOption[T]) +func WithUserConvFn[T any](fn ToTypeFunc[T]) ConvOptionFn[T] func Int(in any) (int, error) func SafeInt(in any) int func QuietInt(in any) int -func MustInt(in any) int func IntOrPanic(in any) int +func MustInt(in any) int func IntOrDefault(in any, defVal int) int func IntOr(in any, defVal int) int -func IntOrErr(in any) (iVal int, err error) -func ToInt(in any) (iVal int, err error) -func ToIntWithFunc(in any, usrFn ToIntFunc) (iVal int, err error) +func IntOrErr(in any) (int, error) +func ToInt(in any) (int, error) +func ToIntWith(in any, optFns ...ConvOptionFn[int]) (iVal int, err error) func StrInt(s string) int func StrIntOr(s string, defVal int) int -func Uint(in any) (uint64, error) -func SafeUint(in any) uint64 -func QuietUint(in any) uint64 -func MustUint(in any) uint64 -func UintOrDefault(in any, defVal uint64) uint64 -func UintOr(in any, defVal uint64) uint64 -func UintOrErr(in any) (uint64, error) -func ToUint(in any) (u64 uint64, err error) -func ToUintWithFunc(in any, usrFn ToUintFunc) (u64 uint64, err error) func Int64(in any) (int64, error) func SafeInt64(in any) int64 func QuietInt64(in any) int64 func MustInt64(in any) int64 func Int64OrDefault(in any, defVal int64) int64 func Int64Or(in any, defVal int64) int64 +func ToInt64(in any) (int64, error) func Int64OrErr(in any) (int64, error) -func ToInt64(in any) (i64 int64, err error) -func ToInt64WithFunc(in any, usrFn ToInt64Func) (i64 int64, err error) +func ToInt64With(in any, optFns ...ConvOptionFn[int64]) (i64 int64, err error) +func Uint(in any) (uint, error) +func SafeUint(in any) uint +func QuietUint(in any) uint +func MustUint(in any) uint +func UintOrDefault(in any, defVal uint) uint +func UintOr(in any, defVal uint) uint +func UintOrErr(in any) (uint, error) +func ToUint(in any) (u64 uint, err error) +func ToUintWith(in any, optFns ...ConvOptionFn[uint]) (uVal uint, err error) +func Uint64(in any) (uint64, error) +func QuietUint64(in any) uint64 +func SafeUint64(in any) uint64 +func MustUint64(in any) uint64 +func Uint64OrDefault(in any, defVal uint64) uint64 +func Uint64Or(in any, defVal uint64) uint64 +func Uint64OrErr(in any) (uint64, error) +func ToUint64(in any) (uint64, error) +func ToUint64With(in any, optFns ...ConvOptionFn[uint64]) (u64 uint64, err error) func QuietFloat(in any) float64 func SafeFloat(in any) float64 func FloatOrPanic(in any) float64 @@ -981,8 +995,8 @@ func FloatOrDefault(in any, defVal float64) float64 func FloatOr(in any, defVal float64) float64 func Float(in any) (float64, error) func FloatOrErr(in any) (float64, error) -func ToFloat(in any) (f64 float64, err error) -func ToFloatWithFunc(in any, usrFn ToFloatFunc) (f64 float64, err error) +func ToFloat(in any) (float64, error) +func ToFloatWith(in any, optFns ...ConvOptionFn[float64]) (f64 float64, err error) func MustString(val any) string func StringOrPanic(val any) string func StringOrDefault(val any, defVal string) string @@ -992,10 +1006,8 @@ func StringOrErr(val any) (string, error) func QuietString(val any) string func String(val any) string func SafeString(val any) string -func TryToString(val any, defaultAsErr bool) (str string, err error) -func ToStringWithFunc(val any, usrFn comdef.ToStringFunc) (str string, err error) -func Percent(val, total int) float64 -func ElapsedTime(startTime time.Time) string +func TryToString(val any, defaultAsErr bool) (string, error) +func ToStringWith(in any, optFns ...comfunc.ConvOptionFn) (string, error) // source at mathutil/format.go func DataSize(size uint64) string func HowLongAgo(sec int64) string @@ -1006,11 +1018,19 @@ func LessOr[T comdef.XintOrFloat](val, max, devVal T) T func LteOr[T comdef.XintOrFloat](val, max, devVal T) T func GreaterOr[T comdef.XintOrFloat](val, min, defVal T) T func GteOr[T comdef.XintOrFloat](val, min, defVal T) T +func Mul[T1, T2 comdef.XintOrFloat](a T1, b T2) float64 +func MulF2i(a, b float64) int +func Div[T1, T2 comdef.XintOrFloat](a T1, b T2) float64 +func DivInt[T comdef.Integer](a, b T) int +func DivF2i(a, b float64) int +func Percent(val, total int) float64 // source at mathutil/random.go func RandomInt(min, max int) int func RandInt(min, max int) int func RandIntWithSeed(min, max int, seed int64) int func RandomIntWithSeed(min, max int, seed int64) int +// source at mathutil/value.go +func New[T comdef.IntOrFloat](v T) *Num[T] ``` ### Reflects @@ -1224,7 +1244,7 @@ func MustString(val any) string func StringOrDefault(val any, defVal string) string func StringOr(val any, defVal string) string func AnyToString(val any, defaultAsErr bool) (s string, err error) -func ToStringWithFunc(val any, fbFn comdef.ToStringFunc) (str string, err error) +func ToStringWith(in any, optFns ...comfunc.ConvOptionFn) (string, error) func ToBool(s string) (bool, error) func QuietBool(s string) bool func SafeBool(s string) bool @@ -1239,8 +1259,8 @@ func QuietInt(s string) int func MustInt(s string) int func IntOrPanic(s string) int func Int64(s string) int64 -func SafeInt64(s string) int64 func QuietInt64(s string) int64 +func SafeInt64(s string) int64 func ToInt64(s string) (int64, error) func Int64OrDefault(s string, defVal int64) int64 func Int64Or(s string, defVal int64) int64 @@ -1402,9 +1422,6 @@ func OrHandle(s string, fn comdef.StringHandleFunc) string func Valid(ss ...string) string func Replaces(str string, pairs map[string]string) string func NewReplacer(pairs map[string]string) *strings.Replacer -func PrettyJSON(v any) (string, error) -func RenderTemplate(input string, data any, fns template.FuncMap, isFile ...bool) string -func RenderText(input string, data any, fns template.FuncMap, isFile ...bool) string func WrapTag(s, tag string) string func SubstrCount(s, substr string, params ...uint64) (int, error) ``` diff --git a/vendor/github.com/gookit/goutil/comdef/comdef.go b/vendor/github.com/gookit/goutil/comdef/comdef.go index 54a16687a9..d1e0ef238d 100644 --- a/vendor/github.com/gookit/goutil/comdef/comdef.go +++ b/vendor/github.com/gookit/goutil/comdef/comdef.go @@ -9,6 +9,9 @@ type ( UnmarshalFunc func(bts []byte, ptr any) error ) +// ToTypeFunc convert value to defined type +type ToTypeFunc[T any] func(any) (T, error) + // IntCheckFunc check func type IntCheckFunc func(val int) error diff --git a/vendor/github.com/gookit/goutil/comdef/errors.go b/vendor/github.com/gookit/goutil/comdef/errors.go index c210de321c..c2f68aa943 100644 --- a/vendor/github.com/gookit/goutil/comdef/errors.go +++ b/vendor/github.com/gookit/goutil/comdef/errors.go @@ -1,6 +1,38 @@ package comdef -import "errors" +import ( + "errors" + "strings" +) // ErrConvType error var ErrConvType = errors.New("convert value type error") + +// Errors multi error list +type Errors []error + +// Error string +func (es Errors) Error() string { + var sb strings.Builder + for _, err := range es { + sb.WriteString(err.Error()) + sb.WriteByte('\n') + } + return sb.String() +} + +// ErrOrNil error +func (es Errors) ErrOrNil() error { + if len(es) == 0 { + return nil + } + return es +} + +// First error +func (es Errors) First() error { + if len(es) > 0 { + return es[0] + } + return nil +} diff --git a/vendor/github.com/gookit/goutil/comdef/interface.go b/vendor/github.com/gookit/goutil/comdef/interface.go index 7718f4263f..3f3fb81459 100644 --- a/vendor/github.com/gookit/goutil/comdef/interface.go +++ b/vendor/github.com/gookit/goutil/comdef/interface.go @@ -24,6 +24,11 @@ type Int64able interface { Int64() (int64, error) } +// Float64able interface +type Float64able interface { + Float64() (float64, error) +} + // // // Matcher type diff --git a/vendor/github.com/gookit/goutil/conv.go b/vendor/github.com/gookit/goutil/conv.go index 6e97c1a48b..b0a0c069c8 100644 --- a/vendor/github.com/gookit/goutil/conv.go +++ b/vendor/github.com/gookit/goutil/conv.go @@ -57,17 +57,28 @@ func ToInt64(v any) (int64, error) { return mathutil.ToInt64(v) } -// Uint convert value to uint64 -func Uint(v any) uint64 { +// Uint convert value to uint +func Uint(v any) uint { iv, _ := mathutil.ToUint(v) return iv } -// ToUint try to convert value to uint64 -func ToUint(v any) (uint64, error) { +// ToUint try to convert value to uint +func ToUint(v any) (uint, error) { return mathutil.ToUint(v) } +// Uint64 convert value to uint64 +func Uint64(v any) uint64 { + iv, _ := mathutil.ToUint64(v) + return iv +} + +// ToUint64 try to convert value to uint64 +func ToUint64(v any) (uint64, error) { + return mathutil.ToUint64(v) +} + // BoolString convert bool to string func BoolString(bl bool) string { return strconv.FormatBool(bl) @@ -166,12 +177,12 @@ func ToKind(val any, kind reflect.Kind, fbFunc func(val any) (any, error)) (newV newVal = dstV } case reflect.Uint: - var dstV uint64 + var dstV uint if dstV, err = mathutil.ToUint(val); err == nil { - newVal = uint(dstV) + newVal = dstV } case reflect.Uint8: - var dstV uint64 + var dstV uint if dstV, err = mathutil.ToUint(val); err == nil { if dstV > math.MaxUint8 { return nil, fmt.Errorf("value overflow uint8. val: %v", val) @@ -179,7 +190,7 @@ func ToKind(val any, kind reflect.Kind, fbFunc func(val any) (any, error)) (newV newVal = uint8(dstV) } case reflect.Uint16: - var dstV uint64 + var dstV uint if dstV, err = mathutil.ToUint(val); err == nil { if dstV > math.MaxUint16 { return nil, fmt.Errorf("value overflow uint16. val: %v", val) @@ -187,7 +198,7 @@ func ToKind(val any, kind reflect.Kind, fbFunc func(val any) (any, error)) (newV newVal = uint16(dstV) } case reflect.Uint32: - var dstV uint64 + var dstV uint if dstV, err = mathutil.ToUint(val); err == nil { if dstV > math.MaxUint32 { return nil, fmt.Errorf("value overflow uint32. val: %v", val) @@ -196,7 +207,7 @@ func ToKind(val any, kind reflect.Kind, fbFunc func(val any) (any, error)) (newV } case reflect.Uint64: var dstV uint64 - if dstV, err = mathutil.ToUint(val); err == nil { + if dstV, err = mathutil.ToUint64(val); err == nil { newVal = dstV } case reflect.Float32: diff --git a/vendor/github.com/gookit/goutil/encodes/encodes.go b/vendor/github.com/gookit/goutil/encodes/encodes.go index 306325ea20..68da3bcfce 100644 --- a/vendor/github.com/gookit/goutil/encodes/encodes.go +++ b/vendor/github.com/gookit/goutil/encodes/encodes.go @@ -1,3 +1,4 @@ +// Package encodes provide some util for encode/decode data package encodes import ( diff --git a/vendor/github.com/gookit/goutil/fsutil/opwrite.go b/vendor/github.com/gookit/goutil/fsutil/opwrite.go index 64e3a1bacd..09fd2fcfd7 100644 --- a/vendor/github.com/gookit/goutil/fsutil/opwrite.go +++ b/vendor/github.com/gookit/goutil/fsutil/opwrite.go @@ -57,7 +57,8 @@ func TempDir(dir, pattern string) (string, error) { // MustSave create file and write contents to file, panic on error. // -// data type allow: string, []byte, io.Reader +// - data type allow: string, []byte, io.Reader +// // default option see NewOpenOption() func MustSave(filePath string, data any, optFns ...OpenOptionFunc) { basefn.MustOK(SaveFile(filePath, data, optFns...)) @@ -65,6 +66,8 @@ func MustSave(filePath string, data any, optFns ...OpenOptionFunc) { // SaveFile create file and write contents to file. will auto create dir. // +// - data type allow: string, []byte, io.Reader +// // default option see NewOpenOption() func SaveFile(filePath string, data any, optFns ...OpenOptionFunc) error { opt := NewOpenOption(optFns...) diff --git a/vendor/github.com/gookit/goutil/func.go b/vendor/github.com/gookit/goutil/func.go index 285e83fac6..a68a418811 100644 --- a/vendor/github.com/gookit/goutil/func.go +++ b/vendor/github.com/gookit/goutil/func.go @@ -46,7 +46,7 @@ func SafeRun(fn func()) (err error) { return nil } -// SafeRun sync run a func with error. +// SafeRunWithError sync run a func with error. // If the func panics, the panic value is returned as an error. func SafeRunWithError(fn func() error) (err error) { defer func() { diff --git a/vendor/github.com/gookit/goutil/internal/checkfn/check.go b/vendor/github.com/gookit/goutil/internal/checkfn/check.go index b6c5629867..3562047848 100644 --- a/vendor/github.com/gookit/goutil/internal/checkfn/check.go +++ b/vendor/github.com/gookit/goutil/internal/checkfn/check.go @@ -1,11 +1,10 @@ package checkfn import ( + "bytes" "fmt" "reflect" "strings" - - "github.com/gookit/goutil/reflects" ) // IsNil value check @@ -13,15 +12,46 @@ func IsNil(v any) bool { if v == nil { return true } - return reflects.IsNil(reflect.ValueOf(v)) + + rv := reflect.ValueOf(v) + switch rv.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return rv.IsNil() + default: + return false + } } -// IsEmpty value check -func IsEmpty(v any) bool { - if v == nil { +// IsSimpleKind kind in: string, bool, intX, uintX, floatX +func IsSimpleKind(k reflect.Kind) bool { + if reflect.String == k { return true } - return reflects.IsEmpty(reflect.ValueOf(v)) + return k > reflect.Invalid && k <= reflect.Float64 +} + +// IsEqual determines if two objects are considered equal. +// +// TIP: cannot compare function type +func IsEqual(src, dst any) bool { + if src == nil || dst == nil { + return src == dst + } + + bs1, ok := src.([]byte) + if !ok { + return reflect.DeepEqual(src, dst) + } + + bs2, ok := dst.([]byte) + if !ok { + return false + } + + if bs1 == nil || bs2 == nil { + return bs1 == nil && bs2 == nil + } + return bytes.Equal(bs1, bs2) } // Contains try loop over the data check if the data includes the element. @@ -57,7 +87,7 @@ func Contains(data, elem any) (valid, found bool) { if dataKind == reflect.Map { mapKeys := dataRv.MapKeys() for i := 0; i < len(mapKeys); i++ { - if reflects.IsEqual(mapKeys[i].Interface(), elem) { + if IsEqual(mapKeys[i].Interface(), elem) { return true, true } } @@ -70,7 +100,7 @@ func Contains(data, elem any) (valid, found bool) { } for i := 0; i < dataRv.Len(); i++ { - if reflects.IsEqual(dataRv.Index(i).Interface(), elem) { + if IsEqual(dataRv.Index(i).Interface(), elem) { return true, true } } diff --git a/vendor/github.com/gookit/goutil/internal/comfunc/comfunc.go b/vendor/github.com/gookit/goutil/internal/comfunc/comfunc.go index 8a3206131d..5b2ad153a9 100644 --- a/vendor/github.com/gookit/goutil/internal/comfunc/comfunc.go +++ b/vendor/github.com/gookit/goutil/internal/comfunc/comfunc.go @@ -16,7 +16,6 @@ func Environ() map[string]string { for _, str := range envList { nodes := strings.SplitN(str, "=", 2) - if len(nodes) < 2 { envMap[nodes[0]] = "" } else { diff --git a/vendor/github.com/gookit/goutil/internal/comfunc/convert.go b/vendor/github.com/gookit/goutil/internal/comfunc/convert.go index c3a015e82e..3d4f3b278e 100644 --- a/vendor/github.com/gookit/goutil/internal/comfunc/convert.go +++ b/vendor/github.com/gookit/goutil/internal/comfunc/convert.go @@ -2,9 +2,13 @@ package comfunc import ( "fmt" + "reflect" + "strconv" "strings" + "time" "github.com/gookit/goutil/comdef" + "github.com/gookit/goutil/internal/checkfn" ) // Bool try to convert type to bool @@ -60,3 +64,117 @@ func FormatWithArgs(fmtAndArgs []any) string { } return fmt.Sprint(fmtAndArgs...) } + +// ConvOption convert options +type ConvOption struct { + // if ture: value is nil, will return convert error; + // if false(default): value is nil, will convert to zero value + NilAsFail bool + // HandlePtr auto convert ptr type(int,float,string) value. eg: *int to int + // - if true: will use real type try convert. default is false + // - NOTE: current T type's ptr is default support. + HandlePtr bool + // set custom fallback convert func for not supported type. + UserConvFn comdef.ToStringFunc +} + +// ConvOptionFn convert option func +type ConvOptionFn func(opt *ConvOption) + +// StrBySprintFn convert any value to string by fmt.Sprint +var StrBySprintFn = func(v any) (string, error) { + return fmt.Sprint(v), nil +} + +// WithHandlePtr set ConvOption.HandlePtr option +func WithHandlePtr(opt *ConvOption) { + opt.HandlePtr = true +} + +// WithUserConvFn set ConvOption.UserConvFn option +func WithUserConvFn(fn comdef.ToStringFunc) ConvOptionFn { + return func(opt *ConvOption) { + opt.UserConvFn = fn + } +} + +// NewConvOption create a new ConvOption +func NewConvOption(optFns ...ConvOptionFn) *ConvOption { + opt := &ConvOption{} + opt.WithOption(optFns...) + return opt +} + +// WithOption set convert option +func (opt *ConvOption) WithOption(optFns ...ConvOptionFn) { + for _, fn := range optFns { + if fn != nil { + fn(opt) + } + } +} + +// ToStringWith try to convert value to string. can with some option func, more see ConvOption. +func ToStringWith(in any, optFns ...ConvOptionFn) (str string, err error) { + opt := NewConvOption(optFns...) + if !opt.NilAsFail && in == nil { + return "", nil + } + + switch value := in.(type) { + case int: + str = strconv.Itoa(value) + case int8: + str = strconv.Itoa(int(value)) + case int16: + str = strconv.Itoa(int(value)) + case int32: // same as `rune` + str = strconv.Itoa(int(value)) + case int64: + str = strconv.FormatInt(value, 10) + case uint: + str = strconv.FormatUint(uint64(value), 10) + case uint8: + str = strconv.FormatUint(uint64(value), 10) + case uint16: + str = strconv.FormatUint(uint64(value), 10) + case uint32: + str = strconv.FormatUint(uint64(value), 10) + case uint64: + str = strconv.FormatUint(value, 10) + case float32: + str = strconv.FormatFloat(float64(value), 'f', -1, 32) + case float64: + str = strconv.FormatFloat(value, 'f', -1, 64) + case bool: + str = strconv.FormatBool(value) + case string: + str = value + case *string: + str = *value + case []byte: + str = string(value) + case time.Duration: + str = strconv.FormatInt(int64(value), 10) + case fmt.Stringer: + str = value.String() + case error: + str = value.Error() + default: + if opt.HandlePtr { + if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer { + rv = rv.Elem() + if checkfn.IsSimpleKind(rv.Kind()) { + return ToStringWith(rv.Interface(), optFns...) + } + } + } + + if opt.UserConvFn != nil { + str, err = opt.UserConvFn(in) + } else { + err = comdef.ErrConvType + } + } + return +} diff --git a/vendor/github.com/gookit/goutil/internal/varexpr/varexpr.go b/vendor/github.com/gookit/goutil/internal/varexpr/varexpr.go index 3c400f9cb7..bc701008db 100644 --- a/vendor/github.com/gookit/goutil/internal/varexpr/varexpr.go +++ b/vendor/github.com/gookit/goutil/internal/varexpr/varexpr.go @@ -20,8 +20,14 @@ import ( "strings" ) -// SepChar separator char -const SepChar = "|" +const ( + // SepChar separator char split var name and default value + SepChar = "|" + VarLeft = "${" // default var left format chars + VarRight = "}" // default var right format chars + + mustPrefix = '?' // must prefix char +) // ParseOptFn option func type ParseOptFn func(o *ParseOpts) @@ -34,8 +40,15 @@ type ParseOpts struct { ParseFn func(string) (string, error) // Regexp custom expression regex. Regexp *regexp.Regexp - // Keyword check chars for expression. default is "${" - Keyword string + // var format chars for expression. + // default left="${", right="}" + VarLeft, VarRight string +} + +func (opt *ParseOpts) useDefaultRegex() { + opt.Regexp = envRegex + opt.VarLeft = VarLeft + opt.VarRight = VarRight } // must add "?" - To ensure that there is no greedy match @@ -71,15 +84,12 @@ type Parser struct { // New create a new Parser func New(optFns ...ParseOptFn) *Parser { - opts := &ParseOpts{ - Getter: os.Getenv, - Regexp: envRegex, - Keyword: "${", - } + opts := &ParseOpts{Getter: os.Getenv} + opts.useDefaultRegex() + for _, fn := range optFns { fn(opts) } - return &Parser{ParseOpts: *opts} } @@ -92,13 +102,19 @@ func New(optFns ...ParseOptFn) *Parser { // ${var_name | ?error} With error on value is empty. func (p *Parser) Parse(val string) (newVal string, err error) { if p.Regexp == nil { - p.Regexp = envRegex + p.useDefaultRegex() } - if p.Keyword != "" && !strings.Contains(val, p.Keyword) { + times := strings.Count(val, p.VarLeft) + if times == 0 { return val, nil } + // enhance: see https://github.com/gookit/goutil/issues/135 + if times == 1 && strings.HasPrefix(val, p.VarLeft) && strings.HasSuffix(val, p.VarRight) { + return p.parseOne(val) + } + // parse expression newVal = p.Regexp.ReplaceAllStringFunc(val, func(s string) string { if err != nil { @@ -116,11 +132,11 @@ func (p *Parser) parseOne(eVar string) (val string, err error) { return p.ParseFn(eVar) } - // eVar like "${NotExist|defValue}", first remove "${" and "}", then split it + // like "${NotExist | defValue}". first remove "${" and "}", then split it ss := strings.SplitN(eVar[2:len(eVar)-1], SepChar, 2) var name, def string - // with default value. ${NotExist|defValue} + // with default value. if len(ss) == 2 { name, def = strings.TrimSpace(ss[0]), strings.TrimSpace(ss[1]) } else { @@ -131,7 +147,7 @@ func (p *Parser) parseOne(eVar string) (val string, err error) { val = p.Getter(name) if val == "" && def != "" { // check def is "?error" - if def[0] == '?' { + if def[0] == mustPrefix { msg := "value is required for var: " + name if len(def) > 1 { msg = def[1:] diff --git a/vendor/github.com/gookit/goutil/maputil/data.go b/vendor/github.com/gookit/goutil/maputil/data.go index 07008e78f6..b284d88924 100644 --- a/vendor/github.com/gookit/goutil/maputil/data.go +++ b/vendor/github.com/gookit/goutil/maputil/data.go @@ -20,8 +20,8 @@ func (d Data) Has(key string) bool { return ok } -// IsEmtpy if the data map -func (d Data) IsEmtpy() bool { +// IsEmpty if the data map +func (d Data) IsEmpty() bool { return len(d) == 0 } @@ -127,13 +127,21 @@ func (d Data) Int64(key string) int64 { } // Uint value get -func (d Data) Uint(key string) uint64 { +func (d Data) Uint(key string) uint { if val, ok := d.GetByPath(key); ok { return mathutil.QuietUint(val) } return 0 } +// Uint64 value get +func (d Data) Uint64(key string) uint64 { + if val, ok := d.GetByPath(key); ok { + return mathutil.QuietUint64(val) + } + return 0 +} + // Str value get by key func (d Data) Str(key string) string { if val, ok := d.GetByPath(key); ok { @@ -223,6 +231,16 @@ func (d Data) Sub(key string) Data { return nil } +// Slice get []any value from data map +func (d Data) Slice(key string) ([]any, error) { + val, ok := d.GetByPath(key) + if !ok { + return nil, nil + } + + return arrutil.AnyToSlice(val) +} + // Keys of the data map func (d Data) Keys() []string { keys := make([]string, 0, len(d)) diff --git a/vendor/github.com/gookit/goutil/maputil/maputil.go b/vendor/github.com/gookit/goutil/maputil/maputil.go index 96247fd92f..795860164a 100644 --- a/vendor/github.com/gookit/goutil/maputil/maputil.go +++ b/vendor/github.com/gookit/goutil/maputil/maputil.go @@ -16,8 +16,7 @@ const ( KeySepChar = '.' ) -// SimpleMerge simple merge two data map by string key. -// will merge the src to dst map +// SimpleMerge simple merge two data map by string key. will merge the src to dst map func SimpleMerge(src, dst map[string]any) map[string]any { if len(src) == 0 { return dst @@ -66,6 +65,27 @@ func MergeStringMap(src, dst map[string]string, ignoreCase bool) map[string]stri return dst } +// MergeMultiSMap quick merge multi string-map data. +func MergeMultiSMap(mps ...map[string]string) map[string]string { + newMp := make(map[string]string) + for _, mp := range mps { + for k, v := range mp { + newMp[k] = v + } + } + return newMp +} + +// FilterSMap filter empty elem for the string map. +func FilterSMap(sm map[string]string) map[string]string { + for key, val := range sm { + if val == "" { + delete(sm, key) + } + } + return sm +} + // MakeByPath build new value by key names // // Example: diff --git a/vendor/github.com/gookit/goutil/maputil/smap.go b/vendor/github.com/gookit/goutil/maputil/smap.go index bcc74cc5e3..8905825b5f 100644 --- a/vendor/github.com/gookit/goutil/maputil/smap.go +++ b/vendor/github.com/gookit/goutil/maputil/smap.go @@ -105,6 +105,20 @@ func (m SMap) Strings(key string) (ss []string) { return } +// IfExist key, then call the fn with value. +func (m SMap) IfExist(key string, fn func(val string)) { + if val, ok := m[key]; ok { + fn(val) + } +} + +// IfValid value is not empty, then call the fn +func (m SMap) IfValid(key string, fn func(val string)) { + if val, ok := m[key]; ok && val != "" { + fn(val) + } +} + // Keys of the string-map func (m SMap) Keys() []string { keys := make([]string, 0, len(m)) diff --git a/vendor/github.com/gookit/goutil/mathutil/convert.go b/vendor/github.com/gookit/goutil/mathutil/convert.go index 1d405714a1..0369519f23 100644 --- a/vendor/github.com/gookit/goutil/mathutil/convert.go +++ b/vendor/github.com/gookit/goutil/mathutil/convert.go @@ -3,11 +3,14 @@ package mathutil import ( "fmt" "math" + "reflect" "strconv" "strings" "time" "github.com/gookit/goutil/comdef" + "github.com/gookit/goutil/internal/checkfn" + "github.com/gookit/goutil/internal/comfunc" ) // ToIntFunc convert value to int @@ -17,19 +20,76 @@ type ToIntFunc func(any) (int, error) type ToInt64Func func(any) (int64, error) // ToUintFunc convert value to uint -type ToUintFunc func(any) (uint64, error) +type ToUintFunc func(any) (uint, error) + +// ToUint64Func convert value to uint +type ToUint64Func func(any) (uint64, error) // ToFloatFunc convert value to float type ToFloatFunc func(any) (float64, error) +// ToTypeFunc convert value to defined type +type ToTypeFunc[T any] func(any) (T, error) + +// ConvOption convert options +type ConvOption[T any] struct { + // if ture: value is nil, will return convert error; + // if false(default): value is nil, will convert to zero value + NilAsFail bool + // HandlePtr auto convert ptr type(int,float,string) value. eg: *int to int + // - if true: will use real type try convert. default is false + // - NOTE: current T type's ptr is default support. + HandlePtr bool + // set custom fallback convert func for not supported type. + UserConvFn ToTypeFunc[T] +} + +// NewConvOption create a new ConvOption +func NewConvOption[T any](optFns ...ConvOptionFn[T]) *ConvOption[T] { + opt := &ConvOption[T]{} + opt.WithOption(optFns...) + return opt +} + +// WithOption set convert option +func (opt *ConvOption[T]) WithOption(optFns ...ConvOptionFn[T]) { + for _, fn := range optFns { + if fn != nil { + fn(opt) + } + } +} + +// ConvOptionFn convert option func +type ConvOptionFn[T any] func(opt *ConvOption[T]) + +// WithNilAsFail set ConvOption.NilAsFail option +// +// Example: +// +// ToIntWithFunc(val, mathutil.WithNilAsFail[int]) +func WithNilAsFail[T any](opt *ConvOption[T]) { + opt.NilAsFail = true +} + +// WithHandlePtr set ConvOption.HandlePtr option +func WithHandlePtr[T any](opt *ConvOption[T]) { + opt.HandlePtr = true +} + +// WithUserConvFn set ConvOption.UserConvFn option +func WithUserConvFn[T any](fn ToTypeFunc[T]) ConvOptionFn[T] { + return func(opt *ConvOption[T]) { + opt.UserConvFn = fn + } +} + /************************************************************* * convert value to int *************************************************************/ // Int convert value to int -func Int(in any) (int, error) { - return ToInt(in) -} +func Int(in any) (int, error) { return ToInt(in) } // SafeInt convert value to int, will ignore error func SafeInt(in any) int { @@ -38,12 +98,10 @@ func SafeInt(in any) int { } // QuietInt convert value to int, will ignore error -func QuietInt(in any) int { - return SafeInt(in) -} +func QuietInt(in any) int { return SafeInt(in) } -// MustInt convert value to int, will panic on error -func MustInt(in any) int { +// IntOrPanic convert value to int, will panic on error +func IntOrPanic(in any) int { val, err := ToInt(in) if err != nil { panic(err) @@ -51,19 +109,15 @@ func MustInt(in any) int { return val } -// IntOrPanic convert value to int, will panic on error -func IntOrPanic(in any) int { - return MustInt(in) -} +// MustInt convert value to int, will panic on error +func MustInt(in any) int { return IntOrPanic(in) } // IntOrDefault convert value to int, return defaultVal on failed -func IntOrDefault(in any, defVal int) int { - return IntOr(in, defVal) -} +func IntOrDefault(in any, defVal int) int { return IntOr(in, defVal) } // IntOr convert value to int, return defaultVal on failed func IntOr(in any, defVal int) int { - val, err := ToIntWithFunc(in, nil) + val, err := ToIntWith(in) if err != nil { return defVal } @@ -71,20 +125,28 @@ func IntOr(in any, defVal int) int { } // IntOrErr convert value to int, return error on failed -func IntOrErr(in any) (iVal int, err error) { - return ToIntWithFunc(in, nil) -} +func IntOrErr(in any) (int, error) { return ToIntWith(in) } // ToInt convert value to int, return error on failed -func ToInt(in any) (iVal int, err error) { - return ToIntWithFunc(in, nil) -} +func ToInt(in any) (int, error) { return ToIntWith(in) } + +// ToIntWith convert value to int, can with some option func. +// +// Example: +// +// ToIntWithFunc(val, mathutil.WithNilAsFail, mathutil.WithUserConvFn(func(in any) (int, error) { +// }) +func ToIntWith(in any, optFns ...ConvOptionFn[int]) (iVal int, err error) { + opt := NewConvOption[int](optFns...) + if !opt.NilAsFail && in == nil { + return 0, nil + } -// ToIntWithFunc convert value to int, will call usrFn on value type not supported. -func ToIntWithFunc(in any, usrFn ToIntFunc) (iVal int, err error) { switch tVal := in.(type) { case int: iVal = tVal + case *int: // default support int ptr type + iVal = *tVal case int8: iVal = int(tVal) case int16: @@ -131,7 +193,7 @@ func ToIntWithFunc(in any, usrFn ToIntFunc) (iVal int, err error) { } case string: iVal, err = strconv.Atoi(strings.TrimSpace(tVal)) - case interface{ Int64() (int64, error) }: // eg: json.Number + case comdef.Int64able: // eg: json.Number var i64 int64 if i64, err = tVal.Int64(); err == nil { if i64 > math.MaxInt32 { @@ -141,11 +203,19 @@ func ToIntWithFunc(in any, usrFn ToIntFunc) (iVal int, err error) { } } default: - if usrFn != nil { - return usrFn(in) - } else { - err = comdef.ErrConvType + if opt.HandlePtr { + if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer { + rv = rv.Elem() + if checkfn.IsSimpleKind(rv.Kind()) { + return ToIntWith(rv.Interface(), optFns...) + } + } } + + if opt.UserConvFn != nil { + return opt.UserConvFn(in) + } + err = comdef.ErrConvType } return } @@ -165,29 +235,127 @@ func StrIntOr(s string, defVal int) int { return iVal } +/************************************************************* + * convert value to int64 + *************************************************************/ + +// Int64 convert value to int64, return error on failed +func Int64(in any) (int64, error) { return ToInt64(in) } + +// SafeInt64 convert value to int64, will ignore error +func SafeInt64(in any) int64 { + i64, _ := ToInt64With(in) + return i64 +} + +// QuietInt64 convert value to int64, will ignore error +func QuietInt64(in any) int64 { return SafeInt64(in) } + +// MustInt64 convert value to int64, will panic on error +func MustInt64(in any) int64 { + i64, err := ToInt64With(in) + if err != nil { + panic(err) + } + return i64 +} + +// Int64OrDefault convert value to int64, return default val on failed +func Int64OrDefault(in any, defVal int64) int64 { return Int64Or(in, defVal) } + +// Int64Or convert value to int64, return default val on failed +func Int64Or(in any, defVal int64) int64 { + i64, err := ToInt64With(in) + if err != nil { + return defVal + } + return i64 +} + +// ToInt64 convert value to int64, return error on failed +func ToInt64(in any) (int64, error) { return ToInt64With(in) } + +// Int64OrErr convert value to int64, return error on failed +func Int64OrErr(in any) (int64, error) { return ToInt64With(in) } + +// ToInt64With try to convert value to int64. can with some option func, more see ConvOption. +func ToInt64With(in any, optFns ...ConvOptionFn[int64]) (i64 int64, err error) { + opt := NewConvOption(optFns...) + if !opt.NilAsFail && in == nil { + return 0, nil + } + + switch tVal := in.(type) { + case string: + i64, err = strconv.ParseInt(strings.TrimSpace(tVal), 10, 0) + case int: + i64 = int64(tVal) + case int8: + i64 = int64(tVal) + case int16: + i64 = int64(tVal) + case int32: + i64 = int64(tVal) + case int64: + i64 = tVal + case *int64: // default support int64 ptr type + i64 = *tVal + case uint: + i64 = int64(tVal) + case uint8: + i64 = int64(tVal) + case uint16: + i64 = int64(tVal) + case uint32: + i64 = int64(tVal) + case uint64: + i64 = int64(tVal) + case float32: + i64 = int64(tVal) + case float64: + i64 = int64(tVal) + case time.Duration: + i64 = int64(tVal) + case comdef.Int64able: // eg: json.Number + i64, err = tVal.Int64() + default: + if opt.HandlePtr { + if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer { + rv = rv.Elem() + if checkfn.IsSimpleKind(rv.Kind()) { + return ToInt64With(rv.Interface(), optFns...) + } + } + } + + if opt.UserConvFn != nil { + i64, err = opt.UserConvFn(in) + } else { + err = comdef.ErrConvType + } + } + return +} + /************************************************************* * convert value to uint *************************************************************/ // Uint convert any to uint, return error on failed -func Uint(in any) (uint64, error) { - return ToUint(in) -} +func Uint(in any) (uint, error) { return ToUint(in) } // SafeUint convert any to uint, will ignore error -func SafeUint(in any) uint64 { +func SafeUint(in any) uint { val, _ := ToUint(in) return val } // QuietUint convert any to uint, will ignore error -func QuietUint(in any) uint64 { - return SafeUint(in) -} +func QuietUint(in any) uint { return SafeUint(in) } // MustUint convert any to uint, will panic on error -func MustUint(in any) uint64 { - val, err := ToUintWithFunc(in, nil) +func MustUint(in any) uint { + val, err := ToUintWith(in) if err != nil { panic(err) } @@ -195,13 +363,11 @@ func MustUint(in any) uint64 { } // UintOrDefault convert any to uint, return default val on failed -func UintOrDefault(in any, defVal uint64) uint64 { - return UintOr(in, defVal) -} +func UintOrDefault(in any, defVal uint) uint { return UintOr(in, defVal) } // UintOr convert any to uint, return default val on failed -func UintOr(in any, defVal uint64) uint64 { - val, err := ToUintWithFunc(in, nil) +func UintOr(in any, defVal uint) uint { + val, err := ToUintWith(in) if err != nil { return defVal } @@ -209,17 +375,124 @@ func UintOr(in any, defVal uint64) uint64 { } // UintOrErr convert value to uint, return error on failed -func UintOrErr(in any) (uint64, error) { - return ToUintWithFunc(in, nil) -} +func UintOrErr(in any) (uint, error) { return ToUintWith(in) } // ToUint convert value to uint, return error on failed -func ToUint(in any) (u64 uint64, err error) { - return ToUintWithFunc(in, nil) +func ToUint(in any) (u64 uint, err error) { return ToUintWith(in) } + +// ToUintWith try to convert value to uint. can with some option func, more see ConvOption. +func ToUintWith(in any, optFns ...ConvOptionFn[uint]) (uVal uint, err error) { + opt := NewConvOption(optFns...) + if !opt.NilAsFail && in == nil { + return 0, nil + } + + switch tVal := in.(type) { + case int: + uVal = uint(tVal) + case int8: + uVal = uint(tVal) + case int16: + uVal = uint(tVal) + case int32: + uVal = uint(tVal) + case int64: + uVal = uint(tVal) + case uint: + uVal = tVal + case *uint: // default support uint ptr type + uVal = *tVal + case uint8: + uVal = uint(tVal) + case uint16: + uVal = uint(tVal) + case uint32: + uVal = uint(tVal) + case uint64: + uVal = uint(tVal) + case float32: + uVal = uint(tVal) + case float64: + uVal = uint(tVal) + case time.Duration: + uVal = uint(tVal) + case comdef.Int64able: // eg: json.Number + var i64 int64 + i64, err = tVal.Int64() + uVal = uint(i64) + case string: + var u64 uint64 + u64, err = strconv.ParseUint(strings.TrimSpace(tVal), 10, 0) + uVal = uint(u64) + default: + if opt.HandlePtr { + if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer { + rv = rv.Elem() + if checkfn.IsSimpleKind(rv.Kind()) { + return ToUintWith(rv.Interface(), optFns...) + } + } + } + + if opt.UserConvFn != nil { + uVal, err = opt.UserConvFn(in) + } else { + err = comdef.ErrConvType + } + } + return } -// ToUintWithFunc convert value to uint, will call usrFn on value type not supported. -func ToUintWithFunc(in any, usrFn ToUintFunc) (u64 uint64, err error) { +/************************************************************* + * convert value to uint64 + *************************************************************/ + +// Uint64 convert any to uint64, return error on failed +func Uint64(in any) (uint64, error) { return ToUint64(in) } + +// QuietUint64 convert any to uint64, will ignore error +func QuietUint64(in any) uint64 { return SafeUint64(in) } + +// SafeUint64 convert any to uint64, will ignore error +func SafeUint64(in any) uint64 { + val, _ := ToUint64(in) + return val +} + +// MustUint64 convert any to uint64, will panic on error +func MustUint64(in any) uint64 { + val, err := ToUint64With(in) + if err != nil { + panic(err) + } + return val +} + +// Uint64OrDefault convert any to uint64, return default val on failed +func Uint64OrDefault(in any, defVal uint64) uint64 { return Uint64Or(in, defVal) } + +// Uint64Or convert any to uint64, return default val on failed +func Uint64Or(in any, defVal uint64) uint64 { + val, err := ToUint64With(in) + if err != nil { + return defVal + } + return val +} + +// Uint64OrErr convert value to uint64, return error on failed +func Uint64OrErr(in any) (uint64, error) { return ToUint64With(in) } + +// ToUint64 convert value to uint64, return error on failed +func ToUint64(in any) (uint64, error) { return ToUint64With(in) } + +// ToUint64With try to convert value to uint64. can with some option func, more see ConvOption. +func ToUint64With(in any, optFns ...ConvOptionFn[uint64]) (u64 uint64, err error) { + opt := NewConvOption(optFns...) + if !opt.NilAsFail && in == nil { + return 0, nil + } + switch tVal := in.(type) { case int: u64 = uint64(tVal) @@ -241,21 +514,32 @@ func ToUintWithFunc(in any, usrFn ToUintFunc) (u64 uint64, err error) { u64 = uint64(tVal) case uint64: u64 = tVal + case *uint64: // default support uint64 ptr type + u64 = *tVal case float32: u64 = uint64(tVal) case float64: u64 = uint64(tVal) case time.Duration: u64 = uint64(tVal) - case interface{ Int64() (int64, error) }: // eg: json.Number + case comdef.Int64able: // eg: json.Number var i64 int64 i64, err = tVal.Int64() u64 = uint64(i64) case string: u64, err = strconv.ParseUint(strings.TrimSpace(tVal), 10, 0) default: - if usrFn != nil { - u64, err = usrFn(in) + if opt.HandlePtr { + if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer { + rv = rv.Elem() + if checkfn.IsSimpleKind(rv.Kind()) { + return ToUint64With(rv.Interface(), optFns...) + } + } + } + + if opt.UserConvFn != nil { + u64, err = opt.UserConvFn(in) } else { err = comdef.ErrConvType } @@ -264,124 +548,24 @@ func ToUintWithFunc(in any, usrFn ToUintFunc) (u64 uint64, err error) { } /************************************************************* - * convert value to int64 - *************************************************************/ - -// Int64 convert value to int64, return error on failed -func Int64(in any) (int64, error) { - return ToInt64(in) -} - -// SafeInt64 convert value to int64, will ignore error -func SafeInt64(in any) int64 { - i64, _ := ToInt64WithFunc(in, nil) - return i64 -} - -// QuietInt64 convert value to int64, will ignore error -func QuietInt64(in any) int64 { - return SafeInt64(in) -} - -// MustInt64 convert value to int64, will panic on error -func MustInt64(in any) int64 { - i64, err := ToInt64WithFunc(in, nil) - if err != nil { - panic(err) - } - return i64 -} - -// Int64OrDefault convert value to int64, return default val on failed -func Int64OrDefault(in any, defVal int64) int64 { - return Int64Or(in, defVal) -} - -// Int64Or convert value to int64, return default val on failed -func Int64Or(in any, defVal int64) int64 { - i64, err := ToInt64WithFunc(in, nil) - if err != nil { - return defVal - } - return i64 -} - -// Int64OrErr convert value to int64, return error on failed -func Int64OrErr(in any) (int64, error) { - return ToInt64(in) -} - -// ToInt64 convert value to int64, return error on failed -func ToInt64(in any) (i64 int64, err error) { - return ToInt64WithFunc(in, nil) -} - -// ToInt64WithFunc convert value to int64, will call usrFn on value type not supported. -func ToInt64WithFunc(in any, usrFn ToInt64Func) (i64 int64, err error) { - switch tVal := in.(type) { - case string: - i64, err = strconv.ParseInt(strings.TrimSpace(tVal), 10, 0) - case int: - i64 = int64(tVal) - case int8: - i64 = int64(tVal) - case int16: - i64 = int64(tVal) - case int32: - i64 = int64(tVal) - case int64: - i64 = tVal - case uint: - i64 = int64(tVal) - case uint8: - i64 = int64(tVal) - case uint16: - i64 = int64(tVal) - case uint32: - i64 = int64(tVal) - case uint64: - i64 = int64(tVal) - case float32: - i64 = int64(tVal) - case float64: - i64 = int64(tVal) - case time.Duration: - i64 = int64(tVal) - case interface{ Int64() (int64, error) }: // eg: json.Number - i64, err = tVal.Int64() - default: - if usrFn != nil { - i64, err = usrFn(in) - } else { - err = comdef.ErrConvType - } - } - return -} - -/************************************************************* - * convert value to float + * convert value to float64 *************************************************************/ // QuietFloat convert value to float64, will ignore error. alias of SafeFloat -func QuietFloat(in any) float64 { - return SafeFloat(in) -} +func QuietFloat(in any) float64 { return SafeFloat(in) } // SafeFloat convert value to float64, will ignore error func SafeFloat(in any) float64 { - val, _ := ToFloatWithFunc(in, nil) + val, _ := ToFloatWith(in) return val } // FloatOrPanic convert value to float64, will panic on error -func FloatOrPanic(in any) float64 { - return MustFloat(in) -} +func FloatOrPanic(in any) float64 { return MustFloat(in) } // MustFloat convert value to float64, will panic on error func MustFloat(in any) float64 { - val, err := ToFloatWithFunc(in, nil) + val, err := ToFloatWith(in) if err != nil { panic(err) } @@ -389,13 +573,11 @@ func MustFloat(in any) float64 { } // FloatOrDefault convert value to float64, will return default value on error -func FloatOrDefault(in any, defVal float64) float64 { - return FloatOr(in, defVal) -} +func FloatOrDefault(in any, defVal float64) float64 { return FloatOr(in, defVal) } // FloatOr convert value to float64, will return default value on error func FloatOr(in any, defVal float64) float64 { - val, err := ToFloatWithFunc(in, nil) + val, err := ToFloatWith(in) if err != nil { return defVal } @@ -403,22 +585,21 @@ func FloatOr(in any, defVal float64) float64 { } // Float convert value to float64, return error on failed -func Float(in any) (float64, error) { - return ToFloatWithFunc(in, nil) -} +func Float(in any) (float64, error) { return ToFloatWith(in) } // FloatOrErr convert value to float64, return error on failed -func FloatOrErr(in any) (float64, error) { - return ToFloatWithFunc(in, nil) -} +func FloatOrErr(in any) (float64, error) { return ToFloatWith(in) } // ToFloat convert value to float64, return error on failed -func ToFloat(in any) (f64 float64, err error) { - return ToFloatWithFunc(in, nil) -} +func ToFloat(in any) (float64, error) { return ToFloatWith(in) } + +// ToFloatWith try to convert value to float64. can with some option func, more see ConvOption. +func ToFloatWith(in any, optFns ...ConvOptionFn[float64]) (f64 float64, err error) { + opt := NewConvOption(optFns...) + if !opt.NilAsFail && in == nil { + return 0, nil + } -// ToFloatWithFunc convert value to float64, will call usrFn if value type not supported. -func ToFloatWithFunc(in any, usrFn ToFloatFunc) (f64 float64, err error) { switch tVal := in.(type) { case string: f64, err = strconv.ParseFloat(strings.TrimSpace(tVal), 64) @@ -446,13 +627,24 @@ func ToFloatWithFunc(in any, usrFn ToFloatFunc) (f64 float64, err error) { f64 = float64(tVal) case float64: f64 = tVal + case *float64: // default support float64 ptr type + f64 = *tVal case time.Duration: f64 = float64(tVal) - case interface{ Float64() (float64, error) }: // eg: json.Number + case comdef.Float64able: // eg: json.Number f64, err = tVal.Float64() default: - if usrFn != nil { - f64, err = usrFn(in) + if opt.HandlePtr { + if rv := reflect.ValueOf(in); rv.Kind() == reflect.Pointer { + rv = rv.Elem() + if checkfn.IsSimpleKind(rv.Kind()) { + return ToFloatWith(rv.Interface(), optFns...) + } + } + } + + if opt.UserConvFn != nil { + f64, err = opt.UserConvFn(in) } else { err = comdef.ErrConvType } @@ -466,7 +658,7 @@ func ToFloatWithFunc(in any, usrFn ToFloatFunc) (f64 float64, err error) { // MustString convert intX/floatX value to string, will panic on error func MustString(val any) string { - str, err := ToStringWithFunc(val, nil) + str, err := ToStringWith(val) if err != nil { panic(err) } @@ -477,13 +669,11 @@ func MustString(val any) string { func StringOrPanic(val any) string { return MustString(val) } // StringOrDefault convert intX/floatX value to string, will return default value on error -func StringOrDefault(val any, defVal string) string { - return StringOr(val, defVal) -} +func StringOrDefault(val any, defVal string) string { return StringOr(val, defVal) } // StringOr convert intX/floatX value to string, will return default value on error func StringOr(val any, defVal string) string { - str, err := ToStringWithFunc(val, nil) + str, err := ToStringWith(val) if err != nil { return defVal } @@ -491,19 +681,13 @@ func StringOr(val any, defVal string) string { } // ToString convert intX/floatX value to string, return error on failed -func ToString(val any) (string, error) { - return ToStringWithFunc(val, nil) -} +func ToString(val any) (string, error) { return ToStringWith(val) } // StringOrErr convert intX/floatX value to string, return error on failed -func StringOrErr(val any) (string, error) { - return ToStringWithFunc(val, nil) -} +func StringOrErr(val any) (string, error) { return ToStringWith(val) } // QuietString convert intX/floatX value to string, other type convert by fmt.Sprint -func QuietString(val any) string { - return SafeString(val) -} +func QuietString(val any) string { return SafeString(val) } // String convert intX/floatX value to string, other type convert by fmt.Sprint func String(val any) string { @@ -520,76 +704,15 @@ func SafeString(val any) string { // TryToString try convert intX/floatX value to string // // if defaultAsErr is False, will use fmt.Sprint convert other type -func TryToString(val any, defaultAsErr bool) (str string, err error) { - var usrFn comdef.ToStringFunc +func TryToString(val any, defaultAsErr bool) (string, error) { + var optFn comfunc.ConvOptionFn if !defaultAsErr { - usrFn = func(v any) (string, error) { - if val == nil { - return "", nil - } - return fmt.Sprint(v), nil - } + optFn = comfunc.WithUserConvFn(comfunc.StrBySprintFn) } - - return ToStringWithFunc(val, usrFn) + return ToStringWith(val, optFn) } -// ToStringWithFunc try convert intX/floatX value to string, will call usrFn if value type not supported. -// -// if defaultAsErr is False, will use fmt.Sprint convert other type -func ToStringWithFunc(val any, usrFn comdef.ToStringFunc) (str string, err error) { - switch value := val.(type) { - case int: - str = strconv.Itoa(value) - case int8: - str = strconv.Itoa(int(value)) - case int16: - str = strconv.Itoa(int(value)) - case int32: // same as `rune` - str = strconv.Itoa(int(value)) - case int64: - str = strconv.FormatInt(value, 10) - case uint: - str = strconv.FormatUint(uint64(value), 10) - case uint8: - str = strconv.FormatUint(uint64(value), 10) - case uint16: - str = strconv.FormatUint(uint64(value), 10) - case uint32: - str = strconv.FormatUint(uint64(value), 10) - case uint64: - str = strconv.FormatUint(value, 10) - case float32: - str = strconv.FormatFloat(float64(value), 'f', -1, 32) - case float64: - str = strconv.FormatFloat(value, 'f', -1, 64) - case time.Duration: - str = strconv.FormatInt(int64(value), 10) - case string: - str = value - case fmt.Stringer: - str = value.String() - default: - if usrFn != nil { - str, err = usrFn(val) - } else { - err = comdef.ErrConvType - } - } - return -} - -// Percent returns a values percent of the total -func Percent(val, total int) float64 { - if total == 0 { - return float64(0) - } - return (float64(val) / float64(total)) * 100 -} - -// ElapsedTime calc elapsed time 计算运行时间消耗 单位 ms(毫秒) -// -// Deprecated: use timex.ElapsedTime() -func ElapsedTime(startTime time.Time) string { - return fmt.Sprintf("%.3f", time.Since(startTime).Seconds()*1000) +// ToStringWith try to convert value to string. can with some option func, more see comfunc.ConvOption. +func ToStringWith(in any, optFns ...comfunc.ConvOptionFn) (string, error) { + return comfunc.ToStringWith(in, optFns...) } diff --git a/vendor/github.com/gookit/goutil/mathutil/mathutil.go b/vendor/github.com/gookit/goutil/mathutil/mathutil.go index 1b47f32a9f..4831ceb1d3 100644 --- a/vendor/github.com/gookit/goutil/mathutil/mathutil.go +++ b/vendor/github.com/gookit/goutil/mathutil/mathutil.go @@ -2,6 +2,8 @@ package mathutil import ( + "math" + "github.com/gookit/goutil/comdef" ) @@ -71,3 +73,37 @@ func GteOr[T comdef.XintOrFloat](val, min, defVal T) T { } return defVal } + +// Mul computes the a*b value, rounding the result. +func Mul[T1, T2 comdef.XintOrFloat](a T1, b T2) float64 { + return math.Round(SafeFloat(a) * SafeFloat(b)) +} + +// MulF2i computes the float64 type a * b value, rounding the result to an integer. +func MulF2i(a, b float64) int { + return int(math.Round(a * b)) +} + +// Div computes the a/b value, result use round handle. +func Div[T1, T2 comdef.XintOrFloat](a T1, b T2) float64 { + return math.Round(SafeFloat(a) / SafeFloat(b)) +} + +// DivInt computes the int type a / b value, rounding the result to an integer. +func DivInt[T comdef.Integer](a, b T) int { + fv := math.Round(float64(a) / float64(b)) + return int(fv) +} + +// DivF2i computes the float64 type a / b value, rounding the result to an integer. +func DivF2i(a, b float64) int { + return int(math.Round(a / b)) +} + +// Percent returns a values percent of the total +func Percent(val, total int) float64 { + if total == 0 { + return float64(0) + } + return (float64(val) / float64(total)) * 100 +} diff --git a/vendor/github.com/gookit/goutil/reflects/check.go b/vendor/github.com/gookit/goutil/reflects/check.go index 062ce6bc5c..8132960fe1 100644 --- a/vendor/github.com/gookit/goutil/reflects/check.go +++ b/vendor/github.com/gookit/goutil/reflects/check.go @@ -10,8 +10,9 @@ func HasChild(v reflect.Value) bool { switch v.Kind() { case reflect.Array, reflect.Slice, reflect.Map, reflect.Struct: return true + default: + return false } - return false } // IsArrayOrSlice check. eg: array, slice @@ -122,9 +123,9 @@ func IsEmpty(v reflect.Value) bool { return v.Float() == 0 case reflect.Interface, reflect.Ptr, reflect.Func: return v.IsNil() + default: + return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) } - - return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) } // IsEmptyValue reflect value check, alias of the IsEmptyReal() @@ -158,7 +159,7 @@ func IsEmptyReal(v reflect.Value) bool { return v.IsNil() case reflect.Invalid: return true + default: + return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) } - - return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) } diff --git a/vendor/github.com/gookit/goutil/reflects/conv.go b/vendor/github.com/gookit/goutil/reflects/conv.go index f5376ec41a..c95dcfe212 100644 --- a/vendor/github.com/gookit/goutil/reflects/conv.go +++ b/vendor/github.com/gookit/goutil/reflects/conv.go @@ -126,12 +126,12 @@ func ConvToKind(val any, kind reflect.Kind) (rv reflect.Value, err error) { rv = reflect.ValueOf(dstV) } case reflect.Uint: - var dstV uint64 + var dstV uint if dstV, err = mathutil.ToUint(val); err == nil { - rv = reflect.ValueOf(uint(dstV)) + rv = reflect.ValueOf(dstV) } case reflect.Uint8: - var dstV uint64 + var dstV uint if dstV, err = mathutil.ToUint(val); err == nil { if dstV > math.MaxUint8 { return rv, fmt.Errorf("value overflow uint8. val: %v", val) @@ -139,7 +139,7 @@ func ConvToKind(val any, kind reflect.Kind) (rv reflect.Value, err error) { rv = reflect.ValueOf(uint8(dstV)) } case reflect.Uint16: - var dstV uint64 + var dstV uint if dstV, err = mathutil.ToUint(val); err == nil { if dstV > math.MaxUint16 { return rv, fmt.Errorf("value overflow uint16. val: %v", val) @@ -147,7 +147,7 @@ func ConvToKind(val any, kind reflect.Kind) (rv reflect.Value, err error) { rv = reflect.ValueOf(uint16(dstV)) } case reflect.Uint32: - var dstV uint64 + var dstV uint if dstV, err = mathutil.ToUint(val); err == nil { if dstV > math.MaxUint32 { return rv, fmt.Errorf("value overflow uint32. val: %v", val) @@ -156,7 +156,7 @@ func ConvToKind(val any, kind reflect.Kind) (rv reflect.Value, err error) { } case reflect.Uint64: var dstV uint64 - if dstV, err = mathutil.ToUint(val); err == nil { + if dstV, err = mathutil.ToUint64(val); err == nil { rv = reflect.ValueOf(dstV) } case reflect.Float32: diff --git a/vendor/github.com/gookit/goutil/reflects/reflects.go b/vendor/github.com/gookit/goutil/reflects/reflects.go index cd2ea0eb55..0a6cdde8a4 100644 --- a/vendor/github.com/gookit/goutil/reflects/reflects.go +++ b/vendor/github.com/gookit/goutil/reflects/reflects.go @@ -2,7 +2,6 @@ package reflects import ( - "fmt" "reflect" ) @@ -12,6 +11,6 @@ var ( anyType = reflect.TypeOf((*any)(nil)).Elem() errorType = reflect.TypeOf((*error)(nil)).Elem() - fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() + // fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() reflectValueType = reflect.TypeOf((*reflect.Value)(nil)).Elem() ) diff --git a/vendor/github.com/gookit/goutil/structs/writer.go b/vendor/github.com/gookit/goutil/structs/writer.go index ac306e1100..dde4bcad95 100644 --- a/vendor/github.com/gookit/goutil/structs/writer.go +++ b/vendor/github.com/gookit/goutil/structs/writer.go @@ -4,9 +4,12 @@ import ( "errors" "fmt" "reflect" + "time" + "github.com/gookit/goutil/comdef" "github.com/gookit/goutil/maputil" "github.com/gookit/goutil/reflects" + "github.com/gookit/goutil/strutil" ) // NewWriter create a struct writer @@ -46,6 +49,9 @@ type SetOptions struct { // // default: false ParseDefaultEnv bool + + // StopOnError if true, will stop set value on error happened. default: false + // StopOnError bool } // WithParseDefault value by tag "default" @@ -85,6 +91,7 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions) error { return nil } + var es comdef.Errors rt := rv.Type() for i := 0; i < rt.NumField(); i++ { @@ -100,7 +107,8 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions) error { if ok { info, err := ParseTagValueDefault(name, tagVal) if err != nil { - return err + es = append(es, err) + continue } name = info.Get("name") } @@ -112,7 +120,7 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions) error { if !ok && opt.ParseDefault && fv.IsZero() { defVal := ft.Tag.Get(opt.DefaultValTag) if err := initDefaultValue(fv, defVal, opt.ParseDefaultEnv); err != nil { - return err + es = append(es, err) } continue } @@ -127,22 +135,39 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions) error { // field is struct if fv.Kind() == reflect.Struct { - asMp, err := maputil.TryAnyMap(val) - if err != nil { - return fmt.Errorf("must provide map data for field %q, err=%v", ft.Name, err) + // up: special handle time.Time struct + if _, ok := fv.Interface().(time.Time); ok { + tm, er := strutil.ToTime(strutil.StringOr(val, "")) + if er != nil { + es = append(es, er) + continue + } + if er = reflects.SetValue(fv, tm); er != nil { + es = append(es, er) + } + continue } - if err := setValues(fv, asMp, opt); err != nil { - return err + asMp, err := maputil.TryAnyMap(val) + if err != nil { + err = fmt.Errorf("must provide map for set struct field %q, err=%v", ft.Name, err) + es = append(es, err) + continue + } + + // recursive processing sub-struct + if err = setValues(fv, asMp, opt); err != nil { + es = append(es, err) } continue } // set field value if err := reflects.SetValue(fv, val); err != nil { - return err + es = append(es, err) + continue } } - return nil + return es.ErrOrNil() } diff --git a/vendor/github.com/gookit/goutil/strutil/convert.go b/vendor/github.com/gookit/goutil/strutil/convert.go index 71ed78a610..c77615d179 100644 --- a/vendor/github.com/gookit/goutil/strutil/convert.go +++ b/vendor/github.com/gookit/goutil/strutil/convert.go @@ -2,7 +2,6 @@ package strutil import ( "errors" - "fmt" "reflect" "regexp" "strconv" @@ -10,7 +9,6 @@ import ( "time" "unsafe" - "github.com/gookit/goutil/comdef" "github.com/gookit/goutil/internal/comfunc" "github.com/gookit/goutil/mathutil" ) @@ -91,39 +89,29 @@ func Implode(sep string, ss ...string) string { return strings.Join(ss, sep) } *************************************************************/ // String convert value to string, return error on failed -func String(val any) (string, error) { - return ToStringWithFunc(val, nil) -} +func String(val any) (string, error) { return ToStringWith(val) } // ToString convert value to string, return error on failed -func ToString(val any) (string, error) { - return ToStringWithFunc(val, nil) -} +func ToString(val any) (string, error) { return ToStringWith(val) } // StringOrErr convert value to string, return error on failed -func StringOrErr(val any) (string, error) { - return ToStringWithFunc(val, nil) -} +func StringOrErr(val any) (string, error) { return ToStringWith(val) } // QuietString convert value to string, will ignore error. same as SafeString() -func QuietString(val any) string { - return SafeString(val) -} +func QuietString(val any) string { return SafeString(val) } // SafeString convert value to string, will ignore error func SafeString(in any) string { - val, _ := ToStringWithFunc(in, SprintToStrFunc) - return val + s, _ := AnyToString(in, false) + return s } // StringOrPanic convert value to string, will panic on error -func StringOrPanic(val any) string { - return MustString(val) -} +func StringOrPanic(val any) string { return MustString(val) } // MustString convert value to string, will panic on error func MustString(val any) string { - s, err := ToStringWithFunc(val, nil) + s, err := ToStringWith(val) if err != nil { panic(err) } @@ -131,93 +119,34 @@ func MustString(val any) string { } // StringOrDefault convert any value to string, return default value on failed -func StringOrDefault(val any, defVal string) string { - return StringOr(val, defVal) -} +func StringOrDefault(val any, defVal string) string { return StringOr(val, defVal) } // StringOr convert any value to string, return default value on failed func StringOr(val any, defVal string) string { - s, err := ToStringWithFunc(val, nil) + s, err := ToStringWith(val) if err != nil { return defVal } return s } -// SprintToStrFunc convert any value to string by fmt.Sprint -var SprintToStrFunc = func(v any) (string, error) { - if v == nil { - return "", nil - } - return fmt.Sprint(v), nil -} - // AnyToString convert any value to string. // // For defaultAsErr: // -// - False will use fmt.Sprint convert complex type -// - True will return error on fail. +// - False will use fmt.Sprint convert unsupported type +// - True will return error on convert fail. func AnyToString(val any, defaultAsErr bool) (s string, err error) { - var fbFunc comdef.ToStringFunc + var optFn comfunc.ConvOptionFn if !defaultAsErr { - fbFunc = SprintToStrFunc + optFn = comfunc.WithUserConvFn(comfunc.StrBySprintFn) } - - return ToStringWithFunc(val, fbFunc) + return ToStringWith(val, optFn) } -// ToStringWithFunc convert value to string, with a func to fallback handle. -// -// On not convert: -// - If fbFn is nil, will return comdef.ErrConvType. -// - If fbFn is not nil, will call it to convert. -func ToStringWithFunc(val any, fbFn comdef.ToStringFunc) (str string, err error) { - switch value := val.(type) { - case int: - str = strconv.Itoa(value) - case int8: - str = strconv.Itoa(int(value)) - case int16: - str = strconv.Itoa(int(value)) - case int32: // same as `rune` - str = strconv.Itoa(int(value)) - case int64: - str = strconv.FormatInt(value, 10) - case uint: - str = strconv.FormatUint(uint64(value), 10) - case uint8: - str = strconv.FormatUint(uint64(value), 10) - case uint16: - str = strconv.FormatUint(uint64(value), 10) - case uint32: - str = strconv.FormatUint(uint64(value), 10) - case uint64: - str = strconv.FormatUint(value, 10) - case float32: - str = strconv.FormatFloat(float64(value), 'f', -1, 32) - case float64: - str = strconv.FormatFloat(value, 'f', -1, 64) - case bool: - str = strconv.FormatBool(value) - case string: - str = value - case []byte: - str = string(value) - case time.Duration: - str = strconv.FormatInt(int64(value), 10) - case fmt.Stringer: - str = value.String() - case error: - str = value.Error() - default: - if fbFn == nil { - err = comdef.ErrConvType - } else { - str, err = fbFn(value) - } - } - return +// ToStringWith try to convert value to string. can with some option func, more see comfunc.ConvOption. +func ToStringWith(in any, optFns ...comfunc.ConvOptionFn) (string, error) { + return comfunc.ToStringWith(in, optFns...) } /************************************************************* @@ -230,9 +159,7 @@ func ToBool(s string) (bool, error) { } // QuietBool convert to bool, will ignore error -func QuietBool(s string) bool { - return SafeBool(s) -} +func QuietBool(s string) bool { return SafeBool(s) } // SafeBool convert to bool, will ignore error func SafeBool(s string) bool { @@ -289,14 +216,10 @@ func SafeInt(s string) int { } // QuietInt convert string to int, will ignore error -func QuietInt(s string) int { - return SafeInt(s) -} +func QuietInt(s string) int { return SafeInt(s) } // MustInt convert string to int, will panic on error -func MustInt(s string) int { - return IntOrPanic(s) -} +func MustInt(s string) int { return IntOrPanic(s) } // IntOrPanic convert value to int, will panic on error func IntOrPanic(s string) int { @@ -312,10 +235,10 @@ func IntOrPanic(s string) int { *************************************************************/ // Int64 convert string to int, will ignore error -func Int64(s string) int64 { - val, _ := Int64OrErr(s) - return val -} +func Int64(s string) int64 { return SafeInt64(s) } + +// QuietInt64 convert string to int, will ignore error +func QuietInt64(s string) int64 { return SafeInt64(s) } // SafeInt64 convert string to int, will ignore error func SafeInt64(s string) int64 { @@ -323,11 +246,6 @@ func SafeInt64(s string) int64 { return val } -// QuietInt64 convert string to int, will ignore error -func QuietInt64(s string) int64 { - return SafeInt64(s) -} - // ToInt64 convert string to int, return error on fail func ToInt64(s string) (int64, error) { return strconv.ParseInt(s, 10, 0) @@ -353,9 +271,7 @@ func Int64OrErr(s string) (int64, error) { } // MustInt64 convert value to int, will panic on error -func MustInt64(s string) int64 { - return Int64OrPanic(s) -} +func MustInt64(s string) int64 { return Int64OrPanic(s) } // Int64OrPanic convert value to int, will panic on error func Int64OrPanic(s string) int64 { @@ -371,10 +287,7 @@ func Int64OrPanic(s string) int64 { *************************************************************/ // Uint convert string to uint, will ignore error -func Uint(s string) uint64 { - val, _ := UintOrErr(s) - return val -} +func Uint(s string) uint64 { return SafeUint(s) } // SafeUint convert string to uint, will ignore error func SafeUint(s string) uint64 { @@ -393,9 +306,7 @@ func UintOrErr(s string) (uint64, error) { } // MustUint convert value to uint, will panic on error. alias of UintOrPanic() -func MustUint(s string) uint64 { - return UintOrPanic(s) -} +func MustUint(s string) uint64 { return UintOrPanic(s) } // UintOrPanic convert value to uint, will panic on error func UintOrPanic(s string) uint64 { diff --git a/vendor/github.com/gookit/goutil/strutil/strutil.go b/vendor/github.com/gookit/goutil/strutil/strutil.go index e81253f90d..7a2ca434b1 100644 --- a/vendor/github.com/gookit/goutil/strutil/strutil.go +++ b/vendor/github.com/gookit/goutil/strutil/strutil.go @@ -2,12 +2,9 @@ package strutil import ( - "bytes" - "encoding/json" "errors" "fmt" "strings" - "text/template" "github.com/gookit/goutil/comdef" ) @@ -92,68 +89,6 @@ func NewReplacer(pairs map[string]string) *strings.Replacer { return strings.NewReplacer(ss...) } -// PrettyJSON get pretty Json string -// Deprecated: please use fmtutil.PrettyJSON() or jsonutil.Pretty() instead it -func PrettyJSON(v any) (string, error) { - out, err := json.MarshalIndent(v, "", " ") - return string(out), err -} - -var builtInFuncs = template.FuncMap{ - // don't escape content - "raw": func(s string) string { - return s - }, - "trim": func(s string) string { - return strings.TrimSpace(s) - }, - // join strings - "join": func(ss []string, sep string) string { - return strings.Join(ss, sep) - }, - // lower first char - "lcFirst": func(s string) string { - return LowerFirst(s) - }, - // upper first char - "upFirst": func(s string) string { - return UpperFirst(s) - }, -} - -// RenderTemplate quickly render text template. -// -// Deprecated: please use textutil.RenderTpl() instead it -func RenderTemplate(input string, data any, fns template.FuncMap, isFile ...bool) string { - return RenderText(input, data, fns, isFile...) -} - -// RenderText quickly render text template -// -// Deprecated: please use textutil.RenderTpl() instead it -func RenderText(input string, data any, fns template.FuncMap, isFile ...bool) string { - t := template.New("simple-text") - t.Funcs(builtInFuncs) - - // add custom template functions - if len(fns) > 0 { - t.Funcs(fns) - } - - if len(isFile) > 0 && isFile[0] { - template.Must(t.ParseFiles(input)) - } else { - template.Must(t.Parse(input)) - } - - // use buffer receive rendered content - buf := new(bytes.Buffer) - if err := t.Execute(buf, data); err != nil { - panic(err) - } - return buf.String() -} - // WrapTag for given string. func WrapTag(s, tag string) string { if s == "" { diff --git a/vendor/modules.txt b/vendor/modules.txt index 01f00a5f0d..82cf397d99 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1107,11 +1107,11 @@ github.com/google/uuid # github.com/gookit/color v1.5.4 ## explicit; go 1.18 github.com/gookit/color -# github.com/gookit/config/v2 v2.2.4 +# github.com/gookit/config/v2 v2.2.5 ## explicit; go 1.19 github.com/gookit/config/v2 github.com/gookit/config/v2/yaml -# github.com/gookit/goutil v0.6.14 +# github.com/gookit/goutil v0.6.15 ## explicit; go 1.19 github.com/gookit/goutil github.com/gookit/goutil/arrutil From fce4d19e3fa13cc53e45cd5768b5753ccd182630 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 12:00:10 +0000 Subject: [PATCH 02/15] build(deps): bump github.com/nats-io/nats.go from 1.31.0 to 1.32.0 Bumps [github.com/nats-io/nats.go](https://github.com/nats-io/nats.go) from 1.31.0 to 1.32.0. - [Release notes](https://github.com/nats-io/nats.go/releases) - [Commits](https://github.com/nats-io/nats.go/compare/v1.31.0...v1.32.0) --- updated-dependencies: - dependency-name: github.com/nats-io/nats.go dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 +- .../github.com/nats-io/nats.go/.golangci.yaml | 3 + vendor/github.com/nats-io/nats.go/README.md | 4 +- vendor/github.com/nats-io/nats.go/context.go | 2 +- vendor/github.com/nats-io/nats.go/enc.go | 2 +- .../nats.go/encoders/builtin/default_enc.go | 2 +- .../nats.go/encoders/builtin/gob_enc.go | 2 +- .../nats.go/encoders/builtin/json_enc.go | 2 +- vendor/github.com/nats-io/nats.go/go_test.mod | 16 ++-- vendor/github.com/nats-io/nats.go/go_test.sum | 33 ++++---- vendor/github.com/nats-io/nats.go/js.go | 76 ++++++++++++------- vendor/github.com/nats-io/nats.go/jserrors.go | 2 +- vendor/github.com/nats-io/nats.go/jsm.go | 20 ++--- vendor/github.com/nats-io/nats.go/kv.go | 75 ++++++++++++++++-- vendor/github.com/nats-io/nats.go/nats.go | 8 +- vendor/github.com/nats-io/nats.go/netchan.go | 2 +- vendor/github.com/nats-io/nats.go/object.go | 45 +++++++---- vendor/github.com/nats-io/nats.go/timer.go | 2 +- vendor/modules.txt | 2 +- 20 files changed, 206 insertions(+), 98 deletions(-) diff --git a/go.mod b/go.mod index 0b3b003bf4..4bf841a186 100644 --- a/go.mod +++ b/go.mod @@ -61,7 +61,7 @@ require ( github.com/mna/pigeon v1.2.1 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 github.com/nats-io/nats-server/v2 v2.10.9 - github.com/nats-io/nats.go v1.31.0 + github.com/nats-io/nats.go v1.32.0 github.com/oklog/run v1.1.0 github.com/olekukonko/tablewriter v0.0.5 github.com/onsi/ginkgo v1.16.5 diff --git a/go.sum b/go.sum index 2a8e210ab5..7875677cda 100644 --- a/go.sum +++ b/go.sum @@ -1744,8 +1744,8 @@ github.com/nats-io/jwt/v2 v2.5.3 h1:/9SWvzc6hTfamcgXJ3uYRpgj+QuY2aLNqRiqrKcrpEo= github.com/nats-io/jwt/v2 v2.5.3/go.mod h1:iysuPemFcc7p4IoYots3IuELSI4EDe9Y0bQMe+I3Bf4= github.com/nats-io/nats-server/v2 v2.10.9 h1:VEW43Zz+p+9lARtiPM9ctd6ckun+92ZT2T17HWtwiFI= github.com/nats-io/nats-server/v2 v2.10.9/go.mod h1:oorGiV9j3BOLLO3ejQe+U7pfAGyPo+ppD7rpgNF6KTQ= -github.com/nats-io/nats.go v1.31.0 h1:/WFBHEc/dOKBF6qf1TZhrdEfTmOZ5JzdJ+Y3m6Y/p7E= -github.com/nats-io/nats.go v1.31.0/go.mod h1:di3Bm5MLsoB4Bx61CBTsxuarI36WbhAwOm8QrW39+i8= +github.com/nats-io/nats.go v1.32.0 h1:Bx9BZS+aXYlxW08k8Gd3yR2s73pV5XSoAQUyp1Kwvp0= +github.com/nats-io/nats.go v1.32.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8= github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI= github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= diff --git a/vendor/github.com/nats-io/nats.go/.golangci.yaml b/vendor/github.com/nats-io/nats.go/.golangci.yaml index be66189ede..fb548e50e4 100644 --- a/vendor/github.com/nats-io/nats.go/.golangci.yaml +++ b/vendor/github.com/nats-io/nats.go/.golangci.yaml @@ -5,6 +5,9 @@ issues: - linters: - errcheck text: "Unsubscribe" + - linters: + - errcheck + text: "Drain" - linters: - errcheck text: "msg.Ack" diff --git a/vendor/github.com/nats-io/nats.go/README.md b/vendor/github.com/nats-io/nats.go/README.md index 042733da1d..d7f2e00d64 100644 --- a/vendor/github.com/nats-io/nats.go/README.md +++ b/vendor/github.com/nats-io/nats.go/README.md @@ -14,6 +14,8 @@ A [Go](http://golang.org) client for the [NATS messaging system](https://nats.io [Coverage-Url]: https://coveralls.io/r/nats-io/nats.go?branch=main [Coverage-image]: https://coveralls.io/repos/github/nats-io/nats.go/badge.svg?branch=main +**Check out [NATS by example](https://natsbyexample.com) - An evolving collection of runnable, cross-client reference examples for NATS.** + ## Installation ```bash @@ -29,7 +31,7 @@ When using or transitioning to Go modules support: ```bash # Go client latest or explicit version go get github.com/nats-io/nats.go/@latest -go get github.com/nats-io/nats.go/@v1.31.0 +go get github.com/nats-io/nats.go/@v1.32.0 # For latest NATS Server, add /v2 at the end go get github.com/nats-io/nats-server/v2 diff --git a/vendor/github.com/nats-io/nats.go/context.go b/vendor/github.com/nats-io/nats.go/context.go index c4ef4be173..20f1782acf 100644 --- a/vendor/github.com/nats-io/nats.go/context.go +++ b/vendor/github.com/nats-io/nats.go/context.go @@ -1,4 +1,4 @@ -// Copyright 2016-2022 The NATS Authors +// Copyright 2016-2023 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/nats-io/nats.go/enc.go b/vendor/github.com/nats-io/nats.go/enc.go index a1c54f246b..4550f618d4 100644 --- a/vendor/github.com/nats-io/nats.go/enc.go +++ b/vendor/github.com/nats-io/nats.go/enc.go @@ -1,4 +1,4 @@ -// Copyright 2012-2019 The NATS Authors +// Copyright 2012-2023 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/nats-io/nats.go/encoders/builtin/default_enc.go b/vendor/github.com/nats-io/nats.go/encoders/builtin/default_enc.go index 65c2d68bb8..7e729637ce 100644 --- a/vendor/github.com/nats-io/nats.go/encoders/builtin/default_enc.go +++ b/vendor/github.com/nats-io/nats.go/encoders/builtin/default_enc.go @@ -1,4 +1,4 @@ -// Copyright 2012-2018 The NATS Authors +// Copyright 2012-2023 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/nats-io/nats.go/encoders/builtin/gob_enc.go b/vendor/github.com/nats-io/nats.go/encoders/builtin/gob_enc.go index 4e7cecba29..7ecf85e4da 100644 --- a/vendor/github.com/nats-io/nats.go/encoders/builtin/gob_enc.go +++ b/vendor/github.com/nats-io/nats.go/encoders/builtin/gob_enc.go @@ -1,4 +1,4 @@ -// Copyright 2013-2018 The NATS Authors +// Copyright 2013-2023 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/nats-io/nats.go/encoders/builtin/json_enc.go b/vendor/github.com/nats-io/nats.go/encoders/builtin/json_enc.go index 9b6ffc017a..0540d9850c 100644 --- a/vendor/github.com/nats-io/nats.go/encoders/builtin/json_enc.go +++ b/vendor/github.com/nats-io/nats.go/encoders/builtin/json_enc.go @@ -1,4 +1,4 @@ -// Copyright 2012-2018 The NATS Authors +// Copyright 2012-2023 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/nats-io/nats.go/go_test.mod b/vendor/github.com/nats-io/nats.go/go_test.mod index 8902c1edd7..d28963c27b 100644 --- a/vendor/github.com/nats-io/nats.go/go_test.mod +++ b/vendor/github.com/nats-io/nats.go/go_test.mod @@ -4,19 +4,19 @@ go 1.19 require ( github.com/golang/protobuf v1.4.2 - github.com/klauspost/compress v1.17.0 - github.com/nats-io/nats-server/v2 v2.10.0 - github.com/nats-io/nkeys v0.4.5 + github.com/klauspost/compress v1.17.4 + github.com/nats-io/nats-server/v2 v2.10.7 + github.com/nats-io/nkeys v0.4.6 github.com/nats-io/nuid v1.0.1 go.uber.org/goleak v1.2.1 - golang.org/x/text v0.13.0 + golang.org/x/text v0.14.0 google.golang.org/protobuf v1.23.0 ) require ( github.com/minio/highwayhash v1.0.2 // indirect - github.com/nats-io/jwt/v2 v2.5.2 // indirect - golang.org/x/crypto v0.13.0 // indirect - golang.org/x/sys v0.12.0 // indirect - golang.org/x/time v0.3.0 // indirect + github.com/nats-io/jwt/v2 v2.5.3 // indirect + golang.org/x/crypto v0.16.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/time v0.5.0 // indirect ) diff --git a/vendor/github.com/nats-io/nats.go/go_test.sum b/vendor/github.com/nats-io/nats.go/go_test.sum index ce4ba9205c..38fe6ef6ff 100644 --- a/vendor/github.com/nats-io/nats.go/go_test.sum +++ b/vendor/github.com/nats-io/nats.go/go_test.sum @@ -10,32 +10,31 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= -github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= -github.com/nats-io/jwt/v2 v2.5.2 h1:DhGH+nKt+wIkDxM6qnVSKjokq5t59AZV5HRcFW0zJwU= -github.com/nats-io/jwt/v2 v2.5.2/go.mod h1:24BeQtRwxRV8ruvC4CojXlx/WQ/VjuwlYiH+vu/+ibI= -github.com/nats-io/nats-server/v2 v2.10.0 h1:rcU++Hzo+wARxtJugrV3J5z5iGdHeVG8tT8Chb3bKDg= -github.com/nats-io/nats-server/v2 v2.10.0/go.mod h1:3PMvMSu2cuK0J9YInRLWdFpFsswKKGUS77zVSAudRto= -github.com/nats-io/nkeys v0.4.5 h1:Zdz2BUlFm4fJlierwvGK+yl20IAKUm7eV6AAZXEhkPk= -github.com/nats-io/nkeys v0.4.5/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64= +github.com/nats-io/jwt/v2 v2.5.3 h1:/9SWvzc6hTfamcgXJ3uYRpgj+QuY2aLNqRiqrKcrpEo= +github.com/nats-io/jwt/v2 v2.5.3/go.mod h1:iysuPemFcc7p4IoYots3IuELSI4EDe9Y0bQMe+I3Bf4= +github.com/nats-io/nats-server/v2 v2.10.7 h1:f5VDy+GMu7JyuFA0Fef+6TfulfCs5nBTgq7MMkFJx5Y= +github.com/nats-io/nats-server/v2 v2.10.7/go.mod h1:V2JHOvPiPdtfDXTuEUsthUnCvSDeFrK4Xn9hRo6du7c= +github.com/nats-io/nkeys v0.4.6 h1:IzVe95ru2CT6ta874rt9saQRkWfe2nFj1NtvYSLqMzY= +github.com/nats-io/nkeys v0.4.6/go.mod h1:4DxZNzenSVd1cYQoAa8948QY3QDjrHfcfVADymtkpts= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= diff --git a/vendor/github.com/nats-io/nats.go/js.go b/vendor/github.com/nats-io/nats.go/js.go index 444278e0f7..0c06730587 100644 --- a/vendor/github.com/nats-io/nats.go/js.go +++ b/vendor/github.com/nats-io/nats.go/js.go @@ -227,14 +227,16 @@ type js struct { opts *jsOpts // For async publish context. - mu sync.RWMutex - rpre string - rsub *Subscription - pafs map[string]*pubAckFuture - stc chan struct{} - dch chan struct{} - rr *rand.Rand - connStatusCh chan (Status) + mu sync.RWMutex + rpre string + rsub *Subscription + pafs map[string]*pubAckFuture + stc chan struct{} + dch chan struct{} + rr *rand.Rand + connStatusCh chan (Status) + replyPrefix string + replyPrefixLen int } type jsOpts struct { @@ -283,6 +285,12 @@ func (nc *Conn) JetStream(opts ...JSOpt) (JetStreamContext, error) { maxpa: defaultAsyncPubAckInflight, }, } + inboxPrefix := InboxPrefix + if js.nc.Opts.InboxPrefix != _EMPTY_ { + inboxPrefix = js.nc.Opts.InboxPrefix + "." + } + js.replyPrefix = inboxPrefix + js.replyPrefixLen = len(js.replyPrefix) + aReplyTokensize + 1 for _, opt := range opts { if err := opt.configureJSContext(js.opts); err != nil { @@ -537,7 +545,7 @@ func (js *js) PublishMsg(m *Msg, opts ...PubOpt) (*PubAck, error) { } if err != nil { - for r, ttl := 0, o.ttl; err == ErrNoResponders && (r < o.rnum || o.rnum < 0); r++ { + for r, ttl := 0, o.ttl; errors.Is(err, ErrNoResponders) && (r < o.rnum || o.rnum < 0); r++ { // To protect against small blips in leadership changes etc, if we get a no responders here retry. if o.ctx != nil { select { @@ -559,7 +567,7 @@ func (js *js) PublishMsg(m *Msg, opts ...PubOpt) (*PubAck, error) { } } if err != nil { - if err == ErrNoResponders { + if errors.Is(err, ErrNoResponders) { err = ErrNoStreamResponse } return nil, err @@ -641,7 +649,6 @@ func (paf *pubAckFuture) Msg() *Msg { } // For quick token lookup etc. -const aReplyPreLen = 14 const aReplyTokensize = 6 func (js *js) newAsyncReply() string { @@ -654,11 +661,7 @@ func (js *js) newAsyncReply() string { for i := 0; i < aReplyTokensize; i++ { b[i] = rdigits[int(b[i]%base)] } - inboxPrefix := InboxPrefix - if js.nc.Opts.InboxPrefix != _EMPTY_ { - inboxPrefix = js.nc.Opts.InboxPrefix + "." - } - js.rpre = fmt.Sprintf("%s%s.", inboxPrefix, b[:aReplyTokensize]) + js.rpre = fmt.Sprintf("%s%s.", js.replyPrefix, b[:aReplyTokensize]) sub, err := js.nc.Subscribe(fmt.Sprintf("%s*", js.rpre), js.handleAsyncReply) if err != nil { js.mu.Unlock() @@ -767,10 +770,10 @@ func (js *js) asyncStall() <-chan struct{} { // Handle an async reply from PublishAsync. func (js *js) handleAsyncReply(m *Msg) { - if len(m.Subject) <= aReplyPreLen { + if len(m.Subject) <= js.replyPrefixLen { return } - id := m.Subject[aReplyPreLen:] + id := m.Subject[js.replyPrefixLen:] js.mu.Lock() paf := js.getPAF(id) @@ -916,7 +919,7 @@ func (js *js) PublishMsgAsync(m *Msg, opts ...PubOpt) (PubAckFuture, error) { return nil, errors.New("nats: error creating async reply handler") } - id := m.Reply[aReplyPreLen:] + id := m.Reply[js.replyPrefixLen:] paf := &pubAckFuture{msg: m, st: time.Now()} numPending, maxPending := js.registerPAF(id, paf) @@ -1241,6 +1244,10 @@ func (sub *Subscription) deleteConsumer() error { sub.mu.Unlock() return nil } + if jsi.stream == _EMPTY_ || jsi.consumer == _EMPTY_ { + sub.mu.Unlock() + return nil + } stream, consumer := jsi.stream, jsi.consumer js := jsi.js sub.mu.Unlock() @@ -1594,7 +1601,7 @@ func (js *js) subscribe(subj, queue string, cb MsgHandler, ch chan *Msg, isSync, if consumer != _EMPTY_ && !o.skipCInfo { info, err = js.ConsumerInfo(stream, consumer) notFoundErr = errors.Is(err, ErrConsumerNotFound) - lookupErr = err == ErrJetStreamNotEnabled || err == ErrTimeout || err == context.DeadlineExceeded + lookupErr = err == ErrJetStreamNotEnabled || errors.Is(err, ErrTimeout) || errors.Is(err, context.DeadlineExceeded) } switch { @@ -1808,7 +1815,9 @@ func (js *js) subscribe(subj, queue string, cb MsgHandler, ch chan *Msg, isSync, if bl < DefaultSubPendingBytesLimit { bl = DefaultSubPendingBytesLimit } - sub.SetPendingLimits(maxap, bl) + if err := sub.SetPendingLimits(maxap, bl); err != nil { + return nil, err + } } // Do heartbeats last if needed. @@ -2047,7 +2056,16 @@ func (sub *Subscription) resetOrderedConsumer(sseq uint64) { js := jsi.js sub.mu.Unlock() - consName := nuid.Next() + sub.mu.Lock() + // Attempt to delete the existing consumer. + // We don't wait for the response since even if it's unsuccessful, + // inactivity threshold will kick in and delete it. + if jsi.consumer != _EMPTY_ { + go js.DeleteConsumer(jsi.stream, jsi.consumer) + } + jsi.consumer = "" + sub.mu.Unlock() + consName := getHash(nuid.Next()) cinfo, err := js.upsertConsumer(jsi.stream, consName, cfg) if err != nil { var apiErr *APIError @@ -2813,7 +2831,7 @@ func (sub *Subscription) Fetch(batch int, opts ...PullOpt) ([]*Msg, error) { // are no messages. msg, err = sub.nextMsgWithContext(ctx, true, false) if err != nil { - if err == errNoMessages { + if errors.Is(err, errNoMessages) { err = nil } break @@ -2893,13 +2911,13 @@ func (sub *Subscription) Fetch(batch int, opts ...PullOpt) ([]*Msg, error) { usrMsg, err = checkMsg(msg, true, noWait) if err == nil && usrMsg { msgs = append(msgs, msg) - } else if noWait && (err == errNoMessages || err == errRequestsPending) && len(msgs) == 0 { + } else if noWait && (errors.Is(err, errNoMessages) || errors.Is(err, errRequestsPending)) && len(msgs) == 0 { // If we have a 404/408 for our "no_wait" request and have // not collected any message, then resend request to // wait this time. noWait = false err = sendReq() - } else if err == ErrTimeout && len(msgs) == 0 { + } else if errors.Is(err, ErrTimeout) && len(msgs) == 0 { // If we get a 408, we will bail if we already collected some // messages, otherwise ignore and go back calling nextMsg. err = nil @@ -3082,7 +3100,7 @@ func (sub *Subscription) FetchBatch(batch int, opts ...PullOpt) (MessageBatch, e // are no messages. msg, err := sub.nextMsgWithContext(ctx, true, false) if err != nil { - if err == errNoMessages { + if errors.Is(err, errNoMessages) { err = nil } result.err = err @@ -3159,7 +3177,7 @@ func (sub *Subscription) FetchBatch(batch int, opts ...PullOpt) (MessageBatch, e usrMsg, err = checkMsg(msg, true, false) if err != nil { - if err == ErrTimeout { + if errors.Is(err, ErrTimeout) { if reqID != "" && !subjectMatchesReqID(msg.Subject, reqID) { // ignore timeout message from server if it comes from a different pull request continue @@ -3188,7 +3206,7 @@ func (sub *Subscription) FetchBatch(batch int, opts ...PullOpt) (MessageBatch, e // checkCtxErr is used to determine whether ErrTimeout should be returned in case of context timeout func (o *pullOpts) checkCtxErr(err error) error { - if o.ctx == nil && err == context.DeadlineExceeded { + if o.ctx == nil && errors.Is(err, context.DeadlineExceeded) { return ErrTimeout } return err @@ -3204,7 +3222,7 @@ func (js *js) getConsumerInfoContext(ctx context.Context, stream, consumer strin ccInfoSubj := fmt.Sprintf(apiConsumerInfoT, stream, consumer) resp, err := js.apiRequestWithContext(ctx, js.apiSubj(ccInfoSubj), nil) if err != nil { - if err == ErrNoResponders { + if errors.Is(err, ErrNoResponders) { err = ErrJetStreamNotEnabled } return nil, err diff --git a/vendor/github.com/nats-io/nats.go/jserrors.go b/vendor/github.com/nats-io/nats.go/jserrors.go index c8b1f5fc62..ef5d4af945 100644 --- a/vendor/github.com/nats-io/nats.go/jserrors.go +++ b/vendor/github.com/nats-io/nats.go/jserrors.go @@ -1,4 +1,4 @@ -// Copyright 2020-2022 The NATS Authors +// Copyright 2020-2023 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/nats-io/nats.go/jsm.go b/vendor/github.com/nats-io/nats.go/jsm.go index 266bf0665f..8f724726b8 100644 --- a/vendor/github.com/nats-io/nats.go/jsm.go +++ b/vendor/github.com/nats-io/nats.go/jsm.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022 The NATS Authors +// Copyright 2021-2023 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -252,11 +252,13 @@ type AccountInfo struct { } type Tier struct { - Memory uint64 `json:"memory"` - Store uint64 `json:"storage"` - Streams int `json:"streams"` - Consumers int `json:"consumers"` - Limits AccountLimits `json:"limits"` + Memory uint64 `json:"memory"` + Store uint64 `json:"storage"` + ReservedMemory uint64 `json:"reserved_memory"` + ReservedStore uint64 `json:"reserved_storage"` + Streams int `json:"streams"` + Consumers int `json:"consumers"` + Limits AccountLimits `json:"limits"` } // APIStats reports on API calls to JetStream for this account. @@ -297,7 +299,7 @@ func (js *js) AccountInfo(opts ...JSOpt) (*AccountInfo, error) { resp, err := js.apiRequestWithContext(o.ctx, js.apiSubj(apiAccountInfo), nil) if err != nil { // todo maybe nats server should never have no responder on this subject and always respond if they know there is no js to be had - if err == ErrNoResponders { + if errors.Is(err, ErrNoResponders) { err = ErrJetStreamNotEnabled } return nil, err @@ -415,7 +417,7 @@ func (js *js) upsertConsumer(stream, consumerName string, cfg *ConsumerConfig, o resp, err := js.apiRequestWithContext(o.ctx, js.apiSubj(ccSubj), req) if err != nil { - if err == ErrNoResponders { + if errors.Is(err, ErrNoResponders) { err = ErrJetStreamNotEnabled } return nil, err @@ -1623,7 +1625,7 @@ func (jsc *js) StreamNameBySubject(subj string, opts ...JSOpt) (string, error) { resp, err := jsc.apiRequestWithContext(o.ctx, jsc.apiSubj(apiStreams), j) if err != nil { - if err == ErrNoResponders { + if errors.Is(err, ErrNoResponders) { err = ErrJetStreamNotEnabled } return _EMPTY_, err diff --git a/vendor/github.com/nats-io/nats.go/kv.go b/vendor/github.com/nats-io/nats.go/kv.go index 7382f4d872..0864f30cce 100644 --- a/vendor/github.com/nats-io/nats.go/kv.go +++ b/vendor/github.com/nats-io/nats.go/kv.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022 The NATS Authors +// Copyright 2021-2023 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -65,7 +65,10 @@ type KeyValue interface { // WatchAll will invoke the callback for all updates. WatchAll(opts ...WatchOpt) (KeyWatcher, error) // Keys will return all keys. + // DEPRECATED: Use ListKeys instead to avoid memory issues. Keys(opts ...WatchOpt) ([]string, error) + // ListKeys will return all keys in a channel. + ListKeys(opts ...WatchOpt) (KeyLister, error) // History will return all historical values for the key. History(key string, opts ...WatchOpt) ([]KeyValueEntry, error) // Bucket returns the current bucket name. @@ -95,6 +98,9 @@ type KeyValueStatus interface { // Bytes returns the size in bytes of the bucket Bytes() uint64 + + // IsCompressed indicates if the data is compressed on disk + IsCompressed() bool } // KeyWatcher is what is returned when doing a watch. @@ -107,6 +113,12 @@ type KeyWatcher interface { Stop() error } +// KeyLister is used to retrieve a list of key value store keys +type KeyLister interface { + Keys() <-chan string + Stop() error +} + type WatchOpt interface { configureWatcher(opts *watchOpts) error } @@ -249,6 +261,10 @@ type KeyValueConfig struct { RePublish *RePublish Mirror *StreamSource Sources []*StreamSource + + // Enable underlying stream compression. + // NOTE: Compression is supported for nats-server 2.10.0+ + Compression bool } // Used to watch all keys. @@ -343,7 +359,7 @@ func (js *js) KeyValue(bucket string) (KeyValue, error) { stream := fmt.Sprintf(kvBucketNameTmpl, bucket) si, err := js.StreamInfo(stream) if err != nil { - if err == ErrStreamNotFound { + if errors.Is(err, ErrStreamNotFound) { err = ErrBucketNotFound } return nil, err @@ -405,6 +421,10 @@ func (js *js) CreateKeyValue(cfg *KeyValueConfig) (KeyValue, error) { if cfg.TTL > 0 && cfg.TTL < duplicateWindow { duplicateWindow = cfg.TTL } + var compression StoreCompression + if cfg.Compression { + compression = S2Compression + } scfg := &StreamConfig{ Name: fmt.Sprintf(kvBucketNameTmpl, cfg.Bucket), Description: cfg.Description, @@ -422,6 +442,7 @@ func (js *js) CreateKeyValue(cfg *KeyValueConfig) (KeyValue, error) { MaxConsumers: -1, AllowDirect: true, RePublish: cfg.RePublish, + Compression: compression, } if cfg.Mirror != nil { // Copy in case we need to make changes so we do not change caller's version. @@ -465,7 +486,7 @@ func (js *js) CreateKeyValue(cfg *KeyValueConfig) (KeyValue, error) { // the stream. // The same logic applies for KVs created pre 2.9.x and // the AllowDirect setting. - if err == ErrStreamNameAlreadyInUse { + if errors.Is(err, ErrStreamNameAlreadyInUse) { if si, _ = js.StreamInfo(scfg.Name); si != nil { // To compare, make the server's stream info discard // policy same than ours. @@ -537,7 +558,7 @@ func keyValid(key string) bool { func (kv *kvs) Get(key string) (KeyValueEntry, error) { e, err := kv.get(key, kvLatestRevision) if err != nil { - if err == ErrKeyDeleted { + if errors.Is(err, ErrKeyDeleted) { return nil, ErrKeyNotFound } return nil, err @@ -550,7 +571,7 @@ func (kv *kvs) Get(key string) (KeyValueEntry, error) { func (kv *kvs) GetRevision(key string, revision uint64) (KeyValueEntry, error) { e, err := kv.get(key, revision) if err != nil { - if err == ErrKeyDeleted { + if errors.Is(err, ErrKeyDeleted) { return nil, ErrKeyNotFound } return nil, err @@ -587,7 +608,7 @@ func (kv *kvs) get(key string, revision uint64) (KeyValueEntry, error) { } } if err != nil { - if err == ErrMsgNotFound { + if errors.Is(err, ErrMsgNotFound) { err = ErrKeyNotFound } return nil, err @@ -654,7 +675,7 @@ func (kv *kvs) Create(key string, value []byte) (revision uint64, err error) { // TODO(dlc) - Since we have tombstones for DEL ops for watchers, this could be from that // so we need to double check. - if e, err := kv.get(key, kvLatestRevision); err == ErrKeyDeleted { + if e, err := kv.get(key, kvLatestRevision); errors.Is(err, ErrKeyDeleted) { return kv.Update(key, value, e.Revision()) } @@ -830,6 +851,41 @@ func (kv *kvs) Keys(opts ...WatchOpt) ([]string, error) { return keys, nil } +type keyLister struct { + watcher KeyWatcher + keys chan string +} + +// ListKeys will return all keys. +func (kv *kvs) ListKeys(opts ...WatchOpt) (KeyLister, error) { + opts = append(opts, IgnoreDeletes(), MetaOnly()) + watcher, err := kv.WatchAll(opts...) + if err != nil { + return nil, err + } + kl := &keyLister{watcher: watcher, keys: make(chan string, 256)} + + go func() { + defer close(kl.keys) + defer watcher.Stop() + for entry := range watcher.Updates() { + if entry == nil { + return + } + kl.keys <- entry.Key() + } + }() + return kl, nil +} + +func (kl *keyLister) Keys() <-chan string { + return kl.keys +} + +func (kl *keyLister) Stop() error { + return kl.watcher.Stop() +} + // History will return all values for the key. func (kv *kvs) History(key string, opts ...WatchOpt) ([]KeyValueEntry, error) { opts = append(opts, IncludeHistory()) @@ -1040,6 +1096,9 @@ func (s *KeyValueBucketStatus) StreamInfo() *StreamInfo { return s.nfo } // Bytes is the size of the stream func (s *KeyValueBucketStatus) Bytes() uint64 { return s.nfo.State.Bytes } +// IsCompressed indicates if the data is compressed on disk +func (s *KeyValueBucketStatus) IsCompressed() bool { return s.nfo.Config.Compression != NoCompression } + // Status retrieves the status and configuration of a bucket func (kv *kvs) Status() (KeyValueStatus, error) { nfo, err := kv.js.StreamInfo(kv.stream) @@ -1062,7 +1121,7 @@ func (js *js) KeyValueStoreNames() <-chan string { if !strings.HasPrefix(name, kvBucketNamePre) { continue } - ch <- name + ch <- strings.TrimPrefix(name, kvBucketNamePre) } } }() diff --git a/vendor/github.com/nats-io/nats.go/nats.go b/vendor/github.com/nats-io/nats.go/nats.go index da13692fd7..a252da2a4b 100644 --- a/vendor/github.com/nats-io/nats.go/nats.go +++ b/vendor/github.com/nats-io/nats.go/nats.go @@ -47,7 +47,7 @@ import ( // Default Constants const ( - Version = "1.31.0" + Version = "1.32.0" DefaultURL = "nats://127.0.0.1:4222" DefaultPort = 4222 DefaultMaxReconnect = 60 @@ -4298,6 +4298,12 @@ func (nc *Conn) removeSub(s *Subscription) { } } + if s.typ != AsyncSubscription { + done := s.pDone + if done != nil { + done(s.Subject) + } + } // Mark as invalid s.closed = true if s.pCond != nil { diff --git a/vendor/github.com/nats-io/nats.go/netchan.go b/vendor/github.com/nats-io/nats.go/netchan.go index 060721eb42..6b13690b4c 100644 --- a/vendor/github.com/nats-io/nats.go/netchan.go +++ b/vendor/github.com/nats-io/nats.go/netchan.go @@ -1,4 +1,4 @@ -// Copyright 2013-2022 The NATS Authors +// Copyright 2013-2023 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at diff --git a/vendor/github.com/nats-io/nats.go/object.go b/vendor/github.com/nats-io/nats.go/object.go index f6ba8fb164..92267918eb 100644 --- a/vendor/github.com/nats-io/nats.go/object.go +++ b/vendor/github.com/nats-io/nats.go/object.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022 The NATS Authors +// Copyright 2021-2023 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -377,13 +377,16 @@ func (obs *obs) Put(meta *ObjectMeta, r io.Reader, opts ...ObjectOpt) (*ObjectIn defer jetStream.(*js).cleanupReplySub() - purgePartial := func() { + purgePartial := func() error { // wait until all pubs are complete or up to default timeout before attempting purge select { case <-jetStream.PublishAsyncComplete(): case <-time.After(obs.js.opts.wait): } - obs.js.purgeStream(obs.stream, &StreamPurgeRequest{Subject: chunkSubj}) + if err := obs.js.purgeStream(obs.stream, &StreamPurgeRequest{Subject: chunkSubj}); err != nil { + return fmt.Errorf("could not cleanup bucket after erronous put operation: %w", err) + } + return nil } m, h := NewMsg(chunkSubj), sha256.New() @@ -404,7 +407,9 @@ func (obs *obs) Put(meta *ObjectMeta, r io.Reader, opts ...ObjectOpt) (*ObjectIn default: } if err != nil { - purgePartial() + if purgeErr := purgePartial(); purgeErr != nil { + return nil, errors.Join(err, purgeErr) + } return nil, err } } @@ -415,7 +420,9 @@ func (obs *obs) Put(meta *ObjectMeta, r io.Reader, opts ...ObjectOpt) (*ObjectIn // Handle all non EOF errors if readErr != nil && readErr != io.EOF { - purgePartial() + if purgeErr := purgePartial(); purgeErr != nil { + return nil, errors.Join(readErr, purgeErr) + } return nil, readErr } @@ -427,11 +434,15 @@ func (obs *obs) Put(meta *ObjectMeta, r io.Reader, opts ...ObjectOpt) (*ObjectIn // Send msg itself. if _, err := jetStream.PublishMsgAsync(m); err != nil { - purgePartial() + if purgeErr := purgePartial(); purgeErr != nil { + return nil, errors.Join(err, purgeErr) + } return nil, err } if err := getErr(); err != nil { - purgePartial() + if purgeErr := purgePartial(); purgeErr != nil { + return nil, errors.Join(err, purgeErr) + } return nil, err } // Update totals. @@ -455,7 +466,9 @@ func (obs *obs) Put(meta *ObjectMeta, r io.Reader, opts ...ObjectOpt) (*ObjectIn mm.Data, err = json.Marshal(info) if err != nil { if r != nil { - purgePartial() + if purgeErr := purgePartial(); purgeErr != nil { + return nil, errors.Join(err, purgeErr) + } } return nil, err } @@ -464,7 +477,9 @@ func (obs *obs) Put(meta *ObjectMeta, r io.Reader, opts ...ObjectOpt) (*ObjectIn _, err = jetStream.PublishMsgAsync(mm) if err != nil { if r != nil { - purgePartial() + if purgeErr := purgePartial(); purgeErr != nil { + return nil, errors.Join(err, purgeErr) + } } return nil, err } @@ -474,7 +489,9 @@ func (obs *obs) Put(meta *ObjectMeta, r io.Reader, opts ...ObjectOpt) (*ObjectIn case <-jetStream.PublishAsyncComplete(): if err := getErr(); err != nil { if r != nil { - purgePartial() + if purgeErr := purgePartial(); purgeErr != nil { + return nil, errors.Join(err, purgeErr) + } } return nil, err } @@ -487,7 +504,9 @@ func (obs *obs) Put(meta *ObjectMeta, r io.Reader, opts ...ObjectOpt) (*ObjectIn // Delete any original chunks. if einfo != nil && !einfo.Deleted { echunkSubj := fmt.Sprintf(objChunksPreTmpl, obs.name, einfo.NUID) - obs.js.purgeStream(obs.stream, &StreamPurgeRequest{Subject: echunkSubj}) + if err := obs.js.purgeStream(obs.stream, &StreamPurgeRequest{Subject: echunkSubj}); err != nil { + return info, err + } } // TODO would it be okay to do this to return the info with the correct time? @@ -626,7 +645,7 @@ func (obs *obs) Get(name string, opts ...GetObjectOpt) (ObjectResult, error) { if ctx != nil { select { case <-ctx.Done(): - if ctx.Err() == context.Canceled { + if errors.Is(ctx.Err(), context.Canceled) { err = ctx.Err() } else { err = ErrTimeout @@ -926,7 +945,7 @@ func (obs *obs) GetInfo(name string, opts ...GetObjectInfoOpt) (*ObjectInfo, err m, err := obs.js.GetLastMsg(stream, metaSubj) if err != nil { - if err == ErrMsgNotFound { + if errors.Is(err, ErrMsgNotFound) { err = ErrObjectNotFound } return nil, err diff --git a/vendor/github.com/nats-io/nats.go/timer.go b/vendor/github.com/nats-io/nats.go/timer.go index 4fb02ecb41..6edeb4cf89 100644 --- a/vendor/github.com/nats-io/nats.go/timer.go +++ b/vendor/github.com/nats-io/nats.go/timer.go @@ -29,7 +29,7 @@ type timerPool struct { // Get returns a timer that completes after the given duration. func (tp *timerPool) Get(d time.Duration) *time.Timer { - if t, _ := tp.p.Get().(*time.Timer); t != nil { + if t, ok := tp.p.Get().(*time.Timer); ok && t != nil { t.Reset(d) return t } diff --git a/vendor/modules.txt b/vendor/modules.txt index 82cf397d99..3f0e58bf44 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1401,7 +1401,7 @@ github.com/nats-io/nats-server/v2/server/certidp github.com/nats-io/nats-server/v2/server/certstore github.com/nats-io/nats-server/v2/server/pse github.com/nats-io/nats-server/v2/server/sysmem -# github.com/nats-io/nats.go v1.31.0 +# github.com/nats-io/nats.go v1.32.0 ## explicit; go 1.20 github.com/nats-io/nats.go github.com/nats-io/nats.go/encoders/builtin From cf5f1bcb8ebec2ab235c9bdf1c671c6c7ed28bce Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Tue, 19 Dec 2023 13:51:20 +0100 Subject: [PATCH 03/15] fix: share jail usage for the listSharedWithMe endpoint --- services/graph/pkg/service/v0/sharedwithme.go | 300 +++++++++++------- 1 file changed, 184 insertions(+), 116 deletions(-) diff --git a/services/graph/pkg/service/v0/sharedwithme.go b/services/graph/pkg/service/v0/sharedwithme.go index 56457e3a71..04e559c6d3 100644 --- a/services/graph/pkg/service/v0/sharedwithme.go +++ b/services/graph/pkg/service/v0/sharedwithme.go @@ -3,17 +3,19 @@ package svc import ( "context" "net/http" + "reflect" "strings" - rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" - "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/go-chi/render" libregraph "github.com/owncloud/libre-graph-api-go" + "github.com/cs3org/reva/v2/pkg/storagespace" + "github.com/cs3org/reva/v2/pkg/utils" + "github.com/owncloud/ocis/v2/ocis-pkg/conversions" "github.com/owncloud/ocis/v2/services/graph/pkg/errorcode" - "github.com/owncloud/ocis/v2/services/graph/pkg/identity" + "github.com/owncloud/ocis/v2/services/graph/pkg/unifiedrole" ) // ListSharedWithMe lists the files shared with the current user. @@ -38,185 +40,251 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er } listReceivedSharesResponse, err := gatewayClient.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{}) - if err != nil { + if errCode := errorcode.FromCS3Status(listReceivedSharesResponse.GetStatus(), err); errCode != nil { g.logger.Error().Err(err).Msg("listing shares failed") - return nil, errorcode.New(errorcode.GeneralException, err.Error()) - } - - switch listReceivedSharesResponse.Status.Code { - case rpc.Code_CODE_NOT_FOUND: - return nil, identity.ErrNotFound + return nil, *errCode } var driveItems []libregraph.DriveItem for _, receivedShare := range listReceivedSharesResponse.GetShares() { - share := receivedShare.GetShare() - if share == nil { - g.logger.Error().Interface("ListReceivedShares", listReceivedSharesResponse).Msg("unexpected empty ReceivedShare.Share") + statRequest := &storageprovider.StatRequest{} + + switch receivedShare.GetState() { + case collaboration.ShareState_SHARE_STATE_ACCEPTED: + statRequest.Ref = &storageprovider.Reference{ + ResourceId: &storageprovider.ResourceId{ + StorageId: utils.ShareStorageProviderID, + OpaqueId: receivedShare.GetShare().GetId().GetOpaqueId(), + SpaceId: utils.ShareStorageSpaceID, + }, + } + case collaboration.ShareState_SHARE_STATE_PENDING: + // return no remoteItem + fallthrough + case collaboration.ShareState_SHARE_STATE_REJECTED: + // what to return here? same as pending? + statRequest.Ref = &storageprovider.Reference{ + ResourceId: receivedShare.GetShare().GetResourceId(), + } + default: continue } - driveItem := &libregraph.DriveItem{} - - statResponse, err := gatewayClient.Stat(ctx, &storageprovider.StatRequest{Ref: &storageprovider.Reference{ResourceId: share.GetResourceId()}}) - if err != nil { + statResponse, err := gatewayClient.Stat(ctx, statRequest) + if errCode := errorcode.FromCS3Status(statResponse.GetStatus(), err); errCode != nil { g.logger.Error().Err(err).Msg("could not stat") continue } - if statResponse.GetStatus().GetCode() != rpc.Code_CODE_OK { - g.logger.Error().Err(err).Msg("invalid stat response") - continue - } - resourceInfo := statResponse.GetInfo() - var driveOwner *libregraph.Identity + var commonResourceOwner *libregraph.Identity if userID := statResponse.GetInfo().GetOwner(); userID != nil { if user, err := g.identityCache.GetUser(ctx, userID.GetOpaqueId()); err != nil { g.logger.Error().Err(err).Msg("could not get user") continue } else { - driveOwner = &libregraph.Identity{ + commonResourceOwner = &libregraph.Identity{ DisplayName: user.GetDisplayName(), Id: libregraph.PtrString(user.GetId()), } } } - var shareCreator *libregraph.Identity - if userID := share.GetCreator(); userID != nil { + var commonShareCreator *libregraph.Identity + if userID := receivedShare.GetShare().GetCreator(); userID != nil { if user, err := g.identityCache.GetUser(ctx, userID.GetOpaqueId()); err != nil { g.logger.Error().Err(err).Msg("could not get user") continue } else { - shareCreator = &libregraph.Identity{ + commonShareCreator = &libregraph.Identity{ DisplayName: user.GetDisplayName(), Id: libregraph.PtrString(user.GetId()), } } } - if cTime := share.GetCtime(); cTime != nil { - driveItem.CreatedDateTime = libregraph.PtrTime(cs3TimestampToTime(cTime)) - } - - driveItem.ETag = libregraph.PtrString(strings.Trim(statResponse.GetInfo().GetEtag(), "\"")) - - if id := share.GetId().GetOpaqueId(); id != "" { - driveItem.Id = libregraph.PtrString(id) - } - - if mTime := share.GetMtime(); mTime != nil { - driveItem.LastModifiedDateTime = libregraph.PtrTime(cs3TimestampToTime(mTime)) - } - - if name := resourceInfo.GetName(); name != "" { - driveItem.Name = libregraph.PtrString(name) - } - + var commonPermission *libregraph.Permission { - addParentReference := false - parentReference := &libregraph.ItemReference{} + permission := libregraph.NewPermission() - if id := share.GetId().GetOpaqueId(); id != "" { - parentReference.DriveId = libregraph.PtrString(id) - addParentReference = true + if id := receivedShare.GetShare().GetId().GetOpaqueId(); id != "" { + permission.SetId(id) } - if addParentReference { - driveItem.ParentReference = parentReference + if permissionSet := statResponse.GetInfo().GetPermissionSet(); permissionSet != nil { + if actions := unifiedrole.CS3ResourcePermissionsToLibregraphActions(*permissionSet); len(actions) > 0 { + permission.SetLibreGraphPermissionsActions(actions) + } + + if role := unifiedrole.CS3ResourcePermissionsToUnifiedRole( + *permissionSet, + unifiedrole.UnifiedRoleConditionGrantee, + g.config.FilesSharing.EnableResharing, + ); role != nil { + permission.SetRoles([]string{role.GetId()}) + } + } + + if expiration := receivedShare.GetShare().GetExpiration(); expiration != nil { + permission.SetExpirationDateTime(cs3TimestampToTime(expiration)) + } + + switch grantee := receivedShare.GetShare().GetGrantee(); { + case grantee.GetType() == storageprovider.GranteeType_GRANTEE_TYPE_USER: + permission.SetGrantedToV2(libregraph.SharePointIdentitySet{ + User: &libregraph.Identity{ + Id: conversions.ToPointer(grantee.GetUserId().GetOpaqueId()), + }, + }) + case grantee.GetType() == storageprovider.GranteeType_GRANTEE_TYPE_GROUP: + permission.SetGrantedToV2(libregraph.SharePointIdentitySet{ + Group: &libregraph.Identity{ + Id: conversions.ToPointer(grantee.GetGroupId().GetOpaqueId()), + }, + }) + } + + if !reflect.ValueOf(*permission).IsZero() { + commonPermission = permission } } + driveItem := libregraph.NewDriveItem() { - remoteItem := &libregraph.RemoteItem{} - - if id := resourceInfo.GetId(); id != nil { - remoteItem.Id = libregraph.PtrString(storagespace.FormatResourceID(*id)) - } - - if mTime := resourceInfo.GetMtime(); mTime != nil { - remoteItem.LastModifiedDateTime = libregraph.PtrTime(cs3TimestampToTime(mTime)) - } - - if name := resourceInfo.GetName(); name != "" { - remoteItem.Name = libregraph.PtrString(name) - } - - // fixMe: - // - negative permission could distort the size, am i right? - remoteItem.Size = libregraph.PtrInt64(int64(resourceInfo.GetSize())) - - remoteItem.CreatedBy = &libregraph.IdentitySet{ - User: driveOwner, + if commonShareCreator != nil { + driveItem.SetCreatedBy(libregraph.IdentitySet{ + User: commonShareCreator, + }) } { + parentReference := libregraph.NewItemReference() - addFileSystemInfo := false - fileSystemInfo := &libregraph.FileSystemInfo{} - - if cTime := share.GetCtime(); cTime != nil { - // fixMe: - // - ms uses the root resource ctime for that, - // the stat response does not contain any information about this, use share instead? - fileSystemInfo.CreatedDateTime = libregraph.PtrTime(cs3TimestampToTime(cTime)) - addFileSystemInfo = true + if spaceType := statResponse.GetInfo().GetSpace().GetSpaceType(); spaceType != "" { + parentReference.SetDriveType(spaceType) } - if mTime := resourceInfo.GetMtime(); mTime != nil { - fileSystemInfo.LastModifiedDateTime = libregraph.PtrTime(cs3TimestampToTime(mTime)) - addFileSystemInfo = true + if root := statResponse.GetInfo().GetSpace().GetRoot(); root != nil { + parentReference.SetDriveId(storagespace.FormatResourceID(*root)) } - if addFileSystemInfo { - remoteItem.FileSystemInfo = fileSystemInfo + if !reflect.ValueOf(*parentReference).IsZero() { + driveItem.ParentReference = parentReference } } + } - switch resourceInfo.GetType() { - case storageprovider.ResourceType_RESOURCE_TYPE_CONTAINER: - remoteItem.Folder = &libregraph.Folder{} - case storageprovider.ResourceType_RESOURCE_TYPE_FILE: - openGraphFile := &libregraph.OpenGraphFile{} + switch receivedShare.GetState() { + case collaboration.ShareState_SHARE_STATE_ACCEPTED: + if resourceID := statRequest.GetRef().GetResourceId(); resourceID != nil { + driveItem.SetId(storagespace.FormatResourceID(*resourceID)) + } - if mimeType := resourceInfo.GetMimeType(); mimeType != "" { - openGraphFile.MimeType = libregraph.PtrString(mimeType) - } + if name := receivedShare.GetMountPoint().GetPath(); name != "" { + driveItem.SetName(receivedShare.GetMountPoint().GetPath()) + } - remoteItem.File = openGraphFile - case storageprovider.ResourceType_RESOURCE_TYPE_INVALID: - g.logger.Error().Msg("invalid resource type") - continue + if mTime := receivedShare.GetShare().GetMtime(); mTime != nil { + driveItem.SetLastModifiedDateTime(cs3TimestampToTime(mTime)) + } + + if cTime := receivedShare.GetShare().GetCtime(); cTime != nil { + driveItem.SetCreatedDateTime(cs3TimestampToTime(cTime)) } { - addShared := false - shared := &libregraph.Shared{ - Owner: &libregraph.IdentitySet{ - User: shareCreator, - }, - SharedBy: &libregraph.IdentitySet{ - User: shareCreator, - }, + remoteItem := libregraph.NewRemoteItem() + + if id := statResponse.GetInfo().GetId(); id != nil { + remoteItem.SetId(storagespace.FormatResourceID(*id)) } - if cTime := share.GetCtime(); cTime != nil { - shared.SharedDateTime = libregraph.PtrTime(cs3TimestampToTime(cTime)) - addShared = true + if mTime := statResponse.GetInfo().GetMtime(); mTime != nil { + remoteItem.SetLastModifiedDateTime(cs3TimestampToTime(mTime)) } - if shareCreator != nil { - shared.Owner.User = shareCreator - shared.SharedBy.User = shareCreator - addShared = true + if name := statResponse.GetInfo().GetName(); name != "" { + remoteItem.SetName(name) } - if addShared { - remoteItem.Shared = shared + if size := statResponse.GetInfo().GetSize(); size != 0 { + remoteItem.SetSize(int64(size)) + } + + if etag := statResponse.GetInfo().GetEtag(); etag != "" { + remoteItem.SetETag(strings.Trim(etag, "\"")) + } + + if commonResourceOwner != nil { + remoteItem.SetCreatedBy(libregraph.IdentitySet{ + User: commonResourceOwner, + }) + } + + switch info := statResponse.GetInfo(); { + case info.GetType() == storageprovider.ResourceType_RESOURCE_TYPE_CONTAINER: + remoteItem.Folder = libregraph.NewFolder() + case info.GetType() == storageprovider.ResourceType_RESOURCE_TYPE_FILE: + openGraphFile := libregraph.NewOpenGraphFile() + + if mimeType := info.GetMimeType(); mimeType != "" { + openGraphFile.MimeType = &mimeType + } + + remoteItem.File = openGraphFile + case info.GetType() == storageprovider.ResourceType_RESOURCE_TYPE_INVALID: + g.logger.Info().Interface("info", info).Msg("invalid resource type") + } + + if commonPermission != nil { + remoteItem.SetPermissions([]libregraph.Permission{*commonPermission}) + } + + { + shared := libregraph.NewShared() + + if cTime := receivedShare.GetShare().GetCtime(); cTime != nil { + shared.SetSharedDateTime(cs3TimestampToTime(cTime)) + } + + if commonResourceOwner != nil { + shared.SetOwner(libregraph.IdentitySet{ + User: commonResourceOwner, + }) + } + + if commonShareCreator != nil { + shared.SetSharedBy(libregraph.IdentitySet{ + User: commonShareCreator, + }) + } + + if !reflect.ValueOf(*shared).IsZero() { + remoteItem.SetShared(*shared) + } + } + + if !reflect.ValueOf(*remoteItem).IsZero() { + driveItem.SetRemoteItem(*remoteItem) } } + case collaboration.ShareState_SHARE_STATE_PENDING: + fallthrough + case collaboration.ShareState_SHARE_STATE_REJECTED: + if id := statResponse.GetInfo().GetId(); id != nil { + driveItem.SetId(storagespace.FormatResourceID(*id)) + } - driveItem.RemoteItem = remoteItem + if name := statResponse.GetInfo().GetName(); name != "" { + driveItem.SetName(name) + } + + if mTime := statResponse.GetInfo().GetMtime(); mTime != nil { + driveItem.SetLastModifiedDateTime(cs3TimestampToTime(mTime)) + } + + if commonPermission != nil { + driveItem.SetPermissions([]libregraph.Permission{*commonPermission}) + } } driveItems = append(driveItems, *driveItem) From 842baaf10fd03740d69c870498a382428a8dbfc4 Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Fri, 22 Dec 2023 15:53:39 +0100 Subject: [PATCH 04/15] fix: bump libre-graph-api-go --- go.mod | 2 +- go.sum | 4 +- .../owncloud/libre-graph-api-go/README.md | 6 +- .../libre-graph-api-go/api_drive_item.go | 132 +++++ .../libre-graph-api-go/api_drives_root.go | 121 +++++ .../owncloud/libre-graph-api-go/client.go | 3 + .../model_directory_object.go | 162 ------- .../libre-graph-api-go/model_drive_item.go | 36 ++ .../model_education_organization.go | 162 ------- .../libre-graph-api-go/model_entity.go | 125 ----- .../libre-graph-api-go/model_permission.go | 71 ++- .../libre-graph-api-go/model_remote_item.go | 37 ++ .../libre-graph-api-go/model_video.go | 458 ++++++++++++++++++ vendor/modules.txt | 2 +- 14 files changed, 848 insertions(+), 473 deletions(-) create mode 100644 vendor/github.com/owncloud/libre-graph-api-go/api_drive_item.go delete mode 100644 vendor/github.com/owncloud/libre-graph-api-go/model_directory_object.go delete mode 100644 vendor/github.com/owncloud/libre-graph-api-go/model_education_organization.go delete mode 100644 vendor/github.com/owncloud/libre-graph-api-go/model_entity.go create mode 100644 vendor/github.com/owncloud/libre-graph-api-go/model_video.go diff --git a/go.mod b/go.mod index 4bf841a186..7191b66142 100644 --- a/go.mod +++ b/go.mod @@ -69,7 +69,7 @@ require ( github.com/onsi/gomega v1.30.0 github.com/open-policy-agent/opa v0.60.0 github.com/orcaman/concurrent-map v1.0.0 - github.com/owncloud/libre-graph-api-go v1.0.5-0.20231201125350-a08244876423 + github.com/owncloud/libre-graph-api-go v1.0.5-0.20240115110609-b018a896364e github.com/pkg/errors v0.9.1 github.com/pkg/xattr v0.4.9 github.com/prometheus/client_golang v1.18.0 diff --git a/go.sum b/go.sum index 7875677cda..8dd0c246a9 100644 --- a/go.sum +++ b/go.sum @@ -1789,8 +1789,8 @@ github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35uk github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HDbW65HOY= github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA= -github.com/owncloud/libre-graph-api-go v1.0.5-0.20231201125350-a08244876423 h1:G3i2n+lY6cTEerVEearRliEGeAxFuFQN0qM/1mdCQvs= -github.com/owncloud/libre-graph-api-go v1.0.5-0.20231201125350-a08244876423/go.mod h1:v2aAl5IwEI8t+GmcWvBd+bvJMYp9Vf1hekLuRf0UnEs= +github.com/owncloud/libre-graph-api-go v1.0.5-0.20240115110609-b018a896364e h1:Y7MEJYsPe+UGlmU7oR9LaBIsQQYTwCq4a/p0JCia2cA= +github.com/owncloud/libre-graph-api-go v1.0.5-0.20240115110609-b018a896364e/go.mod h1:v2aAl5IwEI8t+GmcWvBd+bvJMYp9Vf1hekLuRf0UnEs= 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/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= diff --git a/vendor/github.com/owncloud/libre-graph-api-go/README.md b/vendor/github.com/owncloud/libre-graph-api-go/README.md index 322d7a4658..e574cacf04 100644 --- a/vendor/github.com/owncloud/libre-graph-api-go/README.md +++ b/vendor/github.com/owncloud/libre-graph-api-go/README.md @@ -79,6 +79,7 @@ Class | Method | HTTP request | Description ------------ | ------------- | ------------- | ------------- *ApplicationsApi* | [**GetApplication**](docs/ApplicationsApi.md#getapplication) | **Get** /v1.0/applications/{application-id} | Get application by id *ApplicationsApi* | [**ListApplications**](docs/ApplicationsApi.md#listapplications) | **Get** /v1.0/applications | Get all applications +*DriveItemApi* | [**DeleteDriveItem**](docs/DriveItemApi.md#deletedriveitem) | **Delete** /v1beta1/drives/{drive-id}/items/{item-id} | Delete a DriveItem. *DrivesApi* | [**CreateDrive**](docs/DrivesApi.md#createdrive) | **Post** /v1.0/drives | Create a new drive of a specific type *DrivesApi* | [**DeleteDrive**](docs/DrivesApi.md#deletedrive) | **Delete** /v1.0/drives/{drive-id} | Delete a specific space *DrivesApi* | [**GetDrive**](docs/DrivesApi.md#getdrive) | **Get** /v1.0/drives/{drive-id} | Get drive by id @@ -92,6 +93,7 @@ Class | Method | HTTP request | Description *DrivesPermissionsApi* | [**ListPermissions**](docs/DrivesPermissionsApi.md#listpermissions) | **Get** /v1beta1/drives/{drive-id}/items/{item-id}/permissions | List the effective sharing permissions on a driveItem. *DrivesPermissionsApi* | [**SetPermissionPassword**](docs/DrivesPermissionsApi.md#setpermissionpassword) | **Post** /v1beta1/drives/{drive-id}/items/{item-id}/permissions/{perm-id}/setPassword | Set sharing link password *DrivesPermissionsApi* | [**UpdatePermission**](docs/DrivesPermissionsApi.md#updatepermission) | **Patch** /v1beta1/drives/{drive-id}/items/{item-id}/permissions/{perm-id} | Update sharing permission +*DrivesRootApi* | [**CreateDriveItem**](docs/DrivesRootApi.md#createdriveitem) | **Post** /v1beta1/drives/{drive-id}/root/children | Create a drive item *DrivesRootApi* | [**GetRoot**](docs/DrivesRootApi.md#getroot) | **Get** /v1.0/drives/{drive-id}/root | Get root from arbitrary space *EducationClassApi* | [**AddUserToClass**](docs/EducationClassApi.md#addusertoclass) | **Post** /v1.0/education/classes/{class-id}/members/$ref | Assign a user to a class *EducationClassApi* | [**CreateClass**](docs/EducationClassApi.md#createclass) | **Post** /v1.0/education/classes | Add new education class @@ -180,18 +182,15 @@ Class | Method | HTTP request | Description - [CollectionOfUser](docs/CollectionOfUser.md) - [CollectionOfUsers](docs/CollectionOfUsers.md) - [Deleted](docs/Deleted.md) - - [DirectoryObject](docs/DirectoryObject.md) - [Drive](docs/Drive.md) - [DriveItem](docs/DriveItem.md) - [DriveItemCreateLink](docs/DriveItemCreateLink.md) - [DriveItemInvite](docs/DriveItemInvite.md) - [DriveRecipient](docs/DriveRecipient.md) - [EducationClass](docs/EducationClass.md) - - [EducationOrganization](docs/EducationOrganization.md) - [EducationSchool](docs/EducationSchool.md) - [EducationUser](docs/EducationUser.md) - [EducationUserReference](docs/EducationUserReference.md) - - [Entity](docs/Entity.md) - [ExportPersonalDataRequest](docs/ExportPersonalDataRequest.md) - [FileSystemInfo](docs/FileSystemInfo.md) - [Folder](docs/Folder.md) @@ -227,6 +226,7 @@ Class | Method | HTTP request | Description - [UnifiedRoleDefinition](docs/UnifiedRoleDefinition.md) - [UnifiedRolePermission](docs/UnifiedRolePermission.md) - [User](docs/User.md) + - [Video](docs/Video.md) ## Documentation For Authorization diff --git a/vendor/github.com/owncloud/libre-graph-api-go/api_drive_item.go b/vendor/github.com/owncloud/libre-graph-api-go/api_drive_item.go new file mode 100644 index 0000000000..aa152a84ac --- /dev/null +++ b/vendor/github.com/owncloud/libre-graph-api-go/api_drive_item.go @@ -0,0 +1,132 @@ +/* +Libre Graph API + +Libre Graph is a free API for cloud collaboration inspired by the MS Graph API. + +API version: v1.0.4 +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package libregraph + +import ( + "bytes" + "context" + "io" + "net/http" + "net/url" + "strings" +) + +// DriveItemApiService DriveItemApi service +type DriveItemApiService service + +type ApiDeleteDriveItemRequest struct { + ctx context.Context + ApiService *DriveItemApiService + driveId string + itemId string +} + +func (r ApiDeleteDriveItemRequest) Execute() (*http.Response, error) { + return r.ApiService.DeleteDriveItemExecute(r) +} + +/* +DeleteDriveItem Delete a DriveItem. + +Delete a DriveItem by using its ID. + +Deleting items using this method moves the items to the recycle bin instead of permanently deleting the item. + +Mounted shares in the share jail are unmounted. The `@client.synchronize` property of the `remoteItem` in the [sharedWithMe](#/me.drive/ListSharedWithMe) endpoint will change to false. + + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param driveId key: id of drive + @param itemId key: id of item + @return ApiDeleteDriveItemRequest +*/ +func (a *DriveItemApiService) DeleteDriveItem(ctx context.Context, driveId string, itemId string) ApiDeleteDriveItemRequest { + return ApiDeleteDriveItemRequest{ + ApiService: a, + ctx: ctx, + driveId: driveId, + itemId: itemId, + } +} + +// Execute executes the request +func (a *DriveItemApiService) DeleteDriveItemExecute(r ApiDeleteDriveItemRequest) (*http.Response, error) { + var ( + localVarHTTPMethod = http.MethodDelete + localVarPostBody interface{} + formFiles []formFile + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "DriveItemApiService.DeleteDriveItem") + if err != nil { + return nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/v1beta1/drives/{drive-id}/items/{item-id}" + localVarPath = strings.Replace(localVarPath, "{"+"drive-id"+"}", url.PathEscape(parameterValueToString(r.driveId, "driveId")), -1) + localVarPath = strings.Replace(localVarPath, "{"+"item-id"+"}", url.PathEscape(parameterValueToString(r.itemId, "itemId")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + var v OdataError + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarHTTPResponse, newErr + } + + return localVarHTTPResponse, nil +} diff --git a/vendor/github.com/owncloud/libre-graph-api-go/api_drives_root.go b/vendor/github.com/owncloud/libre-graph-api-go/api_drives_root.go index fcb4f104ae..82314a3116 100644 --- a/vendor/github.com/owncloud/libre-graph-api-go/api_drives_root.go +++ b/vendor/github.com/owncloud/libre-graph-api-go/api_drives_root.go @@ -22,6 +22,127 @@ import ( // DrivesRootApiService DrivesRootApi service type DrivesRootApiService service +type ApiCreateDriveItemRequest struct { + ctx context.Context + ApiService *DrivesRootApiService + driveId string + driveItem *DriveItem +} + +// In the request body, provide a JSON object with the following parameters. For mounting a share the necessary remoteItem id and permission id can be taken from the [sharedWithMe](#/me.drive/ListSharedWithMe) endpoint. +func (r ApiCreateDriveItemRequest) DriveItem(driveItem DriveItem) ApiCreateDriveItemRequest { + r.driveItem = &driveItem + return r +} + +func (r ApiCreateDriveItemRequest) Execute() (*DriveItem, *http.Response, error) { + return r.ApiService.CreateDriveItemExecute(r) +} + +/* +CreateDriveItem Create a drive item + +You can use the root childrens endpoint to mount a remoteItem in the share jail. The `@client.synchronize` property of the `remoteItem` in the [sharedWithMe](#/me.drive/ListSharedWithMe) endpoint will change to true. + + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param driveId key: id of drive + @return ApiCreateDriveItemRequest +*/ +func (a *DrivesRootApiService) CreateDriveItem(ctx context.Context, driveId string) ApiCreateDriveItemRequest { + return ApiCreateDriveItemRequest{ + ApiService: a, + ctx: ctx, + driveId: driveId, + } +} + +// Execute executes the request +// @return DriveItem +func (a *DrivesRootApiService) CreateDriveItemExecute(r ApiCreateDriveItemRequest) (*DriveItem, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *DriveItem + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "DrivesRootApiService.CreateDriveItem") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/v1beta1/drives/{drive-id}/root/children" + localVarPath = strings.Replace(localVarPath, "{"+"drive-id"+"}", url.PathEscape(parameterValueToString(r.driveId, "driveId")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + // body params + localVarPostBody = r.driveItem + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + var v OdataError + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + type ApiGetRootRequest struct { ctx context.Context ApiService *DrivesRootApiService diff --git a/vendor/github.com/owncloud/libre-graph-api-go/client.go b/vendor/github.com/owncloud/libre-graph-api-go/client.go index 1f07d0a65d..0d0a430c1a 100644 --- a/vendor/github.com/owncloud/libre-graph-api-go/client.go +++ b/vendor/github.com/owncloud/libre-graph-api-go/client.go @@ -50,6 +50,8 @@ type APIClient struct { ApplicationsApi *ApplicationsApiService + DriveItemApi *DriveItemApiService + DrivesApi *DrivesApiService DrivesGetDrivesApi *DrivesGetDrivesApiService @@ -110,6 +112,7 @@ func NewAPIClient(cfg *Configuration) *APIClient { // API Services c.ApplicationsApi = (*ApplicationsApiService)(&c.common) + c.DriveItemApi = (*DriveItemApiService)(&c.common) c.DrivesApi = (*DrivesApiService)(&c.common) c.DrivesGetDrivesApi = (*DrivesGetDrivesApiService)(&c.common) c.DrivesPermissionsApi = (*DrivesPermissionsApiService)(&c.common) diff --git a/vendor/github.com/owncloud/libre-graph-api-go/model_directory_object.go b/vendor/github.com/owncloud/libre-graph-api-go/model_directory_object.go deleted file mode 100644 index 12171cb033..0000000000 --- a/vendor/github.com/owncloud/libre-graph-api-go/model_directory_object.go +++ /dev/null @@ -1,162 +0,0 @@ -/* -Libre Graph API - -Libre Graph is a free API for cloud collaboration inspired by the MS Graph API. - -API version: v1.0.4 -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package libregraph - -import ( - "encoding/json" - "time" -) - -// checks if the DirectoryObject type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &DirectoryObject{} - -// DirectoryObject Represents a Directory object. Read-only. -type DirectoryObject struct { - // The unique identifier for the object. 12345678-9abc-def0-1234-56789abcde. The value of the ID property is often, but not exclusively, in the form of a GUID. The value should be treated as an opaque identifier and not based in being a GUID. Null values are not allowed. Read-only. - Id *string `json:"id,omitempty"` - DeletedDateTime *time.Time `json:"deletedDateTime,omitempty"` -} - -// NewDirectoryObject instantiates a new DirectoryObject object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewDirectoryObject() *DirectoryObject { - this := DirectoryObject{} - return &this -} - -// NewDirectoryObjectWithDefaults instantiates a new DirectoryObject object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewDirectoryObjectWithDefaults() *DirectoryObject { - this := DirectoryObject{} - return &this -} - -// GetId returns the Id field value if set, zero value otherwise. -func (o *DirectoryObject) GetId() string { - if o == nil || IsNil(o.Id) { - var ret string - return ret - } - return *o.Id -} - -// GetIdOk returns a tuple with the Id field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *DirectoryObject) GetIdOk() (*string, bool) { - if o == nil || IsNil(o.Id) { - return nil, false - } - return o.Id, true -} - -// HasId returns a boolean if a field has been set. -func (o *DirectoryObject) HasId() bool { - if o != nil && !IsNil(o.Id) { - return true - } - - return false -} - -// SetId gets a reference to the given string and assigns it to the Id field. -func (o *DirectoryObject) SetId(v string) { - o.Id = &v -} - -// GetDeletedDateTime returns the DeletedDateTime field value if set, zero value otherwise. -func (o *DirectoryObject) GetDeletedDateTime() time.Time { - if o == nil || IsNil(o.DeletedDateTime) { - var ret time.Time - return ret - } - return *o.DeletedDateTime -} - -// GetDeletedDateTimeOk returns a tuple with the DeletedDateTime field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *DirectoryObject) GetDeletedDateTimeOk() (*time.Time, bool) { - if o == nil || IsNil(o.DeletedDateTime) { - return nil, false - } - return o.DeletedDateTime, true -} - -// HasDeletedDateTime returns a boolean if a field has been set. -func (o *DirectoryObject) HasDeletedDateTime() bool { - if o != nil && !IsNil(o.DeletedDateTime) { - return true - } - - return false -} - -// SetDeletedDateTime gets a reference to the given time.Time and assigns it to the DeletedDateTime field. -func (o *DirectoryObject) SetDeletedDateTime(v time.Time) { - o.DeletedDateTime = &v -} - -func (o DirectoryObject) MarshalJSON() ([]byte, error) { - toSerialize, err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o DirectoryObject) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - if !IsNil(o.Id) { - toSerialize["id"] = o.Id - } - if !IsNil(o.DeletedDateTime) { - toSerialize["deletedDateTime"] = o.DeletedDateTime - } - return toSerialize, nil -} - -type NullableDirectoryObject struct { - value *DirectoryObject - isSet bool -} - -func (v NullableDirectoryObject) Get() *DirectoryObject { - return v.value -} - -func (v *NullableDirectoryObject) Set(val *DirectoryObject) { - v.value = val - v.isSet = true -} - -func (v NullableDirectoryObject) IsSet() bool { - return v.isSet -} - -func (v *NullableDirectoryObject) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableDirectoryObject(val *DirectoryObject) *NullableDirectoryObject { - return &NullableDirectoryObject{value: val, isSet: true} -} - -func (v NullableDirectoryObject) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableDirectoryObject) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/vendor/github.com/owncloud/libre-graph-api-go/model_drive_item.go b/vendor/github.com/owncloud/libre-graph-api-go/model_drive_item.go index 52750b1fea..061a4f8473 100644 --- a/vendor/github.com/owncloud/libre-graph-api-go/model_drive_item.go +++ b/vendor/github.com/owncloud/libre-graph-api-go/model_drive_item.go @@ -62,6 +62,7 @@ type DriveItem struct { // The set of permissions for the item. Read-only. Nullable. Permissions []Permission `json:"permissions,omitempty"` Audio *Audio `json:"audio,omitempty"` + Video *Video `json:"video,omitempty"` } // NewDriveItem instantiates a new DriveItem object @@ -977,6 +978,38 @@ func (o *DriveItem) SetAudio(v Audio) { o.Audio = &v } +// GetVideo returns the Video field value if set, zero value otherwise. +func (o *DriveItem) GetVideo() Video { + if o == nil || IsNil(o.Video) { + var ret Video + return ret + } + return *o.Video +} + +// GetVideoOk returns a tuple with the Video field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *DriveItem) GetVideoOk() (*Video, bool) { + if o == nil || IsNil(o.Video) { + return nil, false + } + return o.Video, true +} + +// HasVideo returns a boolean if a field has been set. +func (o *DriveItem) HasVideo() bool { + if o != nil && !IsNil(o.Video) { + return true + } + + return false +} + +// SetVideo gets a reference to the given Video and assigns it to the Video field. +func (o *DriveItem) SetVideo(v Video) { + o.Video = &v +} + func (o DriveItem) MarshalJSON() ([]byte, error) { toSerialize, err := o.ToMap() if err != nil { @@ -1071,6 +1104,9 @@ func (o DriveItem) ToMap() (map[string]interface{}, error) { if !IsNil(o.Audio) { toSerialize["audio"] = o.Audio } + if !IsNil(o.Video) { + toSerialize["video"] = o.Video + } return toSerialize, nil } diff --git a/vendor/github.com/owncloud/libre-graph-api-go/model_education_organization.go b/vendor/github.com/owncloud/libre-graph-api-go/model_education_organization.go deleted file mode 100644 index 15ddb74f7c..0000000000 --- a/vendor/github.com/owncloud/libre-graph-api-go/model_education_organization.go +++ /dev/null @@ -1,162 +0,0 @@ -/* -Libre Graph API - -Libre Graph is a free API for cloud collaboration inspired by the MS Graph API. - -API version: v1.0.4 -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package libregraph - -import ( - "encoding/json" -) - -// checks if the EducationOrganization type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &EducationOrganization{} - -// EducationOrganization Abstract. Represents an organization in educational context -type EducationOrganization struct { - // The unique idenfier for an entity. Read-only. - Id *string `json:"id,omitempty"` - // The organization name - DisplayName *string `json:"displayName,omitempty"` -} - -// NewEducationOrganization instantiates a new EducationOrganization object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewEducationOrganization() *EducationOrganization { - this := EducationOrganization{} - return &this -} - -// NewEducationOrganizationWithDefaults instantiates a new EducationOrganization object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewEducationOrganizationWithDefaults() *EducationOrganization { - this := EducationOrganization{} - return &this -} - -// GetId returns the Id field value if set, zero value otherwise. -func (o *EducationOrganization) GetId() string { - if o == nil || IsNil(o.Id) { - var ret string - return ret - } - return *o.Id -} - -// GetIdOk returns a tuple with the Id field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *EducationOrganization) GetIdOk() (*string, bool) { - if o == nil || IsNil(o.Id) { - return nil, false - } - return o.Id, true -} - -// HasId returns a boolean if a field has been set. -func (o *EducationOrganization) HasId() bool { - if o != nil && !IsNil(o.Id) { - return true - } - - return false -} - -// SetId gets a reference to the given string and assigns it to the Id field. -func (o *EducationOrganization) SetId(v string) { - o.Id = &v -} - -// GetDisplayName returns the DisplayName field value if set, zero value otherwise. -func (o *EducationOrganization) GetDisplayName() string { - if o == nil || IsNil(o.DisplayName) { - var ret string - return ret - } - return *o.DisplayName -} - -// GetDisplayNameOk returns a tuple with the DisplayName field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *EducationOrganization) GetDisplayNameOk() (*string, bool) { - if o == nil || IsNil(o.DisplayName) { - return nil, false - } - return o.DisplayName, true -} - -// HasDisplayName returns a boolean if a field has been set. -func (o *EducationOrganization) HasDisplayName() bool { - if o != nil && !IsNil(o.DisplayName) { - return true - } - - return false -} - -// SetDisplayName gets a reference to the given string and assigns it to the DisplayName field. -func (o *EducationOrganization) SetDisplayName(v string) { - o.DisplayName = &v -} - -func (o EducationOrganization) MarshalJSON() ([]byte, error) { - toSerialize, err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o EducationOrganization) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - if !IsNil(o.Id) { - toSerialize["id"] = o.Id - } - if !IsNil(o.DisplayName) { - toSerialize["displayName"] = o.DisplayName - } - return toSerialize, nil -} - -type NullableEducationOrganization struct { - value *EducationOrganization - isSet bool -} - -func (v NullableEducationOrganization) Get() *EducationOrganization { - return v.value -} - -func (v *NullableEducationOrganization) Set(val *EducationOrganization) { - v.value = val - v.isSet = true -} - -func (v NullableEducationOrganization) IsSet() bool { - return v.isSet -} - -func (v *NullableEducationOrganization) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableEducationOrganization(val *EducationOrganization) *NullableEducationOrganization { - return &NullableEducationOrganization{value: val, isSet: true} -} - -func (v NullableEducationOrganization) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableEducationOrganization) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/vendor/github.com/owncloud/libre-graph-api-go/model_entity.go b/vendor/github.com/owncloud/libre-graph-api-go/model_entity.go deleted file mode 100644 index fa4264021a..0000000000 --- a/vendor/github.com/owncloud/libre-graph-api-go/model_entity.go +++ /dev/null @@ -1,125 +0,0 @@ -/* -Libre Graph API - -Libre Graph is a free API for cloud collaboration inspired by the MS Graph API. - -API version: v1.0.4 -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package libregraph - -import ( - "encoding/json" -) - -// checks if the Entity type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &Entity{} - -// Entity Represents an entity. -type Entity struct { - // The unique idenfier for an entity. Read-only. - Id *string `json:"id,omitempty"` -} - -// NewEntity instantiates a new Entity object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewEntity() *Entity { - this := Entity{} - return &this -} - -// NewEntityWithDefaults instantiates a new Entity object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewEntityWithDefaults() *Entity { - this := Entity{} - return &this -} - -// GetId returns the Id field value if set, zero value otherwise. -func (o *Entity) GetId() string { - if o == nil || IsNil(o.Id) { - var ret string - return ret - } - return *o.Id -} - -// GetIdOk returns a tuple with the Id field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Entity) GetIdOk() (*string, bool) { - if o == nil || IsNil(o.Id) { - return nil, false - } - return o.Id, true -} - -// HasId returns a boolean if a field has been set. -func (o *Entity) HasId() bool { - if o != nil && !IsNil(o.Id) { - return true - } - - return false -} - -// SetId gets a reference to the given string and assigns it to the Id field. -func (o *Entity) SetId(v string) { - o.Id = &v -} - -func (o Entity) MarshalJSON() ([]byte, error) { - toSerialize, err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o Entity) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - if !IsNil(o.Id) { - toSerialize["id"] = o.Id - } - return toSerialize, nil -} - -type NullableEntity struct { - value *Entity - isSet bool -} - -func (v NullableEntity) Get() *Entity { - return v.value -} - -func (v *NullableEntity) Set(val *Entity) { - v.value = val - v.isSet = true -} - -func (v NullableEntity) IsSet() bool { - return v.isSet -} - -func (v *NullableEntity) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableEntity(val *Entity) *NullableEntity { - return &NullableEntity{value: val, isSet: true} -} - -func (v NullableEntity) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableEntity) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/vendor/github.com/owncloud/libre-graph-api-go/model_permission.go b/vendor/github.com/owncloud/libre-graph-api-go/model_permission.go index 6dd9880d11..546369acfb 100644 --- a/vendor/github.com/owncloud/libre-graph-api-go/model_permission.go +++ b/vendor/github.com/owncloud/libre-graph-api-go/model_permission.go @@ -34,8 +34,10 @@ type Permission struct { GrantedToIdentities []IdentitySet `json:"grantedToIdentities,omitempty"` // Use this to create a permission with custom actions. LibreGraphPermissionsActions []string `json:"@libre.graph.permissions.actions,omitempty"` + // Indicates if the item is synchronized with the underlying storage provider. Read-only. + ClientSynchronize *bool `json:"@client.synchronize,omitempty"` // Properties or facets (see UI.Facet) annotated with this term will not be rendered if the annotation evaluates to true. Users can set this to hide permissons. - UIHidden *bool `json:"@UI.Hidden,omitempty"` + UiHidden *bool `json:"@ui.hidden,omitempty"` } // NewPermission instantiates a new Permission object @@ -325,36 +327,68 @@ func (o *Permission) SetLibreGraphPermissionsActions(v []string) { o.LibreGraphPermissionsActions = v } -// GetUIHidden returns the UIHidden field value if set, zero value otherwise. -func (o *Permission) GetUIHidden() bool { - if o == nil || IsNil(o.UIHidden) { +// GetClientSynchronize returns the ClientSynchronize field value if set, zero value otherwise. +func (o *Permission) GetClientSynchronize() bool { + if o == nil || IsNil(o.ClientSynchronize) { var ret bool return ret } - return *o.UIHidden + return *o.ClientSynchronize } -// GetUIHiddenOk returns a tuple with the UIHidden field value if set, nil otherwise +// GetClientSynchronizeOk returns a tuple with the ClientSynchronize field value if set, nil otherwise // and a boolean to check if the value has been set. -func (o *Permission) GetUIHiddenOk() (*bool, bool) { - if o == nil || IsNil(o.UIHidden) { +func (o *Permission) GetClientSynchronizeOk() (*bool, bool) { + if o == nil || IsNil(o.ClientSynchronize) { return nil, false } - return o.UIHidden, true + return o.ClientSynchronize, true } -// HasUIHidden returns a boolean if a field has been set. -func (o *Permission) HasUIHidden() bool { - if o != nil && !IsNil(o.UIHidden) { +// HasClientSynchronize returns a boolean if a field has been set. +func (o *Permission) HasClientSynchronize() bool { + if o != nil && !IsNil(o.ClientSynchronize) { return true } return false } -// SetUIHidden gets a reference to the given bool and assigns it to the UIHidden field. -func (o *Permission) SetUIHidden(v bool) { - o.UIHidden = &v +// SetClientSynchronize gets a reference to the given bool and assigns it to the ClientSynchronize field. +func (o *Permission) SetClientSynchronize(v bool) { + o.ClientSynchronize = &v +} + +// GetUiHidden returns the UiHidden field value if set, zero value otherwise. +func (o *Permission) GetUiHidden() bool { + if o == nil || IsNil(o.UiHidden) { + var ret bool + return ret + } + return *o.UiHidden +} + +// GetUiHiddenOk returns a tuple with the UiHidden field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Permission) GetUiHiddenOk() (*bool, bool) { + if o == nil || IsNil(o.UiHidden) { + return nil, false + } + return o.UiHidden, true +} + +// HasUiHidden returns a boolean if a field has been set. +func (o *Permission) HasUiHidden() bool { + if o != nil && !IsNil(o.UiHidden) { + return true + } + + return false +} + +// SetUiHidden gets a reference to the given bool and assigns it to the UiHidden field. +func (o *Permission) SetUiHidden(v bool) { + o.UiHidden = &v } func (o Permission) MarshalJSON() ([]byte, error) { @@ -391,8 +425,11 @@ func (o Permission) ToMap() (map[string]interface{}, error) { if !IsNil(o.LibreGraphPermissionsActions) { toSerialize["@libre.graph.permissions.actions"] = o.LibreGraphPermissionsActions } - if !IsNil(o.UIHidden) { - toSerialize["@UI.Hidden"] = o.UIHidden + if !IsNil(o.ClientSynchronize) { + toSerialize["@client.synchronize"] = o.ClientSynchronize + } + if !IsNil(o.UiHidden) { + toSerialize["@ui.hidden"] = o.UiHidden } return toSerialize, nil } diff --git a/vendor/github.com/owncloud/libre-graph-api-go/model_remote_item.go b/vendor/github.com/owncloud/libre-graph-api-go/model_remote_item.go index b3bd05c324..a188a36a6f 100644 --- a/vendor/github.com/owncloud/libre-graph-api-go/model_remote_item.go +++ b/vendor/github.com/owncloud/libre-graph-api-go/model_remote_item.go @@ -46,6 +46,8 @@ type RemoteItem struct { CTag *string `json:"cTag,omitempty"` ParentReference *ItemReference `json:"parentReference,omitempty"` Shared *Shared `json:"shared,omitempty"` + // The set of permissions for the item. Read-only. Nullable. + Permissions []Permission `json:"permissions,omitempty"` // Size of the remote item. Read-only. Size *int64 `json:"size,omitempty"` SpecialFolder *SpecialFolder `json:"specialFolder,omitempty"` @@ -616,6 +618,38 @@ func (o *RemoteItem) SetShared(v Shared) { o.Shared = &v } +// GetPermissions returns the Permissions field value if set, zero value otherwise. +func (o *RemoteItem) GetPermissions() []Permission { + if o == nil || IsNil(o.Permissions) { + var ret []Permission + return ret + } + return o.Permissions +} + +// GetPermissionsOk returns a tuple with the Permissions field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *RemoteItem) GetPermissionsOk() ([]Permission, bool) { + if o == nil || IsNil(o.Permissions) { + return nil, false + } + return o.Permissions, true +} + +// HasPermissions returns a boolean if a field has been set. +func (o *RemoteItem) HasPermissions() bool { + if o != nil && !IsNil(o.Permissions) { + return true + } + + return false +} + +// SetPermissions gets a reference to the given []Permission and assigns it to the Permissions field. +func (o *RemoteItem) SetPermissions(v []Permission) { + o.Permissions = v +} + // GetSize returns the Size field value if set, zero value otherwise. func (o *RemoteItem) GetSize() int64 { if o == nil || IsNil(o.Size) { @@ -805,6 +839,9 @@ func (o RemoteItem) ToMap() (map[string]interface{}, error) { if !IsNil(o.Shared) { toSerialize["shared"] = o.Shared } + if !IsNil(o.Permissions) { + toSerialize["permissions"] = o.Permissions + } if !IsNil(o.Size) { toSerialize["size"] = o.Size } diff --git a/vendor/github.com/owncloud/libre-graph-api-go/model_video.go b/vendor/github.com/owncloud/libre-graph-api-go/model_video.go new file mode 100644 index 0000000000..b3882f3a2f --- /dev/null +++ b/vendor/github.com/owncloud/libre-graph-api-go/model_video.go @@ -0,0 +1,458 @@ +/* +Libre Graph API + +Libre Graph is a free API for cloud collaboration inspired by the MS Graph API. + +API version: v1.0.4 +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package libregraph + +import ( + "encoding/json" +) + +// checks if the Video type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &Video{} + +// Video The video resource groups video-related data items into a single structure. If a driveItem has a non-null video facet, the item represents a video file. The properties of the video resource are populated by extracting metadata from the file. +type Video struct { + // Number of audio bits per sample. + AudioBitsPerSample *int32 `json:"audioBitsPerSample,omitempty"` + // Number of audio channels. + AudioChannels *int32 `json:"audioChannels,omitempty"` + // Name of the audio format (AAC, MP3, etc.). + AudioFormat *string `json:"audioFormat,omitempty"` + // Number of audio samples per second. + AudioSamplesPerSecond *int32 `json:"audioSamplesPerSecond,omitempty"` + // Bit rate of the video in bits per second. + Bitrate *int32 `json:"bitrate,omitempty"` + // Duration of the file in milliseconds. + Duration *int64 `json:"duration,omitempty"` + // \\\"Four character code\\\" name of the video format. + FourCC *string `json:"fourCC,omitempty"` + // Frame rate of the video. + FrameRate *float64 `json:"frameRate,omitempty"` + // Height of the video, in pixels. + Height *int32 `json:"height,omitempty"` + // Width of the video, in pixels. + Width *int32 `json:"width,omitempty"` +} + +// NewVideo instantiates a new Video object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewVideo() *Video { + this := Video{} + return &this +} + +// NewVideoWithDefaults instantiates a new Video object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewVideoWithDefaults() *Video { + this := Video{} + return &this +} + +// GetAudioBitsPerSample returns the AudioBitsPerSample field value if set, zero value otherwise. +func (o *Video) GetAudioBitsPerSample() int32 { + if o == nil || IsNil(o.AudioBitsPerSample) { + var ret int32 + return ret + } + return *o.AudioBitsPerSample +} + +// GetAudioBitsPerSampleOk returns a tuple with the AudioBitsPerSample field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Video) GetAudioBitsPerSampleOk() (*int32, bool) { + if o == nil || IsNil(o.AudioBitsPerSample) { + return nil, false + } + return o.AudioBitsPerSample, true +} + +// HasAudioBitsPerSample returns a boolean if a field has been set. +func (o *Video) HasAudioBitsPerSample() bool { + if o != nil && !IsNil(o.AudioBitsPerSample) { + return true + } + + return false +} + +// SetAudioBitsPerSample gets a reference to the given int32 and assigns it to the AudioBitsPerSample field. +func (o *Video) SetAudioBitsPerSample(v int32) { + o.AudioBitsPerSample = &v +} + +// GetAudioChannels returns the AudioChannels field value if set, zero value otherwise. +func (o *Video) GetAudioChannels() int32 { + if o == nil || IsNil(o.AudioChannels) { + var ret int32 + return ret + } + return *o.AudioChannels +} + +// GetAudioChannelsOk returns a tuple with the AudioChannels field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Video) GetAudioChannelsOk() (*int32, bool) { + if o == nil || IsNil(o.AudioChannels) { + return nil, false + } + return o.AudioChannels, true +} + +// HasAudioChannels returns a boolean if a field has been set. +func (o *Video) HasAudioChannels() bool { + if o != nil && !IsNil(o.AudioChannels) { + return true + } + + return false +} + +// SetAudioChannels gets a reference to the given int32 and assigns it to the AudioChannels field. +func (o *Video) SetAudioChannels(v int32) { + o.AudioChannels = &v +} + +// GetAudioFormat returns the AudioFormat field value if set, zero value otherwise. +func (o *Video) GetAudioFormat() string { + if o == nil || IsNil(o.AudioFormat) { + var ret string + return ret + } + return *o.AudioFormat +} + +// GetAudioFormatOk returns a tuple with the AudioFormat field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Video) GetAudioFormatOk() (*string, bool) { + if o == nil || IsNil(o.AudioFormat) { + return nil, false + } + return o.AudioFormat, true +} + +// HasAudioFormat returns a boolean if a field has been set. +func (o *Video) HasAudioFormat() bool { + if o != nil && !IsNil(o.AudioFormat) { + return true + } + + return false +} + +// SetAudioFormat gets a reference to the given string and assigns it to the AudioFormat field. +func (o *Video) SetAudioFormat(v string) { + o.AudioFormat = &v +} + +// GetAudioSamplesPerSecond returns the AudioSamplesPerSecond field value if set, zero value otherwise. +func (o *Video) GetAudioSamplesPerSecond() int32 { + if o == nil || IsNil(o.AudioSamplesPerSecond) { + var ret int32 + return ret + } + return *o.AudioSamplesPerSecond +} + +// GetAudioSamplesPerSecondOk returns a tuple with the AudioSamplesPerSecond field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Video) GetAudioSamplesPerSecondOk() (*int32, bool) { + if o == nil || IsNil(o.AudioSamplesPerSecond) { + return nil, false + } + return o.AudioSamplesPerSecond, true +} + +// HasAudioSamplesPerSecond returns a boolean if a field has been set. +func (o *Video) HasAudioSamplesPerSecond() bool { + if o != nil && !IsNil(o.AudioSamplesPerSecond) { + return true + } + + return false +} + +// SetAudioSamplesPerSecond gets a reference to the given int32 and assigns it to the AudioSamplesPerSecond field. +func (o *Video) SetAudioSamplesPerSecond(v int32) { + o.AudioSamplesPerSecond = &v +} + +// GetBitrate returns the Bitrate field value if set, zero value otherwise. +func (o *Video) GetBitrate() int32 { + if o == nil || IsNil(o.Bitrate) { + var ret int32 + return ret + } + return *o.Bitrate +} + +// GetBitrateOk returns a tuple with the Bitrate field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Video) GetBitrateOk() (*int32, bool) { + if o == nil || IsNil(o.Bitrate) { + return nil, false + } + return o.Bitrate, true +} + +// HasBitrate returns a boolean if a field has been set. +func (o *Video) HasBitrate() bool { + if o != nil && !IsNil(o.Bitrate) { + return true + } + + return false +} + +// SetBitrate gets a reference to the given int32 and assigns it to the Bitrate field. +func (o *Video) SetBitrate(v int32) { + o.Bitrate = &v +} + +// GetDuration returns the Duration field value if set, zero value otherwise. +func (o *Video) GetDuration() int64 { + if o == nil || IsNil(o.Duration) { + var ret int64 + return ret + } + return *o.Duration +} + +// GetDurationOk returns a tuple with the Duration field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Video) GetDurationOk() (*int64, bool) { + if o == nil || IsNil(o.Duration) { + return nil, false + } + return o.Duration, true +} + +// HasDuration returns a boolean if a field has been set. +func (o *Video) HasDuration() bool { + if o != nil && !IsNil(o.Duration) { + return true + } + + return false +} + +// SetDuration gets a reference to the given int64 and assigns it to the Duration field. +func (o *Video) SetDuration(v int64) { + o.Duration = &v +} + +// GetFourCC returns the FourCC field value if set, zero value otherwise. +func (o *Video) GetFourCC() string { + if o == nil || IsNil(o.FourCC) { + var ret string + return ret + } + return *o.FourCC +} + +// GetFourCCOk returns a tuple with the FourCC field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Video) GetFourCCOk() (*string, bool) { + if o == nil || IsNil(o.FourCC) { + return nil, false + } + return o.FourCC, true +} + +// HasFourCC returns a boolean if a field has been set. +func (o *Video) HasFourCC() bool { + if o != nil && !IsNil(o.FourCC) { + return true + } + + return false +} + +// SetFourCC gets a reference to the given string and assigns it to the FourCC field. +func (o *Video) SetFourCC(v string) { + o.FourCC = &v +} + +// GetFrameRate returns the FrameRate field value if set, zero value otherwise. +func (o *Video) GetFrameRate() float64 { + if o == nil || IsNil(o.FrameRate) { + var ret float64 + return ret + } + return *o.FrameRate +} + +// GetFrameRateOk returns a tuple with the FrameRate field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Video) GetFrameRateOk() (*float64, bool) { + if o == nil || IsNil(o.FrameRate) { + return nil, false + } + return o.FrameRate, true +} + +// HasFrameRate returns a boolean if a field has been set. +func (o *Video) HasFrameRate() bool { + if o != nil && !IsNil(o.FrameRate) { + return true + } + + return false +} + +// SetFrameRate gets a reference to the given float64 and assigns it to the FrameRate field. +func (o *Video) SetFrameRate(v float64) { + o.FrameRate = &v +} + +// GetHeight returns the Height field value if set, zero value otherwise. +func (o *Video) GetHeight() int32 { + if o == nil || IsNil(o.Height) { + var ret int32 + return ret + } + return *o.Height +} + +// GetHeightOk returns a tuple with the Height field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Video) GetHeightOk() (*int32, bool) { + if o == nil || IsNil(o.Height) { + return nil, false + } + return o.Height, true +} + +// HasHeight returns a boolean if a field has been set. +func (o *Video) HasHeight() bool { + if o != nil && !IsNil(o.Height) { + return true + } + + return false +} + +// SetHeight gets a reference to the given int32 and assigns it to the Height field. +func (o *Video) SetHeight(v int32) { + o.Height = &v +} + +// GetWidth returns the Width field value if set, zero value otherwise. +func (o *Video) GetWidth() int32 { + if o == nil || IsNil(o.Width) { + var ret int32 + return ret + } + return *o.Width +} + +// GetWidthOk returns a tuple with the Width field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Video) GetWidthOk() (*int32, bool) { + if o == nil || IsNil(o.Width) { + return nil, false + } + return o.Width, true +} + +// HasWidth returns a boolean if a field has been set. +func (o *Video) HasWidth() bool { + if o != nil && !IsNil(o.Width) { + return true + } + + return false +} + +// SetWidth gets a reference to the given int32 and assigns it to the Width field. +func (o *Video) SetWidth(v int32) { + o.Width = &v +} + +func (o Video) MarshalJSON() ([]byte, error) { + toSerialize, err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o Video) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + if !IsNil(o.AudioBitsPerSample) { + toSerialize["audioBitsPerSample"] = o.AudioBitsPerSample + } + if !IsNil(o.AudioChannels) { + toSerialize["audioChannels"] = o.AudioChannels + } + if !IsNil(o.AudioFormat) { + toSerialize["audioFormat"] = o.AudioFormat + } + if !IsNil(o.AudioSamplesPerSecond) { + toSerialize["audioSamplesPerSecond"] = o.AudioSamplesPerSecond + } + if !IsNil(o.Bitrate) { + toSerialize["bitrate"] = o.Bitrate + } + if !IsNil(o.Duration) { + toSerialize["duration"] = o.Duration + } + if !IsNil(o.FourCC) { + toSerialize["fourCC"] = o.FourCC + } + if !IsNil(o.FrameRate) { + toSerialize["frameRate"] = o.FrameRate + } + if !IsNil(o.Height) { + toSerialize["height"] = o.Height + } + if !IsNil(o.Width) { + toSerialize["width"] = o.Width + } + return toSerialize, nil +} + +type NullableVideo struct { + value *Video + isSet bool +} + +func (v NullableVideo) Get() *Video { + return v.value +} + +func (v *NullableVideo) Set(val *Video) { + v.value = val + v.isSet = true +} + +func (v NullableVideo) IsSet() bool { + return v.isSet +} + +func (v *NullableVideo) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableVideo(val *Video) *NullableVideo { + return &NullableVideo{value: val, isSet: true} +} + +func (v NullableVideo) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableVideo) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 3f0e58bf44..22c1a082b7 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1590,7 +1590,7 @@ github.com/opentracing/opentracing-go/log # github.com/orcaman/concurrent-map v1.0.0 ## explicit github.com/orcaman/concurrent-map -# github.com/owncloud/libre-graph-api-go v1.0.5-0.20231201125350-a08244876423 +# github.com/owncloud/libre-graph-api-go v1.0.5-0.20240115110609-b018a896364e ## explicit; go 1.18 github.com/owncloud/libre-graph-api-go # github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c From 8c6ba9c36081d32d5c90c37c387f8f04a844622d Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Thu, 28 Dec 2023 17:11:42 +0100 Subject: [PATCH 05/15] fix: shared with me listing --- services/graph/pkg/service/v0/sharedwithme.go | 390 +++++++++--------- 1 file changed, 195 insertions(+), 195 deletions(-) diff --git a/services/graph/pkg/service/v0/sharedwithme.go b/services/graph/pkg/service/v0/sharedwithme.go index 04e559c6d3..ace9005c7d 100644 --- a/services/graph/pkg/service/v0/sharedwithme.go +++ b/services/graph/pkg/service/v0/sharedwithme.go @@ -4,15 +4,17 @@ import ( "context" "net/http" "reflect" - "strings" + "slices" collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/v2/pkg/utils" "github.com/go-chi/render" libregraph "github.com/owncloud/libre-graph-api-go" + "golang.org/x/sync/errgroup" "github.com/cs3org/reva/v2/pkg/storagespace" - "github.com/cs3org/reva/v2/pkg/utils" + "github.com/owncloud/ocis/v2/ocis-pkg/conversions" "github.com/owncloud/ocis/v2/services/graph/pkg/errorcode" "github.com/owncloud/ocis/v2/services/graph/pkg/unifiedrole" @@ -32,6 +34,7 @@ func (g Graph) ListSharedWithMe(w http.ResponseWriter, r *http.Request) { render.JSON(w, r, &ListResponse{Value: driveItems}) } +// listSharedWithMe is a helper function that lists the drive items shared with the current user. func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, error) { gatewayClient, err := g.gatewaySelector.Next() if err != nil { @@ -45,250 +48,247 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er return nil, *errCode } - var driveItems []libregraph.DriveItem - for _, receivedShare := range listReceivedSharesResponse.GetShares() { - statRequest := &storageprovider.StatRequest{} - - switch receivedShare.GetState() { - case collaboration.ShareState_SHARE_STATE_ACCEPTED: - statRequest.Ref = &storageprovider.Reference{ - ResourceId: &storageprovider.ResourceId{ - StorageId: utils.ShareStorageProviderID, - OpaqueId: receivedShare.GetShare().GetId().GetOpaqueId(), - SpaceId: utils.ShareStorageSpaceID, - }, - } - case collaboration.ShareState_SHARE_STATE_PENDING: - // return no remoteItem - fallthrough - case collaboration.ShareState_SHARE_STATE_REJECTED: - // what to return here? same as pending? - statRequest.Ref = &storageprovider.Reference{ - ResourceId: receivedShare.GetShare().GetResourceId(), - } + // doStat is a helper function that stat a resource. + doStat := func(resourceId *storageprovider.ResourceId) (*storageprovider.StatResponse, error) { + shareStat, err := gatewayClient.Stat(ctx, &storageprovider.StatRequest{ + Ref: &storageprovider.Reference{ResourceId: resourceId}, + }) + switch errCode := errorcode.FromCS3Status(shareStat.GetStatus(), err); { + case errCode == nil: + break + // skip ItemNotFound shares, they might have been deleted in the meantime or orphans. + case errCode.GetCode() == errorcode.ItemNotFound: + return nil, nil default: - continue + g.logger.Error().Err(errCode).Msg("could not stat") + return nil, errCode } - statResponse, err := gatewayClient.Stat(ctx, statRequest) - if errCode := errorcode.FromCS3Status(statResponse.GetStatus(), err); errCode != nil { - g.logger.Error().Err(err).Msg("could not stat") - continue - } + return shareStat, nil + } - var commonResourceOwner *libregraph.Identity - if userID := statResponse.GetInfo().GetOwner(); userID != nil { - if user, err := g.identityCache.GetUser(ctx, userID.GetOpaqueId()); err != nil { - g.logger.Error().Err(err).Msg("could not get user") - continue - } else { - commonResourceOwner = &libregraph.Identity{ - DisplayName: user.GetDisplayName(), - Id: libregraph.PtrString(user.GetId()), - } + group := new(errgroup.Group) + receivedShares := listReceivedSharesResponse.GetShares() + driveItems := make([]libregraph.DriveItem, len(receivedShares)) + + for i, receivedShare := range receivedShares { + i, receivedShare := i, receivedShare + group.Go(func() error { + shareStat, err := doStat(receivedShare.GetShare().GetResourceId()) + if shareStat == nil || err != nil { + return err } - } - var commonShareCreator *libregraph.Identity - if userID := receivedShare.GetShare().GetCreator(); userID != nil { - if user, err := g.identityCache.GetUser(ctx, userID.GetOpaqueId()); err != nil { - g.logger.Error().Err(err).Msg("could not get user") - continue - } else { - commonShareCreator = &libregraph.Identity{ - DisplayName: user.GetDisplayName(), - Id: libregraph.PtrString(user.GetId()), - } - } - } - - var commonPermission *libregraph.Permission - { permission := libregraph.NewPermission() - - if id := receivedShare.GetShare().GetId().GetOpaqueId(); id != "" { - permission.SetId(id) - } - - if permissionSet := statResponse.GetInfo().GetPermissionSet(); permissionSet != nil { - if actions := unifiedrole.CS3ResourcePermissionsToLibregraphActions(*permissionSet); len(actions) > 0 { - permission.SetLibreGraphPermissionsActions(actions) - } - - if role := unifiedrole.CS3ResourcePermissionsToUnifiedRole( - *permissionSet, - unifiedrole.UnifiedRoleConditionGrantee, - g.config.FilesSharing.EnableResharing, - ); role != nil { - permission.SetRoles([]string{role.GetId()}) - } - } - - if expiration := receivedShare.GetShare().GetExpiration(); expiration != nil { - permission.SetExpirationDateTime(cs3TimestampToTime(expiration)) - } - - switch grantee := receivedShare.GetShare().GetGrantee(); { - case grantee.GetType() == storageprovider.GranteeType_GRANTEE_TYPE_USER: - permission.SetGrantedToV2(libregraph.SharePointIdentitySet{ - User: &libregraph.Identity{ - Id: conversions.ToPointer(grantee.GetUserId().GetOpaqueId()), - }, - }) - case grantee.GetType() == storageprovider.GranteeType_GRANTEE_TYPE_GROUP: - permission.SetGrantedToV2(libregraph.SharePointIdentitySet{ - Group: &libregraph.Identity{ - Id: conversions.ToPointer(grantee.GetGroupId().GetOpaqueId()), - }, - }) - } - - if !reflect.ValueOf(*permission).IsZero() { - commonPermission = permission - } - } - - driveItem := libregraph.NewDriveItem() - { - if commonShareCreator != nil { - driveItem.SetCreatedBy(libregraph.IdentitySet{ - User: commonShareCreator, - }) - } - { - parentReference := libregraph.NewItemReference() + if id := receivedShare.GetShare().GetId().GetOpaqueId(); id != "" { + permission.SetId(id) + } - if spaceType := statResponse.GetInfo().GetSpace().GetSpaceType(); spaceType != "" { + if expiration := receivedShare.GetShare().GetExpiration(); expiration != nil { + permission.SetExpirationDateTime(cs3TimestampToTime(expiration)) + } + + // todo: handle: + // - @UIUI.Hidden // why @UIUI? + // - @Client.Synchronize + + if permissionSet := shareStat.GetInfo().GetPermissionSet(); permissionSet != nil { + if actions := unifiedrole.CS3ResourcePermissionsToLibregraphActions(*permissionSet); len(actions) > 0 { + permission.SetLibreGraphPermissionsActions(actions) + } + + if role := unifiedrole.CS3ResourcePermissionsToUnifiedRole( + *permissionSet, + unifiedrole.UnifiedRoleConditionGrantee, + g.config.FilesSharing.EnableResharing, + ); role != nil { + permission.SetRoles([]string{role.GetId()}) + } + } + + switch grantee := receivedShare.GetShare().GetGrantee(); { + case grantee.GetType() == storageprovider.GranteeType_GRANTEE_TYPE_USER: + permission.SetGrantedToV2(libregraph.SharePointIdentitySet{ + User: &libregraph.Identity{ + Id: conversions.ToPointer(grantee.GetUserId().GetOpaqueId()), + }, + }) + case grantee.GetType() == storageprovider.GranteeType_GRANTEE_TYPE_GROUP: + permission.SetGrantedToV2(libregraph.SharePointIdentitySet{ + Group: &libregraph.Identity{ + Id: conversions.ToPointer(grantee.GetGroupId().GetOpaqueId()), + }, + }) + } + } + + parentReference := libregraph.NewItemReference() + { + if spaceType := shareStat.GetInfo().GetSpace().GetSpaceType(); spaceType != "" { parentReference.SetDriveType(spaceType) } - if root := statResponse.GetInfo().GetSpace().GetRoot(); root != nil { + if root := shareStat.GetInfo().GetSpace().GetRoot(); root != nil { parentReference.SetDriveId(storagespace.FormatResourceID(*root)) } + } - if !reflect.ValueOf(*parentReference).IsZero() { - driveItem.ParentReference = parentReference + shared := libregraph.NewShared() + { + if cTime := receivedShare.GetShare().GetCtime(); cTime != nil { + shared.SetSharedDateTime(cs3TimestampToTime(cTime)) } } - } - - switch receivedShare.GetState() { - case collaboration.ShareState_SHARE_STATE_ACCEPTED: - if resourceID := statRequest.GetRef().GetResourceId(); resourceID != nil { - driveItem.SetId(storagespace.FormatResourceID(*resourceID)) - } - - if name := receivedShare.GetMountPoint().GetPath(); name != "" { - driveItem.SetName(receivedShare.GetMountPoint().GetPath()) - } - - if mTime := receivedShare.GetShare().GetMtime(); mTime != nil { - driveItem.SetLastModifiedDateTime(cs3TimestampToTime(mTime)) - } - - if cTime := receivedShare.GetShare().GetCtime(); cTime != nil { - driveItem.SetCreatedDateTime(cs3TimestampToTime(cTime)) - } + remoteItem := libregraph.NewRemoteItem() { - remoteItem := libregraph.NewRemoteItem() - - if id := statResponse.GetInfo().GetId(); id != nil { + if id := shareStat.GetInfo().GetId(); id != nil { remoteItem.SetId(storagespace.FormatResourceID(*id)) } - if mTime := statResponse.GetInfo().GetMtime(); mTime != nil { - remoteItem.SetLastModifiedDateTime(cs3TimestampToTime(mTime)) - } - - if name := statResponse.GetInfo().GetName(); name != "" { + if name := shareStat.GetInfo().GetName(); name != "" { remoteItem.SetName(name) } - if size := statResponse.GetInfo().GetSize(); size != 0 { + if etag := shareStat.GetInfo().GetEtag(); etag != "" { + remoteItem.SetETag(etag) + } + + if mTime := shareStat.GetInfo().GetMtime(); mTime != nil { + remoteItem.SetLastModifiedDateTime(cs3TimestampToTime(mTime)) + } + + if size := shareStat.GetInfo().GetSize(); size != 0 { remoteItem.SetSize(int64(size)) } + } - if etag := statResponse.GetInfo().GetEtag(); etag != "" { - remoteItem.SetETag(strings.Trim(etag, "\"")) + driveItem := libregraph.NewDriveItem() + + // handle share state related stuff + switch receivedShare.GetState() { + case collaboration.ShareState_SHARE_STATE_ACCEPTED: + resourceId := &storageprovider.ResourceId{ + StorageId: utils.ShareStorageProviderID, + OpaqueId: receivedShare.GetShare().GetId().GetOpaqueId(), + SpaceId: utils.ShareStorageSpaceID, + } + jailStat, err := doStat(resourceId) + if jailStat == nil || err != nil { + return err } - if commonResourceOwner != nil { - remoteItem.SetCreatedBy(libregraph.IdentitySet{ - User: commonResourceOwner, - }) + driveItem.SetId(storagespace.FormatResourceID(*resourceId)) + + if name := jailStat.GetInfo().GetName(); name != "" { + driveItem.SetName(name) } - switch info := statResponse.GetInfo(); { + if etag := jailStat.GetInfo().GetEtag(); etag != "" { + driveItem.SetETag(etag) + } + + if mTime := jailStat.GetInfo().GetMtime(); mTime != nil { + driveItem.SetLastModifiedDateTime(cs3TimestampToTime(mTime)) + } + + if size := jailStat.GetInfo().GetSize(); size != 0 { + remoteItem.SetSize(int64(size)) + } + case collaboration.ShareState_SHARE_STATE_PENDING: + fallthrough + case collaboration.ShareState_SHARE_STATE_REJECTED: + if name := shareStat.GetInfo().GetName(); name != "" { + driveItem.SetName(name) + } + } + + // connect the dots + { + if userID := shareStat.GetInfo().GetOwner(); userID != nil { + if user, err := g.identityCache.GetUser(ctx, userID.GetOpaqueId()); err != nil { + g.logger.Error().Err(err).Msg("could not get user") + return err + } else { + identitySet := libregraph.IdentitySet{ + User: &libregraph.Identity{ + DisplayName: user.GetDisplayName(), + Id: libregraph.PtrString(user.GetId()), + }, + } + + remoteItem.SetCreatedBy(identitySet) + shared.SetOwner(identitySet) + } + } + + if userID := receivedShare.GetShare().GetCreator(); userID != nil { + if user, err := g.identityCache.GetUser(ctx, userID.GetOpaqueId()); err != nil { + g.logger.Error().Err(err).Msg("could not get user") + return err + } else { + identitySet := libregraph.IdentitySet{ + User: &libregraph.Identity{ + DisplayName: user.GetDisplayName(), + Id: libregraph.PtrString(user.GetId()), + }, + } + + driveItem.SetCreatedBy(identitySet) + shared.SetSharedBy(identitySet) + } + } + + switch info := shareStat.GetInfo(); { case info.GetType() == storageprovider.ResourceType_RESOURCE_TYPE_CONTAINER: - remoteItem.Folder = libregraph.NewFolder() + folder := libregraph.NewFolder() + + remoteItem.Folder = folder + driveItem.Folder = folder case info.GetType() == storageprovider.ResourceType_RESOURCE_TYPE_FILE: - openGraphFile := libregraph.NewOpenGraphFile() + file := libregraph.NewOpenGraphFile() if mimeType := info.GetMimeType(); mimeType != "" { - openGraphFile.MimeType = &mimeType + file.MimeType = &mimeType } - remoteItem.File = openGraphFile - case info.GetType() == storageprovider.ResourceType_RESOURCE_TYPE_INVALID: - g.logger.Info().Interface("info", info).Msg("invalid resource type") + remoteItem.File = file + driveItem.File = file } - if commonPermission != nil { - remoteItem.SetPermissions([]libregraph.Permission{*commonPermission}) + if !reflect.ValueOf(*shared).IsZero() { + remoteItem.Shared = shared } - { - shared := libregraph.NewShared() + if !reflect.ValueOf(*permission).IsZero() { + permissions := []libregraph.Permission{*permission} - if cTime := receivedShare.GetShare().GetCtime(); cTime != nil { - shared.SetSharedDateTime(cs3TimestampToTime(cTime)) - } + remoteItem.Permissions = permissions + driveItem.Permissions = permissions + } - if commonResourceOwner != nil { - shared.SetOwner(libregraph.IdentitySet{ - User: commonResourceOwner, - }) - } - - if commonShareCreator != nil { - shared.SetSharedBy(libregraph.IdentitySet{ - User: commonShareCreator, - }) - } - - if !reflect.ValueOf(*shared).IsZero() { - remoteItem.SetShared(*shared) - } + if !reflect.ValueOf(*parentReference).IsZero() { + remoteItem.ParentReference = parentReference + driveItem.ParentReference = parentReference } if !reflect.ValueOf(*remoteItem).IsZero() { - driveItem.SetRemoteItem(*remoteItem) + driveItem.RemoteItem = remoteItem } } - case collaboration.ShareState_SHARE_STATE_PENDING: - fallthrough - case collaboration.ShareState_SHARE_STATE_REJECTED: - if id := statResponse.GetInfo().GetId(); id != nil { - driveItem.SetId(storagespace.FormatResourceID(*id)) - } - if name := statResponse.GetInfo().GetName(); name != "" { - driveItem.SetName(name) - } + driveItems[i] = *driveItem - if mTime := statResponse.GetInfo().GetMtime(); mTime != nil { - driveItem.SetLastModifiedDateTime(cs3TimestampToTime(mTime)) - } - - if commonPermission != nil { - driveItem.SetPermissions([]libregraph.Permission{*commonPermission}) - } - } - - driveItems = append(driveItems, *driveItem) + return nil + }) } - return driveItems, nil + // wait for concurrent requests to finish + err = group.Wait() + + // filter out empty drive items + return slices.Clip(slices.DeleteFunc(driveItems, func(item libregraph.DriveItem) bool { + return reflect.ValueOf(item).IsZero() + })), err } From 0b796443f2e7e56a6252f36d2a2677dc8dc79e6b Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Fri, 29 Dec 2023 14:21:03 +0100 Subject: [PATCH 06/15] enhancement: add ui hidden and client synced prop --- services/graph/pkg/service/v0/sharedwithme.go | 35 +++++++++++-------- .../graph/pkg/service/v0/sharedwithme_test.go | 2 +- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/services/graph/pkg/service/v0/sharedwithme.go b/services/graph/pkg/service/v0/sharedwithme.go index ace9005c7d..92e559a5ce 100644 --- a/services/graph/pkg/service/v0/sharedwithme.go +++ b/services/graph/pkg/service/v0/sharedwithme.go @@ -73,6 +73,7 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er for i, receivedShare := range receivedShares { i, receivedShare := i, receivedShare + group.Go(func() error { shareStat, err := doStat(receivedShare.GetShare().GetResourceId()) if shareStat == nil || err != nil { @@ -81,6 +82,9 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er permission := libregraph.NewPermission() { + permission.SetUIHidden(receivedShare.GetHidden()) + permission.SetClientSynchronize(receivedShare.GetState() == collaboration.ShareState_SHARE_STATE_ACCEPTED) + if id := receivedShare.GetShare().GetId().GetOpaqueId(); id != "" { permission.SetId(id) } @@ -89,10 +93,6 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er permission.SetExpirationDateTime(cs3TimestampToTime(expiration)) } - // todo: handle: - // - @UIUI.Hidden // why @UIUI? - // - @Client.Synchronize - if permissionSet := shareStat.GetInfo().GetPermissionSet(); permissionSet != nil { if actions := unifiedrole.CS3ResourcePermissionsToLibregraphActions(*permissionSet); len(actions) > 0 { permission.SetLibreGraphPermissionsActions(actions) @@ -169,6 +169,7 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er // handle share state related stuff switch receivedShare.GetState() { case collaboration.ShareState_SHARE_STATE_ACCEPTED: + // fixMe: is this stat necessary? only mtime is used... anything else needed? resourceId := &storageprovider.ResourceId{ StorageId: utils.ShareStorageProviderID, OpaqueId: receivedShare.GetShare().GetId().GetOpaqueId(), @@ -181,21 +182,13 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er driveItem.SetId(storagespace.FormatResourceID(*resourceId)) - if name := jailStat.GetInfo().GetName(); name != "" { - driveItem.SetName(name) + if name := receivedShare.GetMountPoint().GetPath(); name != "" { + driveItem.SetName(receivedShare.GetMountPoint().GetPath()) } if etag := jailStat.GetInfo().GetEtag(); etag != "" { driveItem.SetETag(etag) } - - if mTime := jailStat.GetInfo().GetMtime(); mTime != nil { - driveItem.SetLastModifiedDateTime(cs3TimestampToTime(mTime)) - } - - if size := jailStat.GetInfo().GetSize(); size != 0 { - remoteItem.SetSize(int64(size)) - } case collaboration.ShareState_SHARE_STATE_PENDING: fallthrough case collaboration.ShareState_SHARE_STATE_REJECTED: @@ -206,6 +199,20 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er // connect the dots { + if mTime := shareStat.GetInfo().GetMtime(); mTime != nil { + t := cs3TimestampToTime(mTime) + + driveItem.SetLastModifiedDateTime(t) + remoteItem.SetLastModifiedDateTime(t) + } + + if size := shareStat.GetInfo().GetSize(); size != 0 { + s := int64(size) + + driveItem.SetSize(s) + remoteItem.SetSize(s) + } + if userID := shareStat.GetInfo().GetOwner(); userID != nil { if user, err := g.identityCache.GetUser(ctx, userID.GetOpaqueId()); err != nil { g.logger.Error().Err(err).Msg("could not get user") diff --git a/services/graph/pkg/service/v0/sharedwithme_test.go b/services/graph/pkg/service/v0/sharedwithme_test.go index 5e7cfeec94..c06c5db860 100644 --- a/services/graph/pkg/service/v0/sharedwithme_test.go +++ b/services/graph/pkg/service/v0/sharedwithme_test.go @@ -30,7 +30,7 @@ import ( service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0" ) -var _ = Describe("SharedWithMe", func() { +var _ = PDescribe("SharedWithMe", func() { var ( svc service.Service cfg *config.Config From b91bc535a872a9eee1c68331b337065e093e6dc3 Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Tue, 2 Jan 2024 12:00:56 +0100 Subject: [PATCH 07/15] enhancement: simplify sharedWithMe - remove unnecessary stat for accepted items - only display permission actions if the role cannot be resolved - add permission user and group displayName --- services/graph/pkg/service/v0/sharedwithme.go | 98 +++++++++++-------- 1 file changed, 56 insertions(+), 42 deletions(-) diff --git a/services/graph/pkg/service/v0/sharedwithme.go b/services/graph/pkg/service/v0/sharedwithme.go index 92e559a5ce..d4fafcc5dc 100644 --- a/services/graph/pkg/service/v0/sharedwithme.go +++ b/services/graph/pkg/service/v0/sharedwithme.go @@ -15,7 +15,6 @@ import ( "github.com/cs3org/reva/v2/pkg/storagespace" - "github.com/owncloud/ocis/v2/ocis-pkg/conversions" "github.com/owncloud/ocis/v2/services/graph/pkg/errorcode" "github.com/owncloud/ocis/v2/services/graph/pkg/unifiedrole" ) @@ -83,7 +82,6 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er permission := libregraph.NewPermission() { permission.SetUIHidden(receivedShare.GetHidden()) - permission.SetClientSynchronize(receivedShare.GetState() == collaboration.ShareState_SHARE_STATE_ACCEPTED) if id := receivedShare.GetShare().GetId().GetOpaqueId(); id != "" { permission.SetId(id) @@ -94,30 +92,49 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er } if permissionSet := shareStat.GetInfo().GetPermissionSet(); permissionSet != nil { - if actions := unifiedrole.CS3ResourcePermissionsToLibregraphActions(*permissionSet); len(actions) > 0 { - permission.SetLibreGraphPermissionsActions(actions) - } - - if role := unifiedrole.CS3ResourcePermissionsToUnifiedRole( + role := unifiedrole.CS3ResourcePermissionsToUnifiedRole( *permissionSet, unifiedrole.UnifiedRoleConditionGrantee, g.config.FilesSharing.EnableResharing, - ); role != nil { + ) + + if role != nil { permission.SetRoles([]string{role.GetId()}) } + + actions := unifiedrole.CS3ResourcePermissionsToLibregraphActions(*permissionSet) + + // actions only make sense if no role is set + if role == nil && len(actions) > 0 { + permission.SetLibreGraphPermissionsActions(actions) + } } switch grantee := receivedShare.GetShare().GetGrantee(); { case grantee.GetType() == storageprovider.GranteeType_GRANTEE_TYPE_USER: + user, err := g.identityCache.GetUser(ctx, grantee.GetUserId().GetOpaqueId()) + if err != nil { + g.logger.Error().Err(err).Msg("could not get user") + return err + } + permission.SetGrantedToV2(libregraph.SharePointIdentitySet{ User: &libregraph.Identity{ - Id: conversions.ToPointer(grantee.GetUserId().GetOpaqueId()), + DisplayName: user.GetDisplayName(), + Id: user.Id, }, }) case grantee.GetType() == storageprovider.GranteeType_GRANTEE_TYPE_GROUP: + group, err := g.identityCache.GetGroup(ctx, grantee.GetGroupId().GetOpaqueId()) + if err != nil { + g.logger.Error().Err(err).Msg("could not get group") + return err + } + permission.SetGrantedToV2(libregraph.SharePointIdentitySet{ Group: &libregraph.Identity{ - Id: conversions.ToPointer(grantee.GetGroupId().GetOpaqueId()), + DisplayName: group.GetDisplayName(), + Id: group.Id, }, }) } @@ -169,24 +186,18 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er // handle share state related stuff switch receivedShare.GetState() { case collaboration.ShareState_SHARE_STATE_ACCEPTED: - // fixMe: is this stat necessary? only mtime is used... anything else needed? - resourceId := &storageprovider.ResourceId{ + + driveItem.SetId(storagespace.FormatResourceID(storageprovider.ResourceId{ StorageId: utils.ShareStorageProviderID, OpaqueId: receivedShare.GetShare().GetId().GetOpaqueId(), SpaceId: utils.ShareStorageSpaceID, - } - jailStat, err := doStat(resourceId) - if jailStat == nil || err != nil { - return err - } - - driveItem.SetId(storagespace.FormatResourceID(*resourceId)) + })) if name := receivedShare.GetMountPoint().GetPath(); name != "" { driveItem.SetName(receivedShare.GetMountPoint().GetPath()) } - if etag := jailStat.GetInfo().GetEtag(); etag != "" { + if etag := shareStat.GetInfo().GetEtag(); etag != "" { driveItem.SetETag(etag) } case collaboration.ShareState_SHARE_STATE_PENDING: @@ -214,37 +225,40 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er } if userID := shareStat.GetInfo().GetOwner(); userID != nil { - if user, err := g.identityCache.GetUser(ctx, userID.GetOpaqueId()); err != nil { + user, err := g.identityCache.GetUser(ctx, userID.GetOpaqueId()) + if err != nil { g.logger.Error().Err(err).Msg("could not get user") return err - } else { - identitySet := libregraph.IdentitySet{ - User: &libregraph.Identity{ - DisplayName: user.GetDisplayName(), - Id: libregraph.PtrString(user.GetId()), - }, - } - - remoteItem.SetCreatedBy(identitySet) - shared.SetOwner(identitySet) } + + identitySet := libregraph.IdentitySet{ + User: &libregraph.Identity{ + DisplayName: user.GetDisplayName(), + Id: libregraph.PtrString(user.GetId()), + }, + } + + remoteItem.SetCreatedBy(identitySet) + shared.SetOwner(identitySet) } if userID := receivedShare.GetShare().GetCreator(); userID != nil { - if user, err := g.identityCache.GetUser(ctx, userID.GetOpaqueId()); err != nil { + user, err := g.identityCache.GetUser(ctx, userID.GetOpaqueId()) + if err != nil { g.logger.Error().Err(err).Msg("could not get user") return err - } else { - identitySet := libregraph.IdentitySet{ - User: &libregraph.Identity{ - DisplayName: user.GetDisplayName(), - Id: libregraph.PtrString(user.GetId()), - }, - } - - driveItem.SetCreatedBy(identitySet) - shared.SetSharedBy(identitySet) } + + identitySet := libregraph.IdentitySet{ + User: &libregraph.Identity{ + DisplayName: user.GetDisplayName(), + Id: libregraph.PtrString(user.GetId()), + }, + } + + driveItem.SetCreatedBy(identitySet) + shared.SetSharedBy(identitySet) + } switch info := shareStat.GetInfo(); { From cfba9f29cc9042e9e92b34403473696d54800ac0 Mon Sep 17 00:00:00 2001 From: Florian Schade Date: Wed, 3 Jan 2024 11:49:50 +0100 Subject: [PATCH 08/15] enhancement: make use uo remote item ui hidden and client synchronized --- services/graph/pkg/service/v0/sharedwithme.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/graph/pkg/service/v0/sharedwithme.go b/services/graph/pkg/service/v0/sharedwithme.go index d4fafcc5dc..e9ad612449 100644 --- a/services/graph/pkg/service/v0/sharedwithme.go +++ b/services/graph/pkg/service/v0/sharedwithme.go @@ -81,8 +81,6 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er permission := libregraph.NewPermission() { - permission.SetUIHidden(receivedShare.GetHidden()) - if id := receivedShare.GetShare().GetId().GetOpaqueId(); id != "" { permission.SetId(id) } @@ -160,6 +158,9 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er remoteItem := libregraph.NewRemoteItem() { + remoteItem.SetUiHidden(receivedShare.GetHidden()) + remoteItem.SetClientSynchronize(receivedShare.GetState() == collaboration.ShareState_SHARE_STATE_ACCEPTED) + if id := shareStat.GetInfo().GetId(); id != nil { remoteItem.SetId(storagespace.FormatResourceID(*id)) } From dd61270e7d91202ac59ba7b3da27559881d2bf2f Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Thu, 11 Jan 2024 15:58:45 +0100 Subject: [PATCH 09/15] graph/sharedWithMe: separate method for converting CS3 share to libregraph For readability and reduced complexity of the sharedWithMe method. It was getting too large already. --- services/graph/pkg/service/v0/sharedwithme.go | 120 +++++++++--------- 1 file changed, 63 insertions(+), 57 deletions(-) diff --git a/services/graph/pkg/service/v0/sharedwithme.go b/services/graph/pkg/service/v0/sharedwithme.go index e9ad612449..2d6597ec98 100644 --- a/services/graph/pkg/service/v0/sharedwithme.go +++ b/services/graph/pkg/service/v0/sharedwithme.go @@ -79,63 +79,9 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er return err } - permission := libregraph.NewPermission() - { - if id := receivedShare.GetShare().GetId().GetOpaqueId(); id != "" { - permission.SetId(id) - } - - if expiration := receivedShare.GetShare().GetExpiration(); expiration != nil { - permission.SetExpirationDateTime(cs3TimestampToTime(expiration)) - } - - if permissionSet := shareStat.GetInfo().GetPermissionSet(); permissionSet != nil { - role := unifiedrole.CS3ResourcePermissionsToUnifiedRole( - *permissionSet, - unifiedrole.UnifiedRoleConditionGrantee, - g.config.FilesSharing.EnableResharing, - ) - - if role != nil { - permission.SetRoles([]string{role.GetId()}) - } - - actions := unifiedrole.CS3ResourcePermissionsToLibregraphActions(*permissionSet) - - // actions only make sense if no role is set - if role == nil && len(actions) > 0 { - permission.SetLibreGraphPermissionsActions(actions) - } - } - - switch grantee := receivedShare.GetShare().GetGrantee(); { - case grantee.GetType() == storageprovider.GranteeType_GRANTEE_TYPE_USER: - user, err := g.identityCache.GetUser(ctx, grantee.GetUserId().GetOpaqueId()) - if err != nil { - g.logger.Error().Err(err).Msg("could not get user") - return err - } - - permission.SetGrantedToV2(libregraph.SharePointIdentitySet{ - User: &libregraph.Identity{ - DisplayName: user.GetDisplayName(), - Id: user.Id, - }, - }) - case grantee.GetType() == storageprovider.GranteeType_GRANTEE_TYPE_GROUP: - group, err := g.identityCache.GetGroup(ctx, grantee.GetGroupId().GetOpaqueId()) - if err != nil { - g.logger.Error().Err(err).Msg("could not get group") - return err - } - - permission.SetGrantedToV2(libregraph.SharePointIdentitySet{ - Group: &libregraph.Identity{ - DisplayName: group.GetDisplayName(), - Id: group.Id, - }, - }) - } + permission, err := g.cs3ShareToLibreGraphPermissions(ctx, receivedShare.GetShare(), shareStat.GetInfo()) + if err != nil { + return err } parentReference := libregraph.NewItemReference() @@ -314,3 +260,63 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er return reflect.ValueOf(item).IsZero() })), err } + +func (g Graph) cs3ShareToLibreGraphPermissions(ctx context.Context, share *collaboration.Share, shareStatInfo *storageprovider.ResourceInfo) (*libregraph.Permission, error) { + permission := libregraph.NewPermission() + if id := share.GetId().GetOpaqueId(); id != "" { + permission.SetId(id) + } + + if expiration := share.GetExpiration(); expiration != nil { + permission.SetExpirationDateTime(cs3TimestampToTime(expiration)) + } + + if permissionSet := shareStatInfo.GetPermissionSet(); permissionSet != nil { + role := unifiedrole.CS3ResourcePermissionsToUnifiedRole( + *permissionSet, + unifiedrole.UnifiedRoleConditionGrantee, + g.config.FilesSharing.EnableResharing, + ) + + if role != nil { + permission.SetRoles([]string{role.GetId()}) + } + + actions := unifiedrole.CS3ResourcePermissionsToLibregraphActions(*permissionSet) + + // actions only make sense if no role is set + if role == nil && len(actions) > 0 { + permission.SetLibreGraphPermissionsActions(actions) + } + } + + switch grantee := share.GetGrantee(); { + case grantee.GetType() == storageprovider.GranteeType_GRANTEE_TYPE_USER: + user, err := g.identityCache.GetUser(ctx, grantee.GetUserId().GetOpaqueId()) + if err != nil { + g.logger.Error().Err(err).Msg("could not get user") + return nil, err + } + + permission.SetGrantedToV2(libregraph.SharePointIdentitySet{ + User: &libregraph.Identity{ + DisplayName: user.GetDisplayName(), + Id: user.Id, + }, + }) + case grantee.GetType() == storageprovider.GranteeType_GRANTEE_TYPE_GROUP: + group, err := g.identityCache.GetGroup(ctx, grantee.GetGroupId().GetOpaqueId()) + if err != nil { + g.logger.Error().Err(err).Msg("could not get group") + return nil, err + } + + permission.SetGrantedToV2(libregraph.SharePointIdentitySet{ + Group: &libregraph.Identity{ + DisplayName: group.GetDisplayName(), + Id: group.Id, + }, + }) + } + return permission, nil +} From 03e704c2910e4fb8f1f88a31f97c0197224c21e2 Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Thu, 11 Jan 2024 16:02:27 +0100 Subject: [PATCH 10/15] graph/sharedWithMe: Set the permission property only on the wrapped remoteItem Sematically the outer driveItem shouldn't carryt the permission. It's the `remoteItem` that reflects the grantee's permissions. --- services/graph/pkg/service/v0/sharedwithme.go | 1 - 1 file changed, 1 deletion(-) diff --git a/services/graph/pkg/service/v0/sharedwithme.go b/services/graph/pkg/service/v0/sharedwithme.go index 2d6597ec98..e2a0810497 100644 --- a/services/graph/pkg/service/v0/sharedwithme.go +++ b/services/graph/pkg/service/v0/sharedwithme.go @@ -233,7 +233,6 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er permissions := []libregraph.Permission{*permission} remoteItem.Permissions = permissions - driveItem.Permissions = permissions } if !reflect.ValueOf(*parentReference).IsZero() { From b5af3463843760f6695329204d62af0d9acb75ba Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Thu, 11 Jan 2024 17:47:18 +0100 Subject: [PATCH 11/15] bugfix: graph/sharedWithMe fix shared.Owner property The value of driveItem.remoteItem.shared.Owner should match the owner property of the received share not the owner property of the resourceInfo. --- services/graph/pkg/service/v0/sharedwithme.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/services/graph/pkg/service/v0/sharedwithme.go b/services/graph/pkg/service/v0/sharedwithme.go index e2a0810497..b8b94d1cd7 100644 --- a/services/graph/pkg/service/v0/sharedwithme.go +++ b/services/graph/pkg/service/v0/sharedwithme.go @@ -186,6 +186,22 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er } remoteItem.SetCreatedBy(identitySet) + } + + if userID := receivedShare.GetShare().GetOwner(); userID != nil { + user, err := g.identityCache.GetUser(ctx, userID.GetOpaqueId()) + if err != nil { + g.logger.Error().Err(err).Msg("could not get user") + return err + } + + identitySet := libregraph.IdentitySet{ + User: &libregraph.Identity{ + DisplayName: user.GetDisplayName(), + Id: libregraph.PtrString(user.GetId()), + }, + } + shared.SetOwner(identitySet) } From 5bfc943a0c5a0a23b1be9748cc0dfbd479e960af Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Thu, 11 Jan 2024 17:50:47 +0100 Subject: [PATCH 12/15] graph/sharedWithMe: Renable and adjust unit test for recent changes --- .../graph/pkg/service/v0/sharedwithme_test.go | 87 +++++-------------- 1 file changed, 24 insertions(+), 63 deletions(-) diff --git a/services/graph/pkg/service/v0/sharedwithme_test.go b/services/graph/pkg/service/v0/sharedwithme_test.go index c06c5db860..e63e79db8b 100644 --- a/services/graph/pkg/service/v0/sharedwithme_test.go +++ b/services/graph/pkg/service/v0/sharedwithme_test.go @@ -30,7 +30,7 @@ import ( service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0" ) -var _ = PDescribe("SharedWithMe", func() { +var _ = Describe("SharedWithMe", func() { var ( svc service.Service cfg *config.Config @@ -142,9 +142,12 @@ var _ = PDescribe("SharedWithMe", func() { Expect(tape.Code, errorcode.ItemNotFound) }) - It("ignores hidden received shares by default", func() { + It("includes hidden shares", func() { listReceivedSharesResponse.Shares = append(listReceivedSharesResponse.Shares, &collaborationv1beta1.ReceivedShare{ Hidden: true, + Share: &collaborationv1beta1.Share{ + ResourceId: toResourceID("7$8!9"), + }, }) svc.ListSharedWithMe( @@ -154,35 +157,22 @@ var _ = PDescribe("SharedWithMe", func() { jsonData := gjson.Get(tape.Body.String(), "value") - Expect(len(listReceivedSharesResponse.Shares)).To(Equal(2)) - Expect(jsonData.Get("#").Num).To(Equal(float64(1))) - }) - - It("includes hidden shares if explicitly stated", func() { - listReceivedSharesResponse.Shares = append(listReceivedSharesResponse.Shares, &collaborationv1beta1.ReceivedShare{ - Hidden: true, - Share: &collaborationv1beta1.Share{ - ResourceId: toResourceID("7$8!9"), - }, - }) - - svc.ListSharedWithMe( - tape, - httptest.NewRequest(http.MethodGet, "/graph/v1beta1/me/drive/sharedWithMe?show-hidden=true", nil), - ) - - jsonData := gjson.Get(tape.Body.String(), "value") - Expect(len(listReceivedSharesResponse.Shares)).To(Equal(2)) Expect(jsonData.Get("#").Num).To(Equal(float64(2))) }) - It("populates the driveItem properties", func() { + // TODO clarify which of the various properties should actually be set on the driveItem and what + // semantics those properties have + // createdDateTime: is this the share creation time or the creation time of the shared item (there is not ctime in the stat response)? + // lastModifiedDateTime: see above + // owner: Should this be the owner of the shared item, the creator of the share, or the owner of the sharejail? + // etag: should this just equal the etag of the inner remote item? + PIt("populates the driveItem properties", func() { share := listReceivedSharesResponse.Shares[0].Share share.Id = &collaborationv1beta1.ShareId{OpaqueId: "1:2:3"} - share.Ctime = &typesv1beta1.Timestamp{Seconds: 4000} - share.Mtime = &typesv1beta1.Timestamp{Seconds: 40000} + share.Ctime = &typesv1beta1.Timestamp{Seconds: 4001} + share.Mtime = &typesv1beta1.Timestamp{Seconds: 40002} etag := "5ffb8e4bec7026050af7fde9482b289a" @@ -204,7 +194,7 @@ var _ = PDescribe("SharedWithMe", func() { Expect(jsonData.Get("name").String()).To(Equal(resourceInfo.Name)) }) - It("populates the driveItem parentReference properties", func() { + PIt("populates the driveItem parentReference properties", func() { share := listReceivedSharesResponse.Shares[0].Share share.Id = &collaborationv1beta1.ShareId{OpaqueId: "1:2:3"} @@ -219,13 +209,16 @@ var _ = PDescribe("SharedWithMe", func() { }) It("populates the driveItem remoteItem properties", func() { - share := listReceivedSharesResponse.Shares[0].Share + share.Id = &collaborationv1beta1.ShareId{OpaqueId: "1:2:3"} + share.Ctime = &typesv1beta1.Timestamp{Seconds: 4001} + share.Mtime = &typesv1beta1.Timestamp{Seconds: 40002} resourceInfo := statResponse.Info resourceInfo.Name = "some folder" resourceInfo.Mtime = &typesv1beta1.Timestamp{Seconds: 40000} resourceInfo.Size = 500 + resourceInfo.Etag = "\"5ffb8e4bec7026050af7fde9482b289a\"" svc.ListSharedWithMe( tape, @@ -234,6 +227,7 @@ var _ = PDescribe("SharedWithMe", func() { jsonData := gjson.Get(tape.Body.String(), "value.0.remoteItem") + Expect(jsonData.Get("eTag").String()).To(Equal(resourceInfo.Etag)) Expect(jsonData.Get("id").String()).To(Equal(storagespace.FormatResourceID(*share.ResourceId))) Expect(jsonData.Get("lastModifiedDateTime").String()).To(Equal(utils.TSToTime(resourceInfo.Mtime).Format(time.RFC3339Nano))) Expect(jsonData.Get("name").String()).To(Equal(resourceInfo.Name)) @@ -257,25 +251,6 @@ var _ = PDescribe("SharedWithMe", func() { Expect(jsonData.Get("user.id").String()).To(Equal(driveOwner.Id.OpaqueId)) }) - It("populates the driveItem.remoteItem.fileSystemInfo properties", func() { - - share := listReceivedSharesResponse.Shares[0].Share - share.Ctime = &typesv1beta1.Timestamp{Seconds: 400} - - resourceInfo := statResponse.Info - resourceInfo.Mtime = &typesv1beta1.Timestamp{Seconds: 4000} - - svc.ListSharedWithMe( - tape, - httptest.NewRequest(http.MethodGet, "/graph/v1beta1/me/drive/sharedWithMe", nil), - ) - - jsonData := gjson.Get(tape.Body.String(), "value.0.remoteItem.fileSystemInfo") - - Expect(jsonData.Get("createdDateTime").String()).To(Equal(utils.TSToTime(share.Ctime).Format(time.RFC3339Nano))) - Expect(jsonData.Get("lastModifiedDateTime").String()).To(Equal(utils.TSToTime(resourceInfo.Mtime).Format(time.RFC3339Nano))) - }) - It("populates the driveItem.remoteItem.folder properties", func() { svc.ListSharedWithMe( @@ -305,20 +280,6 @@ var _ = PDescribe("SharedWithMe", func() { Expect(jsonData.Get("file.mimeType").String()).To(Equal(resourceInfo.MimeType)) }) - It("populates the driveItem.remoteItem.folder properties", func() { - resourceInfo := statResponse.Info - resourceInfo.Type = providerv1beta1.ResourceType_RESOURCE_TYPE_CONTAINER - - svc.ListSharedWithMe( - tape, - httptest.NewRequest(http.MethodGet, "/graph/v1beta1/me/drive/sharedWithMe", nil), - ) - - jsonData := gjson.Get(tape.Body.String(), "value.0.remoteItem") - - Expect(jsonData.Get("folder").Exists()).To(BeTrue()) - }) - It("populates the driveItem.remoteItem.shared properties", func() { share := listReceivedSharesResponse.Shares[0].Share share.Ctime = &typesv1beta1.Timestamp{Seconds: 4000} @@ -334,10 +295,10 @@ var _ = PDescribe("SharedWithMe", func() { }) It("populates the driveItem.remoteItem.shared.owner properties", func() { - shareCreator := getUserResponse.User + shareOwner := getUserResponse.User share := listReceivedSharesResponse.Shares[0].Share - share.Creator = shareCreator.Id + share.Owner = shareOwner.Id svc.ListSharedWithMe( tape, @@ -346,8 +307,8 @@ var _ = PDescribe("SharedWithMe", func() { jsonData := gjson.Get(tape.Body.String(), "value.0.remoteItem.shared.owner") - Expect(jsonData.Get("user.displayName").String()).To(Equal(shareCreator.DisplayName)) - Expect(jsonData.Get("user.id").String()).To(Equal(shareCreator.Id.OpaqueId)) + Expect(jsonData.Get("user.displayName").String()).To(Equal(shareOwner.DisplayName)) + Expect(jsonData.Get("user.id").String()).To(Equal(shareOwner.Id.OpaqueId)) }) It("populates the driveItem.remoteItem.shared.sharedBy properties", func() { From eab501f5938445221d7f76c4d40a160c2230bc79 Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Mon, 15 Jan 2024 17:19:10 +0100 Subject: [PATCH 13/15] graph/sharedwithme: move flags from 'remoteItem' back into 'permissions' --- services/graph/pkg/service/v0/sharedwithme.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/services/graph/pkg/service/v0/sharedwithme.go b/services/graph/pkg/service/v0/sharedwithme.go index b8b94d1cd7..4df545963c 100644 --- a/services/graph/pkg/service/v0/sharedwithme.go +++ b/services/graph/pkg/service/v0/sharedwithme.go @@ -79,7 +79,7 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er return err } - permission, err := g.cs3ShareToLibreGraphPermissions(ctx, receivedShare.GetShare(), shareStat.GetInfo()) + permission, err := g.cs3ReceivedShareToLibreGraphPermissions(ctx, receivedShare, shareStat.GetInfo()) if err != nil { return err } @@ -104,9 +104,6 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er remoteItem := libregraph.NewRemoteItem() { - remoteItem.SetUiHidden(receivedShare.GetHidden()) - remoteItem.SetClientSynchronize(receivedShare.GetState() == collaboration.ShareState_SHARE_STATE_ACCEPTED) - if id := shareStat.GetInfo().GetId(); id != nil { remoteItem.SetId(storagespace.FormatResourceID(*id)) } @@ -276,13 +273,13 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er })), err } -func (g Graph) cs3ShareToLibreGraphPermissions(ctx context.Context, share *collaboration.Share, shareStatInfo *storageprovider.ResourceInfo) (*libregraph.Permission, error) { +func (g Graph) cs3ReceivedShareToLibreGraphPermissions(ctx context.Context, receivedShare *collaboration.ReceivedShare, shareStatInfo *storageprovider.ResourceInfo) (*libregraph.Permission, error) { permission := libregraph.NewPermission() - if id := share.GetId().GetOpaqueId(); id != "" { + if id := receivedShare.GetShare().GetId().GetOpaqueId(); id != "" { permission.SetId(id) } - if expiration := share.GetExpiration(); expiration != nil { + if expiration := receivedShare.GetShare().GetExpiration(); expiration != nil { permission.SetExpirationDateTime(cs3TimestampToTime(expiration)) } @@ -305,7 +302,7 @@ func (g Graph) cs3ShareToLibreGraphPermissions(ctx context.Context, share *colla } } - switch grantee := share.GetGrantee(); { + switch grantee := receivedShare.GetShare().GetGrantee(); { case grantee.GetType() == storageprovider.GranteeType_GRANTEE_TYPE_USER: user, err := g.identityCache.GetUser(ctx, grantee.GetUserId().GetOpaqueId()) if err != nil { @@ -333,5 +330,8 @@ func (g Graph) cs3ShareToLibreGraphPermissions(ctx context.Context, share *colla }, }) } + permission.SetUiHidden(receivedShare.GetHidden()) + permission.SetClientSynchronize(receivedShare.GetState() == collaboration.ShareState_SHARE_STATE_ACCEPTED) + return permission, nil } From 2c161881bacd65d131e08cd12b4813370bb511d4 Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Mon, 15 Jan 2024 17:22:37 +0100 Subject: [PATCH 14/15] graph/sharedWithMe: Fix 'parentReference' on 'driveItem' The outer parentreference should refer to the drive containing the mountpoint. In our case this is the storagespaceid of the virtual share jail. Also 'CreatedBy' should be the same as on the wrapped remote item. Not the share creator. --- services/graph/pkg/service/v0/sharedwithme.go | 43 ++++---- .../graph/pkg/service/v0/sharedwithme_test.go | 98 ++++++++++++++----- 2 files changed, 98 insertions(+), 43 deletions(-) diff --git a/services/graph/pkg/service/v0/sharedwithme.go b/services/graph/pkg/service/v0/sharedwithme.go index 4df545963c..14c56caee2 100644 --- a/services/graph/pkg/service/v0/sharedwithme.go +++ b/services/graph/pkg/service/v0/sharedwithme.go @@ -84,17 +84,6 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er return err } - parentReference := libregraph.NewItemReference() - { - if spaceType := shareStat.GetInfo().GetSpace().GetSpaceType(); spaceType != "" { - parentReference.SetDriveType(spaceType) - } - - if root := shareStat.GetInfo().GetSpace().GetRoot(); root != nil { - parentReference.SetDriveId(storagespace.FormatResourceID(*root)) - } - } - shared := libregraph.NewShared() { if cTime := receivedShare.GetShare().GetCtime(); cTime != nil { @@ -123,6 +112,19 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er if size := shareStat.GetInfo().GetSize(); size != 0 { remoteItem.SetSize(int64(size)) } + + parentReference := libregraph.NewItemReference() + if spaceType := shareStat.GetInfo().GetSpace().GetSpaceType(); spaceType != "" { + parentReference.SetDriveType(spaceType) + } + + if root := shareStat.GetInfo().GetSpace().GetRoot(); root != nil { + parentReference.SetDriveId(storagespace.FormatResourceID(*root)) + } + if !reflect.ValueOf(*parentReference).IsZero() { + remoteItem.ParentReference = parentReference + } + } driveItem := libregraph.NewDriveItem() @@ -144,6 +146,18 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er if etag := shareStat.GetInfo().GetEtag(); etag != "" { driveItem.SetETag(etag) } + + // parentReference of the out driveItem should be the drive containing the mountpoint + // i.e. the share jail + driveItem.ParentReference = libregraph.NewItemReference() + driveItem.ParentReference.SetDriveType("virtual") + driveItem.ParentReference.SetDriveId(storagespace.FormatStorageID(utils.ShareStorageProviderID, utils.ShareStorageSpaceID)) + driveItem.ParentReference.SetId(storagespace.FormatResourceID(storageprovider.ResourceId{ + StorageId: utils.ShareStorageProviderID, + OpaqueId: utils.ShareStorageSpaceID, + SpaceId: utils.ShareStorageSpaceID, + })) + case collaboration.ShareState_SHARE_STATE_PENDING: fallthrough case collaboration.ShareState_SHARE_STATE_REJECTED: @@ -183,6 +197,7 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er } remoteItem.SetCreatedBy(identitySet) + driveItem.SetCreatedBy(identitySet) } if userID := receivedShare.GetShare().GetOwner(); userID != nil { @@ -216,7 +231,6 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er }, } - driveItem.SetCreatedBy(identitySet) shared.SetSharedBy(identitySet) } @@ -248,11 +262,6 @@ func (g Graph) listSharedWithMe(ctx context.Context) ([]libregraph.DriveItem, er remoteItem.Permissions = permissions } - if !reflect.ValueOf(*parentReference).IsZero() { - remoteItem.ParentReference = parentReference - driveItem.ParentReference = parentReference - } - if !reflect.ValueOf(*remoteItem).IsZero() { driveItem.RemoteItem = remoteItem } diff --git a/services/graph/pkg/service/v0/sharedwithme_test.go b/services/graph/pkg/service/v0/sharedwithme_test.go index e63e79db8b..48eb01ccfa 100644 --- a/services/graph/pkg/service/v0/sharedwithme_test.go +++ b/services/graph/pkg/service/v0/sharedwithme_test.go @@ -72,9 +72,11 @@ var _ = Describe("SharedWithMe", func() { Describe("ListSharedWithMe", func() { var ( - listReceivedSharesResponse *collaborationv1beta1.ListReceivedSharesResponse - statResponse *providerv1beta1.StatResponse - getUserResponse *userv1beta1.GetUserResponse + listReceivedSharesResponse *collaborationv1beta1.ListReceivedSharesResponse + statResponse *providerv1beta1.StatResponse + getUserResponseDefault *userv1beta1.GetUserResponse + getUserResponseResourceCreator *userv1beta1.GetUserResponse + getUserResponseShareCreator *userv1beta1.GetUserResponse ) toResourceID := func(in string) *providerv1beta1.ResourceId { @@ -86,7 +88,7 @@ var _ = Describe("SharedWithMe", func() { BeforeEach(func() { - getUserResponse = &userv1beta1.GetUserResponse{ + getUserResponseDefault = &userv1beta1.GetUserResponse{ Status: status.NewOK(ctx), User: &userv1beta1.User{ Id: &userv1beta1.UserId{ @@ -95,13 +97,51 @@ var _ = Describe("SharedWithMe", func() { DisplayName: "John Romero", }, } + getUserResponseResourceCreator = &userv1beta1.GetUserResponse{ + Status: status.NewOK(ctx), + User: &userv1beta1.User{ + Id: &userv1beta1.UserId{ + OpaqueId: "resource-creator-id", + }, + DisplayName: "Resource Creator", + }, + } + getUserResponseShareCreator = &userv1beta1.GetUserResponse{ + Status: status.NewOK(ctx), + User: &userv1beta1.User{ + Id: &userv1beta1.UserId{ + OpaqueId: "share-creator-id", + }, + DisplayName: "Share Creator", + }, + } - gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil) + gatewayClient.On("GetUser", mock.Anything, mock.MatchedBy( + func(req *userv1beta1.GetUserRequest) bool { + return req.UserId.OpaqueId == "resource-creator-id" + })). + Return(getUserResponseResourceCreator, nil) + gatewayClient.On("GetUser", mock.Anything, mock.MatchedBy( + func(req *userv1beta1.GetUserRequest) bool { + return req.UserId.OpaqueId == "share-creator-id" + })). + Return(getUserResponseShareCreator, nil) + gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponseDefault, nil) listReceivedSharesResponse = &collaborationv1beta1.ListReceivedSharesResponse{ Status: status.NewOK(ctx), Shares: []*collaborationv1beta1.ReceivedShare{ - {Share: &collaborationv1beta1.Share{ResourceId: toResourceID("1$2!3")}}, + { + Share: &collaborationv1beta1.Share{ResourceId: toResourceID("1$2!3")}, + MountPoint: &providerv1beta1.Reference{ + ResourceId: &providerv1beta1.ResourceId{ + StorageId: utils.ShareStorageProviderID, + SpaceId: utils.ShareStorageSpaceID, + }, + Path: "some folder", + }, + State: collaborationv1beta1.ShareState_SHARE_STATE_ACCEPTED, + }, }, } @@ -161,24 +201,23 @@ var _ = Describe("SharedWithMe", func() { Expect(jsonData.Get("#").Num).To(Equal(float64(2))) }) - // TODO clarify which of the various properties should actually be set on the driveItem and what - // semantics those properties have - // createdDateTime: is this the share creation time or the creation time of the shared item (there is not ctime in the stat response)? - // lastModifiedDateTime: see above - // owner: Should this be the owner of the shared item, the creator of the share, or the owner of the sharejail? - // etag: should this just equal the etag of the inner remote item? - PIt("populates the driveItem properties", func() { + It("populates the driveItem properties", func() { share := listReceivedSharesResponse.Shares[0].Share share.Id = &collaborationv1beta1.ShareId{OpaqueId: "1:2:3"} share.Ctime = &typesv1beta1.Timestamp{Seconds: 4001} - share.Mtime = &typesv1beta1.Timestamp{Seconds: 40002} - - etag := "5ffb8e4bec7026050af7fde9482b289a" + share.Mtime = &typesv1beta1.Timestamp{Seconds: 4002} + share.Creator = &userv1beta1.UserId{ + OpaqueId: "share-creator-id", + } resourceInfo := statResponse.Info resourceInfo.Name = "some folder" - resourceInfo.Etag = "\"" + etag + "\"" + resourceInfo.Etag = "\"5ffb8e4bec7026050af7fde9482b289a\"" + resourceInfo.Owner = &userv1beta1.UserId{ + OpaqueId: "resource-creator-id", + } + resourceInfo.Mtime = &typesv1beta1.Timestamp{Seconds: 40000} svc.ListSharedWithMe( tape, @@ -187,14 +226,21 @@ var _ = Describe("SharedWithMe", func() { jsonData := gjson.Get(tape.Body.String(), "value.0") - Expect(jsonData.Get("createdDateTime").String()).To(Equal(utils.TSToTime(share.Ctime).Format(time.RFC3339Nano))) - Expect(jsonData.Get("eTag").String()).To(Equal(etag)) - Expect(jsonData.Get("id").String()).To(Equal(share.Id.OpaqueId)) - Expect(jsonData.Get("lastModifiedDateTime").String()).To(Equal(utils.TSToTime(share.Mtime).Format(time.RFC3339Nano))) + Expect(jsonData.Get("eTag").String()).To(Equal(resourceInfo.Etag)) + Expect(jsonData.Get("id").String()).To(Equal(storagespace.FormatResourceID( + providerv1beta1.ResourceId{ + StorageId: utils.ShareStorageProviderID, + SpaceId: utils.ShareStorageSpaceID, + OpaqueId: share.Id.OpaqueId, + }, + ))) + Expect(jsonData.Get("lastModifiedDateTime").String()).To(Equal(utils.TSToTime(resourceInfo.Mtime).Format(time.RFC3339Nano))) + Expect(jsonData.Get("createdBy.user.id").String()).To(Equal(resourceInfo.Owner.OpaqueId)) Expect(jsonData.Get("name").String()).To(Equal(resourceInfo.Name)) + }) - PIt("populates the driveItem parentReference properties", func() { + It("populates the driveItem parentReference properties", func() { share := listReceivedSharesResponse.Shares[0].Share share.Id = &collaborationv1beta1.ShareId{OpaqueId: "1:2:3"} @@ -205,7 +251,7 @@ var _ = Describe("SharedWithMe", func() { jsonData := gjson.Get(tape.Body.String(), "value.0.parentReference") - Expect(jsonData.Get("driveId").String()).To(Equal(share.Id.OpaqueId)) + Expect(jsonData.Get("driveId").String()).To(Equal(storagespace.FormatStorageID(utils.ShareStorageProviderID, utils.ShareStorageSpaceID))) }) It("populates the driveItem remoteItem properties", func() { @@ -235,7 +281,7 @@ var _ = Describe("SharedWithMe", func() { }) It("populates the driveItem.remoteItem.createdBy properties", func() { - driveOwner := getUserResponse.User + driveOwner := getUserResponseDefault.User resourceInfo := statResponse.Info resourceInfo.Owner = driveOwner.Id @@ -295,7 +341,7 @@ var _ = Describe("SharedWithMe", func() { }) It("populates the driveItem.remoteItem.shared.owner properties", func() { - shareOwner := getUserResponse.User + shareOwner := getUserResponseDefault.User share := listReceivedSharesResponse.Shares[0].Share share.Owner = shareOwner.Id @@ -312,7 +358,7 @@ var _ = Describe("SharedWithMe", func() { }) It("populates the driveItem.remoteItem.shared.sharedBy properties", func() { - shareCreator := getUserResponse.User + shareCreator := getUserResponseDefault.User share := listReceivedSharesResponse.Shares[0].Share share.Creator = shareCreator.Id From 3cc485a0fc5767961bc53a0b302ad274ba7e32f4 Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Tue, 16 Jan 2024 12:31:58 +0100 Subject: [PATCH 15/15] graph/sharewithme: add unit test for 'remoteitem.permissions' property --- .../graph/pkg/service/v0/sharedwithme_test.go | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/services/graph/pkg/service/v0/sharedwithme_test.go b/services/graph/pkg/service/v0/sharedwithme_test.go index 48eb01ccfa..00df68ac1e 100644 --- a/services/graph/pkg/service/v0/sharedwithme_test.go +++ b/services/graph/pkg/service/v0/sharedwithme_test.go @@ -11,6 +11,7 @@ import ( collaborationv1beta1 "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + roleconversions "github.com/cs3org/reva/v2/pkg/conversions" "github.com/cs3org/reva/v2/pkg/rgrpc/status" "github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/v2/pkg/storagespace" @@ -28,6 +29,7 @@ import ( "github.com/owncloud/ocis/v2/services/graph/pkg/errorcode" identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks" service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0" + "github.com/owncloud/ocis/v2/services/graph/pkg/unifiedrole" ) var _ = Describe("SharedWithMe", func() { @@ -326,6 +328,24 @@ var _ = Describe("SharedWithMe", func() { Expect(jsonData.Get("file.mimeType").String()).To(Equal(resourceInfo.MimeType)) }) + It("populates the driveItem.remoteItem.permissions properties", func() { + resourceInfo := statResponse.Info + resourceInfo.PermissionSet = roleconversions.NewViewerRole(true).CS3ResourcePermissions() + + svc.ListSharedWithMe( + tape, + httptest.NewRequest(http.MethodGet, "/graph/v1beta1/me/drive/sharedWithMe", nil), + ) + + jsonData := gjson.Get(tape.Body.String(), "value.0.remoteItem.permissions.0") + + Expect(jsonData.Get("roles.0").String()).To(Equal(unifiedrole.UnifiedRoleViewerID)) + Expect(jsonData.Get("@ui\\.hidden").Exists()).To(BeTrue()) + Expect(jsonData.Get("@ui\\.hidden").Bool()).To(BeFalse()) + Expect(jsonData.Get("@client\\.synchronize").Exists()).To(BeTrue()) + Expect(jsonData.Get("@client\\.synchronize").Bool()).To(BeTrue()) + }) + It("populates the driveItem.remoteItem.shared properties", func() { share := listReceivedSharesResponse.Shares[0].Share share.Ctime = &typesv1beta1.Timestamp{Seconds: 4000}