Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(snjs): add revisions api v2 #2154

Merged
merged 7 commits into from Jan 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,5 @@
export enum RevisionApiOperations {
List,
Delete,
Get,
}
79 changes: 79 additions & 0 deletions packages/api/src/Domain/Client/Revision/RevisionApiService.ts
@@ -0,0 +1,79 @@
import { ErrorMessage } from '../../Error/ErrorMessage'
import { ApiCallError } from '../../Error/ApiCallError'

import { RevisionApiServiceInterface } from './RevisionApiServiceInterface'
import { RevisionApiOperations } from './RevisionApiOperations'
import { RevisionServerInterface } from '../../Server'
import { DeleteRevisionResponse } from '../../Response/Revision/DeleteRevisionResponse'
import { GetRevisionResponse } from '../../Response/Revision/GetRevisionResponse'
import { ListRevisionsResponse } from '../../Response/Revision/ListRevisionsResponse'

export class RevisionApiService implements RevisionApiServiceInterface {
private operationsInProgress: Map<RevisionApiOperations, boolean>

constructor(private revisionServer: RevisionServerInterface) {
this.operationsInProgress = new Map()
}

async listRevisions(itemUuid: string): Promise<ListRevisionsResponse> {
if (this.operationsInProgress.get(RevisionApiOperations.List)) {
throw new ApiCallError(ErrorMessage.GenericInProgress)
}

this.operationsInProgress.set(RevisionApiOperations.List, true)

try {
const response = await this.revisionServer.listRevisions({
itemUuid,
})

return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericFail)
} finally {
this.operationsInProgress.set(RevisionApiOperations.List, false)
}
}

async getRevision(itemUuid: string, revisionUuid: string): Promise<GetRevisionResponse> {
if (this.operationsInProgress.get(RevisionApiOperations.Get)) {
throw new ApiCallError(ErrorMessage.GenericInProgress)
}

this.operationsInProgress.set(RevisionApiOperations.Get, true)

try {
const response = await this.revisionServer.getRevision({
itemUuid,
revisionUuid,
})

return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericFail)
} finally {
this.operationsInProgress.set(RevisionApiOperations.Get, false)
}
}

async deleteRevision(itemUuid: string, revisionUuid: string): Promise<DeleteRevisionResponse> {
if (this.operationsInProgress.get(RevisionApiOperations.Delete)) {
throw new ApiCallError(ErrorMessage.GenericInProgress)
}

this.operationsInProgress.set(RevisionApiOperations.Delete, true)

try {
const response = await this.revisionServer.deleteRevision({
itemUuid,
revisionUuid,
})

return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericFail)
} finally {
this.operationsInProgress.set(RevisionApiOperations.Delete, false)
}
}
}
@@ -0,0 +1,9 @@
import { DeleteRevisionResponse } from '../../Response/Revision/DeleteRevisionResponse'
import { GetRevisionResponse } from '../../Response/Revision/GetRevisionResponse'
import { ListRevisionsResponse } from '../../Response/Revision/ListRevisionsResponse'

export interface RevisionApiServiceInterface {
listRevisions(itemUuid: string): Promise<ListRevisionsResponse>
getRevision(itemUuid: string, revisionUuid: string): Promise<GetRevisionResponse>
deleteRevision(itemUuid: string, revisionUuid: string): Promise<DeleteRevisionResponse>
}
3 changes: 3 additions & 0 deletions packages/api/src/Domain/Client/index.ts
Expand Up @@ -4,6 +4,9 @@ export * from './Auth/AuthApiServiceInterface'
export * from './Authenticator/AuthenticatorApiOperations'
export * from './Authenticator/AuthenticatorApiService'
export * from './Authenticator/AuthenticatorApiServiceInterface'
export * from './Revision/RevisionApiOperations'
export * from './Revision/RevisionApiService'
export * from './Revision/RevisionApiServiceInterface'
export * from './Subscription/SubscriptionApiOperations'
export * from './Subscription/SubscriptionApiService'
export * from './Subscription/SubscriptionApiServiceInterface'
Expand Down
@@ -0,0 +1,4 @@
export interface DeleteRevisionRequestParams {
itemUuid: string
revisionUuid: string
}
@@ -0,0 +1,4 @@
export interface GetRevisionRequestParams {
itemUuid: string
revisionUuid: string
}
@@ -0,0 +1,3 @@
export interface ListRevisionsRequestParams {
itemUuid: string
}
3 changes: 3 additions & 0 deletions packages/api/src/Domain/Request/index.ts
Expand Up @@ -5,6 +5,9 @@ export * from './Authenticator/VerifyAuthenticatorAuthenticationResponseRequestP
export * from './Authenticator/VerifyAuthenticatorRegistrationResponseRequestParams'
export * from './Recovery/RecoveryKeyParamsRequestParams'
export * from './Recovery/SignInWithRecoveryCodesRequestParams'
export * from './Revision/DeleteRevisionRequestParams'
export * from './Revision/GetRevisionRequestParams'
export * from './Revision/ListRevisionsRequestParams'
export * from './Subscription/AppleIAPConfirmRequestParams'
export * from './Subscription/SubscriptionInviteAcceptRequestParams'
export * from './Subscription/SubscriptionInviteCancelRequestParams'
Expand Down
@@ -0,0 +1,10 @@
import { Either } from '@standardnotes/common'

import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
import { HttpResponse } from '../../Http/HttpResponse'

import { DeleteRevisionResponseBody } from './DeleteRevisionResponseBody'

export interface DeleteRevisionResponse extends HttpResponse {
data: Either<DeleteRevisionResponseBody, HttpErrorResponseBody>
}
@@ -0,0 +1,3 @@
export interface DeleteRevisionResponseBody {
message: string
}
10 changes: 10 additions & 0 deletions packages/api/src/Domain/Response/Revision/GetRevisionResponse.ts
@@ -0,0 +1,10 @@
import { Either } from '@standardnotes/common'

import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
import { HttpResponse } from '../../Http/HttpResponse'

import { GetRevisionResponseBody } from './GetRevisionResponseBody'

export interface GetRevisionResponse extends HttpResponse {
data: Either<GetRevisionResponseBody, HttpErrorResponseBody>
}
@@ -0,0 +1,13 @@
export interface GetRevisionResponseBody {
revision: {
uuid: string
item_uuid: string
content: string | null
content_type: string
items_key_id: string | null
enc_item_key: string | null
auth_hash: string | null
created_at: string
updated_at: string
}
}
10 changes: 10 additions & 0 deletions packages/api/src/Domain/Response/Revision/ListRevisionsResponse.ts
@@ -0,0 +1,10 @@
import { Either } from '@standardnotes/common'

import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
import { HttpResponse } from '../../Http/HttpResponse'

import { ListRevisionsResponseBody } from './ListRevisionsResponseBody'

export interface ListRevisionsResponse extends HttpResponse {
data: Either<ListRevisionsResponseBody, HttpErrorResponseBody>
}
@@ -0,0 +1,9 @@
export interface ListRevisionsResponseBody {
revisions: Array<{
uuid: string
content_type: string
created_at: string
updated_at: string
required_role: string
}>
}
6 changes: 6 additions & 0 deletions packages/api/src/Domain/Response/index.ts
Expand Up @@ -18,6 +18,12 @@ export * from './Recovery/RecoveryKeyParamsResponse'
export * from './Recovery/RecoveryKeyParamsResponseBody'
export * from './Recovery/SignInWithRecoveryCodesResponse'
export * from './Recovery/SignInWithRecoveryCodesResponseBody'
export * from './Recovery/GenerateRecoveryCodesResponse'
export * from './Recovery/GenerateRecoveryCodesResponseBody'
export * from './Recovery/RecoveryKeyParamsResponse'
export * from './Recovery/RecoveryKeyParamsResponseBody'
export * from './Recovery/SignInWithRecoveryCodesResponse'
export * from './Recovery/SignInWithRecoveryCodesResponseBody'
export * from './Subscription/AppleIAPConfirmResponse'
export * from './Subscription/AppleIAPConfirmResponseBody'
export * from './Subscription/SubscriptionInviteAcceptResponse'
Expand Down
11 changes: 11 additions & 0 deletions packages/api/src/Domain/Server/Revision/Paths.ts
@@ -0,0 +1,11 @@
const RevisionsPaths = {
listRevisions: (itemUuid: string) => `/v2/items/${itemUuid}/revisions`,
getRevision: (itemUuid: string, revisionUuid: string) => `/v2/items/${itemUuid}/revisions/${revisionUuid}`,
deleteRevision: (itemUuid: string, revisionUuid: string) => `/v2/items/${itemUuid}/revisions/${revisionUuid}`,
}

export const Paths = {
v2: {
...RevisionsPaths,
},
}
30 changes: 30 additions & 0 deletions packages/api/src/Domain/Server/Revision/RevisionServer.ts
@@ -0,0 +1,30 @@
import { HttpServiceInterface } from '../../Http/HttpServiceInterface'
import { DeleteRevisionRequestParams, GetRevisionRequestParams, ListRevisionsRequestParams } from '../../Request'
import { DeleteRevisionResponse } from '../../Response/Revision/DeleteRevisionResponse'
import { GetRevisionResponse } from '../../Response/Revision/GetRevisionResponse'
import { ListRevisionsResponse } from '../../Response/Revision/ListRevisionsResponse'

import { Paths } from './Paths'
import { RevisionServerInterface } from './RevisionServerInterface'

export class RevisionServer implements RevisionServerInterface {
constructor(private httpService: HttpServiceInterface) {}

async listRevisions(params: ListRevisionsRequestParams): Promise<ListRevisionsResponse> {
const response = await this.httpService.get(Paths.v2.listRevisions(params.itemUuid))

return response as ListRevisionsResponse
}

async getRevision(params: GetRevisionRequestParams): Promise<GetRevisionResponse> {
const response = await this.httpService.get(Paths.v2.getRevision(params.itemUuid, params.revisionUuid))

return response as GetRevisionResponse
}

async deleteRevision(params: DeleteRevisionRequestParams): Promise<DeleteRevisionResponse> {
const response = await this.httpService.delete(Paths.v2.deleteRevision(params.itemUuid, params.revisionUuid))

return response as DeleteRevisionResponse
}
}
12 changes: 12 additions & 0 deletions packages/api/src/Domain/Server/Revision/RevisionServerInterface.ts
@@ -0,0 +1,12 @@
import { DeleteRevisionRequestParams } from '../../Request/Revision/DeleteRevisionRequestParams'
import { GetRevisionRequestParams } from '../../Request/Revision/GetRevisionRequestParams'
import { ListRevisionsRequestParams } from '../../Request/Revision/ListRevisionsRequestParams'
import { DeleteRevisionResponse } from '../../Response/Revision/DeleteRevisionResponse'
import { GetRevisionResponse } from '../../Response/Revision/GetRevisionResponse'
import { ListRevisionsResponse } from '../../Response/Revision/ListRevisionsResponse'

export interface RevisionServerInterface {
listRevisions(params: ListRevisionsRequestParams): Promise<ListRevisionsResponse>
getRevision(params: GetRevisionRequestParams): Promise<GetRevisionResponse>
deleteRevision(params: DeleteRevisionRequestParams): Promise<DeleteRevisionResponse>
}
2 changes: 2 additions & 0 deletions packages/api/src/Domain/Server/index.ts
Expand Up @@ -2,6 +2,8 @@ export * from './Auth/AuthServer'
export * from './Auth/AuthServerInterface'
export * from './Authenticator/AuthenticatorServer'
export * from './Authenticator/AuthenticatorServerInterface'
export * from './Revision/RevisionServer'
export * from './Revision/RevisionServerInterface'
export * from './Subscription/SubscriptionServer'
export * from './Subscription/SubscriptionServerInterface'
export * from './User/UserServer'
Expand Down
Expand Up @@ -12,6 +12,9 @@ import { ClientDisplayableError } from '@standardnotes/responses'
import { SNRootKeyParams } from '../../Keys/RootKey/RootKeyParams'
import { KeyedDecryptionSplit } from '../../Split/KeyedDecryptionSplit'
import { KeyedEncryptionSplit } from '../../Split/KeyedEncryptionSplit'
import { ItemAuthenticatedData } from '../../Types/ItemAuthenticatedData'
import { LegacyAttachedData } from '../../Types/LegacyAttachedData'
import { RootKeyEncryptedAuthenticatedData } from '../../Types/RootKeyEncryptedAuthenticatedData'

export interface EncryptionProviderInterface {
encryptSplitSingle(split: KeyedEncryptionSplit): Promise<EncryptedPayloadInterface>
Expand Down Expand Up @@ -67,4 +70,7 @@ export interface EncryptionProviderInterface {
reencryptItemsKeys(): Promise<void>
getSureDefaultItemsKey(): ItemsKeyInterface
getRootKeyParams(): Promise<SNRootKeyParams | undefined>
getEmbeddedPayloadAuthenticatedData(
payload: EncryptedPayloadInterface,
): RootKeyEncryptedAuthenticatedData | ItemAuthenticatedData | LegacyAttachedData | undefined
}
10 changes: 0 additions & 10 deletions packages/responses/src/Domain/Item/RevisionListEntry.ts

This file was deleted.

4 changes: 0 additions & 4 deletions packages/responses/src/Domain/Item/RevisionListResponse.ts

This file was deleted.

15 changes: 0 additions & 15 deletions packages/responses/src/Domain/Item/SingleRevision.ts

This file was deleted.

6 changes: 0 additions & 6 deletions packages/responses/src/Domain/Item/SingleRevisionResponse.ts

This file was deleted.

4 changes: 0 additions & 4 deletions packages/responses/src/Domain/index.ts
Expand Up @@ -34,11 +34,7 @@ export * from './Item/ConflictType'
export * from './Item/GetSingleItemResponse'
export * from './Item/RawSyncData'
export * from './Item/RawSyncResponse'
export * from './Item/RevisionListEntry'
export * from './Item/RevisionListResponse'
export * from './Item/ServerItemResponse'
export * from './Item/SingleRevision'
export * from './Item/SingleRevisionResponse'
export * from './Item/IntegrityPayload'
export * from './Listed/ActionResponse'
export * from './Listed/ListedAccount'
Expand Down
28 changes: 28 additions & 0 deletions packages/services/src/Domain/Revision/RevisionClientInterface.ts
@@ -0,0 +1,28 @@
import { Uuid } from '@standardnotes/domain-core'

export interface RevisionClientInterface {
listRevisions(itemUuid: Uuid): Promise<
Array<{
uuid: string
content_type: string
created_at: string
updated_at: string
required_role: string
}>
>
deleteRevision(itemUuid: Uuid, revisionUuid: Uuid): Promise<string>
getRevision(
itemUuid: Uuid,
revisionUuid: Uuid,
): Promise<{
uuid: string
item_uuid: string
content: string | null
content_type: string
items_key_id: string | null
enc_item_key: string | null
auth_hash: string | null
created_at: string
updated_at: string
} | null>
}