mirror of
https://github.com/standardnotes/server
synced 2026-07-31 14:16:46 -04:00
feat: sign in setting refactor (#472)
* fix(auth): refactor setting names into domain core value objects * fix(auth): refactor specs with setting name value objects * feat(auth): move mute sign in emails to a subscription kind of setting * feat(auth): add migration script to change sign in email settings to subscription settings * chore: fix setting name usage * fix(auth): upper casing setting names --------- Co-authored-by: Karol Sójko <[email protected]>
This commit is contained in:
co-authored by
Karol Sójko
parent
ec0fb7e0b9
commit
27cf093f85
@@ -14,6 +14,11 @@ import { User } from '../../User/User'
|
||||
import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||
import { UpdateSetting } from './UpdateSetting'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { SubscriptionSettingServiceInterface } from '../../Setting/SubscriptionSettingServiceInterface'
|
||||
import { SubscriptionSettingProjector } from '../../../Projection/SubscriptionSettingProjector'
|
||||
import { UserSubscriptionServiceInterface } from '../../Subscription/UserSubscriptionServiceInterface'
|
||||
import { UserSubscriptionType } from '../../Subscription/UserSubscriptionType'
|
||||
import { UserSubscription } from '../../Subscription/UserSubscription'
|
||||
|
||||
describe('UpdateSetting', () => {
|
||||
let settingService: SettingServiceInterface
|
||||
@@ -25,9 +30,24 @@ describe('UpdateSetting', () => {
|
||||
let userRepository: UserRepositoryInterface
|
||||
let roleService: RoleServiceInterface
|
||||
let logger: Logger
|
||||
let userSubscriptionService: UserSubscriptionServiceInterface
|
||||
let subscriptionSettingProjector: SubscriptionSettingProjector
|
||||
let subscriptionSettingService: SubscriptionSettingServiceInterface
|
||||
let regularSubscription: UserSubscription
|
||||
let sharedSubscription: UserSubscription
|
||||
|
||||
const createUseCase = () =>
|
||||
new UpdateSetting(settingService, settingProjector, settingsAssociationService, userRepository, roleService, logger)
|
||||
new UpdateSetting(
|
||||
settingService,
|
||||
subscriptionSettingService,
|
||||
userSubscriptionService,
|
||||
settingProjector,
|
||||
subscriptionSettingProjector,
|
||||
settingsAssociationService,
|
||||
userRepository,
|
||||
roleService,
|
||||
logger,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
setting = {} as jest.Mocked<Setting>
|
||||
@@ -35,9 +55,32 @@ describe('UpdateSetting', () => {
|
||||
settingService = {} as jest.Mocked<SettingServiceInterface>
|
||||
settingService.createOrReplace = jest.fn().mockReturnValue({ status: 'created', setting })
|
||||
|
||||
subscriptionSettingService = {} as jest.Mocked<SubscriptionSettingServiceInterface>
|
||||
subscriptionSettingService.createOrReplace = jest.fn().mockReturnValue({ status: 'created', setting })
|
||||
|
||||
settingProjector = {} as jest.Mocked<SettingProjector>
|
||||
settingProjector.projectSimple = jest.fn().mockReturnValue(settingProjection)
|
||||
|
||||
regularSubscription = {
|
||||
uuid: '1-2-3',
|
||||
subscriptionType: UserSubscriptionType.Regular,
|
||||
user: Promise.resolve(user),
|
||||
} as jest.Mocked<UserSubscription>
|
||||
|
||||
sharedSubscription = {
|
||||
uuid: '2-3-4',
|
||||
subscriptionType: UserSubscriptionType.Shared,
|
||||
user: Promise.resolve(user),
|
||||
} as jest.Mocked<UserSubscription>
|
||||
|
||||
userSubscriptionService = {} as jest.Mocked<UserSubscriptionServiceInterface>
|
||||
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
|
||||
.fn()
|
||||
.mockReturnValue({ regularSubscription: null, sharedSubscription: null })
|
||||
|
||||
subscriptionSettingProjector = {} as jest.Mocked<SubscriptionSettingProjector>
|
||||
subscriptionSettingProjector.projectSimple = jest.fn().mockReturnValue({ foo: 'sub-bar' })
|
||||
|
||||
user = {} as jest.Mocked<User>
|
||||
|
||||
userRepository = {} as jest.Mocked<UserRepositoryInterface>
|
||||
@@ -57,124 +100,217 @@ describe('UpdateSetting', () => {
|
||||
logger.error = jest.fn()
|
||||
})
|
||||
|
||||
it('should create a setting', async () => {
|
||||
const props = {
|
||||
name: SettingName.ExtensionKey,
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: EncryptionVersion.Default,
|
||||
sensitive: false,
|
||||
}
|
||||
|
||||
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
|
||||
|
||||
expect(settingService.createOrReplace).toHaveBeenCalledWith({
|
||||
props: {
|
||||
name: 'EXTENSION_KEY',
|
||||
describe('no subscription', () => {
|
||||
it('should create a setting', async () => {
|
||||
const props = {
|
||||
name: SettingName.NAMES.ExtensionKey,
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: 1,
|
||||
serverEncryptionVersion: EncryptionVersion.Default,
|
||||
sensitive: false,
|
||||
},
|
||||
user,
|
||||
}
|
||||
|
||||
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
|
||||
|
||||
expect(settingService.createOrReplace).toHaveBeenCalledWith({
|
||||
props: {
|
||||
name: 'EXTENSION_KEY',
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: 1,
|
||||
sensitive: false,
|
||||
},
|
||||
user,
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
success: true,
|
||||
setting: settingProjection,
|
||||
statusCode: 201,
|
||||
})
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
success: true,
|
||||
setting: settingProjection,
|
||||
statusCode: 201,
|
||||
it('should not create a setting if user does not exist', async () => {
|
||||
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
const props = {
|
||||
name: SettingName.NAMES.ExtensionKey,
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
sensitive: false,
|
||||
}
|
||||
|
||||
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
|
||||
|
||||
expect(settingService.createOrReplace).not.toHaveBeenCalled()
|
||||
|
||||
expect(response).toEqual({
|
||||
success: false,
|
||||
error: {
|
||||
message: 'User 1-2-3 not found.',
|
||||
},
|
||||
statusCode: 404,
|
||||
})
|
||||
})
|
||||
|
||||
it('should not create a subscription setting', async () => {
|
||||
const props = {
|
||||
name: SettingName.NAMES.MuteSignInEmails,
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
sensitive: false,
|
||||
}
|
||||
|
||||
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
|
||||
|
||||
expect(settingService.createOrReplace).not.toHaveBeenCalled()
|
||||
|
||||
expect(response).toEqual({
|
||||
success: false,
|
||||
error: {
|
||||
message: 'User 1-2-3 has no subscription to change a subscription setting.',
|
||||
},
|
||||
statusCode: 401,
|
||||
})
|
||||
})
|
||||
|
||||
it('should not create a setting if the setting name is invalid', async () => {
|
||||
const props = {
|
||||
name: 'random-setting',
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
sensitive: false,
|
||||
}
|
||||
|
||||
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
|
||||
|
||||
expect(settingService.createOrReplace).not.toHaveBeenCalled()
|
||||
|
||||
expect(response).toEqual({
|
||||
success: false,
|
||||
error: {
|
||||
message: 'Invalid setting name: random-setting',
|
||||
},
|
||||
statusCode: 400,
|
||||
})
|
||||
})
|
||||
|
||||
it('should not create a setting if user is not permitted to', async () => {
|
||||
settingsAssociationService.getPermissionAssociatedWithSetting = jest
|
||||
.fn()
|
||||
.mockReturnValue(PermissionName.DailyEmailBackup)
|
||||
|
||||
roleService.userHasPermission = jest.fn().mockReturnValue(false)
|
||||
|
||||
const props = {
|
||||
name: SettingName.NAMES.ExtensionKey,
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
sensitive: false,
|
||||
}
|
||||
|
||||
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
|
||||
|
||||
expect(settingService.createOrReplace).not.toHaveBeenCalled()
|
||||
|
||||
expect(response).toEqual({
|
||||
success: false,
|
||||
error: {
|
||||
message: 'User 1-2-3 is not permitted to change the setting.',
|
||||
},
|
||||
statusCode: 401,
|
||||
})
|
||||
})
|
||||
|
||||
it('should not create a setting if setting is not mutable by the client', async () => {
|
||||
settingsAssociationService.isSettingMutableByClient = jest.fn().mockReturnValue(false)
|
||||
|
||||
const props = {
|
||||
name: SettingName.NAMES.ExtensionKey,
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
sensitive: false,
|
||||
}
|
||||
|
||||
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
|
||||
|
||||
expect(settingService.createOrReplace).not.toHaveBeenCalled()
|
||||
|
||||
expect(response).toEqual({
|
||||
success: false,
|
||||
error: {
|
||||
message: 'User 1-2-3 is not permitted to change the setting.',
|
||||
},
|
||||
statusCode: 401,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should not create a setting if user does not exist', async () => {
|
||||
userRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
||||
describe('regular subscription', () => {
|
||||
beforeEach(() => {
|
||||
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
|
||||
.fn()
|
||||
.mockReturnValue({ regularSubscription, sharedSubscription: null })
|
||||
})
|
||||
|
||||
const props = {
|
||||
name: SettingName.ExtensionKey,
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
sensitive: false,
|
||||
}
|
||||
it('should create a subscription setting', async () => {
|
||||
const props = {
|
||||
name: SettingName.NAMES.MuteSignInEmails,
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: EncryptionVersion.Default,
|
||||
sensitive: false,
|
||||
}
|
||||
|
||||
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
|
||||
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
|
||||
|
||||
expect(settingService.createOrReplace).not.toHaveBeenCalled()
|
||||
expect(subscriptionSettingService.createOrReplace).toHaveBeenCalledWith({
|
||||
props: {
|
||||
name: 'MUTE_SIGN_IN_EMAILS',
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: 1,
|
||||
sensitive: false,
|
||||
},
|
||||
userSubscription: regularSubscription,
|
||||
})
|
||||
|
||||
expect(response).toEqual({
|
||||
success: false,
|
||||
error: {
|
||||
message: 'User 1-2-3 not found.',
|
||||
},
|
||||
statusCode: 404,
|
||||
expect(response).toEqual({
|
||||
success: true,
|
||||
setting: { foo: 'sub-bar' },
|
||||
statusCode: 201,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should not create a setting if the setting name is invalid', async () => {
|
||||
const props = {
|
||||
name: 'random-setting',
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
sensitive: false,
|
||||
}
|
||||
|
||||
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
|
||||
|
||||
expect(settingService.createOrReplace).not.toHaveBeenCalled()
|
||||
|
||||
expect(response).toEqual({
|
||||
success: false,
|
||||
error: {
|
||||
message: 'Setting name random-setting is invalid.',
|
||||
},
|
||||
statusCode: 400,
|
||||
describe('shared subscription', () => {
|
||||
beforeEach(() => {
|
||||
userSubscriptionService.findRegularSubscriptionForUserUuid = jest
|
||||
.fn()
|
||||
.mockReturnValue({ regularSubscription, sharedSubscription })
|
||||
})
|
||||
})
|
||||
|
||||
it('should not create a setting if user is not permitted to', async () => {
|
||||
settingsAssociationService.getPermissionAssociatedWithSetting = jest
|
||||
.fn()
|
||||
.mockReturnValue(PermissionName.DailyEmailBackup)
|
||||
it('should create a subscription setting', async () => {
|
||||
const props = {
|
||||
name: SettingName.NAMES.MuteSignInEmails,
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: EncryptionVersion.Default,
|
||||
sensitive: false,
|
||||
}
|
||||
|
||||
roleService.userHasPermission = jest.fn().mockReturnValue(false)
|
||||
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
|
||||
|
||||
const props = {
|
||||
name: SettingName.ExtensionKey,
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
sensitive: false,
|
||||
}
|
||||
expect(subscriptionSettingService.createOrReplace).toHaveBeenCalledWith({
|
||||
props: {
|
||||
name: 'MUTE_SIGN_IN_EMAILS',
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: 1,
|
||||
sensitive: false,
|
||||
},
|
||||
userSubscription: sharedSubscription,
|
||||
})
|
||||
|
||||
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
|
||||
|
||||
expect(settingService.createOrReplace).not.toHaveBeenCalled()
|
||||
|
||||
expect(response).toEqual({
|
||||
success: false,
|
||||
error: {
|
||||
message: 'User 1-2-3 is not permitted to change the setting.',
|
||||
},
|
||||
statusCode: 401,
|
||||
})
|
||||
})
|
||||
|
||||
it('should not create a setting if setting is not mutable by the client', async () => {
|
||||
settingsAssociationService.isSettingMutableByClient = jest.fn().mockReturnValue(false)
|
||||
|
||||
const props = {
|
||||
name: SettingName.ExtensionKey,
|
||||
unencryptedValue: 'test-setting-value',
|
||||
serverEncryptionVersion: EncryptionVersion.Unencrypted,
|
||||
sensitive: false,
|
||||
}
|
||||
|
||||
const response = await createUseCase().execute({ props, userUuid: '1-2-3' })
|
||||
|
||||
expect(settingService.createOrReplace).not.toHaveBeenCalled()
|
||||
|
||||
expect(response).toEqual({
|
||||
success: false,
|
||||
error: {
|
||||
message: 'User 1-2-3 is not permitted to change the setting.',
|
||||
},
|
||||
statusCode: 401,
|
||||
expect(response).toEqual({
|
||||
success: true,
|
||||
setting: { foo: 'sub-bar' },
|
||||
statusCode: 201,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user