Conversation
Fix merge issue
…73405-add-service-contract-layer-for-wiki-providers
Refactor the rest of the code
Updated specs
…er-for-wiki-providers
…i-providers' into implementation/73320-create-and-use-oauth2-auth
Fix some issues. Pour over some specs ✨
|
Caution The provided work package version does not match the core version Details:
Please make sure that:
|
NobodysNightmare
left a comment
There was a problem hiding this comment.
I left some comments during an initial code review.
I didn't yet click through the application and will do that next.
| if oauth_application.present? | ||
| concat(render(Primer::Beta::Label.new(scheme: :success)) { I18n.t(:label_completed) }) | ||
| else | ||
| concat(render(Primer::Beta::Label.new(scheme: :secondary)) { I18n.t("wikis.admin.wiki_providers.oauth.label_pending") }) |
There was a problem hiding this comment.
🟡 For the translations here (and in any other component): Shouldn't we be able to use t (not I18n.t) and pass translation paths in their relative form.
Example:
| concat(render(Primer::Beta::Label.new(scheme: :secondary)) { I18n.t("wikis.admin.wiki_providers.oauth.label_pending") }) | |
| concat(render(Primer::Beta::Label.new(scheme: :secondary)) { t("label_pending") }) |
Note that I chose this line as an example rather randomly, so let me elaborate further:
- I'd do that on all translation strings that are specific to this current component
- this way we ensure a consistent way to structure translations
- translations will be grouped by the component they are used in
- I'd not do it for translations where we know that we need the exact same translation in multiple places
- e.g. a
label_pendingmight already be defined somewhere extremely globally - but if we only know of a use-case for a pending label exactly in this component, we might only define it here
- ... and if we only know of cases of reusing it for wiki stuff, then somewhere in the wiki namespace
- e.g. a
| oauth: | ||
| openproject_oauth: OpenProject OAuth | ||
| openproject_oauth_description: Allow XWiki to access OpenProject data using an OAuth. | ||
| openproject_oauth_description: Allow XWiki to access OpenProject data using an OAuth application. Copy the credentials below into your XWiki instance. |
There was a problem hiding this comment.
🟡 I see an issue about translation abstraction here.
The current key of this translation is wikis.admin.wiki_providers.oauth.openproject_oauth_description. So apparently this is a generic key describing OAuth authentication in a wiki context. It's certainly usable for any given wiki provider that supports OAuth...
But the translation value is specific to XWiki, so it can't be used generically. The two possible solutions:
- Replace "XWiki" with "the wiki provider" and accept that the user-facing translation is not as specific as it could be
- Indicate the specific provider being used in the translation key, ideally in a form that's automatable
- e.g.
wikis.admin.wiki_providers.<provider_type>.oauth.openproject_oauth_description
- e.g.
| heading: Inline page links | ||
| admin: | ||
| wiki_providers: | ||
| index_description: Add an external wiki service to link work packages to existing wiki pages or create new ones directly from OpenProject. |
There was a problem hiding this comment.
Side note: I just saw this and this is a prime example of using t(".description"), which would've probably put the translation key together with other translations for the index view.
| label_new_xwiki_instance: New XWiki provider | ||
| no_results_title: You don't have any wiki providers set up yet. | ||
| no_results_description: Add a wiki provider to see them here. | ||
| label_edit: Edit XWiki provider |
There was a problem hiding this comment.
One more side note: Up until this point the translations were sorted alphabetically.
I am still surprised, that there is no linting rule for that, because non-sorted translations really make it hard to find stuff in longer translation files.
| class GeneralInformationContract < BaseContract | ||
| attribute :authentication_method | ||
| validates :authentication_method, presence: true | ||
| validates :authentication_method, |
There was a problem hiding this comment.
🟢 Shouldn't it be possible to combine the two validates statements into one or is there a semantical difference?
|
|
||
| def new | ||
| @wiki_provider = Wikis::XWikiProvider.new | ||
| @wiki_provider = Wikis::XWikiProvider.new if @wiki_provider.blank? |
There was a problem hiding this comment.
🟡 Since ensure_valid_wizard_parameters is only called for new, can we directly call this here? I was surprised to see that @wiki_provider could be set sometimes and didn't know what would set it.
It would be clearer if the code read like this:
@wiki_provider = continue_from_wizard_params || Wikis::XWikiProvider.new(note that I renamed the method in this example for additional clarity)
|
|
||
| def wiki_provider_wizard(wiki_provider) | ||
| Wikis::Adapters::Registry.resolve("#{wiki_provider}.components.setup_wizard") | ||
| .new(model: wiki_provider, user: current_user) |
There was a problem hiding this comment.
👀 This is something that we should collectively keep an eye on. I was surprised that this wasn't written as wiki_provider.resolve("components.setup_wizard"), until I realized that it required passing in the current_user as well.
This means two thing:
- Not all registry components can be instantiated the same way
- This alone can be a cause for concern, if we aimed at a very uniform interface
- but we might want to accept it
- Not all registry components can be instantiated through the
resolvemethod right now
If we accept that the interface to create things might require additional inputs sometimes, we could extend resolve to support that, e.g.
def resolve(registry_path, **init_options)
Adapters::Registry["#{self.class.registry_prefix}.#{registry_path}"].new(self, **init_options)
endThis would mean we can call it like wiki_provider.resolve("components.setup_wizard", user: current_user)
| label: I18n.t("activerecord.attributes.wikis/xwiki_provider.authentication_method"), | ||
| required: true | ||
| ) do |select| | ||
| Wikis::XWikiProvider::AUTHENTICATION_METHODS.each do |method| |
There was a problem hiding this comment.
🟡 This is another case of mixing abstraction layers. We are in a class called Wikis::Admin::AuthenticationMethodSelectForm. Very generic, not XWiki related, but it acts on the things supported by Wikis::XWikiProvider.
The two ways forward that I see:
- Make this form generic, but hand in information about the provider, so that the form can know which auth methods are supported for the current provider
- Communicate that this is a specific form, by indicating the
XWikiin its name
| has_many :page_links, dependent: :destroy | ||
|
|
||
| scope :enabled, -> { where(enabled: true) } | ||
| scope :visible, ->(user = User.current) { user.admin? ? all : none } |
There was a problem hiding this comment.
🔴 Is this what visibility is supposed to be for wiki providers?
This would mean that a non-admin is not allowed to see any wiki providers ever.
Considering that we might want to show UI elements that indicate the name of a wiki provider, this doesn't seem to be feasible to me.
I understand that we don't want to expose all details of a wiki provider to everyone (once an API for wiki providers exists), but the basic ability to see a wiki provider should be given for anyone that's able to view_wiki_links in any project.
|
|
||
| def url_is_https | ||
| return if url.blank? | ||
| def authenticate_via_oauth2? |
There was a problem hiding this comment.
🟡 Note that both authentication methods are OAuth2 based and both have OAuth2 in their name. This was intentional, because OIDC is not a requirement for any of them, still everyone keeps calling one of the two methods "OIDC based" 🤷
This method should be called accordingly. E.g.
| def authenticate_via_oauth2? | |
| def authenticate_via_two_way_oauth2? |
Ticket
73320
What are you trying to accomplish?
Add OAuth2 auth for the XWiki provider.
This PR also adds the creation of the OAuth clients for future development.
Screenshots
Merge checklist