Skip to content

Commit

Permalink
feat(api): add listing workspace users
Browse files Browse the repository at this point in the history
  • Loading branch information
Karol Sójko committed Oct 11, 2022
1 parent 7a43e2d commit fb41f65
Show file tree
Hide file tree
Showing 30 changed files with 190 additions and 44 deletions.
Binary file not shown.
2 changes: 1 addition & 1 deletion packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"typescript": "*"
},
"dependencies": {
"@standardnotes/common": "^1.36.1",
"@standardnotes/common": "^1.39.0",
"@standardnotes/encryption": "workspace:*",
"@standardnotes/models": "workspace:*",
"@standardnotes/responses": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export enum WorkspaceApiOperations {
Inviting,
Accepting,
ListingWorkspaces,
ListingWorkspaceUsers,
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { WorkspaceType } from '@standardnotes/common'
import { WorkspaceAccessLevel, 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 { WorkspaceUserListResponse } from '../../Response/Workspace/WorkspaceUserListResponse'
import { WorkspaceServerInterface } from '../../Server/Workspace/WorkspaceServerInterface'

import { WorkspaceApiOperations } from './WorkspaceApiOperations'
Expand All @@ -29,6 +30,10 @@ describe('WorkspaceApiService', () => {
status: HttpStatusCode.Success,
data: { ownedWorkspaces: [], joinedWorkspaces: [] },
} as jest.Mocked<WorkspaceListResponse>)
workspaceServer.listWorkspaceUsers = jest.fn().mockReturnValue({
status: HttpStatusCode.Success,
data: { users: [] },
} as jest.Mocked<WorkspaceUserListResponse>)
})

it('should create a workspace', async () => {
Expand Down Expand Up @@ -97,7 +102,7 @@ describe('WorkspaceApiService', () => {
const response = await createService().inviteToWorkspace({
workspaceUuid: 'w-1-2-3',
inviteeEmail: 'test@test.te',
accessLevel: 'write-and-read',
accessLevel: WorkspaceAccessLevel.WriteAndRead,
})

expect(response).toEqual({
Expand All @@ -123,7 +128,7 @@ describe('WorkspaceApiService', () => {
await service.inviteToWorkspace({
workspaceUuid: 'w-1-2-3',
inviteeEmail: 'test@test.te',
accessLevel: 'write-and-read',
accessLevel: WorkspaceAccessLevel.WriteAndRead,
})
} catch (caughtError) {
error = caughtError
Expand All @@ -142,7 +147,7 @@ describe('WorkspaceApiService', () => {
await createService().inviteToWorkspace({
workspaceUuid: 'w-1-2-3',
inviteeEmail: 'test@test.te',
accessLevel: 'write-and-read',
accessLevel: WorkspaceAccessLevel.WriteAndRead,
})
} catch (caughtError) {
error = caughtError
Expand Down Expand Up @@ -256,4 +261,47 @@ describe('WorkspaceApiService', () => {

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

it('should list workspace users', async () => {
const response = await createService().listWorkspaceUsers({ workspaceUuid: 'w-1-2-3' })

expect(response).toEqual({
status: 200,
data: {
users: [],
},
})
expect(workspaceServer.listWorkspaceUsers).toHaveBeenCalledWith({ workspaceUuid: 'w-1-2-3' })
})

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

let error = null
try {
await service.listWorkspaceUsers({ workspaceUuid: 'w-1-2-3' })
} catch (caughtError) {
error = caughtError
}

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

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

let error = null
try {
await createService().listWorkspaceUsers({ workspaceUuid: 'w-1-2-3' })
} catch (caughtError) {
error = caughtError
}

expect(error).not.toBeNull()
})
})
19 changes: 17 additions & 2 deletions packages/api/src/Domain/Client/Workspace/WorkspaceApiService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Uuid, WorkspaceType } from '@standardnotes/common'
import { Uuid, WorkspaceAccessLevel, WorkspaceType } from '@standardnotes/common'

import { ErrorMessage } from '../../Error/ErrorMessage'
import { ApiCallError } from '../../Error/ApiCallError'
Expand All @@ -7,6 +7,7 @@ import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/W
import { WorkspaceInvitationResponse } from '../../Response/Workspace/WorkspaceInvitationResponse'
import { WorkspaceServerInterface } from '../../Server/Workspace/WorkspaceServerInterface'
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'
import { WorkspaceUserListResponse } from '../../Response/Workspace/WorkspaceUserListResponse'

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

async listWorkspaceUsers(dto: { workspaceUuid: string }): Promise<WorkspaceUserListResponse> {
this.lockOperation(WorkspaceApiOperations.ListingWorkspaceUsers)

try {
const response = await this.workspaceServer.listWorkspaceUsers({ workspaceUuid: dto.workspaceUuid })

this.unlockOperation(WorkspaceApiOperations.ListingWorkspaceUsers)

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

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

Expand Down Expand Up @@ -59,7 +74,7 @@ export class WorkspaceApiService implements WorkspaceApiServiceInterface {
async inviteToWorkspace(dto: {
inviteeEmail: string
workspaceUuid: Uuid
accessLevel: string
accessLevel: WorkspaceAccessLevel
}): Promise<WorkspaceInvitationResponse> {
this.lockOperation(WorkspaceApiOperations.Inviting)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Uuid, WorkspaceType } from '@standardnotes/common'
import { Uuid, WorkspaceAccessLevel, 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'
import { WorkspaceUserListResponse } from '../../Response/Workspace/WorkspaceUserListResponse'

export interface WorkspaceApiServiceInterface {
createWorkspace(dto: {
Expand All @@ -16,7 +17,7 @@ export interface WorkspaceApiServiceInterface {
inviteToWorkspace(dto: {
inviteeEmail: string
workspaceUuid: Uuid
accessLevel: string
accessLevel: WorkspaceAccessLevel
}): Promise<WorkspaceInvitationResponse>
acceptInvite(dto: {
inviteUuid: Uuid
Expand All @@ -25,4 +26,5 @@ export interface WorkspaceApiServiceInterface {
encryptedPrivateKey: string
}): Promise<WorkspaceInvitationAcceptingResponse>
listWorkspaces(): Promise<WorkspaceListResponse>
listWorkspaceUsers(dto: { workspaceUuid: Uuid }): Promise<WorkspaceUserListResponse>
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Uuid } from '@standardnotes/common'
import { Uuid, WorkspaceAccessLevel } from '@standardnotes/common'

export type WorkspaceInvitationRequestParams = {
workspaceUuid: Uuid
inviteeEmail: string
accessLevel: string
accessLevel: WorkspaceAccessLevel
[additionalParam: string]: unknown
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Uuid } from '@standardnotes/common'

export type WorkspaceUserListRequestParams = {
workspaceUuid: Uuid
[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 @@ -10,3 +10,4 @@ export * from './Workspace/WorkspaceCreationRequestParams'
export * from './Workspace/WorkspaceInvitationAcceptingRequestParams'
export * from './Workspace/WorkspaceInvitationRequestParams'
export * from './Workspace/WorkspaceListRequestParams'
export * from './Workspace/WorkspaceUserListRequestParams'
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 { WorkspaceUserListResponseBody } from './WorkspaceUserListResponseBody'

export interface WorkspaceUserListResponse extends HttpResponse {
data: Either<WorkspaceUserListResponseBody, HttpErrorResponseBody>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { WorkspaceUser } from '@standardnotes/models'

export type WorkspaceUserListResponseBody = {
users: Array<WorkspaceUser>
}
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 @@ -3,6 +3,7 @@ import { Uuid } from '@standardnotes/common'
const WorkspacePaths = {
createWorkspace: '/v1/workspaces',
listWorkspaces: '/v1/workspaces',
listWorkspaceUsers: (uuid: Uuid) => `/v1/workspaces/${uuid}/users`,
inviteToWorkspace: (uuid: Uuid) => `/v1/workspaces/${uuid}/invites`,
acceptInvite: (uuid: Uuid) => `/v1/invites/${uuid}/accept`,
}
Expand Down
19 changes: 17 additions & 2 deletions packages/api/src/Domain/Server/Workspace/WorkspaceServer.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { WorkspaceType } from '@standardnotes/common'
import { WorkspaceAccessLevel, WorkspaceType } from '@standardnotes/common'
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 { WorkspaceUserListResponse } from '../../Response/Workspace/WorkspaceUserListResponse'

import { WorkspaceServer } from './WorkspaceServer'

Expand Down Expand Up @@ -42,7 +43,7 @@ describe('WorkspaceServer', () => {
const response = await createServer().inviteToWorkspace({
inviteeEmail: 'test@test.te',
workspaceUuid: 'w-1-2-3',
accessLevel: 'write-and-read',
accessLevel: WorkspaceAccessLevel.WriteAndRead,
})

expect(response).toEqual({
Expand Down Expand Up @@ -84,4 +85,18 @@ describe('WorkspaceServer', () => {
data: { ownedWorkspaces: [], joinedWorkspaces: [] },
})
})

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

const response = await createServer().listWorkspaceUsers({ workspaceUuid: 'w-1-2-3' })

expect(response).toEqual({
status: 200,
data: { users: [] },
})
})
})
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 @@ -7,13 +7,21 @@ import { WorkspaceInvitationAcceptingRequestParams } from '../../Request/Workspa
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
import { WorkspaceListRequestParams } from '../../Request/Workspace/WorkspaceListRequestParams'
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'
import { WorkspaceUserListRequestParams } from '../../Request/Workspace/WorkspaceUserListRequestParams'
import { WorkspaceUserListResponse } from '../../Response/Workspace/WorkspaceUserListResponse'

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

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

async listWorkspaceUsers(params: WorkspaceUserListRequestParams): Promise<WorkspaceUserListResponse> {
const response = await this.httpService.get(Paths.v1.listWorkspaceUsers(params.workspaceUuid), params)

return response as WorkspaceUserListResponse
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import { WorkspaceInvitationAcceptingRequestParams } from '../../Request/Workspa
import { WorkspaceInvitationAcceptingResponse } from '../../Response/Workspace/WorkspaceInvitationAcceptingResponse'
import { WorkspaceListRequestParams } from '../../Request/Workspace/WorkspaceListRequestParams'
import { WorkspaceListResponse } from '../../Response/Workspace/WorkspaceListResponse'
import { WorkspaceUserListRequestParams } from '../../Request/Workspace/WorkspaceUserListRequestParams'
import { WorkspaceUserListResponse } from '../../Response/Workspace/WorkspaceUserListResponse'

export interface WorkspaceServerInterface {
createWorkspace(params: WorkspaceCreationRequestParams): Promise<WorkspaceCreationResponse>
listWorkspaces(params: WorkspaceListRequestParams): Promise<WorkspaceListResponse>
listWorkspaceUsers(params: WorkspaceUserListRequestParams): Promise<WorkspaceUserListResponse>
inviteToWorkspace(params: WorkspaceInvitationRequestParams): Promise<WorkspaceInvitationResponse>
acceptInvite(params: WorkspaceInvitationAcceptingRequestParams): Promise<WorkspaceInvitationAcceptingResponse>
}
2 changes: 1 addition & 1 deletion packages/encryption/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"typescript": "*"
},
"dependencies": {
"@standardnotes/common": "^1.36.1",
"@standardnotes/common": "^1.39.0",
"@standardnotes/models": "workspace:*",
"@standardnotes/responses": "workspace:*",
"@standardnotes/sncrypto-common": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion packages/features/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
},
"dependencies": {
"@standardnotes/auth": "^3.19.4",
"@standardnotes/common": "^1.36.1",
"@standardnotes/common": "^1.39.0",
"@standardnotes/security": "^1.2.0",
"reflect-metadata": "^0.1.13"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/filepicker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"ts-node": "^10.5.0"
},
"dependencies": {
"@standardnotes/common": "^1.36.1",
"@standardnotes/common": "^1.39.0",
"@standardnotes/files": "workspace:*",
"@standardnotes/utils": "workspace:*",
"@types/wicg-file-system-access": "^2020.9.5",
Expand Down
2 changes: 1 addition & 1 deletion packages/files/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"ts-jest": "^28.0.5"
},
"dependencies": {
"@standardnotes/common": "^1.36.1",
"@standardnotes/common": "^1.39.0",
"@standardnotes/encryption": "workspace:*",
"@standardnotes/models": "workspace:*",
"@standardnotes/responses": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion packages/models/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"typescript": "*"
},
"dependencies": {
"@standardnotes/common": "^1.36.1",
"@standardnotes/common": "^1.39.0",
"@standardnotes/features": "workspace:*",
"@standardnotes/responses": "workspace:*",
"@standardnotes/utils": "workspace:*",
Expand Down
16 changes: 16 additions & 0 deletions packages/models/src/Domain/Api/Workspace/WorkspaceUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Uuid, WorkspaceAccessLevel, WorkspaceUserStatus } from '@standardnotes/common'

export type WorkspaceUser = {
uuid: Uuid
accessLevel: WorkspaceAccessLevel
userUuid: Uuid
userDisplayName: string | null
workspaceUuid: Uuid
encryptedWorkspaceKey: string | null
publicKey: string | null
encryptedPrivateKey: string | null
status: WorkspaceUserStatus
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 @@ -29,6 +29,7 @@ export * from './Api/Subscription/InvitationStatus'
export * from './Api/Subscription/InviteeIdentifierType'
export * from './Api/Subscription/InviterIdentifierType'
export * from './Api/Workspace/Workspace'
export * from './Api/Workspace/WorkspaceUser'
export * from './Device/Environment'
export * from './Device/Platform'
export * from './Local/KeyParams/RootKeyParamsInterface'
Expand Down
2 changes: 1 addition & 1 deletion packages/responses/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"ts-jest": "^28.0.5"
},
"dependencies": {
"@standardnotes/common": "^1.36.1",
"@standardnotes/common": "^1.39.0",
"@standardnotes/features": "workspace:*",
"@standardnotes/security": "^1.1.0",
"reflect-metadata": "^0.1.13"
Expand Down
2 changes: 1 addition & 1 deletion packages/services/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"dependencies": {
"@standardnotes/api": "workspace:^",
"@standardnotes/auth": "^3.19.4",
"@standardnotes/common": "^1.36.1",
"@standardnotes/common": "^1.39.0",
"@standardnotes/encryption": "workspace:^",
"@standardnotes/files": "workspace:^",
"@standardnotes/models": "workspace:^",
Expand Down

0 comments on commit fb41f65

Please sign in to comment.