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:
Julien Neuhart
2018-05-03 17:18:55 +02:00
committed by GitHub
parent ef9801b1fd
commit 4997b73bac
31 changed files with 770 additions and 692 deletions

View File

@@ -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

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -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",