Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GO-128 Download messages from FS #483

Merged
merged 38 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
bb0a0a0
Draft downloading sent FS message
luciajanikova Oct 15, 2024
0ac9aa8
Move creating message with thread to Fs::Message
luciajanikova Oct 15, 2024
ce6ab5b
Save correlation_id to Fs::MessageDraft
luciajanikova Oct 16, 2024
95e9b4e
Update downloading FS sent messages & Add downloading received messages
luciajanikova Oct 16, 2024
154ee47
Remove format_html_visualization method
luciajanikova Oct 16, 2024
6e6ac84
Update Fs::Api
luciajanikova Oct 16, 2024
823fe35
Small syntax updates
luciajanikova Oct 16, 2024
0dbe7f0
Draft custom FS visualizations
luciajanikova Oct 17, 2024
41e10c9
Update visualizations
luciajanikova Oct 22, 2024
47899d5
Create PDF from message HTML visualization
luciajanikova Oct 22, 2024
fdbe251
Update FS HTML visualizations templates
luciajanikova Oct 22, 2024
a32b402
Update datetime operations & FS API response updates
luciajanikova Oct 22, 2024
8ac7511
Add submission_type_identifier attribute to Fs::Form
luciajanikova Oct 22, 2024
2f935b6
Use paging when requesting FS messages
luciajanikova Oct 22, 2024
351dce0
Draft Fs boxes sync
luciajanikova Oct 23, 2024
fd14363
Update brakeman ignore
luciajanikova Oct 23, 2024
be51628
Update checks in download messages jobs
luciajanikova Oct 23, 2024
e19f6e4
Small syntax updates
luciajanikova Oct 23, 2024
1b81b73
Update sync FS box jobs
luciajanikova Oct 23, 2024
7d2fd3e
Formatting updates
luciajanikova Oct 23, 2024
0747cc4
Update Fs::MessageDraft visualizations
luciajanikova Oct 23, 2024
6840c4f
Add display_url to generated PDF
luciajanikova Oct 23, 2024
50ca8c5
Small fixes after PROD testing
luciajanikova Oct 23, 2024
6dbbe2c
Update Fs::DownloadSentMessageJob
luciajanikova Oct 24, 2024
58bfcc2
Merge branch 'main' into GO-128/download_messages_from_fs
luciajanikova Oct 24, 2024
63ce7b3
Schedule Fs::FetchFormsJob in latest migration
luciajanikova Oct 25, 2024
a5167d9
Add style to Fs::Messages HTML visualizations (to use same font as in…
luciajanikova Oct 25, 2024
1c38460
Merge branch 'main' into GO-128/download_messages_from_fs
luciajanikova Oct 29, 2024
266ffa0
Make download jobs fail if any error
luciajanikova Nov 6, 2024
84f1650
Send uuids to FS API when submitting
luciajanikova Nov 7, 2024
dcfd011
Copy tags to downloaded FS outbox message from draft
luciajanikova Nov 11, 2024
5d9e89d
Update Automation::Condition in order to be able to create Automation…
luciajanikova Nov 11, 2024
5d135b3
Add missing fixtures
luciajanikova Nov 12, 2024
7307fae
Add missing fixtures 2
luciajanikova Nov 12, 2024
9de9135
Update automation to be able to add tag to thread after form object i…
luciajanikova Nov 12, 2024
16dc11c
make grover work in docker container
celuchmarek Nov 12, 2024
6a97f21
Update download message object event
luciajanikova Nov 12, 2024
8474ba5
Try to update Dockerfile
luciajanikova Nov 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ SITE_ADMIN_EMAILS=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOD_JOB_EXECUTION_MODE=
GROVER_NO_SANDBOX=true # must be true for running chromium as root in Docker container
ADMIN_IDS=
LOG_LEVEL=
DB_HOST=
Expand All @@ -24,3 +25,4 @@ UPVS_SSO_SUBJECT=
UPVS_SSO_SP_CERTIFICATE=
UPVS_SSO_SP_PRIVATE_KEY=
UPVS_ENV=
PDF_DISPLAY_URL=
2 changes: 2 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ ZwIDAQAB
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=1
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=2
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=3
GROVER_NO_SANDBOX=true # must be true for running chromium as root in Docker container
PDF_DISPLAY_URL=
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM ruby:3.3.0

# Install packages
RUN apt-get update && apt-get install -y build-essential nodejs libpq-dev npm fop=1:2.* libsaxon-java libsaxonb-java
RUN apt-get update && apt-get install -y build-essential nodejs libpq-dev npm fop=1:2.* libsaxon-java libsaxonb-java chromium

# Setup FOP to use saxon xslt parser
RUN sed -i '/find_jars/i \
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ gem 'jwt'
gem 'stimulus-rails'
gem 'jsbundling-rails'
gem 'pdf-reader'
gem 'grover'

# Monitoring
gem 'rollbar'
Expand Down
7 changes: 7 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ GEM
activesupport
tzinfo
coderay (1.1.3)
combine_pdf (1.0.26)
matrix
ruby-rc4 (>= 0.1.5)
concurrent-ruby (1.2.3)
connection_pool (2.4.1)
crack (1.0.0)
Expand Down Expand Up @@ -164,6 +167,9 @@ GEM
fugit (>= 1.1)
railties (>= 6.0.0)
thor (>= 0.14.1)
grover (1.1.11)
combine_pdf (~> 1.0)
nokogiri (~> 1.0)
hashdiff (1.1.0)
hashery (2.1.2)
hashie (5.0.0)
Expand Down Expand Up @@ -512,6 +518,7 @@ DEPENDENCIES
erb_lint
foreman
good_job
grover
htmlbeautifier
importmap-rails
jbuilder
Expand Down
2 changes: 1 addition & 1 deletion app/components/message_draft_body_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@

<% if @message.html_visualization.present? %>
<div class="w-full">
<%= tag.iframe class: "relative border-none overflow-hidden h-full w-full", srcdoc: @message.format_html_visualization, onload: "(
<%= tag.iframe class: "relative border-none overflow-hidden h-full w-full", srcdoc: @message.html_visualization, onload: "(
function(iframe) {
iframe.contentWindow.document.body.style['height'] = 'unset';
iframe.contentWindow.document.body.style['min-height'] = 'unset';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ def before_render
@trigger_events_list = [
[t('message_created'), 'message_created'],
[t('message_draft_submitted'), 'message_draft_submitted'],
[t('message_object_downloaded'), 'message_object_downloaded'],
]
end
end
5 changes: 5 additions & 0 deletions app/controllers/message_objects_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ def show

def download
authorize @message_object

EventBus.publish(:message_object_downloaded, @message_object)

send_data @message_object.content, filename: MessageObjectHelper.displayable_name(@message_object), type: @message_object.mimetype, disposition: :download
end

Expand All @@ -34,6 +37,8 @@ def download_pdf

pdf_content = @message_object.prepare_pdf_visualization
if pdf_content
EventBus.publish(:message_object_downloaded, @message_object)

send_data pdf_content, filename: MessageObjectHelper.pdf_name(@message_object), type: 'application/pdf', disposition: :download
else
redirect_back fallback_location: message_thread_path(@message_object.message.thread), alert: "Obsah nie je možné stiahnuť."
Expand Down
6 changes: 3 additions & 3 deletions app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ def nice_datetime(datetime)
end
end

def nice_datetime_with_time(datetime)
if datetime.today?
def nice_datetime_with_time(datetime, full_date: false)
if datetime.today? && !full_date
l(datetime, format: '%H:%M')
elsif datetime.year == Date.current.year
elsif datetime.year == Date.current.year && !full_date
l(datetime, format: '%e. %b %H:%M')
else
l(datetime, format: '%e. %b %Y %H:%M')
Expand Down
23 changes: 23 additions & 0 deletions app/helpers/fs/message_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module Fs::MessageHelper
def self.build_html_visualization(message)
return [ActionController::Base.new.render_to_string('fs/messages/_submission', layout: false, locals: { message: message }), build_html_visualization_from_form(message)].compact.join('<hr>') if message.outbox?

# TODO: Vieme aj lepsie identifikovat? Nejake dalsie typy v tejto kategorii neexistuju?
Copy link
Member Author

@luciajanikova luciajanikova Oct 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Toto tu je otvorena vec. FS API v response vracia aj message_type, da sa identifikovat aj podla toho (ale tiez to je trochu len take hadanie, ked nemame nejaku dokumentaciu k tomu). Nevieme ako casto sa mozu zmenit bud tieto nazvy alebo message_type.
Samotna dorucenka si myslim, ze problem nie je (tam by som sa spolahla aj na message_type), ale tieto negativne verzie su narocnejsie. Nevieme ani kolko typov existuje.

template = if message.title.in?(['Informácia o podaní', 'Informácia o odmietnutí podania'])
'fs/messages/_delivery_report'
else
'fs/messages/_generic_message'
end

ActionController::Base.new.render_to_string(template, layout: false, locals: { message: message })
end

def self.build_html_visualization_from_form(message)
raise 'Missing Fs::Form XSLT' unless message.form&.xslt_txt
return unless message.form_object&.unsigned_content

template = Nokogiri::XSLT(message.form.xslt_txt)

ActionController::Base.new.render_to_string('fs/messages/_style', layout: false, locals: { message: message }) + ActionController::Base.helpers.simple_format(template.transform(message.form_object.xml_unsigned_content).to_s)
end
end
6 changes: 0 additions & 6 deletions app/helpers/message_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,4 @@ module MessageHelper
def self.export_filename(message)
"#{message.delivered_at.to_date}-sprava-#{message.id}.zip"
end

def format_html_visualization
return ActionController::Base.helpers.simple_format(html_visualization) if is_a?(Fs::MessageDraft)

html_visualization
end
end
18 changes: 18 additions & 0 deletions app/jobs/fs/download_received_message_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module Fs
class DownloadReceivedMessageJob < ApplicationJob
def perform(fs_message_id, box:, fs_client: FsEnvironment.fs_client)
raise unless box.is_a?(Fs::Box)
return unless box.syncable?

return if box.messages.where("metadata ->> 'fs_message_id' = ?", fs_message_id).any?

ActiveRecord::Base.transaction do
fs_api = fs_client.api(api_connection: box.api_connection, box: box)

raw_message = fs_api.fetch_received_message(fs_message_id)

Fs::Message.create_inbox_message_with_thread!(raw_message, box: box)
end
end
end
end
20 changes: 20 additions & 0 deletions app/jobs/fs/download_sent_message_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module Fs
class DownloadSentMessageJob < ApplicationJob
def perform(fs_message_id, box:, fs_client: FsEnvironment.fs_client)
raise unless box.is_a?(Fs::Box)
return unless box.syncable?

return if box.messages.where(type: [nil, 'Message']).where("metadata ->> 'fs_message_id' = ?", fs_message_id).any?

ActiveRecord::Base.transaction do
fs_api = fs_client.api(api_connection: box.api_connection, box: box)

raw_message = fs_api.fetch_sent_message(fs_message_id)

message = Fs::Message.create_outbox_message_with_thread!(raw_message, box: box)

DownloadSentMessageRelatedMessagesJob.set(wait: 3.minutes).perform_later(message)
end
end
end
end
20 changes: 20 additions & 0 deletions app/jobs/fs/download_sent_message_related_messages_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module Fs
class DownloadSentMessageRelatedMessagesJob < ApplicationJob
def perform(outbox_message, from: nil, to: nil, fs_client: FsEnvironment.fs_client, batch_size: 25)
raise unless outbox_message.box.is_a?(Fs::Box)
return unless outbox_message.box.syncable?

fs_api = fs_client.api(api_connection: outbox_message.box.api_connection, box: outbox_message.box)

0.step do |k|
received_messages = fs_api.fetch_received_messages(sent_message_id: outbox_message.metadata['fs_message_id'], page: k + 1, count: batch_size, from: from, to: to)

received_messages['messages'].each do |received_message|
::Fs::DownloadReceivedMessageJob.perform_later(received_message['message_id'], box: outbox_message.box)
end

break if received_messages['messages'].size < batch_size
end
end
end
end
1 change: 1 addition & 0 deletions app/jobs/fs/fetch_forms_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ def perform(fs_client: FsEnvironment.fs_client, download_related_documents_job:
identifier: fs_form_data['identifier'],
).tap do |form|
form.update(
submission_type_identifier: fs_form_data['submission_type_identifier'],
name: fs_form_data['name'],
group_name: fs_form_data['form_group_name'],
subtype_name: fs_form_data['subtype_name'],
Expand Down
2 changes: 2 additions & 0 deletions app/jobs/fs/submit_message_draft_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ def perform(message_draft, bulk_submit: false, fs_client: FsEnvironment.fs_clien
response = fs_api.post_submission(
message_draft.form.identifier,
Base64.strict_encode64(message_draft.form_object.content),
message_uuid: message_draft.uuid,
form_object_uuid: message_draft.form_object.uuid,
allow_warn_status: true,
is_signed: message_draft.form_object.is_signed,
mime_type: message_draft.form_object.mimetype
Expand Down
3 changes: 3 additions & 0 deletions app/jobs/fs/submit_message_draft_result_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ def perform(message_draft, location_header, fs_client: FsEnvironment.fs_client)

if 200 == response[:status]
message_draft.metadata[:status] = 'submitted'
message_draft.metadata[:fs_message_id] = response[:body]['sent_message_id']
message_draft.save

::Fs::DownloadSentMessageJob.perform_later(response[:body]['sent_message_id'], box: message_draft.box)
elsif [400, 422].include?(response[:status])
message_draft.metadata[:status] = 'submit_fail'
message_draft.add_cascading_tag(message_draft.tenant.submission_error_tag)
Expand Down
12 changes: 12 additions & 0 deletions app/jobs/fs/sync_all_boxes_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module Fs
class SyncAllBoxesJob < ApplicationJob
def perform
Fs::Box.where(syncable: true).find_each do |box|
SyncBoxJob.perform_later(box)
end

# TODO ponastavovat v BetterUptime
BetterUptimeApi.ping_heartbeat('FS_SYNC')
Comment on lines +8 to +9
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@celuchmarek pridame heartbeat? Mozem skusit aj ja ponastavovat, ale povedz, aby som ti nebabrala do toho pripadne.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jj, môžeš pridať a nastaviť. Todo comment vyhoď, to nastavíš hneď.

end
end
end
12 changes: 12 additions & 0 deletions app/jobs/fs/sync_box_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module Fs
class SyncBoxJob < ApplicationJob
def perform(box, from: Date.today - 1.week, to: Date.tomorrow)
raise unless box.is_a?(Fs::Box)
return unless box.syncable?

box.messages.outbox.find_each do |outbox_message|
DownloadSentMessageRelatedMessagesJob.perform_later(outbox_message, from: from, to: to)
end
end
end
end
2 changes: 1 addition & 1 deletion app/lib/event_bus.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def self.reset!
# wiring

# automation
[:message_thread_created, :message_created, :message_draft_submitted].each do |event|
[:message_thread_created, :message_created, :message_draft_submitted, :message_object_downloaded].each do |event|
EventBus.subscribe_job event, Automation::ApplyRulesForEventJob
end

Expand Down
22 changes: 16 additions & 6 deletions app/lib/fs/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,27 @@ def get_subjects
end

def fetch_sent_messages(page: 1, count: 100, obo: @obo)
request(:get, "sent-messages", {}, jwt_header(obo).merge(fs_credentials_header))[:body]
request(:get, "sent-messages?page=#{page}&per_page=#{count}", {}, jwt_header(obo).merge(fs_credentials_header))[:body]
end

def fetch_sent_message(message_id, obo: @obo)
request(:get, "sent-messages/#{message_id}", {}, jwt_header(obo).merge(fs_credentials_header))[:body]
request(:get, "sent-messages/#{CGI.escape(message_id)}", {}, jwt_header(obo).merge(fs_credentials_header))[:body]
end

def fetch_received_messages(page: 1, count: 100, obo: @obo)
request(:get, "received-messages", {}, jwt_header(obo).merge(fs_credentials_header))[:body]
def fetch_received_messages(sent_message_id: nil, page: 1, count: 100, from: nil, to: nil, obo: @obo)
query = {
sent_message_id: sent_message_id,
page: page,
per_page: count,
from: from,
to: to
}.compact.to_query

request(:get, "received-messages?#{query}", {}, jwt_header(obo).merge(fs_credentials_header))[:body]
end

def fetch_received_message(message_id, obo: @obo)
request(:get, "received-messages/#{message_id}", {}, jwt_header(obo).merge(fs_credentials_header))[:body]
request(:get, "received-messages/#{CGI.escape(message_id)}", {}, jwt_header(obo).merge(fs_credentials_header))[:body]
end

def post_validation(form_identifier, content)
Expand All @@ -52,8 +60,10 @@ def delete_validation(validation_id)
request(:delete, "validations/#{validation_id}", {}, jwt_header, accept_negative: true)
end

def post_submission(form_identifier, content, allow_warn_status: true, is_signed: true, mime_type: 'application/vnd.etsi.asic-e+zip', obo: @obo)
def post_submission(form_identifier, content, allow_warn_status: true, message_uuid:, form_object_uuid:, is_signed: true, mime_type: 'application/vnd.etsi.asic-e+zip', obo: @obo)
request(:post, "submissions", {
message_container_message_id: message_uuid,
message_container_form_object_id: form_object_uuid,
is_signed: is_signed,
mime_type: mime_type,
form_identifier: form_identifier,
Expand Down
1 change: 0 additions & 1 deletion app/lib/utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ def file_extension_by_mimetype(mimetype)

# TODO use UPVS API to detect if document is signed
def is_signed?(entry_name:, content:)

case File.extname(entry_name).downcase
when '.asice', '.asics', '.xzep'
true
Expand Down
20 changes: 17 additions & 3 deletions app/models/automation/condition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Condition < ApplicationRecord
attr_accessor :delete_record

# when adding items, check defaults in condition_form_component.rb
ATTR_LIST = %i[box sender_name recipient_name title sender_uri recipient_uri attachment].freeze
ATTR_LIST = %i[box sender_name recipient_name title sender_uri recipient_uri attachment fs_submission_status fs_message_type object_type].freeze

def valid_condition_type_list_for_attr
Automation::Condition.subclasses.map do |subclass|
Expand All @@ -36,7 +36,7 @@ def box_list

class ContainsCondition < Automation::Condition
validates :value, presence: true
VALID_ATTR_LIST = %w[sender_name recipient_name title].freeze
VALID_ATTR_LIST = %w[sender_name recipient_name title object_type].freeze
validates :attr, inclusion: { in: VALID_ATTR_LIST }

def satisfied?(thing)
Expand All @@ -50,7 +50,7 @@ def cleanup_record

class MetadataValueCondition < Automation::Condition
validates :value, presence: true
VALID_ATTR_LIST = %w[sender_uri recipient_uri].freeze
VALID_ATTR_LIST = %w[sender_uri recipient_uri fs_submission_status fs_message_type].freeze
validates :attr, inclusion: { in: VALID_ATTR_LIST }

def satisfied?(thing)
Expand Down Expand Up @@ -81,6 +81,20 @@ def cleanup_record
end
end

class MessageMetadataValueCondition < Automation::Condition
validates :value, presence: true
VALID_ATTR_LIST = %w[fs_message_type].freeze
validates :attr, inclusion: { in: VALID_ATTR_LIST }

def satisfied?(thing)
thing.message.metadata && thing.message.metadata[attr]&.match?(value)
end

def cleanup_record
self.condition_object = nil
end
end

class AttachmentContentContainsCondition < Automation::Condition
validates :value, presence: true
VALID_ATTR_LIST = ['attachment'].freeze
Expand Down
Loading
Loading