mirror of
https://github.com/anikeen-com/id.git
synced 2026-11-06 15:48:36 +00:00
Look up users by email and document invitations
This commit is contained in:
2 files changed
+95
-10
No files matched your search
@@ -5,7 +5,9 @@ namespace Anikeen\Id\Concerns;
|
||||
|
||||
use Anikeen\Id\ApiOperations\Get;
|
||||
use Anikeen\Id\ApiOperations\Post;
|
||||
use Anikeen\Id\Exceptions\ApiException;
|
||||
use Anikeen\Id\Result;
|
||||
use Anikeen\Id\Support\AppRequest;
|
||||
use Throwable;
|
||||
|
||||
trait ManagesUsers
|
||||
@@ -24,20 +26,32 @@ trait ManagesUsers
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new user on behalf of the current user.
|
||||
* Finds the account of an email address or creates it (trusted apps, app token). The email
|
||||
* address is matched regardless of case; a new account stores it in lower case, unverified.
|
||||
* An existing account is returned unchanged (only a missing name is filled in).
|
||||
*
|
||||
* @param array{
|
||||
* first_name: null|string,
|
||||
* last_name: null|string,
|
||||
* username: null|string,
|
||||
* name?: null|string,
|
||||
* first_name?: null|string,
|
||||
* last_name?: null|string,
|
||||
* username?: null|string,
|
||||
* email: string,
|
||||
* password: null|string
|
||||
* password?: null|string,
|
||||
* password_confirmation?: null|string,
|
||||
* send_invitation?: bool
|
||||
* } $attributes The user data
|
||||
* - first_name: The first name (optional)
|
||||
* - last_name: The last name (optional)
|
||||
* - username: The username (optional)
|
||||
* - email: The email (required)
|
||||
* - password: The password (optional, can be reset by the user if not provided)
|
||||
* - name: The display name (optional)
|
||||
* - first_name: The first name (optional, joined with last_name into name)
|
||||
* - last_name: The last name (optional, joined with first_name into name)
|
||||
* - username: The username (optional)
|
||||
* - email: The email (required)
|
||||
* - password: The password (optional, can be reset by the user if not provided)
|
||||
* - password_confirmation: Defaults to password
|
||||
* - send_invitation: Send the user an invitation mail with a link to set a password
|
||||
* (optional, only when this call created the account without a
|
||||
* password, never for an existing account)
|
||||
* @return Result `data` is the user plus `created` (this call created the account) and
|
||||
* `invitation_sent` (the invitation mail was sent); 201 when created, 200 when it existed
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function createUser(array $attributes): Result
|
||||
@@ -57,6 +71,30 @@ trait ManagesUsers
|
||||
return $this->asApp()->post('v1/admin/users', $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the account of an email address without creating or changing anything (trusted
|
||||
* apps, app token). The email address is matched regardless of case.
|
||||
*
|
||||
* `data` is `object{id: string, name: ?string, email: string, email_verified: bool,
|
||||
* has_billing_address: bool}`.
|
||||
*
|
||||
* @return Result|null null when no account uses the email address
|
||||
* @throws ApiException when the request fails for another reason
|
||||
*/
|
||||
public function findUserByEmail(string $email): ?Result
|
||||
{
|
||||
try {
|
||||
return AppRequest::send('POST', 'v1/admin/users/lookup', ['email' => $email]);
|
||||
} catch (ApiException $e) {
|
||||
// only Anikeen ID's own answer means "no account", not e.g. an unknown route
|
||||
if ($e->isNotFound() && $e->error === 'user_not_found') {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the user of a social login or create it (trusted apps, app token).
|
||||
*
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Anikeen\Id\Tests;
|
||||
|
||||
use Anikeen\Id\AnikeenId;
|
||||
use Anikeen\Id\Billable;
|
||||
use Anikeen\Id\Exceptions\ApiException;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
@@ -90,6 +91,52 @@ class EndpointsTest extends TestCase
|
||||
$this->assertSame(['email' => 'erika@example.com', 'password' => 'secret-123', 'name' => 'Erika Mustermann', 'password_confirmation' => 'secret-123'], $this->sentJson(0));
|
||||
}
|
||||
|
||||
public function test_create_user_can_send_an_invitation(): void
|
||||
{
|
||||
$this->respond(201, ['data' => ['id' => '01JNEW', 'email' => 'erika@example.com', 'created' => true, 'invitation_sent' => true]]);
|
||||
|
||||
$result = app(AnikeenId::class)->createUser(['name' => 'Erika Mustermann', 'email' => 'erika@example.com', 'send_invitation' => true]);
|
||||
|
||||
$this->assertSame('POST /api/v1/admin/users Bearer app-token', $this->sentCall(0));
|
||||
$this->assertSame(['name' => 'Erika Mustermann', 'email' => 'erika@example.com', 'send_invitation' => true], $this->sentJson(0));
|
||||
$this->assertTrue($result->data->created);
|
||||
$this->assertTrue($result->data->invitation_sent);
|
||||
}
|
||||
|
||||
public function test_find_user_by_email_uses_the_lookup_endpoint(): void
|
||||
{
|
||||
$this->respond(200, ['data' => ['id' => '01JUSER', 'name' => 'Erika', 'email' => 'erika@example.com', 'email_verified' => true, 'has_billing_address' => false]]);
|
||||
|
||||
$result = app(AnikeenId::class)->findUserByEmail('Erika@Example.com');
|
||||
|
||||
$this->assertSame('POST /api/v1/admin/users/lookup Bearer app-token', $this->sentCall(0));
|
||||
$this->assertSame(['email' => 'Erika@Example.com'], $this->sentJson(0));
|
||||
$this->assertSame('01JUSER', $result->data->id);
|
||||
$this->assertFalse($result->data->has_billing_address);
|
||||
}
|
||||
|
||||
public function test_find_user_by_email_returns_null_without_an_account(): void
|
||||
{
|
||||
$this->respond(404, ['message' => 'User not found.', 'error' => 'user_not_found']);
|
||||
|
||||
$this->assertNull(app(AnikeenId::class)->findUserByEmail('nobody@example.com'));
|
||||
}
|
||||
|
||||
public function test_find_user_by_email_throws_other_failures(): void
|
||||
{
|
||||
$this->respond(404, ['message' => 'Not found.']); // e.g. an Anikeen ID without the lookup
|
||||
$this->respond(500, ['message' => 'Server Error']);
|
||||
|
||||
foreach ([404, 500] as $status) {
|
||||
try {
|
||||
app(AnikeenId::class)->findUserByEmail('erika@example.com');
|
||||
$this->fail('Expected an ApiException.');
|
||||
} catch (ApiException $e) {
|
||||
$this->assertSame($status, $e->status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function test_email_check_and_socialite_users(): void
|
||||
{
|
||||
$this->respond(200, ['data' => ['exists' => true]]);
|
||||
|
||||
Reference in new issue
Block a user