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

Migration: Upload migration files from profile settings and start processing #8274

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from

Conversation

tclaus
Copy link
Member

@tclaus tclaus commented Jul 23, 2021

Currently a import from Settings, while a user already exists.

ToDo:

  • Photos upload and processing compressed file
  • Photos processing
  • Rake file adaptions
  • Use translation for texts

@tclaus tclaus changed the title Upload migration files from profile settings and start processing Migration: Upload migration files from profile settings and start processing Jul 23, 2021
%p
If the pod of the account you are importing is offline, your contacts and posts will still be imported,
but your contacts will receive a notification as if you started sharing with them.
= form_for current_user, url: edit_user_path, html: {method: :put, multipart: true, class: "form-horizontal"} do |f|

Choose a reason for hiding this comment

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

Line is too long. [126/120]

.clearfix
.btn.btn-default{data: {dismiss: "modal"}}
Cancel
= f.submit 'Import and merge', class: "btn btn-primary.pull-right", id: "change_email_preferences"

Choose a reason for hiding this comment

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

Style/StringLiterals: Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping.

.btn.btn-default{data: {dismiss: "modal"}}
Cancel
= f.submit 'Import and merge', class: "btn btn-primary.pull-right", id: "change_email_preferences"

Choose a reason for hiding this comment

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

Line contains trailing whitespace

end
end

private

Choose a reason for hiding this comment

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

  • Layout/AccessModifierIndentation: Indent access modifiers like private.
  • Layout/EmptyLinesAroundAccessModifier: Keep a blank line before and after private.

photo.remote_photo_name = nil # migration sets this attribute, but file needs to be procesed
photo.unprocessed_image = photo_filename
photo.save(touch: false)

Choose a reason for hiding this comment

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

Layout/EmptyLinesAroundBlockBody: Extra empty line detected at block body end.

def unzip_photos_file(photo_file_path)
system("unzip", photo_file_path)
end

Choose a reason for hiding this comment

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

Layout/EmptyLinesAroundClassBody: Extra empty line detected at class body end.

def filename
"#{model.username}_photos_#{secure_token}.zip" if original_filename.present?
if original_filename.present?

Choose a reason for hiding this comment

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

Style/GuardClause: Use a guard clause (return unless original_filename.present?) instead of wrapping the code inside a conditional expression.

end

def filename
"#{model.username}_diaspora_data_#{secure_token}.json.gz" if original_filename.present?
if original_filename.present?

Choose a reason for hiding this comment

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

Style/GuardClause: Use a guard clause (return unless original_filename.present?) instead of wrapping the code inside a conditional expression.

@tclaus tclaus marked this pull request as ready for review September 4, 2021 08:35
def url(name = nil)
if remote_photo_path
name = name.to_s + '_' if name
def url(name=nil)

Choose a reason for hiding this comment

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

Metrics/AbcSize: Assignment Branch Condition size for url is too high. [<2, 19, 7> 20.35/20]

# During migration in bad cases this might be happen
# If this happens, stream loading stops. Better dont show photos as stop loading stream
if remote_photo_path.present? && remote_photo_name.present?
name = "#{name.to_s}_" if name

Choose a reason for hiding this comment

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

Lint/RedundantStringCoercion: Redundant use of Object#to_s in interpolation.

@goobertron
Copy link

I'd like chance to review the text (including code comments) at some point, as Diaspora's (sort of) 'chief proof-reader'. I'll try to do this soon.

Copy link

@goobertron goobertron left a comment

Choose a reason for hiding this comment

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

I've reviewed most of the strings and made suggested changes to some of them. I think I've seen everything, but it's not easy with the text in code files rather than in a separate config file. I'll check again once the text has been extracted.

There are a few comments where I'm not sure exactly what they are supposed to communicate, so I've asked for clarification.

I'll have to think about the two large text blocks at a later date, so I'll come back to them.

Thanks!

@user.export = user_data[:export] if user_data[:export]
@user.exported_photos_file = user_data[:exported_photos_file] if user_data[:exported_photos_file]
if @user.save
flash.now[:notice] = "A profile migration is scheduled"

Choose a reason for hiding this comment

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

Assuming this includes data and photos, I suggest using 'account' rather than 'profile'. How about either:

  • Your account migration has been scheduled
    or
  • Your account migration is in progress

if @user.save
flash.now[:notice] = "A profile migration is scheduled"
else
flash.now[:error] = "An error occured scheduling a migration: #{@user.errors.full_messages}"

Choose a reason for hiding this comment

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

It looks as though a specific error message is appended to this warning, so I suggest:

  • The following error occurred in scheduling your account migration:
    or
  • Your account migration could not be scheduled for the following reason:
    (note: two Rs in occurred)

@@ -29,6 +29,7 @@ def sender
# executes a migration plan according to this AccountMigration object

Choose a reason for hiding this comment

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

Could this be clearer? Or is it clear to any coder?

else
true
end
end

def ownership_of_status_message
message = StatusMessage.find_by_guid(self.status_message_guid)
message = StatusMessage.find_by(guid: status_message_guid)
return unless status_message_guid && message && diaspora_handle != message.diaspora_handle

errors.add(:base, "Photo must have the same owner as status message")

Choose a reason for hiding this comment

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

If this is text in the UI, I'll need to know context in order to be able to polish this.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is internally only. I think, a photo must have a reference to a post ('status message' internally) to be valid. This text goes out to an error log.

Choose a reason for hiding this comment

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

Got it. That is probably clear enough to a coder or admin.

if remote_photo_path
name = name.to_s + '_' if name
def url(name=nil)
# During migration in bad cases this might be happen

Choose a reason for hiding this comment

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

Again, these two lines could be clearer, but context would be helpful.

Copy link
Member Author

Choose a reason for hiding this comment

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

A message to a coder. The if clause the next line was changed, because in rare cases a photo can not be processed. Then the stream will not load. Diaspora stops (crashes). Would be nice to use clear words for this.

Choose a reason for hiding this comment

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

How about 'If a photo cannot be processed during migration, the migration will fail and the stream will stop loading. If this happens, don't show the photos' ???

logger.error "Unexpected error while exporting user '#{username}': #{error.class}: #{error.message}\n" \
"#{error.backtrace.first(15).join("\n")}"
rescue StandardError => e
logger.error "Unexpected error while exporting user '#{username}': #{e.class}: #{e.message}\n" \

Choose a reason for hiding this comment

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

  • Unexpected error while exporting data for '#{username}':
    This mirrors the error message for photos.

%h3= t(".export_data")
%p If you want to migrate this account to another pod, start by exporting your data using the buttons below.

Choose a reason for hiding this comment

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

... start by exporting your data and photos ...

Import another account into
= current_user.diaspora_handle
%p
diaspora* allows you to merge an account into another, even if the accounts are in two different pods.

Choose a reason for hiding this comment

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

I'll have to come back to this text block. Do remind me.

Choose a reason for hiding this comment

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

%label Select the data archive:
= f.file_field :export, accept: "application/json, application/zip, application/gzip"
.col-md-6
%label Select the photos archive:

Choose a reason for hiding this comment

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

'photo archive'

%label Select the photos archive:
= f.file_field :exported_photos_file, accept: "application/zip"
%h4
The original account will be imported

Choose a reason for hiding this comment

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

You data will be imported %strong and the original account deleted.

Copy link

Choose a reason for hiding this comment

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

This needs to be transferred to config/locales/diaspora/en.yml. Also, I still think it's better not to repeat 'the original account' in this line, so change the first one to 'Your data will be imported'.

And maybe add 'This cannot be undone.' after this sentence?

%h3= t(".export_data")
%p If you want to migrate this account to another pod, start by exporting your data and photos using the buttons below.

Choose a reason for hiding this comment

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

Line is too long. [127/120]

@@ -0,0 +1,15 @@
const profileFileBtn = document.getElementById('profile-file-btn');

Choose a reason for hiding this comment

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

Strings must use doublequote.

@@ -0,0 +1,15 @@
const profileFileBtn = document.getElementById('profile-file-btn');

const profileFileChosen = document.getElementById('profile-file-chosen');

Choose a reason for hiding this comment

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

Strings must use doublequote.


const profileFileChosen = document.getElementById('profile-file-chosen');

profileFileBtn.addEventListener('change', function(){

Choose a reason for hiding this comment

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

  • Strings must use doublequote.
  • Missing space before opening brace.

const profileFileChosen = document.getElementById('profile-file-chosen');

profileFileBtn.addEventListener('change', function(){
profileFileChosen.textContent = this.files[0].name

Choose a reason for hiding this comment

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

Missing semicolon.


profileFileBtn.addEventListener('change', function(){
profileFileChosen.textContent = this.files[0].name
})

Choose a reason for hiding this comment

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

Missing semicolon.

.upload-btn-wrapper
.btn.btn-primary
Select profile archive
= f.file_field :export, accept: "application/json, application/zip, application/gzip",

Choose a reason for hiding this comment

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

Style/StringLiterals: Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping.

.upload-btn-wrapper
.btn.btn-primary
Select photo archive
= f.file_field :exported_photos_file, accept: "application/zip",

Choose a reason for hiding this comment

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

  • Layout/SpaceAfterColon: Space missing after colon.
  • Style/StringLiterals: Prefer double-quoted strings unless you need single quotes to avoid extra backslashes for escaping.

display: inline-block;
}

.upload-btn-wrapper input[type=file] {

Choose a reason for hiding this comment

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

  • Merge rule .upload-btn-wrapper input[type=file] with rule on line 21
  • Avoid qualifying attribute selectors with an element.

@@ -18,6 +18,20 @@ textarea {
}
// scss-lint:enable QualifyingElement

.upload-btn-wrapper {
position: relative;

Choose a reason for hiding this comment

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

Properties should be ordered display, overflow, position


.upload-btn-wrapper input[type=file] {
font-size: 100px;
position: absolute;

Choose a reason for hiding this comment

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

Properties should be ordered font-size, left, opacity, position, top

@@ -36,7 +36,8 @@ def create_user(attr)
profile_attributes: profile_attributes
}
)
self.user = User.build(data)
self.user = User.find_or_build(data)
self.user.getting_started = false

Choose a reason for hiding this comment

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

Style/RedundantSelf: Redundant self detected.

overflow: hidden;
position: relative;

input[type=file] {

Choose a reason for hiding this comment

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

Avoid qualifying attribute selectors with an element.

@tclaus
Copy link
Member Author

tclaus commented Sep 12, 2021

Current status of the import dialog is:

Bildschirmfoto 2021-09-08 um 23 14 08

Rake file allows import of photo archives:
Bildschirmfoto 2021-09-12 um 10 03 16

Copy link

@goobertron goobertron left a comment

Choose a reason for hiding this comment

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

Thanks. I can only comment on the wording (and the fact that all the text needs to be in config/locales/diaspora/en.yml.

Fla has said in this Discourse comment that the text is not that accurate, so we might have to rewrite some of it. But with the two minor changes I've just suggested, I think it reads well enough and is clear enough, even if some of it is not yet accurate!

list3: "All your aspects and contacts from that account will be added to this account"
list4: "All posts and comments from that account will be added to this account"
closing_text: "The imported account and all its linked data will be deleted from the previous pod.<br>
Your contacts do not need to do anything; they will automatically be connected to this account.<br>
Copy link

Choose a reason for hiding this comment

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

I would not have the rest of this text in bold – only 'The imported account and all its linked data will be deleted from the previous pod.'

I also suggest separating this text block into three strings, one for each paragraph, rather than using <br>, which I think is not favoured.

%label Select the photos archive:
= f.file_field :exported_photos_file, accept: "application/zip"
%h4
The original account will be imported
Copy link

Choose a reason for hiding this comment

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

This needs to be transferred to config/locales/diaspora/en.yml. Also, I still think it's better not to repeat 'the original account' in this line, so change the first one to 'Your data will be imported'.

And maybe add 'This cannot be undone.' after this sentence?

@Flaburgan
Copy link
Member

Hey, could you please make the rake test to fail when it doesn't find the archive?

.clearfix
.btn.btn-default{data: {dismiss: "modal"}}
= t("users.import_modal.cancel")
= f.submit t("users.import_modal.start_import"), class: "btn btn-primary.pull-right", id: "change_email_preferences"

Choose a reason for hiding this comment

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

Line is too long. [132/120]


profileFileButton.addEventListener("change", function() {
profileFileChosen.textContent = this.files[0].name;
checkProfileUploadButton();

Choose a reason for hiding this comment

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

'checkProfileUploadButton' was used before it was defined


photosFileButton.addEventListener("change", function() {
photosFileChosen.textContent = this.files[0].name;
checkProfileUploadButton();

Choose a reason for hiding this comment

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

'checkProfileUploadButton' was used before it was defined

});

const checkProfileUploadButton = function() {
let photo_files = $("#profile-file-btn")[0].files;

Choose a reason for hiding this comment

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

Identifier 'photo_files' is not in camel case.


const checkProfileUploadButton = function() {
let photo_files = $("#profile-file-btn")[0].files;
let profile_files = $("#photos-file-btn")[0].files;

Choose a reason for hiding this comment

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

Identifier 'profile_files' is not in camel case.

let photo_files = $("#profile-file-btn")[0].files;
let profile_files = $("#photos-file-btn")[0].files;

if ((photo_files.size + profile_files.size) === 0)

Choose a reason for hiding this comment

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

  • Opening curly brace does not appear on the same line as controlling statement.
  • Identifier 'photo_files' is not in camel case.
  • Identifier 'profile_files' is not in camel case.


if ((photo_files.size + profile_files.size) === 0)
{
$("#upload_profile_files").attr('disabled', 'disabled');

Choose a reason for hiding this comment

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

Strings must use doublequote.

{
$("#upload_profile_files").attr('disabled', 'disabled');
} else {
$("#upload_profile_files").removeAttr('disabled');

Choose a reason for hiding this comment

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

Strings must use doublequote.

$("#upload_profile_files").removeAttr('disabled');
}

}

Choose a reason for hiding this comment

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

  • Block must not be padded by blank lines.
  • Missing semicolon.

The datamodel and implementation supports multiple imports from different locations
This Commit removes a test and fixes the validation, which is not supported in the datamodel
- Updates (most) texts to goobs proposal
- Moved texts to localization file
- Fixed file-upload buttons
@Flaburgan
Copy link
Member

Hey @tclaus could you please extract the rake refactor and the photos import (basically everything only linked to the backend) in a smaller pull request? This would allow me to start doing more tests even if the UI isn't ready yet

@Flaburgan
Copy link
Member

So, I tried the UI locally and it didn't work, the validation took too much time and the HTTP connection "has been reinitialized" (says Firefox). I think we need to split this again. Maybe we could have a different setting page so it's actually easier to find (and to link to) for the user?
So we would add an "Export / Import" item in the settings menu. There, we have more space to actually put the import buttons, and allow the user to come back later even for the validation step.

@tclaus
Copy link
Member Author

tclaus commented Sep 27, 2021

So, I tried the UI locally and it didn't work, the validation took too much time and the HTTP connection "has been reinitialized" (says Firefox). I think we need to split this again. Maybe we could have a different setting page so it's actually easier to find (and to link to) for the user?
So we would add an "Export / Import" item in the settings menu. There, we have more space to actually put the import buttons, and allow the user to come back later even for the validation step.

An extra menu is nice.
There is no "wait" for the validation. Instantly after uploading the files, a job should start with validation and processing.
Did you test it locally.. without a running sidekiq for background processing? I have experienced something similar.

@Flaburgan
Copy link
Member

There is no "wait" for the validation. Instantly after uploading the files, a job should start with validation and processing.

But what happens for you when you click the button in the modal then? For me, it does launch the validation, but the modal stays open waiting for it to finish, and with real data, that takes too much time and the http connection expires.

@SuperTux88
Copy link
Member

If you run diaspora locally in dev-mode there is no sidekiq by default (the jobs happen in the request then, which works for most things needed for dev-mode), for certain things like migration (takes too long for being handled in request) and also federation with other local pods (with only 1 unicorn worker that is already busy handling the federation job it doesn't have another worker free to handle a webfinger request from a second pod, so it deadlocks), you need a sidekiq, so you need to enable that in the diaspora.toml.

@Flaburgan
Copy link
Member

Ok, I was able to enable sidekiq by setting single_process_mode = false. Thank you.
Indeed, the modal then close, but when the import fails, there is no message in the settings page. The button to import should disappear and there should be a feedback to the user.
After submitting the modal: "Processing archive XXX"
If the validation failed: "Archive is invalid, ERROR_MESSAGE"
If the import failed: "Import failed, ERROR_MESSAGE, please warn your podmin"
If the import succeed: "Archive XXX imported successfully" and the button to import should reappear.

If you like the idea of having all this on a separated page I can create it.

…_files

# Conflicts:
#	app/controllers/users_controller.rb
#	app/models/photo.rb
#	app/models/user.rb
#	app/services/migration_service.rb
#	app/uploaders/exported_photos.rb
#	app/uploaders/exported_user.rb
#	app/uploaders/secure_uploader.rb
#	lib/archive_importer.rb
#	lib/tasks/accounts.rake
#	spec/models/user_spec.rb
On profile file upload the same fields in the user table are used than for generated exporting files.
This conflicts if any export files exists and user wants to import something
@@ -122,6 +122,10 @@ def confirm_email
redirect_to edit_user_path
end

def has_import_parameter?(import_parameters)

Choose a reason for hiding this comment

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

Naming/PredicateName: Rename has_import_parameter? to import_parameter?.


def copy_import_files(user_data)
{
profile_path: copy_import_file(user_data[:export]),

Choose a reason for hiding this comment

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

Layout/FirstHashElementIndentation: Use 2 spaces for indentation in a hash, relative to the start of the line where the left curly brace is.

def copy_import_files(user_data)
{
profile_path: copy_import_file(user_data[:export]),
photos_path: copy_import_file(user_data[:exported_photos_file])

Choose a reason for hiding this comment

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

Layout/HashAlignment: Align the keys and values of a hash literal if they span more than one line.

end

def copy_import_file(tmp_file)
if tmp_file.present?

Choose a reason for hiding this comment

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

Style/GuardClause: Use a guard clause (return unless tmp_file.present?) instead of wrapping the code inside a conditional expression.


def copy_import_file(tmp_file)
if tmp_file.present?
file_path_to_save_to = Rails.root.join("public","uploads", "users", "#{current_user.username}_#{tmp_file.original_filename}")

Choose a reason for hiding this comment

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

  • Layout/SpaceAfterComma: Space missing after comma.
  • Layout/LineLength: Line is too long. [131/120]

# @param [*String] files
def remove_import_files(*files)
files.each do |file|
if file && File.exist?(file)

Choose a reason for hiding this comment

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

Style/IfUnlessModifier: Favor modifier if usage when having a single-line body. Another good alternative is the usage of control flow &&/||.

def remove_import_files(*files)
files.each do |file|
if file && File.exist?(file)
File.delete(file) rescue nil

Choose a reason for hiding this comment

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

Style/RescueModifier: Avoid using rescue in its modifier form.

@tclaus
Copy link
Member Author

tclaus commented Jun 11, 2022

I merged latest development into it to fix merge errors.
This adds a Import UI to the user settings page.

There might be a problem when uploading a json.gz - when unzipped, the resulting file is just a messed up binary. Can someone check this?

@tclaus tclaus requested a review from SuperTux88 July 2, 2022 13:26
@cmrd-senya
Copy link
Member

There might be a problem when uploading a json.gz - when unzipped, the resulting file is just a messed up binary. Can someone check this?

This may be an issue with the development environment, I've seen something like that happened while downloading the archive on the dev env.

@SuperTux88
Copy link
Member

Oh yes, if you download the file in the dev env, it is gzipped twice, because rails again gzips it on download, while in production it will be served by your reverse proxy which doesn't do stuff like that.

@tclaus
Copy link
Member Author

tclaus commented Jul 19, 2022

Oh yes, if you download the file in the dev env, it is gzipped twice, because rails again gzips it on download, while in production it will be served by your reverse proxy which doesn't do stuff like that.

Good to be on a team that can help.

Copy link
Member

@cmrd-senya cmrd-senya left a comment

Choose a reason for hiding this comment

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

So apart from my comments, a few thoughts:

  1. This PR doesn't add new tests, do we want to add tests here, or we don't consider tests mandatory for everything?
  2. Do we want to give podmins a configuration option to hide/show import functionality? Is there a reason why a podmin may want to turn off this feature on their pods?
  3. Could we combine the two archives -- profile and photos -- in one? We can reduce potential issues if we just needed to upload one file.

= t(".import.warning")
.form-group
.btn.btn-primary{id: "import_account", data: {toggle: "modal", target: "#importAccountModal"}}
Import another account...
Copy link
Member

Choose a reason for hiding this comment

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

this needs to be translatable -- wrap into t method call

end

def copy_import_file(tmp_file)
return if tmp_file.empty?
Copy link
Member

Choose a reason for hiding this comment

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

When I tried uploading archive locally I've got the following error raised on this line:

image

What do you think about adding automated tests for this scenario? Tests could catch issues like that.

Copy link
Member

Choose a reason for hiding this comment

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

I think you need to use blank? here instead

Suggested change
return if tmp_file.empty?
return if tmp_file.blank?

@@ -122,6 +122,10 @@ def confirm_email
redirect_to edit_user_path
end

def import_parameter?(import_parameters)
import_parameters[:profile_path] || import_parameters[:photos_path]
Copy link
Member

Choose a reason for hiding this comment

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

Do we support importing just photos without the profile?

Copy link
Member Author

Choose a reason for hiding this comment

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

A user could first import his/her account and in another step the photos. These files are huge, that to separate them still makes sense.
After the profile is imported the user could import the photos.
I wouldn't put these files into on (very) big file.

if path_to_photos.present?
logger.info("Importing photos from import file for '#{username}' from #{path_to_photos}")
import_user_photos(user, path_to_photos)
end
remove_file_references(user)
remove_import_files(path_to_profile, path_to_photos)
Copy link
Member

Choose a reason for hiding this comment

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

Cleaning up invalid data -- if the archive is in wrong format. If we don't clean up, then someone could abuse the pod by uploading invalid archives indefinitely.

I guess we could handle at least certain known errors when the archive import fails, and clean up the files. But not in every case, because in some cases we may want to retry the import.

Copy link
Member Author

Choose a reason for hiding this comment

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

What do you mean? The 'import_user_profile' and 'import_user_photos' handle (known) errors. Whenever execution comes here, the uploaded files (I guess the zipped ones) will be removed.
Could you describe your idea or give a suggestion?

end

def import_by_files(path_to_profile, path_to_photos, username, opts={})
user = User.find_by(username: username)
raise ArgumentError, "Username #{username} should exist before uploading photos." if user.nil?
Copy link
Member

Choose a reason for hiding this comment

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

I think raising exception from the sidekiq job would schedule this job to retry. If this error is irrecoverable we should instead finish the job without errors, possibly logging some error messages.

user = User.find(user_id)
ImportService.new.import_by_user(user)
ImportService.new.import_by_user(user.username, import_parameters)
Copy link
Member

Choose a reason for hiding this comment

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

So we have a user object, and then we get it's username just to look up the user again inside the ImportService. Maybe we can still pass the user object here?

Instead of changing this signature to username, maybe we could change import_by_files to accepting user object and in the rake task we could call import_by_files passing the user object that we find outside of the service?

@@ -17,6 +17,7 @@ def substitute_author

entity_data["photos"].each do |photo|
photo["entity_data"]["author"] = user.diaspora_handle
photo["entity_data"]["remote_photo_path"] = "#{AppConfig.pod_uri}uploads\/images\/"
Copy link
Member

Choose a reason for hiding this comment

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

Okay, so this is how we link uploaded photos. If there are files in the photos archive that weren't in the end linked to any photo record, should we detect and clean them up?

end

private
def import_profile_if_present(opts, path_to_photos, path_to_profile, username)
if path_to_profile.present?

Choose a reason for hiding this comment

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

Style/GuardClause: Use a guard clause (return unless path_to_profile.present?) instead of wrapping the code inside a conditional expression.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants