mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-08 08:32:16 +01:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8f3f85fb7 | ||
|
|
159c0b3350 | ||
|
|
45788f3235 | ||
|
|
4c85c6ccdf | ||
|
|
08408ae005 | ||
|
|
d470279fb7 | ||
|
|
8260458f8f | ||
|
|
0512dd1e86 | ||
|
|
4972019cb9 | ||
|
|
5e615cab52 | ||
|
|
64ea2b6d3b | ||
|
|
27018fa8e2 | ||
|
|
263d634801 | ||
|
|
c61c33f71c |
2
.github/workflows/continuous_integration.yml
vendored
2
.github/workflows/continuous_integration.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
- name: Run linters
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
version: v1.44
|
||||
version: v1.48
|
||||
|
||||
tests:
|
||||
needs:
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Julien Neuhart
|
||||
Copyright (c) 2022 Julien Neuhart
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
4
Makefile
4
Makefile
@@ -5,14 +5,14 @@ help: ## Show the help
|
||||
.PHONY: it
|
||||
it: build build-tests ## Initialize the development environment
|
||||
|
||||
GOLANG_VERSION=1.17
|
||||
GOLANG_VERSION=1.19
|
||||
DOCKER_REPOSITORY=gotenberg
|
||||
GOTENBERG_VERSION=snapshot
|
||||
GOTENBERG_USER_GID=1001
|
||||
GOTENBERG_USER_UID=1001
|
||||
NOTO_COLOR_EMOJI_VERSION=v2.034 # See https://github.com/googlefonts/noto-emoji/releases.
|
||||
PDFTK_VERSION=v3.3.2 # See https://gitlab.com/pdftk-java/pdftk/-/releases - Binary package.
|
||||
GOLANGCI_LINT_VERSION=v1.44.0 # See https://github.com/golangci/golangci-lint/releases.
|
||||
GOLANGCI_LINT_VERSION=v1.48.0 # See https://github.com/golangci/golangci-lint/releases.
|
||||
|
||||
.PHONY: build
|
||||
build: ## Build the Gotenberg's Docker image
|
||||
|
||||
50
go.mod
50
go.mod
@@ -1,39 +1,39 @@
|
||||
module github.com/gotenberg/gotenberg/v7
|
||||
|
||||
go 1.17
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/alexliesenfeld/health v0.6.0
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/chromedp/cdproto v0.0.0-20220204211718-0109d952cae2
|
||||
github.com/chromedp/chromedp v0.7.7
|
||||
github.com/chromedp/cdproto v0.0.0-20220816211547-b8b15824df23
|
||||
github.com/chromedp/chromedp v0.8.4
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0
|
||||
github.com/klauspost/compress v1.14.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.1
|
||||
github.com/klauspost/compress v1.15.9 // indirect
|
||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||
github.com/labstack/echo/v4 v4.6.3
|
||||
github.com/labstack/echo/v4 v4.8.0
|
||||
github.com/labstack/gommon v0.3.1
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/microcosm-cc/bluemonday v1.0.18
|
||||
github.com/nwaples/rardecode v1.1.2 // indirect
|
||||
github.com/pdfcpu/pdfcpu v0.3.12
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/prometheus/client_golang v1.12.1
|
||||
github.com/microcosm-cc/bluemonday v1.0.19
|
||||
github.com/nwaples/rardecode v1.1.3 // indirect
|
||||
github.com/pdfcpu/pdfcpu v0.3.13
|
||||
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
||||
github.com/prometheus/client_golang v1.13.0
|
||||
github.com/russross/blackfriday/v2 v2.1.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.7.0
|
||||
go.uber.org/zap v1.21.0
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 // indirect
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect
|
||||
golang.org/x/net v0.0.0-20210913180222-943fd674d43e
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.8.0
|
||||
go.uber.org/zap v1.22.0
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 // indirect
|
||||
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 // indirect
|
||||
golang.org/x/net v0.0.0-20220812174116-3211cb980234
|
||||
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde
|
||||
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 // indirect
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035
|
||||
golang.org/x/text v0.3.7
|
||||
)
|
||||
|
||||
@@ -52,15 +52,15 @@ require (
|
||||
github.com/hhrutter/tiff v0.0.0-20190829141212-736cae8d0bc7 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
123
go.sum
123
go.sum
@@ -46,7 +46,6 @@ github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
@@ -55,11 +54,10 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chromedp/cdproto v0.0.0-20220131204822-e6abebe7b8cd/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U=
|
||||
github.com/chromedp/cdproto v0.0.0-20220204211718-0109d952cae2 h1:vr9QO2FB/MkXEv8BUja99AUwbJ32TuA+l64bAEmLqQI=
|
||||
github.com/chromedp/cdproto v0.0.0-20220204211718-0109d952cae2/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U=
|
||||
github.com/chromedp/chromedp v0.7.7 h1:kRN7G7v4cGpTV2Nth218YR2VOsbpHBN3qClYCb6CBnI=
|
||||
github.com/chromedp/chromedp v0.7.7/go.mod h1:cqexhZWjEbF8/cETF57b5dQ3rqrY6q71HcfYQ5oAy3k=
|
||||
github.com/chromedp/cdproto v0.0.0-20220816211547-b8b15824df23 h1:pXlKxzGxVoUO5B04CNAOC4ZHf5Rqhl/00pkoM97He1s=
|
||||
github.com/chromedp/cdproto v0.0.0-20220816211547-b8b15824df23/go.mod h1:5Y4sD/eXpwrChIuxhSr/G20n9CdbCmoerOHnuAf0Zr0=
|
||||
github.com/chromedp/chromedp v0.8.4 h1:50NSLCXC38kGsCmLCi8tXZ5V5FtQ3BOV/90u1O06Gq4=
|
||||
github.com/chromedp/chromedp v0.8.4/go.mod h1:ikuiJrEMoOnTPFUBu6jV0XZFOGW6W/Nhgk/OZyvh00o=
|
||||
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
|
||||
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
@@ -83,9 +81,11 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
@@ -94,7 +94,6 @@ github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm
|
||||
github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA=
|
||||
github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -136,8 +135,8 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
@@ -160,8 +159,8 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hhrutter/lzw v0.0.0-20190827003112-58b82c5a41cc/go.mod h1:yJBvOcu1wLQ9q9XZmfiPfur+3dQJuIhYQsMGLYcItZk=
|
||||
@@ -184,8 +183,8 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.14.2 h1:S0OHlFk/Gbon/yauFJ4FfJJF5V0fc5HbBTJazi28pRw=
|
||||
github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
|
||||
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
|
||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
@@ -197,23 +196,25 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/labstack/echo/v4 v4.6.3 h1:VhPuIZYxsbPmo4m9KAkMU/el2442eB7EBFFhNTTT9ac=
|
||||
github.com/labstack/echo/v4 v4.6.3/go.mod h1:Hk5OiHj0kDqmFq7aHe7eDqI7CUhuCrfpupQtLGGLm7A=
|
||||
github.com/labstack/echo/v4 v4.8.0 h1:wdc6yKVaHxkNOEdz4cRZs1pQkwSXPiRjq69yWP4QQS8=
|
||||
github.com/labstack/echo/v4 v4.8.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks=
|
||||
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
|
||||
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
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-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
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.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/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
|
||||
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
|
||||
github.com/microcosm-cc/bluemonday v1.0.18 h1:6HcxvXDAi3ARt3slx6nTesbvorIc3QeTzBNRvWktHBo=
|
||||
github.com/microcosm-cc/bluemonday v1.0.18/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM=
|
||||
github.com/microcosm-cc/bluemonday v1.0.19 h1:OI7hoF5FY4pFz2VA//RN8TfM0YJ2dJcl4P4APrCWy6c=
|
||||
github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
@@ -222,15 +223,14 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
github.com/nwaples/rardecode v1.1.2 h1:Cj0yZY6T1Zx1R7AhTbyGSALm44/Mmq+BAPc4B/p/d3M=
|
||||
github.com/nwaples/rardecode v1.1.2/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
github.com/orisano/pixelmatch v0.0.0-20210112091706-4fa4c7ba91d5 h1:1SoBaSPudixRecmlHXb/GxmaD3fLMtHIDN13QujwQuc=
|
||||
github.com/orisano/pixelmatch v0.0.0-20210112091706-4fa4c7ba91d5/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
||||
github.com/pdfcpu/pdfcpu v0.3.12 h1:B+MdKisilWNSk5OCO58Z9U6H93usH73xqk6hMOaZCls=
|
||||
github.com/pdfcpu/pdfcpu v0.3.12/go.mod h1:8XVBtVxuuIuSZL4Ez15Q4QoC+H8zeAaGnuiOEwAk8jA=
|
||||
github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
|
||||
github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw=
|
||||
github.com/pdfcpu/pdfcpu v0.3.13 h1:VFon2Yo1PJt+sA57vPAeXWGLSZ7Ux3Jl4h02M0+s3dg=
|
||||
github.com/pdfcpu/pdfcpu v0.3.13/go.mod h1:UJc5xsXg0fpmjp1zOPdyYcAQArc/Zf3V0nv5URe+9fg=
|
||||
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
|
||||
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -241,8 +241,9 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
|
||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
|
||||
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
@@ -251,14 +252,16 @@ github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
|
||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
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=
|
||||
@@ -288,30 +291,27 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMx
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
|
||||
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
|
||||
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
go.uber.org/zap v1.22.0 h1:Zcye5DUgBloQ9BaT4qc9BnjOFog5TvBSAGkJ3Nf70c0=
|
||||
go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8 h1:GIAS/yBem/gq2MUqgNIzUHW7cJMmx3TGZOrnyYaNQ6c=
|
||||
golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -325,9 +325,8 @@ golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMk
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20190823064033-3a9bac650e44/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 h1:/eM0PCrQI2xd471rI+snWuu251/+/jpBpZqir2mPdnU=
|
||||
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -346,7 +345,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -375,18 +373,18 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210913180222-943fd674d43e h1:+b/22bPvDYt4NPDcy4xAGCmON713ONAWFeY3Z7I3tR8=
|
||||
golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
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/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -396,8 +394,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc=
|
||||
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -432,20 +430,21 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/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-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/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-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27 h1:XDXtA5hveEEV8JB2l7nhMTp3t3cHp9ZpwcdjqyEWLlo=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U=
|
||||
golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/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 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -457,7 +456,6 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
@@ -498,11 +496,9 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
@@ -579,8 +575,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
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.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
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=
|
||||
@@ -591,13 +587,12 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
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.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.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
@@ -102,28 +102,28 @@ const (
|
||||
// Middleware is a middleware which can be added to the API's middlewares
|
||||
// chain.
|
||||
//
|
||||
// middleware := Middleware{
|
||||
// Handler: func() echo.MiddlewareFunc {
|
||||
// return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
// return func(c echo.Context) error {
|
||||
// rootPath := c.Get("rootPath").(string)
|
||||
// healthURI := fmt.Sprintf("%shealth", rootPath)
|
||||
// middleware := Middleware{
|
||||
// Handler: func() echo.MiddlewareFunc {
|
||||
// return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
// return func(c echo.Context) error {
|
||||
// rootPath := c.Get("rootPath").(string)
|
||||
// healthURI := fmt.Sprintf("%shealth", rootPath)
|
||||
//
|
||||
// // Skip the middleware if health check URI.
|
||||
// if c.Request().RequestURI == healthURI {
|
||||
// // Call the next middleware in the chain.
|
||||
// return next(c)
|
||||
// }
|
||||
// // Skip the middleware if health check URI.
|
||||
// if c.Request().RequestURI == healthURI {
|
||||
// // Call the next middleware in the chain.
|
||||
// return next(c)
|
||||
// }
|
||||
//
|
||||
// // Your middleware process.
|
||||
// // ...
|
||||
// // Your middleware process.
|
||||
// // ...
|
||||
//
|
||||
// // Call the next middleware in the chain.
|
||||
// return next(c)
|
||||
// }
|
||||
// }
|
||||
// }(),
|
||||
// }
|
||||
// // Call the next middleware in the chain.
|
||||
// return next(c)
|
||||
// }
|
||||
// }
|
||||
// }(),
|
||||
// }
|
||||
type Middleware struct {
|
||||
// Stack tells in which stack the middleware should be located.
|
||||
// Default to DefaultStack.
|
||||
|
||||
@@ -276,73 +276,3 @@ func (ctx Context) OutputFilename(outputPath string) string {
|
||||
|
||||
return fmt.Sprintf("%s%s", filename, filepath.Ext(outputPath))
|
||||
}
|
||||
|
||||
// TODO: move MockContext to mocks.go (and rename it ContextMock).
|
||||
|
||||
// MockContext is a helper for tests.
|
||||
//
|
||||
// ctx := &api.MockContext{Context: &api.Context{}}
|
||||
type MockContext struct {
|
||||
*Context
|
||||
}
|
||||
|
||||
// SetDirPath sets the context's working directory path.
|
||||
//
|
||||
// ctx := &api.MockContext{Context: &api.Context{}}
|
||||
// ctx.SetDirPath("/foo")
|
||||
func (ctx *MockContext) SetDirPath(path string) {
|
||||
ctx.dirPath = path
|
||||
}
|
||||
|
||||
// SetValues sets the values.
|
||||
//
|
||||
// ctx := &api.MockContext{Context: &api.Context{}}
|
||||
// ctx.SetValues(map[string][]string{
|
||||
// "url": {
|
||||
// "foo",
|
||||
// },
|
||||
// })
|
||||
func (ctx *MockContext) SetValues(values map[string][]string) {
|
||||
ctx.values = values
|
||||
}
|
||||
|
||||
// SetFiles sets the files.
|
||||
//
|
||||
// ctx := &api.MockContext{Context: &api.Context{}}
|
||||
// ctx.SetFiles(map[string]string{
|
||||
// "foo": "/foo",
|
||||
// })
|
||||
func (ctx *MockContext) SetFiles(files map[string]string) {
|
||||
ctx.files = files
|
||||
}
|
||||
|
||||
// SetCancelled sets if the context is cancelled or not.
|
||||
//
|
||||
// ctx := &api.MockContext{Context: &api.Context{}}
|
||||
// ctx.SetCancelled(true)
|
||||
func (ctx *MockContext) SetCancelled(cancelled bool) {
|
||||
ctx.cancelled = cancelled
|
||||
}
|
||||
|
||||
// OutputPaths returns the registered output paths.
|
||||
// ctx := &api.MockContext{Context: &api.Context{}}
|
||||
// outputPaths := ctx.OutputPaths()
|
||||
func (ctx MockContext) OutputPaths() []string {
|
||||
return ctx.outputPaths
|
||||
}
|
||||
|
||||
// SetLogger sets the logger.
|
||||
//
|
||||
// ctx := &api.MockContext{Context: &api.Context{}}
|
||||
// ctx.SetLogger(zap.NewNop())
|
||||
func (ctx *MockContext) SetLogger(logger *zap.Logger) {
|
||||
ctx.logger = logger
|
||||
}
|
||||
|
||||
// SetEchoContext sets the echo.Context.
|
||||
//
|
||||
// ctx := &api.MockContext{Context: &api.Context{}}
|
||||
// ctx.setEchoContext(c)
|
||||
func (ctx *MockContext) SetEchoContext(c echo.Context) {
|
||||
ctx.Context.echoCtx = c
|
||||
}
|
||||
|
||||
@@ -334,99 +334,3 @@ func TestContext_OutputFilename(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMockContext_SetDirPath(t *testing.T) {
|
||||
mock := &MockContext{&Context{}}
|
||||
mock.SetDirPath("/foo")
|
||||
|
||||
actual := mock.dirPath
|
||||
expect := "/foo"
|
||||
|
||||
if actual != expect {
|
||||
t.Errorf("expected '%s' but got '%s'", expect, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMockContext_SetValues(t *testing.T) {
|
||||
mock := &MockContext{&Context{}}
|
||||
mock.SetValues(map[string][]string{
|
||||
"foo": {"foo"},
|
||||
})
|
||||
|
||||
actual := mock.values
|
||||
expect := map[string][]string{
|
||||
"foo": {"foo"},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expect) {
|
||||
t.Errorf("expected %+v but got: %+v", expect, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMockContext_SetFiles(t *testing.T) {
|
||||
mock := &MockContext{&Context{}}
|
||||
mock.SetFiles(map[string]string{
|
||||
"foo": "/foo",
|
||||
})
|
||||
|
||||
actual := mock.files
|
||||
expect := map[string]string{
|
||||
"foo": "/foo",
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expect) {
|
||||
t.Errorf("expected %+v but got: %+v", expect, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMockContext_SetCancelled(t *testing.T) {
|
||||
mock := &MockContext{&Context{}}
|
||||
mock.SetCancelled(true)
|
||||
|
||||
actual := mock.cancelled
|
||||
|
||||
if !actual {
|
||||
t.Errorf("expected %t but got %t", true, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMockContext_OutputPaths(t *testing.T) {
|
||||
mock := MockContext{
|
||||
&Context{
|
||||
outputPaths: []string{"/foo"},
|
||||
},
|
||||
}
|
||||
|
||||
actual := mock.OutputPaths()
|
||||
expect := []string{"/foo"}
|
||||
|
||||
if !reflect.DeepEqual(actual, expect) {
|
||||
t.Errorf("expected %+v but got: %+v", expect, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMockContext_SetLogger(t *testing.T) {
|
||||
mock := MockContext{&Context{}}
|
||||
|
||||
expect := zap.NewNop()
|
||||
mock.SetLogger(expect)
|
||||
|
||||
actual := mock.logger
|
||||
|
||||
if actual != expect {
|
||||
t.Errorf("expected %v but got %v", expect, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMockContext_SetEchoContext(t *testing.T) {
|
||||
mock := MockContext{&Context{}}
|
||||
|
||||
expect := echo.New().NewContext(nil, nil)
|
||||
mock.SetEchoContext(expect)
|
||||
|
||||
actual := mock.echoCtx
|
||||
|
||||
if actual != expect {
|
||||
t.Errorf("expected %v but got %v", expect, actual)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,15 +52,15 @@ func (w sentinelWrappedError) HTTPError() (int, string) {
|
||||
// will be displayed in a log, while the SentinelHTTPError will be sent in the
|
||||
// response.
|
||||
//
|
||||
// return api.WrapError(
|
||||
// // This first error will be logged.
|
||||
// fmt.Errorf("my action: %w", err),
|
||||
// // The HTTP error will be sent as a response.
|
||||
// api.NewSentinelHTTPError(
|
||||
// http.StatusForbidden,
|
||||
// "Hey, you did something wrong!"
|
||||
// ),
|
||||
// )
|
||||
// return api.WrapError(
|
||||
// // This first error will be logged.
|
||||
// fmt.Errorf("my action: %w", err),
|
||||
// // The HTTP error will be sent as a response.
|
||||
// api.NewSentinelHTTPError(
|
||||
// http.StatusForbidden,
|
||||
// "Hey, you did something wrong!"
|
||||
// ),
|
||||
// )
|
||||
func WrapError(err error, sentinel SentinelHTTPError) error {
|
||||
return sentinelWrappedError{
|
||||
error: err,
|
||||
|
||||
@@ -2,8 +2,8 @@ package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
// FormData is a helper for validating and hydrating values from a
|
||||
// "multipart/form-data" request.
|
||||
//
|
||||
// form := ctx.FormData()
|
||||
// form := ctx.FormData()
|
||||
type FormData struct {
|
||||
values map[string][]string
|
||||
files map[string]string
|
||||
@@ -27,11 +27,11 @@ type FormData struct {
|
||||
// SentinelHTTPError (status code 400, errors' details as message) wrapped
|
||||
// inside.
|
||||
//
|
||||
// var foo string
|
||||
// var foo string
|
||||
//
|
||||
// err := ctx.FormData().
|
||||
// MandatoryString("foo", &foo, "bar").
|
||||
// Validate()
|
||||
// err := ctx.FormData().
|
||||
// MandatoryString("foo", &foo, "bar").
|
||||
// Validate()
|
||||
func (form FormData) Validate() error {
|
||||
if form.errors == nil {
|
||||
return nil
|
||||
@@ -45,9 +45,9 @@ func (form FormData) Validate() error {
|
||||
|
||||
// String binds a form data value to a string variable.
|
||||
//
|
||||
// var foo string
|
||||
// var foo string
|
||||
//
|
||||
// ctx.FormData().String("foo", &foo, "bar")
|
||||
// ctx.FormData().String("foo", &foo, "bar")
|
||||
func (form *FormData) String(key string, target *string, defaultValue string) *FormData {
|
||||
return form.mustValue(key, target, defaultValue)
|
||||
}
|
||||
@@ -55,9 +55,9 @@ func (form *FormData) String(key string, target *string, defaultValue string) *F
|
||||
// MandatoryString binds a form data value to a string variable. It populates
|
||||
// an error if the value is empty or the "key" does not exist.
|
||||
//
|
||||
// var foo string
|
||||
// var foo string
|
||||
//
|
||||
// ctx.FormData().MandatoryString("foo", &foo)
|
||||
// ctx.FormData().MandatoryString("foo", &foo)
|
||||
func (form *FormData) MandatoryString(key string, target *string) *FormData {
|
||||
return form.mustMandatoryValue(key, target)
|
||||
}
|
||||
@@ -65,9 +65,9 @@ func (form *FormData) MandatoryString(key string, target *string) *FormData {
|
||||
// Bool binds a form data value to a bool variable. It populates an error if
|
||||
// the value is not bool.
|
||||
//
|
||||
// var foo bool
|
||||
// var foo bool
|
||||
//
|
||||
// ctx.FormData().Bool("foo", &foo, true)
|
||||
// ctx.FormData().Bool("foo", &foo, true)
|
||||
func (form *FormData) Bool(key string, target *bool, defaultValue bool) *FormData {
|
||||
return form.mustValue(key, target, defaultValue)
|
||||
}
|
||||
@@ -75,9 +75,9 @@ func (form *FormData) Bool(key string, target *bool, defaultValue bool) *FormDat
|
||||
// MandatoryBool binds a form data value to a bool variable. It populates an
|
||||
// error if the value is not bool, is empty, or the "key" does not exist.
|
||||
//
|
||||
// var foo bool
|
||||
// var foo bool
|
||||
//
|
||||
// ctx.FormData().MandatoryBool("foo", &foo)
|
||||
// ctx.FormData().MandatoryBool("foo", &foo)
|
||||
func (form *FormData) MandatoryBool(key string, target *bool) *FormData {
|
||||
return form.mustMandatoryValue(key, target)
|
||||
}
|
||||
@@ -85,9 +85,9 @@ func (form *FormData) MandatoryBool(key string, target *bool) *FormData {
|
||||
// Int binds a form data value to an int variable. It populates an error if the
|
||||
// value is not int.
|
||||
//
|
||||
// var foo int
|
||||
// var foo int
|
||||
//
|
||||
// ctx.FormData().Int("foo", &foo, 2)
|
||||
// ctx.FormData().Int("foo", &foo, 2)
|
||||
func (form *FormData) Int(key string, target *int, defaultValue int) *FormData {
|
||||
return form.mustValue(key, target, defaultValue)
|
||||
}
|
||||
@@ -95,9 +95,9 @@ func (form *FormData) Int(key string, target *int, defaultValue int) *FormData {
|
||||
// MandatoryInt binds a form data value to an int variable. It populates an
|
||||
// error if the value is not int, is empty, or the "key" does not exist.
|
||||
//
|
||||
// var foo int
|
||||
// var foo int
|
||||
//
|
||||
// ctx.FormData().MandatoryInt("foo", &foo)
|
||||
// ctx.FormData().MandatoryInt("foo", &foo)
|
||||
func (form *FormData) MandatoryInt(key string, target *int) *FormData {
|
||||
return form.mustMandatoryValue(key, target)
|
||||
}
|
||||
@@ -105,9 +105,9 @@ func (form *FormData) MandatoryInt(key string, target *int) *FormData {
|
||||
// Float64 binds a form data value to a float64 variable. It populates an error
|
||||
// if the value is not float64.
|
||||
//
|
||||
// var foo float64
|
||||
// var foo float64
|
||||
//
|
||||
// ctx.FormData().Float64("foo", &foo, 2.0)
|
||||
// ctx.FormData().Float64("foo", &foo, 2.0)
|
||||
func (form *FormData) Float64(key string, target *float64, defaultValue float64) *FormData {
|
||||
return form.mustValue(key, target, defaultValue)
|
||||
}
|
||||
@@ -115,9 +115,9 @@ func (form *FormData) Float64(key string, target *float64, defaultValue float64)
|
||||
// MandatoryFloat64 binds a form data value to a float64 variable. It populates
|
||||
// an error if the is not float64, is empty, or the "key" does not exist.
|
||||
//
|
||||
// var foo float64
|
||||
// var foo float64
|
||||
//
|
||||
// ctx.FormData().MandatoryFloat64("foo", &foo)
|
||||
// ctx.FormData().MandatoryFloat64("foo", &foo)
|
||||
func (form *FormData) MandatoryFloat64(key string, target *float64) *FormData {
|
||||
return form.mustMandatoryValue(key, target)
|
||||
}
|
||||
@@ -125,9 +125,9 @@ func (form *FormData) MandatoryFloat64(key string, target *float64) *FormData {
|
||||
// Duration binds a form data value to a time.Duration variable. It populates
|
||||
// an error if the form data value is not time.Duration.
|
||||
//
|
||||
// var foo time.Duration
|
||||
// var foo time.Duration
|
||||
//
|
||||
// ctx.FormData().Duration("foo", &foo, time.Duration(2) * time.Second)
|
||||
// ctx.FormData().Duration("foo", &foo, time.Duration(2) * time.Second)
|
||||
func (form *FormData) Duration(key string, target *time.Duration, defaultValue time.Duration) *FormData {
|
||||
return form.mustValue(key, target, defaultValue)
|
||||
}
|
||||
@@ -136,31 +136,31 @@ func (form *FormData) Duration(key string, target *time.Duration, defaultValue t
|
||||
// populates an error if the value is not time.Duration, is empty, or the "key"
|
||||
// does not exist.
|
||||
//
|
||||
// var foo time.Duration
|
||||
// var foo time.Duration
|
||||
//
|
||||
// ctx.FormData().MandatoryDuration("foo", &foo)
|
||||
// ctx.FormData().MandatoryDuration("foo", &foo)
|
||||
func (form *FormData) MandatoryDuration(key string, target *time.Duration) *FormData {
|
||||
return form.mustMandatoryValue(key, target)
|
||||
}
|
||||
|
||||
// Custom helps to define a custom binding function for a form data value.
|
||||
//
|
||||
// var foo map[string]string
|
||||
// var foo map[string]string
|
||||
//
|
||||
// ctx.FormData().Custom("foo", func(value string) error {
|
||||
// if value == "" {
|
||||
// foo = "bar"
|
||||
// ctx.FormData().Custom("foo", func(value string) error {
|
||||
// if value == "" {
|
||||
// foo = "bar"
|
||||
//
|
||||
// return nil
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// err := json.Unmarshal([]byte(value), &foo)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("unmarshal foo: %w", err)
|
||||
// }
|
||||
// err := json.Unmarshal([]byte(value), &foo)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("unmarshal foo: %w", err)
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
// })
|
||||
// return nil
|
||||
// })
|
||||
func (form *FormData) Custom(key string, assign func(value string) error) *FormData {
|
||||
var value string
|
||||
form.mustValue(key, &value, "")
|
||||
@@ -179,16 +179,16 @@ func (form *FormData) Custom(key string, assign func(value string) error) *FormD
|
||||
// value. It populates an error if the value is empty or the "key" does not
|
||||
// exist.
|
||||
//
|
||||
// var foo map[string]string
|
||||
// var foo map[string]string
|
||||
//
|
||||
// ctx.FormData().MandatoryCustom("foo", func(value string) error {
|
||||
// err := json.Unmarshal([]byte(value), &foo)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("unmarshal foo: %w", err)
|
||||
// }
|
||||
// ctx.FormData().MandatoryCustom("foo", func(value string) error {
|
||||
// err := json.Unmarshal([]byte(value), &foo)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("unmarshal foo: %w", err)
|
||||
// }
|
||||
//
|
||||
// return nil
|
||||
// })
|
||||
// return nil
|
||||
// })
|
||||
func (form *FormData) MandatoryCustom(key string, assign func(value string) error) *FormData {
|
||||
var value string
|
||||
form.mustMandatoryValue(key, &value)
|
||||
@@ -209,9 +209,9 @@ func (form *FormData) MandatoryCustom(key string, assign func(value string) erro
|
||||
|
||||
// Path binds the absolute path of a form data file to a string variable.
|
||||
//
|
||||
// var path string
|
||||
// var path string
|
||||
//
|
||||
// ctx.FormData().Path("foo.txt", &path)
|
||||
// ctx.FormData().Path("foo.txt", &path)
|
||||
func (form *FormData) Path(filename string, target *string) *FormData {
|
||||
return form.path(filename, target)
|
||||
}
|
||||
@@ -219,18 +219,18 @@ func (form *FormData) Path(filename string, target *string) *FormData {
|
||||
// MandatoryPath binds the absolute path ofa form data file to a string
|
||||
// variable. It populates an error if the file does not exist.
|
||||
//
|
||||
// var path string
|
||||
// var path string
|
||||
//
|
||||
// ctx.FormData().MandatoryPath("foo.txt", &path)
|
||||
// ctx.FormData().MandatoryPath("foo.txt", &path)
|
||||
func (form *FormData) MandatoryPath(filename string, target *string) *FormData {
|
||||
return form.mandatoryPath(filename, target)
|
||||
}
|
||||
|
||||
// Content binds the content of a form data file to a string variable.
|
||||
//
|
||||
// var content string
|
||||
// var content string
|
||||
//
|
||||
// ctx.FormData().Content("foo.txt", &content, "bar")
|
||||
// ctx.FormData().Content("foo.txt", &content, "bar")
|
||||
func (form *FormData) Content(filename string, target *string, defaultValue string) *FormData {
|
||||
var path string
|
||||
form.path(filename, &path)
|
||||
@@ -247,9 +247,9 @@ func (form *FormData) Content(filename string, target *string, defaultValue stri
|
||||
// MandatoryContent binds the content of a form data file to a string variable.
|
||||
// It populates an error if the file does not exist.
|
||||
//
|
||||
// var content string
|
||||
// var content string
|
||||
//
|
||||
// ctx.FormData().MandatoryContent("foo.txt", &content)
|
||||
// ctx.FormData().MandatoryContent("foo.txt", &content)
|
||||
func (form *FormData) MandatoryContent(filename string, target *string) *FormData {
|
||||
var path string
|
||||
form.mandatoryPath(filename, &path)
|
||||
@@ -264,9 +264,9 @@ func (form *FormData) MandatoryContent(filename string, target *string) *FormDat
|
||||
// Paths binds the absolute paths of form data files, according to a list of
|
||||
// file extensions, to a string slice variable.
|
||||
//
|
||||
// var paths []string
|
||||
// var paths []string
|
||||
//
|
||||
// ctx.FormData().Paths([]string{".txt"}, &paths)
|
||||
// ctx.FormData().Paths([]string{".txt"}, &paths)
|
||||
func (form *FormData) Paths(extensions []string, target *[]string) *FormData {
|
||||
return form.paths(extensions, target)
|
||||
}
|
||||
@@ -275,9 +275,9 @@ func (form *FormData) Paths(extensions []string, target *[]string) *FormData {
|
||||
// list of file extensions, to a string slice variable. It populates an error
|
||||
// if there is no file for given file extensions.
|
||||
//
|
||||
// var paths []string
|
||||
// var paths []string
|
||||
//
|
||||
// ctx.FormData().MandatoryPaths([]string{".txt"}, &paths)
|
||||
// ctx.FormData().MandatoryPaths([]string{".txt"}, &paths)
|
||||
func (form *FormData) MandatoryPaths(extensions []string, target *[]string) *FormData {
|
||||
form.paths(extensions, target)
|
||||
|
||||
@@ -426,7 +426,7 @@ func (form *FormData) mandatoryPath(filename string, target *string) *FormData {
|
||||
// readFile binds the content of a file to a string variable. It populates an
|
||||
// error if it fails to read the file content.
|
||||
func (form *FormData) readFile(path, filename string, target *string) *FormData {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
form.append(
|
||||
fmt.Errorf("form file '%s' is invalid (%w)", filename, err),
|
||||
|
||||
@@ -57,7 +57,7 @@ func httpErrorHandler() echo.HTTPErrorHandler {
|
||||
// latencyMiddleware sets the start time in the echo.Context under "startTime".
|
||||
// Its value will be used later to calculate a request latency.
|
||||
//
|
||||
// startTime := c.Get("startTime").(time.Time)
|
||||
// startTime := c.Get("startTime").(time.Time)
|
||||
func latencyMiddleware() echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
@@ -75,14 +75,14 @@ func latencyMiddleware() echo.MiddlewareFunc {
|
||||
// Its value may be used to skip a middleware execution based on a request
|
||||
// URI.
|
||||
//
|
||||
// rootPath := c.Get("rootPath").(string)
|
||||
// healthURI := fmt.Sprintf("%s/health", rootPath)
|
||||
// rootPath := c.Get("rootPath").(string)
|
||||
// healthURI := fmt.Sprintf("%s/health", rootPath)
|
||||
//
|
||||
// // Skip the middleware if health check URI.
|
||||
// if c.Request().RequestURI == healthURI {
|
||||
// // Call the next middleware in the chain.
|
||||
// return next(c)
|
||||
// }
|
||||
// // Skip the middleware if health check URI.
|
||||
// if c.Request().RequestURI == healthURI {
|
||||
// // Call the next middleware in the chain.
|
||||
// return next(c)
|
||||
// }
|
||||
func rootPathMiddleware(rootPath string) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
@@ -98,8 +98,8 @@ func rootPathMiddleware(rootPath string) echo.MiddlewareFunc {
|
||||
// "trace". Its value is either retrieved from the trace header or generated if
|
||||
// the header is not present / its value is empty.
|
||||
//
|
||||
// trace := c.Get("trace").(string)
|
||||
// traceHeader := c.Get("traceHeader").(string).
|
||||
// trace := c.Get("trace").(string)
|
||||
// traceHeader := c.Get("traceHeader").(string).
|
||||
func traceMiddleware(header string) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
@@ -123,7 +123,7 @@ func traceMiddleware(header string) echo.MiddlewareFunc {
|
||||
// loggerMiddleware sets the logger in the echo.Context under "logger" and logs
|
||||
// a synchronous request result.
|
||||
//
|
||||
// logger := c.Get("logger").(*zap.Logger)
|
||||
// logger := c.Get("logger").(*zap.Logger)
|
||||
func loggerMiddleware(logger *zap.Logger, disableLoggingForPaths []string) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
@@ -197,8 +197,8 @@ func loggerMiddleware(logger *zap.Logger, disableLoggingForPaths []string) echo.
|
||||
// and "cancel". If the process is synchronous, it also handles the result of a
|
||||
// "multipart/form-data" request.
|
||||
//
|
||||
// ctx := c.Get("context").(*api.Context)
|
||||
// cancel := c.Get("cancel").(context.CancelFunc)
|
||||
// ctx := c.Get("context").(*api.Context)
|
||||
// cancel := c.Get("cancel").(context.CancelFunc)
|
||||
func contextMiddleware(timeout time.Duration) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
75
pkg/modules/api/mocks.go
Normal file
75
pkg/modules/api/mocks.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// ContextMock is a helper for tests.
|
||||
//
|
||||
// ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
type ContextMock struct {
|
||||
*Context
|
||||
}
|
||||
|
||||
// SetDirPath sets the context's working directory path.
|
||||
//
|
||||
// ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
// ctx.SetDirPath("/foo")
|
||||
func (ctx *ContextMock) SetDirPath(path string) {
|
||||
ctx.dirPath = path
|
||||
}
|
||||
|
||||
// SetValues sets the values.
|
||||
//
|
||||
// ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
// ctx.SetValues(map[string][]string{
|
||||
// "url": {
|
||||
// "foo",
|
||||
// },
|
||||
// })
|
||||
func (ctx *ContextMock) SetValues(values map[string][]string) {
|
||||
ctx.values = values
|
||||
}
|
||||
|
||||
// SetFiles sets the files.
|
||||
//
|
||||
// ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
// ctx.SetFiles(map[string]string{
|
||||
// "foo": "/foo",
|
||||
// })
|
||||
func (ctx *ContextMock) SetFiles(files map[string]string) {
|
||||
ctx.files = files
|
||||
}
|
||||
|
||||
// SetCancelled sets if the context is cancelled or not.
|
||||
//
|
||||
// ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
// ctx.SetCancelled(true)
|
||||
func (ctx *ContextMock) SetCancelled(cancelled bool) {
|
||||
ctx.cancelled = cancelled
|
||||
}
|
||||
|
||||
// OutputPaths returns the registered output paths.
|
||||
//
|
||||
// ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
// outputPaths := ctx.OutputPaths()
|
||||
func (ctx ContextMock) OutputPaths() []string {
|
||||
return ctx.outputPaths
|
||||
}
|
||||
|
||||
// SetLogger sets the logger.
|
||||
//
|
||||
// ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
// ctx.SetLogger(zap.NewNop())
|
||||
func (ctx *ContextMock) SetLogger(logger *zap.Logger) {
|
||||
ctx.logger = logger
|
||||
}
|
||||
|
||||
// SetEchoContext sets the echo.Context.
|
||||
//
|
||||
// ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
// ctx.setEchoContext(c)
|
||||
func (ctx *ContextMock) SetEchoContext(c echo.Context) {
|
||||
ctx.Context.echoCtx = c
|
||||
}
|
||||
105
pkg/modules/api/mocks_test.go
Normal file
105
pkg/modules/api/mocks_test.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func TestContextMock_SetDirPath(t *testing.T) {
|
||||
mock := &ContextMock{&Context{}}
|
||||
mock.SetDirPath("/foo")
|
||||
|
||||
actual := mock.dirPath
|
||||
expect := "/foo"
|
||||
|
||||
if actual != expect {
|
||||
t.Errorf("expected '%s' but got '%s'", expect, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextMock_SetValues(t *testing.T) {
|
||||
mock := &ContextMock{&Context{}}
|
||||
mock.SetValues(map[string][]string{
|
||||
"foo": {"foo"},
|
||||
})
|
||||
|
||||
actual := mock.values
|
||||
expect := map[string][]string{
|
||||
"foo": {"foo"},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expect) {
|
||||
t.Errorf("expected %+v but got: %+v", expect, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextMock_SetFiles(t *testing.T) {
|
||||
mock := &ContextMock{&Context{}}
|
||||
mock.SetFiles(map[string]string{
|
||||
"foo": "/foo",
|
||||
})
|
||||
|
||||
actual := mock.files
|
||||
expect := map[string]string{
|
||||
"foo": "/foo",
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(actual, expect) {
|
||||
t.Errorf("expected %+v but got: %+v", expect, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextMock_SetCancelled(t *testing.T) {
|
||||
mock := &ContextMock{&Context{}}
|
||||
mock.SetCancelled(true)
|
||||
|
||||
actual := mock.cancelled
|
||||
|
||||
if !actual {
|
||||
t.Errorf("expected %t but got %t", true, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextMock_OutputPaths(t *testing.T) {
|
||||
mock := ContextMock{
|
||||
&Context{
|
||||
outputPaths: []string{"/foo"},
|
||||
},
|
||||
}
|
||||
|
||||
actual := mock.OutputPaths()
|
||||
expect := []string{"/foo"}
|
||||
|
||||
if !reflect.DeepEqual(actual, expect) {
|
||||
t.Errorf("expected %+v but got: %+v", expect, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextMock_SetLogger(t *testing.T) {
|
||||
mock := ContextMock{&Context{}}
|
||||
|
||||
expect := zap.NewNop()
|
||||
mock.SetLogger(expect)
|
||||
|
||||
actual := mock.logger
|
||||
|
||||
if actual != expect {
|
||||
t.Errorf("expected %v but got %v", expect, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextMock_SetEchoContext(t *testing.T) {
|
||||
mock := ContextMock{&Context{}}
|
||||
|
||||
expect := echo.New().NewContext(nil, nil)
|
||||
mock.SetEchoContext(expect)
|
||||
|
||||
actual := mock.echoCtx
|
||||
|
||||
if actual != expect {
|
||||
t.Errorf("expected %v but got %v", expect, actual)
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -741,7 +740,6 @@ func (mod Chromium) PDF(ctx context.Context, logger *zap.Logger, URL, outputPath
|
||||
WithMarginBottom(options.MarginBottom).
|
||||
WithMarginLeft(options.MarginLeft).
|
||||
WithMarginRight(options.MarginRight).
|
||||
WithIgnoreInvalidPageRanges(false).
|
||||
WithPageRanges(options.PageRanges).
|
||||
WithDisplayHeaderFooter(true).
|
||||
WithHeaderTemplate(options.HeaderTemplate).
|
||||
@@ -809,7 +807,7 @@ func (mod Chromium) PDF(ctx context.Context, logger *zap.Logger, URL, outputPath
|
||||
return fmt.Errorf("%v: %w", consoleExceptions, ErrConsoleExceptions)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(outputPath, buffer, 0600)
|
||||
err = os.WriteFile(outputPath, buffer, 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("write result to output path: %w", err)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -271,7 +270,7 @@ func convertMarkdownRoute(chromium API, engine gotenberg.PDFEngine) api.Route {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(path)
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("read markdown file '%s': %w", filename, err)
|
||||
}
|
||||
|
||||
@@ -16,16 +16,16 @@ import (
|
||||
|
||||
func TestFormDataChromiumPDFOptions(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
ctx *api.MockContext
|
||||
ctx *api.ContextMock
|
||||
options Options
|
||||
}{
|
||||
{
|
||||
ctx: &api.MockContext{Context: &api.Context{}},
|
||||
ctx: &api.ContextMock{Context: &api.Context{}},
|
||||
options: DefaultOptions(),
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetValues(map[string][]string{
|
||||
"extraHttpHeaders": {
|
||||
"foo",
|
||||
@@ -37,8 +37,8 @@ func TestFormDataChromiumPDFOptions(t *testing.T) {
|
||||
options: DefaultOptions(),
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetValues(map[string][]string{
|
||||
"extraHttpHeaders": {
|
||||
`{"foo":"bar"}`,
|
||||
@@ -57,8 +57,8 @@ func TestFormDataChromiumPDFOptions(t *testing.T) {
|
||||
}(),
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetValues(map[string][]string{
|
||||
"extraHttpHeaders": {
|
||||
"foo",
|
||||
@@ -70,8 +70,8 @@ func TestFormDataChromiumPDFOptions(t *testing.T) {
|
||||
options: DefaultOptions(),
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetValues(map[string][]string{
|
||||
"emulatedMediaType": {
|
||||
"foo",
|
||||
@@ -83,8 +83,8 @@ func TestFormDataChromiumPDFOptions(t *testing.T) {
|
||||
options: DefaultOptions(),
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetValues(map[string][]string{
|
||||
"emulatedMediaType": {
|
||||
"screen",
|
||||
@@ -111,7 +111,7 @@ func TestFormDataChromiumPDFOptions(t *testing.T) {
|
||||
|
||||
func TestConvertURLHandler(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
ctx *api.MockContext
|
||||
ctx *api.ContextMock
|
||||
api API
|
||||
expectErr bool
|
||||
expectHTTPErr bool
|
||||
@@ -119,14 +119,14 @@ func TestConvertURLHandler(t *testing.T) {
|
||||
expectOutputPathsCount int
|
||||
}{
|
||||
{
|
||||
ctx: &api.MockContext{Context: &api.Context{}},
|
||||
ctx: &api.ContextMock{Context: &api.Context{}},
|
||||
expectErr: true,
|
||||
expectHTTPErr: true,
|
||||
expectHTTPStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetValues(map[string][]string{
|
||||
"url": {
|
||||
"",
|
||||
@@ -140,8 +140,8 @@ func TestConvertURLHandler(t *testing.T) {
|
||||
expectHTTPStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetValues(map[string][]string{
|
||||
"url": {
|
||||
"foo",
|
||||
@@ -161,8 +161,8 @@ func TestConvertURLHandler(t *testing.T) {
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetValues(map[string][]string{
|
||||
"url": {
|
||||
"foo",
|
||||
@@ -182,8 +182,8 @@ func TestConvertURLHandler(t *testing.T) {
|
||||
expectOutputPathsCount: 1,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetValues(map[string][]string{
|
||||
"url": {
|
||||
"foo",
|
||||
@@ -208,8 +208,8 @@ func TestConvertURLHandler(t *testing.T) {
|
||||
expectHTTPStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetValues(map[string][]string{
|
||||
"url": {
|
||||
"foo",
|
||||
@@ -234,8 +234,8 @@ func TestConvertURLHandler(t *testing.T) {
|
||||
expectHTTPStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetValues(map[string][]string{
|
||||
"url": {
|
||||
"foo",
|
||||
@@ -300,7 +300,7 @@ func TestConvertURLHandler(t *testing.T) {
|
||||
|
||||
func TestConvertHTMLHandler(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
ctx *api.MockContext
|
||||
ctx *api.ContextMock
|
||||
api API
|
||||
expectErr bool
|
||||
expectHTTPErr bool
|
||||
@@ -308,14 +308,14 @@ func TestConvertHTMLHandler(t *testing.T) {
|
||||
expectOutputPathsCount int
|
||||
}{
|
||||
{
|
||||
ctx: &api.MockContext{Context: &api.Context{}},
|
||||
ctx: &api.ContextMock{Context: &api.Context{}},
|
||||
expectErr: true,
|
||||
expectHTTPErr: true,
|
||||
expectHTTPStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.html": "/foo/foo.html",
|
||||
})
|
||||
@@ -327,8 +327,8 @@ func TestConvertHTMLHandler(t *testing.T) {
|
||||
expectHTTPStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"index.html": "/foo/foo.html",
|
||||
})
|
||||
@@ -346,8 +346,8 @@ func TestConvertHTMLHandler(t *testing.T) {
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"index.html": "/foo/foo.html",
|
||||
})
|
||||
@@ -404,7 +404,7 @@ func TestConvertHTMLHandler(t *testing.T) {
|
||||
|
||||
func TestConvertMarkdownHandler(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
ctx *api.MockContext
|
||||
ctx *api.ContextMock
|
||||
api API
|
||||
outputDir string
|
||||
expectErr bool
|
||||
@@ -413,14 +413,14 @@ func TestConvertMarkdownHandler(t *testing.T) {
|
||||
expectOutputPathsCount int
|
||||
}{
|
||||
{
|
||||
ctx: &api.MockContext{Context: &api.Context{}},
|
||||
ctx: &api.ContextMock{Context: &api.Context{}},
|
||||
expectErr: true,
|
||||
expectHTTPErr: true,
|
||||
expectHTTPStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.html": "/foo/foo.html",
|
||||
})
|
||||
@@ -432,8 +432,8 @@ func TestConvertMarkdownHandler(t *testing.T) {
|
||||
expectHTTPStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"index.html": "/foo/foo.html",
|
||||
})
|
||||
@@ -445,8 +445,8 @@ func TestConvertMarkdownHandler(t *testing.T) {
|
||||
expectHTTPStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"index.html": "/foo/foo.html",
|
||||
"markdown.md": "/foo/markdown.md",
|
||||
@@ -457,8 +457,8 @@ func TestConvertMarkdownHandler(t *testing.T) {
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"index.html": "/tests/test/testdata/chromium/markdown/sample2/index.html",
|
||||
"markdown1.md": "/foo/markdown1.md",
|
||||
@@ -471,8 +471,8 @@ func TestConvertMarkdownHandler(t *testing.T) {
|
||||
expectHTTPStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"index.html": "/tests/test/testdata/chromium/markdown/sample1/index.html",
|
||||
"markdown1.md": "/foo/markdown1.md",
|
||||
@@ -491,8 +491,8 @@ func TestConvertMarkdownHandler(t *testing.T) {
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"index.html": "/tests/test/testdata/chromium/markdown/sample1/index.html",
|
||||
"markdown1.md": "/tests/test/testdata/chromium/markdown/sample1/markdown1.md",
|
||||
@@ -513,8 +513,8 @@ func TestConvertMarkdownHandler(t *testing.T) {
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
|
||||
ctx.SetDirPath("/tmp/foo")
|
||||
ctx.SetFiles(map[string]string{
|
||||
@@ -538,8 +538,8 @@ func TestConvertMarkdownHandler(t *testing.T) {
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
|
||||
ctx.SetDirPath("/tmp/foo")
|
||||
ctx.SetFiles(map[string]string{
|
||||
@@ -563,8 +563,8 @@ func TestConvertMarkdownHandler(t *testing.T) {
|
||||
expectOutputPathsCount: 1,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
|
||||
ctx.SetDirPath("/tmp/foo")
|
||||
ctx.SetFiles(map[string]string{
|
||||
@@ -642,7 +642,7 @@ func TestConvertMarkdownHandler(t *testing.T) {
|
||||
|
||||
func TestConvertURL(t *testing.T) {
|
||||
for i, tc := range []struct {
|
||||
ctx *api.MockContext
|
||||
ctx *api.ContextMock
|
||||
api API
|
||||
engine gotenberg.PDFEngine
|
||||
PDFformat string
|
||||
@@ -653,7 +653,7 @@ func TestConvertURL(t *testing.T) {
|
||||
expectOutputPathsCount int
|
||||
}{
|
||||
{
|
||||
ctx: &api.MockContext{Context: &api.Context{}},
|
||||
ctx: &api.ContextMock{Context: &api.Context{}},
|
||||
api: func() API {
|
||||
chromiumAPI := struct{ ProtoAPI }{}
|
||||
chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, _ Options) error {
|
||||
@@ -668,7 +668,7 @@ func TestConvertURL(t *testing.T) {
|
||||
expectHTTPStatus: http.StatusForbidden,
|
||||
},
|
||||
{
|
||||
ctx: &api.MockContext{Context: &api.Context{}},
|
||||
ctx: &api.ContextMock{Context: &api.Context{}},
|
||||
api: func() API {
|
||||
chromiumAPI := struct{ ProtoAPI }{}
|
||||
chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, _ Options) error {
|
||||
@@ -681,7 +681,7 @@ func TestConvertURL(t *testing.T) {
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
ctx: &api.MockContext{Context: &api.Context{}},
|
||||
ctx: &api.ContextMock{Context: &api.Context{}},
|
||||
api: func() API {
|
||||
chromiumAPI := struct{ ProtoAPI }{}
|
||||
chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, _ Options) error {
|
||||
@@ -701,7 +701,7 @@ func TestConvertURL(t *testing.T) {
|
||||
expectHTTPStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
ctx: &api.MockContext{Context: &api.Context{}},
|
||||
ctx: &api.ContextMock{Context: &api.Context{}},
|
||||
api: func() API {
|
||||
chromiumAPI := struct{ ProtoAPI }{}
|
||||
chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, _ Options) error {
|
||||
@@ -716,7 +716,7 @@ func TestConvertURL(t *testing.T) {
|
||||
expectHTTPStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
ctx: &api.MockContext{Context: &api.Context{}},
|
||||
ctx: &api.ContextMock{Context: &api.Context{}},
|
||||
api: func() API {
|
||||
chromiumAPI := struct{ ProtoAPI }{}
|
||||
chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, _ Options) error {
|
||||
@@ -731,7 +731,7 @@ func TestConvertURL(t *testing.T) {
|
||||
expectHTTPStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
ctx: &api.MockContext{Context: &api.Context{}},
|
||||
ctx: &api.ContextMock{Context: &api.Context{}},
|
||||
api: func() API {
|
||||
chromiumAPI := struct{ ProtoAPI }{}
|
||||
chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, _ Options) error {
|
||||
@@ -746,7 +746,7 @@ func TestConvertURL(t *testing.T) {
|
||||
expectHTTPStatus: http.StatusConflict,
|
||||
},
|
||||
{
|
||||
ctx: &api.MockContext{Context: &api.Context{}},
|
||||
ctx: &api.ContextMock{Context: &api.Context{}},
|
||||
api: func() API {
|
||||
chromiumAPI := struct{ ProtoAPI }{}
|
||||
chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, _ Options) error {
|
||||
@@ -759,7 +759,7 @@ func TestConvertURL(t *testing.T) {
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
ctx: &api.MockContext{Context: &api.Context{}},
|
||||
ctx: &api.ContextMock{Context: &api.Context{}},
|
||||
api: func() API {
|
||||
chromiumAPI := struct{ ProtoAPI }{}
|
||||
chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, _ Options) error {
|
||||
@@ -782,7 +782,7 @@ func TestConvertURL(t *testing.T) {
|
||||
expectHTTPStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
ctx: &api.MockContext{Context: &api.Context{}},
|
||||
ctx: &api.ContextMock{Context: &api.Context{}},
|
||||
api: func() API {
|
||||
chromiumAPI := struct{ ProtoAPI }{}
|
||||
chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, _ Options) error {
|
||||
@@ -803,7 +803,7 @@ func TestConvertURL(t *testing.T) {
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
ctx: &api.MockContext{Context: &api.Context{}},
|
||||
ctx: &api.ContextMock{Context: &api.Context{}},
|
||||
api: func() API {
|
||||
chromiumAPI := struct{ ProtoAPI }{}
|
||||
chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, _ Options) error {
|
||||
@@ -824,8 +824,8 @@ func TestConvertURL(t *testing.T) {
|
||||
expectOutputPathsCount: 1,
|
||||
},
|
||||
{
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetCancelled(true)
|
||||
|
||||
return ctx
|
||||
@@ -842,7 +842,7 @@ func TestConvertURL(t *testing.T) {
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
ctx: &api.MockContext{Context: &api.Context{}},
|
||||
ctx: &api.ContextMock{Context: &api.Context{}},
|
||||
api: func() API {
|
||||
chromiumAPI := struct{ ProtoAPI }{}
|
||||
chromiumAPI.pdf = func(_ context.Context, _ *zap.Logger, _, _ string, _ Options) error {
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
func TestConvertHandler(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx *api.MockContext
|
||||
ctx *api.ContextMock
|
||||
unoAPI uno.API
|
||||
engine gotenberg.PDFEngine
|
||||
expectErr bool
|
||||
@@ -26,8 +26,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "nominal behavior",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.docx": "/foo/foo.docx",
|
||||
})
|
||||
@@ -48,8 +48,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "nominal behavior, but with 3 documents",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.docx": "/foo/foo.docx",
|
||||
"bar.docx": "/bar/bar.docx",
|
||||
@@ -72,8 +72,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "cannot add output paths",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.docx": "/foo/foo.docx",
|
||||
})
|
||||
@@ -95,7 +95,7 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "invalid form data: no documents",
|
||||
ctx: &api.MockContext{Context: &api.Context{}},
|
||||
ctx: &api.ContextMock{Context: &api.Context{}},
|
||||
unoAPI: uno.APIMock{
|
||||
ExtensionsMock: func() []string {
|
||||
return []string{
|
||||
@@ -109,8 +109,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "invalid form data: both nativePdfA1aFormat and nativePdfFormat are set",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.docx": "/foo/foo.docx",
|
||||
})
|
||||
@@ -139,8 +139,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "invalid form data: both nativePdfA1aFormat and pdfFormat are set",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.docx": "/foo/foo.docx",
|
||||
})
|
||||
@@ -169,8 +169,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "invalid form data: both nativePdfFormat and pdfFormat are set",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.docx": "/foo/foo.docx",
|
||||
})
|
||||
@@ -198,8 +198,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "convert to PDF fail",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.docx": "/foo/foo.docx",
|
||||
})
|
||||
@@ -220,8 +220,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "invalid page ranges",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.docx": "/foo/foo.docx",
|
||||
})
|
||||
@@ -244,8 +244,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "convert 3 documents and merge them",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.docx": "/foo/foo.docx",
|
||||
"bar.docx": "/bar/bar.docx",
|
||||
@@ -278,8 +278,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "merge fail",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.docx": "/foo/foo.docx",
|
||||
"bar.docx": "/bar/bar.docx",
|
||||
@@ -312,8 +312,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "convert 3 documents, merge them, and convert them to a PDF format",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.docx": "/foo/foo.docx",
|
||||
"bar.docx": "/bar/bar.docx",
|
||||
@@ -352,8 +352,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "convert 3 documents, merge them, but convert them to PDF format fail",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.docx": "/foo/foo.docx",
|
||||
"bar.docx": "/bar/bar.docx",
|
||||
@@ -392,8 +392,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "convert 3 documents, merge them, but PDF format not available",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.docx": "/foo/foo.docx",
|
||||
"bar.docx": "/bar/bar.docx",
|
||||
@@ -434,8 +434,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "convert 3 documents and merge them, but cannot add output paths",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.docx": "/foo/foo.docx",
|
||||
"bar.docx": "/bar/bar.docx",
|
||||
@@ -469,8 +469,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "convert to PDF format",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.docx": "/foo/foo.docx",
|
||||
})
|
||||
@@ -501,8 +501,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "convert to PDF format using nativePdfA1aFormat",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.docx": "/foo/foo.docx",
|
||||
})
|
||||
@@ -534,8 +534,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "convert to PDF format fail",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.docx": "/foo/foo.docx",
|
||||
})
|
||||
@@ -566,8 +566,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "PDF format not available",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.docx": "/foo/foo.docx",
|
||||
})
|
||||
|
||||
@@ -23,6 +23,7 @@ type listener interface {
|
||||
healthy() bool
|
||||
}
|
||||
|
||||
// TODO: this implementation, even if it's working, is way too complex.
|
||||
type libreOfficeListener struct {
|
||||
binPath string
|
||||
startTimeout time.Duration
|
||||
@@ -33,13 +34,15 @@ type libreOfficeListener struct {
|
||||
cmd gotenberg.Cmd
|
||||
cfgMu sync.RWMutex
|
||||
|
||||
usage int
|
||||
restarting bool
|
||||
restartingMu sync.RWMutex
|
||||
queueLength int
|
||||
queueLengthMu sync.RWMutex
|
||||
lockChan chan struct{}
|
||||
logger *zap.Logger
|
||||
usage int
|
||||
hadFirstStart bool
|
||||
hadFirstStartMu sync.RWMutex
|
||||
restarting bool
|
||||
restartingMu sync.RWMutex
|
||||
queueLength int
|
||||
queueLengthMu sync.RWMutex
|
||||
lockChan chan struct{}
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func newLibreOfficeListener(logger *zap.Logger, binPath string, startTimeout time.Duration, threshold int) listener {
|
||||
@@ -53,14 +56,17 @@ func newLibreOfficeListener(logger *zap.Logger, binPath string, startTimeout tim
|
||||
}
|
||||
|
||||
func (listener *libreOfficeListener) start(logger *zap.Logger) error {
|
||||
listener.hadFirstStartMu.Lock()
|
||||
listener.hadFirstStart = true
|
||||
listener.hadFirstStartMu.Unlock()
|
||||
|
||||
port, err := freePort(logger)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get free port: %w", err)
|
||||
}
|
||||
|
||||
// Good to know: when the supervisor manages the LibreOffice listener,
|
||||
// the garbage collector might delete the next directory while it is
|
||||
// still running. It does seem to cause any issue though.
|
||||
// Good to know: the garbage collector might delete the next directory
|
||||
// while it is still running. It does seem to cause any issue though.
|
||||
userProfileDirPath := gotenberg.NewDirPath()
|
||||
|
||||
args := []string{
|
||||
@@ -100,34 +106,77 @@ func (listener *libreOfficeListener) start(logger *zap.Logger) error {
|
||||
return fmt.Errorf("start LibreOffice listener: %w", err)
|
||||
}
|
||||
|
||||
// As the LibreOffice socket may take some time to be available, we have to
|
||||
// ensure that it is indeed accepting connections.
|
||||
logger.Debug("waiting for the LibreOffice listener socket to be available...")
|
||||
waitChan := make(chan error, 1)
|
||||
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
return fmt.Errorf("waiting for the LibreOffice listener socket to be available: %w", ctx.Err())
|
||||
}
|
||||
go func() {
|
||||
// By waiting the process, we avoid the creation of a zombie process
|
||||
// and make sure we catch an early exit if any.
|
||||
waitChan <- cmd.Wait()
|
||||
}()
|
||||
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", port), time.Duration(1)*time.Second)
|
||||
if err == nil {
|
||||
connChan := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
// As the LibreOffice socket may take some time to be available, we
|
||||
// have to ensure that it is indeed accepting connections.
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
connChan <- ctx.Err()
|
||||
break
|
||||
}
|
||||
|
||||
conn, err := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", port), time.Duration(1)*time.Second)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
connChan <- nil
|
||||
err = conn.Close()
|
||||
if err != nil {
|
||||
logger.Debug(fmt.Sprintf("close connection after health checking the LibreOffice listener: %v", err))
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}()
|
||||
|
||||
var success bool
|
||||
|
||||
defer func() {
|
||||
if success {
|
||||
listener.cfgMu.Lock()
|
||||
listener.socketPort = port
|
||||
listener.userProfileDirPath = userProfileDirPath
|
||||
listener.cmd = cmd
|
||||
listener.cfgMu.Unlock()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Let's make sure the process is killed.
|
||||
err = cmd.Kill()
|
||||
if err != nil {
|
||||
logger.Debug(fmt.Sprintf("kill LibreOffice listener process: %v", err))
|
||||
}
|
||||
}()
|
||||
|
||||
logger.Debug("waiting for the LibreOffice listener socket to be available...")
|
||||
|
||||
for {
|
||||
select {
|
||||
case err = <-connChan:
|
||||
if err != nil {
|
||||
return fmt.Errorf("LibreOffice listener socket not available: %w", err)
|
||||
}
|
||||
|
||||
logger.Debug("LibreOffice listener socket available")
|
||||
success = true
|
||||
|
||||
return nil
|
||||
case err = <-waitChan:
|
||||
return fmt.Errorf("LibreOffice listener process exited: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
logger.Debug("LibreOffice listener socket available")
|
||||
|
||||
listener.cfgMu.Lock()
|
||||
listener.socketPort = port
|
||||
listener.userProfileDirPath = userProfileDirPath
|
||||
listener.cmd = cmd
|
||||
listener.cfgMu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (listener *libreOfficeListener) stop(logger *zap.Logger) error {
|
||||
@@ -147,12 +196,6 @@ func (listener *libreOfficeListener) stop(logger *zap.Logger) error {
|
||||
return fmt.Errorf("kill LibreOffice listener process: %w", err)
|
||||
}
|
||||
|
||||
// Let's wait to make sure the process is no more.
|
||||
err = listener.cmd.Wait()
|
||||
if err != nil {
|
||||
logger.Debug(fmt.Sprintf("wait for the LibreOffice listener: %v", err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -187,33 +230,71 @@ func (listener *libreOfficeListener) lock(ctx context.Context, logger *zap.Logge
|
||||
listener.queueLength += 1
|
||||
listener.queueLengthMu.Unlock()
|
||||
|
||||
defer func() {
|
||||
listener.queueLengthMu.Lock()
|
||||
listener.queueLength -= 1
|
||||
listener.queueLengthMu.Unlock()
|
||||
}()
|
||||
|
||||
doWithContext := func(ctx context.Context, do func() error) error {
|
||||
doChan := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
doChan <- do()
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case err := <-doChan:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case listener.lockChan <- struct{}{}:
|
||||
logger.Debug("LibreOffice listener lock acquired")
|
||||
|
||||
listener.queueLengthMu.Lock()
|
||||
listener.queueLength -= 1
|
||||
listener.queueLengthMu.Unlock()
|
||||
listener.hadFirstStartMu.RLock()
|
||||
|
||||
if !listener.healthy() {
|
||||
logger.Debug("LibreOffice listener is unhealthy, restarting it...")
|
||||
if !listener.hadFirstStart {
|
||||
listener.hadFirstStartMu.RUnlock()
|
||||
|
||||
logger.Debug("starting LibreOffice listener...")
|
||||
|
||||
err := doWithContext(ctx, func() error {
|
||||
return listener.start(logger)
|
||||
})
|
||||
|
||||
err := listener.restart(logger)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("restart LibreOffice listener: %w", err)
|
||||
return fmt.Errorf("start long-running LibreOffice listener: %w", err)
|
||||
}
|
||||
|
||||
listener.hadFirstStartMu.RUnlock()
|
||||
|
||||
if !listener.healthy() {
|
||||
logger.Debug("LibreOffice listener is unhealthy, restarting it...")
|
||||
|
||||
err := doWithContext(ctx, func() error {
|
||||
return listener.restart(logger)
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("restart long-running LibreOffice listener: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
logger.Debug("failed to acquire LibreOffice listener lock before deadline")
|
||||
|
||||
listener.queueLengthMu.Lock()
|
||||
listener.queueLength -= 1
|
||||
listener.queueLengthMu.Unlock()
|
||||
|
||||
return fmt.Errorf("acquire LibreOffice listener lock: %w", ctx.Err())
|
||||
}
|
||||
}
|
||||
@@ -265,6 +346,13 @@ func (listener *libreOfficeListener) queue() int {
|
||||
}
|
||||
|
||||
func (listener *libreOfficeListener) healthy() bool {
|
||||
listener.hadFirstStartMu.RLock()
|
||||
defer listener.hadFirstStartMu.RUnlock()
|
||||
|
||||
if !listener.hadFirstStart {
|
||||
return true
|
||||
}
|
||||
|
||||
listener.restartingMu.RLock()
|
||||
defer listener.restartingMu.RUnlock()
|
||||
|
||||
|
||||
@@ -121,6 +121,14 @@ func TestListener_lock(t *testing.T) {
|
||||
return listener.stop(zap.NewNop())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "first start",
|
||||
listener: newLibreOfficeListener(zap.NewNop(), os.Getenv("LIBREOFFICE_BIN_PATH"), time.Duration(10)*time.Second, 10),
|
||||
ctx: context.Background(),
|
||||
teardown: func(listener listener) error {
|
||||
return listener.stop(zap.NewNop())
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unhealthy listener",
|
||||
listener: func() listener {
|
||||
@@ -390,6 +398,11 @@ func TestListener_healthy(t *testing.T) {
|
||||
logger: zap.NewNop(),
|
||||
}
|
||||
|
||||
// i.e., first start.
|
||||
if !listener.healthy() {
|
||||
t.Error("expected an healthy LibreOffice listener")
|
||||
}
|
||||
|
||||
err := listener.start(zap.NewNop())
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error from listener.start(), but got: %v", err)
|
||||
@@ -404,6 +417,8 @@ func TestListener_healthy(t *testing.T) {
|
||||
t.Fatalf("expected no error from listener.stop(), but got: %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Duration(1) * time.Second)
|
||||
|
||||
if listener.healthy() {
|
||||
t.Errorf("expected a non-healthy LibreOffice listener")
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ func (UNO) Descriptor() gotenberg.ModuleDescriptor {
|
||||
ID: "uno",
|
||||
FlagSet: func() *flag.FlagSet {
|
||||
fs := flag.NewFlagSet("uno", flag.ExitOnError)
|
||||
fs.Duration("uno-listener-start-timeout", time.Duration(10)*time.Second, "Time limit for starting the LibreOffice listener")
|
||||
fs.Duration("uno-listener-start-timeout", time.Duration(10)*time.Second, "Time limit for restarting the LibreOffice listener")
|
||||
fs.Int("uno-listener-restart-threshold", 10, "Conversions limit after which the LibreOffice listener is restarted - 0 means no long-running LibreOffice listener")
|
||||
fs.Bool("unoconv-disable-listener", false, "Do not start a long-running listener - save resources in detriment of unitary performance")
|
||||
|
||||
@@ -161,19 +161,11 @@ func (mod UNO) Validate() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Start starts the long-running LibreOffice listener if the threshold is
|
||||
// superior to zero.
|
||||
// Start does nothing: it is here to validate the contract from the
|
||||
// gotenberg.App interface. The long-running LibreOffice Listener will be
|
||||
// started on the first call to PDF.
|
||||
func (mod UNO) Start() error {
|
||||
if mod.libreOfficeRestartThreshold == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := mod.listener.start(mod.logger)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("start long-running LibreOffice listener: %w", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartupMessage returns a custom startup message.
|
||||
@@ -182,7 +174,7 @@ func (mod UNO) StartupMessage() string {
|
||||
return "long-running LibreOffice listener disabled"
|
||||
}
|
||||
|
||||
return "long-running LibreOffice listener started"
|
||||
return "long-running LibreOffice listener ready to start"
|
||||
}
|
||||
|
||||
// Stop stops the long-running LibreOffice Listener if it exists.
|
||||
@@ -202,7 +194,7 @@ func (mod UNO) Stop(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("stop long-running LibreOffice supervisor")
|
||||
return fmt.Errorf("stop long-running LibreOffice listener")
|
||||
}
|
||||
|
||||
// Metrics returns the metrics.
|
||||
|
||||
@@ -196,55 +196,7 @@ func TestUNO_Validate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUNO_Start(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
mod UNO
|
||||
expectStartErr bool
|
||||
}{
|
||||
{
|
||||
name: "nominal behavior",
|
||||
mod: UNO{
|
||||
libreOfficeRestartThreshold: 10,
|
||||
listener: listenerMock{
|
||||
startMock: func(logger *zap.Logger) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no long-running LibreOffice listener",
|
||||
mod: UNO{
|
||||
libreOfficeRestartThreshold: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "start error",
|
||||
mod: UNO{
|
||||
libreOfficeRestartThreshold: 10,
|
||||
listener: listenerMock{
|
||||
startMock: func(logger *zap.Logger) error {
|
||||
return errors.New("foo")
|
||||
},
|
||||
},
|
||||
},
|
||||
expectStartErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := tc.mod.Start()
|
||||
|
||||
if tc.expectStartErr && err == nil {
|
||||
t.Errorf("expected mod.Start() error, but got none")
|
||||
}
|
||||
|
||||
if !tc.expectStartErr && err != nil {
|
||||
t.Errorf("expected no error from mod.Start(), but got: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUNO_StartupMessage(t *testing.T) {
|
||||
@@ -254,11 +206,11 @@ func TestUNO_StartupMessage(t *testing.T) {
|
||||
expectMessage string
|
||||
}{
|
||||
{
|
||||
name: "long-running LibreOffice listener started",
|
||||
name: "long-running LibreOffice listener ready to start",
|
||||
mod: UNO{
|
||||
libreOfficeRestartThreshold: 10,
|
||||
},
|
||||
expectMessage: "long-running LibreOffice listener started",
|
||||
expectMessage: "long-running LibreOffice listener ready to start",
|
||||
},
|
||||
{
|
||||
name: "long-running LibreOffice listener disabled",
|
||||
@@ -549,11 +501,6 @@ func TestUNO_PDF(t *testing.T) {
|
||||
mod.libreOfficeRestartThreshold,
|
||||
)
|
||||
|
||||
err := mod.Start()
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error from mod.Start(), but got: %v", err)
|
||||
}
|
||||
|
||||
return mod
|
||||
}(),
|
||||
ctx: context.Background(),
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
func TestMergeHandler(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx *api.MockContext
|
||||
ctx *api.ContextMock
|
||||
engine gotenberg.PDFEngine
|
||||
expectErr bool
|
||||
expectHTTPErr bool
|
||||
@@ -24,8 +24,8 @@ func TestMergeHandler(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "nominal behavior",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.pdf": "/foo/foo.pdf",
|
||||
})
|
||||
@@ -41,15 +41,15 @@ func TestMergeHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "invalid form data: no PDF",
|
||||
ctx: &api.MockContext{Context: &api.Context{}},
|
||||
ctx: &api.ContextMock{Context: &api.Context{}},
|
||||
expectErr: true,
|
||||
expectHTTPErr: true,
|
||||
expectHTTPStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "merge fail",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.pdf": "/foo/foo.pdf",
|
||||
})
|
||||
@@ -65,8 +65,8 @@ func TestMergeHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "nominal behavior with a PDF format",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.pdf": "/foo/foo.pdf",
|
||||
})
|
||||
@@ -90,8 +90,8 @@ func TestMergeHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "convert to PDF format fail",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.pdf": "/foo/foo.pdf",
|
||||
})
|
||||
@@ -115,8 +115,8 @@ func TestMergeHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "invalid PDF format",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.pdf": "/foo/foo.pdf",
|
||||
})
|
||||
@@ -142,8 +142,8 @@ func TestMergeHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "cannot add output paths",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.pdf": "/foo/foo.pdf",
|
||||
})
|
||||
@@ -203,7 +203,7 @@ func TestMergeHandler(t *testing.T) {
|
||||
func TestConvertHandler(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx *api.MockContext
|
||||
ctx *api.ContextMock
|
||||
engine gotenberg.PDFEngine
|
||||
expectErr bool
|
||||
expectHTTPErr bool
|
||||
@@ -212,8 +212,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "nominal behavior",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.pdf": "/foo/foo.pdf",
|
||||
})
|
||||
@@ -234,8 +234,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "nominal behavior, but with 3 PDFs",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.pdf": "/foo/foo.pdf",
|
||||
"bar.pdf": "/bar/bar.pdf",
|
||||
@@ -258,8 +258,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "invalid form data: no PDF",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetValues(map[string][]string{
|
||||
"pdfFormat": {
|
||||
gotenberg.FormatPDFA1a,
|
||||
@@ -274,8 +274,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "invalid form data: no PDF format",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.pdf": "/foo/foo.pdf",
|
||||
})
|
||||
@@ -288,8 +288,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "convert to PDF format fail",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.pdf": "/foo/foo.pdf",
|
||||
})
|
||||
@@ -310,8 +310,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "PDF format not available",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.pdf": "/foo/foo.pdf",
|
||||
})
|
||||
@@ -334,8 +334,8 @@ func TestConvertHandler(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "cannot add output paths",
|
||||
ctx: func() *api.MockContext {
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx: func() *api.ContextMock {
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetFiles(map[string]string{
|
||||
"foo.pdf": "/foo/foo.pdf",
|
||||
})
|
||||
|
||||
@@ -252,7 +252,7 @@ func TestWebhookMiddlewareGuards(t *testing.T) {
|
||||
|
||||
c := srv.NewContext(tc.request, httptest.NewRecorder())
|
||||
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetEchoContext(c)
|
||||
|
||||
c.Set("context", ctx.Context)
|
||||
@@ -399,7 +399,7 @@ func TestWebhookMiddlewareAsynchronousProcess(t *testing.T) {
|
||||
c.Set("trace", "foo")
|
||||
c.Set("startTime", time.Now())
|
||||
|
||||
ctx := &api.MockContext{Context: &api.Context{}}
|
||||
ctx := &api.ContextMock{Context: &api.Context{}}
|
||||
ctx.SetLogger(zap.NewNop())
|
||||
ctx.SetEchoContext(c)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ ARG DOCKER_REPOSITORY
|
||||
ARG GOTENBERG_VERSION
|
||||
ARG GOLANGCI_LINT_VERSION
|
||||
|
||||
FROM golang:$GOLANG_VERSION-stretch as golang
|
||||
FROM golang:$GOLANG_VERSION-bullseye as golang
|
||||
|
||||
# We're extending the Gotenberg's Docker image because our code relies on external
|
||||
# dependencies like Google Chrome, LibreOffice, etc.
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
|
||||
set -x
|
||||
|
||||
go test -race -covermode=atomic -coverprofile=/tests/coverage.txt ./...
|
||||
# TODO: remove -buildvcs=false when fix for https://github.com/golang/go/issues/51723 is live.
|
||||
go test -buildvcs=false -race -covermode=atomic -coverprofile=/tests/coverage.txt ./...
|
||||
go tool cover -html=coverage.txt -o /tests/coverage.html
|
||||
Reference in New Issue
Block a user