Skip to content

Conversation

@pizkaz
Copy link
Contributor

@pizkaz pizkaz commented Dec 11, 2025

Fixes #2664

Type

  • Bugfix
  • Feature
  • Documentation
  • Refactoring (e.g. Style updates, Test implementation, etc.)
  • Other (please describe):

Checklist

  • Code updated to current develop branch head
  • Passes CI checks
  • Is a part of an issue
  • Tests added for the bugfix or newly implemented feature, describe below why if not
  • Changelog is updated
  • Documentation of code and features exists

Changes

  • Added a new command import:greenlight-v3
  • Legacy room access codes can now be alphanumeric (because GL3 uses 6-chars-alphanumeric codes)
  • Added / adapted tests for new features and changed behavior (room access codes)

Other information

ToDo:

  • GL3 route handling
  • Documentation
  • e2e Test of a GL3 migration (manually)

Summary by CodeRabbit

  • New Features

    • Add an interactive import tool to migrate data from Greenlight v3, with progress reporting and partial-success handling.
  • Improvements

    • Access-code handling now supports legacy alphanumeric and numeric formats across validation, display, and input masks/placeholders.
    • Room share and moderator messaging formatting updated to handle non-numeric codes.
  • Tests

    • New unit and end-to-end tests covering the import flow and alphanumeric access-code scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 11, 2025

Walkthrough

Adds a Greenlight 3 import Artisan command, extends access_code support to alphanumeric legacy codes across backend and frontend, updates room formatting/input masking, and adds unit and E2E tests for GL3 import and access-code flows.

Changes

Cohort / File(s) Summary
Greenlight 3 Import Command
app/Console/Commands/ImportGreenlight3Command.php
New Artisan command to import users, rooms, and shared accesses from a Greenlight v3 PostgreSQL DB with interactive prompts, transactional flow, progress reporting, and detailed error summaries.
Import Tests & Helpers
tests/Backend/Unit/Console/ImportGreenlight3Test.php, tests/Backend/Unit/Console/helper/Greenlight3User.php, tests/Backend/Unit/Console/helper/Greenlight3Room.php
New unit test coverage and test-helper classes simulating Greenlight v3 DB, exercising import command behavior, validations, and resulting DB state.
Access-code backend logic
app/Http/Middleware/RoomAuthenticate.php, app/Http/Requests/UpdateRoomSettings.php
Relaxed middleware comparison to allow non-numeric access codes; validation rules updated to support legacy 6-char alphanumeric codes and conditional digit/size rules depending on code format.
Room model formatting
app/Models/Room.php
Adjusted moderator-only message formatting to handle raw vs. grouped access_code based on legacy_code flag.
Frontend display & inputs
resources/js/components/RoomShareButton.vue, resources/js/views/RoomsView.vue
RoomShareButton and RoomsView updated to render non-numeric access codes (raw) and change input mask/placeholder when legacy_code is true.
Backend & Frontend tests (access-code)
tests/Backend/Feature/api/v1/Room/RoomTest.php, tests/Frontend/e2e/RoomsViewGeneral.cy.js, tests/Frontend/e2e/RoomsViewSettings.cy.js
Tests updated/added to assert acceptance and UI handling of alphanumeric access codes (examples: 012abc, fck4fd) and related PUT payload validations.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay attention to transactional/rollback behavior and edge cases in ImportGreenlight3Command.
  • Verify consistency of access_code handling across middleware, validation rules, model formatting, and frontend masking.
  • Review test mocks/fakes for fidelity to production DB schemas and interactive command prompts.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 73.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'WIP: Feature: Greenlight3 import' is related to the main changeset objective but uses 'WIP' prefix and lacks specificity about what aspect of Greenlight3 import is being delivered. Remove 'WIP:' prefix and clarify the title to reflect the core deliverable, e.g., 'Add Greenlight3 import command with alphanumeric access code support'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description identifies the linked issue, specifies feature type, lists main changes, and notes TODOs, but most checklist items are unchecked and no justification is provided for incomplete work.
Linked Issues check ✅ Passed The PR implements the core requirement to provide a semi-automated import command for Greenlight 3, with user and room import functionality analogous to Greenlight 2 [#2664].
Out of Scope Changes check ✅ Passed All changes relate to Greenlight3 import functionality: the import command, access code alphanumeric support, and associated tests directly support the stated objective.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a 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 juggling

Switching to $room->access_code == $accessCode correctly allows alphanumeric codes from headers. If access_code is 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" vs 12345):

$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 on legacy_code; verify GL3 behavior

Conditionally formatting the code via $this->legacy_code ? implode('-', str_split(...)) : $this->access_code is a good step toward separating legacy vs non‑legacy display.

Two things to double‑check:

  • That legacy_code is true only 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 into xxx-xxx here 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 codes

The formattedAccessCode computed correctly avoids chunking non‑numeric codes (e.g. GL3 012abc) 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 isNaN for 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 clarity

The Greenlight3User helper cleanly captures the GL3 user shape and aligns with the existing Greenlight3Room helper.

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 semantics

The user import behaves as expected:

  • De‑duplicates by email, mapping multiple GL3 references to the same existing user.
  • Derives authenticator from external_id and preserves password_digest for 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 Role pivot uses an automatic flag, and you want these imported roles to behave like “automatic” assignments, you may prefer attach([$defaultRole => ['automatic' => true]]) instead of bare attach($defaultRole).

169-316: Room and shared access import mapping is clear; verify option mapping and ID collision policy

The room and shared access import functions are well structured:

  • Rooms:
    • Skip import when a room with the same friendly_id already exists, and intentionally don’t add it to $roomMap so 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 corresponding Room fields, plus attach the chosen RoomType.
  • Shared accesses:
    • Only add memberships when both user and room were successfully imported, assigning RoomUserRole::MODERATOR via syncWithoutDetaching, which is appropriate for GL’s shared access.

A few behavioral points worth confirming:

  • For guestPolicy, mapping only ASK_MODERATOR to RoomLobby::ENABLED and treating all other values as DISABLED is 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 continue path 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 codes

Conditionally switching to a 6‑character mask and neutral placeholder for room.legacy_code cleanly enables GL3‑style alphanumeric access codes while preserving the existing 9‑digit format for non‑legacy rooms. The interaction with login() (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 covered

This 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_code to 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 clarity

The 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

📥 Commits

Reviewing files that changed from the base of the PR and between 4fcc7c0 and 2e670e0.

📒 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 code

The 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 solid

The command wiring is coherent:

  • Dynamically registers a greenlight pgsql 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 validation

The 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 lowercase validation 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 lowercase rule for GL3 imports
tests/Frontend/e2e/RoomsViewGeneral.cy.js (1)

386-408: Legacy access-code E2E updated consistently to alphanumeric

Using "012abc" both for the input and the access-code header expectation matches the new legacy alphanumeric behaviour and keeps this test meaningful for GL3 imports. No issues from my side here.

Comment on lines +195 to +205
$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');

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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
Copy link

codecov bot commented Dec 11, 2025

Codecov Report

❌ Patch coverage is 96.95122% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 96.75%. Comparing base (d536e93) to head (c38131b).
⚠️ Report is 41 commits behind head on develop.

Files with missing lines Patch % Lines
app/Console/Commands/ImportGreenlight3Command.php 97.36% 4 Missing ⚠️
resources/js/components/RoomShareButton.vue 66.66% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             develop    #2665    +/-   ##
===========================================
  Coverage      96.75%   96.75%            
- Complexity      1816     1848    +32     
===========================================
  Files            434      435     +1     
  Lines          12483    12643   +160     
  Branches        2078     2080     +2     
===========================================
+ Hits           12078    12233   +155     
- Misses           405      410     +5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@pizkaz pizkaz force-pushed the feat/greenlight3-import branch from 2e670e0 to c38131b Compare December 12, 2025 14:05
Copy link

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between 2e670e0 and c38131b.

📒 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)
Copy link
Collaborator

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

Comment on lines +50 to +55
$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'];
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
$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'];

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Greenlight 3 import command

2 participants