Skip to content

Commit

Permalink
PROD-30974: Implement "user logs in" event for the EDA
Browse files Browse the repository at this point in the history
  • Loading branch information
tregismoreira authored and ribel committed Nov 3, 2024
1 parent 04d6329 commit 272f571
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 7 deletions.
139 changes: 139 additions & 0 deletions modules/social_features/social_user/asyncapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,141 @@ channels:
type: string
format: uri
description: Canonical URL of the actor user.
userLogin:
address: com.getopensocial.cms.user.login
messages:
userLogin:
payload:
allOf:
- $ref: '#/components/schemas/cloudEventsSchema'
- type: object
properties:
data:
type: object
properties:
id:
type: string
description: The UUID of the user.
created:
type: string
format: date-time
description: The creation time of the user.
updated:
type: string
format: date-time
description: The last updated time of the user.
status:
type: string
description: The status of the user.
enum:
- active
- blocked
displayName:
type: string
description: The display name of the user.
firstName:
type: string
nullable: true
description: The first name of the user.
lastName:
type: string
nullable: true
description: The last name of the user.
email:
type: string
format: email
description: The email address of the user.
roles:
type: array
items:
type: string
description: Roles assigned to the user.
timezone:
type: string
description: The timezone of the user.
language:
type: string
description: The preferred language of the user.
address:
type: object
nullable: true
properties:
label:
type: string
description: The label of the user address.
countryCode:
type: string
description: The country code of the user address.
administrativeArea:
type: string
description: The administrative area of the user address.
locality:
type: string
description: The locality of the user address.
dependentLocality:
type: string
description: The dependent locality of the user address.
postalCode:
type: string
description: The postal code of the user address.
sortingCode:
type: string
description: The sorting code of the user address.
addressLine1:
type: string
description: The first address line of the user address.
addressLine2:
type: string
description: The second address line of the user address.
phone:
type: string
nullable: true
description: The phone number of the user.
function:
type: string
nullable: true
description: The job function of the user.
organization:
type: string
nullable: true
description: The organization of the user.
href:
type: object
properties:
canonical:
type: string
format: uri
description: Canonical URL of the user.
actor:
type: object
properties:
application:
type: object
nullable: true
properties:
id:
type: string
description: The UUID of the application.
name:
type: string
description: The name of the application.
user:
type: object
nullable: true
properties:
id:
type: string
description: The UUID of the actor user.
displayName:
type: string
description: The display name of the actor user.
href:
type: object
properties:
canonical:
type: string
format: uri
description: Canonical URL of the actor user.

operations:
onUserCreate:
Expand All @@ -279,3 +414,7 @@ operations:
action: 'receive'
channel:
$ref: '#/channels/profileUpdate'
onUserLogin:
action: 'receive'
channel:
$ref: '#/channels/userLogin'
9 changes: 9 additions & 0 deletions modules/social_features/social_user/social_user.module
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,15 @@ function social_user_profile_update(EntityInterface $entity): void {
}
}

/**
* Implements hook_user_login().
*
* When a user logs in we should trigger the corresponding EDA event.
*/
function social_user_user_login(UserInterface $user): void {
\Drupal::service('social_user.eda_handler')->userLogin($user);
}

/**
* Implements hook_views_data_alter().
*/
Expand Down
43 changes: 36 additions & 7 deletions modules/social_features/social_user/src/EdaHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Drupal\social_eda\Types\Href;
use Drupal\social_eda\Types\User;
use Drupal\social_user\Event\UserEventData;
use Drupal\social_user\Event\UserEventDataLite;
use Drupal\user\UserInterface;
use Symfony\Component\HttpFoundation\RequestStack;

Expand Down Expand Up @@ -91,6 +92,15 @@ public function profileUpdate(UserInterface $user): void {
$this->dispatch($topic_name, $event_type, $user);
}

/**
* User login handler.
*/
public function userLogin(UserInterface $user): void {
$event_type = 'com.getopensocial.cms.user.login';
$topic_name = 'com.getopensocial.cms.user.login';
$this->dispatch($topic_name, $event_type, $user);
}

/**
* Transforms a NodeInterface into a CloudEvent.
*/
Expand Down Expand Up @@ -119,12 +129,23 @@ public function fromEntity(UserInterface $user, string $event_type): CloudEvent
$organization = $profile->get('field_profile_organization')->value;
}

return new CloudEvent(
id: $this->uuid->generate(),
source: $this->source,
type: $event_type,
data: [
'user' => new UserEventData(
switch ($event_type) {
case 'com.getopensocial.cms.user.login':
$user_data = new UserEventDataLite(
id: $user->get('uuid')->value,
created: DateTime::fromTimestamp($user->getCreatedTime())->toString(),
updated: DateTime::fromTimestamp($user->getChangedTime())->toString(),
status: $user->isActive() ? 'active' : 'blocked',
displayName: $user->getDisplayName(),
roles: array_values($user->getRoles()),
timezone: $user->getTimeZone(),
language: $user->getPreferredLangcode(),
href: Href::fromEntity($user),
);
break;

default:
$user_data = new UserEventData(
id: $user->get('uuid')->value,
created: DateTime::fromTimestamp($user->getCreatedTime())->toString(),
updated: DateTime::fromTimestamp($user->getChangedTime())->toString(),
Expand All @@ -143,7 +164,15 @@ public function fromEntity(UserInterface $user, string $event_type): CloudEvent
function: $function ?? '',
organization: $organization ?? '',
href: Href::fromEntity($user),
),
);
}

return new CloudEvent(
id: $this->uuid->generate(),
source: $this->source,
type: $event_type,
data: [
'user' => $user_data,
'actor' => [
'application' => $actor_application ? Application::fromId($actor_application) : NULL,
'user' => $actor_user ? User::fromEntity($actor_user) : NULL,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Drupal\social_user\Event;

use Drupal\social_eda\Types\Href;

/**
* Contains data about an Open Social user.
*/
class UserEventDataLite {

/**
* {@inheritDoc}
*/
public function __construct(
public readonly string $id,
public readonly string $created,
public readonly string $updated,
public readonly string $status,
public readonly string $displayName,
public readonly array $roles,
public readonly string $timezone,
public readonly string $language,
public readonly Href $href,
) {}

}
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,33 @@ public function testProfileUpdate(): void {
$this->assertEquals('com.getopensocial.cms.profile.update', $event->getType());
}

/**
* Test the userLogin() method.
*
* @covers ::userLogin
*/
public function testUserLogin(): void {
// Create the handler instance.
$handler = $this->getMockedHandler();

// Create the event object.
$event = $handler->fromEntity($this->user, 'com.getopensocial.cms.user.login');

// Expect the dispatch method in the dispatcher to be called.
$this->dispatcher->expects($this->once())
->method('dispatch')
->with(
$this->equalTo('com.getopensocial.cms.user.login'),
$this->equalTo($event)
);

// Call the userLogin method.
$handler->userLogin($this->user);

// Assert that the correct event is dispatched.
$this->assertEquals('com.getopensocial.cms.user.login', $event->getType());
}

/**
* Returns a mocked handler with dependencies injected.
*
Expand Down

0 comments on commit 272f571

Please sign in to comment.