mirror of
https://github.com/gotenberg/gotenberg.git
synced 2026-08-18 05:02:15 +01:00
new feature: users are now able to provide more file types to convert by adding entries in their configuration file (#3)
* new feature: users are now able to provide more file types to convert by adding entries in their configuration file * removing README blueprint: was not working correctly * improving code coverage of config package
This commit is contained in:
@@ -75,7 +75,7 @@ func NewConverter(r *http.Request) (*Converter, error) {
|
||||
func (c *Converter) Convert() (string, error) {
|
||||
var filesPaths []string
|
||||
for _, f := range c.files {
|
||||
if f.Type != gfile.PDFType {
|
||||
if f.Extension != ".pdf" {
|
||||
path, err := process.Unconv(c.workingDir, f)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/thecodingmachine/gotenberg/app/config"
|
||||
"github.com/thecodingmachine/gotenberg/app/converter/process"
|
||||
)
|
||||
|
||||
func makeRequest(filesPaths ...string) *http.Request {
|
||||
@@ -43,10 +42,10 @@ func makeRequest(filesPaths ...string) *http.Request {
|
||||
return req
|
||||
}
|
||||
|
||||
func loadCommandConfigs(configurationFilePath string) {
|
||||
func load(configurationFilePath string) {
|
||||
config.Reset()
|
||||
path, _ := filepath.Abs(configurationFilePath)
|
||||
c, _ := config.NewAppConfig(path)
|
||||
process.Load(c.CommandsConfig)
|
||||
config.ParseFile(path)
|
||||
}
|
||||
|
||||
func TestNewConverter(t *testing.T) {
|
||||
@@ -55,6 +54,8 @@ func TestNewConverter(t *testing.T) {
|
||||
oPath string
|
||||
)
|
||||
|
||||
load("../../_tests/configurations/gotenberg.yml")
|
||||
|
||||
// case 1: uses a request with a single file.
|
||||
path, _ = filepath.Abs("../../_tests/file.docx")
|
||||
if _, err := NewConverter(makeRequest(path)); err != nil {
|
||||
@@ -94,7 +95,7 @@ func TestConvert(t *testing.T) {
|
||||
c *Converter
|
||||
)
|
||||
|
||||
loadCommandConfigs("../../_tests/configurations/gotenberg.yml")
|
||||
load("../../_tests/configurations/gotenberg.yml")
|
||||
|
||||
// case 1: uses a request with a single file.
|
||||
path, _ = filepath.Abs("../../_tests/file.docx")
|
||||
@@ -111,7 +112,7 @@ func TestConvert(t *testing.T) {
|
||||
t.Errorf("Converter should have been able to convert '%s' and '%s' to PDF", path, oPath)
|
||||
}
|
||||
|
||||
loadCommandConfigs("../../_tests/configurations/timeout-gotenberg.yml")
|
||||
load("../../_tests/configurations/timeout-gotenberg.yml")
|
||||
|
||||
// case 3: uses a request with a single file and a configuration with an unsuitable timeout for the conversion commands.
|
||||
path, _ = filepath.Abs("../../_tests/file.docx")
|
||||
@@ -120,7 +121,7 @@ func TestConvert(t *testing.T) {
|
||||
t.Errorf("Converter should not have been able to convert '%s' to PDF", path)
|
||||
}
|
||||
|
||||
loadCommandConfigs("../../_tests/configurations/merge-timeout-gotenberg.yml")
|
||||
load("../../_tests/configurations/merge-timeout-gotenberg.yml")
|
||||
|
||||
// case 4: uses a request with two files and a configuration with an unsuitable timeout for the merge command.
|
||||
path, _ = filepath.Abs("../../_tests/file.pdf")
|
||||
@@ -132,6 +133,8 @@ func TestConvert(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestClear(t *testing.T) {
|
||||
load("../../_tests/configurations/gotenberg.yml")
|
||||
|
||||
path, _ := filepath.Abs("../../_tests/file.docx")
|
||||
c, _ := NewConverter(makeRequest(path))
|
||||
if err := c.Clear(); err != nil {
|
||||
|
||||
@@ -7,70 +7,32 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/thecodingmachine/gotenberg/app/config"
|
||||
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
// File represents a file which has been created
|
||||
// from a request.
|
||||
type File struct {
|
||||
// Type is the kind of file.
|
||||
Type Type
|
||||
// Extension is the extension of the file.
|
||||
Extension string
|
||||
// Path is the file path.
|
||||
Path string
|
||||
}
|
||||
|
||||
// Type represents what kind of file we're dealing with.
|
||||
type Type uint32
|
||||
|
||||
const (
|
||||
// PDFType represents a... PDF file.
|
||||
PDFType Type = iota
|
||||
// MarkdownType represents a... Markdown file.
|
||||
MarkdownType
|
||||
// HTMLType represents an... HTML file.
|
||||
HTMLType
|
||||
// OfficeType represents an... Office document.
|
||||
OfficeType
|
||||
)
|
||||
|
||||
// filesTypes associates a file extension with its file kind counterpart.
|
||||
var filesTypes = map[string]Type{
|
||||
".pdf": PDFType,
|
||||
".md": MarkdownType,
|
||||
".htm": HTMLType,
|
||||
".html": HTMLType,
|
||||
".doc": OfficeType,
|
||||
".docx": OfficeType,
|
||||
".odt": OfficeType,
|
||||
".xls": OfficeType,
|
||||
".xlsx": OfficeType,
|
||||
".ods": OfficeType,
|
||||
".ppt": OfficeType,
|
||||
".pptx": OfficeType,
|
||||
".odp": OfficeType,
|
||||
}
|
||||
|
||||
type fileTypeNotFoundError struct {
|
||||
fileName string
|
||||
}
|
||||
|
||||
func (e *fileTypeNotFoundError) Error() string {
|
||||
return fmt.Sprintf("File type was not found for '%s'", e.fileName)
|
||||
}
|
||||
|
||||
// NewFile creates a file in the considered directory.
|
||||
// Returns a *File instance or an error if something bad happened.
|
||||
func NewFile(workingDir string, r io.Reader, fileName string) (*File, error) {
|
||||
ext := filepath.Ext(fileName)
|
||||
|
||||
t, ok := filesTypes[ext]
|
||||
if !ok {
|
||||
return nil, &fileTypeNotFoundError{fileName: fileName}
|
||||
if _, err := config.GetCommand(ext); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f := &File{
|
||||
Path: MakeFilePath(workingDir, ext),
|
||||
Type: t,
|
||||
Extension: ext,
|
||||
Path: MakeFilePath(workingDir, ext),
|
||||
}
|
||||
|
||||
file, err := os.Create(f.Path)
|
||||
|
||||
@@ -2,13 +2,22 @@ package file
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/thecodingmachine/gotenberg/app/config"
|
||||
)
|
||||
|
||||
func load(configurationFilePath string) {
|
||||
config.Reset()
|
||||
path, _ := filepath.Abs(configurationFilePath)
|
||||
config.ParseFile(path)
|
||||
}
|
||||
|
||||
func TestNewFile(t *testing.T) {
|
||||
load("../../../_tests/configurations/gotenberg.yml")
|
||||
|
||||
workingDir := "test"
|
||||
os.Mkdir(workingDir, 0666)
|
||||
|
||||
@@ -17,21 +26,13 @@ func TestNewFile(t *testing.T) {
|
||||
t.Error("File should not have been instantiated with an empty buffer")
|
||||
}
|
||||
|
||||
// case 2: uses a reader from a correct file type.
|
||||
// case 2: uses a file name.
|
||||
filePath, _ := filepath.Abs("../../../_tests/file.pdf")
|
||||
r, _ := os.Open(filePath)
|
||||
defer r.Close()
|
||||
if _, err := NewFile(workingDir, r, "file.pdf"); err != nil {
|
||||
t.Errorf("File should have been instantiated using a reader from '%s'", filePath)
|
||||
t.Errorf("File should have been instantiated using a reader of '%s'", filePath)
|
||||
}
|
||||
|
||||
os.RemoveAll(workingDir)
|
||||
}
|
||||
func TestFileTypeNotFoundError(t *testing.T) {
|
||||
fileName := "file.wp"
|
||||
err := &fileTypeNotFoundError{fileName: fileName}
|
||||
expected := fmt.Sprintf("File type was not found for '%s'", fileName)
|
||||
if err.Error() != expected {
|
||||
t.Errorf("Error returned a wrong message: got '%s' want '%s'", err.Error(), expected)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"sync"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/thecodingmachine/gotenberg/app/config"
|
||||
@@ -14,8 +13,7 @@ import (
|
||||
)
|
||||
|
||||
type runner struct {
|
||||
mu sync.Mutex
|
||||
commandsConfig *config.CommandsConfig
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var forest = &runner{}
|
||||
@@ -64,25 +62,12 @@ func (r *runner) run(command string, timeout int) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Load loads the commands configuration coming from the application configuration.
|
||||
func Load(config *config.CommandsConfig) {
|
||||
forest.commandsConfig = config
|
||||
}
|
||||
|
||||
// conversionData will be applied to the data-driven templates of conversions commands.
|
||||
type conversionData struct {
|
||||
FilePath string
|
||||
ResultFilePath string
|
||||
}
|
||||
|
||||
type impossibleConversionError struct{}
|
||||
|
||||
const impossibleConversionErrorMessage = "Impossible conversion"
|
||||
|
||||
func (e *impossibleConversionError) Error() string {
|
||||
return impossibleConversionErrorMessage
|
||||
}
|
||||
|
||||
// Unconv converts a file to PDF and returns the new file path.
|
||||
func Unconv(workingDir string, file *gfile.File) (string, error) {
|
||||
cmdData := &conversionData{
|
||||
@@ -90,34 +75,17 @@ func Unconv(workingDir string, file *gfile.File) (string, error) {
|
||||
ResultFilePath: gfile.MakeFilePath(workingDir, ".pdf"),
|
||||
}
|
||||
|
||||
var (
|
||||
cmdTimeout int
|
||||
cmdTemplate *template.Template
|
||||
)
|
||||
|
||||
switch file.Type {
|
||||
case gfile.MarkdownType:
|
||||
cmdTimeout = forest.commandsConfig.Markdown.Timeout
|
||||
cmdTemplate = forest.commandsConfig.Markdown.Template
|
||||
break
|
||||
case gfile.HTMLType:
|
||||
cmdTimeout = forest.commandsConfig.HTML.Timeout
|
||||
cmdTemplate = forest.commandsConfig.HTML.Template
|
||||
break
|
||||
case gfile.OfficeType:
|
||||
cmdTimeout = forest.commandsConfig.Office.Timeout
|
||||
cmdTemplate = forest.commandsConfig.Office.Template
|
||||
break
|
||||
default:
|
||||
return "", &impossibleConversionError{}
|
||||
}
|
||||
|
||||
var data bytes.Buffer
|
||||
if err := cmdTemplate.Execute(&data, cmdData); err != nil {
|
||||
cmd, err := config.GetCommand(file.Extension)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err := forest.run(data.String(), cmdTimeout)
|
||||
var data bytes.Buffer
|
||||
if err := cmd.Template.Execute(&data, cmdData); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = forest.run(data.String(), cmd.Timeout)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -138,15 +106,17 @@ func Merge(workingDir string, filesPaths []string) (string, error) {
|
||||
ResultFilePath: gfile.MakeFilePath(workingDir, ".pdf"),
|
||||
}
|
||||
|
||||
cmdTimeout := forest.commandsConfig.Merge.Timeout
|
||||
cmdTemplate := forest.commandsConfig.Merge.Template
|
||||
|
||||
var data bytes.Buffer
|
||||
if err := cmdTemplate.Execute(&data, cmdData); err != nil {
|
||||
cmd, err := config.GetCommand(".pdf")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err := forest.run(data.String(), cmdTimeout)
|
||||
var data bytes.Buffer
|
||||
if err := cmd.Template.Execute(&data, cmdData); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = forest.run(data.String(), cmd.Timeout)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -22,20 +22,10 @@ func makeFile(workingDir string, fileName string) *gfile.File {
|
||||
return f
|
||||
}
|
||||
|
||||
func loadCommandConfigs(configurationFilePath string) {
|
||||
func load(configurationFilePath string) {
|
||||
config.Reset()
|
||||
path, _ := filepath.Abs(configurationFilePath)
|
||||
c, _ := config.NewAppConfig(path)
|
||||
Load(c.CommandsConfig)
|
||||
}
|
||||
|
||||
func TestLoad(t *testing.T) {
|
||||
path, _ := filepath.Abs("../../../_tests/configurations/gotenberg.yml")
|
||||
c, _ := config.NewAppConfig(path)
|
||||
Load(c.CommandsConfig)
|
||||
|
||||
if c.CommandsConfig != forest.commandsConfig {
|
||||
t.Error("Commands configuration should have been loaded correctly")
|
||||
}
|
||||
config.ParseFile(path)
|
||||
}
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
@@ -66,7 +56,7 @@ func TestUnconv(t *testing.T) {
|
||||
workingDir := "test"
|
||||
os.Mkdir(workingDir, 0666)
|
||||
|
||||
loadCommandConfigs("../../../_tests/configurations/gotenberg.yml")
|
||||
load("../../../_tests/configurations/gotenberg.yml")
|
||||
|
||||
// case 1: uses an Markdown file type.
|
||||
file = makeFile(workingDir, "file.md")
|
||||
@@ -92,7 +82,7 @@ func TestUnconv(t *testing.T) {
|
||||
t.Errorf("Converting '%s' to PDF should not have worked", file.Path)
|
||||
}
|
||||
|
||||
loadCommandConfigs("../../../_tests/configurations/timeout-gotenberg.yml")
|
||||
load("../../../_tests/configurations/timeout-gotenberg.yml")
|
||||
|
||||
// case 5: uses a command with an unsuitable timeout.
|
||||
file = makeFile(workingDir, "file.docx")
|
||||
@@ -107,7 +97,7 @@ func TestMerge(t *testing.T) {
|
||||
workingDir := "test"
|
||||
os.Mkdir(workingDir, 0666)
|
||||
|
||||
loadCommandConfigs("../../../_tests/configurations/gotenberg.yml")
|
||||
load("../../../_tests/configurations/gotenberg.yml")
|
||||
|
||||
var filesPaths []string
|
||||
path, _ := filepath.Abs("../../../_tests/file.pdf")
|
||||
@@ -119,7 +109,7 @@ func TestMerge(t *testing.T) {
|
||||
t.Error("Merge should have worked")
|
||||
}
|
||||
|
||||
loadCommandConfigs("../../../_tests/configurations/timeout-gotenberg.yml")
|
||||
load("../../../_tests/configurations/timeout-gotenberg.yml")
|
||||
|
||||
// case 2: uses a command with an unsuitable timeout.
|
||||
if _, err := Merge(workingDir, filesPaths); err == nil {
|
||||
@@ -129,13 +119,6 @@ func TestMerge(t *testing.T) {
|
||||
os.RemoveAll(workingDir)
|
||||
}
|
||||
|
||||
func TestImpossibleConversionError(t *testing.T) {
|
||||
err := &impossibleConversionError{}
|
||||
if err.Error() != impossibleConversionErrorMessage {
|
||||
t.Errorf("Error returned a wrong message: got '%s' want '%s'", err.Error(), impossibleConversionErrorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandTimeoutError(t *testing.T) {
|
||||
err := &commandTimeoutError{
|
||||
command: "echo hello",
|
||||
|
||||
Reference in New Issue
Block a user