mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
refactor(core): rename account prefix to my-account (#6821)
This commit is contained in:
parent
9a3f78d342
commit
9795412f43
8 changed files with 48 additions and 243 deletions
|
@ -1 +1 @@
|
|||
export const accountApiPrefix = '/account';
|
||||
export const accountApiPrefix = '/my-account';
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"tags": [
|
||||
{
|
||||
"name": "Account",
|
||||
"name": "My account",
|
||||
"description": "Account routes provide functionality for managing user profile for the end user to interact directly with access tokens."
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"/api/account": {
|
||||
"/api/my-account": {
|
||||
"get": {
|
||||
"operationId": "GetProfile",
|
||||
"summary": "Get profile",
|
||||
|
@ -53,7 +53,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/account/profile": {
|
||||
"/api/my-account/profile": {
|
||||
"patch": {
|
||||
"operationId": "UpdateOtherProfile",
|
||||
"summary": "Update other profile",
|
||||
|
@ -111,7 +111,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/account/password": {
|
||||
"/api/my-account/password": {
|
||||
"post": {
|
||||
"operationId": "UpdatePassword",
|
||||
"summary": "Update password",
|
||||
|
@ -139,7 +139,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/account/primary-email": {
|
||||
"/api/my-account/primary-email": {
|
||||
"post": {
|
||||
"operationId": "UpdatePrimaryEmail",
|
||||
"summary": "Update primary email",
|
||||
|
@ -183,7 +183,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/account/primary-phone": {
|
||||
"/api/my-account/primary-phone": {
|
||||
"post": {
|
||||
"operationId": "UpdatePrimaryPhone",
|
||||
"summary": "Update primary phone",
|
||||
|
@ -227,7 +227,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/account/identities": {
|
||||
"/api/my-account/identities": {
|
||||
"post": {
|
||||
"operationId": "AddUserIdentities",
|
||||
"summary": "Add a user identity",
|
||||
|
@ -252,7 +252,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/api/account/identities/{target}": {
|
||||
"/api/my-account/identities/{target}": {
|
||||
"delete": {
|
||||
"operationId": "DeleteIdentity",
|
||||
"summary": "Delete a user identity",
|
||||
|
|
|
@ -4,6 +4,9 @@ import pluralize from 'pluralize';
|
|||
|
||||
import { EnvSet } from '#src/env-set/index.js';
|
||||
|
||||
import { accountApiPrefix } from '../../account/constants.js';
|
||||
import { verificationApiPrefix } from '../../verification/index.js';
|
||||
|
||||
import { shouldThrow } from './general.js';
|
||||
|
||||
const chunk = <T>(array: T[], chunkSize: number): T[][] =>
|
||||
|
@ -124,8 +127,8 @@ const exceptionPrefixes = Object.freeze([
|
|||
'/interaction',
|
||||
'/experience',
|
||||
'/sign-in-exp/default/check-password',
|
||||
'/account',
|
||||
'/verifications',
|
||||
accountApiPrefix,
|
||||
verificationApiPrefix,
|
||||
]);
|
||||
|
||||
const isPathParameter = (segment?: string) =>
|
||||
|
|
|
@ -8,7 +8,7 @@ export const updatePassword = async (
|
|||
verificationRecordId: string,
|
||||
password: string
|
||||
) =>
|
||||
api.post('api/account/password', {
|
||||
api.post('api/my-account/password', {
|
||||
json: { password },
|
||||
headers: { [verificationRecordIdHeader]: verificationRecordId },
|
||||
});
|
||||
|
@ -19,13 +19,13 @@ export const updatePrimaryEmail = async (
|
|||
verificationRecordId: string,
|
||||
newIdentifierVerificationRecordId: string
|
||||
) =>
|
||||
api.post('api/account/primary-email', {
|
||||
api.post('api/my-account/primary-email', {
|
||||
json: { email, newIdentifierVerificationRecordId },
|
||||
headers: { [verificationRecordIdHeader]: verificationRecordId },
|
||||
});
|
||||
|
||||
export const deletePrimaryEmail = async (api: KyInstance, verificationRecordId: string) =>
|
||||
api.delete('api/account/primary-email', {
|
||||
api.delete('api/my-account/primary-email', {
|
||||
headers: { [verificationRecordIdHeader]: verificationRecordId },
|
||||
});
|
||||
|
||||
|
@ -35,13 +35,13 @@ export const updatePrimaryPhone = async (
|
|||
verificationRecordId: string,
|
||||
newIdentifierVerificationRecordId: string
|
||||
) =>
|
||||
api.post('api/account/primary-phone', {
|
||||
api.post('api/my-account/primary-phone', {
|
||||
json: { phone, newIdentifierVerificationRecordId },
|
||||
headers: { [verificationRecordIdHeader]: verificationRecordId },
|
||||
});
|
||||
|
||||
export const deletePrimaryPhone = async (api: KyInstance, verificationRecordId: string) =>
|
||||
api.delete('api/account/primary-phone', {
|
||||
api.delete('api/my-account/primary-phone', {
|
||||
headers: { [verificationRecordIdHeader]: verificationRecordId },
|
||||
});
|
||||
|
||||
|
@ -50,7 +50,7 @@ export const updateIdentities = async (
|
|||
verificationRecordId: string,
|
||||
newIdentifierVerificationRecordId: string
|
||||
) =>
|
||||
api.post('api/account/identities', {
|
||||
api.post('api/my-account/identities', {
|
||||
json: { newIdentifierVerificationRecordId },
|
||||
headers: { [verificationRecordIdHeader]: verificationRecordId },
|
||||
});
|
||||
|
@ -60,15 +60,17 @@ export const deleteIdentity = async (
|
|||
target: string,
|
||||
verificationRecordId: string
|
||||
) =>
|
||||
api.delete(`api/account/identities/${target}`, {
|
||||
api.delete(`api/my-account/identities/${target}`, {
|
||||
headers: { [verificationRecordIdHeader]: verificationRecordId },
|
||||
});
|
||||
|
||||
export const updateUser = async (api: KyInstance, body: Record<string, unknown>) =>
|
||||
api.patch('api/account', { json: body }).json<Partial<UserProfileResponse>>();
|
||||
api.patch('api/my-account', { json: body }).json<Partial<UserProfileResponse>>();
|
||||
|
||||
export const updateOtherProfile = async (api: KyInstance, body: Record<string, unknown>) =>
|
||||
api.patch('api/account/profile', { json: body }).json<Partial<UserProfileResponse['profile']>>();
|
||||
api
|
||||
.patch('api/my-account/profile', { json: body })
|
||||
.json<Partial<UserProfileResponse['profile']>>();
|
||||
|
||||
export const getUserInfo = async (api: KyInstance) =>
|
||||
api.get('api/account').json<Partial<UserProfileResponse>>();
|
||||
api.get('api/my-account').json<Partial<UserProfileResponse>>();
|
|
@ -12,7 +12,7 @@ import {
|
|||
updatePrimaryEmail,
|
||||
updatePrimaryPhone,
|
||||
updateUser,
|
||||
} from '#src/api/profile.js';
|
||||
} from '#src/api/my-account.js';
|
||||
import { createVerificationRecordByPassword } from '#src/api/verification-record.js';
|
||||
import { expectRejects } from '#src/helpers/index.js';
|
||||
import {
|
||||
|
@ -40,7 +40,7 @@ describe('account center fields disabled', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should return only name in GET /account', async () => {
|
||||
it('should return only name in GET /my-account', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password, {
|
||||
scopes: [UserScope.Email],
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable max-lines */
|
||||
import { UserScope } from '@logto/core-kit';
|
||||
import { SignInIdentifier } from '@logto/schemas';
|
||||
|
||||
|
@ -10,7 +9,7 @@ import {
|
|||
getUserInfo,
|
||||
updatePrimaryEmail,
|
||||
updatePrimaryPhone,
|
||||
} from '#src/api/profile.js';
|
||||
} from '#src/api/my-account.js';
|
||||
import {
|
||||
createAndVerifyVerificationCode,
|
||||
createVerificationRecordByPassword,
|
||||
|
@ -33,7 +32,7 @@ describe('account (email and phone)', () => {
|
|||
await enableAllAccountCenterFields(authedAdminApi);
|
||||
});
|
||||
|
||||
describe('POST /account/primary-email', () => {
|
||||
describe('POST /my-account/primary-email', () => {
|
||||
it('should fail if scope is missing', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password);
|
||||
|
@ -140,7 +139,7 @@ describe('account (email and phone)', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('DELETE /account/primary-email', () => {
|
||||
describe('DELETE /my-account/primary-email', () => {
|
||||
it('should fail if scope is missing', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password);
|
||||
|
@ -236,103 +235,7 @@ describe('account (email and phone)', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('POST /account/primary-phone', () => {
|
||||
it('should fail if scope is missing', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password);
|
||||
const verificationRecordId = await createVerificationRecordByPassword(api, password);
|
||||
|
||||
await expectRejects(deletePrimaryEmail(api, verificationRecordId), {
|
||||
code: 'auth.unauthorized',
|
||||
status: 400,
|
||||
});
|
||||
|
||||
await deleteDefaultTenantUser(user.id);
|
||||
});
|
||||
|
||||
it('should fail if verification record is invalid', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password, {
|
||||
scopes: [UserScope.Profile, UserScope.Email],
|
||||
});
|
||||
|
||||
await expectRejects(deletePrimaryEmail(api, 'invalid-verification-record-id'), {
|
||||
code: 'verification_record.permission_denied',
|
||||
status: 401,
|
||||
});
|
||||
|
||||
await deleteDefaultTenantUser(user.id);
|
||||
});
|
||||
|
||||
it('should fail if email is the only sign-up identifier', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password, {
|
||||
scopes: [UserScope.Profile, UserScope.Email],
|
||||
});
|
||||
const verificationRecordId = await createVerificationRecordByPassword(api, password);
|
||||
await enableAllPasswordSignInMethods({
|
||||
identifiers: [SignInIdentifier.Email],
|
||||
password: true,
|
||||
verify: true,
|
||||
});
|
||||
|
||||
await expectRejects(deletePrimaryEmail(api, verificationRecordId), {
|
||||
code: 'user.email_required',
|
||||
status: 400,
|
||||
});
|
||||
|
||||
await enableAllPasswordSignInMethods();
|
||||
await deleteDefaultTenantUser(user.id);
|
||||
});
|
||||
|
||||
it('should fail if email or phone is the sign-up identifier', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password, {
|
||||
scopes: [UserScope.Profile, UserScope.Email],
|
||||
});
|
||||
const verificationRecordId = await createVerificationRecordByPassword(api, password);
|
||||
await enableAllPasswordSignInMethods({
|
||||
identifiers: [SignInIdentifier.Email, SignInIdentifier.Phone],
|
||||
password: true,
|
||||
verify: true,
|
||||
});
|
||||
|
||||
await expectRejects(deletePrimaryEmail(api, verificationRecordId), {
|
||||
code: 'user.email_or_phone_required',
|
||||
status: 400,
|
||||
});
|
||||
|
||||
await enableAllPasswordSignInMethods();
|
||||
await deleteDefaultTenantUser(user.id);
|
||||
});
|
||||
|
||||
it('should be able to delete primary email', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password, {
|
||||
scopes: [UserScope.Profile, UserScope.Email],
|
||||
});
|
||||
const verificationRecordId = await createVerificationRecordByPassword(api, password);
|
||||
const newEmail = generateEmail();
|
||||
const newVerificationRecordId = await createAndVerifyVerificationCode(api, {
|
||||
type: SignInIdentifier.Email,
|
||||
value: newEmail,
|
||||
});
|
||||
|
||||
await updatePrimaryEmail(api, newEmail, verificationRecordId, newVerificationRecordId);
|
||||
|
||||
const userInfo = await getUserInfo(api);
|
||||
expect(userInfo).toHaveProperty('primaryEmail', newEmail);
|
||||
|
||||
await deletePrimaryEmail(api, verificationRecordId);
|
||||
|
||||
const userInfoAfterDelete = await getUserInfo(api);
|
||||
expect(userInfoAfterDelete).toHaveProperty('primaryEmail', null);
|
||||
|
||||
await deleteDefaultTenantUser(user.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('DELETE /account/primary-phone', () => {
|
||||
describe('POST /my-account/primary-phone', () => {
|
||||
it('should fail if scope is missing', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password);
|
||||
|
@ -402,113 +305,6 @@ describe('account (email and phone)', () => {
|
|||
await deleteDefaultTenantUser(user.id);
|
||||
});
|
||||
|
||||
describe('POST /account/primary-phone', () => {
|
||||
it('should fail if scope is missing', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password);
|
||||
const newPhone = generatePhone();
|
||||
const verificationRecordId = await createVerificationRecordByPassword(api, password);
|
||||
|
||||
await expectRejects(
|
||||
updatePrimaryPhone(api, newPhone, verificationRecordId, 'new-verification-record-id'),
|
||||
{
|
||||
code: 'auth.unauthorized',
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
|
||||
await deleteDefaultTenantUser(user.id);
|
||||
});
|
||||
|
||||
it('should fail if verification record is invalid', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password, {
|
||||
scopes: [UserScope.Profile, UserScope.Phone],
|
||||
});
|
||||
const newPhone = generatePhone();
|
||||
|
||||
await expectRejects(
|
||||
updatePrimaryPhone(
|
||||
api,
|
||||
newPhone,
|
||||
'invalid-verification-record-id',
|
||||
'new-verification-record-id'
|
||||
),
|
||||
{
|
||||
code: 'verification_record.permission_denied',
|
||||
status: 401,
|
||||
}
|
||||
);
|
||||
|
||||
await deleteDefaultTenantUser(user.id);
|
||||
});
|
||||
|
||||
it('should fail if new identifier verification record is invalid', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password, {
|
||||
scopes: [UserScope.Profile, UserScope.Phone],
|
||||
});
|
||||
const newPhone = generatePhone();
|
||||
const verificationRecordId = await createVerificationRecordByPassword(api, password);
|
||||
|
||||
await expectRejects(
|
||||
updatePrimaryPhone(api, newPhone, verificationRecordId, 'new-verification-record-id'),
|
||||
{
|
||||
code: 'verification_record.not_found',
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
|
||||
await deleteDefaultTenantUser(user.id);
|
||||
});
|
||||
|
||||
it('should be able to update primary phone by verifying password', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password, {
|
||||
scopes: [UserScope.Profile, UserScope.Phone],
|
||||
});
|
||||
const newPhone = generatePhone();
|
||||
const verificationRecordId = await createVerificationRecordByPassword(api, password);
|
||||
const newVerificationRecordId = await createAndVerifyVerificationCode(api, {
|
||||
type: SignInIdentifier.Phone,
|
||||
value: newPhone,
|
||||
});
|
||||
|
||||
await updatePrimaryPhone(api, newPhone, verificationRecordId, newVerificationRecordId);
|
||||
|
||||
const userInfo = await getUserInfo(api);
|
||||
expect(userInfo).toHaveProperty('primaryPhone', newPhone);
|
||||
|
||||
await deleteDefaultTenantUser(user.id);
|
||||
});
|
||||
|
||||
it('should be able to update primary phone by verifying existing phone', async () => {
|
||||
const primaryPhone = generatePhone();
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword({
|
||||
primaryPhone,
|
||||
});
|
||||
const api = await signInAndGetUserApi(username, password, {
|
||||
scopes: [UserScope.Profile, UserScope.Phone],
|
||||
});
|
||||
const newPhone = generatePhone();
|
||||
const verificationRecordId = await createAndVerifyVerificationCode(api, {
|
||||
type: SignInIdentifier.Phone,
|
||||
value: primaryPhone,
|
||||
});
|
||||
const newVerificationRecordId = await createAndVerifyVerificationCode(api, {
|
||||
type: SignInIdentifier.Phone,
|
||||
value: newPhone,
|
||||
});
|
||||
|
||||
await updatePrimaryPhone(api, newPhone, verificationRecordId, newVerificationRecordId);
|
||||
|
||||
const userInfo = await getUserInfo(api);
|
||||
expect(userInfo).toHaveProperty('primaryPhone', newPhone);
|
||||
|
||||
await deleteDefaultTenantUser(user.id);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to delete primary phone', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password, {
|
||||
|
@ -535,7 +331,7 @@ describe('account (email and phone)', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('DELETE /account/primary-phone', () => {
|
||||
describe('DELETE /my-account/primary-phone', () => {
|
||||
it('should fail if scope is missing', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password);
|
||||
|
@ -631,4 +427,3 @@ describe('account (email and phone)', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
/* eslint-enable max-lines */
|
||||
|
|
|
@ -2,7 +2,12 @@ import { UserScope } from '@logto/core-kit';
|
|||
import { hookEvents, SignInIdentifier } from '@logto/schemas';
|
||||
|
||||
import { enableAllAccountCenterFields } from '#src/api/account-center.js';
|
||||
import { getUserInfo, updateOtherProfile, updatePassword, updateUser } from '#src/api/profile.js';
|
||||
import {
|
||||
getUserInfo,
|
||||
updateOtherProfile,
|
||||
updatePassword,
|
||||
updateUser,
|
||||
} from '#src/api/my-account.js';
|
||||
import { updateSignInExperience } from '#src/api/sign-in-experience.js';
|
||||
import { createVerificationRecordByPassword } from '#src/api/verification-record.js';
|
||||
import { WebHookApiTest } from '#src/helpers/hook.js';
|
||||
|
@ -46,11 +51,11 @@ describe('account', () => {
|
|||
await webHookApi.cleanUp();
|
||||
});
|
||||
|
||||
describe('GET /account', () => {
|
||||
describe('GET /my-account', () => {
|
||||
it('should allow all origins', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password);
|
||||
const response = await api.get('api/account');
|
||||
const response = await api.get('api/my-account');
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('*');
|
||||
|
||||
|
@ -119,7 +124,7 @@ describe('account', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('PATCH /account', () => {
|
||||
describe('PATCH /my-account', () => {
|
||||
it('should be able to update name', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password);
|
||||
|
@ -209,7 +214,7 @@ describe('account', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('PATCH /account/profile', () => {
|
||||
describe('PATCH /my-account/profile', () => {
|
||||
it('should be able to update other profile', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password);
|
||||
|
@ -256,7 +261,7 @@ describe('account', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('POST /account/password', () => {
|
||||
describe('POST /my-account/password', () => {
|
||||
it('should fail if verification record is invalid', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password);
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
mockSocialConnectorTarget,
|
||||
} from '#src/__mocks__/connectors-mock.js';
|
||||
import { enableAllAccountCenterFields } from '#src/api/account-center.js';
|
||||
import { deleteIdentity, getUserInfo, updateIdentities } from '#src/api/profile.js';
|
||||
import { deleteIdentity, getUserInfo, updateIdentities } from '#src/api/my-account.js';
|
||||
import {
|
||||
createSocialVerificationRecord,
|
||||
createVerificationRecordByPassword,
|
||||
|
@ -26,7 +26,7 @@ import {
|
|||
} from '#src/helpers/profile.js';
|
||||
import { enableAllPasswordSignInMethods } from '#src/helpers/sign-in-experience.js';
|
||||
|
||||
describe('account (social)', () => {
|
||||
describe('my-account (social)', () => {
|
||||
const state = 'fake_state';
|
||||
const redirectUri = 'http://localhost:3000/redirect';
|
||||
const authorizationCode = 'fake_code';
|
||||
|
@ -47,7 +47,7 @@ describe('account (social)', () => {
|
|||
await clearConnectorsByTypes([ConnectorType.Social, ConnectorType.Email]);
|
||||
});
|
||||
|
||||
describe('POST /account/identities', () => {
|
||||
describe('POST /my-account/identities', () => {
|
||||
it('should fail if scope is missing', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password);
|
||||
|
@ -166,7 +166,7 @@ describe('account (social)', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('DELETE /account/identities/:target', () => {
|
||||
describe('DELETE /my-account/identities/:target', () => {
|
||||
it('should fail if scope is missing', async () => {
|
||||
const { user, username, password } = await createDefaultTenantUserWithPassword();
|
||||
const api = await signInAndGetUserApi(username, password);
|
||||
|
|
Loading…
Reference in a new issue