Fixes #2810 - Attachments of Knowledge Base Answers don't get added to Elasticsearch
This commit is contained in:
parent
6c60021196
commit
695d05689b
7 changed files with 141 additions and 36 deletions
|
@ -7,27 +7,15 @@ class KnowledgeBase::Answer::AttachmentsController < ApplicationController
|
||||||
before_action :fetch_answer
|
before_action :fetch_answer
|
||||||
|
|
||||||
def create
|
def create
|
||||||
file = params[:file]
|
@answer.add_attachment params[:file]
|
||||||
|
|
||||||
Store.add(
|
render json: @answer.assets({})
|
||||||
object: @answer.class.name,
|
|
||||||
o_id: @answer.id,
|
|
||||||
data: file.read,
|
|
||||||
filename: file.original_filename,
|
|
||||||
preferences: headers_for_file(file)
|
|
||||||
)
|
|
||||||
|
|
||||||
output
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
attachment = @answer.attachments.find { |elem| elem.id == params[:id].to_i }
|
@answer.remove_attachment params[:id]
|
||||||
|
|
||||||
raise ActiveRecord::RecordNotFound if attachment.nil?
|
render json: @answer.assets({})
|
||||||
|
|
||||||
Store.remove_item(attachment.id)
|
|
||||||
|
|
||||||
output
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -35,21 +23,4 @@ class KnowledgeBase::Answer::AttachmentsController < ApplicationController
|
||||||
def fetch_answer
|
def fetch_answer
|
||||||
@answer = KnowledgeBase::Answer.find params[:answer_id]
|
@answer = KnowledgeBase::Answer.find params[:answer_id]
|
||||||
end
|
end
|
||||||
|
|
||||||
def output
|
|
||||||
@answer.touch # rubocop:disable Rails/SkipsModelValidations
|
|
||||||
render json: @answer.assets({})
|
|
||||||
end
|
|
||||||
|
|
||||||
def headers_for_file(file)
|
|
||||||
content_type = file.content_type || 'application/octet-stream'
|
|
||||||
|
|
||||||
if content_type == 'application/octet-stream' && (mime = MIME::Types.type_for(file.original_filename).first)
|
|
||||||
content_type = mime
|
|
||||||
end
|
|
||||||
|
|
||||||
{
|
|
||||||
'Content-Type': content_type
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -59,6 +59,46 @@ store attachments for this object
|
||||||
attachments_buffer_check
|
attachments_buffer_check
|
||||||
end
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
Returns attachments in ElasticSearch-compatible format
|
||||||
|
For use in #search_index_attribute_lookup
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def attachments_for_search_index_attribute_lookup
|
||||||
|
# list ignored file extensions
|
||||||
|
attachments_ignore = Setting.get('es_attachment_ignore') || [ '.png', '.jpg', '.jpeg', '.mpeg', '.mpg', '.mov', '.bin', '.exe' ]
|
||||||
|
|
||||||
|
# max attachment size
|
||||||
|
attachment_max_size_in_mb = (Setting.get('es_attachment_max_size_in_mb') || 10).megabytes
|
||||||
|
attachment_total_max_size_in_kb = 314_572.kilobytes
|
||||||
|
attachment_total_max_size_in_kb_current = 0.kilobytes
|
||||||
|
|
||||||
|
attachments.each_with_object([]) do |attachment, memo|
|
||||||
|
# check if attachment exists
|
||||||
|
next if !attachment.content
|
||||||
|
|
||||||
|
size_in_bytes = attachment.content.size.bytes
|
||||||
|
|
||||||
|
# check file size
|
||||||
|
next if size_in_bytes > attachment_max_size_in_mb
|
||||||
|
|
||||||
|
# check ignored files
|
||||||
|
next if !attachment.filename || attachments_ignore.include?(File.extname(attachment.filename).downcase)
|
||||||
|
|
||||||
|
# check if fits into total size limit
|
||||||
|
next if attachment_total_max_size_in_kb_current + size_in_bytes > attachment_total_max_size_in_kb
|
||||||
|
|
||||||
|
attachment_total_max_size_in_kb_current += size_in_bytes
|
||||||
|
|
||||||
|
memo << {
|
||||||
|
'_name' => attachment.filename,
|
||||||
|
'_content' => Base64.encode64(attachment.content).delete("\n")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def attachments_buffer
|
def attachments_buffer
|
||||||
|
|
|
@ -61,6 +61,37 @@ class KnowledgeBase::Answer < ApplicationModel
|
||||||
attachments.sort_by { |elem| elem.filename.downcase }
|
attachments.sort_by { |elem| elem.filename.downcase }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_attachment(file)
|
||||||
|
filename = file.try(:original_filename) || File.basename(file.path)
|
||||||
|
content_type = file.try(:content_type) || MIME::Types.type_for(filename).first&.content_type || 'application/octet-stream'
|
||||||
|
|
||||||
|
Store.add(
|
||||||
|
object: self.class.name,
|
||||||
|
o_id: id,
|
||||||
|
data: file.read,
|
||||||
|
filename: filename,
|
||||||
|
preferences: { 'Content-Type': content_type }
|
||||||
|
)
|
||||||
|
|
||||||
|
touch # rubocop:disable Rails/SkipsModelValidations
|
||||||
|
translations.each(&:touch)
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_attachment(attachment_id)
|
||||||
|
attachment = attachments.find { |elem| elem.id == attachment_id.to_i }
|
||||||
|
|
||||||
|
raise ActiveRecord::RecordNotFound if attachment.nil?
|
||||||
|
|
||||||
|
Store.remove_item(attachment.id)
|
||||||
|
|
||||||
|
touch # rubocop:disable Rails/SkipsModelValidations
|
||||||
|
translations.each(&:touch)
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
def api_url
|
def api_url
|
||||||
Rails.application.routes.url_helpers.knowledge_base_answer_path(category.knowledge_base, self)
|
Rails.application.routes.url_helpers.knowledge_base_answer_path(category.knowledge_base, self)
|
||||||
end
|
end
|
||||||
|
|
|
@ -57,6 +57,7 @@ class KnowledgeBase::Answer::Translation < ApplicationModel
|
||||||
attrs['title'] = ActionController::Base.helpers.strip_tags attrs['title']
|
attrs['title'] = ActionController::Base.helpers.strip_tags attrs['title']
|
||||||
attrs['content'] = content.search_index_attribute_lookup if content
|
attrs['content'] = content.search_index_attribute_lookup if content
|
||||||
attrs['scope_id'] = answer.category_id
|
attrs['scope_id'] = answer.category_id
|
||||||
|
attrs['attachment'] = answer.attachments_for_search_index_attribute_lookup
|
||||||
|
|
||||||
attrs
|
attrs
|
||||||
end
|
end
|
||||||
|
|
|
@ -116,6 +116,17 @@ namespace :searchindex do
|
||||||
}.merge(pipeline_field_attributes),
|
}.merge(pipeline_field_attributes),
|
||||||
}
|
}
|
||||||
}.merge(pipeline_field_attributes),
|
}.merge(pipeline_field_attributes),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
foreach: {
|
||||||
|
field: 'attachment',
|
||||||
|
processor: {
|
||||||
|
attachment: {
|
||||||
|
target_field: '_ingest._value',
|
||||||
|
field: '_ingest._value._content',
|
||||||
|
}.merge(pipeline_field_attributes),
|
||||||
|
}
|
||||||
|
}.merge(pipeline_field_attributes),
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -289,6 +300,23 @@ def get_mapping_properties_object(object)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if object.name == 'KnowledgeBase::Answer::Translation'
|
||||||
|
# do not server attachments if document is requested
|
||||||
|
result[name][:_source] = {
|
||||||
|
excludes: ['attachment']
|
||||||
|
}
|
||||||
|
|
||||||
|
# for elasticsearch 5.5 and lower
|
||||||
|
if !es_pipeline?
|
||||||
|
result[name][:_source] = {
|
||||||
|
excludes: ['attachment']
|
||||||
|
}
|
||||||
|
result[name][:properties][:attachment] = {
|
||||||
|
type: 'attachment',
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return result if es_type_in_mapping?
|
return result if es_type_in_mapping?
|
||||||
|
|
||||||
result[name]
|
result[name]
|
||||||
|
|
10
spec/fixtures/upload/test.rtf
vendored
Normal file
10
spec/fixtures/upload/test.rtf
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf600
|
||||||
|
{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fswiss\fcharset0 Helvetica-Bold;}
|
||||||
|
{\colortbl;\red255\green255\blue255;}
|
||||||
|
{\*\expandedcolortbl;;}
|
||||||
|
\paperw11900\paperh16840\margl1440\margr1440\vieww10800\viewh8400\viewkind0
|
||||||
|
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0
|
||||||
|
|
||||||
|
\f0\fs24 \cf0 Hello world from Rich Text
|
||||||
|
\f1\b RTF
|
||||||
|
\f0\b0 document}
|
|
@ -0,0 +1,24 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
require 'models/concerns/checks_kb_client_notification_examples'
|
||||||
|
require 'models/contexts/factory_context'
|
||||||
|
|
||||||
|
RSpec.describe KnowledgeBase::Answer::Translation, type: :model, current_user_id: 1, searchindex: 1 do
|
||||||
|
include_context 'basic Knowledge Base'
|
||||||
|
|
||||||
|
let(:user) { create(:admin_user) }
|
||||||
|
let(:filename) { 'test.rtf' }
|
||||||
|
let(:query) { 'RTF document' }
|
||||||
|
|
||||||
|
context 'search with attachment' do
|
||||||
|
before do
|
||||||
|
configure_elasticsearch(required: true, rebuild: true) do
|
||||||
|
published_answer.add_attachment File.open "spec/fixtures/upload/#{filename}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it do
|
||||||
|
expect(described_class.search(query: query, current_user: user))
|
||||||
|
.to include published_answer.translations.first
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue