refactor: http service (#2233)
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>>
|
||||
}
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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>>
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
|
||||
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user