[go: up one dir, main page]

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

DEV: Refactor themes controller to services part 1 #29631

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

martin-brennan
Copy link
Contributor
@martin-brennan martin-brennan commented Nov 6, 2024

This commit starts to introduce services to replace actions in the
ThemesController. We will start with the low langing fruit:

  • Create
  • Destroy
  • ....

Endpoints like #import and #update will be much harder.
Also, the https://github.com/discourse/discourse-theme-creator plugin
overrides some of these controller actions directly, so we need
to be careful.

Copy link
Contributor
@Flink Flink left a comment

Choose a reason for hiding this comment

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

I know this is still a draft, but I left some comments, some are just me thinking out loud, I’ll be glad to get your feedback 😁

app/services/themes/create.rb Show resolved Hide resolved
app/services/themes/create.rb Outdated Show resolved Hide resolved
private

def ban_in_allowlist_mode
Theme.allowed_remote_theme_ids.nil?
Copy link
Contributor

Choose a reason for hiding this comment

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

This can’t be an empty array? It looks like it should return an array 😅 We could maybe use #blank? instead of #nil? just to be sure?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's weird, sometimes it's nil and sometimes it's an empty array...but yes I think blank could work here

def self.allowed_remote_theme_ids
return nil if GlobalSetting.allowed_theme_repos.blank?
get_set_cache "allowed_remote_theme_ids" do
urls = GlobalSetting.allowed_theme_repos.split(",").map(&:strip)
Theme.joins(:remote_theme).where("remote_themes.remote_url in (?)", urls).pluck(:id)
end
end

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Annoyingly this return nil seems to be important, if Theme.joins(:remote_theme).where("remote_themes.remote_url in (?)", urls).pluck(:id) returns an [] we don't care for the purposes of other existing code. Not sure if that's intentional or not, don't want to touch it for now

app/services/themes/create.rb Outdated Show resolved Hide resolved
app/services/themes/create.rb Outdated Show resolved Hide resolved
app/services/themes/create.rb Outdated Show resolved Hide resolved
app/services/themes/create.rb Outdated Show resolved Hide resolved
# TODO (martin) Ask loic about this, the old theme controller expected the
# errors from the theme model save to be shown to the user.
def save_theme(theme:)
theme.save!
Copy link
Contributor

Choose a reason for hiding this comment

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

For now, either use a rescue block and call fail! inside it or just call save and check for any errors (and call fail! 😁), as we discussed IIRC.

It seems we should be able to do something like that in the model step, though:

Theme.create(params.slice(:name, :user_id, :user_selectable, :color_scheme_id, :component)) do |theme|
  params.theme_fields.each do |field|
    theme.set_field(**field)
  end
end

The only problem is still that Theme#set_field raises because it does validations that should be done in ThemeField. Maybe a small refactor (moving validations where they should happen) could resolve that situation.

Maybe that’s an approach we could apply for updates too (Model.find_by(…).tap {|model| model.update(…) }), I don’t know 🤔

Otherwise, we could implement a new step, something like persist_model :my_model, it would call save on it, check if the model is both valid and persisted, if that’s not the case, it would stop the execution flow.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure I could try a refactor of set_field, and otherwise do the rescue/error check for save. persist_model sounds nice too 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just went for the theme.save/fail! model errors for now

Copy link
Contributor

Choose a reason for hiding this comment

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

I’m working on adding a new step to handle exceptions: #29660.
That would probably come in handy in a situation like this 😁

@Flink
Copy link
Contributor
Flink commented Nov 19, 2024

@martin-brennan ok, so now that #29660 has been merged, we can do this (just tested locally, did not a lot of tests though, but it’s working):

  transaction do
    try(Theme::InvalidFieldTargetError, Theme::InvalidFieldTypeError) do
      model :theme, :create_theme
    end
    step :update_default_theme
    step :log_theme_change
  end

  def create_theme(params:)
    Theme.create(params.slice(:name, :user_id, :user_selectable, :color_scheme_id, :component)) do |theme|
      params.theme_fields.to_a.each { |field| theme.set_field(**field.symbolize_keys) }
    end
  end

Instead of having 3 steps with their own error logic/handling, we just use one and everything’s covered 😁

In the controller, I see that everything’s matched through on_failure, but if we want more granular control, we can use on_model_errors (meaning the Theme model has errors on it, its creation failed for whatever reason) and on_exceptions (meaning #set_field failed at some points. I don’t think the model will be available at that point, though).

@martin-brennan
Copy link
Contributor Author

Nice that's great @Flink thanks for letting me know, will do some further refactors with this now 👍 Also I need to go and make service specs for all the services I've added 😅 The functionality is covered by controller specs but I still want the low level ones too

@Flink
Copy link
Contributor
Flink commented Nov 21, 2024

Great! Let me know if you need a hand or when you want another review 😁

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

Successfully merging this pull request may close these issues.

2 participants