Improve document extraction and including a document extraction command (#17183)

* Add extract documents content command

* Adding the extraction command and making the pure go pdf library as secondary option

* Improving the memory usage and docextractor interface

* Enable content extraction by default in all the instances

* Tiny improvement on archive indexing

* Adding App interface generation and the opentracing layer

* Fixing linter errors

* Addressing PR review comments

* Addressing PR review comments
Этот коммит содержится в:
Jesús Espino
2021-04-07 13:27:20 +02:00
коммит произвёл GitHub
родитель 75824257d5
Коммит 819e4c0c64
17 изменённых файлов: 172 добавлений и 94 удалений

Просмотреть файл

@@ -515,6 +515,7 @@ type AppIface interface {
EnableUserAccessToken(token *model.UserAccessToken) *model.AppError
EnvironmentConfig(filter func(reflect.StructField) bool) map[string]interface{}
ExportPermissions(w io.Writer) error
ExtractContentFromFileInfo(fileInfo *model.FileInfo) error
FetchSamlMetadataFromIdp(url string) ([]byte, *model.AppError)
FileBackend() (filestore.FileBackend, *model.AppError)
FileExists(path string) (bool, *model.AppError)

Просмотреть файл

@@ -9,7 +9,6 @@ import (
"context"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"image"
"image/color"
@@ -30,6 +29,7 @@ import (
"github.com/disintegration/imaging"
_ "github.com/oov/psd"
"github.com/pkg/errors"
"github.com/rwcarlsen/goexif/exif"
_ "golang.org/x/image/bmp"
_ "golang.org/x/image/tiff"
@@ -75,6 +75,7 @@ const (
ImageThumbnailPixelWidth = 120
ImageThumbnailPixelHeight = 100
ImagePreviewPixelWidth = 1920
MaxContentExtractionSize = 1024 * 1024 // 1Mb
)
func (a *App) FileBackend() (filestore.FileBackend, *model.AppError) {
@@ -773,21 +774,9 @@ func (a *App) UploadFileX(channelID, name string, input io.Reader,
if *a.Config().FileSettings.ExtractContent && a.Config().FeatureFlags.FilesSearch {
infoCopy := *t.fileinfo
a.Srv().Go(func() {
file, aerr := a.FileReader(t.fileinfo.Path)
if aerr != nil {
mlog.Error("Failed to open file for extract file content", mlog.Err(aerr))
return
}
defer file.Close()
text, err := docextractor.Extract(infoCopy.Name, file, docextractor.ExtractSettings{
ArchiveRecursion: *a.Config().FileSettings.ArchiveRecursion,
})
err := a.ExtractContentFromFileInfo(&infoCopy)
if err != nil {
mlog.Error("Failed to extract file content", mlog.Err(err))
return
}
if storeErr := a.Srv().Store.FileInfo().SetContent(infoCopy.Id, text); storeErr != nil {
mlog.Error("Failed to save the extracted file content", mlog.Err(storeErr))
mlog.Error("Failed to extract file content", mlog.Err(err), mlog.String("fileInfoId", infoCopy.Id))
}
})
}
@@ -1043,21 +1032,9 @@ func (a *App) DoUploadFileExpectModification(now time.Time, rawTeamId string, ra
if *a.Config().FileSettings.ExtractContent && a.Config().FeatureFlags.FilesSearch {
infoCopy := *info
a.Srv().Go(func() {
file, aerr := a.FileReader(infoCopy.Path)
if aerr != nil {
mlog.Error("Failed to open file for extract file content", mlog.Err(aerr))
return
}
defer file.Close()
text, err := docextractor.Extract(infoCopy.Name, file, docextractor.ExtractSettings{
ArchiveRecursion: *a.Config().FileSettings.ArchiveRecursion,
})
err := a.ExtractContentFromFileInfo(&infoCopy)
if err != nil {
mlog.Error("Failed to extract file content", mlog.Err(err))
return
}
if storeErr := a.Srv().Store.FileInfo().SetContent(infoCopy.Id, text); storeErr != nil {
mlog.Error("Failed to save the extracted file content", mlog.Err(storeErr))
mlog.Error("Failed to extract file content", mlog.Err(err), mlog.String("fileInfoId", infoCopy.Id))
}
})
}
@@ -1396,3 +1373,26 @@ func (a *App) SearchFilesInTeamForUser(terms string, userId string, teamId strin
return fileInfoSearchResults, nil
}
func (a *App) ExtractContentFromFileInfo(fileInfo *model.FileInfo) error {
file, aerr := a.FileReader(fileInfo.Path)
if aerr != nil {
return errors.Wrap(aerr, "failed to open file for extract file content")
}
defer file.Close()
text, err := docextractor.Extract(fileInfo.Name, file, docextractor.ExtractSettings{
ArchiveRecursion: *a.Config().FileSettings.ArchiveRecursion,
})
if err != nil {
return errors.Wrap(err, "failed to extract file content")
}
if text != "" {
if len(text) > MaxContentExtractionSize {
text = text[0:MaxContentExtractionSize]
}
if storeErr := a.Srv().Store.FileInfo().SetContent(fileInfo.Id, text); storeErr != nil {
return errors.Wrap(storeErr, "failed to save the extracted file content")
}
}
return nil
}

Просмотреть файл

@@ -16,7 +16,6 @@ const EmojisPermissionsMigrationKey = "EmojisPermissionsMigrationComplete"
const GuestRolesCreationMigrationKey = "GuestRolesCreationMigrationComplete"
const SystemConsoleRolesCreationMigrationKey = "SystemConsoleRolesCreationMigrationComplete"
const ContentExtractionConfigMigrationKey = "ContentExtractionConfigMigrationComplete"
const usersLimitToAutoEnableContentExtraction = 500
// This function migrates the default built in roles from code/config to the database.
func (a *App) DoAdvancedPermissionsMigration() {
@@ -285,35 +284,6 @@ func (a *App) DoSystemConsoleRolesCreationMigration() {
}
}
func (a *App) doContentExtractionConfigMigration() {
if !a.Config().FeatureFlags.FilesSearch {
return
}
// If the migration is already marked as completed, don't do it again.
if _, err := a.Srv().Store.System().GetByName(ContentExtractionConfigMigrationKey); err == nil {
return
}
if usersCount, err := a.Srv().Store.User().Count(model.UserCountOptions{}); err != nil {
mlog.Critical("Failed to get the users count for migrating the content extraction, using default value", mlog.Err(err))
} else {
if usersCount < usersLimitToAutoEnableContentExtraction {
a.UpdateConfig(func(config *model.Config) {
config.FileSettings.ExtractContent = model.NewBool(true)
})
}
}
system := model.System{
Name: ContentExtractionConfigMigrationKey,
Value: "true",
}
if err := a.Srv().Store.System().Save(&system); err != nil {
mlog.Critical("Failed to mark content extraction config migration as completed.", mlog.Err(err))
}
}
func (a *App) DoAppMigrations() {
a.DoAdvancedPermissionsMigration()
a.DoEmojisPermissionsMigration()
@@ -325,5 +295,4 @@ func (a *App) DoAppMigrations() {
if err != nil {
mlog.Critical("(app.App).DoPermissionsMigrations failed", mlog.Err(err))
}
a.doContentExtractionConfigMigration()
}

Просмотреть файл

@@ -3795,6 +3795,28 @@ func (a *OpenTracingAppLayer) ExtendSessionExpiryIfNeeded(session *model.Session
return resultVar0
}
func (a *OpenTracingAppLayer) ExtractContentFromFileInfo(fileInfo *model.FileInfo) error {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.ExtractContentFromFileInfo")
a.ctx = newCtx
a.app.Srv().Store.SetContext(newCtx)
defer func() {
a.app.Srv().Store.SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0 := a.app.ExtractContentFromFileInfo(fileInfo)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
ext.Error.Set(span, true)
}
return resultVar0
}
func (a *OpenTracingAppLayer) FetchSamlMetadataFromIdp(url string) ([]byte, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.FetchSamlMetadataFromIdp")

Просмотреть файл

@@ -14,7 +14,6 @@ import (
"github.com/mattermost/mattermost-server/v5/model"
"github.com/mattermost/mattermost-server/v5/plugin"
"github.com/mattermost/mattermost-server/v5/services/docextractor"
"github.com/mattermost/mattermost-server/v5/shared/mlog"
"github.com/mattermost/mattermost-server/v5/store"
)
@@ -303,15 +302,9 @@ func (a *App) UploadData(us *model.UploadSession, rd io.Reader) (*model.FileInfo
if *a.Config().FileSettings.ExtractContent && a.Config().FeatureFlags.FilesSearch {
infoCopy := *info
a.Srv().Go(func() {
text, err := docextractor.Extract(infoCopy.Name, file, docextractor.ExtractSettings{
ArchiveRecursion: *a.Config().FileSettings.ArchiveRecursion,
})
err := a.ExtractContentFromFileInfo(&infoCopy)
if err != nil {
mlog.Error("Failed to extract file content", mlog.Err(err))
return
}
if storeErr := a.Srv().Store.FileInfo().SetContent(infoCopy.Id, text); storeErr != nil {
mlog.Error("Failed to save the extracted file content", mlog.Err(storeErr))
mlog.Error("Failed to extract file content", mlog.Err(err), mlog.String("fileInfoId", infoCopy.Id))
}
})
}