This commit is contained in:
Tyr Mactire 2022-08-10 20:47:37 -07:00 committed by GitHub
parent 32fb83af2f
commit 30e1682104
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
127 changed files with 3338 additions and 813 deletions

17
Jenkinsfile vendored
View File

@ -1,12 +1,11 @@
pipeline { pipeline {
environment { environment {
BUILD_IMAGE = 'gobuild:1.18'
BUILD_ARGS = '-e GOCACHE=/gocache -e HOME=${WORKSPACE} -v /var/lib/jenkins/gocache:/gocache -v /var/lib/jenkins/go/pkg:/go/pkg'
PATH = '/go/bin:~/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin' PATH = '/go/bin:~/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin'
composeFile = "deployments/docker-compose-integration.yaml" composeFile = "deployments/docker-compose-integration.yaml"
networkName = "network-${env.BUILD_TAG}" networkName = "network-${env.BUILD_TAG}"
registry = 'tyrm/relay'
registryCredential = 'docker-io-feditools' registryCredential = 'docker-io-feditools'
dockerImage = ''
gitDescribe = ''
} }
agent any agent any
@ -16,8 +15,8 @@ pipeline {
stage('Build Static Assets') { stage('Build Static Assets') {
agent { agent {
docker { docker {
image 'gobuild:1.18' image "${BUILD_IMAGE}"
args '-e HOME=${WORKSPACE}' args "${BUILD_ARGS}"
reuseNode true reuseNode true
} }
} }
@ -53,8 +52,8 @@ pipeline {
stage('Test') { stage('Test') {
agent { agent {
docker { docker {
image 'gobuild:1.18' image "${BUILD_IMAGE}"
args '--network ${networkName} -e HOME=${WORKSPACE}' args "--network ${networkName} ${BUILD_ARGS}"
reuseNode true reuseNode true
} }
} }
@ -79,8 +78,8 @@ pipeline {
stage('Build Snapshot') { stage('Build Snapshot') {
agent { agent {
docker { docker {
image 'gobuild:1.18' image "${BUILD_IMAGE}"
args '--network ${networkName} -e HOME=${WORKSPACE}' args "--network ${networkName} ${BUILD_ARGS}"
reuseNode true reuseNode true
} }
} }

View File

@ -31,13 +31,13 @@ fmt:
@go fmt $(shell go list ./... | grep -v /vendor/) @go fmt $(shell go list ./... | grep -v /vendor/)
i18n-extract: i18n-extract:
goi18n extract -outdir locales goi18n extract -format yaml -outdir web/locales
i18n-merge: i18n-merge:
goi18n merge -outdir locales locales/active.*.toml locales/translate.*.toml goi18n merge -format yaml -outdir locales web/locales/active.*.toml web/locales/translate.*.toml
i18n-translations: i18n-translations:
goi18n merge -outdir locales locales/active.*.toml goi18n merge -format yaml -outdir locales web/locales/active.*.toml
lint: lint:
@echo linting @echo linting
@ -66,4 +66,4 @@ tidy:
vendor: tidy vendor: tidy
go mod vendor go mod vendor
.PHONY: build-snapshot bun-new-migration clean fmt lint stage-static npm-scss npm-upgrade docker-restart docker-start docker-stop release stage-static test test-ext test-race test-race-ext test-verbose tidy vendor .PHONY: build-snapshot bun-new-migration clean fmt i18n-extract i18n-merge i18n-translations lint stage-static npm-scss npm-upgrade docker-restart docker-start docker-stop release stage-static test test-ext test-race test-race-ext test-verbose tidy vendor

View File

@ -3,7 +3,6 @@ package server
import ( import (
"context" "context"
"github.com/feditools/go-lib" "github.com/feditools/go-lib"
liblanguage "github.com/feditools/go-lib/language"
"github.com/feditools/go-lib/metrics" "github.com/feditools/go-lib/metrics"
"github.com/feditools/relay/internal/config" "github.com/feditools/relay/internal/config"
"github.com/feditools/relay/internal/db" "github.com/feditools/relay/internal/db"
@ -12,6 +11,7 @@ import (
"github.com/feditools/relay/internal/http/activitypub" "github.com/feditools/relay/internal/http/activitypub"
"github.com/feditools/relay/internal/http/static" "github.com/feditools/relay/internal/http/static"
"github.com/feditools/relay/internal/http/webapp" "github.com/feditools/relay/internal/http/webapp"
liblanguage "github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/logic" "github.com/feditools/relay/internal/logic"
"github.com/feditools/relay/internal/runner" "github.com/feditools/relay/internal/runner"
"github.com/feditools/relay/internal/token" "github.com/feditools/relay/internal/token"

View File

@ -3,7 +3,6 @@ package server
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/feditools/go-lib/language"
"github.com/feditools/go-lib/metrics/statsd" "github.com/feditools/go-lib/metrics/statsd"
"github.com/feditools/relay/cmd/relay/action" "github.com/feditools/relay/cmd/relay/action"
"github.com/feditools/relay/internal/clock" "github.com/feditools/relay/internal/clock"
@ -12,6 +11,7 @@ import (
"github.com/feditools/relay/internal/fedi" "github.com/feditools/relay/internal/fedi"
"github.com/feditools/relay/internal/http" "github.com/feditools/relay/internal/http"
"github.com/feditools/relay/internal/kv/redis" "github.com/feditools/relay/internal/kv/redis"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/logic/logic1" "github.com/feditools/relay/internal/logic/logic1"
"github.com/feditools/relay/internal/runner/faktory" "github.com/feditools/relay/internal/runner/faktory"
"github.com/feditools/relay/internal/token" "github.com/feditools/relay/internal/token"
@ -111,7 +111,7 @@ var Start action.Action = func(topCtx context.Context) error {
// create logic module // create logic module
l.Debug("creating logic module") l.Debug("creating logic module")
logicMod, err := logic1.New(ctx, clockMod, dbClient, httpClient, tokz) logicMod, err := logic1.New(ctx, clockMod, dbClient, httpClient, kvClient, tokz)
if err != nil { if err != nil {
l.Errorf("logic: %s", err.Error()) l.Errorf("logic: %s", err.Error())
cancel() cancel()

View File

@ -23,6 +23,8 @@ func Server(cmd *cobra.Command, values config.Values) {
cmd.PersistentFlags().String(config.Keys.WebappBootstrapCSSIntegrity, values.WebappBootstrapCSSIntegrity, usage.WebappBootstrapCSSIntegrity) cmd.PersistentFlags().String(config.Keys.WebappBootstrapCSSIntegrity, values.WebappBootstrapCSSIntegrity, usage.WebappBootstrapCSSIntegrity)
cmd.PersistentFlags().String(config.Keys.WebappBootstrapJSURI, values.WebappBootstrapJSURI, usage.WebappBootstrapJSURI) cmd.PersistentFlags().String(config.Keys.WebappBootstrapJSURI, values.WebappBootstrapJSURI, usage.WebappBootstrapJSURI)
cmd.PersistentFlags().String(config.Keys.WebappBootstrapJSIntegrity, values.WebappBootstrapJSIntegrity, usage.WebappBootstrapJSIntegrity) cmd.PersistentFlags().String(config.Keys.WebappBootstrapJSIntegrity, values.WebappBootstrapJSIntegrity, usage.WebappBootstrapJSIntegrity)
cmd.PersistentFlags().String(config.Keys.WebappChartJSURI, values.WebappChartJSURI, usage.WebappChartJSURI)
cmd.PersistentFlags().String(config.Keys.WebappChartJSIntegrity, values.WebappChartJSIntegrity, usage.WebappChartJSIntegrity)
cmd.PersistentFlags().String(config.Keys.WebappFontAwesomeCSSURI, values.WebappFontAwesomeCSSURI, usage.WebappFontAwesomeCSSURI) cmd.PersistentFlags().String(config.Keys.WebappFontAwesomeCSSURI, values.WebappFontAwesomeCSSURI, usage.WebappFontAwesomeCSSURI)
cmd.PersistentFlags().String(config.Keys.WebappFontAwesomeCSSIntegrity, values.WebappFontAwesomeCSSIntegrity, usage.WebappFontAwesomeCSSIntegrity) cmd.PersistentFlags().String(config.Keys.WebappFontAwesomeCSSIntegrity, values.WebappFontAwesomeCSSIntegrity, usage.WebappFontAwesomeCSSIntegrity)
cmd.PersistentFlags().String(config.Keys.WebappLogoSrcDark, values.WebappLogoSrcDark, usage.WebappLogoSrcDark) cmd.PersistentFlags().String(config.Keys.WebappLogoSrcDark, values.WebappLogoSrcDark, usage.WebappLogoSrcDark)

8
go.mod
View File

@ -5,7 +5,7 @@ go 1.18
require ( require (
github.com/contribsys/faktory v1.6.1 github.com/contribsys/faktory v1.6.1
github.com/contribsys/faktory_worker_go v1.6.0 github.com/contribsys/faktory_worker_go v1.6.0
github.com/feditools/go-lib v0.15.2-0.20220810024219-2d57c75a871e github.com/feditools/go-lib v0.16.0
github.com/go-fed/activity v1.0.0 github.com/go-fed/activity v1.0.0
github.com/go-fed/httpsig v1.1.0 github.com/go-fed/httpsig v1.1.0
github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1 github.com/go-http-utils/etag v0.0.0-20161124023236-513ea8f21eb1
@ -19,6 +19,7 @@ require (
github.com/jackc/pgconn v1.13.0 github.com/jackc/pgconn v1.13.0
github.com/jackc/pgx/v4 v4.17.0 github.com/jackc/pgx/v4 v4.17.0
github.com/nickname76/telegrambot v1.1.1 github.com/nickname76/telegrambot v1.1.1
github.com/nicksnyder/go-i18n/v2 v2.2.0
github.com/rbcervilla/redisstore/v8 v8.1.0 github.com/rbcervilla/redisstore/v8 v8.1.0
github.com/sirupsen/logrus v1.9.0 github.com/sirupsen/logrus v1.9.0
github.com/speps/go-hashids/v2 v2.0.1 github.com/speps/go-hashids/v2 v2.0.1
@ -33,6 +34,8 @@ require (
github.com/uptrace/bun/extra/bundebug v1.1.7 github.com/uptrace/bun/extra/bundebug v1.1.7
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 golang.org/x/sync v0.0.0-20220513210516-0976fa681c29
golang.org/x/text v0.3.7
gopkg.in/yaml.v2 v2.4.0
modernc.org/sqlite v1.17.1 modernc.org/sqlite v1.17.1
) )
@ -71,7 +74,6 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nickname76/repeater v1.0.1 // indirect github.com/nickname76/repeater v1.0.1 // indirect
github.com/nicksnyder/go-i18n/v2 v2.2.0 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.1 // indirect github.com/pelletier/go-toml/v2 v2.0.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
@ -88,11 +90,9 @@ require (
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.10 // indirect golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
gopkg.in/ini.v1 v1.66.6 // indirect gopkg.in/ini.v1 v1.66.6 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/uint128 v1.2.0 // indirect lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.36.0 // indirect modernc.org/cc/v3 v3.36.0 // indirect

6
go.sum
View File

@ -37,8 +37,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
@ -90,8 +90,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/feditools/go-lib v0.15.2-0.20220810024219-2d57c75a871e h1:cvxBlyoDUq1KOnWD0Xb8muGu+X70T8vFe2UYdvpS9Es= github.com/feditools/go-lib v0.16.0 h1:sxAxvRb97Xn/jaDOSljMsWiXTVulbCZZHhr4sMRDrZE=
github.com/feditools/go-lib v0.15.2-0.20220810024219-2d57c75a871e/go.mod h1:LtdLBvApYAhdblS6rTGQ12ZzySI9/6PyTiZlxw5uX10= github.com/feditools/go-lib v0.16.0/go.mod h1:kMw3Dl5vJ2j0dHtD5OHPy9lsm8v+p/szKM01yqX+gMY=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=
github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=

View File

@ -45,6 +45,8 @@ type KeyNames struct {
WebappBootstrapCSSIntegrity string WebappBootstrapCSSIntegrity string
WebappBootstrapJSURI string WebappBootstrapJSURI string
WebappBootstrapJSIntegrity string WebappBootstrapJSIntegrity string
WebappChartJSURI string
WebappChartJSIntegrity string
WebappFontAwesomeCSSURI string WebappFontAwesomeCSSURI string
WebappFontAwesomeCSSIntegrity string WebappFontAwesomeCSSIntegrity string
WebappLogoSrcDark string WebappLogoSrcDark string
@ -104,6 +106,8 @@ var Keys = KeyNames{
WebappBootstrapCSSIntegrity: "webapp-bootstrap-css-integrity", WebappBootstrapCSSIntegrity: "webapp-bootstrap-css-integrity",
WebappBootstrapJSURI: "webapp-bootstrap-js-uri", WebappBootstrapJSURI: "webapp-bootstrap-js-uri",
WebappBootstrapJSIntegrity: "webapp-bootstrap-js-integrity", WebappBootstrapJSIntegrity: "webapp-bootstrap-js-integrity",
WebappChartJSURI: "webapp-chart-js-uri",
WebappChartJSIntegrity: "webapp-chart-js-integrity",
WebappFontAwesomeCSSURI: "webapp-fontawesome-css-uri", WebappFontAwesomeCSSURI: "webapp-fontawesome-css-uri",
WebappFontAwesomeCSSIntegrity: "webapp-fontawesome-css-integrity", WebappFontAwesomeCSSIntegrity: "webapp-fontawesome-css-integrity",
WebappLogoSrcDark: "webapp-logo-src-dark", WebappLogoSrcDark: "webapp-logo-src-dark",

View File

@ -45,6 +45,8 @@ type Values struct {
WebappBootstrapCSSIntegrity string WebappBootstrapCSSIntegrity string
WebappBootstrapJSURI string WebappBootstrapJSURI string
WebappBootstrapJSIntegrity string WebappBootstrapJSIntegrity string
WebappChartJSURI string
WebappChartJSIntegrity string
WebappFontAwesomeCSSURI string WebappFontAwesomeCSSURI string
WebappFontAwesomeCSSIntegrity string WebappFontAwesomeCSSIntegrity string
WebappLogoSrcDark string WebappLogoSrcDark string
@ -104,6 +106,8 @@ var Defaults = Values{
WebappBootstrapCSSIntegrity: "sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor", WebappBootstrapCSSIntegrity: "sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor",
WebappBootstrapJSURI: "https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js", WebappBootstrapJSURI: "https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js",
WebappBootstrapJSIntegrity: "sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2", WebappBootstrapJSIntegrity: "sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2",
WebappChartJSURI: "https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js",
WebappChartJSIntegrity: "sha384-9MhbyIRcBVQiiC7FSd7T38oJNj2Zh+EfxS7/vjhBi4OOT78NlHSnzM31EZRWR1LZ",
WebappFontAwesomeCSSURI: "https://cdn.fedi.tools/vendor/fontawesome-free-6.1.1/css/all.min.css", WebappFontAwesomeCSSURI: "https://cdn.fedi.tools/vendor/fontawesome-free-6.1.1/css/all.min.css",
WebappFontAwesomeCSSIntegrity: "sha384-/frq1SRXYH/bSyou/HUp/hib7RVN1TawQYja658FEOodR/FQBKVqT9Ol+Oz3Olq5", WebappFontAwesomeCSSIntegrity: "sha384-/frq1SRXYH/bSyou/HUp/hib7RVN1TawQYja658FEOodR/FQBKVqT9Ol+Oz3Olq5",
WebappLogoSrcDark: "https://cdn.fedi.tools/img/feditools-logo-dark.svg", WebappLogoSrcDark: "https://cdn.fedi.tools/img/feditools-logo-dark.svg",

View File

@ -1,6 +1,7 @@
package activitypub package activitypub
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"github.com/feditools/go-lib/fedihelper" "github.com/feditools/go-lib/fedihelper"
@ -120,5 +121,7 @@ func (m *Module) inboxPostHandler(w nethttp.ResponseWriter, r *nethttp.Request)
return return
} }
go m.logic.MetricsIncReceived(context.Background(), instance.ID)
w.WriteHeader(nethttp.StatusAccepted) w.WriteHeader(nethttp.StatusAccepted)
} }

View File

@ -0,0 +1,16 @@
package template
import (
"github.com/feditools/relay/internal/models"
)
// AdminInstanceViewName is the name of the admin block list template.
const AdminInstanceViewName = "admin_instance_view"
// AdminInstanceView contains the variables for the admin block list template.
type AdminInstanceView struct {
Common
Instance *models.Instance
Breadcrumbs *[]Breadcrumb
}

View File

@ -0,0 +1,6 @@
package template
type Breadcrumb struct {
HRef string
Text string
}

View File

@ -1,8 +1,8 @@
package template package template
import ( import (
"github.com/feditools/go-lib/language"
libtemplate "github.com/feditools/go-lib/template" libtemplate "github.com/feditools/go-lib/template"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/models" "github.com/feditools/relay/internal/models"
) )

View File

@ -1,9 +1,9 @@
package template package template
import ( import (
"github.com/feditools/go-lib/language"
libtemplate "github.com/feditools/go-lib/template" libtemplate "github.com/feditools/go-lib/template"
"github.com/feditools/relay/internal/config" "github.com/feditools/relay/internal/config"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/models" "github.com/feditools/relay/internal/models"
"github.com/feditools/relay/internal/path" "github.com/feditools/relay/internal/path"
"github.com/feditools/relay/internal/token" "github.com/feditools/relay/internal/token"
@ -41,6 +41,7 @@ func New(tokz *token.Tokenizer) (*template.Template, error) {
"pathAppAdminBlockExportCSV": path.GenAppAdminBlockExportCSV, "pathAppAdminBlockExportCSV": path.GenAppAdminBlockExportCSV,
"pathAppAdminBlockExportJSON": path.GenAppAdminBlockExportJSON, "pathAppAdminBlockExportJSON": path.GenAppAdminBlockExportJSON,
"pathAppAdminHome": path.GenAppAdminHomePath, "pathAppAdminHome": path.GenAppAdminHomePath,
"pathAppAdminInstanceView": path.GenAppAdminInstanceViewPath,
"pathAppHome": path.GenAppHomePath, "pathAppHome": path.GenAppHomePath,
"pathAppLog": path.GenAppLogPath, "pathAppLog": path.GenAppLogPath,
"pathAppLogin": path.GenAppLoginPath, "pathAppLogin": path.GenAppLoginPath,

View File

@ -2,9 +2,9 @@ package webapp
import ( import (
libhttp "github.com/feditools/go-lib/http" libhttp "github.com/feditools/go-lib/http"
"github.com/feditools/go-lib/language"
libtemplate "github.com/feditools/go-lib/template" libtemplate "github.com/feditools/go-lib/template"
"github.com/feditools/relay/internal/http/template" "github.com/feditools/relay/internal/http/template"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/path" "github.com/feditools/relay/internal/path"
"net/http" "net/http"
) )

View File

@ -1,8 +1,8 @@
package webapp package webapp
import ( import (
"github.com/feditools/go-lib/language"
libtemplate "github.com/feditools/go-lib/template" libtemplate "github.com/feditools/go-lib/template"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/models" "github.com/feditools/relay/internal/models"
"github.com/feditools/relay/internal/util" "github.com/feditools/relay/internal/util"
"net/http" "net/http"
@ -93,7 +93,7 @@ func (m *Module) doAddBlock(w http.ResponseWriter, r *http.Request) {
FormAddError: &libtemplate.Alert{ FormAddError: &libtemplate.Alert{
Color: libtemplate.ColorDanger, Color: libtemplate.ColorDanger,
Text: localizer.TextBlockExists(domain).String(), Text: localizer.TextBlockExistsDomain(domain).String(),
}, },
FormAddDomainValue: domain, FormAddDomainValue: domain,

View File

@ -2,9 +2,9 @@ package webapp
import ( import (
"errors" "errors"
"github.com/feditools/go-lib/language"
libtemplate "github.com/feditools/go-lib/template" libtemplate "github.com/feditools/go-lib/template"
"github.com/feditools/relay/internal/db" "github.com/feditools/relay/internal/db"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/models" "github.com/feditools/relay/internal/models"
"github.com/feditools/relay/internal/token" "github.com/feditools/relay/internal/token"
"net/http" "net/http"

View File

@ -3,9 +3,9 @@ package webapp
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/feditools/go-lib/language"
libtemplate "github.com/feditools/go-lib/template" libtemplate "github.com/feditools/go-lib/template"
"github.com/feditools/relay/internal/db" "github.com/feditools/relay/internal/db"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/models" "github.com/feditools/relay/internal/models"
"github.com/feditools/relay/internal/token" "github.com/feditools/relay/internal/token"
"github.com/feditools/relay/internal/util" "github.com/feditools/relay/internal/util"

View File

@ -5,8 +5,8 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
libhttp "github.com/feditools/go-lib/http" libhttp "github.com/feditools/go-lib/http"
"github.com/feditools/go-lib/language"
libtemplate "github.com/feditools/go-lib/template" libtemplate "github.com/feditools/go-lib/template"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/models" "github.com/feditools/relay/internal/models"
"github.com/feditools/relay/internal/util" "github.com/feditools/relay/internal/util"
"io" "io"

View File

@ -2,9 +2,9 @@ package webapp
import ( import (
"context" "context"
"github.com/feditools/go-lib/language"
libtemplate "github.com/feditools/go-lib/template" libtemplate "github.com/feditools/go-lib/template"
"github.com/feditools/relay/internal/http/template" "github.com/feditools/relay/internal/http/template"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/models" "github.com/feditools/relay/internal/models"
"net/http" "net/http"
) )

View File

@ -2,9 +2,9 @@ package webapp
import ( import (
libhttp "github.com/feditools/go-lib/http" libhttp "github.com/feditools/go-lib/http"
"github.com/feditools/go-lib/language"
libtemplate "github.com/feditools/go-lib/template" libtemplate "github.com/feditools/go-lib/template"
"github.com/feditools/relay/internal/http/template" "github.com/feditools/relay/internal/http/template"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/path" "github.com/feditools/relay/internal/path"
"net/http" "net/http"
) )

View File

@ -0,0 +1,113 @@
package webapp
import (
"errors"
libtemplate "github.com/feditools/go-lib/template"
"github.com/feditools/relay/internal/db"
"github.com/feditools/relay/internal/http/template"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/models"
"github.com/feditools/relay/internal/path"
"github.com/feditools/relay/internal/token"
"github.com/gorilla/mux"
"net/http"
)
// AdminInstanceViewGetHandler serves the instance info page.
func (m *Module) AdminInstanceViewGetHandler(w http.ResponseWriter, r *http.Request) {
l := logger.WithField("func", "AdminInstanceViewGetHandler")
// lookup instance
vars := mux.Vars(r)
kind, id, err := m.tokz.DecodeToken(vars[path.VarInstanceID])
if err != nil {
l.Debugf("decode token: %s", err.Error())
m.returnErrorPage(w, r, http.StatusBadRequest, "bad token")
return
}
if kind != token.KindInstance {
l.Debug("token is wrong kind")
m.returnErrorPage(w, r, http.StatusBadRequest, "bad token")
return
}
instance, err := m.db.ReadInstance(r.Context(), id)
if err != nil && !errors.Is(err, db.ErrNoEntries) {
l.Errorf("db read instance: %s", err.Error())
m.returnErrorPage(w, r, http.StatusInternalServerError, err.Error())
return
}
if errors.Is(err, db.ErrNoEntries) {
m.returnErrorPage(w, r, http.StatusNotFound, vars[path.VarInstanceID])
return
}
m.displayAdminInstanceView(w, r, displayAdminInstanceViewConfig{
Instance: instance,
})
}
type displayAdminInstanceViewConfig struct {
Alerts *[]libtemplate.Alert
Instance *models.Instance
}
func (m *Module) displayAdminInstanceView(w http.ResponseWriter, r *http.Request, config displayAdminInstanceViewConfig) {
l := logger.WithField("func", "displayAdminInstanceView")
// get localizer
localizer := r.Context().Value(ContextKeyLocalizer).(*language.Localizer) //nolint
// Init template variables
tmplVars := &template.AdminInstanceView{
Common: template.Common{
PageTitle: localizer.TextInstance(1).String() + " " + config.Instance.UnicodeDomain(),
},
Breadcrumbs: &[]template.Breadcrumb{
{
Text: localizer.TextInstance(2).String(),
HRef: path.AppAdminInstance,
},
{
Text: config.Instance.UnicodeDomain(),
},
},
Instance: config.Instance,
}
// alerts
tmplVars.Alerts = config.Alerts
// get metrics
deliverErrors, err := m.logic.MetricsGetDeliverErrorWeek(r.Context(), config.Instance.ID)
if err != nil {
l.Errorf("get metrics deliver error: %s", err.Error())
m.returnErrorPage(w, r, http.StatusInternalServerError, err.Error())
return
}
deliverSuccess, err := m.logic.MetricsGetDeliverSuccessWeek(r.Context(), config.Instance.ID)
if err != nil {
l.Errorf("get metrics deliver error: %s", err.Error())
m.returnErrorPage(w, r, http.StatusInternalServerError, err.Error())
return
}
received, err := m.logic.MetricsGetReceivedWeek(r.Context(), config.Instance.ID)
if err != nil {
l.Errorf("get metrics deliver error: %s", err.Error())
m.returnErrorPage(w, r, http.StatusInternalServerError, err.Error())
return
}
tmplVars.AddFooterExtraScript(JSAdminInstanceView(deliverErrors, deliverSuccess, received))
tmplVars.AddFooterScript(m.footerScriptChartJS)
m.executeTemplate(w, r, template.AdminInstanceViewName, tmplVars)
}

View File

@ -1,8 +1,8 @@
package webapp package webapp
import ( import (
"github.com/feditools/go-lib/language"
"github.com/feditools/relay/internal/http/template" "github.com/feditools/relay/internal/http/template"
"github.com/feditools/relay/internal/language"
"net/http" "net/http"
) )

View File

@ -1,8 +1,8 @@
package webapp package webapp
import ( import (
"github.com/feditools/go-lib/language"
"github.com/feditools/relay/internal/http/template" "github.com/feditools/relay/internal/http/template"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/models" "github.com/feditools/relay/internal/models"
"github.com/feditools/relay/internal/path" "github.com/feditools/relay/internal/path"
"net/http" "net/http"

View File

@ -2,7 +2,7 @@ package webapp
import ( import (
"fmt" "fmt"
"github.com/feditools/go-lib/language" "github.com/feditools/relay/internal/language"
) )
const jsAdminBlock = ` const jsAdminBlock = `

View File

@ -0,0 +1,82 @@
package webapp
import (
"fmt"
"github.com/feditools/relay/internal/logic"
"strconv"
"strings"
)
const jsAdminInstanceView = `
new Chart(document.getElementById("deliveryChartContainer"), {
type: 'line',
data: {
labels: [%s],
datasets: [{
data: [%s],
label: "Error",
borderColor: "#c45850",
fill: false,
lineTension: 0.2
}, {
data: [%s],
label: "Success",
borderColor: "#3cba9f",
fill: false,
lineTension: 0.2
}
]
},
options: {}
});
new Chart(document.getElementById("receiveChartContainer"), {
type: 'line',
data: {
labels: [%s],
datasets: [{
data: [%s],
label: "Received",
borderColor: "#3e95cd",
fill: false,
lineTension: 0.2
}
]
},
options: {}
});
`
func JSAdminInstanceView(deliverErrors, deliverSuccess, receive *logic.MetricsDataPointsTime) string {
labels := make([]string, len(*deliverErrors))
for i, dp := range *deliverErrors {
labels[i] = dp.X.Format("\"Jan 02 2006\"")
}
deliverErrorsLen := len(*deliverErrors)
deliverErrorsData := make([]string, deliverErrorsLen)
for i, dp := range *deliverErrors {
deliverErrorsData[deliverErrorsLen-1-i] = strconv.FormatInt(int64(dp.Y), 10)
}
deliverSuccessLen := len(*deliverSuccess)
deliverSuccessData := make([]string, deliverSuccessLen)
for i, dp := range *deliverSuccess {
deliverSuccessData[deliverSuccessLen-1-i] = strconv.FormatInt(int64(dp.Y), 10)
}
receiveLen := len(*receive)
receiveData := make([]string, receiveLen)
for i, dp := range *receive {
receiveData[receiveLen-1-i] = strconv.FormatInt(int64(dp.Y), 10)
}
return fmt.Sprintf(
jsAdminInstanceView,
strings.Join(labels, ","),
strings.Join(deliverErrorsData, ","),
strings.Join(deliverSuccessData, ","),
strings.Join(labels, ","),
strings.Join(receiveData, ","),
)
}

View File

@ -3,10 +3,10 @@ package webapp
import ( import (
"errors" "errors"
"github.com/feditools/go-lib" "github.com/feditools/go-lib"
"github.com/feditools/go-lib/language"
libtemplate "github.com/feditools/go-lib/template" libtemplate "github.com/feditools/go-lib/template"
"github.com/feditools/relay/internal/db" "github.com/feditools/relay/internal/db"
"github.com/feditools/relay/internal/http/template" "github.com/feditools/relay/internal/http/template"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/models" "github.com/feditools/relay/internal/models"
"github.com/feditools/relay/internal/path" "github.com/feditools/relay/internal/path"
nethttp "net/http" nethttp "net/http"

View File

@ -2,9 +2,9 @@ package webapp
import ( import (
libhttp "github.com/feditools/go-lib/http" libhttp "github.com/feditools/go-lib/http"
"github.com/feditools/go-lib/language"
libtemplate "github.com/feditools/go-lib/template" libtemplate "github.com/feditools/go-lib/template"
"github.com/feditools/relay/internal/http/template" "github.com/feditools/relay/internal/http/template"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/path" "github.com/feditools/relay/internal/path"
"net/http" "net/http"
) )

View File

@ -3,7 +3,7 @@ package webapp
import ( import (
"context" "context"
libhttp "github.com/feditools/go-lib/http" libhttp "github.com/feditools/go-lib/http"
"github.com/feditools/go-lib/language" "github.com/feditools/relay/internal/language"
"net/http" "net/http"
) )

View File

@ -1 +0,0 @@
package webapp

View File

@ -39,6 +39,7 @@ func (m *Module) Route(s *http.Server) error {
admin.HandleFunc(path.AppAdminSubHome, m.AdminHomeGetHandler).Methods(nethttp.MethodGet) admin.HandleFunc(path.AppAdminSubHome, m.AdminHomeGetHandler).Methods(nethttp.MethodGet)
admin.HandleFunc(path.AppAdminSubHome, m.AdminHomePostHandler).Methods(nethttp.MethodPost) admin.HandleFunc(path.AppAdminSubHome, m.AdminHomePostHandler).Methods(nethttp.MethodPost)
admin.HandleFunc(path.AppAdminSubInstance, m.AdminInstanceGetHandler).Methods(nethttp.MethodGet) admin.HandleFunc(path.AppAdminSubInstance, m.AdminInstanceGetHandler).Methods(nethttp.MethodGet)
admin.HandleFunc(path.AppAdminSubInstanceView, m.AdminInstanceViewGetHandler).Methods(nethttp.MethodGet)
return nil return nil
} }

View File

@ -3,9 +3,9 @@ package webapp
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/feditools/go-lib/language"
libtemplate "github.com/feditools/go-lib/template" libtemplate "github.com/feditools/go-lib/template"
"github.com/feditools/relay/internal/http/template" "github.com/feditools/relay/internal/http/template"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/models" "github.com/feditools/relay/internal/models"
"net/http" "net/http"
) )

View File

@ -2,8 +2,8 @@ package webapp
import ( import (
"bytes" "bytes"
"github.com/feditools/go-lib/language"
"github.com/feditools/relay/internal/http/template" "github.com/feditools/relay/internal/http/template"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/models" "github.com/feditools/relay/internal/models"
"github.com/feditools/relay/internal/path" "github.com/feditools/relay/internal/path"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -97,12 +97,6 @@ func (m *Module) makeNavbar(r *http.Request) (template.Navbar, error) {
FAIcon: "house", FAIcon: "house",
URL: path.AppHome, URL: path.AppHome,
}, },
{
Text: l.TextActivityLog(1).String(),
MatchStr: path.ReAppLog,
FAIcon: "newspaper",
URL: path.AppLog,
},
} }
// show blocks page if any exist // show blocks page if any exist

View File

@ -3,7 +3,6 @@ package webapp
import ( import (
"context" "context"
"encoding/gob" "encoding/gob"
"github.com/feditools/go-lib/language"
"github.com/feditools/go-lib/metrics" "github.com/feditools/go-lib/metrics"
libtemplate "github.com/feditools/go-lib/template" libtemplate "github.com/feditools/go-lib/template"
"github.com/feditools/relay/internal/config" "github.com/feditools/relay/internal/config"
@ -12,6 +11,7 @@ import (
ihttp "github.com/feditools/relay/internal/http" ihttp "github.com/feditools/relay/internal/http"
itemplate "github.com/feditools/relay/internal/http/template" itemplate "github.com/feditools/relay/internal/http/template"
"github.com/feditools/relay/internal/kv" "github.com/feditools/relay/internal/kv"
"github.com/feditools/relay/internal/language"
"github.com/feditools/relay/internal/logic" "github.com/feditools/relay/internal/logic"
"github.com/feditools/relay/internal/path" "github.com/feditools/relay/internal/path"
"github.com/feditools/relay/internal/runner" "github.com/feditools/relay/internal/runner"
@ -49,6 +49,7 @@ type Module struct {
logoSrcLight string logoSrcLight string
headLinks []libtemplate.HeadLink headLinks []libtemplate.HeadLink
footerScripts []libtemplate.Script footerScripts []libtemplate.Script
footerScriptChartJS libtemplate.Script
sigCache map[string]string sigCache map[string]string
sigCacheLock sync.RWMutex sigCacheLock sync.RWMutex
@ -143,6 +144,12 @@ func New(
}, },
} }
fsCanvasJS := libtemplate.Script{
Src: viper.GetString(config.Keys.WebappChartJSURI),
CrossOrigin: COAnonymous,
Integrity: viper.GetString(config.Keys.WebappChartJSIntegrity),
}
return &Module{ return &Module{
db: d, db: d,
fedi: f, fedi: f,
@ -160,6 +167,7 @@ func New(
logoSrcLight: viper.GetString(config.Keys.WebappLogoSrcLight), logoSrcLight: viper.GetString(config.Keys.WebappLogoSrcLight),
headLinks: hl, headLinks: hl,
footerScripts: fs, footerScripts: fs,
footerScriptChartJS: fsCanvasJS,
sigCache: map[string]string{}, sigCache: map[string]string{},
}, nil }, nil

View File

@ -1,6 +1,9 @@
package kv package kv
import "strconv" import (
"strconv"
"time"
)
const ( const (
keyBase = "relay:" keyBase = "relay:"
@ -16,6 +19,15 @@ const (
keyInstance = keyBase + "instance:" keyInstance = keyBase + "instance:"
keyInstanceOAuth = keyInstance + "oauth:" keyInstanceOAuth = keyInstance + "oauth:"
keyMetrics = keyBase + "metrics:"
keyMetricsDeliver = keyMetrics + "deliver:"
keyMetricsDeliverError = keyMetricsDeliver + "e:"
keyMetricsDeliverErrorTotal = keyMetricsDeliver + "et:"
keyMetricsDeliverSuccess = keyMetricsDeliver + "s:"
keyMetricsDeliverSuccessTotal = keyMetricsDeliver + "st:"
keyMetricsReceived = keyMetrics + "received:"
keyMetricsReceivedTotal = keyMetrics + "receivedt:"
keySession = keyBase + "session:" keySession = keyBase + "session:"
) )
@ -34,5 +46,35 @@ func KeyFediHostMeta(d string) string { return keyFediHostMeta + d }
// KeyInstanceOAuth returns the kv key which holds an instance's oauth tokens. // KeyInstanceOAuth returns the kv key which holds an instance's oauth tokens.
func KeyInstanceOAuth(i int64) string { return keyInstanceOAuth + strconv.FormatInt(i, 10) } func KeyInstanceOAuth(i int64) string { return keyInstanceOAuth + strconv.FormatInt(i, 10) }
// KeyMetricsDeliverError returns the kv key which holds the number of delivery errors to an instance.
func KeyMetricsDeliverError(t time.Time) string {
return keyMetricsDeliverError + t.Format("2006:01:02")
}
// KeyMetricsDeliverErrorTotal returns the kv key which holds the number of delivery errors
func KeyMetricsDeliverErrorTotal(t time.Time) string {
return keyMetricsDeliverErrorTotal + t.Format("2006:01:02")
}
// KeyMetricsDeliverSuccess returns the kv key which holds the number of delivery successes to an instance.
func KeyMetricsDeliverSuccess(t time.Time) string {
return keyMetricsDeliverSuccess + t.Format("2006:01:02")
}
// KeyMetricsDeliverSuccessTotal returns the kv key which holds the number of delivery successes to an instance.
func KeyMetricsDeliverSuccessTotal(t time.Time) string {
return keyMetricsDeliverSuccessTotal + t.Format("2006:01:02")
}
// KeyMetricsReceived returns the kv key which holds the number of receives from an instance.
func KeyMetricsReceived(t time.Time) string {
return keyMetricsReceived + t.Format("2006:01:02")
}
// KeyMetricsReceivedTotal returns the kv key which holds the number of receives.
func KeyMetricsReceivedTotal(t time.Time) string {
return keyMetricsReceivedTotal + t.Format("2006:01:02")
}
// KeySession returns the base kv key prefix. // KeySession returns the base kv key prefix.
func KeySession() string { return keySession } func KeySession() string { return keySession }

View File

@ -1,10 +1,26 @@
package kv package kv
import ( import (
"context"
"github.com/feditools/go-lib/fedihelper" "github.com/feditools/go-lib/fedihelper"
"time"
) )
// KV represents a key value store. // KV represents a key value store.
type KV interface { type KV interface {
fedihelper.KV fedihelper.KV
GetMetricsDeliverError(ctx context.Context, instanceID int64, timestamp time.Time) (int, Error)
GetMetricsDeliverErrorTotal(ctx context.Context, timestamp time.Time) (int, Error)
GetMetricsDeliverSuccess(ctx context.Context, instanceID int64, timestamp time.Time) (int, Error)
GetMetricsDeliverSuccessTotal(ctx context.Context, timestamp time.Time) (int, Error)
GetMetricsReceived(ctx context.Context, instanceID int64, timestamp time.Time) (int, Error)
GetMetricsReceivedTotal(ctx context.Context, timestamp time.Time) (int, Error)
IncMetricsDeliverError(ctx context.Context, instanceID int64, timestamp time.Time) Error
IncMetricsDeliverErrorTotal(ctx context.Context, timestamp time.Time) Error
IncMetricsDeliverSuccess(ctx context.Context, instanceID int64, timestamp time.Time) Error
IncMetricsDeliverSuccessTotal(ctx context.Context, timestamp time.Time) Error
IncMetricsReceived(ctx context.Context, instanceID int64, timestamp time.Time) Error
IncMetricsReceivedTotal(ctx context.Context, timestamp time.Time) Error
} }

View File

@ -0,0 +1,116 @@
package redis
import (
"context"
"github.com/feditools/relay/internal/kv"
"strconv"
"time"
)
func (c *Client) GetMetricsDeliverError(ctx context.Context, instanceID int64, timestamp time.Time) (int, kv.Error) {
count, err := c.redis.HGet(ctx, kv.KeyMetricsDeliverError(timestamp), strconv.FormatInt(instanceID, 10)).Int()
if err != nil {
return 0, c.ProcessError(err)
}
return count, nil
}
func (c *Client) GetMetricsDeliverErrorTotal(ctx context.Context, timestamp time.Time) (int, kv.Error) {
count, err := c.redis.Get(ctx, kv.KeyMetricsDeliverErrorTotal(timestamp)).Int()
if err != nil {
return 0, c.ProcessError(err)
}
return count, nil
}
func (c *Client) GetMetricsDeliverSuccess(ctx context.Context, instanceID int64, timestamp time.Time) (int, kv.Error) {
count, err := c.redis.HGet(ctx, kv.KeyMetricsDeliverSuccess(timestamp), strconv.FormatInt(instanceID, 10)).Int()
if err != nil {
return 0, c.ProcessError(err)
}
return count, nil
}
func (c *Client) GetMetricsDeliverSuccessTotal(ctx context.Context, timestamp time.Time) (int, kv.Error) {
count, err := c.redis.Get(ctx, kv.KeyMetricsDeliverSuccessTotal(timestamp)).Int()
if err != nil {
return 0, c.ProcessError(err)
}
return count, nil
}
func (c *Client) GetMetricsReceived(ctx context.Context, instanceID int64, timestamp time.Time) (int, kv.Error) {
count, err := c.redis.HGet(ctx, kv.KeyMetricsReceived(timestamp), strconv.FormatInt(instanceID, 10)).Int()
if err != nil {
return 0, c.ProcessError(err)
}
return count, nil
}
func (c *Client) GetMetricsReceivedTotal(ctx context.Context, timestamp time.Time) (int, kv.Error) {
count, err := c.redis.Get(ctx, kv.KeyMetricsReceivedTotal(timestamp)).Int()
if err != nil {
return 0, c.ProcessError(err)
}
return count, nil
}
func (c *Client) IncMetricsDeliverError(ctx context.Context, instanceID int64, timestamp time.Time) kv.Error {
_, err := c.redis.HIncrBy(ctx, kv.KeyMetricsDeliverError(timestamp), strconv.FormatInt(instanceID, 10), 1).Result()
if err != nil {
return c.ProcessError(err)
}
return nil
}
func (c *Client) IncMetricsDeliverErrorTotal(ctx context.Context, timestamp time.Time) kv.Error {
_, err := c.redis.IncrBy(ctx, kv.KeyMetricsDeliverErrorTotal(timestamp), 1).Result()
if err != nil {
return c.ProcessError(err)
}
return nil
}
func (c *Client) IncMetricsDeliverSuccess(ctx context.Context, instanceID int64, timestamp time.Time) kv.Error {
_, err := c.redis.HIncrBy(ctx, kv.KeyMetricsDeliverSuccess(timestamp), strconv.FormatInt(instanceID, 10), 1).Result()
if err != nil {
return c.ProcessError(err)
}
return nil
}
func (c *Client) IncMetricsDeliverSuccessTotal(ctx context.Context, timestamp time.Time) kv.Error {
_, err := c.redis.IncrBy(ctx, kv.KeyMetricsDeliverSuccessTotal(timestamp), 1).Result()
if err != nil {
return c.ProcessError(err)
}
return nil
}
func (c *Client) IncMetricsReceived(ctx context.Context, instanceID int64, timestamp time.Time) kv.Error {
_, err := c.redis.HIncrBy(ctx, kv.KeyMetricsReceived(timestamp), strconv.FormatInt(instanceID, 10), 1).Result()
if err != nil {
return c.ProcessError(err)
}
return nil
}
func (c *Client) IncMetricsReceivedTotal(ctx context.Context, timestamp time.Time) kv.Error {
_, err := c.redis.IncrBy(ctx, kv.KeyMetricsReceivedTotal(timestamp), 1).Result()
if err != nil {
return c.ProcessError(err)
}
return nil
}

View File

@ -1,19 +1,14 @@
package language package language
import ( import (
"embed" "github.com/feditools/relay/web"
"io/ioutil"
"strings"
"github.com/nicksnyder/go-i18n/v2/i18n" "github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language" "golang.org/x/text/language"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"io/ioutil"
"strings"
) )
// Locales contains static files required by the application
//go:embed locales/active.*.yaml
var Locales embed.FS
// DefaultLanguage is the default language of the application. // DefaultLanguage is the default language of the application.
var DefaultLanguage = language.English var DefaultLanguage = language.English
@ -34,7 +29,7 @@ func New() (*Module, error) {
module.langBundle.RegisterUnmarshalFunc("yaml", yaml.Unmarshal) module.langBundle.RegisterUnmarshalFunc("yaml", yaml.Unmarshal)
dir, err := Locales.ReadDir("locales") dir, err := web.Files.ReadDir("locales")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -45,7 +40,7 @@ func New() (*Module, error) {
l.Debugf("loading language file: %s", d.Name()) l.Debugf("loading language file: %s", d.Name())
// open it // open it
file, err := Locales.Open("locales/" + d.Name()) file, err := web.Files.Open("locales/" + d.Name())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -68,3 +63,16 @@ func New() (*Module, error) {
// Language returns the default language. // Language returns the default language.
func (m Module) Language() language.Tag { return m.lang } func (m Module) Language() language.Tag { return m.lang }
func isEmptyYaml(b []byte) bool {
switch string(b) {
case "":
return true
case "---":
return true
case "---\n":
return true
default:
return false
}
}

View File

@ -0,0 +1,114 @@
package language
import (
"fmt"
"testing"
"golang.org/x/text/language"
)
const (
testCantGetLocalizer = "[%d] can't get localizer for %s: %s"
testGotInvalidLanguage = "[%d] got invalid language for %s, got: %v, want: %v"
testGotInvalidTranslation = "[%d] got invalid translation for %s, got: %v, want: %v"
testTranslatedTo = "[%d] Translating to %s"
)
func TestNew(t *testing.T) {
langMod, err := New()
if err != nil {
t.Errorf("can't get new language module: %s", err.Error())
return
}
if langMod == nil {
t.Errorf("language module is nil")
return
}
if langMod.langBundle == nil {
t.Errorf("language module's bundle is nil")
return
}
if langMod.Language() != DefaultLanguage {
t.Errorf("got invalid language, got: %v, want: %v,", langMod.Language().String(), DefaultLanguage.String())
return
}
}
func TestIsEmptyYaml(t *testing.T) {
t.Parallel()
tables := []struct {
input string
output bool
}{
{"", true},
{"---", true},
{"---\n", true},
{"---\nvalid: yaml", false},
}
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf("[%d] running isEmptyYaml", i)
t.Run(name, func(t *testing.T) {
t.Parallel()
result := isEmptyYaml([]byte(table.input))
if result != table.output {
t.Errorf("[%d] got invalid response, got: %v, want: %v,", i, result, table.output)
}
})
}
}
// text testers
type testTextTable struct {
inputLang language.Tag
inputCount int
inputStrings []string
outputString string
outputLang language.Tag
}
func testText(t *testing.T, tid int, translate func() *LocalizedString, table testTextTable) {
t.Helper()
result := translate()
testTextCheckResults(t, tid, result, table)
}
func testTextWithCount(t *testing.T, tid int, translate func(int) *LocalizedString, table testTextTable) {
t.Helper()
result := translate(table.inputCount)
testTextCheckResults(t, tid, result, table)
}
func testTextWith1String(t *testing.T, tid int, translate func(string) *LocalizedString, table testTextTable) {
t.Helper()
result := translate(table.inputStrings[0])
testTextCheckResults(t, tid, result, table)
}
func testTextCheckResults(t *testing.T, tid int, result *LocalizedString, table testTextTable) {
t.Helper()
if result.String() != table.outputString {
t.Errorf(testGotInvalidTranslation, tid, table.inputLang, result.String(), table.outputString)
}
if result.Language() != table.outputLang {
t.Errorf(testGotInvalidLanguage, tid, table.inputLang, result.Language(), table.outputLang)
}
}

View File

@ -0,0 +1,26 @@
package language
import "testing"
func TestNewLocalizer(t *testing.T) {
langMod, _ := New()
localizer, err := langMod.NewLocalizer()
if err != nil {
t.Errorf("can't get new language module: %s", err.Error())
return
}
if localizer == nil {
t.Errorf("localizer module is nil")
return
}
if localizer.localizer == nil {
t.Errorf("localizer module's localizer is nil")
return
}
}

View File

@ -1,8 +1,6 @@
package language package language
import ( import "github.com/feditools/relay/internal/log"
"github.com/feditools/go-lib/log"
)
type empty struct{} type empty struct{}

111
internal/language/text_a.go Normal file
View File

@ -0,0 +1,111 @@
package language
import "github.com/nicksnyder/go-i18n/v2/i18n"
// TextAccount returns a translated phrase.
func (l *Localizer) TextAccount(count int) *LocalizedString {
lg := logger.WithField("func", "TextAccount")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "Account",
One: "Account",
Other: "Accounts",
},
PluralCount: count,
})
if err != nil {
lg.Warningf(missingTranslationWarning, err.Error())
}
return &LocalizedString{
language: tag,
string: text,
}
}
// TextActivityLog returns a translated phrase.
func (l *Localizer) TextActivityLog(count int) *LocalizedString {
lg := logger.WithField("func", "TextActivityLog")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "ActivityLog",
One: "Activity Log",
Other: "Activity Logs",
},
PluralCount: count,
})
if err != nil {
lg.Warningf(missingTranslationWarning, err.Error())
}
return &LocalizedString{
language: tag,
string: text,
}
}
// TextActorURI returns a translated phrase.
func (l *Localizer) TextActorURI(count int) *LocalizedString {
lg := logger.WithField("func", "TextActorURI")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "ActorURI",
One: "Actor URI",
Other: "Actor URIs",
},
PluralCount: count,
})
if err != nil {
lg.Warningf(missingTranslationWarning, err.Error())
}
return &LocalizedString{
language: tag,
string: text,
}
}
// TextAddBlock returns a translated phrase.
func (l *Localizer) TextAddBlock(count int) *LocalizedString {
lg := logger.WithField("func", "TextAddBlock")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "AddBlock",
One: "Add Block",
Other: "Add Blocks",
},
PluralCount: count,
})
if err != nil {
lg.Warningf(missingTranslationWarning, err.Error())
}
return &LocalizedString{
language: tag,
string: text,
}
}
// TextAdmin returns a translated phrase.
func (l *Localizer) TextAdmin() *LocalizedString {
lg := logger.WithField("func", "TextAdmin")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "Admin",
Other: "Admin",
},
})
if err != nil {
lg.Warningf(missingTranslationWarning, err.Error())
}
return &LocalizedString{
language: tag,
string: text,
}
}

View File

@ -0,0 +1,196 @@
package language
import (
"fmt"
"testing"
"golang.org/x/text/language"
)
func TestLocalizer_TextAccount(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Account",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Accounts",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextAccount, table)
})
}
}
func TestLocalizer_TextActivityLog(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Activity Log",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Activity Logs",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextActivityLog, table)
})
}
}
func TestLocalizer_TextActorURI(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Actor URI",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Actor URIs",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextActorURI, table)
})
}
}
func TestLocalizer_TextAddBlock(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Add Block",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Add Blocks",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextAddBlock, table)
})
}
}
func TestLocalizer_TextAdmin(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "Admin",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextAdmin, table)
})
}
}

View File

@ -24,13 +24,13 @@ func (l *Localizer) TextBlock(count int) *LocalizedString {
} }
} }
// TextBlockExists returns a translated phrase. // TextBlockExistsDomain returns a translated phrase.
func (l *Localizer) TextBlockExists(domain string) *LocalizedString { func (l *Localizer) TextBlockExistsDomain(domain string) *LocalizedString {
lg := logger.WithField("func", "TextBlockExists") lg := logger.WithField("func", "TextBlockExistsDomain")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{ text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{ DefaultMessage: &i18n.Message{
ID: "BlockExists", ID: "BlockExistsDomain",
Other: "Block for domain {{.Domain}} already exists.", Other: "Block for domain {{.Domain}} already exists.",
}, },
TemplateData: map[string]interface{}{ TemplateData: map[string]interface{}{

View File

@ -0,0 +1,195 @@
package language
import (
"fmt"
"golang.org/x/text/language"
"testing"
)
func TestLocalizer_TextBlock(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Block",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Blocks",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextBlock, table)
})
}
}
func TestLocalizer_TextBlockExistsDomain(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputStrings: []string{"example.com"},
outputString: "Block for domain example.com already exists.",
outputLang: language.English,
},
{
inputLang: language.English,
inputStrings: []string{"example2.com"},
outputString: "Block for domain example2.com already exists.",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWith1String(t, i, localizer.TextBlockExistsDomain, table)
})
}
}
func TestLocalizer_TextBlockSubdomain(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Block Subdomain",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Block Subdomains",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextBlockSubdomain, table)
})
}
}
func TestLocalizer_TextBlocked(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "Blocked",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextBlocked, table)
})
}
}
func TestLocalizer_TextBlockedDomain(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Blocked Domain",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Blocked Domains",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextBlockedDomain, table)
})
}
}

View File

@ -2,35 +2,15 @@ package language
import "github.com/nicksnyder/go-i18n/v2/i18n" import "github.com/nicksnyder/go-i18n/v2/i18n"
// TextOauth returns a translated phrase. // TextChatID returns a translated phrase.
func (l *Localizer) TextOauth() *LocalizedString { func (l *Localizer) TextChatID(count int) *LocalizedString {
lg := logger.WithField("func", "TextOauth") lg := logger.WithField("func", "TextChatID")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{ text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{ DefaultMessage: &i18n.Message{
ID: "Oauth", ID: "ChatID",
Other: "OAuth", One: "Chat ID",
}, Other: "Chat IDs",
})
if err != nil {
lg.Warningf(missingTranslationWarning, err.Error())
}
return &LocalizedString{
language: tag,
string: text,
}
}
// TextOauth20Client returns a translated phrase.
func (l *Localizer) TextOauth20Client(count int) *LocalizedString {
lg := logger.WithField("func", "TextOauth20Client")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "Oauth20Client",
One: "OAuth 2.0 Client",
Other: "OAuth 2.0 Clients",
}, },
PluralCount: count, PluralCount: count,
}) })
@ -44,14 +24,14 @@ func (l *Localizer) TextOauth20Client(count int) *LocalizedString {
} }
} }
// TextOauth20Settings returns a translated phrase. // TextClose returns a translated phrase.
func (l *Localizer) TextOauth20Settings() *LocalizedString { func (l *Localizer) TextClose() *LocalizedString {
lg := logger.WithField("func", "TextOauth20Settings") lg := logger.WithField("func", "TextClose")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{ text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{ DefaultMessage: &i18n.Message{
ID: "Oauth20Settings", ID: "Close",
Other: "OAuth 2.0 Settings", Other: "Close",
}, },
}) })
if err != nil { if err != nil {
@ -64,15 +44,15 @@ func (l *Localizer) TextOauth20Settings() *LocalizedString {
} }
} }
// TextObfuscatedDomain returns a translated phrase. // TextConfig returns a translated phrase.
func (l *Localizer) TextObfuscatedDomain(count int) *LocalizedString { func (l *Localizer) TextConfig(count int) *LocalizedString {
lg := logger.WithField("func", "TextObfuscatedDomain") lg := logger.WithField("func", "TextConfig")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{ text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{ DefaultMessage: &i18n.Message{
ID: "ObfuscatedDomain", ID: "Config",
One: "Obfuscated Domain", One: "Config",
Other: "Obfuscated Domains", Other: "Configs",
}, },
PluralCount: count, PluralCount: count,
}) })
@ -85,3 +65,23 @@ func (l *Localizer) TextObfuscatedDomain(count int) *LocalizedString {
string: text, string: text,
} }
} }
// TextCreate returns a translated phrase.
func (l *Localizer) TextCreate() *LocalizedString {
lg := logger.WithField("func", "TextCreate")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "Create",
Other: "Create",
},
})
if err != nil {
lg.Warningf(missingTranslationWarning, err.Error())
}
return &LocalizedString{
language: tag,
string: text,
}
}

View File

@ -0,0 +1,150 @@
package language
import (
"fmt"
"testing"
"golang.org/x/text/language"
)
func TestLocalizer_TextChatID(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Chat ID",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Chat IDs",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextChatID, table)
})
}
}
func TestLocalizer_TextClose(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "Close",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextClose, table)
})
}
}
func TestLocalizer_TextConfig(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Config",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Configs",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextConfig, table)
})
}
}
func TestLocalizer_TextCreate(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "Create",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextCreate, table)
})
}
}

View File

@ -2,28 +2,6 @@ package language
import "github.com/nicksnyder/go-i18n/v2/i18n" import "github.com/nicksnyder/go-i18n/v2/i18n"
// TextDashboard returns a translated phrase.
func (l *Localizer) TextDashboard(count int) *LocalizedString {
lg := logger.WithField("func", "TextDashboard")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "Dashboard",
One: "Dashboard",
Other: "Dashboards",
},
PluralCount: count,
})
if err != nil {
lg.Warningf(missingTranslationWarning, err.Error())
}
return &LocalizedString{
language: tag,
string: text,
}
}
// TextDelete returns a translated phrase. // TextDelete returns a translated phrase.
func (l *Localizer) TextDelete() *LocalizedString { func (l *Localizer) TextDelete() *LocalizedString {
lg := logger.WithField("func", "TextDelete") lg := logger.WithField("func", "TextDelete")
@ -90,18 +68,18 @@ func (l *Localizer) TextDeleteBlockConfirmDomain(domain string) *LocalizedString
} }
} }
// TextDemocrablock returns a translated phrase. // TextDeliveryStat returns a translated phrase.
func (l *Localizer) TextDemocrablock() *LocalizedString { func (l *Localizer) TextDeliveryStat(count int) *LocalizedString {
lg := logger.WithField("func", "TextDemocrablock")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{ text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{ DefaultMessage: &i18n.Message{
ID: "Democrablock", ID: "DeliveryStat",
Other: "Democrablock", One: "Delivery Stat",
Other: "Delivery Stats",
}, },
PluralCount: count,
}) })
if err != nil { if err != nil {
lg.Warningf(missingTranslationWarning, err.Error()) logger.WithField("func", "TextDeliveryStat").Warningf(missingTranslationWarning, err.Error())
} }
return &LocalizedString{ return &LocalizedString{

View File

@ -0,0 +1,235 @@
package language
import (
"fmt"
"testing"
"golang.org/x/text/language"
)
func TestLocalizer_TextDelete(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "Delete",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextDelete, table)
})
}
}
func TestLocalizer_TextDeleteBlockDomain(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputStrings: []string{"example.com"},
outputString: "Delete Block example.com",
outputLang: language.English,
},
{
inputLang: language.English,
inputStrings: []string{"example2.com"},
outputString: "Delete Block example2.com",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWith1String(t, i, localizer.TextDeleteBlockDomain, table)
})
}
}
func TestLocalizer_TextDeleteBlockConfirmDomain(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputStrings: []string{"example.com"},
outputString: "Are you sure you want to delete the block for example.com?",
outputLang: language.English,
},
{
inputLang: language.English,
inputStrings: []string{"example2.com"},
outputString: "Are you sure you want to delete the block for example2.com?",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWith1String(t, i, localizer.TextDeleteBlockConfirmDomain, table)
})
}
}
func TestLocalizer_TextDeliveryStat(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Delivery Stat",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Delivery Stats",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextDeliveryStat, table)
})
}
}
func TestLocalizer_TextDescription(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Description",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Descriptions",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextDescription, table)
})
}
}
func TestLocalizer_TextDomain(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Domain",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Domains",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextDomain, table)
})
}
}

View File

@ -0,0 +1,110 @@
package language
import (
"fmt"
"golang.org/x/text/language"
"testing"
)
func TestLocalizer_TextEditBlock(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputStrings: []string{"example.com"},
outputString: "Edit Block example.com",
outputLang: language.English,
},
{
inputLang: language.English,
inputStrings: []string{"example2.com"},
outputString: "Edit Block example2.com",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWith1String(t, i, localizer.TextEditBlockDomain, table)
})
}
}
func TestLocalizer_TextEnabled(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "Enabled",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextEnabled, table)
})
}
}
func TestLocalizer_TextErrorDatabase(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "database error",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextErrorDatabase, table)
})
}
}

View File

@ -2,14 +2,14 @@ package language
import "github.com/nicksnyder/go-i18n/v2/i18n" import "github.com/nicksnyder/go-i18n/v2/i18n"
// TextFediverse returns a translated phrase. // TextFederation returns a translated phrase.
func (l *Localizer) TextFediverse() *LocalizedString { func (l *Localizer) TextFederation() *LocalizedString {
lg := logger.WithField("func", "TextFediverse") lg := logger.WithField("func", "TextFederation")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{ text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{ DefaultMessage: &i18n.Message{
ID: "Fediverse", ID: "Federation",
Other: "Fediverse", Other: "Federation",
}, },
}) })
if err != nil { if err != nil {

View File

@ -0,0 +1,72 @@
package language
import (
"fmt"
"testing"
"golang.org/x/text/language"
)
func TestLocalizer_TextFederation(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "Federation",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextFederation, table)
})
}
}
func TestLocalizer_TextFollowing(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "Following",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextFollowing, table)
})
}
}

View File

@ -0,0 +1,39 @@
package language
import (
"fmt"
"golang.org/x/text/language"
"testing"
)
func TestLocalizer_TextGeneral(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "General",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextGeneral, table)
})
}
}

View File

@ -0,0 +1,104 @@
package language
import (
"fmt"
"testing"
"golang.org/x/text/language"
)
func TestLocalizer_TextHomeWeb(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "Home",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextHomeWeb, table)
})
}
}
func TestLocalizer_TextHomePageBody(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "Home Page Body",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextHomePageBody, table)
})
}
}
func TestLocalizer_TextHowToJoin(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "How to Join",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextHowToJoin, table)
})
}
}

View File

@ -44,6 +44,28 @@ func (l *Localizer) TextImportBlockList(count int) *LocalizedString {
} }
} }
// TextInboxURI returns a translated phrase.
func (l *Localizer) TextInboxURI(count int) *LocalizedString {
lg := logger.WithField("func", "TextInboxURI")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "InboxURI",
One: "Inbox URI",
Other: "Inbox URIs",
},
PluralCount: count,
})
if err != nil {
lg.Warningf(missingTranslationWarning, err.Error())
}
return &LocalizedString{
language: tag,
string: text,
}
}
// TextInstance returns a translated phrase. // TextInstance returns a translated phrase.
func (l *Localizer) TextInstance(count int) *LocalizedString { func (l *Localizer) TextInstance(count int) *LocalizedString {
lg := logger.WithField("func", "TextInstance") lg := logger.WithField("func", "TextInstance")
@ -65,25 +87,3 @@ func (l *Localizer) TextInstance(count int) *LocalizedString {
string: text, string: text,
} }
} }
// TextInvalidURI returns a translated phrase.
func (l *Localizer) TextInvalidURI(count int) *LocalizedString {
lg := logger.WithField("func", "TextInvalidURI")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "InvalidURI",
One: "Invalid URI",
Other: "Invalid URIs",
},
PluralCount: count,
})
if err != nil {
lg.Warningf(missingTranslationWarning, err.Error())
}
return &LocalizedString{
language: tag,
string: text,
}
}

View File

@ -0,0 +1,157 @@
package language
import (
"fmt"
"testing"
"golang.org/x/text/language"
)
func TestLocalizer_TextImport(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "Import",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextImport, table)
})
}
}
func TestLocalizer_TextImportBlockList(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Import Block List",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Import Block Lists",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextImportBlockList, table)
})
}
}
func TestLocalizer_TextInboxURI(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Inbox URI",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Inbox URIs",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextInboxURI, table)
})
}
}
func TestLocalizer_TextInstance(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Instance",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Instances",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextInstance, table)
})
}
}

View File

@ -2,28 +2,6 @@ package language
import "github.com/nicksnyder/go-i18n/v2/i18n" import "github.com/nicksnyder/go-i18n/v2/i18n"
// TextList returns a translated phrase.
func (l *Localizer) TextList(count int) *LocalizedString {
lg := logger.WithField("func", "TextList")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "List",
One: "List",
Other: "Lists",
},
PluralCount: count,
})
if err != nil {
lg.Warningf(missingTranslationWarning, err.Error())
}
return &LocalizedString{
language: tag,
string: text,
}
}
// TextLogin returns a translated phrase. // TextLogin returns a translated phrase.
func (l *Localizer) TextLogin() *LocalizedString { func (l *Localizer) TextLogin() *LocalizedString {
lg := logger.WithField("func", "TextLogin") lg := logger.WithField("func", "TextLogin")

View File

@ -0,0 +1,104 @@
package language
import (
"fmt"
"testing"
"golang.org/x/text/language"
)
func TestLocalizer_TextLogin(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "Login",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextLogin, table)
})
}
}
func TestLocalizer_TextLogout(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "Logout",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextLogout, table)
})
}
}
func TestLocalizer_TextLookGood(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "Looks Good!",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextLooksGood, table)
})
}
}

View File

@ -2,15 +2,17 @@ package language
import "github.com/nicksnyder/go-i18n/v2/i18n" import "github.com/nicksnyder/go-i18n/v2/i18n"
// TextModeration returns a translated phrase. // TextMetric returns a translated phrase.
func (l *Localizer) TextModeration() *LocalizedString { func (l *Localizer) TextMetric(count int) *LocalizedString {
lg := logger.WithField("func", "TextLogin") lg := logger.WithField("func", "TextMetric")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{ text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{ DefaultMessage: &i18n.Message{
ID: "Moderation", ID: "Metric",
Other: "Moderation", One: "Metric",
Other: "Metrics",
}, },
PluralCount: count,
}) })
if err != nil { if err != nil {
lg.Warningf(missingTranslationWarning, err.Error()) lg.Warningf(missingTranslationWarning, err.Error())

View File

@ -0,0 +1,85 @@
package language
import (
"fmt"
"golang.org/x/text/language"
"testing"
)
func TestLocalizer_TextMetric(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Metric",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Metrics",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextMetric, table)
})
}
}
func TestLocalizer_TextModerator(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Moderator",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Moderators",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextModerator, table)
})
}
}

View File

@ -0,0 +1,46 @@
package language
import (
"fmt"
"golang.org/x/text/language"
"testing"
)
func TestLocalizer_TextNotification(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Notification",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Notifications",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextNotification, table)
})
}
}

View File

@ -0,0 +1,41 @@
package language
import "github.com/nicksnyder/go-i18n/v2/i18n"
// TextOAuthConfigured returns a translated phrase.
func (l *Localizer) TextOAuthConfigured() *LocalizedString {
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "OAuthConfigured",
Other: "OAuth Configured",
},
})
if err != nil {
logger.WithField("func", "TextOAuthConfigured").Warningf(missingTranslationWarning, err.Error())
}
return &LocalizedString{
language: tag,
string: text,
}
}
// TextObfuscatedDomain returns a translated phrase.
func (l *Localizer) TextObfuscatedDomain(count int) *LocalizedString {
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "ObfuscatedDomain",
One: "Obfuscated Domain",
Other: "Obfuscated Domains",
},
PluralCount: count,
})
if err != nil {
logger.WithField("func", "TextObfuscatedDomain").Warningf(missingTranslationWarning, err.Error())
}
return &LocalizedString{
language: tag,
string: text,
}
}

View File

@ -0,0 +1,79 @@
package language
import (
"fmt"
"testing"
"golang.org/x/text/language"
)
func TestLocalizer_TextOAuthConfigured(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "OAuth Configured",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextOAuthConfigured, table)
})
}
}
func TestLocalizer_TextObfuscatedDomain(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Obfuscated Domain",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Obfuscated Domains",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextObfuscatedDomain, table)
})
}
}

View File

@ -2,6 +2,26 @@ package language
import "github.com/nicksnyder/go-i18n/v2/i18n" import "github.com/nicksnyder/go-i18n/v2/i18n"
// TextReceivedStat returns a translated phrase.
func (l *Localizer) TextReceivedStat(count int) *LocalizedString {
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "ReceivedStat",
One: "Received Stat",
Other: "Received Stats",
},
PluralCount: count,
})
if err != nil {
logger.WithField("func", "TextReceivedStat").Warningf(missingTranslationWarning, err.Error())
}
return &LocalizedString{
language: tag,
string: text,
}
}
// TextRelay returns a translated phrase. // TextRelay returns a translated phrase.
func (l *Localizer) TextRelay(count int) *LocalizedString { func (l *Localizer) TextRelay(count int) *LocalizedString {
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{ text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
@ -22,40 +42,18 @@ func (l *Localizer) TextRelay(count int) *LocalizedString {
} }
} }
// TextRequired returns a translated phrase. // TextRepo returns a translated phrase.
func (l *Localizer) TextRequired() *LocalizedString { func (l *Localizer) TextRepo(count int) *LocalizedString {
lg := logger.WithField("func", "TextRequired")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{ text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{ DefaultMessage: &i18n.Message{
ID: "Required", ID: "Repo",
Other: "Required", One: "Repo",
}, Other: "Repos",
})
if err != nil {
lg.Warningf(missingTranslationWarning, err.Error())
}
return &LocalizedString{
language: tag,
string: text,
}
}
// TextRedirectURI returns a translated phrase.
func (l *Localizer) TextRedirectURI(count int) *LocalizedString {
lg := logger.WithField("func", "TextRedirectURI")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "RedirectURI",
One: "Redirect URI",
Other: "Redirect URIs",
}, },
PluralCount: count, PluralCount: count,
}) })
if err != nil { if err != nil {
lg.Warningf(missingTranslationWarning, err.Error()) logger.WithField("func", "TextRepo").Warningf(missingTranslationWarning, err.Error())
} }
return &LocalizedString{ return &LocalizedString{

View File

@ -0,0 +1,125 @@
package language
import (
"fmt"
"testing"
"golang.org/x/text/language"
)
func TestLocalizer_TextReceivedStat(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Received Stat",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Received Stats",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextReceivedStat, table)
})
}
}
func TestLocalizer_TextRelay(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Relay",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Relays",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextRelay, table)
})
}
}
func TestLocalizer_TextRepo(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Repo",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Repos",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextRepo, table)
})
}
}

View File

@ -22,6 +22,28 @@ func (l *Localizer) TextSave() *LocalizedString {
} }
} }
// TextServerHostname returns a translated phrase.
func (l *Localizer) TextServerHostname(count int) *LocalizedString {
lg := logger.WithField("func", "TextServerHostname")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "ServerHostname",
One: "Server Hostname",
Other: "Server Hostnames",
},
PluralCount: count,
})
if err != nil {
lg.Warningf(missingTranslationWarning, err.Error())
}
return &LocalizedString{
language: tag,
string: text,
}
}
// TextSetting returns a translated phrase. // TextSetting returns a translated phrase.
func (l *Localizer) TextSetting(count int) *LocalizedString { func (l *Localizer) TextSetting(count int) *LocalizedString {
lg := logger.WithField("func", "TextSetting") lg := logger.WithField("func", "TextSetting")
@ -44,17 +66,15 @@ func (l *Localizer) TextSetting(count int) *LocalizedString {
} }
} }
// TextSystem returns a translated phrase. // TextSoftware returns a translated phrase.
func (l *Localizer) TextSystem(count int) *LocalizedString { func (l *Localizer) TextSoftware() *LocalizedString {
lg := logger.WithField("func", "TextSystem") lg := logger.WithField("func", "TextSoftware")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{ text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{ DefaultMessage: &i18n.Message{
ID: "System", ID: "Software",
One: "System", Other: "Software",
Other: "Systems",
}, },
PluralCount: count,
}) })
if err != nil { if err != nil {
lg.Warningf(missingTranslationWarning, err.Error()) lg.Warningf(missingTranslationWarning, err.Error())

View File

@ -0,0 +1,150 @@
package language
import (
"fmt"
"testing"
"golang.org/x/text/language"
)
func TestLocalizer_TextSave(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "Save",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextSave, table)
})
}
}
func TestLocalizer_TextServerHostname(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Server Hostname",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Server Hostnames",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextServerHostname, table)
})
}
}
func TestLocalizer_TextSetting(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Setting",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Settings",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextSetting, table)
})
}
}
func TestLocalizer_TextSoftware(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "Software",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextSoftware, table)
})
}
}

View File

@ -0,0 +1,86 @@
package language
import (
"fmt"
"testing"
"golang.org/x/text/language"
)
func TestLocalizer_TextTimestamp(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Timestamp",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Timestamps",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextTimestamp, table)
})
}
}
func TestLocalizer_TextToken(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Token",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Tokens",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextToken, table)
})
}
}

View File

@ -43,25 +43,3 @@ func (l *Localizer) TextUpdate(count int) *LocalizedString {
string: text, string: text,
} }
} }
// TextUsername returns a translated phrase.
func (l *Localizer) TextUsername(count int) *LocalizedString {
lg := logger.WithField("func", "TextUsername")
text, tag, err := l.localizer.LocalizeWithTag(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: "Username",
One: "Username",
Other: "Usernames",
},
PluralCount: count,
})
if err != nil {
lg.Warningf(missingTranslationWarning, err.Error())
}
return &LocalizedString{
language: tag,
string: text,
}
}

View File

@ -0,0 +1,79 @@
package language
import (
"fmt"
"testing"
"golang.org/x/text/language"
)
func TestLocalizer_TextUnauthorized(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
outputString: "Unauthorized",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testText(t, i, localizer.TextUnauthorized, table)
})
}
}
func TestLocalizer_TextUpdate(t *testing.T) {
t.Parallel()
tables := []testTextTable{
{
inputLang: language.English,
inputCount: 1,
outputString: "Update",
outputLang: language.English,
},
{
inputLang: language.English,
inputCount: 2,
outputString: "Updates",
outputLang: language.English,
},
}
langMod, _ := New()
for i, table := range tables {
i := i
table := table
name := fmt.Sprintf(testTranslatedTo, i, table.inputLang)
t.Run(name, func(t *testing.T) {
t.Parallel()
localizer, err := langMod.NewLocalizer(table.inputLang.String())
if err != nil {
t.Errorf(testCantGetLocalizer, i, table.inputLang, err.Error())
return
}
testTextWithCount(t, i, localizer.TextUpdate, table)
})
}
}

View File

@ -25,6 +25,12 @@ type Logic interface {
GetLoginURL(ctx context.Context, instance *models.Instance) (*url.URL, error) GetLoginURL(ctx context.Context, instance *models.Instance) (*url.URL, error)
GetPeers(ctx context.Context) (*[]string, error) GetPeers(ctx context.Context) (*[]string, error)
IsDomainBlocked(ctx context.Context, d string) (bool, error) IsDomainBlocked(ctx context.Context, d string) (bool, error)
MetricsGetDeliverErrorWeek(ctx context.Context, instanceID int64) (*MetricsDataPointsTime, error)
MetricsGetDeliverSuccessWeek(ctx context.Context, instanceID int64) (*MetricsDataPointsTime, error)
MetricsGetReceivedWeek(ctx context.Context, instanceID int64) (*MetricsDataPointsTime, error)
MetricsIncDeliverError(ctx context.Context, instanceID int64)
MetricsIncDeliverSuccess(ctx context.Context, instanceID int64)
MetricsIncReceived(ctx context.Context, instanceID int64)
ProcessActivity(ctx context.Context, jid string, instanceID int64, actorIRI *url.URL, activity fedihelper.Activity) error ProcessActivity(ctx context.Context, jid string, instanceID int64, actorIRI *url.URL, activity fedihelper.Activity) error
ProcessBlockAdd(ctx context.Context, blockID int64) error ProcessBlockAdd(ctx context.Context, blockID int64) error
ProcessBlockDelete(ctx context.Context, blockID int64) error ProcessBlockDelete(ctx context.Context, blockID int64) error

View File

@ -47,11 +47,16 @@ func (l *Logic) DeliverActivity(ctx context.Context, jid string, instanceID int6
log.Debugf("sending activity: %s to %s", string(body), inboxIRI.String()) log.Debugf("sending activity: %s to %s", string(body), inboxIRI.String())
resp, err := l.transport.InstancePost(ctx, inboxIRI, body, libhttp.MimeAppActivityJSON, libhttp.MimeAppActivityJSON) resp, err := l.transport.InstancePost(ctx, inboxIRI, body, libhttp.MimeAppActivityJSON, libhttp.MimeAppActivityJSON)
if err != nil { if err != nil {
log.Errorf("can't post to instance: %s\n%s", err.Error(), resp) msg := fmt.Errorf("can't post to instance: %s\n%s", err.Error(), resp)
log.Error(msg.Error())
return fmt.Errorf("can't post to instance: %s\n%s", err.Error(), resp) l.MetricsIncDeliverError(ctx, instance.ID)
return msg
} }
l.MetricsIncDeliverSuccess(ctx, instance.ID)
return nil return nil
} }
@ -166,26 +171,9 @@ func (l *Logic) doFollow(ctx context.Context, jid string, instanceID int64, acti
// send accept // send accept
outgoingActivity := genActivityAccept(l.domain, instance.ActorIRI, id) outgoingActivity := genActivityAccept(l.domain, instance.ActorIRI, id)
body, err := json.Marshal(outgoingActivity) err = l.runner.EnqueueDeliverActivity(ctx, instance.ID, outgoingActivity)
if err != nil { if err != nil {
log.Errorf("can't marshal response: %s", err.Error()) log.Errorf("enqueueing delivery: %s", err.Error())
return fmt.Errorf("can't marshal response: %s", err.Error())
}
inboxIRI, err := url.Parse(instance.InboxIRI)
if err != nil {
log.Errorf("can't parse actor iri: %s", err.Error())
return fmt.Errorf("can't parse actor iri: %s", err.Error())
}
log.Debugf("sending activity: %s", string(body))
resp, err := l.transport.InstancePost(ctx, inboxIRI, body, libhttp.MimeAppActivityJSON, libhttp.MimeAppActivityJSON)
if err != nil {
log.Errorf("can't post to instance: %s\n%s", err.Error(), resp)
return fmt.Errorf("can't post to instance: %s\n%s", err.Error(), resp)
} }
go l.runner.EnqueueSendNotification( go l.runner.EnqueueSendNotification(
@ -391,26 +379,9 @@ func (l *Logic) doUndo(ctx context.Context, jid string, instanceID int64, activi
// send undo // send undo
outgoingActivity := genActivityUndo(l.domain, instance.ActorIRI) outgoingActivity := genActivityUndo(l.domain, instance.ActorIRI)
body, err := json.Marshal(outgoingActivity) err = l.runner.EnqueueDeliverActivity(ctx, instance.ID, outgoingActivity)
if err != nil { if err != nil {
log.Errorf("can't marshal response: %s", err.Error()) log.Errorf("enqueueing delivery: %s", err.Error())
return fmt.Errorf("can't marshal response: %s", err.Error())
}
inboxIRI, err := url.Parse(instance.InboxIRI)
if err != nil {
log.Errorf("can't parse actor iri: %s", err.Error())
return fmt.Errorf("can't parse actor iri: %s", err.Error())
}
log.Debugf("sending activity: %s", string(body))
resp, err := l.transport.InstancePost(ctx, inboxIRI, body, libhttp.MimeAppActivityJSON, libhttp.MimeAppActivityJSON)
if err != nil {
log.Errorf("can't post to instance: %s\n%s", err.Error(), resp)
return fmt.Errorf("can't post to instance: %s\n%s", err.Error(), resp)
} }
go l.runner.EnqueueSendNotification( go l.runner.EnqueueSendNotification(

View File

@ -7,6 +7,7 @@ import (
"github.com/feditools/relay/internal/db" "github.com/feditools/relay/internal/db"
"github.com/feditools/relay/internal/fedi" "github.com/feditools/relay/internal/fedi"
ihttp "github.com/feditools/relay/internal/http" ihttp "github.com/feditools/relay/internal/http"
"github.com/feditools/relay/internal/kv"
"github.com/feditools/relay/internal/notification" "github.com/feditools/relay/internal/notification"
"github.com/feditools/relay/internal/path" "github.com/feditools/relay/internal/path"
"github.com/feditools/relay/internal/runner" "github.com/feditools/relay/internal/runner"
@ -25,6 +26,7 @@ import (
type Logic struct { type Logic struct {
db db.DB db db.DB
fedi *fedi.Module fedi *fedi.Module
kv kv.KV
notifier notification.Notifier notifier notification.Notifier
runner runner.Runner runner runner.Runner
tokz *token.Tokenizer tokz *token.Tokenizer
@ -48,7 +50,7 @@ type Logic struct {
} }
// New created a new logic module // New created a new logic module
func New(ctx context.Context, c pub.Clock, d db.DB, h *ihttp.Client, tokz *token.Tokenizer) (*Logic, error) { func New(ctx context.Context, c pub.Clock, d db.DB, h *ihttp.Client, k kv.KV, tokz *token.Tokenizer) (*Logic, error) {
log := logger.WithFields(logrus.Fields{ log := logger.WithFields(logrus.Fields{
"func": "New", "func": "New",
}) })
@ -56,6 +58,7 @@ func New(ctx context.Context, c pub.Clock, d db.DB, h *ihttp.Client, tokz *token
// create module // create module
l := Logic{ l := Logic{
db: d, db: d,
kv: k,
tokz: tokz, tokz: tokz,
domain: viper.GetString(config.Keys.ServerExternalHostname), domain: viper.GetString(config.Keys.ServerExternalHostname),

View File

@ -0,0 +1,81 @@
package logic1
import (
"context"
"errors"
"github.com/feditools/relay/internal/kv"
"github.com/feditools/relay/internal/logic"
"time"
)
func (l *Logic) MetricsGetDeliverErrorWeek(ctx context.Context, instanceID int64) (*logic.MetricsDataPointsTime, error) {
now := time.Now().UTC()
now = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC)
var days logic.MetricsDataPointsTime = make([]logic.MetricsDataPointTime, 7)
for i := 0; i < 7; i++ {
day := now.Add(-24 * time.Duration(i) * time.Hour)
count := 0
var err error
count, err = l.kv.GetMetricsDeliverError(ctx, instanceID, day)
if err != nil && !errors.Is(err, kv.ErrNil) {
return nil, err
}
days[i] = logic.MetricsDataPointTime{
X: day,
Y: count,
}
}
return &days, nil
}
func (l *Logic) MetricsGetDeliverSuccessWeek(ctx context.Context, instanceID int64) (*logic.MetricsDataPointsTime, error) {
now := time.Now().UTC()
now = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC)
var days logic.MetricsDataPointsTime = make([]logic.MetricsDataPointTime, 7)
for i := 0; i < 7; i++ {
day := now.Add(-24 * time.Duration(i) * time.Hour)
count := 0
var err error
count, err = l.kv.GetMetricsDeliverSuccess(ctx, instanceID, day)
if err != nil && !errors.Is(err, kv.ErrNil) {
return nil, err
}
days[i] = logic.MetricsDataPointTime{
X: day,
Y: count,
}
}
return &days, nil
}
func (l *Logic) MetricsGetReceivedWeek(ctx context.Context, instanceID int64) (*logic.MetricsDataPointsTime, error) {
now := time.Now().UTC()
now = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC)
var days logic.MetricsDataPointsTime = make([]logic.MetricsDataPointTime, 7)
for i := 0; i < 7; i++ {
day := now.Add(-24 * time.Duration(i) * time.Hour)
count := 0
var err error
count, err = l.kv.GetMetricsReceived(ctx, instanceID, day)
if err != nil && !errors.Is(err, kv.ErrNil) {
return nil, err
}
days[i] = logic.MetricsDataPointTime{
X: day,
Y: count,
}
}
return &days, nil
}

View File

@ -0,0 +1,54 @@
package logic1
import (
"context"
"time"
)
func (l *Logic) MetricsIncDeliverError(ctx context.Context, instanceID int64) {
log := logger.WithField("func", "MetricsIncDeliverError")
now := time.Now().UTC()
err := l.kv.IncMetricsDeliverError(ctx, instanceID, now)
if err != nil {
log.Warnf("instance: %s", err.Error())
}
err = l.kv.IncMetricsDeliverErrorTotal(ctx, now)
if err != nil {
log.Warnf("total: %s", err.Error())
}
}
func (l *Logic) MetricsIncDeliverSuccess(ctx context.Context, instanceID int64) {
log := logger.WithField("func", "MetricsIncDeliverSuccess")
now := time.Now().UTC()
err := l.kv.IncMetricsDeliverSuccess(ctx, instanceID, now)
if err != nil {
log.Warnf("instance: %s", err.Error())
}
err = l.kv.IncMetricsDeliverSuccessTotal(ctx, now)
if err != nil {
log.Warnf("total: %s", err.Error())
}
}
func (l *Logic) MetricsIncReceived(ctx context.Context, instanceID int64) {
log := logger.WithField("func", "MetricsIncReceived")
now := time.Now().UTC()
err := l.kv.IncMetricsReceived(ctx, instanceID, now)
if err != nil {
log.Warnf("instance: %s", err.Error())
}
err = l.kv.IncMetricsReceivedTotal(ctx, now)
if err != nil {
log.Warnf("total: %s", err.Error())
}
}

10
internal/logic/metrics.go Normal file
View File

@ -0,0 +1,10 @@
package logic
import "time"
type MetricsDataPointsTime []MetricsDataPointTime
type MetricsDataPointTime struct {
X time.Time
Y int
}

View File

@ -27,6 +27,11 @@ func GenAppAdminHomePath() string {
return AppAdminHome return AppAdminHome
} }
// GenAppAdminInstanceViewPath returns a path for the admin instance view page.
func GenAppAdminInstanceViewPath(t string) string {
return AppAdminPreInstanceView + t
}
// GenAppHomePath returns a path for the home page. // GenAppHomePath returns a path for the home page.
func GenAppHomePath() string { func GenAppHomePath() string {
return AppHome return AppHome

View File

@ -69,4 +69,8 @@ const (
AppAdminInstance = AppAdmin + AppAdminSubInstance AppAdminInstance = AppAdmin + AppAdminSubInstance
// AppAdminSubInstance is the sub path for the admin instances page. // AppAdminSubInstance is the sub path for the admin instances page.
AppAdminSubInstance = "/" + PartInstance AppAdminSubInstance = "/" + PartInstance
// AppAdminPreInstanceView is the prefix path for the admin instance view page.
AppAdminPreInstanceView = AppAdmin + AppAdminSubInstance + "/"
// AppAdminSubInstanceView is the sub path for the admin instance view page.
AppAdminSubInstanceView = AppAdminSubInstance + "/" + VarInstance
) )

38
test/fake_metrics.sh Executable file
View File

@ -0,0 +1,38 @@
#!/bin/bash -x
METRIC_MAX=500
KEY_DELIVERY_S="relay:metrics:deliver:s:"
KEY_DELIVERY_S_TOTAL="relay:metrics:deliver:st:"
KEY_DELIVERY_E="relay:metrics:deliver:e:"
KEY_DELIVERY_E_TOTAL="relay:metrics:deliver:et:"
KEY_RECEIVED="relay:metrics:received:"
KEY_RECEIVED_TOTAL="relay:metrics:receivedt:"
for day in {0..30}; do
#echo $day
DATE1=$(date -v -${day}d +"%Y:%m:%d")
DELIVERY_S_TOTAL=0
DELIVERY_E_TOTAL=0
RECEIVED_TOTAL=0
for instance in {2..3}; do
DELIVERY_S=$((1 + $RANDOM % ${METRIC_MAX}))
DELIVERY_E=$((1 + $RANDOM % ${METRIC_MAX}))
RECEIVED=$((1 + $RANDOM % ${METRIC_MAX}))
DELIVERY_S_TOTAL=$(expr ${DELIVERY_S_TOTAL} + ${DELIVERY_S})
DELIVERY_E_TOTAL=$(expr ${DELIVERY_E_TOTAL} + ${DELIVERY_E})
RECEIVED_TOTAL=$(expr ${RECEIVED_TOTAL} + ${RECEIVED})
docker exec -it relay_redis_1 redis-cli --pass test HSET ${KEY_DELIVERY_S}${DATE1} ${instance} ${DELIVERY_S}
docker exec -it relay_redis_1 redis-cli --pass test HSET ${KEY_DELIVERY_E}${DATE1} ${instance} ${DELIVERY_E}
docker exec -it relay_redis_1 redis-cli --pass test HSET ${KEY_RECEIVED}${DATE1} ${instance} ${RECEIVED}
done
docker exec -it relay_redis_1 redis-cli --pass test SET ${KEY_DELIVERY_S_TOTAL}${DATE1} ${DELIVERY_S_TOTAL}
docker exec -it relay_redis_1 redis-cli --pass test SET ${KEY_DELIVERY_E_TOTAL}${DATE1} ${DELIVERY_E_TOTAL}
docker exec -it relay_redis_1 redis-cli --pass test SET ${KEY_RECEIVED_TOTAL}${DATE1} ${RECEIVED_TOTAL}
done

View File

@ -1,6 +1,6 @@
pipeline { pipeline {
environment { environment {
BUILD_IMAGE = 'gobuild:1.17' BUILD_IMAGE = 'gobuild:1.18'
BUILD_ARGS = '-e GOCACHE=/gocache -e HOME=${WORKSPACE} -v /var/lib/jenkins/gocache:/gocache -v /var/lib/jenkins/go/pkg:/go/pkg' BUILD_ARGS = '-e GOCACHE=/gocache -e HOME=${WORKSPACE} -v /var/lib/jenkins/gocache:/gocache -v /var/lib/jenkins/go/pkg:/go/pkg'
PATH = '/go/bin:~/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin' PATH = '/go/bin:~/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin'
} }

View File

@ -1 +0,0 @@
---

View File

@ -1 +0,0 @@
---

View File

@ -1 +0,0 @@
---

View File

@ -1 +0,0 @@
---

View File

@ -1 +0,0 @@
---

View File

@ -1 +0,0 @@
---

View File

@ -1 +0,0 @@
---

View File

@ -1,39 +0,0 @@
---
Account:
one: Cuenta
other: Cuentas
AddOauth20Client:
one: Añadir Cliente OAuth 2.0
other: Añadir Clientes de OAuth 2.0
Client:
one: Cliente
other: Clientes
ClientID:
one: ID de Cliente
other: IDs de Cliente
ClientSecret:
one: Secreto de Cliente
other: Secretos del Cliente
Create: Crear
Dashboard:
one: Tablero
other: Tableros
Description:
one: Descripción
other: Descripciones
HomeWeb: Inicio
InvalidURI:
one: URI Inválidas
other: URIs Inválidas
Login: Ingresar
LooksGood: '¡Se ve bien!'
Oauth: OAuth
Oauth20Client:
one: Cliente OAuth 2.0
other: Clientes de OAuth 2.0
Oauth20Settings: Configuración de OAuth 2.0
RedirectURI:
one: Redirigir URI
other: Redirigir URIs
Required: Requerido
Unauthorized: No Autorizado

View File

@ -1 +0,0 @@
---

View File

@ -1 +0,0 @@
---

View File

@ -1 +0,0 @@
---

View File

@ -1 +0,0 @@
---

View File

@ -1 +0,0 @@
---

Some files were not shown because too many files have changed in this diff Show More