From b44dc45a3ebfaaa9e4d517401f2e898bac0def1d Mon Sep 17 00:00:00 2001 From: Ancor Cruz Date: Thu, 27 Jun 2024 10:50:10 +0100 Subject: [PATCH 01/11] Add DataExport CreateService receives user, format, resource_type and resource_query, creates a new record and queues a job to produce the export file --- app/jobs/data_exports/export_resources_job.rb | 6 +++ app/services/data_exports/create_service.rb | 24 ++++++++++ .../data_exports/export_resources_job_spec.rb | 5 ++ .../data_exports/create_service_spec.rb | 46 +++++++++++++++++++ 4 files changed, 81 insertions(+) create mode 100644 app/jobs/data_exports/export_resources_job.rb create mode 100644 app/services/data_exports/create_service.rb create mode 100644 spec/jobs/data_exports/export_resources_job_spec.rb create mode 100644 spec/services/data_exports/create_service_spec.rb diff --git a/app/jobs/data_exports/export_resources_job.rb b/app/jobs/data_exports/export_resources_job.rb new file mode 100644 index 00000000000..ec7a27acfd9 --- /dev/null +++ b/app/jobs/data_exports/export_resources_job.rb @@ -0,0 +1,6 @@ +module DataExports + class ExportResourcesJob < ApplicationJob + def perfrom(data_export) + end + end +end diff --git a/app/services/data_exports/create_service.rb b/app/services/data_exports/create_service.rb new file mode 100644 index 00000000000..8e76f61b0ce --- /dev/null +++ b/app/services/data_exports/create_service.rb @@ -0,0 +1,24 @@ +module DataExports + class CreateService < BaseService + def initialize(user:, format:, resource_type:, resource_query:) + @user = user + @format = format + @resource_type = resource_type + @resource_query = resource_query + + super(user) + end + + def call + data_export = DataExport.create!(user:, format:, resource_type:, resource_query:) + ExportResourcesJob.perform_later(data_export) + + result.data_export = data_export + result + end + + private + + attr_reader :user, :format, :resource_type, :resource_query + end +end diff --git a/spec/jobs/data_exports/export_resources_job_spec.rb b/spec/jobs/data_exports/export_resources_job_spec.rb new file mode 100644 index 00000000000..a25a603d180 --- /dev/null +++ b/spec/jobs/data_exports/export_resources_job_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe DataExports::ExportResourcesJob, type: :job do + it "does something" +end diff --git a/spec/services/data_exports/create_service_spec.rb b/spec/services/data_exports/create_service_spec.rb new file mode 100644 index 00000000000..b38f048d0c7 --- /dev/null +++ b/spec/services/data_exports/create_service_spec.rb @@ -0,0 +1,46 @@ +require 'rails_helper' + +RSpec.describe DataExports::CreateService, type: :service do + subject(:result) do + described_class.call(user:, format:, resource_type:, resource_query:) + end + + let(:user) { create(:user) } + + let(:format) { "csv" } + let(:resource_type) { "invoices" } + let(:resource_query) do + { + "search_term" => "service 1", + "filters" => { + "currency" => "USD" + } + } + end + + before do + allow(DataExports::ExportResourcesJob).to receive(:perform_later) + end + + it 'creates a new data export record' do + aggregate_failures do + expect(result).to be_success + + data_export = result.data_export + expect(data_export.id).to be_present + expect(data_export.user_id).to eq(user.id) + expect(data_export.format).to eq("csv") + expect(data_export.resource_type).to eq("invoices") + expect(data_export.resource_query).to match(resource_query) + expect(data_export.status).to eq("pending") + end + end + + it 'calls ExportResourcesJob' do + data_export = result.data_export + + expect(DataExports::ExportResourcesJob) + .to have_received(:perform_later) + .with(data_export) + end +end From d0ad629218f50a62c956680bc3bc1a69b33c4ab1 Mon Sep 17 00:00:00 2001 From: Ancor Cruz Date: Thu, 27 Jun 2024 14:42:58 +0100 Subject: [PATCH 02/11] Add DataExports::ExportResourcesJob this job calls the ExportResourcesService --- app/jobs/data_exports/export_resources_job.rb | 5 ++++- .../data_exports/export_resources_service.rb | 6 ++++++ spec/factories/data_exports.rb | 18 +++++++++++++++--- .../data_exports/export_resources_job_spec.rb | 18 +++++++++++++++++- .../export_resources_service_spec.rb | 7 +++++++ 5 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 app/services/data_exports/export_resources_service.rb create mode 100644 spec/services/data_exports/export_resources_service_spec.rb diff --git a/app/jobs/data_exports/export_resources_job.rb b/app/jobs/data_exports/export_resources_job.rb index ec7a27acfd9..bb6f5a7a7c9 100644 --- a/app/jobs/data_exports/export_resources_job.rb +++ b/app/jobs/data_exports/export_resources_job.rb @@ -1,6 +1,9 @@ module DataExports class ExportResourcesJob < ApplicationJob - def perfrom(data_export) + queue_as :data_export + + def perform(data_export) + ExportResourcesService.call(data_export:).raise_if_error! end end end diff --git a/app/services/data_exports/export_resources_service.rb b/app/services/data_exports/export_resources_service.rb new file mode 100644 index 00000000000..be23f8918a4 --- /dev/null +++ b/app/services/data_exports/export_resources_service.rb @@ -0,0 +1,6 @@ +module DataExports + class ExportResourcesService < BaseService + def call + end + end +end diff --git a/spec/factories/data_exports.rb b/spec/factories/data_exports.rb index 550b6b1e1e9..a1bd2babe4b 100644 --- a/spec/factories/data_exports.rb +++ b/spec/factories/data_exports.rb @@ -7,8 +7,20 @@ resource_query { {filters: {currency: 'EUR'}} } status { 'pending' } file { nil } - expires_at { 7.days.from_now } - started_at { 2.hours.ago } - completed_at { 30.minutes.ago } + + trait :processing do + status { 'processing' } + started_at { 2.hours.ago } + end + + trait :completed do + status { 'completed' } + expires_at { 7.days.from_now } + completed_at { 30.minutes.ago } + end + + trait :failed do + status { 'failed' } + end end end diff --git a/spec/jobs/data_exports/export_resources_job_spec.rb b/spec/jobs/data_exports/export_resources_job_spec.rb index a25a603d180..95ed26e977a 100644 --- a/spec/jobs/data_exports/export_resources_job_spec.rb +++ b/spec/jobs/data_exports/export_resources_job_spec.rb @@ -1,5 +1,21 @@ require 'rails_helper' RSpec.describe DataExports::ExportResourcesJob, type: :job do - it "does something" + let(:data_export) { create(:data_export) } + let(:result) { BaseService::Result.new } + + before do + allow(DataExports::ExportResourcesService) + .to receive(:call) + .with(data_export:) + .and_return(result) + end + + it "calls ExportResources service" do + described_class.perform_now(data_export) + + expect(DataExports::ExportResourcesService) + .to have_received(:call) + .with(data_export:) + end end diff --git a/spec/services/data_exports/export_resources_service_spec.rb b/spec/services/data_exports/export_resources_service_spec.rb new file mode 100644 index 00000000000..1d4a03fbb06 --- /dev/null +++ b/spec/services/data_exports/export_resources_service_spec.rb @@ -0,0 +1,7 @@ +require 'rails_helper' + +RSpec.describe DataExports::ExportResourcesService, type: :service do + subject(:result) { described_class.call(data_export:) } + + it 'does something' +end From 3201116e4855d1e70168df18ef11c6351954a2d6 Mon Sep 17 00:00:00 2001 From: Ancor Cruz Date: Fri, 28 Jun 2024 12:27:07 +0100 Subject: [PATCH 03/11] DataExport belongs to organization and memberhip remove relationship with user to ensure we have access to all the required data (organization, membership and user through membership) --- app/models/data_export.rb | 5 ++++- app/models/membership.rb | 2 ++ app/models/organization.rb | 1 + app/services/data_exports/create_service.rb | 18 +++++++++++++++--- ...p_id_and_organization_id_to_data_exports.rb | 6 ++++++ ...8083830_remove_user_id_from_data_exports.rb | 5 +++++ db/schema.rb | 11 +++++++---- spec/factories/data_exports.rb | 11 +++++++---- spec/models/data_export_spec.rb | 3 ++- spec/models/membership_spec.rb | 2 ++ spec/models/organization_spec.rb | 1 + .../data_exports/create_service_spec.rb | 8 ++++++-- 12 files changed, 58 insertions(+), 15 deletions(-) create mode 100644 db/migrate/20240628083654_add_membership_id_and_organization_id_to_data_exports.rb create mode 100644 db/migrate/20240628083830_remove_user_id_from_data_exports.rb diff --git a/app/models/data_export.rb b/app/models/data_export.rb index 1d553ba91db..7598dc0516e 100644 --- a/app/models/data_export.rb +++ b/app/models/data_export.rb @@ -2,7 +2,8 @@ class DataExport < ApplicationRecord EXPORT_FORMATS = %w[csv].freeze STATUSES = %w[pending processing completed failed].freeze - belongs_to :user + belongs_to :organization + belongs_to :membership has_one_attached :file validates :resource_type, :resource_query, presence: true @@ -11,4 +12,6 @@ class DataExport < ApplicationRecord enum format: EXPORT_FORMATS enum status: STATUSES + + delegate :user, to: :membership end diff --git a/app/models/membership.rb b/app/models/membership.rb index 30f88951ae7..85e15640f0c 100644 --- a/app/models/membership.rb +++ b/app/models/membership.rb @@ -6,6 +6,8 @@ class Membership < ApplicationRecord belongs_to :organization belongs_to :user + has_many :data_exports + STATUSES = [ :active, :revoked diff --git a/app/models/organization.rb b/app/models/organization.rb index 192cd09aea9..16d4c239521 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -32,6 +32,7 @@ class Organization < ApplicationRecord has_many :webhook_endpoints has_many :webhooks, through: :webhook_endpoints has_many :cached_aggregations + has_many :data_exports has_many :stripe_payment_providers, class_name: 'PaymentProviders::StripeProvider' has_many :gocardless_payment_providers, class_name: 'PaymentProviders::GocardlessProvider' diff --git a/app/services/data_exports/create_service.rb b/app/services/data_exports/create_service.rb index 8e76f61b0ce..b7a977ecb9c 100644 --- a/app/services/data_exports/create_service.rb +++ b/app/services/data_exports/create_service.rb @@ -1,6 +1,7 @@ module DataExports class CreateService < BaseService - def initialize(user:, format:, resource_type:, resource_query:) + def initialize(organization:, user:, format:, resource_type:, resource_query:) + @organization = organization @user = user @format = format @resource_type = resource_type @@ -10,7 +11,14 @@ def initialize(user:, format:, resource_type:, resource_query:) end def call - data_export = DataExport.create!(user:, format:, resource_type:, resource_query:) + data_export = DataExport.create( + organization:, + membership:, + format:, + resource_type:, + resource_query: + ) + ExportResourcesJob.perform_later(data_export) result.data_export = data_export @@ -19,6 +27,10 @@ def call private - attr_reader :user, :format, :resource_type, :resource_query + attr_reader :organization, :user, :format, :resource_type, :resource_query + + def membership + user.memberships.find_by(organization: organization) + end end end diff --git a/db/migrate/20240628083654_add_membership_id_and_organization_id_to_data_exports.rb b/db/migrate/20240628083654_add_membership_id_and_organization_id_to_data_exports.rb new file mode 100644 index 00000000000..cb9d921516e --- /dev/null +++ b/db/migrate/20240628083654_add_membership_id_and_organization_id_to_data_exports.rb @@ -0,0 +1,6 @@ +class AddMembershipIdAndOrganizationIdToDataExports < ActiveRecord::Migration[7.1] + def change + add_reference :data_exports, :membership, null: false, foreign_key: true, type: :uuid + add_reference :data_exports, :organization, null: false, foreign_key: true, type: :uuid + end +end diff --git a/db/migrate/20240628083830_remove_user_id_from_data_exports.rb b/db/migrate/20240628083830_remove_user_id_from_data_exports.rb new file mode 100644 index 00000000000..3c59364bad7 --- /dev/null +++ b/db/migrate/20240628083830_remove_user_id_from_data_exports.rb @@ -0,0 +1,5 @@ +class RemoveUserIdFromDataExports < ActiveRecord::Migration[7.1] + def change + remove_reference :data_exports, :user, null: false, foreign_key: true, type: :uuid + end +end diff --git a/db/schema.rb b/db/schema.rb index 9bde0210c54..c1be43b9523 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_06_26_094521) do +ActiveRecord::Schema[7.1].define(version: 2024_06_28_083830) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" @@ -442,7 +442,6 @@ end create_table "data_exports", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| - t.uuid "user_id", null: false t.integer "format" t.string "resource_type", null: false t.jsonb "resource_query", default: {}, null: false @@ -452,7 +451,10 @@ t.datetime "completed_at", precision: nil t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.index ["user_id"], name: "index_data_exports_on_user_id" + t.uuid "membership_id", null: false + t.uuid "organization_id", null: false + t.index ["membership_id"], name: "index_data_exports_on_membership_id" + t.index ["organization_id"], name: "index_data_exports_on_organization_id" end create_table "events", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| @@ -1112,7 +1114,8 @@ add_foreign_key "customers", "organizations" add_foreign_key "customers_taxes", "customers" add_foreign_key "customers_taxes", "taxes" - add_foreign_key "data_exports", "users" + add_foreign_key "data_exports", "memberships" + add_foreign_key "data_exports", "organizations" add_foreign_key "fees", "add_ons" add_foreign_key "fees", "applied_add_ons" add_foreign_key "fees", "charges" diff --git a/spec/factories/data_exports.rb b/spec/factories/data_exports.rb index a1bd2babe4b..de15db86514 100644 --- a/spec/factories/data_exports.rb +++ b/spec/factories/data_exports.rb @@ -1,6 +1,7 @@ FactoryBot.define do factory :data_export do - user + organization + membership { association :membership, organization: organization } format { 'csv' } resource_type { "invoices" } @@ -15,12 +16,14 @@ trait :completed do status { 'completed' } - expires_at { 7.days.from_now } + started_at { 2.hours.ago } completed_at { 30.minutes.ago } + expires_at { 7.days.from_now } end - trait :failed do - status { 'failed' } + trait :expired do + completed + expires_at { 1.day.ago } end end end diff --git a/spec/models/data_export_spec.rb b/spec/models/data_export_spec.rb index f5fc76d0ff9..a8854e415a4 100644 --- a/spec/models/data_export_spec.rb +++ b/spec/models/data_export_spec.rb @@ -1,7 +1,8 @@ require 'rails_helper' RSpec.describe DataExport, type: :model do - it { is_expected.to belong_to(:user) } + it { is_expected.to belong_to(:organization) } + it { is_expected.to belong_to(:membership) } it { is_expected.to validate_presence_of(:format) } it { is_expected.to validate_presence_of(:resource_type) } diff --git a/spec/models/membership_spec.rb b/spec/models/membership_spec.rb index 039fa39f0ad..a6ceb716293 100644 --- a/spec/models/membership_spec.rb +++ b/spec/models/membership_spec.rb @@ -5,6 +5,8 @@ RSpec.describe Membership, type: :model do subject(:membership) { create(:membership) } + it { is_expected.to have_many(:data_exports) } + it_behaves_like 'paper_trail traceable' describe '#mark_as_revoked' do diff --git a/spec/models/organization_spec.rb b/spec/models/organization_spec.rb index bc8e93907ee..360d913a80e 100644 --- a/spec/models/organization_spec.rb +++ b/spec/models/organization_spec.rb @@ -20,6 +20,7 @@ it { is_expected.to have_many(:webhooks).through(:webhook_endpoints) } it { is_expected.to have_many(:netsuite_integrations) } it { is_expected.to have_many(:xero_integrations) } + it { is_expected.to have_many(:data_exports) } it { is_expected.to validate_inclusion_of(:default_currency).in_array(described_class.currency_list) } diff --git a/spec/services/data_exports/create_service_spec.rb b/spec/services/data_exports/create_service_spec.rb index b38f048d0c7..075cc2929a6 100644 --- a/spec/services/data_exports/create_service_spec.rb +++ b/spec/services/data_exports/create_service_spec.rb @@ -2,10 +2,12 @@ RSpec.describe DataExports::CreateService, type: :service do subject(:result) do - described_class.call(user:, format:, resource_type:, resource_query:) + described_class.call(organization:, user:, format:, resource_type:, resource_query:) end + let(:organization) { create(:organization) } let(:user) { create(:user) } + let(:membership) { create(:membership, user:, organization:) } let(:format) { "csv" } let(:resource_type) { "invoices" } @@ -19,6 +21,7 @@ end before do + membership allow(DataExports::ExportResourcesJob).to receive(:perform_later) end @@ -28,7 +31,8 @@ data_export = result.data_export expect(data_export.id).to be_present - expect(data_export.user_id).to eq(user.id) + expect(data_export.organization_id).to eq(organization.id) + expect(data_export.membership_id).to eq(membership.id) expect(data_export.format).to eq("csv") expect(data_export.resource_type).to eq("invoices") expect(data_export.resource_query).to match(resource_query) From ba9dee51cb232a2a8245a6560eb8393ed14e610f Mon Sep 17 00:00:00 2001 From: Ancor Cruz Date: Fri, 28 Jun 2024 15:05:23 +0100 Subject: [PATCH 04/11] Configure rails email previews also, ensure preview does not clutter the development database --- config/environments/development.rb | 1 + spec/mailers/previews/base_preview_mailer.rb | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 spec/mailers/previews/base_preview_mailer.rb diff --git a/config/environments/development.rb b/config/environments/development.rb index 1d0005e4b40..c96bbb279c5 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -63,6 +63,7 @@ address: 'mailhog', port: 1025 } + config.action_mailer.preview_paths << "#{Rails.root}/spec/mailers/previews" Dotenv.load end diff --git a/spec/mailers/previews/base_preview_mailer.rb b/spec/mailers/previews/base_preview_mailer.rb new file mode 100644 index 00000000000..610b4697185 --- /dev/null +++ b/spec/mailers/previews/base_preview_mailer.rb @@ -0,0 +1,10 @@ +class BasePreviewMailer < ActionMailer::Preview + def self.call(...) + message = nil + ActiveRecord::Base.transaction do + message = super(...) + raise ActiveRecord::Rollback + end + message + end +end From bf0c210fa7f31671f9135437329704d913dd48d2 Mon Sep 17 00:00:00 2001 From: Ancor Cruz Date: Fri, 28 Jun 2024 15:06:21 +0100 Subject: [PATCH 05/11] Add #expired? to DataExport model --- app/models/data_export.rb | 6 ++++++ spec/models/data_export_spec.rb | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/app/models/data_export.rb b/app/models/data_export.rb index 7598dc0516e..78228f98d05 100644 --- a/app/models/data_export.rb +++ b/app/models/data_export.rb @@ -14,4 +14,10 @@ class DataExport < ApplicationRecord enum status: STATUSES delegate :user, to: :membership + + def expired? + return false unless expires_at + + expires_at < Time.zone.now + end end diff --git a/spec/models/data_export_spec.rb b/spec/models/data_export_spec.rb index a8854e415a4..662af3c6434 100644 --- a/spec/models/data_export_spec.rb +++ b/spec/models/data_export_spec.rb @@ -8,4 +8,24 @@ it { is_expected.to validate_presence_of(:resource_type) } it { is_expected.to validate_presence_of(:resource_query) } it { is_expected.to validate_presence_of(:status) } + + describe '#expired?' do + subject(:expired?) { data_export.expired? } + + let(:data_export) { build :data_export } + + it { is_expected.to eq false } + + context 'when export is completed' do + let(:data_export) { build :data_export, :completed } + + it { is_expected.to eq false } + end + + context 'when the expiration date is reached' do + let(:data_export) { build :data_export, :expired } + + it { is_expected.to eq true } + end + end end From b072aae03fe328aed0bd63ff180b163fb61c4ab0 Mon Sep 17 00:00:00 2001 From: Ancor Cruz Date: Fri, 28 Jun 2024 15:22:17 +0100 Subject: [PATCH 06/11] Add data export completed email --- app/mailers/data_export_mailer.rb | 23 ++++++++++ app/models/data_export.rb | 1 + app/views/data_export_mailer/completed.slim | 42 +++++++++++++++++++ config/locales/en/email.yml | 8 ++++ spec/factories/data_exports.rb | 1 + spec/mailers/data_export_mailer_spec.rb | 39 +++++++++++++++++ .../previews/data_export_mailer_preview.rb | 6 +++ 7 files changed, 120 insertions(+) create mode 100644 app/mailers/data_export_mailer.rb create mode 100644 app/views/data_export_mailer/completed.slim create mode 100644 spec/mailers/data_export_mailer_spec.rb create mode 100644 spec/mailers/previews/data_export_mailer_preview.rb diff --git a/app/mailers/data_export_mailer.rb b/app/mailers/data_export_mailer.rb new file mode 100644 index 00000000000..f6f7ddc7607 --- /dev/null +++ b/app/mailers/data_export_mailer.rb @@ -0,0 +1,23 @@ +class DataExportMailer < ApplicationMailer + def completed + @data_export = params[:data_export] + @download_url = "https://google.com" + @export_filename = "filename" + user = @data_export.user + + return if @data_export.file.blank? + return if @data_export.expired? + return unless @data_export.completed? + + I18n.with_locale(:en) do + mail( + to: user.email, + from: ENV['LAGO_FROM_EMAIL'], + subject: I18n.t( + 'email.data_export.completed.subject', + resource_type: @data_export.resource_type + ) + ) + end + end +end diff --git a/app/models/data_export.rb b/app/models/data_export.rb index 78228f98d05..f5612131053 100644 --- a/app/models/data_export.rb +++ b/app/models/data_export.rb @@ -4,6 +4,7 @@ class DataExport < ApplicationRecord belongs_to :organization belongs_to :membership + has_one_attached :file validates :resource_type, :resource_query, presence: true diff --git a/app/views/data_export_mailer/completed.slim b/app/views/data_export_mailer/completed.slim new file mode 100644 index 00000000000..403ee9ab961 --- /dev/null +++ b/app/views/data_export_mailer/completed.slim @@ -0,0 +1,42 @@ +div style='margin-bottom: 32px;width: 80px;height: 24px;' + svg xmlns="http://www.w3.org/2000/svg" fill="none" viewbox="0 0 80 24" + g clip-path="url(#a)" + g fill="#19212E" clip-path="url(#b)" + path d="M69.85 18.161a5.529 5.529 0 0 1-2.324-2.326c-.557-1.003-.819-2.158-.819-3.463 0-1.305.279-2.46.819-3.463.54-1.004 1.326-1.773 2.324-2.326 1.015-.535 2.178-.82 3.504-.82s2.488.268 3.503.82a5.625 5.625 0 0 1 2.325 2.326c.54 1.004.818 2.158.818 3.463 0 1.322-.278 2.476-.819 3.48a5.678 5.678 0 0 1-2.324 2.309c-1.015.535-2.177.82-3.503.82s-2.505-.268-3.504-.82Zm5.795-3.095c.557-.686.852-1.59.852-2.677 0-1.104-.279-1.991-.852-2.677-.572-.686-1.326-1.037-2.291-1.037-.95 0-1.703.351-2.276 1.037-.573.686-.851 1.573-.851 2.677s.278 2.008.851 2.677c.573.686 1.326 1.037 2.276 1.037.965 0 1.719-.351 2.291-1.037ZM65.512 5.931v12.515c0 1.69-.556 3.044-1.637 4.048-1.097 1.004-2.8 1.506-5.108 1.506-1.784 0-3.224-.401-4.321-1.204-1.097-.804-1.686-1.941-1.768-3.413h3.487c.163.619.49 1.087.965 1.422.475.334 1.114.502 1.9.502.982 0 1.751-.252 2.291-.753.54-.502.819-1.255.819-2.226v-1.355c-.917 1.188-2.227 1.774-3.896 1.774-1.114 0-2.112-.268-2.996-.803-.884-.536-1.572-1.289-2.08-2.276-.507-.97-.752-2.124-.752-3.43 0-1.288.245-2.425.753-3.413a5.367 5.367 0 0 1 2.095-2.275c.884-.535 1.9-.803 3.012-.803 1.654 0 2.963.653 3.93 1.958L62.5 5.93h3.012Zm-4.24 8.984c.557-.669.835-1.539.835-2.61 0-1.087-.278-1.974-.835-2.66-.556-.686-1.31-1.02-2.242-1.02-.934 0-1.687.334-2.243 1.02-.573.67-.852 1.556-.852 2.644 0 1.087.279 1.974.852 2.643.573.67 1.31 1.004 2.242 1.004.934-.017 1.687-.351 2.243-1.02ZM51.22 5.931v12.9h-3.077l-.294-1.807c-1 1.288-2.309 1.924-3.93 1.924-1.113 0-2.111-.268-2.995-.803-.884-.536-1.572-1.305-2.08-2.31-.507-1.003-.752-2.158-.752-3.496 0-1.305.245-2.46.753-3.463A5.5 5.5 0 0 1 40.94 6.55c.884-.535 1.9-.82 3.012-.82.852 0 1.605.168 2.26.502a4.659 4.659 0 0 1 1.62 1.372l.344-1.706h3.045v.033Zm-4.24 9.152c.557-.67.836-1.573.836-2.677 0-1.121-.279-2.024-.835-2.71-.557-.686-1.31-1.038-2.243-1.038-.933 0-1.686.352-2.243 1.038-.573.686-.85 1.589-.85 2.71 0 1.104.277 1.99.85 2.677.573.686 1.31 1.02 2.243 1.02.933 0 1.686-.35 2.243-1.02ZM27.5 18.83V1.263h3.683v14.338h6.827v3.23H27.5Z" + g clip-path="url(#c)" + g fill="#19212E" clip-path="url(#d)" + path d="M19.875 11.693a9.973 9.973 0 0 1-2.804 5.558c-1.517 1.534-3.41 2.508-5.5 2.833 0-.018-.017-.036-.017-.054v-.018c-.018-.036-.018-.054-.036-.108l-.054-.163a5.625 5.625 0 0 1-.196-.83v-.018c0-.036-.018-.072-.018-.108v-.018c0-.036-.018-.072-.018-.09v-.036c0-.036-.018-.072-.018-.127-.018-.09-.018-.18-.018-.288v-.127c0-.108-.017-.216-.017-.343 0-3.572 2.892-6.496 6.428-6.496H18.071c.09 0 .197.018.286.036.036 0 .09 0 .143.018.036 0 .071.018.09.018h.017c.036 0 .072 0 .107.018h.018c.286.055.554.127.84.217l.16.054c.036.018.054.018.09.036h.017c0 .018.018.036.036.036Z" + path d="M20 10.213h-.018c-.16-.054-.321-.09-.482-.144-.018 0-.036-.018-.071-.018a3.26 3.26 0 0 0-.447-.09h-.018c-.035 0-.089-.018-.125-.018h-.035c-.054 0-.108-.018-.143-.018-.054 0-.125-.018-.161-.018-.107-.018-.232-.018-.34-.036h-.589c-4.339 0-7.857 3.554-7.857 7.94 0 .126 0 .252.018.396v.09c0 .037 0 .073.018.109.018.108.018.235.036.343 0 .054.018.108.018.162 0 .054.017.108.017.145.018.072.018.126.036.18.054.343.143.686.25 1.029v.018a9.768 9.768 0 0 1-6.09-2.003V17.847c0-7.561 6.09-13.715 13.572-13.715H18.018c1.321 1.697 2 3.844 1.982 6.082Z" opacity=".6" + path d="M16.732 2.617c-.071 0-.16.018-.232.018-.125 0-.232.018-.357.036-.036 0-.09 0-.125.018-.036 0-.054 0-.09.018a7.867 7.867 0 0 0-.642.09c-.161.018-.304.054-.465.072-.089.018-.196.036-.285.054-.107.018-.215.054-.34.072-.035.019-.089.019-.125.037-.071.018-.16.036-.232.054-.035 0-.053.018-.089.018-.09.018-.179.036-.25.072-.036.018-.09.018-.125.036-.071.018-.125.036-.196.054-.054.018-.108.036-.143.054-.09.018-.161.054-.232.072-.018 0-.036.019-.054.019-.107.036-.214.072-.321.126-.125.036-.233.09-.34.126a.656.656 0 0 0-.196.09c-.072.018-.125.055-.197.073-.107.054-.232.09-.339.144-.196.09-.393.18-.59.289-.25.126-.481.252-.731.397-.072.054-.161.09-.232.144-.09.054-.179.108-.286.18-.09.055-.179.109-.25.163-.072.054-.16.108-.232.162l-.215.163c-.178.126-.339.252-.5.379-.071.054-.125.108-.196.162-.107.09-.232.199-.34.289-.089.072-.178.162-.267.234-.072.054-.125.127-.197.18-.214.217-.428.416-.642.65a1.79 1.79 0 0 0-.179.199c-.09.09-.16.18-.232.27-.107.109-.197.235-.286.343-.053.073-.107.127-.16.199-.126.162-.25.343-.376.505l-.16.217c-.054.072-.107.162-.161.234-.054.09-.107.163-.16.253-.054.09-.126.18-.18.289-.053.072-.089.162-.142.234-.143.235-.268.487-.393.722a9.036 9.036 0 0 0-.286.595c-.053.109-.107.217-.143.343-.017.054-.053.127-.071.199-.036.072-.054.144-.09.198-.053.109-.089.235-.124.343-.036.108-.072.217-.125.325 0 .018-.018.036-.018.054-.036.072-.054.163-.072.235-.017.054-.035.108-.053.144-.018.072-.036.127-.054.199-.018.036-.018.09-.035.126-.018.09-.054.18-.072.253 0 .018-.018.054-.018.09-.018.072-.035.162-.053.234 0 .037-.018.073-.036.127-.018.108-.054.216-.071.343a8.66 8.66 0 0 0-.054.288 4.253 4.253 0 0 0-.071.47c-.036.216-.054.432-.09.65 0 .035 0 .053-.018.09 0 .035 0 .072-.017.126-.018.126-.018.234-.036.36 0 .073-.018.163-.018.235C.946 15.068.035 12.722 0 10.232A10.1 10.1 0 0 1 2.75 3.14c.054-.072.125-.126.179-.18l.178-.181C4.982.992 7.43 0 10 0h.125a9.965 9.965 0 0 1 6.607 2.617Z" opacity=".3" + defs + clippath#a + path fill="#fff" d="M0 0h80v24H0z" + clippath#b + path fill="#fff" d="M27.5 1.263H80V24H27.5z" + clippath#c + path fill="#fff" d="M0 0h20v20.21H0z" + clippath#d + path fill="#fff" d="M0 0h20v20.21H0z" +div style='margin-bottom: 24px;font-style: normal;font-weight: 400;font-size: 16px;line-height: 24px;color: #19212E;' + = I18n.t('email.data_export.greetings') +div style='margin-bottom: 32px;font-style: normal;font-weight: 400;font-size: 16px;line-height: 24px;color: #19212E;' + = I18n.t('email.data_export.intro') +table style='margin-bottom: 32px' width="100%" cellspacing="0" cellpadding="0" + tr + td + table cellspacing="0" cellpadding="0" + tr + td style="border-radius: 12px;" bgcolor="#006CFA" + a href="#{@download_url}" download="#{@export_filename}" style="padding: 10px 16px;font-size: 16px; color: #ffffff;text-decoration: none;font-weight:bold;display: inline-block;font-weight: 400;font-style: normal;line-height: 24px;" + = I18n.t('email.data_export.main_cta_label') +div style='margin-bottom: 32px;font-style: normal;font-weight: 400;font-size: 16px;line-height: 24px;color: #19212E;' + = I18n.t('email.data_export.fallback_text') + a href="#{@forgot_url}" target="_blank" style="color: #006CFA;text-decoration: none;" + = @forgot_url +div style='font-style: normal;font-weight: 400;font-size: 16px;line-height: 24px;' + = I18n.t('email.data_export.thanks') +div style='margin-bottom: 32px;font-style: normal;font-weight: 400;font-size: 16px;line-height: 24px;color: #19212E;' + = I18n.t('email.data_export.lago_team') +div style='width: 100%;height: 1px;background-color: #D9DEE7;margin-bottom: 32px;' +div style="color: #66758F;font-style: normal;font-weight: 400;font-size: 14px;line-height: 20px;" + = I18n.t('email.data_export.email_info') diff --git a/config/locales/en/email.yml b/config/locales/en/email.yml index 403dfb64470..885053c8300 100644 --- a/config/locales/en/email.yml +++ b/config/locales/en/email.yml @@ -1,6 +1,14 @@ --- en: email: + data_export: + fallback_text: If the link has expired, please generate a new one from your Dashboard. + greetings: Hello + intro: Your invoices export is ready! You can download it using the link below, which will be available for 7 days. + lago_team: The Lago Team + main_cta_label: Download export + subject: Your Lago %{resource_type} export in ready! + thanks: Thanks, credit_note: created: credit_note_from: Credit Note from %{organization_name} diff --git a/spec/factories/data_exports.rb b/spec/factories/data_exports.rb index de15db86514..d86b0fe0da8 100644 --- a/spec/factories/data_exports.rb +++ b/spec/factories/data_exports.rb @@ -19,6 +19,7 @@ started_at { 2.hours.ago } completed_at { 30.minutes.ago } expires_at { 7.days.from_now } + file { Rack::Test::UploadedFile.new(Rails.root.join('spec', 'fixtures', 'export.csv')) } end trait :expired do diff --git a/spec/mailers/data_export_mailer_spec.rb b/spec/mailers/data_export_mailer_spec.rb new file mode 100644 index 00000000000..005b20d9e9c --- /dev/null +++ b/spec/mailers/data_export_mailer_spec.rb @@ -0,0 +1,39 @@ +require 'rails_helper' + +RSpec.describe DataExportMailer, type: :mailer do + subject(:data_export_mailer) { described_class } + + let(:data_export) { create(:data_export, :completed) } + + describe '#completed' do + let(:mailer) { data_export_mailer.with(data_export:).completed } + + specify do + expect(mailer.to).to eq([data_export.user.email]) + end + + context 'when data export is expired' do + let(:data_export) { create(:data_export, :expired) } + + it 'does something' do + expect(mailer.to).to be_nil + end + end + + context 'when data export is not completed' do + let(:data_export) { create(:data_export, :processing) } + + it 'does something' do + expect(mailer.to).to be_nil + end + end + + context 'when data export has no attached file' do + let(:data_export) { create(:data_export, :completed, file: nil) } + + it 'does something' do + expect(mailer.to).to be_nil + end + end + end +end diff --git a/spec/mailers/previews/data_export_mailer_preview.rb b/spec/mailers/previews/data_export_mailer_preview.rb new file mode 100644 index 00000000000..8f5b8565e26 --- /dev/null +++ b/spec/mailers/previews/data_export_mailer_preview.rb @@ -0,0 +1,6 @@ +class DataExportMailerPreview < BasePreviewMailer + def completed + data_export = FactoryBot.create :data_export, :completed + DataExportMailer.with(data_export:).completed + end +end From 1d11e6df3885f74c071a81ea1a64ee5143f3d698 Mon Sep 17 00:00:00 2001 From: Ancor Cruz Date: Mon, 1 Jul 2024 08:34:55 +0100 Subject: [PATCH 07/11] Add DataExport file_url and filename methods file_url creates a link with expiration time filename is based in the creation date, resource type and format of the export --- app/mailers/data_export_mailer.rb | 2 -- app/models/data_export.rb | 18 +++++++++++ app/views/data_export_mailer/completed.slim | 7 +--- spec/fixtures/export.csv | 3 ++ spec/mailers/data_export_mailer_spec.rb | 4 +-- spec/models/data_export_spec.rb | 36 +++++++++++++++++++++ 6 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 spec/fixtures/export.csv diff --git a/app/mailers/data_export_mailer.rb b/app/mailers/data_export_mailer.rb index f6f7ddc7607..c8cfbc64058 100644 --- a/app/mailers/data_export_mailer.rb +++ b/app/mailers/data_export_mailer.rb @@ -1,8 +1,6 @@ class DataExportMailer < ApplicationMailer def completed @data_export = params[:data_export] - @download_url = "https://google.com" - @export_filename = "filename" user = @data_export.user return if @data_export.file.blank? diff --git a/app/models/data_export.rb b/app/models/data_export.rb index f5612131053..b9f7ee030c4 100644 --- a/app/models/data_export.rb +++ b/app/models/data_export.rb @@ -21,4 +21,22 @@ def expired? expires_at < Time.zone.now end + + def filename + return if file.blank? + + "#{created_at.strftime('%Y%m%d%H%M%S')}_#{resource_type}.#{format}" + end + + def file_url + return if file.blank? + + blob_path = Rails.application.routes.url_helpers.rails_blob_path( + file, + host: 'void', + expires_in: 7.days + ) + + File.join(ENV['LAGO_API_URL'], blob_path) + end end diff --git a/app/views/data_export_mailer/completed.slim b/app/views/data_export_mailer/completed.slim index 403ee9ab961..af314c25f07 100644 --- a/app/views/data_export_mailer/completed.slim +++ b/app/views/data_export_mailer/completed.slim @@ -27,16 +27,11 @@ table style='margin-bottom: 32px' width="100%" cellspacing="0" cellpadding="0" table cellspacing="0" cellpadding="0" tr td style="border-radius: 12px;" bgcolor="#006CFA" - a href="#{@download_url}" download="#{@export_filename}" style="padding: 10px 16px;font-size: 16px; color: #ffffff;text-decoration: none;font-weight:bold;display: inline-block;font-weight: 400;font-style: normal;line-height: 24px;" + a href="#{@data_export.file_url}" download="#{@data_export.filename}" style="padding: 10px 16px;font-size: 16px; color: #ffffff;text-decoration: none;font-weight:bold;display: inline-block;font-weight: 400;font-style: normal;line-height: 24px;" = I18n.t('email.data_export.main_cta_label') div style='margin-bottom: 32px;font-style: normal;font-weight: 400;font-size: 16px;line-height: 24px;color: #19212E;' = I18n.t('email.data_export.fallback_text') - a href="#{@forgot_url}" target="_blank" style="color: #006CFA;text-decoration: none;" - = @forgot_url div style='font-style: normal;font-weight: 400;font-size: 16px;line-height: 24px;' = I18n.t('email.data_export.thanks') div style='margin-bottom: 32px;font-style: normal;font-weight: 400;font-size: 16px;line-height: 24px;color: #19212E;' = I18n.t('email.data_export.lago_team') -div style='width: 100%;height: 1px;background-color: #D9DEE7;margin-bottom: 32px;' -div style="color: #66758F;font-style: normal;font-weight: 400;font-size: 14px;line-height: 20px;" - = I18n.t('email.data_export.email_info') diff --git a/spec/fixtures/export.csv b/spec/fixtures/export.csv new file mode 100644 index 00000000000..24b641339ba --- /dev/null +++ b/spec/fixtures/export.csv @@ -0,0 +1,3 @@ +invoice.lago_id,invoice.sequential_id,invoice.issuing_date,invoice.customer.customer_lago_id,invoice.customer.external_id,invoice.customer.country,invoice.customer.tax_identification_number,invoice.number,invoice.total_amount_cents,invoice.currency,invoice.invoice_type,invoice.payment_status,invoice.status,invoice.file_url,invoice.taxes_amount_cents,invoice.credit_notes_amount_cents,invoice.prepaid_credit_amount_cents,invoice.coupons_amount_cents,invoice.payment_due_date,invoice.payment_dispute_lost_at,invoice.payment_overdue +292ef60b-9e0c-42e7-9f50-44d5af4162ec,1,2024-06-06,80ebcc26-3703-4577-b13e-765591255df4,hooli_1,US,US12345,TWI-2B86-170-001,1000,USD,subscription,pending,finalized,https://file1.com,100,0,1000,0,2024-06-06,,true +7d430962-02cb-4183-b255-de3bb75af798,2,2024-06-07,80ebcc26-3703-4577-b13e-765591255df4,hooli_1,US,US12345,TWI-2B86-170-002,2000,USD,subscription,failed,draft,https://file2.com,200,100,0,0,2024-07-20,2024-06-06,false diff --git a/spec/mailers/data_export_mailer_spec.rb b/spec/mailers/data_export_mailer_spec.rb index 005b20d9e9c..163775cb67c 100644 --- a/spec/mailers/data_export_mailer_spec.rb +++ b/spec/mailers/data_export_mailer_spec.rb @@ -23,7 +23,7 @@ context 'when data export is not completed' do let(:data_export) { create(:data_export, :processing) } - it 'does something' do + it 'returns a mailer with nil values' do expect(mailer.to).to be_nil end end @@ -31,7 +31,7 @@ context 'when data export has no attached file' do let(:data_export) { create(:data_export, :completed, file: nil) } - it 'does something' do + it 'returns a mailer with nil values' do expect(mailer.to).to be_nil end end diff --git a/spec/models/data_export_spec.rb b/spec/models/data_export_spec.rb index 662af3c6434..3f0e20b8564 100644 --- a/spec/models/data_export_spec.rb +++ b/spec/models/data_export_spec.rb @@ -28,4 +28,40 @@ it { is_expected.to eq true } end end + + describe '.filename' do + subject(:filename) { data_export.filename } + + let(:data_export) { create :data_export, :completed } + + it 'returns the file name' do + freeze_time do + timestamp = Time.zone.now.strftime('%Y%m%d%H%M%S') + expect(filename).to eq("#{timestamp}_invoices.csv") + end + end + + context 'when data export does not have a file' do + let(:data_export) { create :data_export } + + it { is_expected.to be_nil } + end + end + + describe '.file_url' do + subject(:file_url) { data_export.file_url } + + let(:data_export) { create :data_export, :completed } + + it 'returns the file url' do + expect(file_url).to be_present + expect(file_url).to include(ENV['LAGO_API_URL']) + end + + context 'when data export does not have a file' do + let(:data_export) { create :data_export } + + it { is_expected.to be_nil } + end + end end From 0f4de5535d1379d89cb69a81a48cb48afb70f0af Mon Sep 17 00:00:00 2001 From: Ancor Cruz Date: Mon, 1 Jul 2024 09:17:19 +0100 Subject: [PATCH 08/11] Fix data_export complete email translations --- app/views/data_export_mailer/completed.slim | 12 ++++++------ config/locales/en/email.yml | 15 ++++++++------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/app/views/data_export_mailer/completed.slim b/app/views/data_export_mailer/completed.slim index af314c25f07..f356837fb72 100644 --- a/app/views/data_export_mailer/completed.slim +++ b/app/views/data_export_mailer/completed.slim @@ -18,9 +18,9 @@ div style='margin-bottom: 32px;width: 80px;height: 24px;' clippath#d path fill="#fff" d="M0 0h20v20.21H0z" div style='margin-bottom: 24px;font-style: normal;font-weight: 400;font-size: 16px;line-height: 24px;color: #19212E;' - = I18n.t('email.data_export.greetings') + = I18n.t('email.data_export.completed.greetings') div style='margin-bottom: 32px;font-style: normal;font-weight: 400;font-size: 16px;line-height: 24px;color: #19212E;' - = I18n.t('email.data_export.intro') + = I18n.t('email.data_export.completed.intro') table style='margin-bottom: 32px' width="100%" cellspacing="0" cellpadding="0" tr td @@ -28,10 +28,10 @@ table style='margin-bottom: 32px' width="100%" cellspacing="0" cellpadding="0" tr td style="border-radius: 12px;" bgcolor="#006CFA" a href="#{@data_export.file_url}" download="#{@data_export.filename}" style="padding: 10px 16px;font-size: 16px; color: #ffffff;text-decoration: none;font-weight:bold;display: inline-block;font-weight: 400;font-style: normal;line-height: 24px;" - = I18n.t('email.data_export.main_cta_label') + = I18n.t('email.data_export.completed.main_cta_label') div style='margin-bottom: 32px;font-style: normal;font-weight: 400;font-size: 16px;line-height: 24px;color: #19212E;' - = I18n.t('email.data_export.fallback_text') + = I18n.t('email.data_export.completed.fallback_text') div style='font-style: normal;font-weight: 400;font-size: 16px;line-height: 24px;' - = I18n.t('email.data_export.thanks') + = I18n.t('email.data_export.completed.thanks') div style='margin-bottom: 32px;font-style: normal;font-weight: 400;font-size: 16px;line-height: 24px;color: #19212E;' - = I18n.t('email.data_export.lago_team') + = I18n.t('email.data_export.completed.lago_team') diff --git a/config/locales/en/email.yml b/config/locales/en/email.yml index 885053c8300..29af4cc35bb 100644 --- a/config/locales/en/email.yml +++ b/config/locales/en/email.yml @@ -2,13 +2,14 @@ en: email: data_export: - fallback_text: If the link has expired, please generate a new one from your Dashboard. - greetings: Hello - intro: Your invoices export is ready! You can download it using the link below, which will be available for 7 days. - lago_team: The Lago Team - main_cta_label: Download export - subject: Your Lago %{resource_type} export in ready! - thanks: Thanks, + completed: + fallback_text: If the link has expired, please generate a new one from your Dashboard. + greetings: Hello + intro: Your invoices export is ready! You can download it using the link below, which will be available for 7 days. + lago_team: The Lago Team + main_cta_label: Download export + subject: Your Lago %{resource_type} export in ready! + thanks: Thanks, credit_note: created: credit_note_from: Credit Note from %{organization_name} From 57a0098104a0a36afa194a547e8ca452d601f6e6 Mon Sep 17 00:00:00 2001 From: Ancor Cruz Date: Mon, 1 Jul 2024 19:04:04 +0100 Subject: [PATCH 09/11] align with rubocop style guide --- app/models/data_export.rb | 2 +- config/environments/development.rb | 2 +- ...4_add_membership_id_and_organization_id_to_data_exports.rb | 4 ++-- db/schema.rb | 4 ++-- spec/factories/data_exports.rb | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/models/data_export.rb b/app/models/data_export.rb index b9f7ee030c4..4f426b2b6e0 100644 --- a/app/models/data_export.rb +++ b/app/models/data_export.rb @@ -25,7 +25,7 @@ def expired? def filename return if file.blank? - "#{created_at.strftime('%Y%m%d%H%M%S')}_#{resource_type}.#{format}" + "#{created_at.strftime("%Y%m%d%H%M%S")}_#{resource_type}.#{format}" end def file_url diff --git a/config/environments/development.rb b/config/environments/development.rb index c96bbb279c5..cff0b72c1b3 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -63,7 +63,7 @@ address: 'mailhog', port: 1025 } - config.action_mailer.preview_paths << "#{Rails.root}/spec/mailers/previews" + config.action_mailer.preview_paths << Rails.root.join("spec/mailers/previews").to_s Dotenv.load end diff --git a/db/migrate/20240628083654_add_membership_id_and_organization_id_to_data_exports.rb b/db/migrate/20240628083654_add_membership_id_and_organization_id_to_data_exports.rb index cb9d921516e..f070eea18ec 100644 --- a/db/migrate/20240628083654_add_membership_id_and_organization_id_to_data_exports.rb +++ b/db/migrate/20240628083654_add_membership_id_and_organization_id_to_data_exports.rb @@ -1,6 +1,6 @@ class AddMembershipIdAndOrganizationIdToDataExports < ActiveRecord::Migration[7.1] def change - add_reference :data_exports, :membership, null: false, foreign_key: true, type: :uuid - add_reference :data_exports, :organization, null: false, foreign_key: true, type: :uuid + add_reference :data_exports, :membership, foreign_key: true, type: :uuid + add_reference :data_exports, :organization, foreign_key: true, type: :uuid end end diff --git a/db/schema.rb b/db/schema.rb index c1be43b9523..1db1bd7efe9 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -451,8 +451,8 @@ t.datetime "completed_at", precision: nil t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.uuid "membership_id", null: false - t.uuid "organization_id", null: false + t.uuid "membership_id" + t.uuid "organization_id" t.index ["membership_id"], name: "index_data_exports_on_membership_id" t.index ["organization_id"], name: "index_data_exports_on_organization_id" end diff --git a/spec/factories/data_exports.rb b/spec/factories/data_exports.rb index d86b0fe0da8..5cc874e0261 100644 --- a/spec/factories/data_exports.rb +++ b/spec/factories/data_exports.rb @@ -19,7 +19,7 @@ started_at { 2.hours.ago } completed_at { 30.minutes.ago } expires_at { 7.days.from_now } - file { Rack::Test::UploadedFile.new(Rails.root.join('spec', 'fixtures', 'export.csv')) } + file { Rack::Test::UploadedFile.new(Rails.root.join("spec/fixtures/export.csv")) } end trait :expired do From b712119d3cafe7ef65e6632fefa95d5cfdd3676c Mon Sep 17 00:00:00 2001 From: Ancor Cruz Date: Mon, 1 Jul 2024 19:20:10 +0100 Subject: [PATCH 10/11] Normalize locales --- config/locales/en/email.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/config/locales/en/email.yml b/config/locales/en/email.yml index 29af4cc35bb..784e48eab80 100644 --- a/config/locales/en/email.yml +++ b/config/locales/en/email.yml @@ -1,15 +1,6 @@ --- en: email: - data_export: - completed: - fallback_text: If the link has expired, please generate a new one from your Dashboard. - greetings: Hello - intro: Your invoices export is ready! You can download it using the link below, which will be available for 7 days. - lago_team: The Lago Team - main_cta_label: Download export - subject: Your Lago %{resource_type} export in ready! - thanks: Thanks, credit_note: created: credit_note_from: Credit Note from %{organization_name} @@ -21,6 +12,15 @@ en: issue_date: Issue date refunded_notice: Refunded on %{date} subject: 'Your credit note from %{organization_name} #%{credit_note_number}' + data_export: + completed: + fallback_text: If the link has expired, please generate a new one from your Dashboard. + greetings: Hello + intro: Your invoices export is ready! You can download it using the link below, which will be available for 7 days. + lago_team: The Lago Team + main_cta_label: Download export + subject: Your Lago %{resource_type} export in ready! + thanks: Thanks, invoice: finalized: download: Download invoice for details From 9c4d65b81d60ab3ad4301f4c120d8ac7d7c5a572 Mon Sep 17 00:00:00 2001 From: Ancor Cruz Date: Tue, 2 Jul 2024 09:42:52 +0100 Subject: [PATCH 11/11] use default queue for now --- app/jobs/data_exports/export_resources_job.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/jobs/data_exports/export_resources_job.rb b/app/jobs/data_exports/export_resources_job.rb index bb6f5a7a7c9..49e9439dcc4 100644 --- a/app/jobs/data_exports/export_resources_job.rb +++ b/app/jobs/data_exports/export_resources_job.rb @@ -1,6 +1,6 @@ module DataExports class ExportResourcesJob < ApplicationJob - queue_as :data_export + queue_as :default def perform(data_export) ExportResourcesService.call(data_export:).raise_if_error!