Skip to content

Commit

Permalink
feat(api): add listing workspaces
Browse files Browse the repository at this point in the history
  • Loading branch information
Karol Sójko committed Oct 11, 2022
1 parent 15e2c82 commit 8376381
Show file tree
Hide file tree
Showing 17 changed files with 145 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export enum WorkspaceApiOperations {
Creating,
Inviting,
Accepting,
ListingWorkspaces,
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { WorkspaceType } from '@standardnotes/common'
import { HttpStatusCode } from '../../Http'
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceInvitationResponse'
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'
import { WorkspaceServerInterface } from '../../Server/Workspace/WorkspaceServerInterface'

import { WorkspaceApiOperations } from './WorkspaceApiOperations'
Expand All @@ -23,6 +25,10 @@ describe('WorkspaceApiService', () => {
workspaceServer.acceptInvite = jest.fn().mockReturnValue({
data: { success: true },
} as jest.Mocked<WorkspaceInvitationAcceptingResponse>)
workspaceServer.listWorkspaces = jest.fn().mockReturnValue({
status: HttpStatusCode.Success,
data: { ownedWorkspaces: [], joinedWorkspaces: [] },
} as jest.Mocked<WorkspaceListResponse>)
})

it('should create a workspace', async () => {
Expand Down Expand Up @@ -202,4 +208,48 @@ describe('WorkspaceApiService', () => {

expect(error).not.toBeNull()
})

it('should list workspaces', async () => {
const response = await createService().listWorkspaces()

expect(response).toEqual({
status: 200,
data: {
ownedWorkspaces: [],
joinedWorkspaces: [],
},
})
expect(workspaceServer.listWorkspaces).toHaveBeenCalled()
})

it('should not list workspaces if it is already listing them', async () => {
const service = createService()
Object.defineProperty(service, 'operationsInProgress', {
get: () => new Map([[WorkspaceApiOperations.ListingWorkspaces, true]]),
})

let error = null
try {
await service.listWorkspaces()
} catch (caughtError) {
error = caughtError
}

expect(error).not.toBeNull()
})

it('should not list workspaces if the server fails', async () => {
workspaceServer.listWorkspaces = jest.fn().mockImplementation(() => {
throw new Error('Oops')
})

let error = null
try {
await createService().listWorkspaces()
} catch (caughtError) {
error = caughtError
}

expect(error).not.toBeNull()
})
})
15 changes: 15 additions & 0 deletions packages/api/src/Domain/Client/Workspace/WorkspaceApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCre
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceInvitationResponse'
import { WorkspaceServerInterface } from '../../Server/Workspace/WorkspaceServerInterface'
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'

import { WorkspaceApiServiceInterface } from './WorkspaceApiServiceInterface'
import { WorkspaceApiOperations } from './WorkspaceApiOperations'
Expand All @@ -17,6 +18,20 @@ export class WorkspaceApiService implements WorkspaceApiServiceInterface {
this.operationsInProgress = new Map()
}

async listWorkspaces(): Promise<WorkspaceListResponse> {
this.lockOperation(WorkspaceApiOperations.ListingWorkspaces)

try {
const response = await this.workspaceServer.listWorkspaces({})

this.unlockOperation(WorkspaceApiOperations.ListingWorkspaces)

return response
} catch (error) {
throw new ApiCallError(ErrorMessage.GenericFail)
}
}

async acceptInvite(dto: {
inviteUuid: string
userUuid: string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Uuid, WorkspaceType } from '@standardnotes/common'
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceInvitationResponse'
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'

export interface WorkspaceApiServiceInterface {
createWorkspace(dto: {
Expand All @@ -19,4 +20,5 @@ export interface WorkspaceApiServiceInterface {
publicKey: string
encryptedPrivateKey: string
}): Promise<WorkspaceInvitationAcceptingResponse>
listWorkspaces(): Promise<WorkspaceListResponse>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type WorkspaceListRequestParams = {
[additionalParam: string]: unknown
}
1 change: 1 addition & 0 deletions packages/api/src/Domain/Request/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export * from './WebSocket/WebSocketConnectionTokenRequestParams'
export * from './Workspace/WorkspaceCreationRequestParams'
export * from './Workspace/WorkspaceInvitationAcceptingRequestParams'
export * from './Workspace/WorkspaceInvitationRequestParams'
export * from './Workspace/WorkspaceListRequestParams'
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Either } from '@standardnotes/common'

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

export interface WorkspaceListResponse extends HttpResponse {
data: Either<WorkspaceListResponseBody, HttpErrorResponseBody>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Workspace } from '@standardnotes/models'

export type WorkspaceListResponseBody = {
ownedWorkspaces: Array<Workspace>
joinedWorkspaces: Array<Workspace>
}
2 changes: 2 additions & 0 deletions packages/api/src/Domain/Response/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ export * from './Workspace/WorkspaceInvitationAcceptingResponse'
export * from './Workspace/WorkspaceInvitationAcceptingResponseBody'
export * from './Workspace/WorkspaceInvitationResponse'
export * from './Workspace/WorkspaceInvitationResponseBody'
export * from './Workspace/WorkspaceListResponse'
export * from './Workspace/WorkspaceListResponseBody'
1 change: 1 addition & 0 deletions packages/api/src/Domain/Server/Workspace/Paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Uuid } from '@standardnotes/common'

const WorkspacePaths = {
createWorkspace: '/v1/workspaces',
listWorkspaces: '/v1/workspaces',
inviteToWorkspace: (uuid: Uuid) => `/v1/workspaces/${uuid}/invites`,
acceptInvite: (uuid: Uuid) => `/v1/invites/${uuid}/accept`,
}
Expand Down
17 changes: 16 additions & 1 deletion packages/api/src/Domain/Server/Workspace/WorkspaceServer.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { WorkspaceType } from '@standardnotes/common'
import { HttpServiceInterface } from '../../Http'
import { HttpServiceInterface, HttpStatusCode } from '../../Http'
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceInvitationResponse'
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'

import { WorkspaceServer } from './WorkspaceServer'

Expand Down Expand Up @@ -68,4 +69,18 @@ describe('WorkspaceServer', () => {
},
})
})

it('should list workspaces', async () => {
httpService.get = jest.fn().mockReturnValue({
status: HttpStatusCode.Success,
data: { ownedWorkspaces: [], joinedWorkspaces: [] },
} as jest.Mocked<WorkspaceListResponse>)

const response = await createServer().listWorkspaces({})

expect(response).toEqual({
status: 200,
data: { ownedWorkspaces: [], joinedWorkspaces: [] },
})
})
})
8 changes: 8 additions & 0 deletions packages/api/src/Domain/Server/Workspace/WorkspaceServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@ import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceI
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
import { WorkspaceInvitationAcceptingRequestParams } from '../../Request/Workspace/WorkspaceInvitationAcceptingRequestParams'
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
import { WorkspaceListRequestParams } from '../../Request/Workspace/WorkspaceListRequestParams'
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'

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

export class WorkspaceServer implements WorkspaceServerInterface {
constructor(private httpService: HttpServiceInterface) {}

async listWorkspaces(params: WorkspaceListRequestParams): Promise<WorkspaceListResponse> {
const response = await this.httpService.get(Paths.v1.listWorkspaces, params)

return response as WorkspaceListResponse
}

async acceptInvite(params: WorkspaceInvitationAcceptingRequestParams): Promise<WorkspaceInvitationAcceptingResponse> {
const response = await this.httpService.post(Paths.v1.acceptInvite(params.inviteUuid), params)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceI
import { WorkspaceCreationResponse } from '../../Response/Workspace/WorkspaceCreationResponse'
import { WorkspaceInvitationAcceptingRequestParams } from '../../Request/Workspace/WorkspaceInvitationAcceptingRequestParams'
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
import { WorkspaceListRequestParams } from '../../Request/Workspace/WorkspaceListRequestParams'
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'

export interface WorkspaceServerInterface {
createWorkspace(params: WorkspaceCreationRequestParams): Promise<WorkspaceCreationResponse>
listWorkspaces(params: WorkspaceListRequestParams): Promise<WorkspaceListResponse>
inviteToWorkspace(params: WorkspaceInvitationRequestParams): Promise<WorkspaceInvitationResponse>
acceptInvite(params: WorkspaceInvitationAcceptingRequestParams): Promise<WorkspaceInvitationAcceptingResponse>
}
10 changes: 10 additions & 0 deletions packages/models/src/Domain/Api/Workspace/Workspace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { WorkspaceType } from '@standardnotes/common'

export type Workspace = {
uuid: string
type: WorkspaceType
name: string | null
keyRotationIndex: number
createdAt: number
updatedAt: number
}
1 change: 1 addition & 0 deletions packages/models/src/Domain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export * from './Api/Subscription/Invitation'
export * from './Api/Subscription/InvitationStatus'
export * from './Api/Subscription/InviteeIdentifierType'
export * from './Api/Subscription/InviterIdentifierType'
export * from './Api/Workspace/Workspace'
export * from './Device/Environment'
export * from './Device/Platform'
export * from './Local/KeyParams/RootKeyParamsInterface'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Uuid, WorkspaceType } from '@standardnotes/common'
import { Workspace } from '@standardnotes/models'

export interface WorkspaceClientInterface {
createWorkspace(dto: {
Expand All @@ -15,4 +16,5 @@ export interface WorkspaceClientInterface {
publicKey: string
encryptedPrivateKey: string
}): Promise<{ success: boolean }>
listWorkspaces(): Promise<{ ownedWorkspaces: Array<Workspace>; joinedWorkspaces: Array<Workspace> }>
}
15 changes: 15 additions & 0 deletions packages/services/src/Domain/Workspace/WorkspaceManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { WorkspaceApiServiceInterface } from '@standardnotes/api'
import { Uuid, WorkspaceType } from '@standardnotes/common'
import { Workspace } from '@standardnotes/models'

import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
import { AbstractService } from '../Service/AbstractService'
Expand All @@ -13,6 +14,20 @@ export class WorkspaceManager extends AbstractService implements WorkspaceClient
super(internalEventBus)
}

async listWorkspaces(): Promise<{ ownedWorkspaces: Workspace[]; joinedWorkspaces: Workspace[] }> {
try {
const result = await this.workspaceApiService.listWorkspaces()

if (result.data.error !== undefined) {
return { ownedWorkspaces: [], joinedWorkspaces: [] }
}

return result.data
} catch (error) {
return { ownedWorkspaces: [], joinedWorkspaces: [] }
}
}

async acceptInvite(dto: {
inviteUuid: string
userUuid: string
Expand Down

0 comments on commit 8376381

Please sign in to comment.