Export location model and first pass export to configurable endpoint.

pull/501/head
Mikhael Rakauskas 4 months ago
parent c9af371bc0
commit 53d0dee930

@ -2,33 +2,40 @@
require 'faraday' require 'faraday'
class ExportController < ApplicationController class ExportController < ApplicationController
skip_authorization_check
skip_before_action :maybe_redirect_to_setup skip_before_action :maybe_redirect_to_setup
skip_before_action :verify_authenticity_token skip_before_action :verify_authenticity_token
# Template is sent as JSON already; we're just gonnna send it on to the third party # Template is sent as JSON already; we're just gonnna send it on to the third party.
# It's assumed that any extra details required will also be passed through from the front end -
# set by the ExportLocation.extra_params
def export_template def export_template
conn = Faraday.new(url: 'https://api.thirdparty.com') do |faraday| export_location = ExportLocation.default_location
conn = Faraday.new(url: export_location.api_base_url) do |faraday|
faraday.request :json faraday.request :json
faraday.response :json faraday.response :json
faraday.adapter Faraday.default_adapter faraday.adapter Faraday.default_adapter
end end
response = conn.post('/endpoint') do |req| response = conn.post(export_location.templates_endpoint) do |req|
req.headers['Authorization'] = 'Bearer YOUR_API_KEY' # req.headers['Authorization'] = "Bearer #{export_location.authorization_token}" lol
# Pass along the entire JSON payload received in the request
req.body = request.raw_post.present? ? JSON.parse(request.raw_post) : params.to_unsafe_h req.body = request.raw_post.present? ? JSON.parse(request.raw_post) : params.to_unsafe_h
req.body.merge!(export_location.extra_params) if export_location.extra_params
end end
if response.success? if response.success?
redirect_to templates_path, alert: I18n.t('exports.templates.success') head :ok # alert: I18n.t('exports.templates.success')
else else
Rollbar.error("Third party API error: #{response.status} - #{response.body}") if defined?(Rollbar) Rails.logger.error("Failed to send to third party Faraday: #{response.status}")
redirect_to templates_path, alert: I18n.t('exports.templates.api_error') Rollbar.error("#{export_location.name} API error: #{response.status}") if defined?(Rollbar)
head :ok # templates_path, alert: I18n.t('exports.templates.api_error')
end end
rescue Faraday::Error => e rescue Faraday::Error => e
Rails.logger.error("Failed to send to third party Faraday: #{e.message}")
Rollbar.error("Failed to send to third party: #{e.message}") if defined?(Rollbar) Rollbar.error("Failed to send to third party: #{e.message}") if defined?(Rollbar)
redirect_to templates_path, alert: I18n.t('exports.templates.api_error') redirect_to templates_path, alert: I18n.t('exports.templates.api_error')
rescue StandardError => e rescue StandardError => e
Rails.logger.error("Failed to send to third party: #{e.message}")
Rollbar.error(e) if defined?(Rollbar) Rollbar.error(e) if defined?(Rollbar)
redirect_to templates_path, alert: I18n.t('exports.templates.error') redirect_to templates_path, alert: I18n.t('exports.templates.error')
end end

@ -73,6 +73,25 @@
name="buttons" name="buttons"
/> />
<template v-else> <template v-else>
<button
class="base-button"
:class="{ disabled: isExporting }"
v-bind="isExporting ? { disabled: true } : {}"
@click.prevent="onExportClick"
>
<IconInnerShadowTop
v-if="isExporting"
width="22"
class="animate-spin"
/>
<IconDeviceFloppy
v-else
width="22"
/>
<span class="hidden md:inline">
{{ t('Export') }}
</span>
</button>
<span <span
v-if="editable" v-if="editable"
id="save_button_container" id="save_button_container"
@ -1739,6 +1758,14 @@ export default {
} }
} }
}, },
onExportClick () {
this.isExporting = true
this.export().then(() => {
window.Turbo.visit(`/templates/${this.template.id}`)
}).finally(() => {
this.isExporting = false
})
},
scrollToArea (area) { scrollToArea (area) {
const documentRef = this.documentRefs.find((a) => a.document.uuid === area.attachment_uuid) const documentRef = this.documentRefs.find((a) => a.document.uuid === area.attachment_uuid)
@ -1791,6 +1818,25 @@ export default {
this.onSave(this.template) this.onSave(this.template)
} }
}) })
},
export () {
this.baseFetch(`/export/export_template`, {
method: 'PUT',
body: JSON.stringify({
template: {
name: this.template.name,
schema: this.template.schema,
submitters: this.template.submitters,
fields: this.template.fields
}
}),
headers: { 'Content-Type': 'application/json' }
}).then(() => {
console.log('exported!');
// if (this.onSave) {
// this.onSave(this.template)
// }
})
} }
} }
} }

@ -0,0 +1,22 @@
# == Schema Information
#
# Table name: export_locations
#
# id :bigint not null, primary key
# api_base_url :string not null
# authorization_token :string
# default_location :boolean default(FALSE), not null
# extra_params :jsonb not null
# name :string not null
# templates_endpoint :string
# created_at :datetime not null
# updated_at :datetime not null
#
class ExportLocation < ApplicationRecord
validates :name, presence: true
validates :api_base_url, presence: true
def self.default_location
where(default_location: true).first || ExportLocation.first
end
end

@ -852,6 +852,11 @@ en: &en
items: items:
range_with_total: "%{from}-%{to} of %{count} items" range_with_total: "%{from}-%{to} of %{count} items"
range_without_total: "%{from}-%{to} items" range_without_total: "%{from}-%{to} items"
exports:
templates:
error: Error exporting template
api_error: Error with remote endpoint
success: Template exported successfully
es: &es es: &es
authenticate_embedded_form_preview_with_token: Autenticar vista previa del formulario incrustado con token authenticate_embedded_form_preview_with_token: Autenticar vista previa del formulario incrustado con token

@ -55,6 +55,13 @@ Rails.application.routes.draw do
end end
end end
resources :export, controller: 'export' do
collection do
put :export_template
put :export_template_by_id
end
end
resources :verify_pdf_signature, only: %i[create] resources :verify_pdf_signature, only: %i[create]
resource :mfa_setup, only: %i[show new edit create destroy], controller: 'mfa_setup' resource :mfa_setup, only: %i[show new edit create destroy], controller: 'mfa_setup'
resources :account_configs, only: %i[create destroy] resources :account_configs, only: %i[create destroy]

@ -0,0 +1,14 @@
class CreateExportLocations < ActiveRecord::Migration[8.0]
def change
create_table :export_locations do |t|
t.string :name, null: false
t.boolean :default_location, null: false, default: false
t.string :authorization_token
t.string :api_base_url, null: false
# t.string extra_params, null: false, default: '{}'
t.string :templates_endpoint
# t.string other_export_type_endpoints_maybe_one_day
t.timestamps
end
end
end

@ -0,0 +1,5 @@
class AddAuthParamsToExportLocations < ActiveRecord::Migration[8.0]
def change
add_column :export_locations, :extra_params, :jsonb, null: false, default: {}
end
end

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2025_06_18_085322) do ActiveRecord::Schema[8.0].define(version: 2025_07_02_204317) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "btree_gin" enable_extension "btree_gin"
enable_extension "pg_catalog.plpgsql" enable_extension "pg_catalog.plpgsql"
@ -160,7 +160,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_18_085322) do
t.string "event_name", null: false t.string "event_name", null: false
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.index ["submitter_id", "event_name"], name: "index_document_generation_events_on_submitter_id_and_event_name", unique: true, where: "((event_name)::text = ANY ((ARRAY['start'::character varying, 'complete'::character varying])::text[]))" t.index ["submitter_id", "event_name"], name: "index_document_generation_events_on_submitter_id_and_event_name", unique: true, where: "((event_name)::text = ANY (ARRAY[('start'::character varying)::text, ('complete'::character varying)::text]))"
t.index ["submitter_id"], name: "index_document_generation_events_on_submitter_id" t.index ["submitter_id"], name: "index_document_generation_events_on_submitter_id"
end end
@ -177,7 +177,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_18_085322) do
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.index ["account_id", "event_datetime"], name: "index_email_events_on_account_id_and_event_datetime" t.index ["account_id", "event_datetime"], name: "index_email_events_on_account_id_and_event_datetime"
t.index ["email"], name: "index_email_events_on_email" t.index ["email"], name: "index_email_events_on_email"
t.index ["email"], name: "index_email_events_on_email_event_types", where: "((event_type)::text = ANY ((ARRAY['bounce'::character varying, 'soft_bounce'::character varying, 'complaint'::character varying, 'soft_complaint'::character varying])::text[]))" t.index ["email"], name: "index_email_events_on_email_event_types", where: "((event_type)::text = ANY (ARRAY[('bounce'::character varying)::text, ('soft_bounce'::character varying)::text, ('complaint'::character varying)::text, ('soft_complaint'::character varying)::text]))"
t.index ["emailable_type", "emailable_id"], name: "index_email_events_on_emailable" t.index ["emailable_type", "emailable_id"], name: "index_email_events_on_emailable"
t.index ["message_id"], name: "index_email_events_on_message_id" t.index ["message_id"], name: "index_email_events_on_message_id"
end end
@ -216,6 +216,17 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_18_085322) do
t.index ["user_id"], name: "index_encrypted_user_configs_on_user_id" t.index ["user_id"], name: "index_encrypted_user_configs_on_user_id"
end end
create_table "export_locations", force: :cascade do |t|
t.string "name", null: false
t.boolean "default_location", default: false, null: false
t.string "authorization_token"
t.string "api_base_url", null: false
t.string "templates_endpoint"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.jsonb "extra_params", default: {}, null: false
end
create_table "oauth_access_grants", force: :cascade do |t| create_table "oauth_access_grants", force: :cascade do |t|
t.integer "resource_owner_id", null: false t.integer "resource_owner_id", null: false
t.integer "application_id", null: false t.integer "application_id", null: false

Loading…
Cancel
Save