Skip to content

app-profile#274

Open
BrunoSFreschi wants to merge 2 commits into
he4rt:4.xfrom
BrunoSFreschi:4.x
Open

app-profile#274
BrunoSFreschi wants to merge 2 commits into
he4rt:4.xfrom
BrunoSFreschi:4.x

Conversation

@BrunoSFreschi
Copy link
Copy Markdown

@BrunoSFreschi BrunoSFreschi commented May 21, 2026

Principais pontos:

  • Novo pacote he4rt/profile registrado em composer.json e composer.lock.
  • Migration user_profiles com UUID, escopo por tenant, unique(user_id, tenant_id) e índice parcial para available_for_proposals = true.
  • Model Profile com HasUuids, factory, relacionamentos, casts de enum/date/bool/array e validação das chaves de social_links.
  • Enums SeniorityLevel, StartAvailability e SocialPlatform com labels pt-BR.
  • ProfileServiceProvider registrando migrations, morph map e criação eager quando há insert em tenant_users via attach.
  • Testes cobrindo criação automática, não duplicação, múltiplos tenants, factory, labels e validação de social_links.

Description

Introduces the new he4rt/profile module as a reusable Laravel package providing user profile management. The module includes a Profile Eloquent model with tenant-scoped data, three enums for seniority levels, social platforms, and availability periods, automatic profile creation when users join tenants, and comprehensive tests covering creation, idempotency, and factory behavior.

References

  • Previous profile-related work: #207 (duplicate user recovery from import-profiles), #133 (profile command), #102 (user profile address)
  • Module design aligns with project's Identity area: "Centralized authentication and user profiles"

Dependencies & Requirements

  • New Composer package dependency: he4rt/profile: >=1
  • Module requires Laravel 11+ (standard for project)
  • Tenant and User models must exist for foreign key relationships
  • Database supports partial/filtered indexes (PostgreSQL-compatible)

Contributor Summary

Contributor Lines Added Lines Removed Files Changed
BrunoSFreschi 532 0 15

Changes Summary

File Path Change Description
app-modules/profile/composer.json Package configuration with PSR-4 autoloading and Laravel provider registration
app-modules/profile/src/Models/Profile.php Core Eloquent model with relationships, casts, and social links validation
app-modules/profile/src/ProfileServiceProvider.php Service provider registering migrations, morph map, and eager profile creation on tenant join
app-modules/profile/src/Enums/SeniorityLevel.php String enum (Junior, Pleno, Senior, Specialist, Lead) with pt-BR labels
app-modules/profile/src/Enums/SocialPlatform.php String enum (Instagram, Twitter, Website, YouTube, Bluesky) with labels and values helper
app-modules/profile/src/Enums/StartAvailability.php String enum for availability periods with Portuguese labels
app-modules/profile/src/Listeners/CreateProfileForTenantMember.php Event listener ensuring profile creation on tenant membership
app-modules/profile/database/migrations/2026_05_21_000000_create_user_profiles_table.php Migration creating user_profiles table with UUID PK, tenant scope, and partial index
app-modules/profile/database/factories/ProfileFactory.php Factory with base and complete() state definitions
app-modules/profile/tests/Feature/ProfileCreationTest.php Feature tests covering creation, idempotency, multi-tenant scenarios
app-modules/profile/tests/Unit/ProfileEnumTest.php Unit test validating pt-BR enum labels
app-modules/profile/tests/Unit/ProfileSocialLinksTest.php Unit test for social links validation
app-modules/profile/phpstan.neon PHPStan configuration for static analysis
app-modules/profile/phpstan.ignore.neon PHPStan ignore rules
composer.json Added he4rt/profile dependency to root project

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

📝 Walkthrough

Walkthrough

This pull request introduces a new he4rt/profile Laravel module that provides tenant-scoped user profile management. The module defines a data schema with a user_profiles table, supporting enums for seniority levels, social platforms, and availability states, and an Eloquent model with validation for social links. The core feature automatically creates profiles when users join tenants via a database query listener that intercepts inserts into tenant_users. The module includes a factory for testing, comprehensive feature and unit tests, and full Composer and static analysis configuration.

Possibly related issues

  • #251: Directly related; this PR implements the identical Profile module components, including the Profile model, enums, migration, factory, ProfileServiceProvider with tenant_users insert listener, and tests.
  • #250: Directly related; this PR implements the Profile Core features and artifacts described in the product requirements document, including the database schema, model, enums, service provider, and factory.

Suggested reviewers

  • gvieira18
  • danielhe4rt
  • thalesmengue
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'app-profile' is vague and uses a generic term that does not convey meaningful information about the changeset. It lacks descriptive detail about the actual changes being introduced. Consider using a more descriptive title that summarizes the main change, such as 'Add profile module with user profile management system' or 'Implement Profile module with database schema and model relationships'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app-modules/profile/src/ProfileServiceProvider.php`:
- Around line 25-26: The SQL matching is too strict: update the INSERT detection
in ProfileServiceProvider (the preg_match that checks $event->sql) to accept
optional leading whitespace, optional schema qualification, and either backtick
or double-quote quoting around identifiers (tenant_users), e.g. allow
`schema`.`tenant_users`, "schema"."tenant_users", or tenant_users; similarly,
harden insertColumns() to strip/normalize backticks and double-quotes and to
handle schema-qualified table names before extracting column lists — normalize
the SQL (lowercase for matching, remove/replace surrounding quotes on
identifiers) then apply relaxed regexes so schema-qualified and backtick-quoted
INSERTs are correctly detected and parsed.

In `@app-modules/profile/tests/Feature/ProfileCreationTest.php`:
- Around line 68-77: The test is relying on ordered tenant_id values which is
flaky with UUIDs; remove the explicit orderBy('tenant_id') on the
Profile::query() call and change the assertion that checks tenant IDs (the
expect(...)->toHaveCount(2) and ->and($profiles->pluck('tenant_id')->all())
portion) to assert membership regardless of order (e.g., compare as unordered
sets or use an assertion that canonicalizes/sorts both arrays) so the test
verifies the two tenant IDs are present without depending on their order.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Pro

Run ID: c1bc62ca-283d-42d9-bc68-ea5b7468afff

📥 Commits

Reviewing files that changed from the base of the PR and between efd05ff and a27371f.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • app-modules/profile/composer.json
  • app-modules/profile/database/factories/ProfileFactory.php
  • app-modules/profile/database/migrations/2026_05_21_000000_create_user_profiles_table.php
  • app-modules/profile/phpstan.ignore.neon
  • app-modules/profile/phpstan.neon
  • app-modules/profile/src/Enums/SeniorityLevel.php
  • app-modules/profile/src/Enums/SocialPlatform.php
  • app-modules/profile/src/Enums/StartAvailability.php
  • app-modules/profile/src/Listeners/CreateProfileForTenantMember.php
  • app-modules/profile/src/Models/Profile.php
  • app-modules/profile/src/ProfileServiceProvider.php
  • app-modules/profile/tests/Feature/ProfileCreationTest.php
  • app-modules/profile/tests/Unit/ProfileEnumTest.php
  • app-modules/profile/tests/Unit/ProfileSocialLinksTest.php
  • composer.json

Comment on lines +25 to +26
if (! preg_match('/^insert into "?tenant_users"?/i', $event->sql)) {
return;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the file and print the relevant sections with line numbers
FILE="app-modules/profile/src/ProfileServiceProvider.php"
if [ ! -f "$FILE" ]; then
  echo "Missing file: $FILE" >&2
  exit 1
fi

nl -ba "$FILE" | sed -n '1,120p'

Repository: he4rt/heartdevs.com

Length of output: 107


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for tenant_users / preg_match patterns in the file
FILE="app-modules/profile/src/ProfileServiceProvider.php"
rg -n "tenant_users|preg_match|QueryExecuted|sql" "$FILE"

Repository: he4rt/heartdevs.com

Length of output: 472


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="app-modules/profile/src/ProfileServiceProvider.php"
# show around the listener and helper
sed -n '15,90p' "$FILE" | cat -n

Repository: he4rt/heartdevs.com

Length of output: 2166


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="app-modules/profile/src/ProfileServiceProvider.php"
# grab full helper function
sed -n '50,120p' "$FILE" | cat -n

Repository: he4rt/heartdevs.com

Length of output: 789


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="app-modules/profile/src/ProfileServiceProvider.php"
rg -n "insertColumns\\(|tenant_users|preg_match\\(" "$FILE"

Repository: he4rt/heartdevs.com

Length of output: 362


Make SQL matching/parsing resilient to driver-specific INSERT formats.

The current regexes only match INSERT statements that start with insert into "?tenant_users"? and only support optional double-quoting; they miss backtick-quoted and/or schema-qualified inserts, causing the listener to return silently (so profile creation is skipped). This also impacts column extraction in insertColumns().

Proposed hardening
-            if (! preg_match('/^insert into "?tenant_users"?/i', $event->sql)) {
+            if (! preg_match('/^\s*insert\s+into\s+(?:(?:"[^"]+"|`[^`]+`)\.)?(?:"tenant_users"|`tenant_users`|tenant_users)\b/i', $event->sql)) {
                 return;
             }
@@
-        preg_match('/insert into "?tenant_users"? \((?<columns>[^)]+)\)/i', $sql, $matches);
+        preg_match('/insert\s+into\s+(?:(?:"[^"]+"|`[^`]+`)\.)?(?:"tenant_users"|`tenant_users`|tenant_users)\s*\((?<columns>[^)]+)\)/i', $sql, $matches);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app-modules/profile/src/ProfileServiceProvider.php` around lines 25 - 26, The
SQL matching is too strict: update the INSERT detection in
ProfileServiceProvider (the preg_match that checks $event->sql) to accept
optional leading whitespace, optional schema qualification, and either backtick
or double-quote quoting around identifiers (tenant_users), e.g. allow
`schema`.`tenant_users`, "schema"."tenant_users", or tenant_users; similarly,
harden insertColumns() to strip/normalize backticks and double-quotes and to
handle schema-qualified table names before extracting column lists — normalize
the SQL (lowercase for matching, remove/replace surrounding quotes on
identifiers) then apply relaxed regexes so schema-qualified and backtick-quoted
INSERTs are correctly detected and parsed.

Comment on lines +68 to +77
$profiles = Profile::query()
->whereBelongsTo($user)
->orderBy('tenant_id')
->get();

expect($profiles)->toHaveCount(2)
->and($profiles->pluck('tenant_id')->all())->toBe([
$firstTenant->id,
$secondTenant->id,
])
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid order-dependent assertion on tenant UUIDs.

Line 70 and Lines 74-77 make the test depend on tenant_id ordering. With UUID-based IDs, this can be nondeterministic and cause flaky failures. Assert membership without relying on order.

Suggested fix
-    $profiles = Profile::query()
-        ->whereBelongsTo($user)
-        ->orderBy('tenant_id')
-        ->get();
+    $profiles = Profile::query()
+        ->whereBelongsTo($user)
+        ->get();
+
+    $tenantIds = $profiles->pluck('tenant_id')->all();
+    $expectedTenantIds = [$firstTenant->id, $secondTenant->id];
+    sort($tenantIds);
+    sort($expectedTenantIds);

     expect($profiles)->toHaveCount(2)
-        ->and($profiles->pluck('tenant_id')->all())->toBe([
-            $firstTenant->id,
-            $secondTenant->id,
-        ])
+        ->and($tenantIds)->toBe($expectedTenantIds)
         ->and($profiles->pluck('id')->unique())->toHaveCount(2);
📝 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.

Suggested change
$profiles = Profile::query()
->whereBelongsTo($user)
->orderBy('tenant_id')
->get();
expect($profiles)->toHaveCount(2)
->and($profiles->pluck('tenant_id')->all())->toBe([
$firstTenant->id,
$secondTenant->id,
])
$profiles = Profile::query()
->whereBelongsTo($user)
->get();
$tenantIds = $profiles->pluck('tenant_id')->all();
$expectedTenantIds = [$firstTenant->id, $secondTenant->id];
sort($tenantIds);
sort($expectedTenantIds);
expect($profiles)->toHaveCount(2)
->and($tenantIds)->toBe($expectedTenantIds)
->and($profiles->pluck('id')->unique())->toHaveCount(2);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app-modules/profile/tests/Feature/ProfileCreationTest.php` around lines 68 -
77, The test is relying on ordered tenant_id values which is flaky with UUIDs;
remove the explicit orderBy('tenant_id') on the Profile::query() call and change
the assertion that checks tenant IDs (the expect(...)->toHaveCount(2) and
->and($profiles->pluck('tenant_id')->all()) portion) to assert membership
regardless of order (e.g., compare as unordered sets or use an assertion that
canonicalizes/sorts both arrays) so the test verifies the two tenant IDs are
present without depending on their order.

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.

1 participant