feat(api): add websocket api definitions
This commit is contained in:
@@ -10,10 +10,10 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
coverageThreshold: {
|
coverageThreshold: {
|
||||||
global: {
|
global: {
|
||||||
branches: 17,
|
branches: 20,
|
||||||
functions: 43,
|
functions: 66,
|
||||||
lines: 46,
|
lines: 63,
|
||||||
statements: 46
|
statements: 63
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,7 +32,8 @@
|
|||||||
"eslint": "^8.23.0",
|
"eslint": "^8.23.0",
|
||||||
"eslint-plugin-prettier": "*",
|
"eslint-plugin-prettier": "*",
|
||||||
"jest": "^28.1.2",
|
"jest": "^28.1.2",
|
||||||
"ts-jest": "^28.0.5"
|
"ts-jest": "^28.0.5",
|
||||||
|
"typescript": "*"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@standardnotes/common": "^1.32.0",
|
"@standardnotes/common": "^1.32.0",
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export enum WebSocketApiOperations {
|
||||||
|
CreatingConnectionToken,
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { WebSocketConnectionTokenResponse } from '../../Response'
|
||||||
|
|
||||||
|
import { WebSocketServerInterface } from '../../Server/WebSocket/WebSocketServerInterface'
|
||||||
|
import { WebSocketApiOperations } from './WebSocketApiOperations'
|
||||||
|
|
||||||
|
import { WebSocketApiService } from './WebSocketApiService'
|
||||||
|
|
||||||
|
describe('WebSocketApiService', () => {
|
||||||
|
let webSocketServer: WebSocketServerInterface
|
||||||
|
|
||||||
|
const createService = () => new WebSocketApiService(webSocketServer)
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
webSocketServer = {} as jest.Mocked<WebSocketServerInterface>
|
||||||
|
webSocketServer.createConnectionToken = jest.fn().mockReturnValue({
|
||||||
|
data: { token: 'foobar' },
|
||||||
|
} as jest.Mocked<WebSocketConnectionTokenResponse>)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create a websocket connection token', async () => {
|
||||||
|
const response = await createService().createConnectionToken()
|
||||||
|
|
||||||
|
expect(response).toEqual({
|
||||||
|
data: {
|
||||||
|
token: 'foobar',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(webSocketServer.createConnectionToken).toHaveBeenCalledWith({})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not create a token if it is already creating', async () => {
|
||||||
|
const service = createService()
|
||||||
|
Object.defineProperty(service, 'operationsInProgress', {
|
||||||
|
get: () => new Map([[WebSocketApiOperations.CreatingConnectionToken, true]]),
|
||||||
|
})
|
||||||
|
|
||||||
|
let error = null
|
||||||
|
try {
|
||||||
|
await service.createConnectionToken()
|
||||||
|
} catch (caughtError) {
|
||||||
|
error = caughtError
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).not.toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not create a token if the server fails', async () => {
|
||||||
|
webSocketServer.createConnectionToken = jest.fn().mockImplementation(() => {
|
||||||
|
throw new Error('Oops')
|
||||||
|
})
|
||||||
|
|
||||||
|
let error = null
|
||||||
|
try {
|
||||||
|
await createService().createConnectionToken()
|
||||||
|
} catch (caughtError) {
|
||||||
|
error = caughtError
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(error).not.toBeNull()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { ErrorMessage } from '../../Error/ErrorMessage'
|
||||||
|
import { ApiCallError } from '../../Error/ApiCallError'
|
||||||
|
|
||||||
|
import { WebSocketApiServiceInterface } from './WebSocketApiServiceInterface'
|
||||||
|
import { WebSocketApiOperations } from './WebSocketApiOperations'
|
||||||
|
import { WebSocketServerInterface } from '../../Server'
|
||||||
|
import { WebSocketConnectionTokenResponse } from '../../Response'
|
||||||
|
|
||||||
|
export class WebSocketApiService implements WebSocketApiServiceInterface {
|
||||||
|
private operationsInProgress: Map<WebSocketApiOperations, boolean>
|
||||||
|
|
||||||
|
constructor(private webSocketServer: WebSocketServerInterface) {
|
||||||
|
this.operationsInProgress = new Map()
|
||||||
|
}
|
||||||
|
|
||||||
|
async createConnectionToken(): Promise<WebSocketConnectionTokenResponse> {
|
||||||
|
if (this.operationsInProgress.get(WebSocketApiOperations.CreatingConnectionToken)) {
|
||||||
|
throw new ApiCallError(ErrorMessage.GenericInProgress)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.operationsInProgress.set(WebSocketApiOperations.CreatingConnectionToken, true)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.webSocketServer.createConnectionToken({})
|
||||||
|
|
||||||
|
this.operationsInProgress.set(WebSocketApiOperations.CreatingConnectionToken, false)
|
||||||
|
|
||||||
|
return response
|
||||||
|
} catch (error) {
|
||||||
|
throw new ApiCallError(ErrorMessage.GenericFail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { WebSocketConnectionTokenResponse } from '../../Response'
|
||||||
|
|
||||||
|
export interface WebSocketApiServiceInterface {
|
||||||
|
createConnectionToken(): Promise<WebSocketConnectionTokenResponse>
|
||||||
|
}
|
||||||
@@ -3,3 +3,5 @@ export * from './Subscription/SubscriptionApiService'
|
|||||||
export * from './Subscription/SubscriptionApiServiceInterface'
|
export * from './Subscription/SubscriptionApiServiceInterface'
|
||||||
export * from './User/UserApiService'
|
export * from './User/UserApiService'
|
||||||
export * from './User/UserApiServiceInterface'
|
export * from './User/UserApiServiceInterface'
|
||||||
|
export * from './WebSocket/WebSocketApiService'
|
||||||
|
export * from './WebSocket/WebSocketApiServiceInterface'
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export type WebSocketConnectionTokenRequestParams = {
|
||||||
|
[additionalParam: string]: unknown
|
||||||
|
}
|
||||||
@@ -5,3 +5,4 @@ export * from './Subscription/SubscriptionInviteDeclineRequestParams'
|
|||||||
export * from './Subscription/SubscriptionInviteListRequestParams'
|
export * from './Subscription/SubscriptionInviteListRequestParams'
|
||||||
export * from './Subscription/SubscriptionInviteRequestParams'
|
export * from './Subscription/SubscriptionInviteRequestParams'
|
||||||
export * from './User/UserRegistrationRequestParams'
|
export * from './User/UserRegistrationRequestParams'
|
||||||
|
export * from './WebSocket/WebSocketConnectionTokenRequestParams'
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { Either } from '@standardnotes/common'
|
||||||
|
|
||||||
|
import { HttpErrorResponseBody } from '../../Http/HttpErrorResponseBody'
|
||||||
|
import { HttpResponse } from '../../Http/HttpResponse'
|
||||||
|
import { WebSocketConnectionTokenResponseBody } from './WebSocketConnectionTokenResponseBody'
|
||||||
|
|
||||||
|
export interface WebSocketConnectionTokenResponse extends HttpResponse {
|
||||||
|
data: Either<WebSocketConnectionTokenResponseBody, HttpErrorResponseBody>
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export type WebSocketConnectionTokenResponseBody = {
|
||||||
|
token: string
|
||||||
|
}
|
||||||
@@ -10,3 +10,5 @@ export * from './Subscription/SubscriptionInviteResponse'
|
|||||||
export * from './Subscription/SubscriptionInviteResponseBody'
|
export * from './Subscription/SubscriptionInviteResponseBody'
|
||||||
export * from './User/UserRegistrationResponse'
|
export * from './User/UserRegistrationResponse'
|
||||||
export * from './User/UserRegistrationResponseBody'
|
export * from './User/UserRegistrationResponseBody'
|
||||||
|
export * from './WebSocket/WebSocketConnectionTokenResponse'
|
||||||
|
export * from './WebSocket/WebSocketConnectionTokenResponseBody'
|
||||||
|
|||||||
9
packages/api/src/Domain/Server/WebSocket/Paths.ts
Normal file
9
packages/api/src/Domain/Server/WebSocket/Paths.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const TokenPaths = {
|
||||||
|
createConnectionToken: '/v1/sockets/tokens',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Paths = {
|
||||||
|
v1: {
|
||||||
|
...TokenPaths,
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { HttpServiceInterface } from '../../Http'
|
||||||
|
import { WebSocketConnectionTokenResponse } from '../../Response'
|
||||||
|
|
||||||
|
import { WebSocketServer } from './WebSocketServer'
|
||||||
|
|
||||||
|
describe('WebSocketServer', () => {
|
||||||
|
let httpService: HttpServiceInterface
|
||||||
|
|
||||||
|
const createServer = () => new WebSocketServer(httpService)
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
httpService = {} as jest.Mocked<HttpServiceInterface>
|
||||||
|
httpService.post = jest.fn().mockReturnValue({
|
||||||
|
data: { token: 'foobar' },
|
||||||
|
} as jest.Mocked<WebSocketConnectionTokenResponse>)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create a websocket connection token', async () => {
|
||||||
|
const response = await createServer().createConnectionToken({})
|
||||||
|
|
||||||
|
expect(response).toEqual({
|
||||||
|
data: {
|
||||||
|
token: 'foobar',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
17
packages/api/src/Domain/Server/WebSocket/WebSocketServer.ts
Normal file
17
packages/api/src/Domain/Server/WebSocket/WebSocketServer.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { HttpServiceInterface } from '../../Http/HttpServiceInterface'
|
||||||
|
import { WebSocketConnectionTokenRequestParams } from '../../Request/WebSocket/WebSocketConnectionTokenRequestParams'
|
||||||
|
import { WebSocketConnectionTokenResponse } from '../../Response/WebSocket/WebSocketConnectionTokenResponse'
|
||||||
|
import { Paths } from './Paths'
|
||||||
|
import { WebSocketServerInterface } from './WebSocketServerInterface'
|
||||||
|
|
||||||
|
export class WebSocketServer implements WebSocketServerInterface {
|
||||||
|
constructor(private httpService: HttpServiceInterface) {}
|
||||||
|
|
||||||
|
async createConnectionToken(
|
||||||
|
params: WebSocketConnectionTokenRequestParams,
|
||||||
|
): Promise<WebSocketConnectionTokenResponse> {
|
||||||
|
const response = await this.httpService.post(Paths.v1.createConnectionToken, params)
|
||||||
|
|
||||||
|
return response as WebSocketConnectionTokenResponse
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { WebSocketConnectionTokenRequestParams } from '../../Request/WebSocket/WebSocketConnectionTokenRequestParams'
|
||||||
|
import { WebSocketConnectionTokenResponse } from '../../Response/WebSocket/WebSocketConnectionTokenResponse'
|
||||||
|
|
||||||
|
export interface WebSocketServerInterface {
|
||||||
|
createConnectionToken(params: WebSocketConnectionTokenRequestParams): Promise<WebSocketConnectionTokenResponse>
|
||||||
|
}
|
||||||
@@ -2,3 +2,5 @@ export * from './Subscription/SubscriptionServer'
|
|||||||
export * from './Subscription/SubscriptionServerInterface'
|
export * from './Subscription/SubscriptionServerInterface'
|
||||||
export * from './User/UserServer'
|
export * from './User/UserServer'
|
||||||
export * from './User/UserServerInterface'
|
export * from './User/UserServerInterface'
|
||||||
|
export * from './WebSocket/WebSocketServer'
|
||||||
|
export * from './WebSocket/WebSocketServerInterface'
|
||||||
|
|||||||
Reference in New Issue
Block a user