From 1b3d787c27660d5aee82ee90a6cc604be47dda5c Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Fri, 10 Mar 2017 06:34:51 +0100 Subject: [PATCH] Improved article attachment api (implemented easier attaching attachments via rest). --- .../concerns/creates_ticket_articles.rb | 72 ++++- app/controllers/ticket_articles_controller.rb | 19 +- app/models/ticket/article.rb | 89 +++++- app/models/ticket/article/assets.rb | 19 +- .../ticket_articles_controller_test.rb | 273 ++++++++++++++++++ test/controllers/tickets_controller_test.rb | 225 +++++++++++++++ test/unit/ticket_test.rb | 95 +++++- 7 files changed, 723 insertions(+), 69 deletions(-) create mode 100644 test/controllers/ticket_articles_controller_test.rb diff --git a/app/controllers/concerns/creates_ticket_articles.rb b/app/controllers/concerns/creates_ticket_articles.rb index 77e95166b..c087d12a1 100644 --- a/app/controllers/concerns/creates_ticket_articles.rb +++ b/app/controllers/concerns/creates_ticket_articles.rb @@ -46,23 +46,22 @@ module CreatesTicketArticles article.ticket_id = ticket.id # store dataurl images to store - if form_id && article.body && article.content_type =~ %r{text/html}i - article.body.gsub!( %r{(}i ) { |_item| + attachments_inline = [] + if article.body && article.content_type =~ %r{text/html}i + article.body.gsub!( %r{(}im ) { |_item| file_attributes = StaticAssets.data_url_attributes($2) - cid = "#{ticket.id}.#{form_id}.#{rand(999_999)}@#{Setting.get('fqdn')}" - headers_store = { - 'Content-Type' => file_attributes[:mime_type], - 'Mime-Type' => file_attributes[:mime_type], - 'Content-ID' => cid, - 'Content-Disposition' => 'inline', - } - store = Store.add( - object: 'UploadCache', - o_id: form_id, + cid = "#{ticket.id}.#{rand(999_999_999)}@#{Setting.get('fqdn')}" + attachment = { data: file_attributes[:content], filename: cid, - preferences: headers_store - ) + preferences: { + 'Content-Type' => file_attributes[:mime_type], + 'Mime-Type' => file_attributes[:mime_type], + 'Content-ID' => cid, + 'Content-Disposition' => 'inline', + }, + } + attachments_inline.push attachment "#{$1}cid:#{cid}\">" } end @@ -76,6 +75,48 @@ module CreatesTicketArticles end article.save! + # store inline attachments + attachments_inline.each { |attachment| + Store.add( + object: 'Ticket::Article', + o_id: article.id, + data: attachment[:data], + filename: attachment[:filename], + preferences: attachment[:preferences], + ) + } + + # add attachments as param + if params[:attachments] + params[:attachments].each_with_index { |attachment, index| + + # validation + ['mime-type', 'filename', 'data'].each { |key| + next if attachment[key] + raise Exceptions::UnprocessableEntity, "Attachment needs '#{key}' param for attachment with index '#{index}'" + } + + preferences = {} + ['charset', 'mime-type'].each { |key| + next if !attachment[key] + store_key = key.tr('-', '_').camelize.gsub(/(.+)([A-Z])/, '\1_\2').tr('_', '-') + preferences[store_key] = attachment[key] + } + + if attachment[:data] !~ %r{^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$} + raise Exceptions::UnprocessableEntity, "Invalid base64 for attachment with index '#{index}'" + end + + Store.add( + object: 'Ticket::Article', + o_id: article.id, + data: Base64.decode64(attachment[:data]), + filename: attachment[:filename], + preferences: preferences, + ) + } + end + # account time if time_unit.present? Ticket::TimeAccounting.create!( @@ -85,9 +126,9 @@ module CreatesTicketArticles ) end - # remove attachments from upload cache return article if !form_id + # remove attachments from upload cache Store.remove( object: 'UploadCache', o_id: form_id, @@ -95,4 +136,5 @@ module CreatesTicketArticles article end + end diff --git a/app/controllers/ticket_articles_controller.rb b/app/controllers/ticket_articles_controller.rb index 490b0054f..7c00282b4 100644 --- a/app/controllers/ticket_articles_controller.rb +++ b/app/controllers/ticket_articles_controller.rb @@ -14,14 +14,11 @@ class TicketArticlesController < ApplicationController # GET /articles/1 def show - - # permission check article = Ticket::Article.find(params[:id]) article_permission(article) if params[:expand] result = article.attributes_with_association_names - result[:attachments] = article.attachments render json: result, status: :ok return end @@ -37,8 +34,6 @@ class TicketArticlesController < ApplicationController # GET /ticket_articles/by_ticket/1 def index_by_ticket - - # permission check ticket = Ticket.find(params[:id]) ticket_permission(ticket) @@ -50,9 +45,6 @@ class TicketArticlesController < ApplicationController # ignore internal article if customer is requesting next if article.internal == true && current_user.permissions?('ticket.customer') result = article.attributes_with_association_names - - # add attachments - result[:attachments] = article.attachments articles.push result } @@ -95,7 +87,6 @@ class TicketArticlesController < ApplicationController if params[:expand] result = article.attributes_with_association_names - result[:attachments] = article.attachments render json: result, status: :created return end @@ -111,8 +102,6 @@ class TicketArticlesController < ApplicationController # PUT /articles/1 def update - - # permission check article = Ticket::Article.find(params[:id]) article_permission(article) @@ -127,7 +116,6 @@ class TicketArticlesController < ApplicationController if params[:expand] result = article.attributes_with_association_names - result[:attachments] = article.attachments render json: result, status: :ok return end @@ -220,8 +208,6 @@ class TicketArticlesController < ApplicationController # GET /ticket_attachment/:ticket_id/:article_id/:id def attachment - - # permission check ticket = Ticket.lookup(id: params[:ticket_id]) if !ticket_permission(ticket) raise Exceptions::NotAuthorized, 'No such ticket.' @@ -255,8 +241,6 @@ class TicketArticlesController < ApplicationController # GET /ticket_article_plain/1 def article_plain - - # permission check article = Ticket::Article.find(params[:id]) article_permission(article) @@ -276,6 +260,9 @@ class TicketArticlesController < ApplicationController private def article_permission(article) + if current_user.permissions?('ticket.customer') + raise Exceptions::NotAuthorized if article.internal == true + end ticket = Ticket.lookup(id: article.ticket_id) return true if ticket.permission(current_user: current_user) raise Exceptions::NotAuthorized diff --git a/app/models/ticket/article.rb b/app/models/ticket/article.rb index b9c75acc6..4b1f839b6 100644 --- a/app/models/ticket/article.rb +++ b/app/models/ticket/article.rb @@ -44,10 +44,7 @@ class Ticket::Article < ApplicationModel insert inline image urls to body - article_attributes = Ticket::Article.insert_urls( - article_attributes, - attachments, - ) + article_attributes = Ticket::Article.insert_urls(article_attributes) returns @@ -55,23 +52,30 @@ returns =end - def self.insert_urls(article, attachments) + def self.insert_urls(article) + return article if article['attachments'].empty? + return article if article['content_type'] !~ %r{text/html}i + return article if article['body'] !~ /)/i ) { |item| + article['body'].gsub!( /(/im ) { |item| + tag_start = $1 + cid = $3 + tag_end = $4 replace = item # look for attachment - attachments.each { |file| - next if !file.preferences['Content-ID'] || file.preferences['Content-ID'] != $3 - replace = "#{$1}/api/v1/ticket_attachment/#{article['ticket_id']}/#{article['id']}/#{file.id}#{$4}" - inline_attachments[file.id] = true + article['attachments'].each { |file| + next if !file[:preferences] || !file[:preferences]['Content-ID'] || (file[:preferences]['Content-ID'] != cid && file[:preferences]['Content-ID'] != "<#{cid}>" ) + replace = "#{tag_start}/api/v1/ticket_attachment/#{article['ticket_id']}/#{article['id']}/#{file[:id]}\"#{tag_end}>" + inline_attachments[file[:id]] = true break } replace } new_attachments = [] - attachments.each { |file| - next if inline_attachments[file.id] + article['attachments'].each { |file| + next if inline_attachments[file[:id]] new_attachments.push file } article['attachments'] = new_attachments @@ -93,11 +97,12 @@ returns def attachments_inline inline_attachments = {} - body.gsub( //i ) { |_item| + body.gsub( //im ) { |_item| + cid = $2 # look for attachment attachments.each { |file| - next if !file.preferences['Content-ID'] || file.preferences['Content-ID'] != $2 + next if !file.preferences['Content-ID'] || (file.preferences['Content-ID'] != cid && file.preferences['Content-ID'] != "<#{cid}>" ) inline_attachments[file.id] = true break } @@ -212,6 +217,62 @@ returns: content_type =~ /html/i end +=begin + +get relation name of model based on params + + model = Model.find(1) + attributes = model.attributes_with_association_names + +returns + + hash with attributes, association ids, association names and relation name + +=end + + def attributes_with_association_names + attributes = super + attributes['attachments'] = [] + attachments.each { |attachment| + item = { + id: attachment['id'], + filename: attachment['filename'], + size: attachment['size'], + preferences: attachment['preferences'], + } + attributes['attachments'].push item + } + Ticket::Article.insert_urls(attributes) + end + +=begin + +get relations of model based on params + + model = Model.find(1) + attributes = model.attributes_with_association_ids + +returns + + hash with attributes and association ids + +=end + + def attributes_with_association_ids + attributes = super + attributes['attachments'] = [] + attachments.each { |attachment| + item = { + id: attachment['id'], + filename: attachment['filename'], + size: attachment['size'], + preferences: attachment['preferences'], + } + attributes['attachments'].push item + } + Ticket::Article.insert_urls(attributes) + end + private # strip not wanted chars diff --git a/app/models/ticket/article/assets.rb b/app/models/ticket/article/assets.rb index 28b69a80b..eef72101c 100644 --- a/app/models/ticket/article/assets.rb +++ b/app/models/ticket/article/assets.rb @@ -39,24 +39,7 @@ returns data[ app_model_article ] = {} end if !data[ app_model_article ][ id ] - data[ app_model_article ][ id ] = attributes - - # add attachment list to article - data[ app_model_article ][ id ]['attachments'] = attachments - - if !data[ app_model_article ][ id ]['attachments'].empty? - if data[ app_model_article ][ id ]['content_type'] =~ %r{text/html}i - if data[ app_model_article ][ id ]['body'] =~ / 'application/json', 'CONTENT_TYPE' => 'application/json' } + + # create agent + roles = Role.where(name: %w(Admin Agent)) + groups = Group.all + + UserInfo.current_user_id = 1 + @admin = User.create_or_update( + login: 'tickets-admin', + firstname: 'Tickets', + lastname: 'Admin', + email: 'tickets-admin@example.com', + password: 'adminpw', + active: true, + roles: roles, + groups: groups, + ) + + # create agent + roles = Role.where(name: 'Agent') + @agent = User.create_or_update( + login: 'tickets-agent@example.com', + firstname: 'Tickets', + lastname: 'Agent', + email: 'tickets-agent@example.com', + password: 'agentpw', + active: true, + roles: roles, + groups: groups, + ) + + # create customer without org + roles = Role.where(name: 'Customer') + @customer_without_org = User.create_or_update( + login: 'tickets-customer1@example.com', + firstname: 'Tickets', + lastname: 'Customer1', + email: 'tickets-customer1@example.com', + password: 'customer1pw', + active: true, + roles: roles, + ) + + end + + test '01.01 ticket create with agent and articles' do + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('tickets-agent@example.com', 'agentpw') + + params = { + title: 'a new ticket #1', + group: 'Users', + customer_id: @customer_without_org.id, + article: { + body: 'some body', + } + } + post '/api/v1/tickets', params.to_json, @headers.merge('Authorization' => credentials) + assert_response(201) + result = JSON.parse(@response.body) + + params = { + ticket_id: result['id'], + content_type: 'text/plain', # or text/html + body: 'some body', + type: 'note', + } + post '/api/v1/ticket_articles', params.to_json, @headers.merge('Authorization' => credentials) + assert_response(201) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal(nil, result['subject']) + assert_equal('some body', result['body']) + assert_equal('text/plain', result['content_type']) + assert_equal(@agent.id, result['updated_by_id']) + assert_equal(@agent.id, result['created_by_id']) + + ticket = Ticket.find(result['ticket_id']) + assert_equal(2, ticket.articles.count) + assert_equal(0, ticket.articles[0].attachments.count) + assert_equal(0, ticket.articles[1].attachments.count) + + params = { + ticket_id: result['ticket_id'], + content_type: 'text/html', # or text/html + body: 'some body Red dot', + type: 'note', + } + post '/api/v1/ticket_articles', params.to_json, @headers.merge('Authorization' => credentials) + assert_response(201) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal(nil, result['subject']) + assert_no_match(/some body 'some_file.txt', + 'data' => 'dGVzdCAxMjM=', + 'mime-type' => 'text/plain', + ], + } + post '/api/v1/ticket_articles', params.to_json, @headers.merge('Authorization' => credentials) + assert_response(201) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal(nil, result['subject']) + assert_equal('some body', result['body']) + assert_equal('text/html', result['content_type']) + assert_equal(@agent.id, result['updated_by_id']) + assert_equal(@agent.id, result['created_by_id']) + + assert_equal(4, ticket.articles.count) + assert_equal(0, ticket.articles[0].attachments.count) + assert_equal(0, ticket.articles[1].attachments.count) + assert_equal(1, ticket.articles[2].attachments.count) + assert_equal(1, ticket.articles[3].attachments.count) + + get "/api/v1/ticket_articles/#{result['id']}?expand=true", {}.to_json, @headers.merge('Authorization' => credentials) + assert_response(200) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal(1, result['attachments'].count) + end + + test '02.01 ticket create with customer and articles' do + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('tickets-customer1@example.com', 'customer1pw') + + params = { + title: 'a new ticket #2', + group: 'Users', + article: { + body: 'some body', + } + } + post '/api/v1/tickets', params.to_json, @headers.merge('Authorization' => credentials) + assert_response(201) + result = JSON.parse(@response.body) + + params = { + ticket_id: result['id'], + content_type: 'text/plain', # or text/html + body: 'some body', + type: 'note', + } + post '/api/v1/ticket_articles', params.to_json, @headers.merge('Authorization' => credentials) + assert_response(201) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal(nil, result['subject']) + assert_equal('some body', result['body']) + assert_equal('text/plain', result['content_type']) + assert_equal(@customer_without_org.id, result['updated_by_id']) + assert_equal(@customer_without_org.id, result['created_by_id']) + + ticket = Ticket.find(result['ticket_id']) + assert_equal(2, ticket.articles.count) + assert_equal('Customer', ticket.articles[1].sender.name) + assert_equal(0, ticket.articles[0].attachments.count) + assert_equal(0, ticket.articles[1].attachments.count) + + params = { + ticket_id: result['ticket_id'], + content_type: 'text/plain', # or text/html + body: 'some body', + sender: 'Agent', + type: 'note', + } + post '/api/v1/ticket_articles', params.to_json, @headers.merge('Authorization' => credentials) + assert_response(201) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal(nil, result['subject']) + assert_equal('some body', result['body']) + assert_equal('text/plain', result['content_type']) + assert_equal(@customer_without_org.id, result['updated_by_id']) + assert_equal(@customer_without_org.id, result['created_by_id']) + + ticket = Ticket.find(result['ticket_id']) + assert_equal(3, ticket.articles.count) + assert_equal('Customer', ticket.articles[2].sender.name) + assert_equal(false, ticket.articles[2].internal) + assert_equal(0, ticket.articles[0].attachments.count) + assert_equal(0, ticket.articles[1].attachments.count) + assert_equal(0, ticket.articles[2].attachments.count) + + params = { + ticket_id: result['ticket_id'], + content_type: 'text/plain', # or text/html + body: 'some body 2', + sender: 'Agent', + type: 'note', + internal: true, + } + post '/api/v1/ticket_articles', params.to_json, @headers.merge('Authorization' => credentials) + assert_response(201) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal(nil, result['subject']) + assert_equal('some body 2', result['body']) + assert_equal('text/plain', result['content_type']) + assert_equal(@customer_without_org.id, result['updated_by_id']) + assert_equal(@customer_without_org.id, result['created_by_id']) + + ticket = Ticket.find(result['ticket_id']) + assert_equal(4, ticket.articles.count) + assert_equal('Customer', ticket.articles[3].sender.name) + assert_equal(false, ticket.articles[3].internal) + assert_equal(0, ticket.articles[0].attachments.count) + assert_equal(0, ticket.articles[1].attachments.count) + assert_equal(0, ticket.articles[2].attachments.count) + assert_equal(0, ticket.articles[3].attachments.count) + + # add internal article + article = Ticket::Article.create( + ticket_id: ticket.id, + from: 'some_sender@example.com', + to: 'some_recipient@example.com', + subject: 'some subject', + message_id: 'some@id', + body: 'some message 123', + internal: true, + sender: Ticket::Article::Sender.find_by(name: 'Agent'), + type: Ticket::Article::Type.find_by(name: 'note'), + updated_by_id: 1, + created_by_id: 1, + ) + assert_equal(5, ticket.articles.count) + assert_equal('Agent', ticket.articles[4].sender.name) + assert_equal(1, ticket.articles[4].updated_by_id) + assert_equal(1, ticket.articles[4].created_by_id) + assert_equal(0, ticket.articles[0].attachments.count) + assert_equal(0, ticket.articles[1].attachments.count) + assert_equal(0, ticket.articles[2].attachments.count) + assert_equal(0, ticket.articles[3].attachments.count) + assert_equal(0, ticket.articles[4].attachments.count) + + get "/api/v1/ticket_articles/#{article.id}", {}.to_json, @headers.merge('Authorization' => credentials) + assert_response(401) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal('Not authorized', result['error']) + + put "/api/v1/ticket_articles/#{article.id}", { internal: false }.to_json, @headers.merge('Authorization' => credentials) + assert_response(401) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal('Not authorized', result['error']) + + end + +end diff --git a/test/controllers/tickets_controller_test.rb b/test/controllers/tickets_controller_test.rb index 9b6cc148e..5fe9c8537 100644 --- a/test/controllers/tickets_controller_test.rb +++ b/test/controllers/tickets_controller_test.rb @@ -263,6 +263,231 @@ class TicketsControllerTest < ActionDispatch::IntegrationTest assert_equal(@agent.id, result['created_by_id']) end + test '01.10 ticket create with agent - minimal article with missing body - with customer' do + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('tickets-agent@example.com', 'agentpw') + params = { + title: 'a new ticket #10', + group: 'Users', + customer_id: @customer_without_org.id, + article: { + subject: 'some test 123', + }, + } + post '/api/v1/tickets', params.to_json, @headers.merge('Authorization' => credentials) + assert_response(422) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal('Need at least article: { body: "some text" }', result['error']) + end + + test '01.11 ticket create with agent - minimal article and attachment with customer' do + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('tickets-agent@example.com', 'agentpw') + params = { + title: 'a new ticket #11', + group: 'Users', + customer_id: @customer_without_org.id, + article: { + subject: 'some test 123', + body: 'some test 123', + attachments: [ + 'filename' => 'some_file.txt', + 'data' => 'dGVzdCAxMjM=', + 'mime-type' => 'text/plain', + ], + }, + } + post '/api/v1/tickets', params.to_json, @headers.merge('Authorization' => credentials) + assert_response(201) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal(Ticket::State.lookup(name: 'new').id, result['state_id']) + assert_equal('a new ticket #11', result['title']) + assert_equal(@customer_without_org.id, result['customer_id']) + assert_equal(@agent.id, result['updated_by_id']) + assert_equal(@agent.id, result['created_by_id']) + + ticket = Ticket.find(result['id']) + assert_equal(1, ticket.articles.count) + assert_equal(1, ticket.articles.first.attachments.count) + file = ticket.articles.first.attachments.first + assert_equal('test 123', file.content) + assert_equal('some_file.txt', file.filename) + assert_equal('text/plain', file.preferences['Mime-Type']) + assert_not(file.preferences['Content-ID']) + end + + test '01.12 ticket create with agent - minimal article and attachment with customer' do + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('tickets-agent@example.com', 'agentpw') + params = { + title: 'a new ticket #12', + group: 'Users', + customer_id: @customer_without_org.id, + article: { + subject: 'some test 123', + body: 'some test 123', + attachments: [ + { + 'filename' => 'some_file1.txt', + 'data' => 'dGVzdCAxMjM=', + 'mime-type' => 'text/plain', + }, + { + 'filename' => 'some_file2.txt', + 'data' => 'w6TDtsO8w58=', + 'mime-type' => 'text/plain', + }, + ], + }, + } + post '/api/v1/tickets', params.to_json, @headers.merge('Authorization' => credentials) + assert_response(201) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal(Ticket::State.lookup(name: 'new').id, result['state_id']) + assert_equal('a new ticket #12', result['title']) + assert_equal(@customer_without_org.id, result['customer_id']) + assert_equal(@agent.id, result['updated_by_id']) + assert_equal(@agent.id, result['created_by_id']) + + ticket = Ticket.find(result['id']) + assert_equal(1, ticket.articles.count) + assert_equal(2, ticket.articles.first.attachments.count) + file = ticket.articles.first.attachments.first + assert_equal('test 123', file.content) + assert_equal('some_file1.txt', file.filename) + assert_equal('text/plain', file.preferences['Mime-Type']) + assert_not(file.preferences['Content-ID']) + end + + test '01.13 ticket create with agent - minimal article and attachment missing mine-type with customer' do + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('tickets-agent@example.com', 'agentpw') + params = { + title: 'a new ticket #13', + group: 'Users', + customer_id: @customer_without_org.id, + article: { + subject: 'some test 123', + body: 'some test 123', + attachments: [ + 'filename' => 'some_file.txt', + 'data' => 'ABC_INVALID_BASE64', + 'mime-type' => 'text/plain', + ], + }, + } + post '/api/v1/tickets', params.to_json, @headers.merge('Authorization' => credentials) + assert_response(422) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal('Invalid base64 for attachment with index \'0\'', result['error']) + end + + test '01.14 ticket create with agent - minimal article and attachment invalid base64 with customer' do + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('tickets-agent@example.com', 'agentpw') + params = { + title: 'a new ticket #14', + group: 'Users', + customer_id: @customer_without_org.id, + article: { + subject: 'some test 123', + body: 'some test 123', + attachments: [ + 'filename' => 'some_file.txt', + 'data' => 'dGVzdCAxMjM=', + ], + }, + } + post '/api/v1/tickets', params.to_json, @headers.merge('Authorization' => credentials) + assert_response(422) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal('Attachment needs \'mime-type\' param for attachment with index \'0\'', result['error']) + end + + test '01.15 ticket create with agent - minimal article and inline attachments with customer' do + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('tickets-agent@example.com', 'agentpw') + params = { + title: 'a new ticket #15', + group: 'Users', + customer_id: @customer_without_org.id, + article: { + content_type: 'text/html', + subject: 'some test 123', + body: 'some test 123 Red dot ', + }, + } + + post '/api/v1/tickets', params.to_json, @headers.merge('Authorization' => credentials) + assert_response(201) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal(Ticket::State.lookup(name: 'new').id, result['state_id']) + assert_equal('a new ticket #15', result['title']) + assert_equal(@customer_without_org.id, result['customer_id']) + assert_equal(@agent.id, result['updated_by_id']) + assert_equal(@agent.id, result['created_by_id']) + + ticket = Ticket.find(result['id']) + assert_equal(1, ticket.articles.count) + assert_equal(2, ticket.articles.first.attachments.count) + file = ticket.articles.first.attachments[0] + assert_equal('d3c1e09bdefb92b6a06b791a24ca9599', Digest::MD5.hexdigest(file.content)) + assert_match(/#{ticket.id}\..+?@zammad.example.com/, file.filename) + assert_equal('image/png', file.preferences['Mime-Type']) + assert(file.preferences['Content-ID']) + file = ticket.articles.first.attachments[1] + assert_equal('006a2ca3793b550c8fe444acdeb39252', Digest::MD5.hexdigest(file.content)) + assert_match(/#{ticket.id}\..+?@zammad.example.com/, file.filename) + assert_equal('image/jpeg', file.preferences['Mime-Type']) + assert(file.preferences['Content-ID']) + end + + test '01.16 ticket create with agent - minimal article and inline attachments with customer' do + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('tickets-agent@example.com', 'agentpw') + params = { + title: 'a new ticket #16', + group: 'Users', + customer_id: @customer_without_org.id, + article: { + content_type: 'text/html', + subject: 'some test 123', + body: 'some test 123 ', + attachments: [ + 'filename' => 'some_file.txt', + 'data' => 'dGVzdCAxMjM=', + 'mime-type' => 'text/plain', + ], + }, + } + + post '/api/v1/tickets', params.to_json, @headers.merge('Authorization' => credentials) + assert_response(201) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal(Ticket::State.lookup(name: 'new').id, result['state_id']) + assert_equal('a new ticket #16', result['title']) + assert_equal(@customer_without_org.id, result['customer_id']) + assert_equal(@agent.id, result['updated_by_id']) + assert_equal(@agent.id, result['created_by_id']) + + ticket = Ticket.find(result['id']) + assert_equal(1, ticket.articles.count) + assert_equal(2, ticket.articles.first.attachments.count) + file = ticket.articles.first.attachments[0] + assert_equal('006a2ca3793b550c8fe444acdeb39252', Digest::MD5.hexdigest(file.content)) + assert_match(/#{ticket.id}\..+?@zammad.example.com/, file.filename) + assert_equal('image/jpeg', file.preferences['Mime-Type']) + assert(file.preferences['Content-ID']) + file = ticket.articles.first.attachments[1] + assert_equal('39d0d586a701e199389d954f2d592720', Digest::MD5.hexdigest(file.content)) + assert_equal('some_file.txt', file.filename) + assert_equal('text/plain', file.preferences['Mime-Type']) + assert_not(file.preferences['Content-ID']) + end + test '02.02 ticket create with agent' do credentials = ActionController::HttpAuthentication::Basic.encode_credentials('tickets-agent@example.com', 'agentpw') params = { diff --git a/test/unit/ticket_test.rb b/test/unit/ticket_test.rb index caaccf7ca..a44c34c0e 100644 --- a/test/unit/ticket_test.rb +++ b/test/unit/ticket_test.rb @@ -307,7 +307,7 @@ class TicketTest < ActiveSupport::TestCase end - test 'article attachment helper' do + test 'article attachment helper 1' do ticket1 = Ticket.create( title: 'some article helper test1', @@ -376,10 +376,7 @@ class TicketTest < ActiveSupport::TestCase created_by_id: 1, ) - article_attributes = Ticket::Article.insert_urls( - article1.attributes, - article1.attachments, - ) + article_attributes = Ticket::Article.insert_urls(article1.attributes_with_association_ids) assert_no_match('15.274327094.140938@zammad.example.com', article_attributes['body']) assert_no_match('15.274327094.140939@zammad.example.com', article_attributes['body']) @@ -394,6 +391,92 @@ class TicketTest < ActiveSupport::TestCase assert_equal(store1.id, attachments.first.id) ticket1.destroy - end + + test 'article attachment helper 2' do + + ticket1 = Ticket.create( + title: 'some article helper test2', + group: Group.lookup(name: 'Users'), + customer_id: 2, + state: Ticket::State.lookup(name: 'new'), + priority: Ticket::Priority.lookup(name: '2 normal'), + updated_by_id: 1, + created_by_id: 1, + ) + assert(ticket1, 'ticket created') + + # create inbound article #1 + article1 = Ticket::Article.create( + ticket_id: ticket1.id, + from: 'some_sender@example.com', + to: 'some_recipient@example.com', + subject: 'some subject', + message_id: 'some@id', + content_type: 'text/html', + body: 'some message article helper test2
asdasdBeschreibung: Beschreibung: efqmLogo
', + internal: false, + sender: Ticket::Article::Sender.find_by(name: 'Customer'), + type: Ticket::Article::Type.find_by(name: 'email'), + updated_by_id: 1, + created_by_id: 1, + ) + + store1 = Store.add( + object: 'Ticket::Article', + o_id: article1.id, + data: 'content_file1_normally_should_be_an_image', + filename: 'some_file1.jpg', + preferences: { + 'Content-Type' => 'image/jpeg', + 'Mime-Type' => 'image/jpeg', + 'Content-ID' => '15.274327094.140938@zammad.example.com', + 'Content-Disposition' => 'inline' + }, + created_by_id: 1, + ) + store2 = Store.add( + object: 'Ticket::Article', + o_id: article1.id, + data: 'content_file2_normally_should_be_an_image', + filename: 'some_file2.jpg', + preferences: { + 'Content-Type' => 'image/jpeg', + 'Mime-Type' => 'image/jpeg', + 'Content-ID' => '15.274327094.140939@zammad.example.com', + 'Content-Disposition' => 'inline' + }, + created_by_id: 1, + ) + store3 = Store.add( + object: 'Ticket::Article', + o_id: article1.id, + data: 'content_file3', + filename: 'some_file3.txt', + preferences: { + 'Content-Type' => 'text/stream', + 'Mime-Type' => 'text/stream', + 'Content-ID' => '15.274327094.99999@zammad.example.com', + 'Content-Disposition' => 'inline' + }, + created_by_id: 1, + ) + + article_attributes = Ticket::Article.insert_urls(article1.attributes_with_association_ids) + + assert_no_match('15.274327094.140938@zammad.example.com', article_attributes['body']) + assert_no_match('15.274327094.140939@zammad.example.com', article_attributes['body']) + assert_no_match('15.274327094.99999@zammad.example.com', article_attributes['body']) + assert_match("api/v1/ticket_attachment/#{ticket1.id}/#{article1.id}/#{store1.id}", article_attributes['body']) + assert_match("api/v1/ticket_attachment/#{ticket1.id}/#{article1.id}/#{store2.id}", article_attributes['body']) + assert_no_match("api/v1/ticket_attachment/#{ticket1.id}/#{article1.id}/#{store3.id}", article_attributes['body']) + + article1 = Ticket::Article.find(article1.id) + attachments = article1.attachments_inline + assert_equal(2, attachments.length) + assert_equal(store1.id, attachments.first.id) + + ticket1.destroy + end + end