refactor: http service (#2233)

This commit is contained in:
Mo
2023-02-28 20:43:25 -06:00
committed by GitHub
parent 6e7618b258
commit e7f1d35341
142 changed files with 1116 additions and 1307 deletions

View File

@@ -1,6 +1,6 @@
import { AuthApiServiceInterface } from '@standardnotes/api'
import { AnyKeyParamsContent } from '@standardnotes/common'
import { SessionBody } from '@standardnotes/responses'
import { isErrorResponse, SessionBody } from '@standardnotes/responses'
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
import { AbstractService } from '../Service/AbstractService'
@@ -18,7 +18,7 @@ export class AuthManager extends AbstractService implements AuthClientInterface
try {
const result = await this.authApiService.generateRecoveryCodes()
if (result.data.error) {
if (isErrorResponse(result)) {
return false
}
@@ -35,8 +35,7 @@ export class AuthManager extends AbstractService implements AuthClientInterface
}): Promise<AnyKeyParamsContent | false> {
try {
const result = await this.authApiService.recoveryKeyParams(dto)
if (result.data.error) {
if (isErrorResponse(result)) {
return false
}
@@ -66,7 +65,7 @@ export class AuthManager extends AbstractService implements AuthClientInterface
try {
const result = await this.authApiService.signInWithRecoveryCodes(dto)
if (result.data.error) {
if (isErrorResponse(result)) {
return false
}

View File

@@ -2,6 +2,7 @@
import { AuthenticatorApiServiceInterface } from '@standardnotes/api'
import { Username, Uuid } from '@standardnotes/domain-core'
import { isErrorResponse } from '@standardnotes/responses'
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
import { AbstractService } from '../Service/AbstractService'
@@ -19,7 +20,7 @@ export class AuthenticatorManager extends AbstractService implements Authenticat
try {
const result = await this.authenticatorApiService.list()
if (result.data.error) {
if (isErrorResponse(result)) {
return []
}
@@ -32,8 +33,7 @@ export class AuthenticatorManager extends AbstractService implements Authenticat
async delete(authenticatorId: Uuid): Promise<boolean> {
try {
const result = await this.authenticatorApiService.delete(authenticatorId.value)
if (result.data.error) {
if (isErrorResponse(result)) {
return false
}
@@ -47,7 +47,7 @@ export class AuthenticatorManager extends AbstractService implements Authenticat
try {
const result = await this.authenticatorApiService.generateRegistrationOptions()
if (result.data.error) {
if (isErrorResponse(result)) {
return null
}
@@ -69,7 +69,7 @@ export class AuthenticatorManager extends AbstractService implements Authenticat
registrationCredential,
)
if (result.data.error) {
if (isErrorResponse(result)) {
return false
}
@@ -83,7 +83,7 @@ export class AuthenticatorManager extends AbstractService implements Authenticat
try {
const result = await this.authenticatorApiService.generateAuthenticationOptions(username.value)
if (result.data.error) {
if (isErrorResponse(result)) {
return null
}

View File

@@ -154,7 +154,7 @@ describe('fileService', () => {
const alertMock = (alertService.confirm = jest.fn().mockReturnValue(true))
const deleteItemMock = (itemManager.setItemToBeDeleted = jest.fn())
apiService.deleteFile = jest.fn().mockReturnValue({ error: true })
apiService.deleteFile = jest.fn().mockReturnValue({ data: { error: true } })
await fileService.deleteFile(file)

View File

@@ -1,4 +1,4 @@
import { ClientDisplayableError } from '@standardnotes/responses'
import { ClientDisplayableError, isErrorResponse } from '@standardnotes/responses'
import { ContentType } from '@standardnotes/common'
import {
FileItem,
@@ -101,7 +101,7 @@ export class FileService extends AbstractService implements FilesClientInterface
const uploadSessionStarted = await this.api.startUploadSession(tokenResult)
if (!uploadSessionStarted.uploadId) {
if (isErrorResponse(uploadSessionStarted) || !uploadSessionStarted.data.uploadId) {
return new ClientDisplayableError('Could not start upload session')
}
@@ -243,7 +243,7 @@ export class FileService extends AbstractService implements FilesClientInterface
const result = await this.api.deleteFile(tokenResult)
if (result.error) {
if (result.data?.error) {
const deleteAnyway = await this.alertService.confirm(
spaceSeparatedStrings(
'This file could not be deleted from the server, possibly because you are attempting to delete a file item',
@@ -257,7 +257,7 @@ export class FileService extends AbstractService implements FilesClientInterface
)
if (!deleteAnyway) {
return ClientDisplayableError.FromError(result.error)
return ClientDisplayableError.FromError(result.data?.error)
}
}

View File

@@ -1,5 +1,5 @@
import { CheckIntegrityResponse, IntegrityPayload } from '@standardnotes/responses'
import { CheckIntegrityResponse, IntegrityPayload, HttpResponse } from '@standardnotes/responses'
export interface IntegrityApiInterface {
checkIntegrity(integrityPayloads: IntegrityPayload[]): Promise<CheckIntegrityResponse>
checkIntegrity(integrityPayloads: IntegrityPayload[]): Promise<HttpResponse<CheckIntegrityResponse>>
}

View File

@@ -100,7 +100,9 @@ describe('IntegrityService', () => {
it('should not publish mismatches if checking integrity fails', async () => {
integrityApi.checkIntegrity = jest.fn().mockReturnValue({
error: 'Ooops',
data: {
error: 'Ooops',
},
})
await createService().handleEvent({
@@ -121,7 +123,9 @@ describe('IntegrityService', () => {
},
})
itemApi.getSingleItem = jest.fn().mockReturnValue({
error: 'Ooops',
data: {
error: 'Ooops',
},
})
await createService().handleEvent({

View File

@@ -2,7 +2,7 @@ import { IntegrityEvent } from './IntegrityEvent'
import { AbstractService } from '../Service/AbstractService'
import { ItemsServerInterface } from '../Item/ItemsServerInterface'
import { IntegrityApiInterface } from './IntegrityApiInterface'
import { GetSingleItemResponse } from '@standardnotes/responses'
import { GetSingleItemResponse, HttpResponse, isErrorResponse, ServerItemResponse } from '@standardnotes/responses'
import { InternalEventHandlerInterface } from '../Internal/InternalEventHandlerInterface'
import { InternalEventInterface } from '../Internal/InternalEventInterface'
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
@@ -30,23 +30,31 @@ export class IntegrityService
}
const integrityCheckResponse = await this.integrityApi.checkIntegrity(this.payloadManager.integrityPayloads)
if (integrityCheckResponse.error !== undefined) {
this.log(`Could not obtain integrity check: ${integrityCheckResponse.error}`)
if (isErrorResponse(integrityCheckResponse)) {
this.log(`Could not obtain integrity check: ${integrityCheckResponse.data.error}`)
return
}
const serverItemResponsePromises: Promise<GetSingleItemResponse>[] = []
const serverItemResponsePromises: Promise<HttpResponse<GetSingleItemResponse>>[] = []
for (const mismatch of integrityCheckResponse.data.mismatches) {
serverItemResponsePromises.push(this.itemApi.getSingleItem(mismatch.uuid))
}
const serverItemResponses = await Promise.all(serverItemResponsePromises)
const rawPayloads = []
const rawPayloads: ServerItemResponse[] = []
for (const serverItemResponse of serverItemResponses) {
if (serverItemResponse.data === undefined || serverItemResponse.error || !('item' in serverItemResponse.data)) {
this.log(`Could not obtain item for integrity adjustments: ${serverItemResponse.error}`)
if (
serverItemResponse.data == undefined ||
isErrorResponse(serverItemResponse) ||
!('item' in serverItemResponse.data)
) {
this.log(
`Could not obtain item for integrity adjustments: ${
isErrorResponse(serverItemResponse) ? serverItemResponse.data.error : ''
}`,
)
continue
}

View File

@@ -1,5 +1,5 @@
import { GetSingleItemResponse } from '@standardnotes/responses'
import { GetSingleItemResponse, HttpResponse } from '@standardnotes/responses'
export interface ItemsServerInterface {
getSingleItem(itemUuid: string): Promise<GetSingleItemResponse>
getSingleItem(itemUuid: string): Promise<HttpResponse<GetSingleItemResponse>>
}

View File

@@ -1,5 +1,6 @@
import { RevisionApiServiceInterface } from '@standardnotes/api'
import { Uuid } from '@standardnotes/domain-core'
import { isErrorResponse } from '@standardnotes/responses'
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
import { AbstractService } from '../Service/AbstractService'
@@ -18,7 +19,7 @@ export class RevisionManager extends AbstractService implements RevisionClientIn
): Promise<{ uuid: string; content_type: string; created_at: string; updated_at: string; required_role: string }[]> {
const result = await this.revisionApiService.listRevisions(itemUuid.value)
if (result.data.error) {
if (isErrorResponse(result)) {
throw new Error(result.data.error.message)
}
@@ -28,7 +29,7 @@ export class RevisionManager extends AbstractService implements RevisionClientIn
async deleteRevision(itemUuid: Uuid, revisionUuid: Uuid): Promise<string> {
const result = await this.revisionApiService.deleteRevision(itemUuid.value, revisionUuid.value)
if (result.data.error) {
if (isErrorResponse(result)) {
throw new Error(result.data.error.message)
}
@@ -51,7 +52,7 @@ export class RevisionManager extends AbstractService implements RevisionClientIn
} | null> {
const result = await this.revisionApiService.getRevision(itemUuid.value, revisionUuid.value)
if (result.data.error) {
if (isErrorResponse(result)) {
throw new Error(result.data.error.message)
}

View File

@@ -1,9 +1,9 @@
import { HttpResponse, SignInResponse } from '@standardnotes/responses'
import { AnyKeyParamsContent } from '@standardnotes/common'
import { RootKeyInterface } from '@standardnotes/models'
import { HttpResponse } from '@standardnotes/responses'
export type SessionManagerResponse = {
response: HttpResponse
response: HttpResponse<SignInResponse>
rootKey?: RootKeyInterface
keyParams?: AnyKeyParamsContent
}

View File

@@ -2,7 +2,7 @@ import { UserRegistrationResponseBody } from '@standardnotes/api'
import { ProtocolVersion } from '@standardnotes/common'
import { SNRootKey } from '@standardnotes/encryption'
import { RootKeyInterface } from '@standardnotes/models'
import { ClientDisplayableError, HttpResponse, SessionBody, SignInResponse, User } from '@standardnotes/responses'
import { ClientDisplayableError, SessionBody, SignInResponse, User, HttpResponse } from '@standardnotes/responses'
import { Base64String } from '@standardnotes/sncrypto-common'
import { SessionManagerResponse } from './SessionManagerResponse'
@@ -25,7 +25,7 @@ export interface SessionsClientInterface {
email: string,
rootKey: RootKeyInterface,
ephemeral: boolean,
): Promise<SignInResponse | HttpResponse>
): Promise<HttpResponse<SignInResponse>>
signOut(): Promise<void>
changeCredentials(parameters: {
currentServerPassword: string

View File

@@ -4,6 +4,7 @@ import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface
import { AbstractService } from '../Service/AbstractService'
import { SubscriptionClientInterface } from './SubscriptionClientInterface'
import { AppleIAPReceipt } from './AppleIAPReceipt'
import { isErrorResponse } from '@standardnotes/responses'
export class SubscriptionManager extends AbstractService implements SubscriptionClientInterface {
constructor(
@@ -17,7 +18,7 @@ export class SubscriptionManager extends AbstractService implements Subscription
try {
const result = await this.subscriptionApiService.acceptInvite(inviteUuid)
if (result.data.error) {
if (isErrorResponse(result)) {
return { success: false, message: result.data.error.message }
}
@@ -29,9 +30,13 @@ export class SubscriptionManager extends AbstractService implements Subscription
async listSubscriptionInvitations(): Promise<Invitation[]> {
try {
const response = await this.subscriptionApiService.listInvites()
const result = await this.subscriptionApiService.listInvites()
return response.data.invitations ?? []
if (isErrorResponse(result)) {
return []
}
return result.data.invitations ?? []
} catch (error) {
return []
}
@@ -41,6 +46,10 @@ export class SubscriptionManager extends AbstractService implements Subscription
try {
const result = await this.subscriptionApiService.invite(inviteeEmail)
if (isErrorResponse(result)) {
return false
}
return result.data.success === true
} catch (error) {
return false
@@ -51,6 +60,10 @@ export class SubscriptionManager extends AbstractService implements Subscription
try {
const result = await this.subscriptionApiService.cancelInvite(inviteUuid)
if (isErrorResponse(result)) {
return false
}
return result.data.success === true
} catch (error) {
return false
@@ -67,7 +80,7 @@ export class SubscriptionManager extends AbstractService implements Subscription
subscription_token: subscriptionToken,
})
if (result.data.error) {
if (isErrorResponse(result)) {
return { success: false, message: result.data.error.message }
}

View File

@@ -1,6 +1,6 @@
import { Base64String } from '@standardnotes/sncrypto-common'
import { EncryptionProviderInterface, SNRootKey, SNRootKeyParams } from '@standardnotes/encryption'
import { HttpResponse, SignInResponse, User } from '@standardnotes/responses'
import { HttpResponse, SignInResponse, User, HttpError, isErrorResponse } from '@standardnotes/responses'
import { Either, KeyParamsOrigination, UserRequestType } from '@standardnotes/common'
import { UuidGenerator } from '@standardnotes/utils'
import { UserApiServiceInterface, UserRegistrationResponseBody } from '@standardnotes/api'
@@ -28,8 +28,7 @@ import { ProtectionsClientInterface } from '../Protection/ProtectionClientInterf
import { InternalEventHandlerInterface } from '../Internal/InternalEventHandlerInterface'
import { InternalEventInterface } from '../Internal/InternalEventInterface'
export type CredentialsChangeFunctionResponse = { error?: { message: string } }
export type AccountServiceResponse = HttpResponse
export type CredentialsChangeFunctionResponse = { error?: HttpError }
export enum AccountEvent {
SignedInOrRegistered = 'SignedInOrRegistered',
@@ -181,7 +180,7 @@ export class UserService
ephemeral = false,
mergeLocal = true,
awaitSync = false,
): Promise<AccountServiceResponse> {
): Promise<HttpResponse<SignInResponse>> {
if (this.protocolService.hasAccount()) {
throw Error('Tried to sign in when an account already exists.')
}
@@ -196,9 +195,9 @@ export class UserService
/** Prevent a timed sync from occuring while signing in. */
this.lockSyncing()
const result = await this.sessionManager.signIn(email, password, strict, ephemeral)
const { response } = await this.sessionManager.signIn(email, password, strict, ephemeral)
if (!result.response.error) {
if (!isErrorResponse(response)) {
const notifyingFunction = awaitSync ? this.notifyEventSync.bind(this) : this.notifyEvent.bind(this)
await notifyingFunction(AccountEvent.SignedInOrRegistered, {
payload: {
@@ -212,7 +211,7 @@ export class UserService
this.unlockSyncing()
}
return result.response
return response
} finally {
this.signingIn = false
}
@@ -237,7 +236,7 @@ export class UserService
const uuid = this.sessionManager.getSureUser().uuid
const response = await this.userApiService.deleteAccount(uuid)
if (response.data.error) {
if (isErrorResponse(response)) {
return {
error: true,
message: response.data.error.message,
@@ -261,7 +260,7 @@ export class UserService
requestType,
})
if (result.data.error) {
if (isErrorResponse(result)) {
return false
}
@@ -276,7 +275,7 @@ export class UserService
* for missing keys or storage values. Unlike regular sign in, this doesn't worry about
* performing one of marking all items as needing sync or deleting all local data.
*/
public async correctiveSignIn(rootKey: SNRootKey): Promise<HttpResponse | SignInResponse> {
public async correctiveSignIn(rootKey: SNRootKey): Promise<HttpResponse<SignInResponse>> {
this.lockSyncing()
const response = await this.sessionManager.bypassChecksAndSignInWithRootKey(
@@ -285,7 +284,7 @@ export class UserService
false,
)
if (!response.error) {
if (!isErrorResponse(response)) {
await this.notifyEvent(AccountEvent.SignedInOrRegistered, {
payload: {
mergeLocal: true,
@@ -583,7 +582,7 @@ export class UserService
this.lockSyncing()
/** Now, change the credentials on the server. Roll back on failure */
const result = await this.sessionManager.changeCredentials({
const { response } = await this.sessionManager.changeCredentials({
currentServerPassword: rootKeys.currentRootKey.serverPassword as string,
newRootKey: rootKeys.newRootKey,
wrappingKey,
@@ -592,29 +591,31 @@ export class UserService
this.unlockSyncing()
if (!result.response.error) {
const rollback = await this.protocolService.createNewItemsKeyWithRollback()
await this.protocolService.reencryptItemsKeys()
await this.syncService.sync({ awaitAll: true })
const defaultItemsKey = this.protocolService.getSureDefaultItemsKey()
const itemsKeyWasSynced = !defaultItemsKey.neverSynced
if (!itemsKeyWasSynced) {
await this.sessionManager.changeCredentials({
currentServerPassword: rootKeys.newRootKey.serverPassword as string,
newRootKey: rootKeys.currentRootKey,
wrappingKey,
})
await this.protocolService.reencryptItemsKeys()
await rollback()
await this.syncService.sync({ awaitAll: true })
return { error: Error(Messages.CredentialsChangeStrings.Failed) }
}
if (isErrorResponse(response)) {
return { error: Error(response.data.error?.message) }
}
return result.response
const rollback = await this.protocolService.createNewItemsKeyWithRollback()
await this.protocolService.reencryptItemsKeys()
await this.syncService.sync({ awaitAll: true })
const defaultItemsKey = this.protocolService.getSureDefaultItemsKey()
const itemsKeyWasSynced = !defaultItemsKey.neverSynced
if (!itemsKeyWasSynced) {
await this.sessionManager.changeCredentials({
currentServerPassword: rootKeys.newRootKey.serverPassword as string,
newRootKey: rootKeys.currentRootKey,
wrappingKey,
})
await this.protocolService.reencryptItemsKeys()
await rollback()
await this.syncService.sync({ awaitAll: true })
return { error: Error(Messages.CredentialsChangeStrings.Failed) }
}
return {}
}
private async recomputeRootKeysForCredentialChange(parameters: {