-
Notifications
You must be signed in to change notification settings - Fork 29
WIP: Feature: Greenlight3 import #2665
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: develop
Are you sure you want to change the base?
Conversation
WalkthroughAdds a Greenlight v3 import Artisan command with tests and helpers, and broadens access_code handling to accept legacy alphanumeric codes across backend validation, middleware, model formatting, frontend inputs/display, and related tests. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (1 warning, 2 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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: 1
🧹 Nitpick comments (9)
app/Http/Middleware/RoomAuthenticate.php (1)
86-99: Access code comparison now supports non‑numeric values; consider avoiding type jugglingSwitching to
$room->access_code == $accessCodecorrectly allows alphanumeric codes from headers. Ifaccess_codeis stored as a string in the DB, consider normalizing both sides to string and using strict comparison to avoid subtle PHP type coercion (e.g."012345"vs12345):$accessCode = (string) $request->header('Access-Code'); if ((string) $room->access_code === $accessCode) { $authenticated = true; // ... }Not required for correctness given your current tests, but would make the comparison behavior more explicit and future‑proof.
app/Models/Room.php (1)
383-388: Moderator invitation code formatting now depends onlegacy_code; verify GL3 behaviorConditionally formatting the code via
$this->legacy_code ? implode('-', str_split(...)) : $this->access_codeis a good step toward separating legacy vs non‑legacy display.Two things to double‑check:
- That
legacy_codeistrueonly for the legacy formats you intend (e.g. old 6‑digit numeric codes) and not for new 6‑character alphanumeric GL3 codes, otherwise those GL3 codes will still be split intoxxx-xxxhere while the share button shows them unformatted.- That this difference from the frontend behavior (which still groups all numeric codes into chunks) is intentional; if not, consider centralizing the formatting logic so moderator messages and the share popover stay in sync.
resources/js/components/RoomShareButton.vue (1)
117-123: Formatted access code now preserves non‑numeric GL3 codesThe
formattedAccessCodecomputed correctly avoids chunking non‑numeric codes (e.g. GL3012abc) while preserving the existing grouped format for numeric codes. This matches the new backend behavior and room tests.If you ever want to tighten the numeric check, you could swap
isNaNfor a simple regex like/^\d+$/for more predictable behavior, but it's not required here.tests/Backend/Unit/Console/helper/Greenlight3User.php (1)
5-27: Test helper DTO looks fine; consider typed properties for clarityThe
Greenlight3Userhelper cleanly captures the GL3 user shape and aligns with the existingGreenlight3Roomhelper.Optionally, you could add property types for quicker static feedback in tests:
public string $id; public string $name; public string $email; public ?string $external_id; public ?string $password_digest;Not necessary for functionality, but can improve IDE support and reduce test helper misuse.
app/Console/Commands/ImportGreenlight3Command.php (2)
104-158: User import logic is reasonable; be explicit about password and role semanticsThe user import behaves as expected:
- De‑duplicates by email, mapping multiple GL3 references to the same existing user.
- Derives
authenticatorfromexternal_idand preservespassword_digestfor local users, while generating a random password for external (OIDC) users.- Sets locale and timezone from your app settings and attaches the selected default role only for non‑external users.
Two small considerations:
- Document somewhere (e.g., in command help) that existing local users are not modified (authenticator, external_id, roles), only linked, so operators know what to expect.
- If your
Rolepivot uses anautomaticflag, and you want these imported roles to behave like “automatic” assignments, you may preferattach([$defaultRole => ['automatic' => true]])instead of bareattach($defaultRole).
169-316: Room and shared access import mapping is clear; verify option mapping and ID collision policyThe room and shared access import functions are well structured:
- Rooms:
- Skip import when a room with the same
friendly_idalready exists, and intentionally don’t add it to$roomMapso shared accesses for that ID are skipped too.- Require a mapped owner in
$userMap, collecting failures for reporting instead of throwing.- Copy key options (
glAnyoneCanStart,glAnyoneJoinAsModerator,glRequireAuthentication,glViewerAccessCode,guestPolicy,muteOnStart,record) into the correspondingRoomfields, plus attach the chosenRoomType.- Shared accesses:
- Only add memberships when both user and room were successfully imported, assigning
RoomUserRole::MODERATORviasyncWithoutDetaching, which is appropriate for GL’s shared access.A few behavioral points worth confirming:
- For
guestPolicy, mapping onlyASK_MODERATORtoRoomLobby::ENABLEDand treating all other values asDISABLEDis a design choice; if GL3 has multiple guest policies you care about, you may want a more granular mapping later.- The current ID‑collision behavior (skipping rooms whose friendly ID already exists and not importing their shared accesses) is safe, but it might surprise admins who expect shared accesses to be merged for matching rooms. If that merge is ever desired, you’d need to change the
continuepath to still populate$roomMap.Overall, nothing blocking, just trade‑offs to be aware of.
resources/js/views/RoomsView.vue (1)
118-136: Access-code mask/placeholder correctly handle legacy alphanumeric codesConditionally switching to a 6‑character mask and neutral placeholder for
room.legacy_codecleanly enables GL3‑style alphanumeric access codes while preserving the existing 9‑digit format for non‑legacy rooms. The interaction withlogin()(dash stripping) also stays correct for both paths.If this mask/placeholder logic ends up needed in more places (e.g. other components), consider extracting a small computed helper for reuse.
tests/Frontend/e2e/RoomsViewSettings.cy.js (1)
1239-1304: GL3 access-code settings flow is well coveredThis new test exercises loading a non‑numeric
"fck4fd"access code, editing settings, and asserting that the PUT payload preserves that value along with the expected flags. That gives good coverage for the GL3 access‑code scenario.Optionally, you could add a post‑save assertion on
#room-setting-access_codeto confirm the UI still shows"fck4fd"after the update, mirroring the payload check.tests/Backend/Unit/Console/helper/Greenlight3Room.php (1)
5-27: DTO is fine; consider typed properties for clarityThe helper does its job as a simple value object for mocked rows. If you want slightly stronger guarantees and better static analysis, you could add typed properties, e.g.:
class Greenlight3Room { public string $id; public string $friendly_id; public string $user_id; public string $name; public bool $deleted; public function __construct(string $id, string $friendly_id, string $user_id, string $name, bool $deleted = false) { $this->id = $id; $this->friendly_id = $friendly_id; $this->user_id = $user_id; $this->name = $name; $this->deleted = $deleted; } }Purely a nicety; not blocking.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
app/Console/Commands/ImportGreenlight3Command.php(1 hunks)app/Http/Middleware/RoomAuthenticate.php(1 hunks)app/Http/Requests/UpdateRoomSettings.php(1 hunks)app/Models/Room.php(1 hunks)resources/js/components/RoomShareButton.vue(1 hunks)resources/js/views/RoomsView.vue(1 hunks)tests/Backend/Feature/api/v1/Room/RoomTest.php(1 hunks)tests/Backend/Unit/Console/ImportGreenlight3Test.php(1 hunks)tests/Backend/Unit/Console/helper/Greenlight3Room.php(1 hunks)tests/Backend/Unit/Console/helper/Greenlight3User.php(1 hunks)tests/Frontend/e2e/RoomsViewGeneral.cy.js(2 hunks)tests/Frontend/e2e/RoomsViewSettings.cy.js(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
tests/Backend/Unit/Console/ImportGreenlight3Test.php (5)
tests/Backend/Unit/Console/helper/Greenlight3Room.php (1)
Greenlight3Room(5-28)tests/Backend/Unit/Console/helper/Greenlight3User.php (1)
Greenlight3User(5-28)tests/Backend/Unit/Console/helper/GreenlightSharedAccess.php (1)
GreenlightSharedAccess(5-22)app/Models/RoomType.php (1)
rooms(37-40)app/Models/Room.php (2)
owner(214-217)members(263-266)
tests/Backend/Unit/Console/helper/Greenlight3User.php (1)
tests/Backend/Unit/Console/helper/Greenlight3Room.php (1)
__construct(20-27)
app/Console/Commands/ImportGreenlight3Command.php (5)
app/Models/Role.php (2)
Role(15-112)users(35-38)app/Settings/GeneralSettings.php (1)
GeneralSettings(7-29)app/Models/RoomType.php (1)
rooms(37-40)app/Enums/RoomUserRole.php (1)
label(16-25)app/Models/Room.php (2)
owner(214-217)members(263-266)
tests/Backend/Unit/Console/helper/Greenlight3Room.php (1)
tests/Backend/Unit/Console/helper/Greenlight3User.php (1)
__construct(20-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Docker Build
- GitHub Check: Backend
🔇 Additional comments (4)
tests/Backend/Feature/api/v1/Room/RoomTest.php (1)
561-566: Good coverage for legacy alphanumeric access codeThe extra assertion for a 6‑character alphanumeric legacy access code (
012abc) nicely exercises the new backend behavior for guests; no issues spotted.app/Console/Commands/ImportGreenlight3Command.php (1)
35-95: Overall command flow and transaction handling look solidThe command wiring is coherent:
- Dynamically registers a
greenlightpgsql connection and reads users, rooms, and shared accesses from it.- Prompts for room type, optional name prefix, and default role using
Laravel\Prompts.- Wraps all local DB writes (users, rooms, memberships) in a single transaction with a final
confirm()gate, so the operator can review counts before committing.This is a good balance between safety (rollback on failure or cancel) and usability for a one‑shot migration command.
app/Http/Requests/UpdateRoomSettings.php (1)
45-56: Verify GL3 code case handling during import and in validationThe access code rules correctly distinguish legacy 6-character vs new 9-digit codes. However, there's a potential issue with imported GL3 codes:
GL3 viewer access codes are imported without case normalization (stored as-is from the source database). When users later update room settings without changing the access code, if that code contains uppercase letters, the validation will fail because the alphanumeric legacy case enforces
lowercasevalidation rule (line 54).If GL3 instances ever store codes in non-lowercase format, either:
- Normalize to lowercase during import in
ImportGreenlight3Command, or- Apply case-insensitive comparison when detecting legacy codes to preserve and remove the
lowercaserule for GL3 importstests/Frontend/e2e/RoomsViewGeneral.cy.js (1)
386-408: Legacy access-code E2E updated consistently to alphanumericUsing
"012abc"both for the input and theaccess-codeheader expectation matches the new legacy alphanumeric behaviour and keeps this test meaningful for GL3 imports. No issues from my side here.
| $rooms = []; | ||
| $rooms[] = new Greenlight3Room(1, 'abc-def-xyz-123', $users[0]->id, 'Test Room 1'); | ||
| $rooms[] = new Greenlight3Room(2, 'abc-def-xyz-234', $users[1]->id, 'Test Room 2'); | ||
| $rooms[] = new Greenlight3Room(3, 'abc-def-xyz-345', $users[2]->id, 'Test Room 3'); | ||
| $rooms[] = new Greenlight3Room(4, 'abc-def-xyz-456', $users[3]->id, 'Test Room 4'); | ||
|
|
||
| $rooms[] = new Greenlight3Room(5, 'hij-klm-xyz-123', $users[0]->id, 'Test Room 5'); | ||
| $rooms[] = new Greenlight3Room(6, 'hij-klm-xyz-234', $users[0]->id, 'Test Room 6'); | ||
| $rooms[] = new Greenlight3Room(7, 'hij-klm-xyz-456', 99, 'Test Room 9', '012345abcd'); | ||
| $rooms[] = new Greenlight3Room(8, $existingRoom->id, $users[0]->id, 'Test Room 10'); | ||
|
|
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.
Fix mismatched constructor argument for Greenlight3Room
Here:
$rooms[] = new Greenlight3Room(7, 'hij-klm-xyz-456', 99, 'Test Room 9', '012345abcd');the 5th parameter is "012345abcd", but Greenlight3Room::__construct declares that argument as bool $deleted = false. PHP will coerce this non‑empty string to true, so $deleted becomes true for room 7, and the string itself is never used (the viewer access code for room 5 already comes from the room_meeting_options mock).
This is confusing and could break if strict types are ever enabled or if deleted starts to matter in the test.
Consider either:
- Dropping the 5th argument entirely, if this room isn’t meant to be “deleted”:
- $rooms[] = new Greenlight3Room(7, 'hij-klm-xyz-456', 99, 'Test Room 9', '012345abcd');
+ $rooms[] = new Greenlight3Room(7, 'hij-klm-xyz-456', 99, 'Test Room 9');or, if you intended to mark it as deleted for future assertions, pass a boolean:
- $rooms[] = new Greenlight3Room(7, 'hij-klm-xyz-456', 99, 'Test Room 9', '012345abcd');
+ $rooms[] = new Greenlight3Room(7, 'hij-klm-xyz-456', 99, 'Test Room 9', true);Either way, removing the stray string argument will better reflect the actual data model.
🤖 Prompt for AI Agents
tests/Backend/Unit/Console/ImportGreenlight3Test.php around lines 195-205: the
Greenlight3Room constructor call for room id 7 passes a string "012345abcd" as
the 5th argument which maps to the bool $deleted parameter and will coerce to
true; update that call to either remove the 5th argument entirely (if the room
is not meant to be deleted) or replace it with an explicit boolean (true if you
intend it to be deleted, false otherwise) so the parameter type matches the
constructor and the viewer access code remains sourced from the
room_meeting_options mock.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #2665 +/- ##
=============================================
- Coverage 96.75% 96.74% -0.02%
- Complexity 1822 1860 +38
=============================================
Files 439 441 +2
Lines 12472 12642 +170
Branches 2073 2075 +2
=============================================
+ Hits 12067 12230 +163
- Misses 405 412 +7 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
2e670e0 to
c38131b
Compare
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: 0
🧹 Nitpick comments (1)
resources/js/components/RoomShareButton.vue (1)
117-123: Consider using an explicit check for all-numeric strings.The
isNaN()function performs type coercion before checking, which can lead to unexpected behavior with edge cases (e.g., empty strings, null). While the current guards prevent these cases, using an explicit regex test would be more robust and clearer.Apply this diff to use a regex test:
const formattedAccessCode = computed(() => { - return isNaN(props.room.access_code) - ? props.room.access_code - : String(props.room.access_code) + const code = String(props.room.access_code); + return /^\d+$/.test(code) + ? code .match(/.{1,3}/g) - .join("-"); + .join("-") + : code; });This explicitly checks whether the code contains only digits, making the intent clearer and avoiding type coercion quirks.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
app/Http/Middleware/RoomAuthenticate.php(1 hunks)app/Http/Requests/UpdateRoomSettings.php(1 hunks)app/Models/Room.php(1 hunks)resources/js/components/RoomShareButton.vue(1 hunks)resources/js/views/RoomsView.vue(1 hunks)tests/Backend/Feature/api/v1/Room/RoomTest.php(1 hunks)tests/Frontend/e2e/RoomsViewGeneral.cy.js(2 hunks)tests/Frontend/e2e/RoomsViewSettings.cy.js(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
- tests/Backend/Feature/api/v1/Room/RoomTest.php
- app/Http/Middleware/RoomAuthenticate.php
- app/Models/Room.php
- resources/js/views/RoomsView.vue
- tests/Frontend/e2e/RoomsViewSettings.cy.js
- tests/Frontend/e2e/RoomsViewGeneral.cy.js
- app/Http/Requests/UpdateRoomSettings.php
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Backend
- GitHub Check: Docker Build
| return String(props.room.access_code) | ||
| .match(/.{1,3}/g) | ||
| .join("-"); | ||
| return isNaN(props.room.access_code) |
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.
That would format 6 digit numeric codes with a dash.
However when entering the 6 digit code there is no dash input mask
| $alphanumeric = $current == $incoming && ! is_numeric($current); | ||
| $digits = ($current == $incoming && strlen($current) == 6) ? 6 : 9; | ||
|
|
||
| $rules = ['numeric', 'digits:'.$digits, 'bail']; | ||
| $rules = $alphanumeric | ||
| ? ['alpha_num:ascii', 'lowercase', 'size:6', 'bail'] | ||
| : ['numeric', 'digits:'.$digits, 'bail']; |
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.
| $alphanumeric = $current == $incoming && ! is_numeric($current); | |
| $digits = ($current == $incoming && strlen($current) == 6) ? 6 : 9; | |
| $rules = ['numeric', 'digits:'.$digits, 'bail']; | |
| $rules = $alphanumeric | |
| ? ['alpha_num:ascii', 'lowercase', 'size:6', 'bail'] | |
| : ['numeric', 'digits:'.$digits, 'bail']; | |
| $legacy = $current == $incoming && strlen($current) == 6); | |
| $rules = $legacy | |
| ? ['alpha_num:ascii', 'lowercase', 'size:6', 'bail'] | |
| : ['numeric', 'digits:9', 'bail']; |
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: 1
🤖 Fix all issues with AI agents
In `@app/Console/Commands/ImportGreenlight3Command.php`:
- Around line 26-31: The command currently exposes the database password via the
$signature in ImportGreenlight3Command; remove the {password ...} argument from
the $signature and instead obtain the password inside handle() using Laravel's
interactive password prompt (password(...)) or fallback to an environment
variable (e.g., config or env('GREENLIGHT_DB_PASSWORD')) if present; ensure you
update any references to the removed argument in handle() to use the
prompted/fallback value and adjust the command description/help text
accordingly.
🧹 Nitpick comments (8)
app/Console/Commands/ImportGreenlight3Command.php (8)
12-17: Consider using fully qualified facade imports.Using facade aliases (
Config,DB,Hash,Str) works but importing full namespaces (e.g.,Illuminate\Support\Facades\DB) improves IDE support and code clarity. This is a minor style preference.
51-53: Potential memory issue when importing large datasets.All users, rooms, and shared accesses are loaded into memory at once. For large Greenlight installations, consider using chunked queries or lazy collections to reduce memory footprint.
♻️ Example using lazy() for memory efficiency
- $users = DB::connection('greenlight')->table('users')->where('provider', 'greenlight')->get(['id', 'name', 'email', 'external_id', 'password_digest']); - $rooms = DB::connection('greenlight')->table('rooms')->get(['id', 'friendly_id', 'user_id', 'name']); - $sharedAccesses = DB::connection('greenlight')->table('shared_accesses')->get(['room_id', 'user_id']); + $users = DB::connection('greenlight')->table('users')->where('provider', 'greenlight')->lazy(); + $rooms = DB::connection('greenlight')->table('rooms')->lazy(); + $sharedAccesses = DB::connection('greenlight')->table('shared_accesses')->lazy();Note: If using
lazy(), you'll need to callcount()separately for progress bars, or switch to indeterminate progress indicators.
37-49: Missing connection validation before querying.If the database connection fails, the error will only surface when querying. Consider testing the connection first to provide a clearer error message.
♻️ Add connection validation
Config::set('database.connections.greenlight', [ 'driver' => 'pgsql', // ... config ... ]); + try { + DB::connection('greenlight')->getPdo(); + } catch (\Exception $e) { + $this->error('Failed to connect to Greenlight database: ' . $e->getMessage()); + return 1; + } + $users = DB::connection('greenlight')->table('users')...
212-239: N+1 query pattern: room options fetched inside loop.Each room iteration issues a separate query to fetch
room_meeting_options. For many rooms, this causes significant database overhead.♻️ Prefetch all room options before the loop
protected function importRooms(Collection $rooms, int $roomType, array $userMap, ?string $prefix): array { $this->line('Importing rooms'); + + // Prefetch all room options + $allRoomOptions = DB::connection('greenlight') + ->table('room_meeting_options') + ->join('meeting_options', 'meeting_option_id', '=', 'meeting_options.id') + ->whereIn('room_id', $rooms->pluck('id')) + ->get(['room_id', 'name', 'value']) + ->groupBy('room_id'); // ... in the foreach loop: - $roomOptions = DB::connection('greenlight')->table('room_meeting_options')->join('meeting_options', 'meeting_option_id', '=', 'meeting_options.id')->where('room_id', $room->id)->get(['name', 'value']); + $roomOptions = $allRoomOptions->get($room->id, collect());
306-308: Shared access may add room owner as a member.If a room owner also has a shared_access entry in Greenlight, they'll be added as a MODERATOR member of their own room. While not breaking, this creates redundant data since owners already have full access.
♻️ Skip if user is the room owner
// find room object and add user as moderator to the room $dbRoom = Room::find($roomMap[$room]); + + // Skip if user is the room owner + if ($dbRoom->user_id === $userMap[$user]) { + $bar->advance(); + continue; + } + $dbRoom->members()->syncWithoutDetaching([$userMap[$user] => ['role' => RoomUserRole::MODERATOR]]);
280-280: Missing return type declaration.The method has no return type. Since it doesn't return anything, add
: voidfor clarity.- protected function importSharedAccesses(Collection $sharedAccesses, array $roomMap, array $userMap) + protected function importSharedAccesses(Collection $sharedAccesses, array $roomMap, array $userMap): void
35-95: Missing return value fromhandle()method.Laravel Artisan commands should return an integer exit code (0 for success, non-zero for failure). This helps when running commands in scripts or CI pipelines.
♻️ Add return codes
public function handle() + public function handle(): int { // ... existing code ... if (confirm('Do you wish to commit the import?')) { DB::commit(); $this->info('Import completed'); + return 0; } else { DB::rollBack(); $this->warn('Import canceled; nothing was imported'); + return 1; } } catch (\Exception $e) { DB::rollBack(); $this->error('Import failed: '.$e->getMessage()); + return 1; } }
109-110: Inconsistent progress bar implementation.
importUsers()uses Laravel Prompts'progress()whileimportRooms()andimportSharedAccesses()use$this->output->createProgressBar(). Consider using the same approach throughout for consistency.Also applies to: 173-174
| protected $signature = 'import:greenlight-v3 | ||
| {host : ip or hostname of postgres database server} | ||
| {port : port of postgres database server} | ||
| {database : greenlight database name, see greenlight .env variable DB_NAME} | ||
| {username : greenlight database username, see greenlight .env variable DB_USERNAME} | ||
| {password : greenlight database password, see greenlight .env variable DB_PASSWORD}'; |
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: Password argument is visible in process listings.
Passing the password as a command-line argument exposes it in ps output and shell history. Consider using Laravel Prompts' password() function to interactively prompt for the password, or use environment variables.
🔒 Suggested approach using interactive password prompt
+use function Laravel\Prompts\password;
+
class ImportGreenlight3Command extends Command
{
protected $signature = 'import:greenlight-v3
{host : ip or hostname of postgres database server}
{port : port of postgres database server}
{database : greenlight database name, see greenlight .env variable DB_NAME}
- {username : greenlight database username, see greenlight .env variable DB_USERNAME}
- {password : greenlight database password, see greenlight .env variable DB_PASSWORD}';
+ {username : greenlight database username, see greenlight .env variable DB_USERNAME}';Then in handle():
$dbPassword = password(
label: 'Enter the Greenlight database password',
required: true
);🤖 Prompt for AI Agents
In `@app/Console/Commands/ImportGreenlight3Command.php` around lines 26 - 31, The
command currently exposes the database password via the $signature in
ImportGreenlight3Command; remove the {password ...} argument from the $signature
and instead obtain the password inside handle() using Laravel's interactive
password prompt (password(...)) or fallback to an environment variable (e.g.,
config or env('GREENLIGHT_DB_PASSWORD')) if present; ensure you update any
references to the removed argument in handle() to use the prompted/fallback
value and adjust the command description/help text accordingly.
29d66c5 to
43039e4
Compare
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: 1
🤖 Fix all issues with AI agents
In `@app/Console/Commands/ImportGreenlight3Command.php`:
- Around line 61-83: The code in ImportGreenlight3Command misuses options() and
has a typo: replace calls like this->options('room-type') and
this->options('prefix')/('default-role') with this->option('room-type'),
this->option('room-prefix') and this->option('default-role'); fix the typo
firsthere to firstWhere (or use ->value('id') to get an int id) when resolving
RoomType/Role, and ensure you pass integer IDs (e.g., $roomTypeId and $roleId)
into importRooms() and importUsers() instead of Eloquent models; add null guards
after resolving names so you throw/exit or prompt if RoomType or Role lookup
returns null to avoid TypeErrors.
♻️ Duplicate comments (1)
app/Console/Commands/ImportGreenlight3Command.php (1)
26-48: Avoid passing DB passwords via CLI arguments.Command-line args are visible via
ps/shell history. Use an interactive prompt or env/config fallback instead.🔒 Suggested direction (prompt or env fallback)
+use function Laravel\Prompts\password; + class ImportGreenlight3Command extends Command { protected $signature = 'import:greenlight-v3 {host : ip or hostname of postgres database server} {port : port of postgres database server} {database : greenlight database name, see greenlight .env variable DB_NAME} - {username : greenlight database username, see greenlight .env variable DB_USERNAME} - {password : greenlight database password, see greenlight .env variable DB_PASSWORD} + {username : greenlight database username, see greenlight .env variable DB_USERNAME} {--no-confirm : do not ask if the import should be committed} {--default-role= : default role for imported external users} {--room-prefix= : room name prefix for imported rooms} {--room-type= : room type for imported rooms} '; ... - Config::set('database.connections.greenlight', [ + $dbPassword = env('GREENLIGHT_DB_PASSWORD') ?? password( + label: 'Enter the Greenlight database password', + required: true + ); + + Config::set('database.connections.greenlight', [ 'driver' => 'pgsql', 'host' => $this->argument('host'), 'database' => $this->argument('database'), 'username' => $this->argument('username'), - 'password' => $this->argument('password'), + 'password' => $dbPassword, 'port' => $this->argument('port'),
| $roomType = $this->options('room-type') != null | ||
| ? RoomType::firsthere('name', $this->options('room-type')) | ||
| : select( | ||
| label: 'What room type should the rooms be assigned to?', | ||
| options: RoomType::pluck('name', 'id'), | ||
| scroll: 10 | ||
| ); | ||
|
|
||
| // ask user to add prefix to room names | ||
| $prefix = $this->options('prefix') != null ? $this->options('prefix') : text( | ||
| label: 'Prefix for room names', | ||
| placeholder: 'E.g. (Imported)', | ||
| hint: '(Optional).' | ||
| ); | ||
|
|
||
| // ask user what room type the imported rooms should get | ||
| $defaultRole = $this->options('default-role') != null | ||
| ? Role::firstWhere('name', $this->options('default-role')) | ||
| : select( | ||
| 'Please select the default role for new imported non-ldap users', | ||
| options: Role::pluck('name', 'id'), | ||
| scroll: 10 | ||
| ); |
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.
🧩 Analysis chain
🏁 Script executed:
cd /tmp && find . -name "ImportGreenlight3Command.php" -o -name "*.php" | grep -i import | head -20Repository: THM-Health/PILOS
Length of output: 42
🏁 Script executed:
git ls-files | grep -i importRepository: THM-Health/PILOS
Length of output: 766
🏁 Script executed:
git ls-files app/Console/Commands/ | head -20Repository: THM-Health/PILOS
Length of output: 1020
🏁 Script executed:
git ls-files | grep ImportGreenlight3CommandRepository: THM-Health/PILOS
Length of output: 109
🏁 Script executed:
find . -type f -name "ImportGreenlight3Command.php" 2>/dev/nullRepository: THM-Health/PILOS
Length of output: 111
🏁 Script executed:
cat -n app/Console/Commands/ImportGreenlight3Command.php | head -150Repository: THM-Health/PILOS
Length of output: 7471
🏁 Script executed:
wc -l app/Console/Commands/ImportGreenlight3Command.phpRepository: THM-Health/PILOS
Length of output: 113
🏁 Script executed:
cat -n app/Console/Commands/ImportGreenlight3Command.php | sed -n '150,220p'Repository: THM-Health/PILOS
Length of output: 3461
🏁 Script executed:
cat -n app/Console/Commands/ImportGreenlight3Command.php | sed -n '160,220p'Repository: THM-Health/PILOS
Length of output: 3144
🏁 Script executed:
grep -n "importRooms\|importUsers" app/Console/Commands/ImportGreenlight3Command.phpRepository: THM-Health/PILOS
Length of output: 407
🏁 Script executed:
cat -n app/Console/Commands/ImportGreenlight3Command.php | sed -n '160,250p'Repository: THM-Health/PILOS
Length of output: 4866
🏁 Script executed:
cat -n app/Console/Commands/ImportGreenlight3Command.php | sed -n '220,260p'Repository: THM-Health/PILOS
Length of output: 2518
🏁 Script executed:
cat -n app/Console/Commands/ImportGreenlight3Command.php | sed -n '220,280p'Repository: THM-Health/PILOS
Length of output: 3235
🏁 Script executed:
cat -n app/Console/Commands/ImportGreenlight3Command.php | sed -n '26,36p'Repository: THM-Health/PILOS
Length of output: 1030
🌐 Web query:
Laravel Command class option() vs options() method signature
💡 Result:
-
option(string|null $key = null): returns a single option's value (string|array|bool|null) when you pass a key; if called with null it returns the same types for the "current" key. Defined on the Command (InteractsWithIO) trait. [1]
-
options(): returns an array of all options passed to the command. [1][2]
Use option('name') when you need one option; use options() when you need the full options array. References: Laravel Command API (method signatures) and Artisan docs. [1] https://api.laravel.com/docs/11.x/Illuminate/Console/Command.html [2] https://laravel.com/docs/10.x/artisan
🏁 Script executed:
rg "firsthere" app/Repository: THM-Health/PILOS
Length of output: 180
🏁 Script executed:
rg "firstWhere" app/ | head -20Repository: THM-Health/PILOS
Length of output: 592
🏁 Script executed:
cat -n app/Console/Commands/ImportGreenlight3Command.php | sed -n '148,149p'Repository: THM-Health/PILOS
Length of output: 151
🏁 Script executed:
cat -n app/Console/Commands/ImportGreenlight3Command.php | sed -n '113,113p' && cat -n app/Console/Commands/ImportGreenlight3Command.php | sed -n '178,178p'Repository: THM-Health/PILOS
Length of output: 261
Fix method calls and option names to prevent runtime errors.
options()doesn't accept arguments and returns an array; useoption('key')to retrieve individual optionsfirsthereis a typo and doesn't exist; usefirstWhereor->value('id')to return an ID- Option name is
room-prefixin the signature (line 34), notprefix - The methods
importUsers()andimportRooms()expectintparameters (lines 113, 178), but the current code passes Eloquent model objects, which will cause TypeErrors
Proposed fixes
- $roomType = $this->options('room-type') != null
- ? RoomType::firsthere('name', $this->options('room-type'))
+ $roomType = $this->option('room-type') !== null
+ ? RoomType::where('name', $this->option('room-type'))->value('id')
: select(
label: 'What room type should the rooms be assigned to?',
options: RoomType::pluck('name', 'id'),
scroll: 10
);
// ask user to add prefix to room names
- $prefix = $this->options('prefix') != null ? $this->options('prefix') : text(
+ $prefix = $this->option('room-prefix') ?? text(
label: 'Prefix for room names',
placeholder: 'E.g. (Imported)',
hint: '(Optional).'
);
// ask user what room type the imported rooms should get
- $defaultRole = $this->options('default-role') != null
- ? Role::firstWhere('name', $this->options('default-role'))
+ $defaultRole = $this->option('default-role') !== null
+ ? Role::where('name', $this->option('default-role'))->value('id')
: select(
'Please select the default role for new imported non-ldap users',
options: Role::pluck('name', 'id'),
scroll: 10
);
...
- if ($this->options('no-confirm') || confirm('Do you wish to commit the import?')) {
+ if ($this->option('no-confirm') || confirm('Do you wish to commit the import?')) {Also add null guards when a provided role/room-type name doesn't resolve to prevent passing null to methods expecting int.
🤖 Prompt for AI Agents
In `@app/Console/Commands/ImportGreenlight3Command.php` around lines 61 - 83, The
code in ImportGreenlight3Command misuses options() and has a typo: replace calls
like this->options('room-type') and this->options('prefix')/('default-role')
with this->option('room-type'), this->option('room-prefix') and
this->option('default-role'); fix the typo firsthere to firstWhere (or use
->value('id') to get an int id) when resolving RoomType/Role, and ensure you
pass integer IDs (e.g., $roomTypeId and $roleId) into importRooms() and
importUsers() instead of Eloquent models; add null guards after resolving names
so you throw/exit or prompt if RoomType or Role lookup returns null to avoid
TypeErrors.
01e4fae to
bdaf483
Compare
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: 1
🤖 Fix all issues with AI agents
In `@app/Console/Commands/ImportGreenlight3Command.php`:
- Around line 26-35: Add a new CLI option to the command signature and use it
when no-confirm is set: update the protected $signature to include
"{--room-prefix= : prefix to prepend to imported room names}" and then modify
the prefix resolution in handle() (where the command currently prompts for a
prefix) to first check $this->option('room-prefix') and use that if provided; if
not provided and $this->option('no-confirm') is true, keep the existing behavior
(skip the interactive prompt and use an empty/default prefix), otherwise
continue prompting the user for the prefix as before.
♻️ Duplicate comments (3)
app/Console/Commands/ImportGreenlight3Command.php (2)
26-31: Avoid passing DB passwords as CLI args.
Command-line args are visible in process listings and shell history; prefer an interactive prompt or env/config fallback.
59-82: Guard against invalid--room-type/--default-rolevalues.
firstWhere(...)->idwill throw when the name isn’t found; add a null guard and exit or re-prompt.🐛 Suggested guard
- $roomType = $this->option('room-type') != null - ? RoomType::firstWhere('name', $this->option('room-type'))->id + $roomType = $this->option('room-type') !== null + ? RoomType::where('name', $this->option('room-type'))->value('id') : select( label: 'What room type should the rooms be assigned to?', options: RoomType::pluck('name', 'id'), scroll: 10 ); + if ($roomType === null) { + $this->error('Unknown room type: '.$this->option('room-type')); + return self::FAILURE; + } ... - $defaultRole = $this->option('default-role') != null - ? Role::firstWhere('name', $this->option('default-role'))->id + $defaultRole = $this->option('default-role') !== null + ? Role::where('name', $this->option('default-role'))->value('id') : select( 'Please select the default role for new imported non-ldap users', options: Role::pluck('name', 'id'), scroll: 10 ); + if ($defaultRole === null) { + $this->error('Unknown role: '.$this->option('default-role')); + return self::FAILURE; + }tests/Backend/Unit/Console/ImportGreenlight3Test.php (1)
159-296: FixGreenlight3Roomctor arg type for room id 7.
The 5th parameter isbool $deleted; passing"012345abcd"coerces totrueand the string is ignored.🧪 Suggested fix
- $rooms[] = new Greenlight3Room(7, 'hij-klm-xyz-456', 99, 'Test Room 9', '012345abcd'); + $rooms[] = new Greenlight3Room(7, 'hij-klm-xyz-456', 99, 'Test Room 9');
| protected $signature = 'import:greenlight-v3 | ||
| {host : ip or hostname of postgres database server} | ||
| {port : port of postgres database server} | ||
| {database : greenlight database name, see greenlight .env variable DB_NAME} | ||
| {username : greenlight database username, see greenlight .env variable DB_USERNAME} | ||
| {password : greenlight database password, see greenlight .env variable DB_PASSWORD} | ||
| {--no-confirm : do not ask if the import should be committed} | ||
| {--default-role= : default role for imported external users} | ||
| {--room-type= : room type for imported rooms} | ||
| '; |
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.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "ImportGreenlight3Command.php" | head -5Repository: THM-Health/PILOS
Length of output: 111
🏁 Script executed:
wc -l ./app/Console/Commands/ImportGreenlight3Command.phpRepository: THM-Health/PILOS
Length of output: 115
🏁 Script executed:
head -n 100 ./app/Console/Commands/ImportGreenlight3Command.php | cat -nRepository: THM-Health/PILOS
Length of output: 4910
🏁 Script executed:
sed -n '39,100p' ./app/Console/Commands/ImportGreenlight3Command.php | cat -nRepository: THM-Health/PILOS
Length of output: 3188
🏁 Script executed:
grep -n "function importRooms" ./app/Console/Commands/ImportGreenlight3Command.phpRepository: THM-Health/PILOS
Length of output: 172
🏁 Script executed:
sed -n '177,230p' ./app/Console/Commands/ImportGreenlight3Command.php | cat -nRepository: THM-Health/PILOS
Length of output: 3010
Add --room-prefix= CLI option to allow prefix specification with --no-confirm.
Currently, when --no-confirm is used, the prefix prompt is skipped with no alternative way to provide a prefix via CLI, making it impossible to set one in non-interactive mode.
The signature should include the option:
{--room-type= : room type for imported rooms}
+{--room-prefix= : prefix to add to imported room names}However, adjust the prefix logic to preserve the --no-confirm behavior:
-$prefix = $this->option('no-confirm') ? null : text(
+$prefix = $this->option('room-prefix') ?? ($this->option('no-confirm') ? null : text(
label: 'Prefix for room names',
placeholder: 'E.g. (Imported)',
hint: '(Optional).'
-);
+));🤖 Prompt for AI Agents
In `@app/Console/Commands/ImportGreenlight3Command.php` around lines 26 - 35, Add
a new CLI option to the command signature and use it when no-confirm is set:
update the protected $signature to include "{--room-prefix= : prefix to prepend
to imported room names}" and then modify the prefix resolution in handle()
(where the command currently prompts for a prefix) to first check
$this->option('room-prefix') and use that if provided; if not provided and
$this->option('no-confirm') is true, keep the existing behavior (skip the
interactive prompt and use an empty/default prefix), otherwise continue
prompting the user for the prefix as before.
bdaf483 to
7aedc53
Compare
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: 1
🤖 Fix all issues with AI agents
In `@tests/Frontend/e2e/RoomsViewSettings.cy.js`:
- Around line 1239-1299: Replace the profanity-like access code "fck4fd" used in
the test with a neutral sample (e.g. "abcd1234") wherever it is set or asserted:
update the assignment to roomSettings.data.access_code in the GET fixture
intercept and the PUT fixture intercept, update the expected interception
request body access_code value, and keep the existing checks against
"#room-setting-access_code" and the intercept aliases
("roomSettingsRequest"/"roomSettingsSaveRequest") intact so the test logic
remains unchanged.
Fixes #2664
Type
Checklist
Changes
import:greenlight-v3Other information
ToDo:
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Tests
✏️ Tip: You can customize this high-level summary in your review settings.