diff --git a/.golangci.yml b/.golangci.yml index 92ff7fe..a184b86 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -50,7 +50,6 @@ linters: # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint disable-all: true enable: - - deadcode - depguard - dogsled - gochecknoinits @@ -64,7 +63,6 @@ linters: - ineffassign - nakedret - unconvert - - varcheck - whitespace - gocognit - prealloc @@ -82,12 +80,14 @@ issues: - gocritic - ineffassign - govet + - path: main.go + linters: - typecheck new: false # golangci.com configuration # https://github.com/golangci/golangci/wiki/Configuration service: - golangci-lint-version: 1.43.x # use the fixed version to not introduce new linters unexpectedly + golangci-lint-version: 1.52.x # use the fixed version to not introduce new linters unexpectedly prepare: - - echo "here I can run custom commands, but no preparation needed for this repo" \ No newline at end of file + - echo "here I can run custom commands, but no preparation needed for this repo" diff --git a/Makefile b/Makefile index c17db34..20cfa24 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ BINPATH ?= build BUILD_TIME=$(shell date +%s) GIT_COMMIT=$(shell git rev-parse HEAD) VERSION ?= $(shell git tag --points-at HEAD | grep ^v | head -n 1) -LOCAL_DP_RENDERER_IN_USE = $(shell grep -c "\"github.com/ONSdigital/dp-renderer\" =" go.mod) +LOCAL_DP_RENDERER_IN_USE = $(shell grep -c "\"github.com/ONSdigital/dp-renderer/v2\" =" go.mod) LDFLAGS = -ldflags "-X main.BuildTime=$(BUILD_TIME) -X main.GitCommit=$(GIT_COMMIT) -X main.Version=$(VERSION)" @@ -18,8 +18,9 @@ audit: build: generate-prod go build -tags 'production' $(LDFLAGS) -o $(BINPATH)/dp-frontend-feedback-controller +.PHONY: lint lint: - go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.45.2 + go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.52.2 golangci-lint run ./... .PHONY: debug @@ -58,8 +59,8 @@ generate-prod: fetch-renderer-lib .PHONY: fetch-dp-renderer fetch-renderer-lib: ifeq ($(LOCAL_DP_RENDERER_IN_USE), 1) - $(eval CORE_ASSETS_PATH = $(shell grep -w "\"github.com/ONSdigital/dp-renderer\" =>" go.mod | awk -F '=> ' '{print $$2}' | tr -d '"')) + $(eval CORE_ASSETS_PATH = $(shell grep -w "\"github.com/ONSdigital/dp-renderer/v2\" =>" go.mod | awk -F '=> ' '{print $$2}' | tr -d '"')) else - $(eval APP_RENDERER_VERSION=$(shell grep "github.com/ONSdigital/dp-renderer" go.mod | cut -d ' ' -f2 )) - $(eval CORE_ASSETS_PATH = $(shell go get github.com/ONSdigital/dp-renderer@$(APP_RENDERER_VERSION) && go list -f '{{.Dir}}' -m github.com/ONSdigital/dp-renderer)) + $(eval APP_RENDERER_VERSION=$(shell grep "github.com/ONSdigital/dp-renderer/v2" go.mod | cut -d ' ' -f2 )) + $(eval CORE_ASSETS_PATH = $(shell go get github.com/ONSdigital/dp-renderer/v2@$(APP_RENDERER_VERSION) && go list -f '{{.Dir}}' -m github.com/ONSdigital/dp-renderer/v2)) endif diff --git a/README.md b/README.md index 2a0234f..c5e57ed 100644 --- a/README.md +++ b/README.md @@ -4,32 +4,36 @@ To allow users to provide feedback about the ONS website ## Getting started -* Run `make debug` +- Run `make debug` ## Dependencies -* No further dependencies other than those defined in `go.mod` +- No further dependencies other than those defined in `go.mod` ## Configuration -| Environment variable | Default | Description -| ------------------------------ | --------- | ----------- -| BIND_ADDR | localhost:25200 | The host and port to bind to -| GRACEFUL_SHUTDOWN_TIMEOUT | 5s | The graceful shutdown timeout in seconds (`time.Duration` format) -| HEALTHCHECK_INTERVAL | 30s | Time between self-healthchecks (`time.Duration` format) -| HEALTHCHECK_CRITICAL_TIMEOUT | 90s | Time to wait until an unhealthy dependent propagates its state to make this app unhealthy (`time.Duration` format) -| RENDERER_URL | http://localhost:20010 | The URL of [dp-frontend-renderer](https://www.github.com/ONSdigital/dp-frontend-renderer). -| MAIL_HOST | "" | The host for the mail server. -| MAIL_PORT | "" | The port for the mail server. -| MAIL_USER | "" | A user on the mail server. -| MAIL_PASSWORD | "" | The password for the mail server user. -| FEEDBACK_TO | "" | Receiver email address for feedback. -| FEEDBACK_FROM | "" | Sender email address for feedback. -| DEBUG | false | Enable debug mode -| API_ROUTER_URL | http://localhost:23200/v1 | The URL of the [dp-api-router](https://github.com/ONSdigital/dp-api-router) -| SITE_DOMAIN | localhost | -| PATTERN_LIBRARY_ASSETS_PATH | "" | Pattern library location -| SUPPORTED_LANGUAGES | [2]string{"en", "cy"} | Supported languages +| Environment variable | Default | Description | +| ------------------------------ | --------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| API_ROUTER_URL | | The URL of the [dp-api-router](https://github.com/ONSdigital/dp-api-router) | +| BIND_ADDR | localhost:25200 | The host and port to bind to | +| CENSUS_TOPIC_ID | 4445 | The census topic id | +| DEBUG | false | Enable debug mode | +| ENABLE_CENSUS_TOPIC_SUBSECTION | false | Enable census topic subsection | +| ENABLE_NEW_NAVBAR | false | Enable new navigation bar | +| GRACEFUL_SHUTDOWN_TIMEOUT | 5s | The graceful shutdown timeout in seconds (`time.Duration` format) | +| HEALTHCHECK_INTERVAL | 30s | Time between self-healthchecks (`time.Duration` format) | +| HEALTHCHECK_CRITICAL_TIMEOUT | 90s | Time to wait until an unhealthy dependent propagates its state to make this app unhealthy (`time.Duration` format) | +| MAIL_HOST | "" | The host for the mail server. | +| MAIL_PORT | "" | The port for the mail server. | +| MAIL_USER | "" | A user on the mail server. | +| MAIL_PASSWORD | "" | The password for the mail server user. | +| FEEDBACK_TO | "" | Receiver email address for feedback. | +| FEEDBACK_FROM | "" | Sender email address for feedback. | +| IS_PUBLISHING_MODE | false | +| PATTERN_LIBRARY_ASSETS_PATH | "" | Pattern library location | +| SERVICE_AUTH_TOKEN | "" | Service authorisation token | +| SITE_DOMAIN | localhost | +| SUPPORTED_LANGUAGES | []string{"en", "cy"} | Supported languages | ## Contributing @@ -37,7 +41,6 @@ See [CONTRIBUTING](CONTRIBUTING.md) for details. ## License -Copyright © 2021, Office for National Statistics (https://www.ons.gov.uk) +Copyright © 2023, Office for National Statistics () Released under MIT license, see [LICENSE](LICENSE.md) for details. - diff --git a/assets/locales/service.cy.toml b/assets/locales/service.cy.toml index e8975d8..2626dd5 100644 --- a/assets/locales/service.cy.toml +++ b/assets/locales/service.cy.toml @@ -14,8 +14,8 @@ #------------------------------------------------------------------------------------------------------------------------------------------------------------------- [FeedbackTitle] -description = "{{.Metadata.Title}}" -one = "{{.arg0}}" +description = "Feedback" +one = "Feedback" [FeedbackDesc] descriptions = "You can use the form below to ask a question, report a problem or suggest an improvement we can make to ONS.GOV.UK" @@ -30,16 +30,20 @@ description = "This specific page" one = "This specific page" [FeedbackWhatEnterURL] -description = "Enter URL or name of the page" -one = "Enter URL or name of the page" +description = "Enter URL of the page" +one = "Enter URL of the page" -[FeedbackWhatOptHintSpecificPage] -description = "{{.ServiceDescription}}" -one = "({{.arg0}})" +[FeedbackValidURL] +description = "Enter a valid URL" +one = "Enter a valid URL" + +[FeedbackChooseType] +description = "Choose a type" +one = "Choose a type" [FeedbackWhatOptNewService] -description = "This new service" -one = "This new service" +description = "This {{.ServiceName}} service" +one = "The {{.arg0}} service" [FeedbackWhatOptGeneral] description = "The whole ONS website or general feedback" @@ -86,8 +90,8 @@ description = "Send feedback" one = "Send feedback" [FeedbackFinished] -description = "Your feedback will help us to improve the website. We are unable to respond to all enquiries. If your matter is urgent, please contact us.

Return to {{.Metadata.Description}}" -one = "Your feedback will help us to improve the website. We are unable to respond to all enquiries. If your matter is urgent, please contact us.

Return to {{.arg0}}" +description = "Your feedback will help us to improve the website. We are unable to respond to all enquiries. If your matter is urgent, please contact us." +one = "Your feedback will help us to improve the website. We are unable to respond to all enquiries. If your matter is urgent, please contact us." [FeedbackWholeWebsite] description = "The whole ONS website or general feedback" diff --git a/assets/locales/service.en.toml b/assets/locales/service.en.toml index f9f5d5f..760312b 100644 --- a/assets/locales/service.en.toml +++ b/assets/locales/service.en.toml @@ -14,8 +14,8 @@ #------------------------------------------------------------------------------------------------------------------------------------------------------------------- [FeedbackTitle] -description = "{{.Metadata.Title}}" -one = "{{.arg0}}" +description = "Feedback" +one = "Feedback" [FeedbackDesc] descriptions = "You can use the form below to ask a question, report a problem or suggest an improvement we can make to ONS.GOV.UK" @@ -30,16 +30,20 @@ description = "This specific page" one = "This specific page" [FeedbackWhatEnterURL] -description = "Enter URL or name of the page" -one = "Enter URL or name of the page" +description = "Enter URL of the page" +one = "Enter URL of the page" -[FeedbackWhatOptHintSpecificPage] -description = "{{.ServiceDescription}}" -one = "({{.arg0}})" +[FeedbackValidURL] +description = "Enter a valid URL" +one = "Enter a valid URL" + +[FeedbackChooseType] +description = "Choose a type" +one = "Choose a type" [FeedbackWhatOptNewService] -description = "This new service" -one = "This new service" +description = "This {{.ServiceName}} service" +one = "The {{.arg0}} service" [FeedbackWhatOptGeneral] description = "The whole ONS website or general feedback" @@ -86,8 +90,8 @@ description = "Send feedback" one = "Send feedback" [FeedbackFinished] -description = "Your feedback will help us to improve the website. We are unable to respond to all enquiries. If your matter is urgent, please contact us.

Return to {{.Metadata.Description}}" -one = "Your feedback will help us to improve the website. We are unable to respond to all enquiries. If your matter is urgent, please contact us.

Return to {{.arg0}}" +description = "Your feedback will help us to improve the website. We are unable to respond to all enquiries. If your matter is urgent, please contact us." +one = "Your feedback will help us to improve the website. We are unable to respond to all enquiries. If your matter is urgent, please contact us." [FeedbackWholeWebsite] description = "The whole ONS website or general feedback" diff --git a/assets/templates/feedback-thanks.tmpl b/assets/templates/feedback-thanks.tmpl index 99cc706..1e576cb 100644 --- a/assets/templates/feedback-thanks.tmpl +++ b/assets/templates/feedback-thanks.tmpl @@ -1,21 +1,36 @@ -{{$Language := .Language}} - -
-
-
-
- {{ localise "FeedbackFinished" $Language 1 .Metadata.Description .Metadata.Description | safeHTML }} +
+
+
+
+ + + + + Done + + +
diff --git a/assets/templates/feedback.tmpl b/assets/templates/feedback.tmpl index fc84cda..144a0c1 100644 --- a/assets/templates/feedback.tmpl +++ b/assets/templates/feedback.tmpl @@ -1,120 +1,41 @@ -{{$Language := .Language}} - -
-
-
- {{if eq .Metadata.Title "Feedback"}} -
-

{{ localise "FeedbackDesc" $Language 1}}

-
-
-
- -
-

{{ localise "FeedbackTitleWhat" $Language 1 }}

-
- - {{if .ServiceDescription}} - - {{end}} - {{ if .PreviousURL }} - - {{ else }} - - {{ end }} -
+
+
+ {{ if .Page.Error.Title }} + {{ template "partials/error-summary" .Page.Error }} + {{ end }} +

+ {{- localise "FeedbackTitle" .Language 1 $.Metadata.Title -}} +

+
+
+

{{- localise "FeedbackDesc" .Language 1 -}}

+ + + {{ template "partials/fields/fieldset-radio" .TypeRadios }} + {{ template "partials/fields/field-textarea" .DescriptionField }} +
+ {{- localise "FeedbackTitleReply" .Language 1 -}} +

{{- localise "FeedbackDescReply" .Language 1 -}}

+

{{- localise "FeedbackReplyDisclaimer" .Language 1 -}}

+ {{ range .Contact }} + {{ template "partials/fields/field-text" . }} + {{ end }}
-
- - {{if eq .ErrorType "description"}}{{ localise "FeedbackAlertEntry" $Language 1 }}{{end}} - -
-
-
- -

{{ localise "FeedbackTitleReply" $Language 1 }}

-

{{ localise "FeedbackDescReply" $Language 1 }}

-
- - - - {{if eq .ErrorType "email"}}{{ localise "FeedbackAlertEmail" $Language 1 }}{{end}} - -
{{ localise "FeedbackReplyDisclaimer" $Language 1 }}
-
-
-
- -
+
- {{else}} -
- {{ localise "FeedbackFinished" $Language 1 .Metadata.Description .Metadata.Description | safeHTML }} -
- {{end}}
- diff --git a/ci/build.yml b/ci/build.yml index 04c46ce..774a382 100644 --- a/ci/build.yml +++ b/ci/build.yml @@ -6,7 +6,7 @@ image_resource: type: docker-image source: repository: golang - tag: 1.19.2 + tag: 1.20.4 inputs: - name: dp-frontend-feedback-controller diff --git a/ci/component.yml b/ci/component.yml index 6dcbf46..2173f1d 100644 --- a/ci/component.yml +++ b/ci/component.yml @@ -6,7 +6,7 @@ image_resource: type: docker-image source: repository: golang - tag: 1.18.6 + tag: 1.20.4 inputs: - name: dp-frontend-feedback-controller diff --git a/ci/lint.yml b/ci/lint.yml index c20ee82..f43c5c3 100644 --- a/ci/lint.yml +++ b/ci/lint.yml @@ -6,7 +6,7 @@ image_resource: type: docker-image source: repository: golang - tag: 1.19.2 + tag: 1.20.4 inputs: - name: dp-frontend-feedback-controller diff --git a/ci/unit.yml b/ci/unit.yml index 04224c1..d17c72e 100644 --- a/ci/unit.yml +++ b/ci/unit.yml @@ -6,7 +6,7 @@ image_resource: type: docker-image source: repository: golang - tag: 1.19.2 + tag: 1.20.4 inputs: - name: dp-frontend-feedback-controller diff --git a/config/config.go b/config/config.go index d9abad2..c07a6d2 100644 --- a/config/config.go +++ b/config/config.go @@ -24,8 +24,7 @@ type Config struct { MailPort string `envconfig:"MAIL_PORT"` FeedbackTo string `envconfig:"FEEDBACK_TO"` FeedbackFrom string `envconfig:"FEEDBACK_FROM"` - IsPublishingMode bool `envconfig:"IS_PUBLISHING_MODE"` - Languages string `envconfig:"LANGUAGES"` + IsPublishing bool `envconfig:"IS_PUBLISHING"` PatternLibraryAssetsPath string `envconfig:"PATTERN_LIBRARY_ASSETS_PATH"` ServiceAuthToken string `envconfig:"SERVICE_AUTH_TOKEN" json:"-"` SiteDomain string `envconfig:"SITE_DOMAIN"` @@ -71,7 +70,7 @@ func get() (*Config, error) { MailPassword: "", FeedbackTo: "to@gmail.com", FeedbackFrom: "from@gmail.com", - IsPublishingMode: false, + IsPublishing: false, ServiceAuthToken: "", SiteDomain: "localhost", SupportedLanguages: []string{"en", "cy"}, diff --git a/config/config_test.go b/config/config_test.go index 1fe79f2..31b4fa0 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -28,7 +28,7 @@ func TestConfig(t *testing.T) { So(cfg.SiteDomain, ShouldEqual, "localhost") So(cfg.Debug, ShouldEqual, false) So(cfg.SupportedLanguages, ShouldResemble, []string{"en", "cy"}) - So(cfg.IsPublishingMode, ShouldEqual, false) + So(cfg.IsPublishing, ShouldEqual, false) So(cfg.EnableCensusTopicSubsection, ShouldEqual, false) }) diff --git a/go.mod b/go.mod index a219a93..a329c75 100644 --- a/go.mod +++ b/go.mod @@ -1,49 +1,44 @@ module github.com/ONSdigital/dp-frontend-feedback-controller -go 1.19 +go 1.20 require ( - github.com/ONSdigital/dp-frontend-cache-helper v0.2.0 - github.com/ONSdigital/dp-frontend-models v1.10.1 - github.com/ONSdigital/dp-healthcheck v1.5.0 - github.com/ONSdigital/dp-net/v2 v2.6.0 - github.com/ONSdigital/dp-renderer v1.62.0 - github.com/ONSdigital/dp-topic-api v0.16.0 + github.com/ONSdigital/dp-frontend-cache-helper v0.3.0 + github.com/ONSdigital/dp-healthcheck v1.6.1 + github.com/ONSdigital/dp-net/v2 v2.9.1 + github.com/ONSdigital/dp-renderer/v2 v2.1.0 + github.com/ONSdigital/dp-topic-api v0.17.1 github.com/ONSdigital/go-ns v0.0.0-20210916104633-ac1c1c52327e - github.com/ONSdigital/log.go/v2 v2.3.0 + github.com/ONSdigital/log.go/v2 v2.4.1 + github.com/elazarl/go-bindata-assetfs v1.0.1 github.com/gorilla/mux v1.8.0 github.com/gorilla/schema v1.2.0 github.com/kelseyhightower/envconfig v1.4.0 - github.com/smartystreets/goconvey v1.7.2 + github.com/smartystreets/goconvey v1.8.0 ) require ( - github.com/BurntSushi/toml v0.3.1 // indirect - github.com/ONSdigital/dp-api-clients-go v1.43.0 // indirect - github.com/ONSdigital/dp-api-clients-go/v2 v2.187.0 // indirect + github.com/BurntSushi/toml v1.2.1 // indirect + github.com/ONSdigital/dp-api-clients-go/v2 v2.252.1 // indirect github.com/ONSdigital/dp-cache v0.3.0 // indirect - github.com/ONSdigital/dp-net v1.5.0 // indirect - github.com/aws/aws-sdk-go v1.44.76 // indirect - github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2 // indirect - github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect - github.com/fatih/color v1.13.0 // indirect - github.com/fsnotify/fsnotify v1.4.9 // indirect + github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-bindata/go-bindata v3.1.2+incompatible // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect - github.com/gosimple/slug v1.9.0 // indirect + github.com/gosimple/slug v1.13.1 // indirect + github.com/gosimple/unidecode v1.0.1 // indirect github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect github.com/justinas/alice v1.2.0 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect - github.com/nicksnyder/go-i18n/v2 v2.1.2 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect + github.com/nicksnyder/go-i18n/v2 v2.2.1 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/smartystreets/assertions v1.13.0 // indirect - github.com/unrolled/render v1.4.0 // indirect - golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect - golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect - golang.org/x/text v0.4.0 // indirect + github.com/smartystreets/assertions v1.13.1 // indirect + github.com/unrolled/render v1.6.0 // indirect + golang.org/x/net v0.9.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect ) diff --git a/go.sum b/go.sum index 0d9f058..18fd740 100644 --- a/go.sum +++ b/go.sum @@ -1,68 +1,37 @@ -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/ONSdigital/dp-api-clients-go v1.1.0/go.mod h1:9lqor0I7caCnRWr04gU/r7x5dqxgoODob8L48q+cE4E= -github.com/ONSdigital/dp-api-clients-go v1.28.0/go.mod h1:iyJy6uRL4B6OYOJA0XMr5UHt6+Q8XmN9uwmURO+9Oj4= -github.com/ONSdigital/dp-api-clients-go v1.34.3/go.mod h1:kX+YKuoLYLfkeLHMvQKRRydZVxO7ZEYyYiwG2xhV51E= -github.com/ONSdigital/dp-api-clients-go v1.41.1/go.mod h1:Ga1+ANjviu21NFJI9wp5NctJIdB4TJLDGbpQFl2V8Wc= -github.com/ONSdigital/dp-api-clients-go v1.43.0 h1:0982P/YxnYXvba1RhEcFmwF3xywC4eXokWQ8YH3Mm24= -github.com/ONSdigital/dp-api-clients-go v1.43.0/go.mod h1:V5MfINik+o3OAF985UXUoMjXIfrZe3JKYa5AhZn5jts= -github.com/ONSdigital/dp-api-clients-go/v2 v2.187.0 h1:HKZ2DEMdoUlz4nAlVReyFv1XCXYcAvQBXukGnE+zmuY= -github.com/ONSdigital/dp-api-clients-go/v2 v2.187.0/go.mod h1:JhWrjETosIKYjbLoa3d19QJlKv7dx+/+x09qg355Rxg= +github.com/ONSdigital/dp-api-clients-go/v2 v2.252.1 h1:8iT85wgqtJynoPNbVpb5M+RamR5yjEVL1nlQW4deb6w= +github.com/ONSdigital/dp-api-clients-go/v2 v2.252.1/go.mod h1:N/8TXJmmDpa9YA9oQjgqaB172+WExUJwt3yI47bVXa0= github.com/ONSdigital/dp-cache v0.3.0 h1:E0T3XVjpcl6PDHL/4Go9a+EGekIwwNsfL0MsSgNK/jM= github.com/ONSdigital/dp-cache v0.3.0/go.mod h1:cyduXeUO8n9TLZIorCWDzEoLkk1jK8q4Ex5CBsxptKA= -github.com/ONSdigital/dp-frontend-cache-helper v0.2.0 h1:IL1wUKY1gbI4rkLCmzNDDke0gm6D7YQgHufekFq94p8= -github.com/ONSdigital/dp-frontend-cache-helper v0.2.0/go.mod h1:3tOGFnElMHDTfAiFbWD2W64zF+IWJSlPS/f/wbfvEpg= +github.com/ONSdigital/dp-frontend-cache-helper v0.3.0 h1:JB+rFzV8WkNsh5x3PDfthPDsLRjek9Iefpe5/YgR+wQ= +github.com/ONSdigital/dp-frontend-cache-helper v0.3.0/go.mod h1:hTaxjzr2n1OV+F8Rwvwf19eU8tGKW5LuA18HuUKfbnI= github.com/ONSdigital/dp-frontend-models v1.1.0/go.mod h1:TT96P7Mi69N3Tc/jFNdbjiwG4GAaMjP26HLotFQ6BPw= -github.com/ONSdigital/dp-frontend-models v1.10.1 h1:fHXZB/MwcS7pR2MySFvAhkzZpMSaKyhYb6v80cRKVhI= -github.com/ONSdigital/dp-frontend-models v1.10.1/go.mod h1:K4n0EwATkzbuWzSajBHja+uc9zvqnKiq6WtUwLav4Kg= github.com/ONSdigital/dp-healthcheck v0.0.0-20200131122546-9db6d3f0494e/go.mod h1:zighxZ/0m5u7zo0eAr8XFlA+Dz2ic7A1vna6YXvhCjQ= -github.com/ONSdigital/dp-healthcheck v1.0.5/go.mod h1:2wbVAUHMl9+4tWhUlxYUuA1dnf2+NrwzC+So5f5BMLk= -github.com/ONSdigital/dp-healthcheck v1.1.0/go.mod h1:vZwyjMJiCHjp/sJ2R1ZEqzZT0rJ0+uHVGwxqdP4J5vg= -github.com/ONSdigital/dp-healthcheck v1.5.0 h1:w3iMuq+CIr8lyu8xPgXPlU3MQGXWlm4eiQgsQHyqRWc= -github.com/ONSdigital/dp-healthcheck v1.5.0/go.mod h1:oz2V3rz3D0GNso9GnIjxl8g4GhBExHTCRtTl/T4+iOk= +github.com/ONSdigital/dp-healthcheck v1.6.1 h1:YDAnxE2fI3G2hhGC42mKI/fRhAhIYmFZGQwQ/8M65M0= +github.com/ONSdigital/dp-healthcheck v1.6.1/go.mod h1:FURB2RUJHw3lssamKtsGsrbu31ar9yhMSDYzG9vgSIo= github.com/ONSdigital/dp-mocking v0.0.0-20190905163309-fee2702ad1b9/go.mod h1:BcIRgitUju//qgNePRBmNjATarTtynAgc0yV29VpLEk= -github.com/ONSdigital/dp-mocking v0.9.1 h1:lfpk7D8xB+6OvCN/PXMoTp+zCYr0tDdfMCl/lJTml2g= -github.com/ONSdigital/dp-net v1.0.5-0.20200805082802-e518bc287596/go.mod h1:wDVhk2pYosQ1q6PXxuFIRYhYk2XX5+1CeRRnXpSczPY= -github.com/ONSdigital/dp-net v1.0.5-0.20200805145012-9227a11caddb/go.mod h1:MrSZwDUvp8u1VJEqa+36Gwq4E7/DdceW+BDCvGes6Cs= -github.com/ONSdigital/dp-net v1.0.5-0.20200805150805-cac050646ab5/go.mod h1:de3LB9tedE0tObBwa12dUOt5rvTW4qQkF5rXtt4b6CE= -github.com/ONSdigital/dp-net v1.0.7/go.mod h1:1QFzx32FwPKD2lgZI6MtcsUXritsBdJihlzIWDrQ/gc= -github.com/ONSdigital/dp-net v1.0.12/go.mod h1:2lvIKOlD4T3BjWQwjHhBUO2UNWDk82u/+mHRn0R3C9A= -github.com/ONSdigital/dp-net v1.2.0/go.mod h1:NinlaqcsPbIR+X7j5PXCl3UI5G2zCL041SDF6WIiiO4= -github.com/ONSdigital/dp-net v1.4.1/go.mod h1:VK8dah+G0TeVO/Os/w17Rk4WM6hIGmdUXrm8fBWyC+g= -github.com/ONSdigital/dp-net v1.5.0 h1:H47O5N+eXyXCYqPoQGWwuK12uLds3pCgzxpYwepg+bQ= -github.com/ONSdigital/dp-net v1.5.0/go.mod h1:d/S4n6FJlQwmVIa2rnVt1HnAUgM8XsG0QEJBQp48uGg= -github.com/ONSdigital/dp-net/v2 v2.6.0 h1:orxdb0SVDrJgz/zma0QXgQCAGJdLDhp6g2XqH6VFUZw= -github.com/ONSdigital/dp-net/v2 v2.6.0/go.mod h1:4/2ZyId//hFa7AtbbRNQctY729C1Vbw4UEdOliRvpZI= +github.com/ONSdigital/dp-mocking v0.10.1 h1:yEEglJ458kUztHlnGxhMiKhIr13gMYWYOBKFbCbp89M= +github.com/ONSdigital/dp-net/v2 v2.9.1 h1:2hGa0ArL0m2pMT9cFxMPcSOgvJ0zstH3NxWeykU9Elg= +github.com/ONSdigital/dp-net/v2 v2.9.1/go.mod h1:iy1XmnqC7aKwHCpbTDhfAVrlYDGJGkZl36zeXyHq+Hw= github.com/ONSdigital/dp-rchttp v0.0.0-20190919143000-bb5699e6fd59/go.mod h1:KkW68U3FPuivW4ogi9L8CPKNj9ZxGko4qcUY7KoAAkQ= github.com/ONSdigital/dp-rchttp v0.0.0-20200114090501-463a529590e8/go.mod h1:821jZtK0oBsV8hjIkNr8vhAWuv0FxJBPJuAHa2B70Gk= -github.com/ONSdigital/dp-renderer v1.58.0 h1:GBGKiFFtP91Q6XJ4VwYlKUh8LPjpNRNVAaDk1Mkmc0Q= -github.com/ONSdigital/dp-renderer v1.58.0/go.mod h1:aEgUZoxxw4vy5QG/1WYid3ApbLkaakbyn3/CqIGR/yE= -github.com/ONSdigital/dp-renderer v1.62.0 h1:RMIYXulgOICDKz0SivAgcpUsECIVYyNOP3rH2rgss0U= -github.com/ONSdigital/dp-renderer v1.62.0/go.mod h1:aEgUZoxxw4vy5QG/1WYid3ApbLkaakbyn3/CqIGR/yE= -github.com/ONSdigital/dp-topic-api v0.16.0 h1:SScnzJ9uaVxBoXECAbVOsaqLmGqjss7UPdyVLu9L9C8= -github.com/ONSdigital/dp-topic-api v0.16.0/go.mod h1:HySqqTOmVX6ElSOrioTtjWiU3dV9hfy2MFg0730lY1g= +github.com/ONSdigital/dp-renderer/v2 v2.1.0 h1:fgD+BTMTkf+1GZRC6C6/gU4AA5NefY0aq3ojMHSCSn8= +github.com/ONSdigital/dp-renderer/v2 v2.1.0/go.mod h1:EiepEQWRSybj43r9jlHojJidSls6M8ERmWn1XdH3Iek= +github.com/ONSdigital/dp-topic-api v0.17.1 h1:rYQi0Ve+ckiL5Bk8E1iUHrazQrZFxL0JsIl8xEWL+gc= +github.com/ONSdigital/dp-topic-api v0.17.1/go.mod h1:HySqqTOmVX6ElSOrioTtjWiU3dV9hfy2MFg0730lY1g= github.com/ONSdigital/go-ns v0.0.0-20191104121206-f144c4ec2e58/go.mod h1:iWos35il+NjbvDEqwtB736pyHru0MPFE/LqcwkV1wDc= github.com/ONSdigital/go-ns v0.0.0-20210916104633-ac1c1c52327e h1:o+AK5m0lxRIFn4t9ng9x19kez72ErAB0cW9ArT6sAZM= github.com/ONSdigital/go-ns v0.0.0-20210916104633-ac1c1c52327e/go.mod h1:BCx4ULp5nT3dT7Mft5iMrp9439JG9lqIlc0JOPmsHTg= github.com/ONSdigital/log.go v0.0.0-20191127134126-2a610b254f20/go.mod h1:BD7D8FWP1fzwUWsrCopEG72jl9cchCaVNIGSz6YvL+Y= github.com/ONSdigital/log.go v1.0.0/go.mod h1:UnGu9Q14gNC+kz0DOkdnLYGoqugCvnokHBRBxFRpVoQ= -github.com/ONSdigital/log.go v1.0.1-0.20200805084515-ee61165ea36a/go.mod h1:dDnQATFXCBOknvj6ZQuKfmDhbOWf3e8mtV+dPEfWJqs= -github.com/ONSdigital/log.go v1.0.1-0.20200805145532-1f25087a0744/go.mod h1:y4E9MYC+cV9VfjRD0UBGj8PA7H3wABqQi87/ejrDhYc= -github.com/ONSdigital/log.go v1.0.1/go.mod h1:dIwSXuvFB5EsZG5x44JhsXZKMd80zlb0DZxmiAtpL4M= -github.com/ONSdigital/log.go v1.1.0 h1:XFE8U5lPeiXyujgUtbh+pKCotiICeIGFEAauNk9c24A= -github.com/ONSdigital/log.go v1.1.0/go.mod h1:0hOVuYR3bDUI30VRo48d5KHfJIoe+spuPXqgt6UF78o= -github.com/ONSdigital/log.go/v2 v2.0.0/go.mod h1:PR7vXrv9dZKUc7SI/0toxBbStk84snmybBnWpe+xY2o= -github.com/ONSdigital/log.go/v2 v2.0.5/go.mod h1:PR7vXrv9dZKUc7SI/0toxBbStk84snmybBnWpe+xY2o= github.com/ONSdigital/log.go/v2 v2.0.9/go.mod h1:VyTDkL82FtiAkaNFaT+bURBhLbP7NsIx4rkVbdpiuEg= -github.com/ONSdigital/log.go/v2 v2.3.0 h1:go+KkUR36/CClez+UCCwVIVqFie1w3PYgvAyoclKVYM= -github.com/ONSdigital/log.go/v2 v2.3.0/go.mod h1:s5iqJuW0jDE8V7VQJqLHT73nn/H8u1c+A2Nqw2QPEeo= -github.com/aws/aws-sdk-go v1.38.15/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.44.76 h1:5e8yGO/XeNYKckOjpBKUd5wStf0So3CrQIiOMCVLpOI= -github.com/aws/aws-sdk-go v1.44.76/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2 h1:t8KYCwSKsOEZBFELI4Pn/phbp38iJ1RRAkDFNin1aak= -github.com/c2h5oh/datasize v0.0.0-20200825124411-48ed595a09d2/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= -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/ONSdigital/log.go/v2 v2.4.1 h1:QAHQqtXgXx43OUTSebNAocVfN21RwrHzagN6zDAzwdo= +github.com/ONSdigital/log.go/v2 v2.4.1/go.mod h1:hJTjxs9r8k49maNelGpL4SBWv8NG45vCKp15+6ce9bw= +github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY= +github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw= github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= @@ -70,21 +39,17 @@ github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9 h1:wWke/RUCl7V github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9/go.mod h1:uPmAp6Sws4L7+Q/OokbWDAK1ibXYhB3PXFP1kol5hPg= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/go-avro/avro v0.0.0-20171219232920-444163702c11/go.mod h1:kxj6THYP0dmFPk4Z+bijIAhJoGgeBfyOKXMduhvdJPA= github.com/go-bindata/go-bindata v3.1.2+incompatible h1:5vjJMVhowQdPzjE1LdxyFF7YFTXg5IgGVW4gBr5IbvE= github.com/go-bindata/go-bindata v3.1.2+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20210202160940-bed99a852dfe/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -93,16 +58,14 @@ github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB7 github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc= github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= -github.com/gosimple/slug v1.9.0 h1:r5vDcYrFz9BmfIAMC829un9hq7hKM4cHUrsv36LbEqs= -github.com/gosimple/slug v1.9.0/go.mod h1:AMZ+sOVe65uByN3kgEyf9WEBKBCSS+dJjMX9x4vDJbg= +github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q= +github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= +github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= +github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= github.com/hokaccha/go-prettyjson v0.0.0-20190818114111-108c894c2c0e/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI= github.com/hokaccha/go-prettyjson v0.0.0-20210113012101-fb4e108d2519/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI= github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f h1:7LYC+Yfkj3CTRcShK0KOL/w6iTiKyqqBA9a41Wnggw8= github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 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/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo= @@ -111,106 +74,73 @@ github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dv github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/nicksnyder/go-i18n/v2 v2.1.2 h1:QHYxcUJnGHBaq7XbvgunmZ2Pn0focXFqTD61CkH146c= -github.com/nicksnyder/go-i18n/v2 v2.1.2/go.mod h1:d++QJC9ZVf7pa48qrsRWhMJ5pSHIPmS3OLqK1niyLxs= +github.com/nicksnyder/go-i18n/v2 v2.2.1 h1:aOzRCdwsJuoExfZhoiXHy4bjruwCMdt5otbYojM/PaA= +github.com/nicksnyder/go-i18n/v2 v2.2.1/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0= 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/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ= -github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= -github.com/smartystreets/assertions v1.13.0 h1:Dx1kYM01xsSqKPno3aqLnrwac2LetPvN23diwyr69Qs= -github.com/smartystreets/assertions v1.13.0/go.mod h1:wDmR7qL282YbGsPy6H/yAsesrxfxaaSlJazyFLYVFx8= +github.com/smartystreets/assertions v1.13.1 h1:Ef7KhSmjZcK6AVf9YbJdvPYG9avaF0ZxudX+ThRdWfU= +github.com/smartystreets/assertions v1.13.1/go.mod h1:cXr/IwVfSo/RbCSPhoAPv73p3hlSdrBH/b3SdnW/LMY= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -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/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/smartystreets/goconvey v1.8.0 h1:Oi49ha/2MURE0WexF052Z0m+BNSGirfjg5RL+JXWq3w= +github.com/smartystreets/goconvey v1.8.0/go.mod h1:EdX8jtrTIj26jmjCOVNMVSIYAtgexqXKHOXW2Dx9JLg= github.com/unrolled/render v1.0.2/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM= -github.com/unrolled/render v1.4.0 h1:p73obhpsXuE3paXOtcuXTBKgBJpLCfmABnsUiO35x+Q= -github.com/unrolled/render v1.4.0/go.mod h1:cK4RSTTVdND5j9EYEc0LAMOvdG11JeiKjyjfyZRvV2w= +github.com/unrolled/render v1.6.0 h1:CMhr7HKRAzVI1RltKSo8JMRaokFi60ObV9I5uSxETJE= +github.com/unrolled/render v1.6.0/go.mod h1:NoaP3JGGHcYDAqu6gTDz01E2TMqBybJ8dpR6qqRBVPQ= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= -golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/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.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210414055047-fe65e336abe0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/avro.v0 v0.0.0-20171217001914-a730b5802183/go.mod h1:FvqrFXt+jCsyQibeRv4xxEJBL5iG2DDW5aeJwzDiq4A= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -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= diff --git a/handlers/feedback.go b/handlers/feedback.go index 4243da9..e67d349 100644 --- a/handlers/feedback.go +++ b/handlers/feedback.go @@ -4,99 +4,65 @@ import ( "bytes" "context" "fmt" - "html" "net/http" + "net/url" "regexp" + "strings" cacheHelper "github.com/ONSdigital/dp-frontend-cache-helper/pkg/navigation/helper" "github.com/ONSdigital/dp-frontend-feedback-controller/config" "github.com/ONSdigital/dp-frontend-feedback-controller/email" "github.com/ONSdigital/dp-frontend-feedback-controller/interfaces" + "github.com/ONSdigital/dp-frontend-feedback-controller/mapper" "github.com/ONSdigital/dp-frontend-feedback-controller/model" dphandlers "github.com/ONSdigital/dp-net/v2/handlers" + core "github.com/ONSdigital/dp-renderer/v2/model" "github.com/ONSdigital/log.go/v2/log" "github.com/gorilla/schema" ) -// Feedback represents a user's feedback -type Feedback struct { - Type string `schema:"type"` - URI string `schema:":uri"` - URL string `schema:"url"` - Description string `schema:"description"` - Name string `schema:"name"` - Email string `schema:"email"` - FeedbackFormType string `schema:"feedback-form-type"` -} - // FeedbackThanks loads the Feedback Thank you page -func FeedbackThanks(rend interfaces.Renderer, cacheService *cacheHelper.Helper) http.HandlerFunc { +func (f *Feedback) FeedbackThanks() http.HandlerFunc { return dphandlers.ControllerHandler(func(w http.ResponseWriter, req *http.Request, lang, collectionID, accessToken string) { - feedbackThanks(w, req, req.Referer(), "", rend, cacheService, lang) + feedbackThanks(w, req, req.Referer(), f.Render, f.CacheService, lang) }) } -func feedbackThanks(w http.ResponseWriter, req *http.Request, url, errorType string, rend interfaces.Renderer, cacheHelperService *cacheHelper.Helper, lang string) { +func feedbackThanks(w http.ResponseWriter, req *http.Request, uri string, rend interfaces.Renderer, cacheHelperService *cacheHelper.Helper, lang string) { ctx := req.Context() - basePage := rend.NewBasePageModel() - p := model.Feedback{ - Page: basePage, - } - var wholeSite string + cfg, err := config.Get() if err != nil { log.Warn(ctx, "Unable to retrieve configuration", log.FormatErrors([]error{err})) } else { wholeSite = cfg.SiteDomain } + + basePage := rend.NewBasePageModel() + p := mapper.CreateGetFeedbackThanks(req, basePage, lang, uri, wholeSite) + if cfg.EnableNewNavBar { mappedNavContent, err := cacheHelperService.GetMappedNavigationContent(ctx, lang) if err == nil { p.NavigationContent = mappedNavContent } } - p.Type = "feedback" - p.Metadata.Title = "Thank you" - p.ErrorType = errorType - p.PreviousURL = url - - // returnTo is redered on page so needs XSS protection - returnTo := html.EscapeString(req.URL.Query().Get("returnTo")) - if returnTo == "Whole site" { - returnTo = wholeSite - } else if returnTo == "" { - returnTo = url - } - - p.Metadata.Description = returnTo rend.BuildPage(w, p, "feedback-thanks") } // GetFeedback handles the loading of a feedback page -func GetFeedback(rend interfaces.Renderer, cacheService *cacheHelper.Helper) http.HandlerFunc { +func (f *Feedback) GetFeedback() http.HandlerFunc { return dphandlers.ControllerHandler(func(w http.ResponseWriter, req *http.Request, lang, collectionID, accessToken string) { - getFeedback(w, req, req.Referer(), "", "", "", "", lang, rend, cacheService) + getFeedback(w, req, []core.ErrorItem{}, model.FeedbackForm{URL: req.Referer()}, lang, f.Render, f.CacheService) }) } -func getFeedback(w http.ResponseWriter, req *http.Request, url, errorType, description, name, userEmail, lang string, rend interfaces.Renderer, cacheHelperService *cacheHelper.Helper) { +func getFeedback(w http.ResponseWriter, req *http.Request, validationErrors []core.ErrorItem, ff model.FeedbackForm, lang string, rend interfaces.Renderer, cacheHelperService *cacheHelper.Helper) { basePage := rend.NewBasePageModel() - p := model.Feedback{ - Page: basePage, - } - - var services = make(map[string]string) - services["cmd"] = "Customising data by applying filters" - services["dev"] = "ONS developer website" + p := mapper.CreateGetFeedback(req, basePage, validationErrors, ff, lang) - p.ServiceDescription = services[req.URL.Query().Get("service")] - - p.Language = lang - p.Type = "feedback" - p.Metadata.Title = "Feedback" - p.Metadata.Description = url ctx := context.Background() cfg, err := config.Get() if err != nil { @@ -109,100 +75,147 @@ func getFeedback(w http.ResponseWriter, req *http.Request, url, errorType, descr } } - if len(p.Metadata.Description) > 50 { - p.Metadata.Description = p.Metadata.Description[len(p.Metadata.Description)-50 : len(p.Metadata.Description)] - } - - p.ErrorType = errorType - p.Feedback = description - p.Name = name - p.Email = userEmail - p.PreviousURL = url - rend.BuildPage(w, p, "feedback") } -// AddFeedback handles a users feedback request and sends a message to slack -func AddFeedback(to, from string, isPositive bool, rend interfaces.Renderer, emailSender email.Sender, cacheService *cacheHelper.Helper) http.HandlerFunc { +// AddFeedback handles a users feedback request +func (f *Feedback) AddFeedback() http.HandlerFunc { return dphandlers.ControllerHandler(func(w http.ResponseWriter, req *http.Request, lang, collectionID, accessToken string) { - addFeedback(w, req, isPositive, rend, emailSender, from, to, lang, cacheService) + addFeedback(w, req, f.Render, f.EmailSender, f.Config.FeedbackFrom, f.Config.FeedbackTo, lang, f.CacheService) }) } -func addFeedback(w http.ResponseWriter, req *http.Request, isPositive bool, rend interfaces.Renderer, emailSender email.Sender, from, to, lang string, cacheService *cacheHelper.Helper) { +func addFeedback(w http.ResponseWriter, req *http.Request, rend interfaces.Renderer, emailSender email.Sender, from, to, lang string, cacheService *cacheHelper.Helper) { ctx := req.Context() if err := req.ParseForm(); err != nil { log.Error(ctx, "unable to parse request form", err) - w.WriteHeader(http.StatusInternalServerError) + w.WriteHeader(http.StatusBadRequest) return } decoder := schema.NewDecoder() decoder.IgnoreUnknownKeys(true) - var f Feedback - if err := decoder.Decode(&f, req.Form); err != nil { + var ff model.FeedbackForm + if err := decoder.Decode(&ff, req.Form); err != nil { log.Error(ctx, "unable to decode request form", err) w.WriteHeader(http.StatusInternalServerError) return } - if f.Description == "" && !isPositive { - getFeedback(w, req, f.URL, "description", f.Description, f.Name, f.Email, lang, rend, cacheService) + validationErrors := validateForm(&ff) + if len(validationErrors) > 0 { + getFeedback(w, req, validationErrors, ff, lang, rend, cacheService) return } - if len(f.Email) > 0 && !isPositive { - if ok, err := regexp.MatchString(`^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}$`, f.Email); !ok || err != nil { - getFeedback(w, req, f.URL, "email", f.Description, f.Name, f.Email, lang, rend, cacheService) - return - } - } - - if f.URL == "" { - f.URL = "Whole site" + if ff.URL == "" { + ff.URL = "Whole site" } if err := emailSender.Send( from, []string{to}, - generateFeedbackMessage(f, from, to, isPositive), + generateFeedbackMessage(ff, from, to), ); err != nil { log.Error(ctx, "failed to send message", err) w.WriteHeader(http.StatusInternalServerError) return } - returnTo := f.URL + returnTo := ff.URL if returnTo == "Whole site" || returnTo == "" { returnTo = "https://www.ons.gov.uk" } - redirectURL := "/feedback/thanks?returnTo=" + returnTo + redirectURL := fmt.Sprintf("/feedback/thanks?returnTo=%s", returnTo) http.Redirect(w, req, redirectURL, http.StatusMovedPermanently) } -func generateFeedbackMessage(f Feedback, from, to string, isPositive bool) []byte { - var description string - if isPositive { - description = "Positive feedback received" - } else { - description = f.Description +// validateForm is a helper function that validates a slice of FeedbackForm to determine if there are form validation errors +func validateForm(ff *model.FeedbackForm) (validationErrors []core.ErrorItem) { + if ff.Type == "" && ff.FormLocation != "footer" { + validationErrors = append(validationErrors, core.ErrorItem{ + Description: core.Localisation{ + LocaleKey: "FeedbackChooseType", + Plural: 1, + }, + URL: "#type-error", + }) + ff.IsTypeErr = true + } + + ff.URL = strings.TrimSpace(ff.URL) + if ff.Type == "A specific page" && ff.URL == "" { + validationErrors = append(validationErrors, core.ErrorItem{ + Description: core.Localisation{ + LocaleKey: "FeedbackWhatEnterURL", + Plural: 1, + }, + URL: "#type-error", + }) + ff.IsURLErr = true + } + + if ff.Type == "A specific page" && ff.URL != "" { + _, err := url.ParseRequestURI(ff.URL) + if err != nil { + validationErrors = append(validationErrors, core.ErrorItem{ + Description: core.Localisation{ + LocaleKey: "FeedbackValidURL", + Plural: 1, + }, + URL: "#type-error", + }) + ff.IsURLErr = true + } } + if ff.Type != "A specific page" && ff.URL != "" { + ff.URL = "" + } + + ff.Description = strings.TrimSpace(ff.Description) + if ff.Description == "" { + validationErrors = append(validationErrors, core.ErrorItem{ + Description: core.Localisation{ + LocaleKey: "FeedbackAlertEntry", + Plural: 1, + }, + URL: "#feedback-error", + }) + ff.IsDescriptionErr = true + } + + if len(ff.Email) > 0 { + if ok, err := regexp.MatchString(`^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}$`, ff.Email); !ok || err != nil { + validationErrors = append(validationErrors, core.ErrorItem{ + Description: core.Localisation{ + LocaleKey: "FeedbackAlertEmail", + Plural: 1, + }, + URL: "#email-error", + }) + ff.IsEmailErr = true + } + } + return validationErrors +} + +func generateFeedbackMessage(f model.FeedbackForm, from, to string) []byte { var b bytes.Buffer b.WriteString(fmt.Sprintf("From: %s\n", from)) b.WriteString(fmt.Sprintf("To: %s\n", to)) - b.WriteString(fmt.Sprintf("Subject: Feedback received\n\n")) + b.WriteString("Subject: Feedback received\n\n") if len(f.Type) > 0 { b.WriteString(fmt.Sprintf("Feedback Type: %s\n", f.Type)) } b.WriteString(fmt.Sprintf("Page URL: %s\n", f.URL)) - b.WriteString(fmt.Sprintf("Description: %s\n", description)) + b.WriteString(fmt.Sprintf("Description: %s\n", f.Description)) if len(f.Name) > 0 { b.WriteString(fmt.Sprintf("Name: %s\n", f.Name)) diff --git a/handlers/feedback_test.go b/handlers/feedback_test.go index 5e0f3b8..7f345b1 100644 --- a/handlers/feedback_test.go +++ b/handlers/feedback_test.go @@ -3,6 +3,7 @@ package handlers import ( "context" "errors" + "fmt" "io" "net/http" "net/http/httptest" @@ -14,23 +15,22 @@ import ( cacheHelper "github.com/ONSdigital/dp-frontend-cache-helper/pkg/navigation/helper" "github.com/ONSdigital/dp-frontend-feedback-controller/email/emailtest" "github.com/ONSdigital/dp-frontend-feedback-controller/interfaces/interfacestest" + "github.com/ONSdigital/dp-frontend-feedback-controller/mocks" "github.com/ONSdigital/dp-frontend-feedback-controller/model" - "github.com/ONSdigital/dp-frontend-models/model/feedback" - coreModel "github.com/ONSdigital/dp-renderer/model" + "github.com/ONSdigital/dp-renderer/v2/helper" + coreModel "github.com/ONSdigital/dp-renderer/v2/model" topicModel "github.com/ONSdigital/dp-topic-api/models" . "github.com/smartystreets/goconvey/convey" ) func Test_getFeedback(t *testing.T) { - Convey("Given a request without a query string", t, func() { + helper.InitialiseLocalisationsHelper(mocks.MockAssetFunction) + Convey("Given a valid request", t, func() { req := httptest.NewRequest("GET", "http://localhost", nil) w := httptest.NewRecorder() - url := "whatever" - errorType := "" - description := "" - name := "" - email := "" + ff := model.FeedbackForm{} + ff.URL = "whatever" lang := "en" mockRenderer := &interfacestest.RendererMock{ BuildPageFunc: func(w io.Writer, pageModel interface{}, templateName string) {}, @@ -53,67 +53,21 @@ func Test_getFeedback(t *testing.T) { }, }} Convey("When getFeedback is called", func() { - getFeedback(w, req, url, errorType, description, name, email, lang, mockRenderer, mockNagivationCache) + getFeedback(w, req, []coreModel.ErrorItem{}, ff, lang, mockRenderer, mockNagivationCache) Convey("Then a 200 request is returned", func() { So(w.Code, ShouldEqual, http.StatusOK) }) }) }) - - Convey("Given a valid request", t, func() { - req := httptest.NewRequest("GET", "http://localhost?service=dev", nil) - w := httptest.NewRecorder() - url := "whatever" - errorType := "" - description := "" - name := "" - email := "" - lang := "en" - mockRenderer := &interfacestest.RendererMock{ - BuildPageFunc: func(w io.Writer, pageModel interface{}, templateName string) {}, - NewBasePageModelFunc: func() coreModel.Page { - return coreModel.Page{} - }, - } - mockNagivationCache := &cacheHelper.Helper{ - Clienter: &cacheClient.ClienterMock{ - AddNavigationCacheFunc: func(ctx context.Context, updateInterval *time.Duration) error { - return nil - }, - CloseFunc: func() { - }, - GetNavigationDataFunc: func(ctx context.Context, lang string) (*topicModel.Navigation, error) { - return &topicModel.Navigation{}, nil - }, - StartBackgroundUpdateFunc: func(ctx context.Context, errorChannel chan error) { - }, - }} - - Convey("When getFeedback is called", func() { - getFeedback(w, req, url, errorType, description, name, email, lang, mockRenderer, mockNagivationCache) - Convey("Then the page model is sent to the renderer", func() { - var expectedPage feedback.Page - expectedPage.Language = "en" - expectedPage.Metadata.Title = "Feedback" - expectedPage.PreviousURL = url - expectedPage.Metadata.Description = url - expectedPage.ServiceDescription = "ONS developer website" - expectedPage.Type = "feedback" - - So(len(mockRenderer.BuildPageCalls()), ShouldEqual, 1) - }) - Convey("Then a 200 response is returned", func() { - So(w.Code, ShouldEqual, http.StatusOK) - }) - }) - }) } func Test_addFeedback(t *testing.T) { + helper.InitialiseLocalisationsHelper(mocks.MockAssetFunction) Convey("Given a valid request", t, func() { - req := httptest.NewRequest("GET", "http://localhost?description=whatever", nil) + body := strings.NewReader("description=testing1234&type=test") + req := httptest.NewRequest("POST", "http://localhost", body) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") w := httptest.NewRecorder() - isPositive := false from := "" to := "" lang := "en" @@ -144,7 +98,7 @@ func Test_addFeedback(t *testing.T) { }, }} Convey("When addFeedback is called", func() { - addFeedback(w, req, isPositive, mockRenderer, mockSender, from, to, lang, mockNagivationCache) + addFeedback(w, req, mockRenderer, mockSender, from, to, lang, mockNagivationCache) Convey("Then the renderer is not called", func() { So(len(mockRenderer.BuildPageCalls()), ShouldEqual, 0) }) @@ -158,9 +112,10 @@ func Test_addFeedback(t *testing.T) { }) Convey("Given an error returned from the sender", t, func() { - req := httptest.NewRequest("GET", "http://localhost?description=whatever", nil) + body := strings.NewReader("description=testing1234&type=test") + req := httptest.NewRequest("POST", "http://localhost", body) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") w := httptest.NewRecorder() - isPositive := false from := "" to := "" lang := "en" @@ -191,7 +146,7 @@ func Test_addFeedback(t *testing.T) { }, }} Convey("When addFeedback is called", func() { - addFeedback(w, req, isPositive, mockRenderer, mockSender, from, to, lang, mockNagivationCache) + addFeedback(w, req, mockRenderer, mockSender, from, to, lang, mockNagivationCache) Convey("Then the renderer is not called", func() { So(len(mockRenderer.BuildPageCalls()), ShouldEqual, 0) }) @@ -209,7 +164,6 @@ func Test_addFeedback(t *testing.T) { Convey("Given a request with invalid form data", t, func() { req := httptest.NewRequest("POST", "http://localhost?!@£$@$£%£$%^^&^&*", nil) w := httptest.NewRecorder() - isPositive := false from := "" to := "" lang := "en" @@ -240,75 +194,26 @@ func Test_addFeedback(t *testing.T) { }, }} Convey("When addFeedback is called", func() { - addFeedback(w, req, isPositive, mockRenderer, mockSender, from, to, lang, mockNagivationCache) + addFeedback(w, req, mockRenderer, mockSender, from, to, lang, mockNagivationCache) Convey("Then the renderer is not called", func() { So(len(mockRenderer.BuildPageCalls()), ShouldEqual, 0) }) - Convey("Then the email sender is called", func() { + Convey("Then the email sender is not called", func() { So(len(mockSender.SendCalls()), ShouldEqual, 0) }) - Convey("Then a 500 response is returned", func() { - So(w.Code, ShouldEqual, http.StatusInternalServerError) + Convey("Then a 400 response is returned", func() { + So(w.Code, ShouldEqual, http.StatusBadRequest) }) }) }) Convey("Given a request for feedback with an empty description value", t, func() { - req := httptest.NewRequest("POST", "http://localhost?service=dev", nil) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - w := httptest.NewRecorder() - isPositive := false - from := "" - to := "" - lang := "en" - - mockRenderer := &interfacestest.RendererMock{ - BuildPageFunc: func(w io.Writer, pageModel interface{}, templateName string) {}, - NewBasePageModelFunc: func() coreModel.Page { - return coreModel.Page{} - }, - } - - mockSender := &emailtest.SenderMock{ - SendFunc: func(from string, to []string, msg []byte) error { - return nil - }, - } - mockNagivationCache := &cacheHelper.Helper{ - Clienter: &cacheClient.ClienterMock{ - AddNavigationCacheFunc: func(ctx context.Context, updateInterval *time.Duration) error { - return nil - }, - CloseFunc: func() { - }, - GetNavigationDataFunc: func(ctx context.Context, lang string) (*topicModel.Navigation, error) { - return &topicModel.Navigation{}, nil - }, - StartBackgroundUpdateFunc: func(ctx context.Context, errorChannel chan error) { - }, - }} - Convey("When addFeedback is called", func() { - addFeedback(w, req, isPositive, mockRenderer, mockSender, from, to, lang, mockNagivationCache) - Convey("Then the renderer is called to render the feedback page", func() { - So(len(mockRenderer.BuildPageCalls()), ShouldEqual, 1) - }) - Convey("Then the email sender is called", func() { - So(len(mockSender.SendCalls()), ShouldEqual, 0) - }) - Convey("Then a 200 response is returned", func() { - So(w.Code, ShouldEqual, http.StatusOK) - }) - }) - }) - Convey("Given a request for feedback with an invalid email address", t, func() { - body := strings.NewReader("email=hello&description=hfjkshk") + body := strings.NewReader("description=") req := httptest.NewRequest("POST", "http://localhost?service=dev", body) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - w := httptest.NewRecorder() - isPositive := false from := "" to := "" lang := "en" @@ -339,11 +244,11 @@ func Test_addFeedback(t *testing.T) { }, }} Convey("When addFeedback is called", func() { - addFeedback(w, req, isPositive, mockRenderer, mockSender, from, to, lang, mockNagivationCache) + addFeedback(w, req, mockRenderer, mockSender, from, to, lang, mockNagivationCache) Convey("Then the renderer is called to render the feedback page", func() { So(len(mockRenderer.BuildPageCalls()), ShouldEqual, 1) }) - Convey("Then the email sender is called", func() { + Convey("Then the email sender is not called", func() { So(len(mockSender.SendCalls()), ShouldEqual, 0) }) Convey("Then a 200 response is returned", func() { @@ -354,12 +259,12 @@ func Test_addFeedback(t *testing.T) { } func Test_feedbackThanks(t *testing.T) { + helper.InitialiseLocalisationsHelper(mocks.MockAssetFunction) lang := "en" Convey("Given a valid request", t, func() { req := httptest.NewRequest("GET", "http://localhost", nil) w := httptest.NewRecorder() url := "www.test.com" - errorType := "" mockRenderer := &interfacestest.RendererMock{ BuildPageFunc: func(w io.Writer, pageModel interface{}, templateName string) {}, @@ -381,7 +286,7 @@ func Test_feedbackThanks(t *testing.T) { }, }} Convey("When feedbackThanks is called", func() { - feedbackThanks(w, req, url, errorType, mockRenderer, mockNagivationCache, lang) + feedbackThanks(w, req, url, mockRenderer, mockNagivationCache, lang) Convey("Then the renderer is called", func() { So(len(mockRenderer.BuildPageCalls()), ShouldEqual, 1) }) @@ -395,7 +300,6 @@ func Test_feedbackThanks(t *testing.T) { req := httptest.NewRequest("GET", "http://localhost?returnTo=", nil) w := httptest.NewRecorder() url := "www.test.com" - errorType := "" mockRenderer := &interfacestest.RendererMock{ BuildPageFunc: func(w io.Writer, pageModel interface{}, templateName string) {}, NewBasePageModelFunc: func() coreModel.Page { @@ -416,12 +320,214 @@ func Test_feedbackThanks(t *testing.T) { }, }} Convey("When feedbackThanks is called", func() { - feedbackThanks(w, req, url, errorType, mockRenderer, mockNagivationCache, lang) + feedbackThanks(w, req, url, mockRenderer, mockNagivationCache, lang) Convey("Then the handler sanitises the request text", func() { dataSentToRender := mockRenderer.BuildPageCalls()[0].PageModel.(model.Feedback) - returnToUrl := dataSentToRender.Metadata.Description + returnToUrl := dataSentToRender.ReturnTo So(returnToUrl, ShouldEqual, "<script>alert(1)</script>") }) }) }) } + +func TestValidateForm(t *testing.T) { + Convey("Given the validateForm function", t, func() { + testCases := []struct { + givenDescription string + given *model.FeedbackForm + expectedDescription string + expected []coreModel.ErrorItem + }{ + { + givenDescription: "the form is valid", + given: &model.FeedbackForm{ + Type: "Whole site", + Description: "Some text", + }, + expectedDescription: "no validation errors are returned", + expected: []coreModel.ErrorItem(nil), + }, + { + givenDescription: "the form does not have a type selected", + given: &model.FeedbackForm{ + Description: "Some text", + }, + expectedDescription: "a type validation error is returned", + expected: []coreModel.ErrorItem{ + { + Description: coreModel.Localisation{ + LocaleKey: "FeedbackChooseType", + Plural: 1, + }, + URL: "#type-error", + }, + }, + }, + { + givenDescription: "the a specific page/url type is chosen but the child input field is empty", + given: &model.FeedbackForm{ + Type: "A specific page", + Description: "Some text", + }, + expectedDescription: "a page/url validation error is returned", + expected: []coreModel.ErrorItem{ + { + Description: coreModel.Localisation{ + LocaleKey: "FeedbackWhatEnterURL", + Plural: 1, + }, + URL: "#type-error", + }, + }, + }, + { + givenDescription: "the a specific page/url type is chosen and the url is invalid", + given: &model.FeedbackForm{ + Type: "A specific page", + Description: "Some text", + URL: "not a url", + }, + expectedDescription: "a url validation error is returned", + expected: []coreModel.ErrorItem{ + { + Description: coreModel.Localisation{ + LocaleKey: "FeedbackValidURL", + Plural: 1, + }, + URL: "#type-error", + }, + }, + }, + { + givenDescription: "the a specific page/url type is chosen and the url is valid", + given: &model.FeedbackForm{ + Type: "A specific page", + Description: "Some text", + URL: "https://somewhere.com", + }, + expectedDescription: "no validation errors are returned", + expected: []coreModel.ErrorItem(nil), + }, + { + givenDescription: "the a whole site type is chosen but the child input for a specific page is not empty", + given: &model.FeedbackForm{ + Type: "Whole site", + Description: "Some text", + URL: "http://somewhere.com", + }, + expectedDescription: "no validation error is returned", + expected: []coreModel.ErrorItem(nil), + }, + { + givenDescription: "the form does not have a type selected but is located on the footer", + given: &model.FeedbackForm{ + FormLocation: "footer", + Description: "Some text", + }, + expectedDescription: "no validation error is returned", + expected: []coreModel.ErrorItem(nil), + }, + { + givenDescription: "the form does not have any feedback", + given: &model.FeedbackForm{ + Type: "Whole site", + }, + expectedDescription: "a description validation error is returned", + expected: []coreModel.ErrorItem{ + { + Description: coreModel.Localisation{ + LocaleKey: "FeedbackAlertEntry", + Plural: 1, + }, + URL: "#feedback-error", + }, + }, + }, + { + givenDescription: "the feedback provided is whitespace", + given: &model.FeedbackForm{ + Type: "Whole site", + Description: " ", + }, + expectedDescription: "a description validation error is returned", + expected: []coreModel.ErrorItem{ + { + Description: coreModel.Localisation{ + LocaleKey: "FeedbackAlertEntry", + Plural: 1, + }, + URL: "#feedback-error", + }, + }, + }, + { + givenDescription: "the email field has an invalid email address", + given: &model.FeedbackForm{ + Type: "Whole site", + Description: "A description", + Email: "a.string", + }, + expectedDescription: "an email validation error is returned", + expected: []coreModel.ErrorItem{ + { + Description: coreModel.Localisation{ + LocaleKey: "FeedbackAlertEmail", + Plural: 1, + }, + URL: "#email-error", + }, + }, + }, + { + givenDescription: "the email field has a valid email address", + given: &model.FeedbackForm{ + Type: "Whole site", + Description: "A description", + Email: "hello@world.com", + }, + expectedDescription: "no validation errors are returned", + expected: []coreModel.ErrorItem(nil), + }, + { + givenDescription: "multiple form validation errors", + given: &model.FeedbackForm{ + Type: "A specific page", + URL: "", + Description: "", + Email: "not an email address", + }, + expectedDescription: "validation errors are returned", + expected: []coreModel.ErrorItem{ + { + Description: coreModel.Localisation{ + LocaleKey: "FeedbackWhatEnterURL", + Plural: 1, + }, + URL: "#type-error", + }, + { + Description: coreModel.Localisation{ + LocaleKey: "FeedbackAlertEntry", + Plural: 1, + }, + URL: "#feedback-error", + }, + { + Description: coreModel.Localisation{ + LocaleKey: "FeedbackAlertEmail", + Plural: 1, + }, + URL: "#email-error", + }, + }, + }, + } + for _, t := range testCases { + Convey(fmt.Sprintf("When %s", t.givenDescription), func() { + Convey(fmt.Sprintf("Then %s", t.expectedDescription), func() { + So(validateForm(t.given), ShouldResemble, t.expected) + }) + }) + } + }) +} diff --git a/handlers/handlers.go b/handlers/handlers.go index f8c144c..6b891f9 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -3,9 +3,31 @@ package handlers import ( "net/http" + cacheHelper "github.com/ONSdigital/dp-frontend-cache-helper/pkg/navigation/helper" + "github.com/ONSdigital/dp-frontend-feedback-controller/config" + "github.com/ONSdigital/dp-frontend-feedback-controller/email" + "github.com/ONSdigital/dp-frontend-feedback-controller/interfaces" "github.com/ONSdigital/log.go/v2/log" ) +// Feedback represents the handlers required to provide feedback +type Feedback struct { + Render interfaces.Renderer + CacheService *cacheHelper.Helper + Config *config.Config + EmailSender email.Sender +} + +// NewFeedback creates a new instance of Feedback +func NewFeedback(rc interfaces.Renderer, c *cacheHelper.Helper, cfg *config.Config, es email.Sender) *Feedback { + return &Feedback{ + Render: rc, + CacheService: c, + Config: cfg, + EmailSender: es, + } +} + // ClientError is an interface that can be used to retrieve the status code if a client has errored type ClientError interface { Error() string diff --git a/interfaces/interfacestest/renderer.go b/interfaces/interfacestest/renderer.go index f46bade..104c57d 100644 --- a/interfaces/interfacestest/renderer.go +++ b/interfaces/interfacestest/renderer.go @@ -5,7 +5,7 @@ package interfacestest import ( "github.com/ONSdigital/dp-frontend-feedback-controller/interfaces" - "github.com/ONSdigital/dp-renderer/model" + "github.com/ONSdigital/dp-renderer/v2/model" "io" "sync" ) diff --git a/interfaces/renderer.go b/interfaces/renderer.go index 6db5ddc..5346a39 100644 --- a/interfaces/renderer.go +++ b/interfaces/renderer.go @@ -3,7 +3,7 @@ package interfaces import ( "io" - coreModel "github.com/ONSdigital/dp-renderer/model" + coreModel "github.com/ONSdigital/dp-renderer/v2/model" ) //go:generate moq -out interfacestest/renderer.go -pkg interfacestest . Renderer diff --git a/main.go b/main.go index 46abc7f..e5f5c70 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,7 @@ import ( "github.com/ONSdigital/dp-frontend-feedback-controller/config" "github.com/ONSdigital/dp-frontend-feedback-controller/routes" health "github.com/ONSdigital/dp-healthcheck/healthcheck" - render "github.com/ONSdigital/dp-renderer" + render "github.com/ONSdigital/dp-renderer/v2" "github.com/ONSdigital/go-ns/server" "github.com/ONSdigital/log.go/v2/log" "github.com/gorilla/mux" @@ -60,7 +60,7 @@ func main() { EnableNewNavBar: cfg.EnableNewNavBar, EnableCensusTopicSubsection: cfg.EnableCensusTopicSubsection, CensusTopicID: cfg.CensusTopicID, - IsPublishingMode: cfg.IsPublishingMode, + IsPublishingMode: cfg.IsPublishing, Languages: cfg.SupportedLanguages, ServiceAuthToken: cfg.ServiceAuthToken, } @@ -69,7 +69,7 @@ func main() { cacheService, err := cacheHelper.Init(ctx, cacheConfig) cacheService.RunUpdates(ctx, svcErrors) - // nolint: typecheck + //nolint:typecheck rend := render.NewWithDefaultClient(assets.Asset, assets.AssetNames, cfg.PatternLibraryAssetsPath, cfg.SiteDomain) routes.Setup(ctx, r, cfg, rend, healthcheck, cacheService) diff --git a/mapper/mapper.go b/mapper/mapper.go index ad5eac3..b462aa8 100644 --- a/mapper/mapper.go +++ b/mapper/mapper.go @@ -1 +1,214 @@ package mapper + +import ( + "html" + "net/http" + + "github.com/ONSdigital/dp-frontend-feedback-controller/model" + "github.com/ONSdigital/dp-renderer/v2/helper" + core "github.com/ONSdigital/dp-renderer/v2/model" +) + +// CreateGetFeedback returns a mapped feedback page to the feedback model +func CreateGetFeedback(req *http.Request, basePage core.Page, validationErrors []core.ErrorItem, ff model.FeedbackForm, lang string) model.Feedback { + p := model.Feedback{ + Page: basePage, + } + p.Breadcrumb = []core.TaxonomyNode{ + { + Title: "Home", + URI: "/", + }, + } + + var services = make(map[string]string) + services["cmd"] = "customising data by applying filters" + services["dev"] = "ONS developer" + serviceDescription := services[req.URL.Query().Get("service")] + + p.Language = lang + p.Type = "feedback" + p.URI = req.URL.Path + p.Metadata.Title = helper.Localise("FeedbackTitle", lang, 1) + p.Metadata.Description = ff.URL + + if len(p.Metadata.Description) > 50 { + p.Metadata.Description = p.Metadata.Description[len(p.Metadata.Description)-50 : len(p.Metadata.Description)] + } + + if len(validationErrors) > 0 { + p.Page.Error = core.Error{ + Title: p.Metadata.Title, + ErrorItems: validationErrors, + Language: lang, + } + } + + radioErrDetail := helper.Localise("FeedbackChooseType", lang, 1) + if ff.IsURLErr { + radioErrDetail = helper.Localise("FeedbackWhatEnterURL", lang, 1) + } + p.TypeRadios = core.RadioFieldset{ + Legend: core.Localisation{ + LocaleKey: "FeedbackTitleWhat", + Plural: 1, + }, + Radios: []core.Radio{ + { + Input: core.Input{ + ID: "whole-site", + IsChecked: ff.Type == "The whole website", + Label: core.Localisation{ + LocaleKey: "FeedbackWholeWebsite", + Plural: 1, + }, + Name: "type", + Value: "The whole website", + }, + }, + { + Input: core.Input{ + ID: "specific-page", + IsChecked: ff.Type == "A specific page" || ff.URL != "", + Label: core.Localisation{ + LocaleKey: "FeedbackASpecificPage", + Plural: 1, + }, + Name: "type", + Value: "A specific page", + }, + OtherInput: core.Input{ + Autocomplete: "url", + ID: "page-url-field", + Name: "url", + Value: ff.URL, + Label: core.Localisation{ + LocaleKey: "FeedbackWhatEnterURL", + Plural: 1, + }, + Type: core.Url, + }, + }, + }, + ValidationErr: core.ValidationErr{ + HasValidationErr: ff.IsTypeErr || ff.IsURLErr, + ErrorItem: core.ErrorItem{ + Description: core.Localisation{ + Text: radioErrDetail, + }, + ID: "type-error", + }, + }, + } + + if serviceDescription != "" { + p.TypeRadios.Radios = append( + p.TypeRadios.Radios[:1], + core.Radio{ + Input: core.Input{ + ID: "new-service", + IsChecked: ff.Type == "new-service", + Label: core.Localisation{ + Text: helper.Localise("FeedbackWhatOptNewService", lang, 1, serviceDescription), + }, + Name: "type", + Value: "The new service", + }, + }, + p.TypeRadios.Radios[1]) + } + + p.Contact = []core.TextField{ + { + Input: core.Input{ + Autocomplete: "name", + ID: "name-field", + Name: "name", + Value: ff.Name, + Label: core.Localisation{ + LocaleKey: "FeedbackTitleName", + Plural: 1, + }, + }, + }, + { + Input: core.Input{ + Autocomplete: "email", + ID: "email-field", + Name: "email", + Label: core.Localisation{ + LocaleKey: "FeedbackTitleEmail", + Plural: 1, + }, + Type: core.Email, + Value: ff.Email, + }, + ValidationErr: core.ValidationErr{ + HasValidationErr: ff.IsEmailErr, + ErrorItem: core.ErrorItem{ + Description: core.Localisation{ + LocaleKey: "FeedbackAlertEmail", + Plural: 1, + }, + ID: "email-error", + }, + }, + }, + } + + p.DescriptionField = core.TextareaField{ + Input: core.Input{ + Autocomplete: "off", + Description: core.Localisation{ + LocaleKey: "FeedbackHintEntry", + Plural: 1, + }, + ID: "description-field", + Label: core.Localisation{ + LocaleKey: "FeedbackTitleEntry", + Plural: 1, + }, + Language: lang, + Name: "description", + Value: ff.Description, + }, + ValidationErr: core.ValidationErr{ + HasValidationErr: ff.IsDescriptionErr, + ErrorItem: core.ErrorItem{ + Description: core.Localisation{ + LocaleKey: "FeedbackAlertEntry", + Plural: 1, + }, + ID: "feedback-error", + }, + }, + } + + p.PreviousURL = ff.URL + + return p +} + +func CreateGetFeedbackThanks(req *http.Request, basePage core.Page, lang, url, wholeSite string) model.Feedback { + p := model.Feedback{ + Page: basePage, + } + + p.Language = lang + p.Type = "feedback" + p.URI = req.URL.Path + p.Metadata.Title = helper.Localise("FeedbackThanks", lang, 1) + p.PreviousURL = url + + // returnTo is rendered on page so needs XSS protection + returnTo := html.EscapeString(req.URL.Query().Get("returnTo")) + if returnTo == "Whole site" { + returnTo = wholeSite + } else if returnTo == "" { + returnTo = url + } + + p.ReturnTo = returnTo + + return p +} diff --git a/mapper/mapper_test.go b/mapper/mapper_test.go index ad5eac3..774824b 100644 --- a/mapper/mapper_test.go +++ b/mapper/mapper_test.go @@ -1 +1,139 @@ package mapper + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/ONSdigital/dp-frontend-feedback-controller/mocks" + "github.com/ONSdigital/dp-frontend-feedback-controller/model" + "github.com/ONSdigital/dp-renderer/v2/helper" + core "github.com/ONSdigital/dp-renderer/v2/model" + . "github.com/smartystreets/goconvey/convey" +) + +func TestCreateGetFeedback(t *testing.T) { + helper.InitialiseLocalisationsHelper(mocks.MockAssetFunction) + Convey("Given a valid page request", t, func() { + Convey("When the parameters area valid", func() { + req := httptest.NewRequest(http.MethodGet, "/", nil) + bp := core.Page{} + validationErr := []core.ErrorItem{} + ff := model.FeedbackForm{} + lang := "en" + sut := CreateGetFeedback(req, bp, validationErr, ff, lang) + + Convey("Then it sets the page metadata", func() { + So(sut.Type, ShouldEqual, "feedback") + So(sut.Metadata.Title, ShouldEqual, "Feedback") + So(sut.Metadata.Description, ShouldEqual, ff.URL) + So(sut.Language, ShouldEqual, lang) + }) + + Convey("Then it maps the expected radio inputs", func() { + So(sut.TypeRadios, ShouldNotBeEmpty) + So(sut.TypeRadios.Radios, ShouldHaveLength, 2) + }) + + Convey("Then it maps the contact text inputs", func() { + So(sut.Contact, ShouldNotBeEmpty) + So(sut.Contact, ShouldHaveLength, 2) + }) + + Convey("Then it maps the description textarea input", func() { + So(sut.DescriptionField, ShouldNotBeEmpty) + }) + + Convey("Then it maps the previous url field", func() { + So(sut.PreviousURL, ShouldEqual, ff.URL) + }) + }) + + Convey("When a valid service parameter is passed", func() { + req := httptest.NewRequest(http.MethodGet, "/?service=cmd", nil) + bp := core.Page{} + validationErr := []core.ErrorItem{} + ff := model.FeedbackForm{} + lang := "en" + sut := CreateGetFeedback(req, bp, validationErr, ff, lang) + + Convey("Then it maps the additional radio input", func() { + So(sut.TypeRadios, ShouldNotBeEmpty) + So(sut.TypeRadios.Radios, ShouldHaveLength, 3) + }) + }) + + Convey("When validation errors are passed", func() { + req := httptest.NewRequest(http.MethodGet, "/", nil) + bp := core.Page{} + lang := "en" + validationErr := []core.ErrorItem{ + { + Description: core.Localisation{ + Text: "Error one", + }, + Language: lang, + ID: "error-one", + URL: "#error-one", + }, + } + ff := model.FeedbackForm{} + sut := CreateGetFeedback(req, bp, validationErr, ff, lang) + + Convey("Then it maps the error panel", func() { + So(sut.Error.Title, ShouldNotBeEmpty) + }) + + Convey("The error items are mapped", func() { + So(sut.Error.ErrorItems, ShouldResemble, validationErr) + }) + + ff.IsURLErr = true + sut = CreateGetFeedback(req, bp, validationErr, ff, lang) + Convey("Then it changes the radio validation field description", func() { + So(sut.TypeRadios.ValidationErr.ErrorItem.Description.Text, ShouldEqual, "Enter URL or name of the page") + }) + }) + }) +} + +func TestCreateGetFeedbackThanks(t *testing.T) { + helper.InitialiseLocalisationsHelper(mocks.MockAssetFunction) + Convey("Given a valid page request", t, func() { + Convey("When the parameters area valid", func() { + req := httptest.NewRequest(http.MethodGet, "/", nil) + bp := core.Page{} + url := "https://localhost/a/page/somewhere" + lang := "en" + wholeSite := "https://ons.gov.uk" + sut := CreateGetFeedbackThanks(req, bp, lang, url, wholeSite) + + Convey("Then it sets the page metadata", func() { + So(sut.Metadata.Title, ShouldEqual, "Thank you") + So(sut.Page.Type, ShouldEqual, "feedback") + So(sut.Language, ShouldEqual, lang) + }) + + Convey("Then it sets the previousUrl property", func() { + So(sut.PreviousURL, ShouldEqual, url) + }) + + Convey("Then it sets the returnTo property", func() { + So(sut.ReturnTo, ShouldEqual, url) + }) + }) + + Convey("When the return to parameter is set", func() { + req := httptest.NewRequest(http.MethodGet, "/?returnTo=Whole%20site", nil) + bp := core.Page{} + url := "https://localhost/a/page/somewhere" + lang := "en" + wholeSite := "https://ons.gov.uk" + sut := CreateGetFeedbackThanks(req, bp, lang, url, wholeSite) + + Convey("Then it sets the returnTo property", func() { + So(sut.ReturnTo, ShouldEqual, wholeSite) + }) + }) + }) +} diff --git a/mocks/mocks.go b/mocks/mocks.go new file mode 100644 index 0000000..53c8c2f --- /dev/null +++ b/mocks/mocks.go @@ -0,0 +1,22 @@ +package mocks + +import "strings" + +// English only as no Welsh translations exist +var enLocale = []string{ + "[FeedbackTitle]", + "one = \"Feedback\"", + "[FeedbackThanks]", + "one = \"Thank you\"", + "[FeedbackChooseType]", + "one = \"Choose feedback type\"", + "[FeedbackWhatOptNewService]", + "one = \"This service\"", + "[FeedbackWhatEnterURL]", + "one = \"Enter URL or name of the page\"", +} + +// MockAssetFunction returns mocked toml []bytes +func MockAssetFunction(name string) ([]byte, error) { //nolint:all // app does not use welsh + return []byte(strings.Join(enLocale, "\n")), nil +} diff --git a/model/model.go b/model/model.go index 9d11cb3..f18e5b8 100644 --- a/model/model.go +++ b/model/model.go @@ -1,15 +1,28 @@ package model -import "github.com/ONSdigital/dp-renderer/model" +import "github.com/ONSdigital/dp-renderer/v2/model" // Page contains data reused for feedback model type Feedback struct { model.Page - Radio string `json:"radio"` - Feedback string `json:"feedback"` - Name string `json:"name"` - Email string `json:"email"` - ErrorType string `json:"error_type"` - PreviousURL string `json:"previous_url"` - ServiceDescription string `json:"service_description"` + Contact []model.TextField `json:"contact"` + TypeRadios model.RadioFieldset `json:"type_radios"` + DescriptionField model.TextareaField `json:"description_field"` + PreviousURL string `json:"previous_url"` + ReturnTo string `json:"return_to"` +} + +// FeedbackForm represents the user feedback form +type FeedbackForm struct { + FormLocation string `schema:"feedback-form-type"` + Type string `schema:"type"` + IsTypeErr bool `schema:"is_type_err"` + URI string `schema:":uri"` + URL string `schema:"url"` + IsURLErr bool `schema:"is_url_err"` + Description string `schema:"description"` + IsDescriptionErr bool `schema:"is_description_err"` + Name string `schema:"name"` + Email string `schema:"email"` + IsEmailErr bool `schema:"is_email_err"` } diff --git a/routes/routes.go b/routes/routes.go index d4f78ea..ee8b0ab 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -10,7 +10,7 @@ import ( "github.com/ONSdigital/dp-frontend-feedback-controller/config" "github.com/ONSdigital/dp-frontend-feedback-controller/handlers" - render "github.com/ONSdigital/dp-renderer" + render "github.com/ONSdigital/dp-renderer/v2" cacheHelper "github.com/ONSdigital/dp-frontend-cache-helper/pkg/navigation/helper" health "github.com/ONSdigital/dp-healthcheck/healthcheck" @@ -36,12 +36,14 @@ func Setup(ctx context.Context, r *mux.Router, cfg *config.Config, rend *render. Auth: auth, } + f := handlers.NewFeedback(rend, cacheService, cfg, emailSender) + log.Info(ctx, "adding routes") r.StrictSlash(true).Path("/health").HandlerFunc(hc.Handler) - r.StrictSlash(true).Path("/feedback").Methods("GET").HandlerFunc(handlers.GetFeedback(rend, cacheService)) - r.StrictSlash(true).Path("/feedback").Methods("POST").HandlerFunc(handlers.AddFeedback(cfg.FeedbackTo, cfg.FeedbackFrom, false, rend, emailSender, cacheService)) - r.StrictSlash(true).Path("/feedback/thanks").Methods("GET").HandlerFunc(handlers.FeedbackThanks(rend, cacheService)) - r.StrictSlash(true).Path("/feedback/thanks").Methods("POST").HandlerFunc(handlers.AddFeedback(cfg.FeedbackTo, cfg.FeedbackFrom, false, rend, emailSender, cacheService)) + r.StrictSlash(true).Path("/feedback").Methods("GET").HandlerFunc(f.GetFeedback()) + r.StrictSlash(true).Path("/feedback").Methods("POST").HandlerFunc(f.AddFeedback()) + r.StrictSlash(true).Path("/feedback/thanks").Methods("GET").HandlerFunc(f.FeedbackThanks()) + r.StrictSlash(true).Path("/feedback/thanks").Methods("POST").HandlerFunc(f.AddFeedback()) } type unencryptedAuth struct {