1042 lines
30 KiB
Ruby
Executable File
1042 lines
30 KiB
Ruby
Executable File
#!/usr/bin/env ruby
|
|
|
|
require_relative "../config/environment"
|
|
require "pathname"
|
|
require "optparse"
|
|
|
|
class Import
|
|
attr_reader :db_path, :untenanted_db_path
|
|
attr_reader :account, :tenant, :mapping
|
|
|
|
def initialize(db_path, untenanted_db_path)
|
|
@db_path = Pathname(db_path)
|
|
@untenanted_db_path = Pathname(untenanted_db_path)
|
|
@mapping = nil
|
|
end
|
|
|
|
def import_database
|
|
raise "The given database file doesn't exist" unless db_path.exist?
|
|
|
|
@mapping = {}
|
|
|
|
setup_account
|
|
|
|
ActiveRecord::Base.no_touching do
|
|
Event.suppress do
|
|
Current.with(account: account) do
|
|
Webhook.skip_callback(:create, :after, :create_delinquency_tracker!)
|
|
Comment.skip_callback(:commit, :after, :watch_card_by_creator)
|
|
Mention.skip_callback(:commit, :after, :watch_source_by_mentionee)
|
|
Notification.skip_callback(:commit, :after, :broadcast_unread)
|
|
Notification.skip_callback(:create, :after, :bundle)
|
|
Reaction.skip_callback(:create, :after, :register_card_activity)
|
|
Card.skip_callback(:save, :before, :set_default_title)
|
|
Card.skip_callback(:update, :after, :handle_board_change)
|
|
|
|
# copy_entropies
|
|
copy_users
|
|
copy_boards
|
|
copy_accesses
|
|
copy_columns
|
|
copy_cards
|
|
copy_steps
|
|
copy_comments
|
|
copy_mentions
|
|
copy_reactions
|
|
copy_tags
|
|
copy_watches
|
|
copy_pins
|
|
copy_webhooks
|
|
copy_push_subscriptions
|
|
copy_notifications
|
|
copy_notification_bundles
|
|
copy_filters
|
|
copy_events
|
|
|
|
fix_links
|
|
end
|
|
end
|
|
end
|
|
|
|
@mapping
|
|
end
|
|
|
|
private
|
|
def setup_account
|
|
puts "⏩ Setting up account"
|
|
oldest_admin = import.users.order(id: :asc).where(role: :admin, active: true).first
|
|
raise "No admin user found in the database" unless oldest_admin
|
|
|
|
membership = untenanted.memberships.find(oldest_admin.membership_id)
|
|
account = import.accounts.sole
|
|
|
|
new_identity = Identity.find_or_create_by!(email_address: membership.identity.email_address)
|
|
new_membership = new_identity.memberships.find_or_create_by!(tenant: account.external_account_id.to_s)
|
|
|
|
if Account.all.exists?(external_account_id: account.external_account_id)
|
|
@account = Account.find_by!(external_account_id: account.external_account_id)
|
|
@tenant = @account.external_account_id
|
|
else
|
|
@account = Account.create_with_admin_user(
|
|
account: {
|
|
external_account_id: account.external_account_id.to_s,
|
|
name: account.name
|
|
},
|
|
owner: {
|
|
name: oldest_admin.name,
|
|
membership_id: new_membership.id
|
|
}
|
|
)
|
|
@tenant = @account.external_account_id
|
|
end
|
|
|
|
old_join_code = import.account_join_codes.sole
|
|
|
|
attributes = {
|
|
usage_count: old_join_code.usage_count,
|
|
usage_limit: old_join_code.usage_limit
|
|
}
|
|
attributes[:code] = old_join_code.code unless Account::JoinCode.all.exists?(code: old_join_code.code)
|
|
|
|
@account.join_code.update_columns(**attributes)
|
|
puts "✅ Account set up!"
|
|
end
|
|
|
|
def copy_users
|
|
puts "⏩ Copying users"
|
|
mapping[:users] ||= {}
|
|
import.users.find_each do |old_user|
|
|
new_membership = nil
|
|
|
|
if old_user.active && old_user.membership_id
|
|
membership = untenanted.memberships.find(old_user.membership_id)
|
|
new_identity = Identity.find_or_create_by!(email_address: membership.identity.email_address)
|
|
new_membership = new_identity.memberships.find_or_create_by!(tenant: tenant)
|
|
end
|
|
|
|
new_user = User.find_or_create_by!(account_id: account.id, membership_id: new_membership&.id) do |u|
|
|
u.name = old_user.name
|
|
u.role = old_user.role
|
|
u.active = old_user.active
|
|
end
|
|
|
|
old_settings = old_user.settings
|
|
if old_settings
|
|
User::Settings.find_or_create_by!(user_id: new_user.id) do |s|
|
|
s.bundle_email_frequency = old_settings.bundle_email_frequency
|
|
s.timezone_name = old_settings.timezone_name
|
|
end
|
|
end
|
|
|
|
mapping[:users][old_user.id] = new_user.id
|
|
end
|
|
puts "✅ Copied #{mapping[:users].size} users"
|
|
end
|
|
|
|
def copy_boards
|
|
puts "⏩ Copying boards"
|
|
mapping[:boards] ||= {}
|
|
import.boards.find_each do |old_board|
|
|
new_board = Board.create!(
|
|
account_id: account.id,
|
|
creator_id: mapping[:users][old_board.creator_id],
|
|
name: old_board.name,
|
|
all_access: old_board.all_access,
|
|
created_at: old_board.created_at,
|
|
updated_at: old_board.updated_at
|
|
)
|
|
|
|
old_publication = old_board.publication
|
|
if old_publication
|
|
Board::Publication.create!(
|
|
board_id: new_board.id,
|
|
key: old_publication.key,
|
|
created_at: old_publication.created_at,
|
|
updated_at: old_publication.updated_at
|
|
)
|
|
end
|
|
|
|
mapping[:boards][old_board.id] = new_board.id
|
|
end
|
|
puts "✅ Copied #{mapping[:boards].size} boards"
|
|
end
|
|
|
|
def copy_columns
|
|
puts "⏩ Copying columns"
|
|
mapping[:columns] ||= {}
|
|
|
|
import.columns.find_each do |old_column|
|
|
new_column = Column.create!(
|
|
account_id: account.id,
|
|
board_id: mapping[:boards][old_column.board_id],
|
|
name: old_column.name,
|
|
color: old_column.color,
|
|
position: old_column.position,
|
|
created_at: old_column.created_at,
|
|
updated_at: old_column.updated_at
|
|
)
|
|
|
|
mapping[:columns][old_column.id] = new_column.id
|
|
end
|
|
puts "✅ Copied #{mapping[:columns].size} columns"
|
|
end
|
|
|
|
def copy_cards
|
|
puts "⏩ Copying cards"
|
|
mapping[:cards] ||= {}
|
|
import.cards.find_each do |old_card|
|
|
new_card = Card.create!(
|
|
account_id: account.id,
|
|
board_id: mapping[:boards][old_card.board_id],
|
|
column_id: old_card.column_id ? mapping[:columns][old_card.column_id] : nil,
|
|
creator_id: mapping[:users][old_card.creator_id],
|
|
title: old_card.title,
|
|
status: old_card.status,
|
|
due_on: old_card.due_on,
|
|
last_active_at: old_card.last_active_at,
|
|
created_at: old_card.created_at,
|
|
updated_at: old_card.updated_at
|
|
)
|
|
|
|
copy_rich_text(old_card, new_card, "Card", "description")
|
|
copy_attachment(old_card, new_card, "Card", "image")
|
|
|
|
old_activity_spike = old_card.activity_spike
|
|
if old_activity_spike
|
|
Card::ActivitySpike.create!(
|
|
card_id: new_card.id,
|
|
created_at: old_activity_spike.created_at,
|
|
updated_at: old_activity_spike.updated_at
|
|
)
|
|
end
|
|
|
|
old_engagement = old_card.engagement
|
|
if old_engagement
|
|
Card::Engagement.create!(
|
|
card_id: new_card.id,
|
|
status: old_engagement.status,
|
|
created_at: old_engagement.created_at,
|
|
updated_at: old_engagement.updated_at
|
|
)
|
|
end
|
|
|
|
old_goldness = old_card.goldness
|
|
if old_goldness
|
|
Card::Goldness.create!(
|
|
card_id: new_card.id,
|
|
created_at: old_goldness.created_at,
|
|
updated_at: old_goldness.updated_at
|
|
)
|
|
end
|
|
|
|
old_not_now = old_card.not_now
|
|
if old_not_now
|
|
Card::NotNow.create!(
|
|
card_id: new_card.id,
|
|
user_id: old_not_now.user_id ? mapping[:users][old_not_now.user_id] : nil,
|
|
created_at: old_not_now.created_at,
|
|
updated_at: old_not_now.updated_at
|
|
)
|
|
end
|
|
|
|
old_card.assignments.each do |old_assignment|
|
|
Assignment.create!(
|
|
card_id: new_card.id,
|
|
assignee_id: mapping[:users][old_assignment.assignee_id],
|
|
assigner_id: mapping[:users][old_assignment.assigner_id],
|
|
created_at: old_assignment.created_at,
|
|
updated_at: old_assignment.updated_at
|
|
)
|
|
end
|
|
|
|
old_closure = old_card.closure
|
|
if old_closure
|
|
Closure.create!(
|
|
card_id: new_card.id,
|
|
user_id: old_closure.user_id ? mapping[:users][old_closure.user_id] : nil,
|
|
created_at: old_closure.created_at,
|
|
updated_at: old_closure.updated_at
|
|
)
|
|
end
|
|
|
|
mapping[:cards][old_card.id] = new_card.id
|
|
end
|
|
puts "✅ Copied #{mapping[:cards].size} cards"
|
|
end
|
|
|
|
def copy_steps
|
|
puts "⏩ Copying steps"
|
|
import.steps.find_each do |old_step|
|
|
Step.create!(
|
|
account_id: account.id,
|
|
card_id: mapping[:cards][old_step.card_id],
|
|
content: old_step.content,
|
|
completed: old_step.completed,
|
|
created_at: old_step.created_at,
|
|
updated_at: old_step.updated_at
|
|
)
|
|
end
|
|
puts "✅ Copied steps"
|
|
end
|
|
|
|
def copy_comments
|
|
puts "⏩ Copying comments"
|
|
mapping[:comments] ||= {}
|
|
import.comments.find_each do |old_comment|
|
|
new_comment = Comment.create!(
|
|
account_id: account.id,
|
|
card_id: mapping[:cards][old_comment.card_id],
|
|
creator_id: mapping[:users][old_comment.creator_id],
|
|
created_at: old_comment.created_at,
|
|
updated_at: old_comment.updated_at
|
|
)
|
|
|
|
copy_rich_text(old_comment, new_comment, "Comment", "body")
|
|
|
|
mapping[:comments][old_comment.id] = new_comment.id
|
|
end
|
|
puts "✅ Copied #{mapping[:comments].size} comments"
|
|
end
|
|
|
|
def copy_mentions
|
|
puts "⏩ Copying mentions"
|
|
mapping[:mentions] ||= {}
|
|
import.mentions.find_each do |old_mention|
|
|
new_mention = Mention.create!(
|
|
account_id: account.id,
|
|
source_type: old_mention.source_type,
|
|
source_id: mapping[old_mention.source_type.tableize.to_sym][old_mention.source_id],
|
|
mentioner_id: mapping[:users][old_mention.mentioner_id],
|
|
mentionee_id: mapping[:users][old_mention.mentionee_id],
|
|
created_at: old_mention.created_at,
|
|
updated_at: old_mention.updated_at
|
|
)
|
|
|
|
mapping[:mentions][old_mention.id] = new_mention.id
|
|
end
|
|
puts "✅ Copied #{mapping[:mentions].size} mentions"
|
|
end
|
|
|
|
def copy_accesses
|
|
puts "⏩ Copying accesses"
|
|
import.accesses.find_each do |old_access|
|
|
new_access = Access.find_or_create_by!(
|
|
board_id: mapping[:boards][old_access.board_id],
|
|
user_id: mapping[:users][old_access.user_id]
|
|
) do |access|
|
|
access.involvement = old_access.involvement
|
|
access.accessed_at = old_access.accessed_at
|
|
access.created_at = old_access.created_at
|
|
access.updated_at = old_access.updated_at
|
|
end
|
|
|
|
mapping[:accesses] ||= {}
|
|
mapping[:accesses][old_access.id] = new_access.id
|
|
end
|
|
puts "✅ Copied #{mapping[:accesses].size} accesses"
|
|
end
|
|
|
|
def copy_notifications
|
|
puts "⏩ Copying notifications"
|
|
mapping[:notifications] ||= {}
|
|
|
|
import.notifications.find_each do |old_notification|
|
|
new_notification = Notification.create!(
|
|
account_id: account.id,
|
|
user_id: mapping[:users][old_notification.user_id],
|
|
creator_id: old_notification.creator_id ? mapping[:users][old_notification.creator_id] : nil,
|
|
source_type: old_notification.source_type,
|
|
source_id: mapping[old_notification.source_type.tableize.to_sym][old_notification.source_id],
|
|
read_at: old_notification.read_at,
|
|
created_at: old_notification.created_at,
|
|
updated_at: old_notification.updated_at
|
|
)
|
|
|
|
mapping[:notifications][old_notification.id] = new_notification.id
|
|
end
|
|
puts "✅ Copied #{mapping[:notifications].size} notifications"
|
|
end
|
|
|
|
def copy_notification_bundles
|
|
puts "⏩ Copying notification bundles"
|
|
mapping[:notification_bundles] ||= {}
|
|
|
|
import.notification_bundles.find_each do |old_bundle|
|
|
new_bundle = Notification::Bundle.create!(
|
|
account_id: account.id,
|
|
user_id: mapping[:users][old_bundle.user_id],
|
|
status: old_bundle.status,
|
|
starts_at: old_bundle.starts_at,
|
|
ends_at: old_bundle.ends_at,
|
|
created_at: old_bundle.created_at,
|
|
updated_at: old_bundle.updated_at
|
|
)
|
|
|
|
mapping[:notification_bundles][old_bundle.id] = new_bundle.id
|
|
end
|
|
puts "✅ Copied #{mapping[:notification_bundles].size} notification bundles"
|
|
end
|
|
|
|
def copy_entropies
|
|
puts "⏩ Copying entropies"
|
|
import.entropies.find_each do |old_entropy|
|
|
container_id = case old_entropy.container_type
|
|
when "Account" then account.id
|
|
when "Board" then mapping[:boards][old_entropy.container_id]
|
|
when "Card" then mapping[:cards][old_entropy.container_id]
|
|
else next
|
|
end
|
|
|
|
Entropy.create!(
|
|
container_type: old_entropy.container_type,
|
|
container_id: container_id,
|
|
auto_postpone_period: old_entropy.auto_postpone_period,
|
|
created_at: old_entropy.created_at,
|
|
updated_at: old_entropy.updated_at
|
|
)
|
|
end
|
|
puts "✅ Copied entropies"
|
|
end
|
|
|
|
def copy_filters
|
|
puts "⏩ Copying filters"
|
|
mapping[:filters] ||= {}
|
|
|
|
import.filters.find_each do |old_filter|
|
|
new_filter = Filter.create!(
|
|
account_id: account.id,
|
|
creator_id: mapping[:users][old_filter.creator_id],
|
|
fields: old_filter.fields,
|
|
params_digest: old_filter.params_digest,
|
|
created_at: old_filter.created_at,
|
|
updated_at: old_filter.updated_at
|
|
)
|
|
|
|
import.assignees_filters.where(filter_id: old_filter.id).each do |join|
|
|
AssigneesFilter.find_or_create_by!(filter_id: new_filter.id, user_id: mapping[:users][join.user_id])
|
|
end
|
|
|
|
import.assigners_filters.where(filter_id: old_filter.id).each do |join|
|
|
AssignersFilter.find_or_create_by!(filter_id: new_filter.id, user_id: mapping[:users][join.user_id])
|
|
end
|
|
|
|
import.boards_filters.where(filter_id: old_filter.id).each do |join|
|
|
BoardsFilter.find_or_create_by!(filter_id: new_filter.id, board_id: mapping[:boards][join.board_id])
|
|
end
|
|
|
|
import.closers_filters.where(filter_id: old_filter.id).each do |join|
|
|
ClosersFilter.find_or_create_by!(filter_id: new_filter.id, user_id: mapping[:users][join.user_id])
|
|
end
|
|
|
|
import.creators_filters.where(filter_id: old_filter.id).each do |join|
|
|
CreatorsFilter.find_or_create_by!(filter_id: new_filter.id, user_id: mapping[:users][join.user_id])
|
|
end
|
|
|
|
import.filters_tags.where(filter_id: old_filter.id).each do |join|
|
|
FiltersTag.find_or_create_by!(filter_id: new_filter.id, tag_id: mapping[:tags][join.tag_id])
|
|
end
|
|
|
|
mapping[:filters][old_filter.id] = new_filter.id
|
|
end
|
|
puts "✅ Copied #{mapping[:filters].size} filters"
|
|
end
|
|
|
|
def copy_events
|
|
puts "⏩ Copying events"
|
|
import.events.find_each do |old_event|
|
|
new_event = Event.create!(
|
|
account_id: account.id,
|
|
board_id: mapping[:boards][old_event.board_id],
|
|
creator_id: mapping[:users][old_event.creator_id],
|
|
eventable_type: old_event.eventable_type,
|
|
eventable_id: mapping[old_event.eventable_type.tableize.to_sym][old_event.eventable_id],
|
|
action: old_event.action,
|
|
particulars: old_event.particulars,
|
|
created_at: old_event.created_at,
|
|
updated_at: old_event.updated_at
|
|
)
|
|
|
|
mapping[:events] ||= {}
|
|
mapping[:events][old_event.id] = new_event.id
|
|
end
|
|
puts "✅ Copied #{mapping[:events].size} events"
|
|
end
|
|
|
|
def copy_rich_text(old_record, new_record, record_type, name)
|
|
old_rich_text = import.rich_texts.find_by(record_type: record_type, record_id: old_record.id, name: name)
|
|
return unless old_rich_text
|
|
|
|
new_rich_text = ActionText::RichText.create!(
|
|
record: new_record,
|
|
name: name,
|
|
body: old_rich_text.body,
|
|
created_at: old_rich_text.created_at,
|
|
updated_at: old_rich_text.updated_at
|
|
)
|
|
|
|
mapping[:rich_text] ||= {}
|
|
mapping[:rich_text][old_rich_text.id] = new_rich_text.id
|
|
end
|
|
|
|
def copy_attachment(old_record, new_record, record_type, name)
|
|
old_attachment = import.attachments.find_by(record_type: record_type, record_id: old_record.id, name: name)
|
|
return unless old_attachment
|
|
|
|
old_blob = import.blobs.find(old_attachment.blob_id)
|
|
|
|
new_blob = ActiveStorage::Blob.find_or_create_by!(
|
|
key: old_blob.key,
|
|
filename: old_blob.filename,
|
|
content_type: old_blob.content_type,
|
|
metadata: old_blob.metadata,
|
|
service_name: old_blob.service_name,
|
|
byte_size: old_blob.byte_size,
|
|
checksum: old_blob.checksum,
|
|
created_at: old_blob.created_at
|
|
)
|
|
|
|
mapping[:blobs] ||= {}
|
|
mapping[:blobs][old_blob.id] = new_blob.id
|
|
|
|
new_attachment = ActiveStorage::Attachment.find_or_create_by!(
|
|
name: name,
|
|
record: new_record,
|
|
blob: new_blob,
|
|
created_at: old_attachment.created_at
|
|
)
|
|
|
|
mapping[:attachments] ||= {}
|
|
mapping[:attachments][old_attachment.id] = new_attachment.id
|
|
end
|
|
|
|
def copy_reactions
|
|
puts "⏩ Copying reactions"
|
|
mapping[:reactions] ||= {}
|
|
import.reactions.find_each do |old_reaction|
|
|
new_reaction = Reaction.create!(
|
|
account_id: account.id,
|
|
comment_id: mapping[:comments][old_reaction.comment_id],
|
|
reacter_id: mapping[:users][old_reaction.reacter_id],
|
|
content: old_reaction.content,
|
|
created_at: old_reaction.created_at,
|
|
updated_at: old_reaction.updated_at
|
|
)
|
|
|
|
mapping[:reactions][old_reaction.id] = new_reaction.id
|
|
end
|
|
puts "✅ Copied #{mapping[:reactions].size} reactions"
|
|
end
|
|
|
|
def copy_tags
|
|
puts "⏩ Copying tags"
|
|
mapping[:tags] ||= {}
|
|
mapping[:taggings] ||= {}
|
|
|
|
import.tags.find_each do |old_tag|
|
|
new_tag = Tag.find_or_create_by!(title: old_tag.title) do |t|
|
|
t.account_id = account.id
|
|
t.created_at = old_tag.created_at
|
|
t.updated_at = old_tag.updated_at
|
|
end
|
|
|
|
mapping[:tags][old_tag.id] = new_tag.id
|
|
end
|
|
|
|
import.taggings.find_each do |old_tagging|
|
|
new_tagging = Tagging.create!(
|
|
tag_id: mapping[:tags][old_tagging.tag_id],
|
|
card_id: mapping[:cards][old_tagging.card_id],
|
|
created_at: old_tagging.created_at,
|
|
updated_at: old_tagging.updated_at
|
|
)
|
|
|
|
mapping[:taggings][old_tagging.id] = new_tagging.id
|
|
end
|
|
puts "✅ Copied #{mapping[:tags].size} tags and #{mapping[:taggings].size} taggings"
|
|
end
|
|
|
|
def copy_watches
|
|
puts "⏩ Copying watches"
|
|
mapping[:watches] ||= {}
|
|
|
|
import.watches.find_each do |old_watch|
|
|
new_watch = Watch.create!(
|
|
user_id: mapping[:users][old_watch.user_id],
|
|
card_id: mapping[:cards][old_watch.card_id],
|
|
watching: old_watch.watching,
|
|
created_at: old_watch.created_at,
|
|
updated_at: old_watch.updated_at
|
|
)
|
|
|
|
mapping[:watches][old_watch.id] = new_watch.id
|
|
end
|
|
puts "✅ Copied #{mapping[:watches].size} watches"
|
|
end
|
|
|
|
def copy_pins
|
|
puts "⏩ Copying pins"
|
|
mapping[:pins] ||= {}
|
|
|
|
import.pins.find_each do |old_pin|
|
|
new_pin = Pin.create!(
|
|
user_id: mapping[:users][old_pin.user_id],
|
|
card_id: mapping[:cards][old_pin.card_id],
|
|
created_at: old_pin.created_at,
|
|
updated_at: old_pin.updated_at
|
|
)
|
|
|
|
mapping[:pins][old_pin.id] = new_pin.id
|
|
end
|
|
puts "✅ Copied #{mapping[:pins].size} pins"
|
|
end
|
|
|
|
def copy_webhooks
|
|
puts "⏩ Copying webhooks"
|
|
mapping[:webhooks] ||= {}
|
|
mapping[:webhook_deliveries] ||= {}
|
|
|
|
import.webhooks.find_each do |old_webhook|
|
|
new_webhook = Webhook.create!(
|
|
account_id: account.id,
|
|
board_id: mapping[:boards][old_webhook.board_id],
|
|
name: old_webhook.name,
|
|
url: old_webhook.url,
|
|
signing_secret: old_webhook.signing_secret,
|
|
subscribed_actions: old_webhook.subscribed_actions,
|
|
active: old_webhook.active,
|
|
created_at: old_webhook.created_at,
|
|
updated_at: old_webhook.updated_at
|
|
)
|
|
|
|
mapping[:webhooks][old_webhook.id] = new_webhook.id
|
|
|
|
old_tracker = import.webhook_delinquency_trackers.find_by(webhook_id: old_webhook.id)
|
|
Webhook::DelinquencyTracker.create!(
|
|
webhook_id: new_webhook.id,
|
|
consecutive_failures_count: old_tracker.consecutive_failures_count,
|
|
first_failure_at: old_tracker.first_failure_at,
|
|
created_at: old_tracker.created_at,
|
|
updated_at: old_tracker.updated_at
|
|
)
|
|
end
|
|
|
|
import.webhook_deliveries.find_each do |old_delivery|
|
|
new_delivery = Webhook::Delivery.create!(
|
|
webhook_id: mapping[:webhooks][old_delivery.webhook_id],
|
|
event_id: mapping[:events][old_delivery.event_id],
|
|
state: old_delivery.state,
|
|
request: old_delivery.request,
|
|
response: old_delivery.response,
|
|
created_at: old_delivery.created_at,
|
|
updated_at: old_delivery.updated_at
|
|
)
|
|
|
|
mapping[:webhook_deliveries][old_delivery.id] = new_delivery.id
|
|
end
|
|
puts "✅ Copied #{mapping[:webhooks].size} webhooks and #{mapping[:webhook_deliveries].size} deliveries"
|
|
end
|
|
|
|
def copy_push_subscriptions
|
|
puts "⏩ Copying push subscriptions"
|
|
mapping[:push_subscriptions] ||= {}
|
|
|
|
import.push_subscriptions.find_each do |old_subscription|
|
|
new_subscription = Push::Subscription.create!(
|
|
account_id: account.id,
|
|
user_id: mapping[:users][old_subscription.user_id],
|
|
endpoint: old_subscription.endpoint,
|
|
p256dh_key: old_subscription.p256dh_key,
|
|
auth_key: old_subscription.auth_key,
|
|
user_agent: old_subscription.user_agent,
|
|
created_at: old_subscription.created_at,
|
|
updated_at: old_subscription.updated_at
|
|
)
|
|
|
|
mapping[:push_subscriptions][old_subscription.id] = new_subscription.id
|
|
end
|
|
puts "✅ Copied #{mapping[:push_subscriptions].size} push subscriptions"
|
|
end
|
|
|
|
def fix_links
|
|
puts "⏩ Fixing links"
|
|
puts "✅ Fixed #{mapping[:cards].size} links"
|
|
end
|
|
|
|
def import
|
|
@import ||= Models.new(db_path)
|
|
rescue => e
|
|
$stderr.puts e.backtrace.join("\n") if ENV["DEBUG"]
|
|
raise "Couldn't open the given database: #{e}"
|
|
end
|
|
|
|
def untenanted
|
|
@untenanted ||= Models.new(untenanted_db_path)
|
|
rescue => e
|
|
$stderr.puts e.backtrace.join("\n") if ENV["DEBUG"]
|
|
raise "Couldn't open the given untenanted database: #{e}"
|
|
end
|
|
end
|
|
|
|
class Models
|
|
attr_reader :application_record
|
|
|
|
def initialize(db_path)
|
|
const_name = "ImportBase#{db_path.hash.abs}"
|
|
|
|
if self.class.const_defined?(const_name)
|
|
@application_record = self.class.const_get(const_name)
|
|
else
|
|
@application_record = Class.new(ActiveRecord::Base) do
|
|
self.abstract_class = true
|
|
|
|
def self.models
|
|
const_get("MODELS")
|
|
end
|
|
|
|
delegate :models, to: :class
|
|
end
|
|
self.class.const_set(const_name, @application_record)
|
|
end
|
|
|
|
@application_record.establish_connection adapter: "sqlite3", database: db_path
|
|
@application_record.const_set("MODELS", self)
|
|
end
|
|
|
|
def identities
|
|
@identities ||= Class.new(application_record) do
|
|
self.table_name = "identities"
|
|
end
|
|
end
|
|
|
|
def memberships
|
|
@memberships ||= begin
|
|
models = self
|
|
Class.new(application_record) do
|
|
self.table_name = "memberships"
|
|
|
|
def identity
|
|
@identity ||= models.identities.find_by(id: identity_id)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def accounts
|
|
@accounts ||= Class.new(application_record) do
|
|
self.table_name = "accounts"
|
|
end
|
|
end
|
|
|
|
def account_join_codes
|
|
@account_join_codes ||= Class.new(application_record) do
|
|
self.table_name = "account_join_codes"
|
|
end
|
|
end
|
|
|
|
def users
|
|
@users ||= begin
|
|
models = self
|
|
Class.new(application_record) do
|
|
self.table_name = "users"
|
|
|
|
def settings
|
|
@settings ||= models.user_settings.find_by(user_id: id)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def boards
|
|
@boards ||= begin
|
|
models = self
|
|
Class.new(application_record) do
|
|
self.table_name = "boards"
|
|
|
|
def publication
|
|
@publication ||= models.board_publications.find_by(board_id: id)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def columns
|
|
@columns ||= Class.new(application_record) do
|
|
self.table_name = "columns"
|
|
end
|
|
end
|
|
|
|
def cards
|
|
@cards ||= begin
|
|
models = self
|
|
Class.new(application_record) do
|
|
self.table_name = "cards"
|
|
|
|
def activity_spike
|
|
@activity_spike ||= models.card_activity_spikes.find_by(card_id: id)
|
|
end
|
|
|
|
def engagement
|
|
@engagement ||= models.card_engagements.find_by(card_id: id)
|
|
end
|
|
|
|
def goldness
|
|
@goldness ||= models.card_goldnesses.find_by(card_id: id)
|
|
end
|
|
|
|
def not_now
|
|
@not_now ||= models.card_not_nows.find_by(card_id: id)
|
|
end
|
|
|
|
def assignments
|
|
models.assignments.where(card_id: id)
|
|
end
|
|
|
|
def closure
|
|
@closure ||= models.closures.find_by(card_id: id)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def comments
|
|
@comments ||= Class.new(application_record) do
|
|
self.table_name = "comments"
|
|
end
|
|
end
|
|
|
|
def steps
|
|
@steps ||= Class.new(application_record) do
|
|
self.table_name = "steps"
|
|
end
|
|
end
|
|
|
|
def reactions
|
|
@reactions ||= Class.new(application_record) do
|
|
self.table_name = "reactions"
|
|
end
|
|
end
|
|
|
|
def tags
|
|
@tags ||= Class.new(application_record) do
|
|
self.table_name = "tags"
|
|
end
|
|
end
|
|
|
|
def taggings
|
|
@taggings ||= Class.new(application_record) do
|
|
self.table_name = "taggings"
|
|
end
|
|
end
|
|
|
|
def watches
|
|
@watches ||= Class.new(application_record) do
|
|
self.table_name = "watches"
|
|
end
|
|
end
|
|
|
|
def pins
|
|
@pins ||= Class.new(application_record) do
|
|
self.table_name = "pins"
|
|
end
|
|
end
|
|
|
|
def webhooks
|
|
@webhooks ||= Class.new(application_record) do
|
|
self.table_name = "webhooks"
|
|
end
|
|
end
|
|
|
|
def webhook_deliveries
|
|
@webhook_deliveries ||= Class.new(application_record) do
|
|
self.table_name = "webhook_deliveries"
|
|
end
|
|
end
|
|
|
|
def webhook_delinquency_trackers
|
|
@webhook_delinquency_trackers ||= Class.new(application_record) do
|
|
self.table_name = "webhook_delinquency_trackers"
|
|
end
|
|
end
|
|
|
|
def push_subscriptions
|
|
@push_subscriptions ||= Class.new(application_record) do
|
|
self.table_name = "push_subscriptions"
|
|
end
|
|
end
|
|
|
|
def assignments
|
|
@assignments ||= Class.new(application_record) do
|
|
self.table_name = "assignments"
|
|
end
|
|
end
|
|
|
|
def closures
|
|
@closures ||= Class.new(application_record) do
|
|
self.table_name = "closures"
|
|
end
|
|
end
|
|
|
|
def accesses
|
|
@accesses ||= Class.new(application_record) do
|
|
self.table_name = "accesses"
|
|
end
|
|
end
|
|
|
|
def events
|
|
@events ||= Class.new(application_record) do
|
|
self.table_name = "events"
|
|
end
|
|
end
|
|
|
|
def rich_texts
|
|
@rich_texts ||= Class.new(application_record) do
|
|
self.table_name = "action_text_rich_texts"
|
|
end
|
|
end
|
|
|
|
def attachments
|
|
@attachments ||= Class.new(application_record) do
|
|
self.table_name = "active_storage_attachments"
|
|
end
|
|
end
|
|
|
|
def blobs
|
|
@blobs ||= Class.new(application_record) do
|
|
self.table_name = "active_storage_blobs"
|
|
end
|
|
end
|
|
|
|
def user_settings
|
|
@user_settings ||= Class.new(application_record) do
|
|
self.table_name = "user_settings"
|
|
end
|
|
end
|
|
|
|
def board_publications
|
|
@board_publications ||= Class.new(application_record) do
|
|
self.table_name = "board_publications"
|
|
end
|
|
end
|
|
|
|
def card_activity_spikes
|
|
@card_activity_spikes ||= Class.new(application_record) do
|
|
self.table_name = "card_activity_spikes"
|
|
end
|
|
end
|
|
|
|
def card_engagements
|
|
@card_engagements ||= Class.new(application_record) do
|
|
self.table_name = "card_engagements"
|
|
end
|
|
end
|
|
|
|
def card_goldnesses
|
|
@card_goldnesses ||= Class.new(application_record) do
|
|
self.table_name = "card_goldnesses"
|
|
end
|
|
end
|
|
|
|
def card_not_nows
|
|
@card_not_nows ||= Class.new(application_record) do
|
|
self.table_name = "card_not_nows"
|
|
end
|
|
end
|
|
|
|
def mentions
|
|
@mentions ||= Class.new(application_record) do
|
|
self.table_name = "mentions"
|
|
end
|
|
end
|
|
|
|
def notifications
|
|
@notifications ||= Class.new(application_record) do
|
|
self.table_name = "notifications"
|
|
end
|
|
end
|
|
|
|
def notification_bundles
|
|
@notification_bundles ||= Class.new(application_record) do
|
|
self.table_name = "notification_bundles"
|
|
end
|
|
end
|
|
|
|
def entropies
|
|
@entropies ||= Class.new(application_record) do
|
|
self.table_name = "entropies"
|
|
end
|
|
end
|
|
|
|
def filters
|
|
@filters ||= Class.new(application_record) do
|
|
self.table_name = "filters"
|
|
end
|
|
end
|
|
|
|
def assignees_filters
|
|
@assignees_filters ||= Class.new(application_record) do
|
|
self.table_name = "assignees_filters"
|
|
end
|
|
end
|
|
|
|
def assigners_filters
|
|
@assigners_filters ||= Class.new(application_record) do
|
|
self.table_name = "assigners_filters"
|
|
end
|
|
end
|
|
|
|
def boards_filters
|
|
@boards_filters ||= Class.new(application_record) do
|
|
self.table_name = "boards_filters"
|
|
end
|
|
end
|
|
|
|
def closers_filters
|
|
@closers_filters ||= Class.new(application_record) do
|
|
self.table_name = "closers_filters"
|
|
end
|
|
end
|
|
|
|
def creators_filters
|
|
@creators_filters ||= Class.new(application_record) do
|
|
self.table_name = "creators_filters"
|
|
end
|
|
end
|
|
|
|
def filters_tags
|
|
@filters_tags ||= Class.new(application_record) do
|
|
self.table_name = "filters_tags"
|
|
end
|
|
end
|
|
end
|
|
|
|
options = {}
|
|
|
|
parser = OptionParser.new do |parser|
|
|
parser.banner = "Usage: #{$PROGRAM_NAME} <db_path> <untenanted_db_path>"
|
|
|
|
parser.on("-h", "--help", "Show this help message") do
|
|
puts parser
|
|
exit
|
|
end
|
|
end
|
|
|
|
parser.parse!
|
|
|
|
db_path = ARGV[0]
|
|
untenanted_db_path = ARGV[1]
|
|
|
|
if db_path.nil? || untenanted_db_path.nil?
|
|
$stderr.puts "Error: both db_path and untenanted_db_path are required"
|
|
$stderr.puts
|
|
$stderr.puts parser
|
|
exit 1
|
|
end
|
|
|
|
begin
|
|
Import.new(db_path, untenanted_db_path).import_database
|
|
rescue => e
|
|
$stderr.puts "Error: #{e.message}"
|
|
$stderr.puts e.backtrace.join("\n") if ENV["DEBUG"]
|
|
exit 1
|
|
end
|