feat(grpc): protobuf definitions, gRPC server and client
This commit is contained in:
@@ -2,26 +2,31 @@ module somegit.dev/vikingowl/reddit-reader
|
||||
|
||||
go 1.26
|
||||
|
||||
require (
|
||||
github.com/pelletier/go-toml/v2 v2.3.0
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/vartanbeno/go-reddit/v2 v2.0.1
|
||||
google.golang.org/grpc v1.80.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
modernc.org/sqlite v1.48.0
|
||||
somegit.dev/vikingowl/mistral-go-sdk v1.2.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/golang/protobuf v1.2.0 // indirect
|
||||
github.com/google/go-querystring v1.0.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.3.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/spf13/cobra v1.10.2 // indirect
|
||||
github.com/spf13/pflag v1.0.9 // indirect
|
||||
github.com/vartanbeno/go-reddit/v2 v2.0.1 // indirect
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
|
||||
golang.org/x/net v0.49.0 // indirect
|
||||
golang.org/x/oauth2 v0.34.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
google.golang.org/appengine v1.4.0 // indirect
|
||||
golang.org/x/text v0.33.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 // indirect
|
||||
modernc.org/libc v1.70.0 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
modernc.org/sqlite v1.48.0 // indirect
|
||||
somegit.dev/vikingowl/mistral-go-sdk v1.2.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
@@ -17,6 +31,7 @@ github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOF
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM=
|
||||
github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
@@ -26,31 +41,82 @@ github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiT
|
||||
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/vartanbeno/go-reddit/v2 v2.0.1 h1:P6ITpf5YHjdy7DHZIbUIDn/iNAoGcEoDQnMa+L4vutw=
|
||||
github.com/vartanbeno/go-reddit/v2 v2.0.1/go.mod h1:758/S10hwZSLm43NPtwoNQdZFSg3sjB5745Mwjb0ANI=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
|
||||
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
|
||||
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
|
||||
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
|
||||
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
|
||||
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516 h1:sNrWoksmOyF5bvJUcnmbeAmQi8baNhqg5IWaI3llQqU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
|
||||
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.32.0 h1:hjG66bI/kqIPX1b2yT6fr/jt+QedtP2fqojG2VrFuVw=
|
||||
modernc.org/ccgo/v4 v4.32.0/go.mod h1:6F08EBCx5uQc38kMGl+0Nm0oWczoo1c7cgpzEry7Uc0=
|
||||
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
|
||||
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo=
|
||||
modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.70.0 h1:U58NawXqXbgpZ/dcdS9kMshu08aiA6b7gusEusqzNkw=
|
||||
modernc.org/libc v1.70.0/go.mod h1:OVmxFGP1CI/Z4L3E0Q3Mf1PDE0BucwMkcXjjLntvHJo=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.48.0 h1:ElZyLop3Q2mHYk5IFPPXADejZrlHu7APbpB0sF78bq4=
|
||||
modernc.org/sqlite v1.48.0/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
somegit.dev/vikingowl/mistral-go-sdk v1.2.0 h1:9NEGCKzw1Bu2c8LaSEKNlpj08iMsU0fkDFJO6W1Zh+Y=
|
||||
somegit.dev/vikingowl/mistral-go-sdk v1.2.0/go.mod h1:pN7nQdOIYYEMRdwye5cSfymtwhZJHd+caK6J69Z4XMY=
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"somegit.dev/vikingowl/reddit-reader/internal/domain"
|
||||
pb "somegit.dev/vikingowl/reddit-reader/proto/redditreader"
|
||||
)
|
||||
|
||||
// Client wraps a gRPC connection to the RedditReader service.
|
||||
type Client struct {
|
||||
conn *grpc.ClientConn
|
||||
client pb.RedditReaderClient
|
||||
}
|
||||
|
||||
// Dial connects to the gRPC server via a Unix socket.
|
||||
func Dial(socketPath string) (*Client, error) {
|
||||
conn, err := grpc.NewClient(
|
||||
"unix://"+socketPath,
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("grpc dial: %w", err)
|
||||
}
|
||||
return &Client{
|
||||
conn: conn,
|
||||
client: pb.NewRedditReaderClient(conn),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close closes the underlying gRPC connection.
|
||||
func (c *Client) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
// ListPosts retrieves posts matching the given subreddit and limit.
|
||||
func (c *Client) ListPosts(ctx context.Context, subreddit string, limit int) ([]domain.Post, error) {
|
||||
resp, err := c.client.ListPosts(ctx, &pb.ListRequest{
|
||||
Subreddit: subreddit,
|
||||
Limit: int32(limit),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list posts: %w", err)
|
||||
}
|
||||
posts := make([]domain.Post, len(resp.GetPosts()))
|
||||
for i, p := range resp.GetPosts() {
|
||||
posts[i] = protoToDomain(p)
|
||||
}
|
||||
return posts, nil
|
||||
}
|
||||
|
||||
// UpdatePost updates flags on a post and returns the updated version.
|
||||
func (c *Client) UpdatePost(ctx context.Context, id string, read, starred, dismissed *bool) (domain.Post, error) {
|
||||
req := &pb.UpdateRequest{Id: id}
|
||||
if read != nil {
|
||||
req.Read = read
|
||||
}
|
||||
if starred != nil {
|
||||
req.Starred = starred
|
||||
}
|
||||
if dismissed != nil {
|
||||
req.Dismissed = dismissed
|
||||
}
|
||||
resp, err := c.client.UpdatePost(ctx, req)
|
||||
if err != nil {
|
||||
return domain.Post{}, fmt.Errorf("update post: %w", err)
|
||||
}
|
||||
return protoToDomain(resp), nil
|
||||
}
|
||||
|
||||
// SubmitFeedback records a vote for a post.
|
||||
func (c *Client) SubmitFeedback(ctx context.Context, postID string, vote int) error {
|
||||
_, err := c.client.SubmitFeedback(ctx, &pb.FeedbackRequest{
|
||||
PostId: postID,
|
||||
Vote: int32(vote),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("submit feedback: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListSubreddits returns all configured subreddits.
|
||||
func (c *Client) ListSubreddits(ctx context.Context) ([]domain.Subreddit, error) {
|
||||
resp, err := c.client.ListSubreddits(ctx, &pb.Empty{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list subreddits: %w", err)
|
||||
}
|
||||
subs := make([]domain.Subreddit, len(resp.GetSubreddits()))
|
||||
for i, s := range resp.GetSubreddits() {
|
||||
subs[i] = domain.Subreddit{
|
||||
Name: s.GetName(),
|
||||
Enabled: s.GetEnabled(),
|
||||
PollSort: s.GetPollSort(),
|
||||
}
|
||||
}
|
||||
return subs, nil
|
||||
}
|
||||
|
||||
// AddSubreddit adds a subreddit with the given sort order.
|
||||
func (c *Client) AddSubreddit(ctx context.Context, name, sort string) error {
|
||||
_, err := c.client.AddSubreddit(ctx, &pb.AddSubredditRequest{
|
||||
Name: name,
|
||||
PollSort: sort,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("add subreddit: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveSubreddit deletes a subreddit by name.
|
||||
func (c *Client) RemoveSubreddit(ctx context.Context, name string) error {
|
||||
_, err := c.client.RemoveSubreddit(ctx, &pb.RemoveRequest{Name: name})
|
||||
if err != nil {
|
||||
return fmt.Errorf("remove subreddit: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// StreamPosts returns a channel that receives posts as they are pushed by the server.
|
||||
func (c *Client) StreamPosts(ctx context.Context) (<-chan domain.Post, error) {
|
||||
stream, err := c.client.StreamPosts(ctx, &pb.StreamRequest{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("stream posts: %w", err)
|
||||
}
|
||||
ch := make(chan domain.Post, 64)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
for {
|
||||
p, err := stream.Recv()
|
||||
if err == io.EOF {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case ch <- protoToDomain(p):
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
// Status returns the server's status information.
|
||||
func (c *Client) Status(ctx context.Context) (*pb.StatusResponse, error) {
|
||||
resp, err := c.client.Status(ctx, &pb.Empty{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("status: %w", err)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func protoToDomain(p *pb.Post) domain.Post {
|
||||
post := domain.Post{
|
||||
ID: p.GetId(),
|
||||
Subreddit: p.GetSubreddit(),
|
||||
Title: p.GetTitle(),
|
||||
Author: p.GetAuthor(),
|
||||
URL: p.GetUrl(),
|
||||
SelfText: p.GetSelfText(),
|
||||
Score: int(p.GetScore()),
|
||||
Read: p.GetRead(),
|
||||
Starred: p.GetStarred(),
|
||||
Dismissed: p.GetDismissed(),
|
||||
}
|
||||
if ts := p.GetCreatedUtc(); ts != nil {
|
||||
post.CreatedUTC = ts.AsTime()
|
||||
}
|
||||
if ts := p.GetFetchedAt(); ts != nil {
|
||||
post.FetchedAt = ts.AsTime()
|
||||
}
|
||||
if p.Relevance != nil {
|
||||
v := p.GetRelevance()
|
||||
post.Relevance = &v
|
||||
}
|
||||
if p.Summary != nil {
|
||||
v := p.GetSummary()
|
||||
post.Summary = &v
|
||||
}
|
||||
return post
|
||||
}
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"somegit.dev/vikingowl/reddit-reader/internal/domain"
|
||||
"somegit.dev/vikingowl/reddit-reader/internal/store"
|
||||
pb "somegit.dev/vikingowl/reddit-reader/proto/redditreader"
|
||||
)
|
||||
|
||||
// Server implements the RedditReader gRPC service.
|
||||
type Server struct {
|
||||
pb.UnimplementedRedditReaderServer
|
||||
store *store.Store
|
||||
startedAt time.Time
|
||||
mu sync.RWMutex
|
||||
subscribers map[chan *pb.Post]struct{}
|
||||
}
|
||||
|
||||
// Register creates a Server and registers it with the gRPC server.
|
||||
func Register(srv *grpc.Server, st *store.Store, startedAt time.Time) *Server {
|
||||
s := &Server{
|
||||
store: st,
|
||||
startedAt: startedAt,
|
||||
subscribers: make(map[chan *pb.Post]struct{}),
|
||||
}
|
||||
pb.RegisterRedditReaderServer(srv, s)
|
||||
return s
|
||||
}
|
||||
|
||||
// Notify pushes new posts to all connected stream subscribers.
|
||||
func (s *Server) Notify(posts []domain.Post) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
for ch := range s.subscribers {
|
||||
for i := range posts {
|
||||
p := domainToProto(posts[i])
|
||||
select {
|
||||
case ch <- p:
|
||||
default:
|
||||
// subscriber too slow, drop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StreamPosts adds a subscriber channel and sends posts as they arrive.
|
||||
func (s *Server) StreamPosts(_ *pb.StreamRequest, stream grpc.ServerStreamingServer[pb.Post]) error {
|
||||
ch := make(chan *pb.Post, 64)
|
||||
|
||||
s.mu.Lock()
|
||||
s.subscribers[ch] = struct{}{}
|
||||
s.mu.Unlock()
|
||||
|
||||
defer func() {
|
||||
s.mu.Lock()
|
||||
delete(s.subscribers, ch)
|
||||
s.mu.Unlock()
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-stream.Context().Done():
|
||||
return stream.Context().Err()
|
||||
case post := <-ch:
|
||||
if err := stream.Send(post); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ListPosts returns posts matching the filter criteria.
|
||||
func (s *Server) ListPosts(_ context.Context, req *pb.ListRequest) (*pb.ListResponse, error) {
|
||||
f := store.ListFilter{
|
||||
Subreddit: req.GetSubreddit(),
|
||||
Limit: int(req.GetLimit()),
|
||||
}
|
||||
if req.Unread != nil {
|
||||
v := req.GetUnread()
|
||||
f.Unread = &v
|
||||
}
|
||||
if req.Starred != nil {
|
||||
v := req.GetStarred()
|
||||
f.Starred = &v
|
||||
}
|
||||
if req.Dismissed != nil {
|
||||
v := req.GetDismissed()
|
||||
f.Dismissed = &v
|
||||
}
|
||||
|
||||
posts, err := s.store.ListPosts(f)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "list posts: %v", err)
|
||||
}
|
||||
|
||||
resp := &pb.ListResponse{Posts: make([]*pb.Post, len(posts))}
|
||||
for i := range posts {
|
||||
resp.Posts[i] = domainToProto(posts[i])
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// UpdatePost updates flags on a post and returns the updated version.
|
||||
func (s *Server) UpdatePost(_ context.Context, req *pb.UpdateRequest) (*pb.Post, error) {
|
||||
if req.GetId() == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "id is required")
|
||||
}
|
||||
|
||||
u := store.PostUpdate{}
|
||||
if req.Read != nil {
|
||||
v := req.GetRead()
|
||||
u.Read = &v
|
||||
}
|
||||
if req.Starred != nil {
|
||||
v := req.GetStarred()
|
||||
u.Starred = &v
|
||||
}
|
||||
if req.Dismissed != nil {
|
||||
v := req.GetDismissed()
|
||||
u.Dismissed = &v
|
||||
}
|
||||
|
||||
if err := s.store.UpdatePost(req.GetId(), u); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "update post: %v", err)
|
||||
}
|
||||
|
||||
post, err := s.store.GetPost(req.GetId())
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.NotFound, "post %q not found", req.GetId())
|
||||
}
|
||||
return domainToProto(post), nil
|
||||
}
|
||||
|
||||
// SubmitFeedback records a vote for a post.
|
||||
func (s *Server) SubmitFeedback(_ context.Context, req *pb.FeedbackRequest) (*pb.FeedbackResponse, error) {
|
||||
if err := s.store.AddFeedback(req.GetPostId(), int(req.GetVote())); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "add feedback: %v", err)
|
||||
}
|
||||
return &pb.FeedbackResponse{}, nil
|
||||
}
|
||||
|
||||
// ListSubreddits returns all configured subreddits.
|
||||
func (s *Server) ListSubreddits(_ context.Context, _ *pb.Empty) (*pb.SubredditList, error) {
|
||||
subs, err := s.store.ListSubreddits()
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "list subreddits: %v", err)
|
||||
}
|
||||
|
||||
resp := &pb.SubredditList{Subreddits: make([]*pb.SubredditMsg, len(subs))}
|
||||
for i, sub := range subs {
|
||||
resp.Subreddits[i] = &pb.SubredditMsg{
|
||||
Name: sub.Name,
|
||||
Enabled: sub.Enabled,
|
||||
PollSort: sub.PollSort,
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// AddSubreddit adds a subreddit and returns it.
|
||||
func (s *Server) AddSubreddit(_ context.Context, req *pb.AddSubredditRequest) (*pb.SubredditMsg, error) {
|
||||
sub := domain.Subreddit{
|
||||
Name: req.GetName(),
|
||||
PollSort: req.GetPollSort(),
|
||||
}
|
||||
if err := s.store.AddSubreddit(sub); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "add subreddit: %v", err)
|
||||
}
|
||||
return &pb.SubredditMsg{
|
||||
Name: sub.Name,
|
||||
Enabled: true,
|
||||
PollSort: sub.PollSort,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RemoveSubreddit deletes a subreddit by name.
|
||||
func (s *Server) RemoveSubreddit(_ context.Context, req *pb.RemoveRequest) (*pb.Empty, error) {
|
||||
if err := s.store.RemoveSubreddit(req.GetName()); err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "remove subreddit: %v", err)
|
||||
}
|
||||
return &pb.Empty{}, nil
|
||||
}
|
||||
|
||||
// UpdateFilters adds the provided filters for a subreddit and returns all filters.
|
||||
func (s *Server) UpdateFilters(_ context.Context, req *pb.FilterRequest) (*pb.FilterResponse, error) {
|
||||
for _, f := range req.GetFilters() {
|
||||
_, err := s.store.AddFilter(domain.Filter{
|
||||
Subreddit: req.GetSubreddit(),
|
||||
Pattern: f.GetPattern(),
|
||||
IsRegex: f.GetIsRegex(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "add filter: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
filters, err := s.store.ListFilters(req.GetSubreddit())
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "list filters: %v", err)
|
||||
}
|
||||
|
||||
resp := &pb.FilterResponse{Filters: make([]*pb.FilterMsg, len(filters))}
|
||||
for i, f := range filters {
|
||||
resp.Filters[i] = &pb.FilterMsg{
|
||||
Id: f.ID,
|
||||
Subreddit: f.Subreddit,
|
||||
Pattern: f.Pattern,
|
||||
IsRegex: f.IsRegex,
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Status returns uptime and post counts.
|
||||
func (s *Server) Status(_ context.Context, _ *pb.Empty) (*pb.StatusResponse, error) {
|
||||
allPosts, err := s.store.ListPosts(store.ListFilter{})
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, "count posts: %v", err)
|
||||
}
|
||||
|
||||
unread := 0
|
||||
for _, p := range allPosts {
|
||||
if !p.Read {
|
||||
unread++
|
||||
}
|
||||
}
|
||||
|
||||
return &pb.StatusResponse{
|
||||
UptimeSeconds: int64(time.Since(s.startedAt).Seconds()),
|
||||
TotalPosts: int32(len(allPosts)),
|
||||
UnreadPosts: int32(unread),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func domainToProto(p domain.Post) *pb.Post {
|
||||
out := &pb.Post{
|
||||
Id: p.ID,
|
||||
Subreddit: p.Subreddit,
|
||||
Title: p.Title,
|
||||
Author: p.Author,
|
||||
Url: p.URL,
|
||||
SelfText: p.SelfText,
|
||||
Score: int32(p.Score),
|
||||
CreatedUtc: timestamppb.New(p.CreatedUTC),
|
||||
FetchedAt: timestamppb.New(p.FetchedAt),
|
||||
Read: p.Read,
|
||||
Starred: p.Starred,
|
||||
Dismissed: p.Dismissed,
|
||||
}
|
||||
if p.Relevance != nil {
|
||||
out.Relevance = p.Relevance
|
||||
}
|
||||
if p.Summary != nil {
|
||||
out.Summary = p.Summary
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
package server_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
||||
"somegit.dev/vikingowl/reddit-reader/internal/domain"
|
||||
grpcserver "somegit.dev/vikingowl/reddit-reader/internal/grpc/server"
|
||||
"somegit.dev/vikingowl/reddit-reader/internal/store"
|
||||
pb "somegit.dev/vikingowl/reddit-reader/proto/redditreader"
|
||||
)
|
||||
|
||||
func setupTestServer(t *testing.T) (pb.RedditReaderClient, *store.Store) {
|
||||
t.Helper()
|
||||
st, err := store.Open(":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("store.Open: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { st.Close() })
|
||||
|
||||
lis, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("net.Listen: %v", err)
|
||||
}
|
||||
srv := grpc.NewServer()
|
||||
grpcserver.Register(srv, st, time.Now())
|
||||
go srv.Serve(lis)
|
||||
t.Cleanup(func() { srv.GracefulStop() })
|
||||
|
||||
conn, err := grpc.NewClient(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
t.Fatalf("grpc.NewClient: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { conn.Close() })
|
||||
|
||||
return pb.NewRedditReaderClient(conn), st
|
||||
}
|
||||
|
||||
func TestListPostsEmpty(t *testing.T) {
|
||||
client, _ := setupTestServer(t)
|
||||
resp, err := client.ListPosts(context.Background(), &pb.ListRequest{})
|
||||
if err != nil {
|
||||
t.Fatalf("ListPosts: %v", err)
|
||||
}
|
||||
if len(resp.Posts) != 0 {
|
||||
t.Errorf("expected 0 posts, got %d", len(resp.Posts))
|
||||
}
|
||||
}
|
||||
|
||||
func TestListPostsWithData(t *testing.T) {
|
||||
client, st := setupTestServer(t)
|
||||
rel := 0.8
|
||||
if err := st.InsertPost(domain.Post{
|
||||
ID: "t3_a",
|
||||
Subreddit: "golang",
|
||||
Title: "Test",
|
||||
CreatedUTC: time.Now(),
|
||||
Relevance: &rel,
|
||||
}); err != nil {
|
||||
t.Fatalf("InsertPost: %v", err)
|
||||
}
|
||||
|
||||
resp, err := client.ListPosts(context.Background(), &pb.ListRequest{})
|
||||
if err != nil {
|
||||
t.Fatalf("ListPosts: %v", err)
|
||||
}
|
||||
if len(resp.Posts) != 1 {
|
||||
t.Fatalf("expected 1 post, got %d", len(resp.Posts))
|
||||
}
|
||||
if resp.Posts[0].Title != "Test" {
|
||||
t.Errorf("Title = %q, want Test", resp.Posts[0].Title)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatePost(t *testing.T) {
|
||||
client, st := setupTestServer(t)
|
||||
if err := st.InsertPost(domain.Post{
|
||||
ID: "t3_a",
|
||||
Subreddit: "test",
|
||||
Title: "Test",
|
||||
CreatedUTC: time.Now(),
|
||||
}); err != nil {
|
||||
t.Fatalf("InsertPost: %v", err)
|
||||
}
|
||||
|
||||
starred := true
|
||||
resp, err := client.UpdatePost(context.Background(), &pb.UpdateRequest{
|
||||
Id: "t3_a",
|
||||
Starred: &starred,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("UpdatePost: %v", err)
|
||||
}
|
||||
if !resp.Starred {
|
||||
t.Error("expected starred")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitFeedback(t *testing.T) {
|
||||
client, st := setupTestServer(t)
|
||||
if err := st.InsertPost(domain.Post{
|
||||
ID: "t3_a",
|
||||
Subreddit: "test",
|
||||
Title: "Test",
|
||||
CreatedUTC: time.Now(),
|
||||
}); err != nil {
|
||||
t.Fatalf("InsertPost: %v", err)
|
||||
}
|
||||
|
||||
_, err := client.SubmitFeedback(context.Background(), &pb.FeedbackRequest{
|
||||
PostId: "t3_a",
|
||||
Vote: 1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("SubmitFeedback: %v", err)
|
||||
}
|
||||
|
||||
fb, err := st.RecentFeedback(10)
|
||||
if err != nil {
|
||||
t.Fatalf("RecentFeedback: %v", err)
|
||||
}
|
||||
if len(fb) != 1 || fb[0].Vote != 1 {
|
||||
t.Errorf("feedback = %v", fb)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubredditCRUD(t *testing.T) {
|
||||
client, _ := setupTestServer(t)
|
||||
|
||||
_, err := client.AddSubreddit(context.Background(), &pb.AddSubredditRequest{
|
||||
Name: "golang",
|
||||
PollSort: "new",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("AddSubreddit: %v", err)
|
||||
}
|
||||
|
||||
list, err := client.ListSubreddits(context.Background(), &pb.Empty{})
|
||||
if err != nil {
|
||||
t.Fatalf("ListSubreddits: %v", err)
|
||||
}
|
||||
if len(list.Subreddits) != 1 {
|
||||
t.Fatalf("expected 1 sub, got %d", len(list.Subreddits))
|
||||
}
|
||||
|
||||
_, err = client.RemoveSubreddit(context.Background(), &pb.RemoveRequest{Name: "golang"})
|
||||
if err != nil {
|
||||
t.Fatalf("RemoveSubreddit: %v", err)
|
||||
}
|
||||
|
||||
list, err = client.ListSubreddits(context.Background(), &pb.Empty{})
|
||||
if err != nil {
|
||||
t.Fatalf("ListSubreddits after remove: %v", err)
|
||||
}
|
||||
if len(list.Subreddits) != 0 {
|
||||
t.Errorf("expected 0 after remove, got %d", len(list.Subreddits))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatus(t *testing.T) {
|
||||
client, _ := setupTestServer(t)
|
||||
resp, err := client.Status(context.Background(), &pb.Empty{})
|
||||
if err != nil {
|
||||
t.Fatalf("Status: %v", err)
|
||||
}
|
||||
if resp.UptimeSeconds < 0 {
|
||||
t.Error("uptime should be >= 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateFilters(t *testing.T) {
|
||||
client, st := setupTestServer(t)
|
||||
|
||||
// Need to add the subreddit first (foreign key constraint).
|
||||
if err := st.AddSubreddit(domain.Subreddit{Name: "golang", PollSort: "new"}); err != nil {
|
||||
t.Fatalf("AddSubreddit: %v", err)
|
||||
}
|
||||
|
||||
resp, err := client.UpdateFilters(context.Background(), &pb.FilterRequest{
|
||||
Subreddit: "golang",
|
||||
Filters: []*pb.FilterMsg{
|
||||
{Pattern: "hiring", IsRegex: false},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateFilters: %v", err)
|
||||
}
|
||||
if len(resp.Filters) != 1 {
|
||||
t.Fatalf("expected 1 filter, got %d", len(resp.Filters))
|
||||
}
|
||||
if resp.Filters[0].Pattern != "hiring" {
|
||||
t.Errorf("Pattern = %q, want hiring", resp.Filters[0].Pattern)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStreamNotify(t *testing.T) {
|
||||
st, err := store.Open(":memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("store.Open: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { st.Close() })
|
||||
|
||||
lis, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("net.Listen: %v", err)
|
||||
}
|
||||
srv := grpc.NewServer()
|
||||
s := grpcserver.Register(srv, st, time.Now())
|
||||
go srv.Serve(lis)
|
||||
t.Cleanup(func() { srv.GracefulStop() })
|
||||
|
||||
conn, err := grpc.NewClient(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
t.Fatalf("grpc.NewClient: %v", err)
|
||||
}
|
||||
t.Cleanup(func() { conn.Close() })
|
||||
|
||||
client := pb.NewRedditReaderClient(conn)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
stream, err := client.StreamPosts(ctx, &pb.StreamRequest{})
|
||||
if err != nil {
|
||||
t.Fatalf("StreamPosts: %v", err)
|
||||
}
|
||||
|
||||
// Give the stream a moment to register the subscriber.
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
s.Notify([]domain.Post{
|
||||
{ID: "t3_stream", Subreddit: "test", Title: "Streamed", CreatedUTC: time.Now()},
|
||||
})
|
||||
|
||||
post, err := stream.Recv()
|
||||
if err != nil {
|
||||
t.Fatalf("Recv: %v", err)
|
||||
}
|
||||
if post.Title != "Streamed" {
|
||||
t.Errorf("Title = %q, want Streamed", post.Title)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,108 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package redditreader;
|
||||
|
||||
option go_package = "somegit.dev/vikingowl/reddit-reader/proto/redditreader";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
message Post {
|
||||
string id = 1;
|
||||
string subreddit = 2;
|
||||
string title = 3;
|
||||
string author = 4;
|
||||
string url = 5;
|
||||
string self_text = 6;
|
||||
int32 score = 7;
|
||||
google.protobuf.Timestamp created_utc = 8;
|
||||
google.protobuf.Timestamp fetched_at = 9;
|
||||
optional double relevance = 10;
|
||||
optional string summary = 11;
|
||||
bool read = 12;
|
||||
bool starred = 13;
|
||||
bool dismissed = 14;
|
||||
}
|
||||
|
||||
message StreamRequest {}
|
||||
|
||||
message ListRequest {
|
||||
string subreddit = 1;
|
||||
optional bool unread = 2;
|
||||
optional bool starred = 3;
|
||||
optional bool dismissed = 4;
|
||||
int32 limit = 5;
|
||||
}
|
||||
|
||||
message ListResponse {
|
||||
repeated Post posts = 1;
|
||||
}
|
||||
|
||||
message UpdateRequest {
|
||||
string id = 1;
|
||||
optional bool read = 2;
|
||||
optional bool starred = 3;
|
||||
optional bool dismissed = 4;
|
||||
}
|
||||
|
||||
message FeedbackRequest {
|
||||
string post_id = 1;
|
||||
int32 vote = 2;
|
||||
}
|
||||
|
||||
message FeedbackResponse {}
|
||||
|
||||
message SubredditMsg {
|
||||
string name = 1;
|
||||
bool enabled = 2;
|
||||
string poll_sort = 3;
|
||||
}
|
||||
|
||||
message SubredditList {
|
||||
repeated SubredditMsg subreddits = 1;
|
||||
}
|
||||
|
||||
message AddSubredditRequest {
|
||||
string name = 1;
|
||||
string poll_sort = 2;
|
||||
}
|
||||
|
||||
message RemoveRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message FilterMsg {
|
||||
int64 id = 1;
|
||||
string subreddit = 2;
|
||||
string pattern = 3;
|
||||
bool is_regex = 4;
|
||||
}
|
||||
|
||||
message FilterRequest {
|
||||
string subreddit = 1;
|
||||
repeated FilterMsg filters = 2;
|
||||
}
|
||||
|
||||
message FilterResponse {
|
||||
repeated FilterMsg filters = 1;
|
||||
}
|
||||
|
||||
message Empty {}
|
||||
|
||||
message StatusResponse {
|
||||
int64 uptime_seconds = 1;
|
||||
string last_poll = 2;
|
||||
int32 total_posts = 3;
|
||||
int32 unread_posts = 4;
|
||||
}
|
||||
|
||||
service RedditReader {
|
||||
rpc StreamPosts(StreamRequest) returns (stream Post);
|
||||
rpc ListPosts(ListRequest) returns (ListResponse);
|
||||
rpc UpdatePost(UpdateRequest) returns (Post);
|
||||
rpc SubmitFeedback(FeedbackRequest) returns (FeedbackResponse);
|
||||
rpc ListSubreddits(Empty) returns (SubredditList);
|
||||
rpc AddSubreddit(AddSubredditRequest) returns (SubredditMsg);
|
||||
rpc RemoveSubreddit(RemoveRequest) returns (Empty);
|
||||
rpc UpdateFilters(FilterRequest) returns (FilterResponse);
|
||||
rpc Status(Empty) returns (StatusResponse);
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.0
|
||||
// - protoc v6.33.1
|
||||
// source: redditreader.proto
|
||||
|
||||
package redditreader
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
RedditReader_StreamPosts_FullMethodName = "/redditreader.RedditReader/StreamPosts"
|
||||
RedditReader_ListPosts_FullMethodName = "/redditreader.RedditReader/ListPosts"
|
||||
RedditReader_UpdatePost_FullMethodName = "/redditreader.RedditReader/UpdatePost"
|
||||
RedditReader_SubmitFeedback_FullMethodName = "/redditreader.RedditReader/SubmitFeedback"
|
||||
RedditReader_ListSubreddits_FullMethodName = "/redditreader.RedditReader/ListSubreddits"
|
||||
RedditReader_AddSubreddit_FullMethodName = "/redditreader.RedditReader/AddSubreddit"
|
||||
RedditReader_RemoveSubreddit_FullMethodName = "/redditreader.RedditReader/RemoveSubreddit"
|
||||
RedditReader_UpdateFilters_FullMethodName = "/redditreader.RedditReader/UpdateFilters"
|
||||
RedditReader_Status_FullMethodName = "/redditreader.RedditReader/Status"
|
||||
)
|
||||
|
||||
// RedditReaderClient is the client API for RedditReader service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type RedditReaderClient interface {
|
||||
StreamPosts(ctx context.Context, in *StreamRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Post], error)
|
||||
ListPosts(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error)
|
||||
UpdatePost(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*Post, error)
|
||||
SubmitFeedback(ctx context.Context, in *FeedbackRequest, opts ...grpc.CallOption) (*FeedbackResponse, error)
|
||||
ListSubreddits(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*SubredditList, error)
|
||||
AddSubreddit(ctx context.Context, in *AddSubredditRequest, opts ...grpc.CallOption) (*SubredditMsg, error)
|
||||
RemoveSubreddit(ctx context.Context, in *RemoveRequest, opts ...grpc.CallOption) (*Empty, error)
|
||||
UpdateFilters(ctx context.Context, in *FilterRequest, opts ...grpc.CallOption) (*FilterResponse, error)
|
||||
Status(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*StatusResponse, error)
|
||||
}
|
||||
|
||||
type redditReaderClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewRedditReaderClient(cc grpc.ClientConnInterface) RedditReaderClient {
|
||||
return &redditReaderClient{cc}
|
||||
}
|
||||
|
||||
func (c *redditReaderClient) StreamPosts(ctx context.Context, in *StreamRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Post], error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
stream, err := c.cc.NewStream(ctx, &RedditReader_ServiceDesc.Streams[0], RedditReader_StreamPosts_FullMethodName, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &grpc.GenericClientStream[StreamRequest, Post]{ClientStream: stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type RedditReader_StreamPostsClient = grpc.ServerStreamingClient[Post]
|
||||
|
||||
func (c *redditReaderClient) ListPosts(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListResponse)
|
||||
err := c.cc.Invoke(ctx, RedditReader_ListPosts_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *redditReaderClient) UpdatePost(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*Post, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(Post)
|
||||
err := c.cc.Invoke(ctx, RedditReader_UpdatePost_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *redditReaderClient) SubmitFeedback(ctx context.Context, in *FeedbackRequest, opts ...grpc.CallOption) (*FeedbackResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(FeedbackResponse)
|
||||
err := c.cc.Invoke(ctx, RedditReader_SubmitFeedback_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *redditReaderClient) ListSubreddits(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*SubredditList, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SubredditList)
|
||||
err := c.cc.Invoke(ctx, RedditReader_ListSubreddits_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *redditReaderClient) AddSubreddit(ctx context.Context, in *AddSubredditRequest, opts ...grpc.CallOption) (*SubredditMsg, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SubredditMsg)
|
||||
err := c.cc.Invoke(ctx, RedditReader_AddSubreddit_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *redditReaderClient) RemoveSubreddit(ctx context.Context, in *RemoveRequest, opts ...grpc.CallOption) (*Empty, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, RedditReader_RemoveSubreddit_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *redditReaderClient) UpdateFilters(ctx context.Context, in *FilterRequest, opts ...grpc.CallOption) (*FilterResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(FilterResponse)
|
||||
err := c.cc.Invoke(ctx, RedditReader_UpdateFilters_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *redditReaderClient) Status(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*StatusResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(StatusResponse)
|
||||
err := c.cc.Invoke(ctx, RedditReader_Status_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// RedditReaderServer is the server API for RedditReader service.
|
||||
// All implementations must embed UnimplementedRedditReaderServer
|
||||
// for forward compatibility.
|
||||
type RedditReaderServer interface {
|
||||
StreamPosts(*StreamRequest, grpc.ServerStreamingServer[Post]) error
|
||||
ListPosts(context.Context, *ListRequest) (*ListResponse, error)
|
||||
UpdatePost(context.Context, *UpdateRequest) (*Post, error)
|
||||
SubmitFeedback(context.Context, *FeedbackRequest) (*FeedbackResponse, error)
|
||||
ListSubreddits(context.Context, *Empty) (*SubredditList, error)
|
||||
AddSubreddit(context.Context, *AddSubredditRequest) (*SubredditMsg, error)
|
||||
RemoveSubreddit(context.Context, *RemoveRequest) (*Empty, error)
|
||||
UpdateFilters(context.Context, *FilterRequest) (*FilterResponse, error)
|
||||
Status(context.Context, *Empty) (*StatusResponse, error)
|
||||
mustEmbedUnimplementedRedditReaderServer()
|
||||
}
|
||||
|
||||
// UnimplementedRedditReaderServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedRedditReaderServer struct{}
|
||||
|
||||
func (UnimplementedRedditReaderServer) StreamPosts(*StreamRequest, grpc.ServerStreamingServer[Post]) error {
|
||||
return status.Error(codes.Unimplemented, "method StreamPosts not implemented")
|
||||
}
|
||||
func (UnimplementedRedditReaderServer) ListPosts(context.Context, *ListRequest) (*ListResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListPosts not implemented")
|
||||
}
|
||||
func (UnimplementedRedditReaderServer) UpdatePost(context.Context, *UpdateRequest) (*Post, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method UpdatePost not implemented")
|
||||
}
|
||||
func (UnimplementedRedditReaderServer) SubmitFeedback(context.Context, *FeedbackRequest) (*FeedbackResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method SubmitFeedback not implemented")
|
||||
}
|
||||
func (UnimplementedRedditReaderServer) ListSubreddits(context.Context, *Empty) (*SubredditList, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListSubreddits not implemented")
|
||||
}
|
||||
func (UnimplementedRedditReaderServer) AddSubreddit(context.Context, *AddSubredditRequest) (*SubredditMsg, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method AddSubreddit not implemented")
|
||||
}
|
||||
func (UnimplementedRedditReaderServer) RemoveSubreddit(context.Context, *RemoveRequest) (*Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method RemoveSubreddit not implemented")
|
||||
}
|
||||
func (UnimplementedRedditReaderServer) UpdateFilters(context.Context, *FilterRequest) (*FilterResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method UpdateFilters not implemented")
|
||||
}
|
||||
func (UnimplementedRedditReaderServer) Status(context.Context, *Empty) (*StatusResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Status not implemented")
|
||||
}
|
||||
func (UnimplementedRedditReaderServer) mustEmbedUnimplementedRedditReaderServer() {}
|
||||
func (UnimplementedRedditReaderServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeRedditReaderServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to RedditReaderServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeRedditReaderServer interface {
|
||||
mustEmbedUnimplementedRedditReaderServer()
|
||||
}
|
||||
|
||||
func RegisterRedditReaderServer(s grpc.ServiceRegistrar, srv RedditReaderServer) {
|
||||
// If the following call panics, it indicates UnimplementedRedditReaderServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&RedditReader_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _RedditReader_StreamPosts_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(StreamRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(RedditReaderServer).StreamPosts(m, &grpc.GenericServerStream[StreamRequest, Post]{ServerStream: stream})
|
||||
}
|
||||
|
||||
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
|
||||
type RedditReader_StreamPostsServer = grpc.ServerStreamingServer[Post]
|
||||
|
||||
func _RedditReader_ListPosts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RedditReaderServer).ListPosts(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: RedditReader_ListPosts_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RedditReaderServer).ListPosts(ctx, req.(*ListRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _RedditReader_UpdatePost_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdateRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RedditReaderServer).UpdatePost(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: RedditReader_UpdatePost_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RedditReaderServer).UpdatePost(ctx, req.(*UpdateRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _RedditReader_SubmitFeedback_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(FeedbackRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RedditReaderServer).SubmitFeedback(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: RedditReader_SubmitFeedback_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RedditReaderServer).SubmitFeedback(ctx, req.(*FeedbackRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _RedditReader_ListSubreddits_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RedditReaderServer).ListSubreddits(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: RedditReader_ListSubreddits_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RedditReaderServer).ListSubreddits(ctx, req.(*Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _RedditReader_AddSubreddit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AddSubredditRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RedditReaderServer).AddSubreddit(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: RedditReader_AddSubreddit_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RedditReaderServer).AddSubreddit(ctx, req.(*AddSubredditRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _RedditReader_RemoveSubreddit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RemoveRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RedditReaderServer).RemoveSubreddit(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: RedditReader_RemoveSubreddit_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RedditReaderServer).RemoveSubreddit(ctx, req.(*RemoveRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _RedditReader_UpdateFilters_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(FilterRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RedditReaderServer).UpdateFilters(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: RedditReader_UpdateFilters_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RedditReaderServer).UpdateFilters(ctx, req.(*FilterRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _RedditReader_Status_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RedditReaderServer).Status(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: RedditReader_Status_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RedditReaderServer).Status(ctx, req.(*Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// RedditReader_ServiceDesc is the grpc.ServiceDesc for RedditReader service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var RedditReader_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "redditreader.RedditReader",
|
||||
HandlerType: (*RedditReaderServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "ListPosts",
|
||||
Handler: _RedditReader_ListPosts_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdatePost",
|
||||
Handler: _RedditReader_UpdatePost_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SubmitFeedback",
|
||||
Handler: _RedditReader_SubmitFeedback_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListSubreddits",
|
||||
Handler: _RedditReader_ListSubreddits_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "AddSubreddit",
|
||||
Handler: _RedditReader_AddSubreddit_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "RemoveSubreddit",
|
||||
Handler: _RedditReader_RemoveSubreddit_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateFilters",
|
||||
Handler: _RedditReader_UpdateFilters_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Status",
|
||||
Handler: _RedditReader_Status_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "StreamPosts",
|
||||
Handler: _RedditReader_StreamPosts_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "redditreader.proto",
|
||||
}
|
||||
Reference in New Issue
Block a user