commit a5f121ca767cd9478b8da9c25316c0a939b5a23d Author: Daniel Thomas Date: Wed Aug 30 17:44:17 2023 +0200 feat: initial commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0977931 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = tab +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{yaml,yml,md,mdx}] +indent_style = space diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..1ba1f3b --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..4222c9b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,33 @@ +name: Release Tag + +on: + push: + tags: + - v*.*.* + workflow_dispatch: + +env: + GOFLAGS: -mod=readonly + GOPROXY: https://proxy.golang.org + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - run: git fetch --force --tags + + - uses: actions/setup-go@v4 + with: + check-latest: true + go-version-file: 'go.mod' + + - uses: goreleaser/goreleaser-action@v4 + with: + version: latest + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..3059464 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,34 @@ +name: Test Branch + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + merge_group: + branches: [ main ] + workflow_dispatch: + +env: + GOFLAGS: -mod=readonly + GOPROXY: https://proxy.golang.org + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-go@v4 + with: + check-latest: true + go-version-file: 'go.mod' + + - uses: golangci/golangci-lint-action@v3 + + - name: Run tests + run: go test -v ./... diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f14e605 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +.* +*.log +!.github/ +!.husky/ +!.editorconfig +!.gitignore +!.golangci.yml +!.goreleaser.yml +!.husky.yaml +/coverage.out +/coverage.html +/tmp/ diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..443058c --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,60 @@ +run: + timeout: 5m + +linters-settings: + importas: + alias: + - pkg: '^github.com\/foomo\/contentfulvalidation\/(.*\/)?([^\/]+)\/?$' + alias: '${2}x' # enforce `x` suffix + no-unaliased: true + +linters: + enable: + # Enabled by default linters: + - errcheck + - gosimple + - govet + - ineffassign + - staticcheck + + # Disabled by default linters: + - asciicheck # Simple linter to check that your code does not contain non-ASCII identifiers [fast: true, auto-fix: false] + - bidichk # Checks for dangerous unicode character sequences [fast: true, auto-fix: false] + - depguard # Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false] + - dupl # Tool for code clone detection [fast: true, auto-fix: false] + - forcetypeassert # finds forced type assertions [fast: true, auto-fix: false] + - gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false] + - goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false] + - gocritic # Provides diagnostics that check for bugs, performance and style issues. [fast: false, auto-fix: false] + - goimports # In addition to fixing imports, goimports also formats your code in the same style as gofmt. [fast: true, auto-fix: true] + - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. [fast: true, auto-fix: false] + - gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. [fast: true, auto-fix: false] + - goprintffuncname # Checks that printf-like functions are named with `f` at the end [fast: true, auto-fix: false] + - gosec # (gas): Inspects source code for security problems [fast: false, auto-fix: false] + - grouper # An analyzer to analyze expression groups. [fast: true, auto-fix: false] + - importas # Enforces consistent import aliases [fast: false, auto-fix: false] + - maintidx # maintidx measures the maintainability index of each function. [fast: true, auto-fix: false] + - makezero # Finds slice declarations with non-zero initial length [fast: false, auto-fix: false] + - misspell # Finds commonly misspelled English words in comments [fast: true, auto-fix: true] + - nakedret # Finds naked returns in functions greater than a specified function length [fast: true, auto-fix: false] + - nestif # Reports deeply nested if statements [fast: true, auto-fix: false] + - nilerr # Finds the code that returns nil even if it checks that the error is not nil. [fast: false, auto-fix: false] + - nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. [fast: false, auto-fix: false] + - noctx # noctx finds sending http request without context.Context [fast: false, auto-fix: false] + - nolintlint # Reports ill-formed or insufficient nolint directives [fast: true, auto-fix: false] + - nonamedreturns # Reports all named returns [fast: false, auto-fix: false] + - nosprintfhostport # Checks for misuse of Sprintf to construct a host with port in a URL. [fast: true, auto-fix: false] + - prealloc # Finds slice declarations that could potentially be pre-allocated [fast: true, auto-fix: false] + - predeclared # find code that shadows one of Go's predeclared identifiers [fast: true, auto-fix: false] + - promlinter # Check Prometheus metrics naming via promlint [fast: true, auto-fix: false] + - revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. [fast: false, auto-fix: false] + - tagliatelle # Checks the struct tags. [fast: true, auto-fix: false] + - testpackage # linter that makes you use a separate _test package [fast: true, auto-fix: false] + - thelper # thelper detects golang test helpers without t.Helper() call and checks the consistency of test helpers [fast: false, auto-fix: false] + - unconvert # Remove unnecessary type conversions [fast: false, auto-fix: false] + - unparam # Reports unused function parameters [fast: false, auto-fix: false] + - usestdlibvars # A linter that detect the possibility to use variables/constants from the Go standard library. [fast: true, auto-fix: false] + - wastedassign # wastedassign finds wasted assignment statements. [fast: false, auto-fix: false] + - whitespace # Tool for detection of leading and trailing whitespace [fast: true, auto-fix: true] + disable: + - unused diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..65027d3 --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,34 @@ +builds: + - skip: true + +changelog: + filters: + exclude: + - "^wip" + - "^test" + - "^docs" + - "^chore" + - "^style" + - "go mod tidy" + - "merge conflict" + - "Merge pull request" + - "Merge remote-tracking branch" + - "Merge branch" + groups: + - title: Features + regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$' + order: 0 + - title: Dependency updates + regexp: '^.*?(feat|fix)\(deps\)!?:.+$' + order: 100 + - title: "Bug fixes" + regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$' + order: 150 + - title: "Security" + regexp: '^.*?sec(\([[:word:]]+\))??!?:.+$' + order: 200 + - title: "Performace" + regexp: '^.*?perf(\([[:word:]]+\))??!?:.+$' + order: 250 + - title: Other + order: 999 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..34aabf4 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +info@bestbytes.de. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b024ab4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Foomo web framework + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..20be245 --- /dev/null +++ b/Makefile @@ -0,0 +1,91 @@ +.DEFAULT_GOAL:=help +-include .makerc + +# --- Targets ----------------------------------------------------------------- + +# This allows us to accept extra arguments +%: .husky + @: + +.PHONY: .husky +# Configure git hooks for husky +.husky: + @if ! command -v husky &> /dev/null; then \ + echo "ERROR: missing executeable 'husky', please run:"; \ + echo "\n$ go install github.com/go-courier/husky/cmd/husky@latest\n"; \ + fi + @git config core.hooksPath .husky + +## === Tasks === + +.PHONY: doc +## Run tests +doc: + @open "http://localhost:6060/pkg/github.com/foomo/contentfulvalidation/" + @godoc -http=localhost:6060 -play + +.PHONY: test +## Run tests +test: + @set -euo pipefail && go test -json -v ./... | gotestfmt + +.PHONY: test.cover +## Run tests with coverage +test.cover: + @go test -v -coverprofile=coverage.out ./... + @go tool cover -func=coverage.out + @go tool cover -html=coverage.out + +.PHONY: lint +## Run linter +lint: + @golangci-lint run + +.PHONY: lint.fix +## Run linter and fix violations +lint.fix: + @golangci-lint run --fix + +.PHONY: tidy +## Run go mod tidy +tidy: + @go mod tidy + +.PHONY: outdated +## Show outdated direct dependencies +outdated: + @go list -u -m -json all | go-mod-outdated -update -direct + +## === Utils === + +## Show help text +help: + @awk '{ \ + if ($$0 ~ /^.PHONY: [a-zA-Z\-\_0-9]+$$/) { \ + helpCommand = substr($$0, index($$0, ":") + 2); \ + if (helpMessage) { \ + printf "\033[36m%-23s\033[0m %s\n", \ + helpCommand, helpMessage; \ + helpMessage = ""; \ + } \ + } else if ($$0 ~ /^[a-zA-Z\-\_0-9.]+:/) { \ + helpCommand = substr($$0, 0, index($$0, ":")); \ + if (helpMessage) { \ + printf "\033[36m%-23s\033[0m %s\n", \ + helpCommand, helpMessage"\n"; \ + helpMessage = ""; \ + } \ + } else if ($$0 ~ /^##/) { \ + if (helpMessage) { \ + helpMessage = helpMessage"\n "substr($$0, 3); \ + } else { \ + helpMessage = substr($$0, 3); \ + } \ + } else { \ + if (helpMessage) { \ + print "\n "helpMessage"\n" \ + } \ + helpMessage = ""; \ + } \ + }' \ + $(MAKEFILE_LIST) diff --git a/README.md b/README.md new file mode 100644 index 0000000..063157b --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# contentfulvalidation + +[![GoDoc](https://godoc.org/github.com/foomo/go?status.svg)](https://godoc.org/github.com/foomo/go) +[![Go Report Card](https://goreportcard.com/badge/github.com/foomo/contentfulvalidation)](https://goreportcard.com/report/github.com/foomo/contentfulvalidation) +[![Tests](https://github.com/foomo/contentfulvalidation/actions/workflows/test.yml/badge.svg)](https://github.com/foomo/contentfulvalidation/actions/workflows/test.yml) + +Framework to kickstart a entity validation service for contentful content models. + +## License + +Distributed under MIT License, please see license file within the code for more details. diff --git a/contants/constants.go b/contants/constants.go new file mode 100644 index 0000000..8f91b3f --- /dev/null +++ b/contants/constants.go @@ -0,0 +1,20 @@ +package contants + +import ( + "github.com/foomo/contentfulvalidation/validator" +) + +const ( + SeverityFatal validator.Severity = "fatal" + SeverityError validator.Severity = "error" + SeverityWarn validator.Severity = "warn" + SeverityInfo validator.Severity = "info" +) + +const ( + HealthError validator.Health = "error" + HealthWarn validator.Health = "warn" + HealthOk validator.Health = "ok" +) + +const DateFormat = "02 Jan 2006" diff --git a/errors/errors.go b/errors/errors.go new file mode 100644 index 0000000..c184c32 --- /dev/null +++ b/errors/errors.go @@ -0,0 +1,28 @@ +package errors + +const ( + ValidationErrorUnknownType ValidationError = "UNKNOWN_MODEL_TYPE" + ValidationErrorContentfulLoading ValidationError = "CONTENTFUL_LOADING_ERROR" +) + +var ( + ErrValidationErrorUnknownType = NewError(ValidationErrorUnknownType) + ErrValidationErrorContentfulLoading = NewError(ValidationErrorContentfulLoading) +) + +type ( + ValidationError string +) + +// ValidationError constructor +func NewError(e ValidationError) *ValidationError { + return &e +} + +// Error api method +func (e *ValidationError) Error() string { + if e != nil { + return string(*e) + } + return "" +} diff --git a/generate.go b/generate.go new file mode 100644 index 0000000..72d7f17 --- /dev/null +++ b/generate.go @@ -0,0 +1,3 @@ +package contentfulvalidation + +//go:generate gotsrpc gotsrpc.yml diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..083f967 --- /dev/null +++ b/go.mod @@ -0,0 +1,46 @@ +module github.com/foomo/contentfulvalidation + +go 1.20 + +require ( + github.com/bestbytes/catalogue v0.39.0 + github.com/foomo/contentful v0.4.4 + github.com/foomo/contentserver v1.10.2 + github.com/foomo/gotsrpc/v2 v2.7.2 + github.com/foomo/keel v0.16.1 + github.com/go-co-op/gocron v1.33.0 + github.com/pkg/errors v0.9.1 + go.uber.org/zap v1.25.0 +) + +require ( + github.com/RoaringBitmap/roaring v1.3.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.8.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.3.1 // indirect + github.com/iancoleman/strcase v0.2.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mschoch/smat v0.2.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + go.opentelemetry.io/otel v1.7.0 // indirect + go.opentelemetry.io/otel/trace v1.7.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/mod v0.11.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/tools v0.10.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + moul.io/http2curl v1.0.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..a4d1432 --- /dev/null +++ b/go.sum @@ -0,0 +1,141 @@ +github.com/RoaringBitmap/roaring v1.3.0 h1:aQmu9zQxDU0uhwR8SXOH/OrqEf+X8A0LQmwW3JX8Lcg= +github.com/RoaringBitmap/roaring v1.3.0/go.mod h1:plvDsJQpxOC5bw8LRteu/MLWHsHez/3y6cubLI4/1yE= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bestbytes/catalogue v0.39.0 h1:UYUhABB7ByGuxZNdr+9CqK4DZtmC9UIB37MpTJyDmAk= +github.com/bestbytes/catalogue v0.39.0/go.mod h1:eyZEeaZJSqc/r78jejYM+h8fkLnS1Kzd+IPzG0hxLH0= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c= +github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/foomo/contentful v0.4.4 h1:5/jWA8ep0cuVZvByY0Vq7G5PVGUs5GGtM6zrzy4T1kk= +github.com/foomo/contentful v0.4.4/go.mod h1:zBLj5DDYdLL6gZ0t1/lqYLtOUmkbCc8OylajvB8wXfE= +github.com/foomo/contentserver v1.10.2 h1:a0bWTAI/aVQjXM8otuDHY8mML+7rXTU4L6j7ratfycw= +github.com/foomo/contentserver v1.10.2/go.mod h1:ycAEPcUR+v6rL5rPinOanLeHLfcRuab28n/2JYMwSYg= +github.com/foomo/gotsrpc/v2 v2.7.2 h1:a94V/a8LSssq+aRN3Fv1lJPjWoyMilOvRq+yEaDTHVM= +github.com/foomo/gotsrpc/v2 v2.7.2/go.mod h1:n5SiKVNCZ7Tob6wcROWT5Sx1i/W42+ErpTbNqT3etM8= +github.com/foomo/keel v0.16.1 h1:nGPjprqqj96Nuu0LpiNjLSs+a2Cs/ad/px3hGKgE8iA= +github.com/foomo/keel v0.16.1/go.mod h1:ExaBRf52e3Z7vzuC+DTbs4i2NYVj49LIvaXFiV6+R84= +github.com/go-co-op/gocron v1.33.0 h1:lqQMwewbTIlh2/3l+1ieEjgseZ1AITe6YQQ5bCf0mhY= +github.com/go-co-op/gocron v1.33.0/go.mod h1:NLi+bkm4rRSy1F8U7iacZOz0xPseMoIOnvabGoSe/no= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= +github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= +github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= +github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= +github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM= +go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= +go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o= +go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= +go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= +golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8= +moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE= diff --git a/gotsrpc.yml b/gotsrpc.yml new file mode 100644 index 0000000..a76cf2d --- /dev/null +++ b/gotsrpc.yml @@ -0,0 +1,13 @@ +module: + name: github.com/foomo/contentfulvalidation + path: ./ + +targets: + services: + services: + /services/contenfulvalidation/validation: Validation + /services/contenfulvalidation/webhook: Webhook + package: github.com/foomo/contentfulvalidation/services + tsrpc: + - Validation + - Webhook diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000..d5978d8 --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,69 @@ +package utils + +import ( + "encoding/json" + catvo "github.com/bestbytes/catalogue/vo" + "github.com/foomo/contentfulvalidation/contants" + "github.com/foomo/contentfulvalidation/validator" + "time" + + "github.com/foomo/contentful" + "github.com/pkg/errors" +) + +func GetAssetImage(asset *contentful.AssetNoLocale) *contentful.FileImage { + if asset != nil && asset.Fields != nil && asset.Fields.File != nil && asset.Fields.File.Detail != nil && asset.Fields.File.Detail.Image != nil { + return asset.Fields.File.Detail.Image + } + return nil +} + +func GetAspectRatio(asset *contentful.AssetNoLocale) (float64, error) { + var aspectRatio float64 + image := GetAssetImage(asset) + if image == nil { + return aspectRatio, errors.New("No linked image available") + } + if image.Width == 0 || image.Height == 0 { + return aspectRatio, errors.New("Width or height are zero") + } + aspectRatio = float64(image.Width) / float64(image.Height) + return aspectRatio, nil +} + +func LoadQuery(rawQuery any) (*catvo.Query, error) { + query := &catvo.Query{} + errMarshal := loadInterfaceAsJSON(rawQuery, query) + if errMarshal != nil { + return nil, errMarshal + } + return query, nil +} +func loadInterfaceAsJSON(source interface{}, target interface{}) error { + jsonBytes, errMarshal := json.Marshal(source) + if errMarshal != nil { + return errMarshal + } + return json.Unmarshal(jsonBytes, &target) +} + +func ConvertTimeFormat(timeToFormat string, parseTemplate string, formatTemplate string) (string, error) { + p, err := time.Parse(parseTemplate, timeToFormat) + if err != nil { + return "", err + } + return p.Format(formatTemplate), nil +} + +func InitEmptyValidationResult(modelType validator.ModelType, modelID validator.ModelID, title string, internalTitle string, lastUpdatedDate string) *validator.ValidationResult { + var messages []*validator.ValidationResultMessage + return &validator.ValidationResult{ + ID: modelID, + Title: title, + InternalTitle: internalTitle, + LastUpdatedDate: lastUpdatedDate, + ModelType: modelType, + Health: contants.HealthOk, + Messages: messages, + } +} diff --git a/validation/validation.go b/validation/validation.go new file mode 100644 index 0000000..8a67daf --- /dev/null +++ b/validation/validation.go @@ -0,0 +1,13 @@ +package validation + +import ( + "github.com/foomo/contentfulvalidation/errors" + "github.com/foomo/contentfulvalidation/validator" +) + +type Validation interface { + ValidationResult(modelType validator.ModelType, modelID validator.ModelID) (validationResult *validator.ValidationResult, validationError *errors.ValidationError) + ValidationResults(modelType validator.ModelType) (validationResults map[validator.ModelID]*validator.ValidationResult, validationError *errors.ValidationError) + ValidateEntity(modelType validator.ModelType, modelID validator.ModelID, commit bool) (validationResult *validator.ValidationResult, validationError *errors.ValidationError) + ListModelTypes() (availableModelTypes []*validator.ModelTypeInfo) +} diff --git a/validations/asset.go b/validations/asset.go new file mode 100644 index 0000000..5d38f30 --- /dev/null +++ b/validations/asset.go @@ -0,0 +1,22 @@ +package validations + +import ( + "github.com/foomo/contentful" + "github.com/foomo/contentfulvalidation/utils" +) + +func IsLandscapeRatio(asset *contentful.AssetNoLocale) bool { + aspectRatio, err := utils.GetAspectRatio(asset) + if err != nil { + return false + } + return aspectRatio >= 1.00 +} + +func IsPortraitRatio(asset *contentful.AssetNoLocale) bool { + aspectRatio, err := utils.GetAspectRatio(asset) + if err != nil { + return false + } + return aspectRatio <= 1.00 +} diff --git a/validations/query.go b/validations/query.go new file mode 100644 index 0000000..ee9747e --- /dev/null +++ b/validations/query.go @@ -0,0 +1,66 @@ +package validations + +import ( + "fmt" + + catvo "github.com/bestbytes/catalogue/vo" +) + +func IsAttributeExpired(query *catvo.Query, attributes catvo.Attributes) bool { + + // TODO is this ok? + isValueExpired := func(value string, def catvo.AttributeDefinition) bool { + if _, ok := def.EnumStrings[catvo.AttributeValueID(value)]; !ok { + // thow error + // provide contex on value with this id .... + fmt.Println("Attribute NOT found: ", ok) + return true + + } else { + fmt.Println("Attribute found: ", ok) + return false + } + } + + areValuesExpired := func(values []string, def catvo.AttributeDefinition) bool { + expired := false + for _, v := range values { + if isValueExpired(v, def) { + expired = true + } + } + return expired + } + + for _, e := range query.Elements { + fmt.Println("THE Matcher: ", e.Matcher) + + // @TODO validate if there is even an attribute set, or is empty string + if e.Matcher != nil { + if def, ok := attributes[e.Matcher.Attribute]; ok { + switch { + case e.Matcher.StringIn != nil: + return areValuesExpired(e.Matcher.StringIn.Values, def) + case e.Matcher.StringAllIn != nil: + return areValuesExpired(e.Matcher.StringAllIn.Values, def) + case e.Matcher.StringNotIn != nil: + return areValuesExpired(e.Matcher.StringNotIn.Values, def) + case e.Matcher.StringEquals != nil: + return isValueExpired(e.Matcher.StringEquals.Value, def) + case e.Matcher.StringNotEquals != nil: + return isValueExpired(e.Matcher.StringNotEquals.Value, def) + } + } else { + // throw error + fmt.Println("NO attribute within ALL ATTRIBUTES: ") + // TODO uncomment once catalogue attr are in, maybe a different validation for this + // return true + } + + } else { + fmt.Println("MATCHER is NIL e.matcher: ") + return true + } + } + return false +} diff --git a/validator/attributeprovider.go b/validator/attributeprovider.go new file mode 100644 index 0000000..4cbff10 --- /dev/null +++ b/validator/attributeprovider.go @@ -0,0 +1,46 @@ +package validator + +import ( + "context" + "time" + + "github.com/bestbytes/catalogue/vo" + "github.com/foomo/keel/log" + "github.com/go-co-op/gocron" + "go.uber.org/zap" +) + +type AttributeProviderFunc func() vo.Attributes +type AttributeUpdateFunc func(ctx context.Context) vo.Attributes + +type AttributeProvider struct { + l *zap.Logger + ctx context.Context + attributes vo.Attributes + updateFunc AttributeUpdateFunc +} + +func NewAttributeProvider(ctx context.Context, l *zap.Logger, uf AttributeUpdateFunc) *AttributeProvider { + return &AttributeProvider{ + l: l, + ctx: ctx, + updateFunc: uf, + } +} + +func (ap *AttributeProvider) Init() error { + ap.attributes = ap.updateFunc(ap.ctx) + + // TODO: make configurable + s := gocron.NewScheduler(time.Local) + _, err := s.Every(1).Day().At("03:00").Do(func() { + ap.attributes = ap.updateFunc(ap.ctx) + }) + log.Must(ap.l, err, "failed to ...") + s.StartAsync() + return nil +} + +func (ap *AttributeProvider) GetAttributes() vo.Attributes { + return ap.attributes +} diff --git a/validator/cache.go b/validator/cache.go new file mode 100644 index 0000000..ae73a17 --- /dev/null +++ b/validator/cache.go @@ -0,0 +1,56 @@ +package validator + +import ( + "sync" + + "go.uber.org/zap" +) + +type Cache struct { + sync.RWMutex + l *zap.Logger + pool ValidationResults +} + +func NewCache(l *zap.Logger) (*Cache, error) { + logger := l.With(zap.String("routine", "contentfulvalidation-cache")) + c := &Cache{ + RWMutex: sync.RWMutex{}, + l: logger, + pool: map[ModelType]map[ModelID]*ValidationResult{}, + } + return c, nil +} + +func (c *Cache) Get(modelType ModelType, modelID ModelID) (*ValidationResult, bool) { + c.RLock() + defer c.RUnlock() + // check if the modelType is populated at all + _, typeMapExists := c.pool[modelType] + if !typeMapExists { + return nil, false + } + // check if the modelID has a validation result and return it + validationResult, ok := c.pool[modelType][modelID] + return validationResult, ok +} + +func (c *Cache) GetForType(modelType ModelType) map[ModelID]*ValidationResult { + c.RLock() + defer c.RUnlock() + results, typeMapExists := c.pool[modelType] + if !typeMapExists { + return nil + } + return results +} + +func (c *Cache) SetForType(modelType ModelType, results map[ModelID]*ValidationResult) { + c.Lock() + defer c.Unlock() + c.pool[modelType] = results +} + +func (c *Cache) GetPool() ValidationResults { + return c.pool +} diff --git a/validator/interfaces.go b/validator/interfaces.go new file mode 100644 index 0000000..24a9aa7 --- /dev/null +++ b/validator/interfaces.go @@ -0,0 +1,12 @@ +package validator + +import "github.com/foomo/contentfulvalidation/errors" + +type ModelValidator interface { + Validate(modelID ModelID) (*ValidationResult, *errors.ValidationError) + ValidateAll() (map[ModelID]*ValidationResult, error) +} + +type ValidatorProvider interface { + GetValidators() map[ModelType]ModelValidator +} diff --git a/validator/validationresult.go b/validator/validationresult.go new file mode 100644 index 0000000..6a5ae45 --- /dev/null +++ b/validator/validationresult.go @@ -0,0 +1,42 @@ +package validator + +import "github.com/foomo/contentfulvalidation/contants" + +type ValidationResult struct { + ID ModelID `json:"id"` + Title string `json:"title,omitempty"` + InternalTitle string `json:"internalTitle,omitempty"` + LastUpdatedDate string `json:"lastUpdatedDate,omitempty"` + ModelType ModelType `json:"modelType"` + Health Health `json:"health"` + Messages []*ValidationResultMessage `json:"messages"` +} + +type ValidationResultMessage struct { + Code MessageCode `json:"code"` + Message string `json:"message"` + Severity Severity `json:"severity"` +} + +func (result *ValidationResult) Log(severity Severity, message string, code MessageCode) { + msg := &ValidationResultMessage{ + Code: code, + Message: message, + Severity: severity, + } + result.Messages = append(result.Messages, msg) +} + +func (result *ValidationResult) UpdateHealth() { + if len(result.Messages) > 0 { + for _, msg := range result.Messages { + if msg.Severity == contants.SeverityError || msg.Severity == contants.SeverityFatal { + result.Health = contants.HealthError + return + } + if msg.Severity == contants.SeverityWarn { + result.Health = contants.HealthWarn + } + } + } +} diff --git a/validator/validator.go b/validator/validator.go new file mode 100644 index 0000000..0a48f90 --- /dev/null +++ b/validator/validator.go @@ -0,0 +1,161 @@ +package validator + +import ( + "github.com/foomo/contentfulvalidation/contants" + "github.com/foomo/contentfulvalidation/errors" + "github.com/foomo/contentfulvalidation/utils" + "strings" + "time" + + "github.com/foomo/contentserver/client" + keellog "github.com/foomo/keel/log" + "go.uber.org/zap" +) + +type Validator struct { + l *zap.Logger + csClient *client.Client + Validators map[ModelType]ModelValidator + Cache *Cache +} + +func NewValidator( + l *zap.Logger, + csClient *client.Client, + validatorProvider ValidatorProvider, +) (*Validator, error) { + logger := l.With(zap.String("routine", "contentfulvalidation-validator")) + cache, err := NewCache(l) + if err != nil { + return nil, err + } + + return &Validator{ + l: logger, + csClient: csClient, + Validators: validatorProvider.GetValidators(), + Cache: cache, + }, nil +} + +func (v *Validator) Get(modelType ModelType, modelID ModelID) ( + *ValidationResult, + *errors.ValidationError, +) { + result, ok := v.Cache.Get(modelType, modelID) + if !ok { + return nil, nil + } + return result, nil +} + +func (v *Validator) Validate(modelType ModelType, modelID ModelID) ( + *ValidationResult, + *errors.ValidationError, +) { + // select validator + validator, err := v.getValidatorByType(modelType) + if err != nil { + return nil, err + } + result, err := validator.Validate(modelID) + if err != nil { + return nil, err + } + return result, nil +} + +func (v *Validator) List(modelType ModelType) ( + map[ModelID]*ValidationResult, + error, +) { + v.l.Debug("getting all validation results for model type", keellog.FValue(modelType)) + results := v.Cache.GetForType(modelType) + v.l.Debug("got validation results", keellog.FValue(modelType), keellog.FValue(len(results))) + return results, nil +} + +func (v *Validator) MapListToCSV(modelType ModelType) ( + string, + error, +) { + r, err := v.List(modelType) + if err != nil { + return "", err + } + + csvString := "" + rows := []string{"ID;Link;Element;Internal Title;Last Updated;Status;Details"} + + for i := range r { + v, err := v.Get(modelType, i) + if err != nil { + return "", err + } + + if v.Health != contants.HealthOk { + date, err := utils.ConvertTimeFormat(v.LastUpdatedDate, time.RFC3339, contants.DateFormat) + if err != nil { + return "", err + } + idString := string(v.ID) + hyperLink := `=HYPERLINK("https://app.contentful.com/spaces/qfsyzz7ytbcy/entries/` + idString + `")` + details := []string{} + for _, m := range v.Messages { + details = append(details, m.Message) + } + cells := []string{ + idString, + hyperLink, + v.Title, + v.InternalTitle, + date, + string(v.Health), + strings.Join(details, " "), + } + rows = append(rows, strings.Join(cells, ";")) + csvString = strings.Join(rows, "\n") + } + } + + return csvString, nil +} + +func (v *Validator) ListModelTypes() []ModelType { + availableTypes := []ModelType{} + for modelType := range v.Cache.GetPool() { + availableTypes = append(availableTypes, modelType) + } + return availableTypes +} + +func (v *Validator) ValidateAll() error { + v.l.Debug("running validation on all model types") + for modelType, modelValidator := range v.Validators { + results, err := modelValidator.ValidateAll() + if err != nil { + keellog.WithError(v.l, err).Error("error on running validation", keellog.FValue(modelType)) + continue + } + v.l.Debug("successful validation run", keellog.FValue(modelType), keellog.FValue(len(results))) + // set the whole result to the cache + v.Cache.SetForType(modelType, results) + } + return nil +} + +func (v *Validator) Update() { + v.l.Debug("received update signal") + err := v.ValidateAll() + if err != nil { + keellog.WithError(v.l, err).Error("error on validate all update") + } +} + +func (v *Validator) getValidatorByType(modelType ModelType) (ModelValidator, *errors.ValidationError) { + validator, ok := v.Validators[modelType] + if !ok { + return nil, errors.ErrValidationErrorUnknownType + } + return validator, nil +} diff --git a/validator/vo.go b/validator/vo.go new file mode 100644 index 0000000..c9e7da0 --- /dev/null +++ b/validator/vo.go @@ -0,0 +1,15 @@ +package validator + +type SysType string +type ModelType string +type ModelID string +type MessageCode string +type Severity string +type Health string + +type ValidationResults map[ModelType]map[ModelID]*ValidationResult + +type ModelTypeInfo struct { + ModelType ModelType `json:"modelType"` + Title string `json:"title"` +} diff --git a/webhook/webhook.go b/webhook/webhook.go new file mode 100644 index 0000000..7e7580e --- /dev/null +++ b/webhook/webhook.go @@ -0,0 +1,9 @@ +package webhook + +import ( + "github.com/foomo/contentfulvalidation/validator" +) + +type Webhook interface { + UpdateCache(sysType validator.SysType, modelType validator.ModelType, modelID validator.ModelID) +}