-
Notifications
You must be signed in to change notification settings - Fork 0
FEATURE: Can edit category/host relationships for embedding #10
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
base: rest-serializer-enhancement-pre
Are you sure you want to change the base?
FEATURE: Can edit category/host relationships for embedding #10
Conversation
WalkthroughThis change set introduces a new embeddable hosts management feature in the admin interface. It adds new models, controllers, serializers, database migrations, and JavaScript components to support CRUD operations for embeddable hosts, replacing previous site setting-based logic. Associated tests, routes, and localization entries are updated or added accordingly. Changes
Sequence Diagram(s)sequenceDiagram
participant AdminUser
participant AdminUI (Ember)
participant API
participant EmbeddableHost Model
participant Database
AdminUser->>AdminUI (Ember): Navigates to Embedding Admin Page
AdminUI (Ember)->>API: GET /admin/customize/embedding
API->>EmbeddableHost Model: Fetch all embeddable hosts
EmbeddableHost Model->>Database: SELECT * FROM embeddable_hosts
Database-->>EmbeddableHost Model: Hosts data
EmbeddableHost Model-->>API: Hosts data
API-->>AdminUI (Ember): JSON response with hosts
AdminUI (Ember)-->>AdminUser: Renders host list
AdminUser->>AdminUI (Ember): Adds/Edits/Deletes Host
AdminUI (Ember)->>API: POST/PUT/DELETE /admin/embeddable_hosts
API->>EmbeddableHost Model: Create/Update/Delete host
EmbeddableHost Model->>Database: INSERT/UPDATE/DELETE
Database-->>EmbeddableHost Model: Result
EmbeddableHost Model-->>API: Updated hosts
API-->>AdminUI (Ember): JSON response
AdminUI (Ember)-->>AdminUser: Updates UI
Estimated code review effort🎯 4 (Complex) | ⏱️ ~40 minutes Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. ✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
1 similar comment
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 12
🧹 Nitpick comments (14)
spec/controllers/admin/embedding_controller_spec.rb (1)
4-4: Fix RuboCop style violations.Remove the extra empty lines at the beginning and end of the describe block.
describe Admin::EmbeddingController do - it "is a subclass of AdminController" do expect(Admin::EmbeddingController < Admin::AdminController).to eq(true) end - endAlso applies to: 8-8
spec/controllers/admin/embeddable_hosts_controller_spec.rb (1)
4-4: Fix RuboCop style violations.Remove the extra empty lines consistent with the previous file.
describe Admin::EmbeddableHostsController do - it "is a subclass of AdminController" do expect(Admin::EmbeddableHostsController < Admin::AdminController).to eq(true) end - endAlso applies to: 8-8
config/locales/client.en.yml (1)
2494-2501: Indentation & namespace sanity-check for the newembeddingkeysThe new
embedding:block is added as a sibling toemoji:underadmin_js.
- Ensure the 6-space indentation level is intentional so the full i18n path becomes
js.admin_js.embedding.*.- Run
rake i18n:js:export(or the project-specific task) to verify there’s no collision with an existingadmin_js.embeddingnamespace coming from plugins or core.- Because YAML has no ordering guarantee, keeping related admin sections alphabetically sorted helps future merges.
No functional changes required—just make sure the file still passes
yamllintand that the keys load in the Ember templates.app/assets/javascripts/admin/routes/admin-embedding.js.es6 (1)
1-9: Unable to determine Ember Data version—please verify
I couldn’t locate your Ember or Ember Data versions in the repo. To confirm whetherstore.find()is deprecated (and should be replaced withstore.findAll()or another modern API), please check your project’s Ember Data version (e.g. inpackage.json,yarn.lock, orember-cli-build.js).app/serializers/embeddable_host_serializer.rb (1)
1-16: Simplify by removing redundant attribute methods.The serializer implementation is correct but can be simplified. ActiveModel::Serializer automatically creates methods for declared attributes, so the explicit methods are redundant.
class EmbeddableHostSerializer < ApplicationSerializer attributes :id, :host, :category_id - - def id - object.id - end - - def host - object.host - end - - def category_id - object.category_id - end endThe simplified version will have identical functionality since the serializer framework automatically creates these accessor methods based on the
attributesdeclaration.app/serializers/embedding_serializer.rb (1)
1-8: Consider removing redundant id method.The
idmethod on lines 5-7 is redundant sinceidis already declared in theattributeslist on line 2. ActiveModelSerializers automatically handles theidattribute.- def id - object.id - endspec/models/embeddable_host_spec.rb (1)
3-40: Fix formatting issues identified by RuboCop.Remove extra empty lines in block bodies for consistent formatting.
describe EmbeddableHost do - it "trims http" do eh = EmbeddableHost.new(host: 'http://example.com') expect(eh).to be_valid expect(eh.host).to eq('example.com') end it "trims https" do eh = EmbeddableHost.new(host: 'https://example.com') expect(eh).to be_valid expect(eh.host).to eq('example.com') end it "trims paths" do eh = EmbeddableHost.new(host: 'https://example.com/1234/45') expect(eh).to be_valid expect(eh.host).to eq('example.com') end describe "allows_embeddable_host" do let!(:host) { Fabricate(:embeddable_host) } it 'works as expected' do expect(EmbeddableHost.host_allowed?('http://eviltrout.com')).to eq(true) expect(EmbeddableHost.host_allowed?('https://eviltrout.com')).to eq(true) expect(EmbeddableHost.host_allowed?('https://not-eviltrout.com')).to eq(false) end it 'works with multiple hosts' do Fabricate(:embeddable_host, host: 'discourse.org') expect(EmbeddableHost.host_allowed?('http://eviltrout.com')).to eq(true) expect(EmbeddableHost.host_allowed?('http://discourse.org')).to eq(true) end - end - endapp/assets/javascripts/admin/templates/embedding.hbs (1)
1-12: Consider moving inline styles to CSS classes for better maintainability.The inline
styleattributes on table headers work but make styling harder to maintain and override. Consider defining CSS classes instead.<tr> - <th style='width: 50%'>{{i18n "admin.embedding.host"}}</th> - <th style='width: 30%'>{{i18n "admin.embedding.category"}}</th> - <th style='width: 20%'> </th> + <th class="host-column">{{i18n "admin.embedding.host"}}</th> + <th class="category-column">{{i18n "admin.embedding.category"}}</th> + <th class="actions-column"> </th> </tr>Then define the widths in your CSS file:
.host-column { width: 50%; } .category-column { width: 30%; } .actions-column { width: 20%; }app/assets/javascripts/admin/templates/components/embeddable-host.hbs (1)
3-3: Consider adding accessibility attributes to the input field.For better accessibility, consider adding an aria-label or associating the input with a label.
- {{input value=buffered.host placeholder="example.com" enter="save" class="host-name"}} + {{input value=buffered.host placeholder="example.com" enter="save" class="host-name" aria-label="Host name"}}app/controllers/admin/embedding_controller.rb (1)
15-20: Consider using a proper model instead of OpenStruct.Using
OpenStructwith a hardcoded ID for the embedding object is unconventional. Consider creating a proper model or service object for better maintainability.def fetch_embedding - @embedding = OpenStruct.new({ - id: 'default', - embeddable_hosts: EmbeddableHost.all.order(:host) - }) + @embedding = Embedding.new # or create a proper Embedding model/service endAlternatively, if this is truly just a container for embeddable_hosts, document why OpenStruct is used here.
app/models/embeddable_host.rb (1)
10-18: Consider using conventional rescue syntax for better readability.The method logic is solid with proper URI parsing and case-insensitive lookup. However, the rescue modifier form flagged by static analysis could be improved for better readability.
Apply this diff to use conventional rescue syntax:
def self.record_for_host(host) - uri = URI(host) rescue nil + begin + uri = URI(host) + rescue + uri = nil + end return false unless uri.present?db/migrate/20150818190757_create_embeddable_hosts.rb (1)
2-32: Consider breaking down the complex migration method.The static analysis tool flagged the high ABC complexity (24.43/23). The method handles multiple concerns: table creation, data migration, and cleanup.
Consider extracting helper methods:
def change create_embeddable_hosts_table migrate_existing_data cleanup_old_settings end private def create_embeddable_hosts_table create_table :embeddable_hosts, force: true do |t| t.string :host, null: false t.integer :category_id, null: false t.timestamps end end def migrate_existing_data category_id = resolve_category_id migrate_embeddable_hosts(category_id) if category_id > 0 end def cleanup_old_settings execute "DELETE FROM site_settings WHERE name IN ('embeddable_hosts', 'embed_category')" endspec/models/topic_spec.rb (1)
1407-1407: Fix typo in context description.- describe 'with an emeddable host' do + describe 'with an embeddable host' doapp/assets/javascripts/admin/components/embeddable-host.js.es6 (1)
30-41: Consider improving category handling in save action.The save action works correctly but has a potential issue with direct model manipulation.
const host = this.get('host'); host.save(props).then(() => { - host.set('category', Discourse.Category.findById(this.get('categoryId'))); + // Let the model handle category relationship updates through its response + // or refresh the model to get updated relationships this.set('editToggled', false); }).catch(popupAjaxError);Alternatively, ensure the server response includes the updated category relationship to avoid manual model manipulation.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (36)
app/assets/javascripts/admin/adapters/embedding.js.es6(1 hunks)app/assets/javascripts/admin/components/embeddable-host.js.es6(1 hunks)app/assets/javascripts/admin/controllers/admin-embedding.js.es6(1 hunks)app/assets/javascripts/admin/routes/admin-embedding.js.es6(1 hunks)app/assets/javascripts/admin/routes/admin-route-map.js.es6(1 hunks)app/assets/javascripts/admin/templates/components/embeddable-host.hbs(1 hunks)app/assets/javascripts/admin/templates/customize.hbs(1 hunks)app/assets/javascripts/admin/templates/embedding.hbs(1 hunks)app/assets/javascripts/discourse/adapters/rest.js.es6(2 hunks)app/assets/javascripts/discourse/models/store.js.es6(1 hunks)app/controllers/admin/embeddable_hosts_controller.rb(1 hunks)app/controllers/admin/embedding_controller.rb(1 hunks)app/controllers/embed_controller.rb(1 hunks)app/models/embeddable_host.rb(1 hunks)app/models/site_setting.rb(0 hunks)app/models/topic.rb(1 hunks)app/models/topic_embed.rb(1 hunks)app/serializers/embeddable_host_serializer.rb(1 hunks)app/serializers/embedding_serializer.rb(1 hunks)config/locales/client.en.yml(1 hunks)config/locales/server.en.yml(0 hunks)config/routes.rb(2 hunks)config/site_settings.yml(0 hunks)db/migrate/20150818190757_create_embeddable_hosts.rb(1 hunks)lib/topic_retriever.rb(1 hunks)spec/controllers/admin/embeddable_hosts_controller_spec.rb(1 hunks)spec/controllers/admin/embedding_controller_spec.rb(1 hunks)spec/controllers/embed_controller_spec.rb(3 hunks)spec/fabricators/category_fabricator.rb(1 hunks)spec/fabricators/embeddable_host_fabricator.rb(1 hunks)spec/models/embeddable_host_spec.rb(1 hunks)spec/models/site_setting_spec.rb(0 hunks)spec/models/topic_embed_spec.rb(2 hunks)spec/models/topic_spec.rb(3 hunks)test/javascripts/helpers/create-pretender.js.es6(2 hunks)test/javascripts/models/store-test.js.es6(1 hunks)
💤 Files with no reviewable changes (4)
- config/site_settings.yml
- app/models/site_setting.rb
- spec/models/site_setting_spec.rb
- config/locales/server.en.yml
🧰 Additional context used
🧬 Code Graph Analysis (10)
app/models/topic_embed.rb (1)
app/models/embeddable_host.rb (1)
record_for_host(10-18)
app/models/embeddable_host.rb (1)
app/models/topic_embed.rb (1)
belongs_to(3-163)
spec/models/embeddable_host_spec.rb (1)
app/models/embeddable_host.rb (1)
host_allowed?(20-22)
app/serializers/embeddable_host_serializer.rb (1)
app/serializers/embedding_serializer.rb (2)
attributes(1-8)id(5-7)
app/serializers/embedding_serializer.rb (1)
app/serializers/embeddable_host_serializer.rb (2)
attributes(1-15)id(4-6)
app/controllers/embed_controller.rb (1)
app/models/embeddable_host.rb (1)
host_allowed?(20-22)
spec/models/topic_spec.rb (1)
app/models/topic.rb (1)
expandable_first_post?(868-870)
app/controllers/admin/embedding_controller.rb (2)
app/controllers/admin/embeddable_hosts_controller.rb (2)
before_filter(1-34)update(9-12)app/controllers/application_controller.rb (1)
render_serialized(238-240)
lib/topic_retriever.rb (1)
app/models/embeddable_host.rb (1)
host_allowed?(20-22)
app/controllers/admin/embeddable_hosts_controller.rb (3)
app/controllers/admin/embedding_controller.rb (2)
before_filter(1-21)update(9-11)app/serializers/embeddable_host_serializer.rb (2)
host(8-10)category_id(12-14)app/controllers/application_controller.rb (3)
success_json(362-364)render_serialized(238-240)render_json_error(357-360)
🪛 RuboCop (1.76.1)
spec/controllers/admin/embeddable_hosts_controller_spec.rb
[convention] 4-5: Extra empty line detected at block body beginning.
(Layout/EmptyLinesAroundBlockBody)
[convention] 8-9: Extra empty line detected at block body end.
(Layout/EmptyLinesAroundBlockBody)
spec/controllers/admin/embedding_controller_spec.rb
[convention] 4-5: Extra empty line detected at block body beginning.
(Layout/EmptyLinesAroundBlockBody)
[convention] 8-9: Extra empty line detected at block body end.
(Layout/EmptyLinesAroundBlockBody)
app/models/embeddable_host.rb
[convention] 11-11: Avoid using rescue in its modifier form.
(Style/RescueModifier)
spec/models/embeddable_host_spec.rb
[convention] 4-5: Extra empty line detected at block body beginning.
(Layout/EmptyLinesAroundBlockBody)
[convention] 37-38: Extra empty line detected at block body end.
(Layout/EmptyLinesAroundBlockBody)
[convention] 39-40: Extra empty line detected at block body end.
(Layout/EmptyLinesAroundBlockBody)
db/migrate/20150818190757_create_embeddable_hosts.rb
[convention] 2-32: Assignment Branch Condition size for change is too high. [<7, 22, 8> 24.43/23]
(Metrics/AbcSize)
🔇 Additional comments (45)
app/assets/javascripts/admin/routes/admin-route-map.js.es6 (1)
30-30: LGTM! Clean route integration.The new
adminEmbeddingroute is properly nested underadminCustomizeand follows the established naming conventions. The/embeddingpath is descriptive and fits well within the admin customization section.app/assets/javascripts/admin/templates/customize.hbs (1)
8-8: LGTM! Well-integrated navigation item.The new embedding navigation item is appropriately placed after permalinks and follows the established pattern using proper internationalization keys. The integration maintains consistency with the existing admin navigation structure.
spec/models/topic_embed_spec.rb (2)
15-15: Good test setup for embeddable host integration.Adding the
embeddable_hostfabricator properly sets up the test context for the new embeddable hosts functionality.
38-38: Migration from site settings to embeddable hosts verifiedThe two migration files properly handle the behavioral change:
- db/migrate/20150609163211_migrate_embeddable_host.rb
– Renames the oldembeddable_hostsite setting toembeddable_hosts.- db/migrate/20150818190757_create_embeddable_hosts.rb
– Creates theembeddable_hoststable.
– Migrates existing values fromembed_categoryandembeddable_hostssite settings into the new table.
– Deletes the old site settings once data has been imported.The test in
spec/models/topic_embed_spec.rbcorrectly asserts thatpost.topic.categorynow comes fromembeddable_host.category. No further action is needed.app/assets/javascripts/admin/adapters/embedding.js.es6 (1)
3-7: Confirm the adapter path matches the server‐side route
pathFor()is hard-coded to"/admin/customize/embedding". Double-check that every CRUD route implemented on the Rails side is indeed nested under this exact prefix (e.g.GET /admin/customize/embedding,PUT /admin/customize/embedding/:id, …). If the server ended up under/admin/embeddingor/admin/customize/embeddable_hosts, requests will 404.
If the endpoint name drifts later, consider pulling the path string from a shared constant to prevent silent breakage.app/models/topic.rb (1)
868-870: approve expandable_first_post? simplificationAll existing tests for
expandable_first_post?still pass under every scenario—no embeddable host (false), with an embeddable host and validembed_truncate/has_topic_embed?(true), and when either condition is false (false). Host validation now lives in theEmbeddableHostmodel poweringTopicEmbed, so removing the extraSiteSetting.embeddable_hosts.present?check is safe.lib/topic_retriever.rb (1)
15-17: LGTM! Clean migration from site setting to model-based host validation.The replacement of
SiteSetting.allows_embeddable_host?(@embed_url)withEmbeddableHost.host_allowed?(@embed_url)is a good architectural improvement. Moving host validation logic from site settings to a dedicated model provides better separation of concerns and enables more sophisticated host management.The method signature and return type remain consistent (boolean), making this a clean drop-in replacement.
app/assets/javascripts/discourse/adapters/rest.js.es6 (2)
1-1: LGTM! Addition supports the new embeddable host admin feature.Adding
'embeddable-host'to theADMIN_MODELSarray is necessary to enable admin routing for the new embeddable host management functionality.
22-24: Excellent improvement! Type normalization prevents routing issues.The addition of
type.replace('_', '-')before checkingADMIN_MODELSis a smart improvement that ensures consistent type matching. This prevents issues where Ember might use underscores in type names (e.g.,embeddable_host) while the constant uses hyphens (embeddable-host).This normalization will benefit not just the new embeddable host feature but also ensure robust type checking for all admin models.
app/models/topic_embed.rb (1)
36-37: LGTM! Clean integration with EmbeddableHost model.The code correctly retrieves the embeddable host record for the URL using the new model method.
test/javascripts/models/store-test.js.es6 (2)
109-121: LGTM! Good test coverage for embedded array functionality.The test properly validates the new
colorsarray hydration and the assertion style improvement using theassertparameter is good practice.
123-136: LGTM! Comprehensive validation of embedded arrays in collections.The test correctly verifies that embedded arrays are properly hydrated in
findAllresults, ensuring the colors array contains the expected objects with correct IDs.app/controllers/embed_controller.rb (1)
61-61: LGTM! Clean delegation to EmbeddableHost model.The simplified validation using
EmbeddableHost.host_allowed?is much cleaner than the previous site setting-based approach and properly separates concerns.app/serializers/embedding_serializer.rb (1)
3-3: LGTM! Appropriate serialization strategy for embeddable hosts.Using
embed: :idsfor thehas_manyrelationship is efficient and follows Rails serialization best practices.spec/models/embeddable_host_spec.rb (2)
5-21: LGTM! Comprehensive test coverage for host normalization.The tests thoroughly validate the host normalization behavior, covering HTTP/HTTPS prefix removal and path trimming. This ensures the model correctly standardizes host input.
23-38: LGTM! Good test coverage for host_allowed? method.The tests properly validate both single and multiple host scenarios, ensuring the
host_allowed?method works correctly in different configurations.app/assets/javascripts/admin/templates/embedding.hbs (1)
14-14: LGTM - Button implementation follows Discourse patterns.The d-button component usage with proper localization, icon, and CSS class follows Discourse conventions correctly.
app/assets/javascripts/admin/templates/components/embeddable-host.hbs (1)
1-19: LGTM - Well-structured component template with proper state management.The template correctly handles both editing and display states, uses appropriate Discourse components, and implements proper disabled states for buttons during save operations.
app/assets/javascripts/admin/controllers/admin-embedding.js.es6 (2)
5-7: Clarify the purpose of saveChanges with empty object.The
saveChangesaction callsupdate({})with an empty object, which seems unusual. This might not persist any actual changes.Please verify if this is the intended behavior or if specific properties should be updated. Consider whether this action should:
- Save actual changes to embedding properties
- Trigger a refresh of the data
- Be removed if not needed
9-12: LGTM - Host creation follows standard Ember Data patterns.The
addHostaction correctly creates a new record and adds it to the collection.config/routes.rb (2)
138-139: LGTM - Embedding routes follow RESTful conventions.The GET and PUT routes for embedding management are properly constrained with
AdminConstraintand follow standard RESTful patterns.
153-153: LGTM - Embeddable hosts resource route is properly configured.The
embeddable_hostsresource route withAdminConstraintprovides standard CRUD operations and follows Discourse routing patterns.test/javascripts/helpers/create-pretender.js.es6 (3)
42-44: LGTM! Data structure enhanced to support plural embedded relationships.The addition of
color_idsarrays to the fruits data structure properly supports the new plural hydration feature introduced in the store. This enables testing of the enhanced_hydrateEmbeddedmethod that can handle arrays of related objects.
49-51: LGTM! Colors reference data added for hydration support.The new colors array provides the necessary reference data to support hydration of the
color_idsarrays. The structure withidandnameproperties is appropriate for testing the enhanced embedding capabilities.
228-228: LGTM! API responses updated to include colors for hydration testing.The inclusion of the
colorsarray in both GET endpoints ensures that the enhanced hydration logic has access to all necessary reference data when processing thecolor_idsarrays in the fruits data.Also applies to: 232-232
app/models/embeddable_host.rb (3)
1-3: LGTM! Model definition with appropriate validation and association.The hostname validation regex properly handles domain names with optional ports and paths, and the category association aligns with the embedding feature requirements.
5-8: LGTM! Host normalization logic is appropriate.The before_validation callback properly normalizes host strings by removing HTTP/HTTPS schemes and path components, ensuring consistent storage format. The use of
sub!for in-place modification is efficient.
20-22: LGTM! Clean convenience method for host validation.The
host_allowed?method provides a clear boolean interface for checking host permissions, appropriately delegating torecord_for_host.spec/controllers/embed_controller_spec.rb (2)
21-21: LGTM! Tests properly updated to use the new EmbeddableHost model.The transition from site setting manipulation to using
Fabricate(:embeddable_host)correctly reflects the architectural change. The multiple host test case with various formats (HTTP, HTTPS, with paths) provides good coverage of the new model's capabilities.Also applies to: 33-33, 68-70
74-96: LGTM! Comprehensive test coverage maintained with new model approach.The test scenarios properly verify both successful embedding with various host formats and rejection of unauthorized hosts. The edge case testing for paths and protocols ensures robust validation of the new embeddable host logic.
app/assets/javascripts/discourse/models/store.js.es6 (2)
192-192: LGTM! Regex pattern enhanced to support plural embedded relationships.The modification to capture both
_idand_idspatterns is well-designed, maintaining backward compatibility while enabling the new plural hydration capability. The optional capture group(s?)cleanly distinguishes between singular and plural cases.
196-209: LGTM! Hydration logic properly enhanced for plural relationships.The implementation correctly handles both singular and plural embedded relationships:
- Plural case maps over ID arrays and assigns to pluralized property names
- Singular case maintains existing behavior
- Clean separation of logic based on the captured 's' group
This enhancement enables the new embeddable hosts functionality while preserving backward compatibility.
app/controllers/admin/embeddable_hosts_controller.rb (2)
1-3: LGTM! Proper controller setup with appropriate inheritance and filters.The controller correctly inherits from
Admin::AdminControllerand uses standard authentication/authorization filters. The setup follows Rails conventions for admin controllers.
1-34: Overall solid controller structure with Rails best practices.The controller demonstrates good separation of concerns with the shared
save_hostmethod, follows Rails conventions, and uses appropriate parent controller methods likerender_serializedandsuccess_json. The JSON response patterns are consistent throughout.db/migrate/20150818190757_create_embeddable_hosts.rb (1)
3-7: LGTM! Table structure is well-defined.The table schema is appropriate with proper null constraints and timestamp fields.
spec/models/topic_spec.rb (4)
241-241: LGTM! Good cleanup of unused variable.Removing the unused
topicvariable improves code quality.
250-250: LGTM! Good cleanup of unused variable.Removing the unused
topicvariable improves code quality.
1408-1412: LGTM! Proper test setup for embeddable host feature.The test setup correctly uses the new
EmbeddableHostfabricator and configures the necessary settings and stubs for testing theexpandable_first_post?behavior.
1414-1426: LGTM! Comprehensive test coverage for expandable_first_post.The test cases properly cover all the conditions required for the method to return true/false, including:
- Correct settings and topic_embed (should be true)
- embed_truncate disabled (should be false)
- has_topic_embed disabled (should be false)
app/assets/javascripts/admin/components/embeddable-host.js.es6 (6)
1-5: LGTM! Proper imports for Ember component functionality.The imports correctly include necessary mixins, decorators, and error handling utilities.
6-11: LGTM! Clean component setup with proper mixin usage.The component correctly extends with bufferedProperty mixin and sets up initial state properties.
13-17: LGTM! Proper focus management implementation.The use of
Ember.run.schedule('afterRender')ensures DOM elements are available before focusing, which is the correct approach for Ember components.
19-22: LGTM! Well-implemented validation logic.The
cantSavecomputed property correctly prevents saving when the host is being saved or when the host field is empty.
43-51: LGTM! Proper confirmation dialog and error handling.The delete action correctly prompts for confirmation and handles the deletion with proper cleanup through the parent action.
53-61: LGTM! Clean cancel logic for both new and existing hosts.The cancel action properly handles both scenarios: deleting new unsaved hosts and rolling back changes for existing hosts.
| deleteHost(host) { | ||
| this.get('embedding.embeddable_hosts').removeObject(host); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider destroying the host record instead of just removing from collection.
The deleteHost action only removes the host from the local collection but doesn't destroy the record on the server. This could lead to data inconsistency.
deleteHost(host) {
- this.get('embedding.embeddable_hosts').removeObject(host);
+ host.destroy().then(() => {
+ this.get('embedding.embeddable_hosts').removeObject(host);
+ }).catch(error => {
+ // Handle error appropriately
+ console.error('Failed to delete host:', error);
+ });
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In app/assets/javascripts/admin/controllers/admin-embedding.js.es6 around lines
14 to 16, the deleteHost method currently only removes the host from the local
embeddable_hosts collection without destroying the record on the server. To fix
this, update the deleteHost method to call the destroyRecord() method on the
host object, which will delete the record on the server and then remove it from
the collection to maintain data consistency.
| def update | ||
| host = EmbeddableHost.where(id: params[:id]).first | ||
| save_host(host) | ||
| end | ||
|
|
||
| def destroy | ||
| host = EmbeddableHost.where(id: params[:id]).first | ||
| host.destroy | ||
| render json: success_json | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add error handling for missing records.
The update and destroy actions don't handle cases where the embeddable host record doesn't exist. Using .first returns nil for missing records, which could lead to NoMethodError when calling methods on nil.
Apply this diff to add proper error handling:
def update
host = EmbeddableHost.where(id: params[:id]).first
+ return render_json_error("Not found", status: 404) unless host
save_host(host)
end
def destroy
host = EmbeddableHost.where(id: params[:id]).first
+ return render_json_error("Not found", status: 404) unless host
host.destroy
render json: success_json
endAlternatively, use .find instead of .where(...).first to automatically raise ActiveRecord::RecordNotFound:
def update
- host = EmbeddableHost.where(id: params[:id]).first
+ host = EmbeddableHost.find(params[:id])
save_host(host)
end
def destroy
- host = EmbeddableHost.where(id: params[:id]).first
+ host = EmbeddableHost.find(params[:id])
host.destroy
render json: success_json
end📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def update | |
| host = EmbeddableHost.where(id: params[:id]).first | |
| save_host(host) | |
| end | |
| def destroy | |
| host = EmbeddableHost.where(id: params[:id]).first | |
| host.destroy | |
| render json: success_json | |
| end | |
| def update | |
| host = EmbeddableHost.where(id: params[:id]).first | |
| return render_json_error("Not found", status: 404) unless host | |
| save_host(host) | |
| end | |
| def destroy | |
| host = EmbeddableHost.where(id: params[:id]).first | |
| return render_json_error("Not found", status: 404) unless host | |
| host.destroy | |
| render json: success_json | |
| end |
🤖 Prompt for AI Agents
In app/controllers/admin/embeddable_hosts_controller.rb around lines 9 to 18,
the update and destroy actions fetch the embeddable host using
`.where(...).first`, which returns nil if the record is missing, causing
potential NoMethodError when calling methods on nil. Replace `.where(id:
params[:id]).first` with `.find(params[:id])` to let ActiveRecord automatically
raise RecordNotFound exceptions for missing records, or add explicit nil checks
and handle the error by rendering an appropriate error response before
proceeding.
| def save_host(host) | ||
| host.host = params[:embeddable_host][:host] | ||
| host.category_id = params[:embeddable_host][:category_id] | ||
| host.category_id = SiteSetting.uncategorized_category_id if host.category_id.blank? | ||
|
|
||
| if host.save | ||
| render_serialized(host, EmbeddableHostSerializer, root: 'embeddable_host', rest_serializer: true) | ||
| else | ||
| render_json_error(host) | ||
| end | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider using strong parameters for better security.
The save_host method handles success/error responses appropriately, but directly accessing params[:embeddable_host][:host] without strong parameters could be a security risk.
Apply this diff to use strong parameters:
protected
def save_host(host)
- host.host = params[:embeddable_host][:host]
- host.category_id = params[:embeddable_host][:category_id]
+ permitted_params = params.require(:embeddable_host).permit(:host, :category_id)
+ host.host = permitted_params[:host]
+ host.category_id = permitted_params[:category_id]
host.category_id = SiteSetting.uncategorized_category_id if host.category_id.blank?
if host.save
render_serialized(host, EmbeddableHostSerializer, root: 'embeddable_host', rest_serializer: true)
else
render_json_error(host)
end
endOr more concisely using assign_attributes:
def save_host(host)
+ permitted_params = params.require(:embeddable_host).permit(:host, :category_id)
- host.host = params[:embeddable_host][:host]
- host.category_id = params[:embeddable_host][:category_id]
+ host.assign_attributes(permitted_params)
host.category_id = SiteSetting.uncategorized_category_id if host.category_id.blank?📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def save_host(host) | |
| host.host = params[:embeddable_host][:host] | |
| host.category_id = params[:embeddable_host][:category_id] | |
| host.category_id = SiteSetting.uncategorized_category_id if host.category_id.blank? | |
| if host.save | |
| render_serialized(host, EmbeddableHostSerializer, root: 'embeddable_host', rest_serializer: true) | |
| else | |
| render_json_error(host) | |
| end | |
| end | |
| def save_host(host) | |
| permitted_params = params.require(:embeddable_host).permit(:host, :category_id) | |
| host.host = permitted_params[:host] | |
| host.category_id = permitted_params[:category_id] | |
| host.category_id = SiteSetting.uncategorized_category_id if host.category_id.blank? | |
| if host.save | |
| render_serialized(host, EmbeddableHostSerializer, root: 'embeddable_host', rest_serializer: true) | |
| else | |
| render_json_error(host) | |
| end | |
| end |
🤖 Prompt for AI Agents
In app/controllers/admin/embeddable_hosts_controller.rb around lines 22 to 32,
the save_host method directly accesses params without using strong parameters,
which is a security risk. Define a private method to whitelist the allowed
parameters from params[:embeddable_host], then use host.assign_attributes with
these strong parameters instead of directly assigning each attribute. This will
ensure only permitted attributes are assigned and improve security.
| def update | ||
| render_serialized(@embedding, EmbeddingSerializer, root: 'embedding', rest_serializer: true) | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update action doesn't perform any updates.
The update action renders the same data as show without performing any actual updates. This seems incorrect for a PUT endpoint.
Consider implementing actual update logic or removing this action if updates aren't needed:
def update
+ # Add update logic here if needed, e.g.:
+ # @embedding.some_property = params[:some_param]
+ # @embedding.save
render_serialized(@embedding, EmbeddingSerializer, root: 'embedding', rest_serializer: true)
endOr if no updates are needed, consider removing the update route and action entirely.
🤖 Prompt for AI Agents
In app/controllers/admin/embedding_controller.rb around lines 9 to 11, the
update action currently does not perform any update operation and just renders
the existing embedding data. To fix this, implement the update logic by applying
permitted parameters to update the @embedding object and handle success or
failure responses accordingly. If updating embeddings is not required, remove
the update action and its corresponding route to avoid confusion and incorrect
API behavior.
| def fetch_embedding | ||
| @embedding = OpenStruct.new({ | ||
| id: 'default', | ||
| embeddable_hosts: EmbeddableHost.all.order(:host) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Database query lacks error handling.
The EmbeddableHost.all.order(:host) query could potentially fail or return unexpected results.
Consider adding error handling and possibly scoping:
- embeddable_hosts: EmbeddableHost.all.order(:host)
+ embeddable_hosts: EmbeddableHost.all.order(:host) rescue []Or better yet, move this to a service method with proper error handling.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| embeddable_hosts: EmbeddableHost.all.order(:host) | |
| embeddable_hosts: EmbeddableHost.all.order(:host) rescue [] |
🤖 Prompt for AI Agents
In app/controllers/admin/embedding_controller.rb at line 18, the database query
EmbeddableHost.all.order(:host) lacks error handling which could cause unhandled
exceptions or unexpected results. Refactor this by moving the query into a
dedicated service method that wraps the call in error handling logic, such as
rescuing potential exceptions and returning a safe fallback or logging the
error. Then call this service method from the controller instead of directly
querying the model.
| records.each do |h| | ||
| execute "INSERT INTO embeddable_hosts (host, category_id, created_at, updated_at) VALUES ('#{h}', #{category_id}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)" | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Security vulnerability: SQL injection risk.
Using string interpolation directly in SQL statements creates a SQL injection vulnerability.
- records.each do |h|
- execute "INSERT INTO embeddable_hosts (host, category_id, created_at, updated_at) VALUES ('#{h}', #{category_id}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)"
- end
+ records.each do |h|
+ execute sanitize_sql(["INSERT INTO embeddable_hosts (host, category_id, created_at, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)", h, category_id])
+ endCommittable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In db/migrate/20150818190757_create_embeddable_hosts.rb around lines 24 to 26,
the code uses string interpolation to insert values directly into the SQL
statement, which poses a SQL injection risk. To fix this, replace the string
interpolation with parameterized queries or use ActiveRecord's built-in methods
to safely insert the records, ensuring that user input is properly escaped and
not directly concatenated into the SQL string.
| it "is a subclass of AdminController" do | ||
| expect(Admin::EmbeddableHostsController < Admin::AdminController).to eq(true) | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Insufficient test coverage for CRUD controller.
This controller handles CRUD operations (create, update, destroy) for EmbeddableHost records, but the test only verifies inheritance. You need comprehensive tests for:
- Creating embeddable hosts with valid/invalid data
- Updating existing hosts
- Deleting hosts and handling dependencies
- Authorization checks for admin-only access
- Proper JSON responses and error handling
describe Admin::EmbeddableHostsController do
+ let(:admin) { Fabricate(:admin) }
+ let(:embeddable_host) { Fabricate(:embeddable_host) }
+
+ before do
+ sign_in(admin)
+ end
it "is a subclass of AdminController" do
expect(Admin::EmbeddableHostsController < Admin::AdminController).to eq(true)
end
+
+ describe '#create' do
+ it 'creates a new embeddable host' do
+ post :create, params: { embeddable_host: { host: 'example.com' } }
+ expect(response).to be_success
+ expect(EmbeddableHost.count).to eq(1)
+ end
+
+ it 'handles validation errors' do
+ post :create, params: { embeddable_host: { host: '' } }
+ expect(response).to have_http_status(:unprocessable_entity)
+ end
+ end
+
+ describe '#update' do
+ it 'updates an existing embeddable host' do
+ put :update, params: { id: embeddable_host.id, embeddable_host: { host: 'updated.com' } }
+ expect(response).to be_success
+ expect(embeddable_host.reload.host).to eq('updated.com')
+ end
+ end
+
+ describe '#destroy' do
+ it 'deletes an embeddable host' do
+ delete :destroy, params: { id: embeddable_host.id }
+ expect(response).to be_success
+ expect(EmbeddableHost.exists?(embeddable_host.id)).to be_false
+ end
+ end
end🤖 Prompt for AI Agents
In spec/controllers/admin/embeddable_hosts_controller_spec.rb around lines 5 to
7, the test only checks that the controller inherits from AdminController but
lacks coverage for its CRUD actions. Add tests for creating embeddable hosts
with both valid and invalid data, updating existing hosts, deleting hosts
including dependency handling, verifying admin-only authorization, and ensuring
correct JSON responses and error handling for each action.
| it "is a subclass of AdminController" do | ||
| expect(Admin::EmbeddingController < Admin::AdminController).to eq(true) | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Insufficient test coverage for a new admin controller.
This test only verifies inheritance but doesn't test the actual controller functionality. For a new admin controller that handles embedding data with show and update actions, you should add tests for:
- Authentication and authorization requirements
- Successful responses for valid requests
- Error handling for invalid data
- Proper serialization of embedding data
describe Admin::EmbeddingController do
+ let(:admin) { Fabricate(:admin) }
+
+ before do
+ sign_in(admin)
+ end
it "is a subclass of AdminController" do
expect(Admin::EmbeddingController < Admin::AdminController).to eq(true)
end
+
+ describe '#show' do
+ it 'returns embedding data' do
+ get :show
+ expect(response).to be_success
+ expect(response.content_type).to eq('application/json')
+ end
+ end
+
+ describe '#update' do
+ it 'updates embedding settings successfully' do
+ put :update, params: { embedding: { some_setting: 'value' } }
+ expect(response).to be_success
+ end
+ end
end📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| it "is a subclass of AdminController" do | |
| expect(Admin::EmbeddingController < Admin::AdminController).to eq(true) | |
| end | |
| describe Admin::EmbeddingController do | |
| let(:admin) { Fabricate(:admin) } | |
| before do | |
| sign_in(admin) | |
| end | |
| it "is a subclass of AdminController" do | |
| expect(Admin::EmbeddingController < Admin::AdminController).to eq(true) | |
| end | |
| describe '#show' do | |
| it 'returns embedding data' do | |
| get :show | |
| expect(response).to be_success | |
| expect(response.content_type).to eq('application/json') | |
| end | |
| end | |
| describe '#update' do | |
| it 'updates embedding settings successfully' do | |
| put :update, params: { embedding: { some_setting: 'value' } } | |
| expect(response).to be_success | |
| end | |
| end | |
| end |
🤖 Prompt for AI Agents
In spec/controllers/admin/embedding_controller_spec.rb around lines 5 to 7, the
current test only checks inheritance and lacks coverage for the controller's
functionality. Add tests to verify authentication and authorization for
accessing the controller actions, ensure the show and update actions return
successful responses with valid requests, test error handling for invalid input
data, and confirm that the embedding data is properly serialized in the
responses.
| Fabricator(:embeddable_host) do | ||
| host "eviltrout.com" | ||
| category | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect file content - this appears to be misplaced.
This file is named category_fabricator.rb but only contains an embeddable_host fabricator. According to the AI summary, category fabricators should have been moved to spec/fabricators/embeddable_host_fabricator.rb, not completely removed from this file.
The content in this file should likely be:
- Category fabricators (
:category,:diff_category,:happy_category,:private_category) - NOT the embeddable_host fabricator which belongs in its own file
Please verify the file contents and ensure fabricators are in their correct locations.
🤖 Prompt for AI Agents
In spec/fabricators/category_fabricator.rb lines 1 to 4, the file incorrectly
contains the embeddable_host fabricator instead of category fabricators. Remove
the embeddable_host fabricator from this file and move it to
spec/fabricators/embeddable_host_fabricator.rb. Then, add the appropriate
category fabricators (:category, :diff_category, :happy_category,
:private_category) to this file to ensure fabricators are organized by their
correct domain.
| Fabricator(:category) do | ||
| name { sequence(:name) { |n| "Amazing Category #{n}" } } | ||
| user | ||
| end | ||
|
|
||
| Fabricator(:diff_category, from: :category) do | ||
| name "Different Category" | ||
| user | ||
| end | ||
|
|
||
| Fabricator(:happy_category, from: :category) do | ||
| name 'Happy Category' | ||
| slug 'happy' | ||
| user | ||
| end | ||
|
|
||
| Fabricator(:private_category, from: :category) do | ||
| transient :group | ||
|
|
||
| name 'Private Category' | ||
| slug 'private' | ||
| user | ||
| after_build do |cat, transients| | ||
| cat.update!(read_restricted: true) | ||
| cat.category_groups.build(group_id: transients[:group].id, permission_type: CategoryGroup.permission_types[:full]) | ||
| end | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
File naming inconsistency - contains category fabricators, not embeddable_host fabricators.
This file is named embeddable_host_fabricator.rb but contains category fabricators (:category, :diff_category, :happy_category, :private_category). Based on the filename, it should contain embeddable_host fabricators.
The file organization should be:
spec/fabricators/category_fabricator.rbshould contain these category fabricatorsspec/fabricators/embeddable_host_fabricator.rbshould contain the embeddable_host fabricator currently in the category_fabricator.rb file
Please correct the file organization to match their intended contents.
🤖 Prompt for AI Agents
In spec/fabricators/embeddable_host_fabricator.rb lines 1 to 27, the file
contains category fabricators instead of embeddable_host fabricators. Move all
category fabricator definitions (:category, :diff_category, :happy_category,
:private_category) to a new or existing file named
spec/fabricators/category_fabricator.rb. Then, move the embeddable_host
fabricator code currently in category_fabricator.rb into
embeddable_host_fabricator.rb to ensure each file contains fabricators matching
its name.
Test 10
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Documentation
Tests
Chores