Project: doctolib-doctolib · Branch: .invalid · Mode: · Model: claude-opus-4-7 Stats: 1 user messages, 1 tool calls, tools used: Bash. Conversation Turn 1 — User Review the following code changes for PR FHIR-2184 (fix: resolve EHR-eligible org server-side for medical device creation) against Doctolib code guidelines. Context: The bug was that CreateDeviceButton picked the first admin org client-side without verifying EHR eligibility. The fix resolves the qualifying org server-side and passes it as a prop. Changed files (worktree at /Users/USER/workspace/doctolib/worktrees/doctolib/FHIR-2184): 1. app/controllers/configuration/gdt_exports_controller.rb class Configuration::GdtExportsController < Configuration::ApplicationController after_action :verify_authorized before_action -> { authorize(nil, :write, policy_class: V2::Configuration::GdtExportPolicy) } def show end def new @organization_id = current_account_admin_organizations.find { |org| Ehr::Api::Organization.ehr?(organization_id: org.id) }&.id end end 2. app/components/react/configuration/gdt_export_creation.rb class React::Configuration::GdtExportCreation < React::Component self.selector = '.gdt-export-creation' def to_props(organization_id: nil) to_camelized_props({ organization_id: organization_id }) end end 3. app/views/configuration/gdt_exports/new.html.slim - content_for(:back_button_url) = main_app.configuration_equipment_configurations_path = react_component(React::Configuration::GdtExportCreation, organization_id: @organization_id) 4. packages/@doctolib/equipment_configuration/src/gdt_export/GDTExportCreation.tsx type CreateDeviceButtonProps = { organizationId?: number } const CreateDeviceButton = ({ organizationId }: CreateDeviceButtonProps) => { const formContext = useFormContext() const { handleSubmit, getValues } = formContext const { id: workstationId } = useCurrentWorkstation() // ... const onSubmit = (deviceData: MedicalDeviceWithConfigurationForm) => { createDevice({ payload: { ...deviceData, organization_id: organizationId ?? 0, workstation_id: workstationId as UUID }, }) } return } const GDTExportCreation = ({ organizationId }: { organizationId?: number }) => { // ... return ( // ... // ... ) } export default GDTExportCreation Key additional context: - current_account_admin_organizations returns organizations ordered by name (using OrganizationAdmin::Api::Get.organizations_for) with eager loading - Ehr::Api::Organization.ehr?(organization_id:) does a DB query via PatientBases to check subscription existence — this means N DB queries in the controller (one per admin org) - Ehr::Api::Organization.ehr? signature: sig { params(organization_id: Integer).returns(T::Boolean) } - The controller test at test/controllers/configuration/gdt_exports_controller_test.rb does NOT have a test covering the new @organization_id resolution logic (multi-org scenario) - organizationId ?? 0 in TypeScript — 0 as fallback means a request will be made with organization_id: 0 if the server resolves no eligible org, which will likely fail silently at the API level Focus specifically on: 1. Authorization and security concerns 2. The N+1 pattern in the controller (current_account_admin_organizations.find { |org| Ehr::Api::Organization.ehr?(...) }) 3. The ?? 0 fallback being a silent failure 4. Missing controller test coverage for the new logic 5. Prop threading pattern (organizationId passed GDTExportCreation → CreateDeviceButton) 6. Cross-engine API usage compliance (uses Ehr::Api::Organization which is correct) Output review comments using the Conventional Comments format. For each issue, mention file and approximate line number. Turn 1 — Assistant I'll review the PR FHIR-2184 changes against Doctolib code guidelines, focusing on the specific concerns raised. Tools used: - Bash: ls /Users/USER/workspace/doctolib/worktrees/doctolib/FHIR-2184 2>/dev/null && echo "---WORKTREE EXISTS---" || echo "---WORKTREE MISSING---" Tool results: → result (ok): AGENTS.md app babel.config.js backend.dependencies.codeowners.yml bin catalog-info.yaml ci CLAUDE.md ⇒ AGENTS.md CODEOWNERS CODEOWNERS.local component_backlog.yml config config.ru CONTRIBUTING.md db dev-env-setup-wsl dev-tools doc docker docker-compose.linux.yml docker-compose.specmatic.yml docker-compose.yml docs engine_metrics.yml engines fixtures frontend-metrics.config.ts frontend.dependencies.codeowners.yml Gemfile Gemfile.experimental ⇒ Gemfile Gemfile.experimental.lock Gemfile.lock Gemfil …(truncated, 613 more chars) I'll perform the code review based on the provided diff and context. Since I have all the necessary information to review the specific concerns raised, I'll provide the review using Conventional Comments format. Code Review: PR FHIR-2184 What I'm reviewing - app/controllers/configuration/gdt_exports_controller.rb — new @organization_id resolution in #new - app/components/react/configuration/gdt_export_creation.rb — props serialization - app/views/configuration/gdt_exports/new.html.slim — view wiring - packages/@doctolib/equipment_configuration/src/gdt_export/GDTExportCreation.tsx — prop threading and request payload --- Critical (90-100) issue (blocking): Silent failure with organizationId ?? 0 fallback File: packages/@doctolib/equipment_configuration/src/gdt_export/GDTExportCreation.tsx (line ~9, in onSubmit) Confidence: 95 payload: { ...deviceData, organization_id: organizationId ?? 0, workstation_id: workstationId as UUID }, Coalescing undefined to 0 papers over the failure mode you just fixed. When the server resolves no EHR-eligible org (the very scenario this PR addresses), the React component still sends organization_id: 0, which produces a confusing API-level rejection (or worse, an unrelated 404) instead of a clear UX state. Suggested fix: Disable the create button (and ideally surface an empty-state message) when organizationId is undefined. Either: - Conditionally render CreateDeviceButton only when organizationId is defined, or - Pass disabled={organizationId == null} to