-
Notifications
You must be signed in to change notification settings - Fork 17
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
Bc 4256 integration tldraw #4361
Changes from 119 commits
2dda3e7
860e585
e8a29ec
b3c1b53
f39abca
29d597f
a97da82
ec32f16
047e870
a6410de
48fc70e
4b71c4a
a78fd9a
93ddafd
9a5bdb2
bb75a6a
1d6ae7a
aae1b3b
ab201ae
35b4941
3c78b47
4786e15
306d8b6
fd24b34
ed433be
ecd8a5f
73940db
25a559f
9695c5a
c6c075a
957c83e
2a37276
b88925b
bb3de3d
f564943
bf4a5fb
779a1bc
18681a7
c9bf0d8
f643ed0
b519dce
a69995e
9a80ae4
47bb9e2
427fd76
4670a6e
94c1096
147bac2
75594cb
23cd0ca
02785cf
91c5431
e735058
56c9aea
8e3d661
80f0b73
19b9796
62ff413
8d740f2
9b09f8a
10b40c2
f0214f1
fc77f7d
7aab343
7c1ca24
0318faa
f5faed5
b83e095
aca59e6
c59e0c9
e0658e7
728fd6b
4e941ce
9c3a5ee
a8503eb
5aac216
b9fc8bd
a0bccbb
b1ad383
faf6015
dfbf5fb
bf77f9f
9a7f895
b3fddd4
045b9c2
ad55a8f
80bc773
42aeac3
cbf415f
761f224
eee4fdc
a717448
6e89051
bc862a3
a66cba0
f8643c4
b5db76b
f4841bf
4ff48ef
3521378
3115c4e
564f929
f471d00
465bbbe
9d02852
eab54dc
1ca757e
07276e4
d8ed180
b0be792
e8d6d0d
ff4b97f
69da678
e22b43a
acc7f1f
310cb47
24b61bf
2a02a5b
0af7104
3bc3cdc
fbd698b
b966167
8326300
a4784bf
97f5b6c
23129c0
f4c522b
823ba51
439f5e7
1cc735a
de7b6f9
b454c4c
9126b04
5f8127c
8b8d7e5
635281f
b844426
b262bf3
dd329c8
206fe2d
4cdc81c
418e569
705d093
a7d9beb
d073aef
6a90db1
3dd1f08
752b403
3ef0637
33a9726
015c300
f11d39f
b2fd397
7eecf87
a9037cf
336de77
7475bf5
9afa7b5
e1ddad5
d1b24cc
67e40a3
eef6243
0420836
42d1221
def15ff
188c151
c92e243
5f6fc6d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -83,3 +83,15 @@ | |
kubeconfig: ~/.kube/config | ||
namespace: "{{ NAMESPACE }}" | ||
template: amqp-files-deployment.yml.j2 | ||
|
||
- name: TlDrawServerDeployment | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Whats the differents between TlDrawServerDeployment and TlDrawServerService? From name i expect that is nearly the same. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is resolved without comment. Do you already think about the naming? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, forgot to reply. Deployment describes how to create or modify instances of the pods, it is responsible for keeping a set of pods running. Service is responsible for enabling network access to a set of pods. |
||
kubernetes.core.k8s: | ||
kubeconfig: ~/.kube/config | ||
namespace: "{{ NAMESPACE }}" | ||
template: tldraw-deployment.yml.j2 | ||
|
||
- name: TlDrawServerService | ||
blazejpass marked this conversation as resolved.
Show resolved
Hide resolved
|
||
kubernetes.core.k8s: | ||
kubeconfig: ~/.kube/config | ||
namespace: "{{ NAMESPACE }}" | ||
template: tldraw-svc.yml.j2 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
apiVersion: apps/v1 | ||
kind: Deployment | ||
metadata: | ||
name: tldraw-deployment | ||
namespace: {{ NAMESPACE }} | ||
labels: | ||
app: tldraw | ||
spec: | ||
replicas: {{ TLDRAW_EDITOR_REPLICAS|default("1", true) }} | ||
strategy: | ||
type: RollingUpdate | ||
rollingUpdate: | ||
maxSurge: 1 | ||
#maxUnavailable: 1 | ||
revisionHistoryLimit: 4 | ||
paused: false | ||
selector: | ||
matchLabels: | ||
app: tldraw | ||
template: | ||
metadata: | ||
labels: | ||
app: tldraw | ||
spec: | ||
securityContext: | ||
runAsUser: 1000 | ||
runAsGroup: 1000 | ||
fsGroup: 1000 | ||
runAsNonRoot: true | ||
containers: | ||
- name: tldraw | ||
image: {{ SCHULCLOUD_SERVER_IMAGE }}:{{ SCHULCLOUD_SERVER_IMAGE_TAG }} | ||
imagePullPolicy: IfNotPresent | ||
ports: | ||
- containerPort: 3345 | ||
name: tldraw | ||
protocol: TCP | ||
envFrom: | ||
- configMapRef: | ||
name: api-configmap | ||
- secretRef: | ||
name: api-secret | ||
command: ['npm', 'run', 'nest:start:tldraw:prod'] | ||
resources: | ||
limits: | ||
cpu: {{ TLDRAW_EDITOR_CPU_LIMITS|default("2000m", true) }} | ||
memory: {{ TLDRAW_EDITOR_MEMORY_LIMITS|default("500Mi", true) }} | ||
requests: | ||
cpu: {{ TLDRAW_EDITOR_CPU_REQUESTS|default("100m", true) }} | ||
memory: {{ TLDRAW_EDITOR_MEMORY_REQUESTS|default("50Mi", true) }} |
blazejpass marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
apiVersion: v1 | ||
kind: Service | ||
metadata: | ||
name: tldraw-svc | ||
namespace: {{ NAMESPACE }} | ||
labels: | ||
app: tldraw | ||
spec: | ||
type: ClusterIP | ||
ports: | ||
- port: 3345 | ||
targetPort: 3345 | ||
protocol: TCP | ||
name: tldraw | ||
selector: | ||
app: tldraw |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/* istanbul ignore file */ | ||
/* eslint-disable no-console */ | ||
import { NestFactory } from '@nestjs/core'; | ||
import { install as sourceMapInstall } from 'source-map-support'; | ||
import { TldrawModule } from '@src/modules/tldraw'; | ||
import { Logger } from '@src/core/logger'; | ||
import * as WebSocket from 'ws'; | ||
wiaderwek marked this conversation as resolved.
Show resolved
Hide resolved
|
||
import { WsAdapter } from '@nestjs/platform-ws'; | ||
import { enableOpenApiDocs } from '@shared/controller/swagger'; | ||
import { AppStartLoggable } from '@src/apps/helpers/app-start-loggable'; | ||
|
||
async function bootstrap() { | ||
sourceMapInstall(); | ||
|
||
const nestApp = await NestFactory.create(TldrawModule); | ||
const wss = new WebSocket.Server({ noServer: true }); | ||
nestApp.useWebSocketAdapter(new WsAdapter(wss)); | ||
nestApp.enableCors(); | ||
enableOpenApiDocs(nestApp, 'docs'); | ||
|
||
const logger = await nestApp.resolve(Logger); | ||
await nestApp.init(); | ||
|
||
logger.info( | ||
new AppStartLoggable({ | ||
appName: 'Tldraw server app', | ||
}) | ||
); | ||
} | ||
|
||
void bootstrap(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from './interface'; | ||
export * from './guard/jwt-auth.guard'; | ||
export * from './authentication.module'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { Configuration } from '@hpi-schul-cloud/commons'; | ||
import { NodeEnvType } from '@src/modules/server'; | ||
blazejpass marked this conversation as resolved.
Show resolved
Hide resolved
|
||
import { TLDRAW_DB_URL } from '@src/config'; | ||
|
||
export interface TldrawConfig { | ||
NEST_LOG_LEVEL: string; | ||
INCOMING_REQUEST_TIMEOUT: number; | ||
NODE_ENV: string; | ||
TLDRAW_DB_COLLECTION_NAME: string; | ||
TLDRAW_DB_FLUSH_SIZE: string; | ||
TLDRAW_DB_MULTIPLE_COLLECTIONS: boolean; | ||
CONNECTION_STRING: string; | ||
FEATURE_TLDRAW_ENABLED: boolean; | ||
} | ||
|
||
const tldrawConfig = { | ||
NEST_LOG_LEVEL: Configuration.get('NEST_LOG_LEVEL') as string, | ||
INCOMING_REQUEST_TIMEOUT: Configuration.get('INCOMING_REQUEST_TIMEOUT_API') as number, | ||
NODE_ENV: Configuration.get('NODE_ENV') as NodeEnvType, | ||
TLDRAW_DB_COLLECTION_NAME: Configuration.get('TLDRAW__DB_COLLECTION_NAME') as string, | ||
TLDRAW_DB_FLUSH_SIZE: Configuration.get('TLDRAW__DB_FLUSH_SIZE') as number, | ||
TLDRAW_DB_MULTIPLE_COLLECTIONS: Configuration.get('TLDRAW__DB_MULTIPLE_COLLECTIONS') as boolean, | ||
FEATURE_TLDRAW_ENABLED: Configuration.get('FEATURE_TLDRAW_ENABLED') as boolean, | ||
CONNECTION_STRING: TLDRAW_DB_URL, | ||
}; | ||
|
||
export const SOCKET_PORT = Configuration.get('TLDRAW__SOCKET_PORT') as number; | ||
export const config = () => tldrawConfig; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
import { WsAdapter } from '@nestjs/platform-ws'; | ||
import { Test } from '@nestjs/testing'; | ||
import WebSocket from 'ws'; | ||
import { TextEncoder } from 'util'; | ||
import { INestApplication } from '@nestjs/common'; | ||
import { CoreModule } from '@src/core'; | ||
import { ConfigModule } from '@nestjs/config'; | ||
import { createConfigModuleOptions } from '@src/config'; | ||
import { config } from '@src/modules/tldraw/config'; | ||
import * as Utils from '../../utils/utils'; | ||
import { TldrawGateway } from '../tldraw.gateway'; | ||
|
||
describe('WebSocketGateway (WsAdapter)', () => { | ||
let app: INestApplication; | ||
let gateway: TldrawGateway; | ||
let ws: WebSocket; | ||
|
||
const gatewayPort = 3346; | ||
const wsUrl = `ws://localhost:${gatewayPort}`; | ||
const testMessage = | ||
'AZQBAaCbuLANBIsBeyJ0ZFVzZXIiOnsiaWQiOiJkNGIxZThmYi0yMWUwLTQ3ZDAtMDI0Y' + | ||
'S0zZGEwYjMzNjQ3MjIiLCJjb2xvciI6IiNGMDRGODgiLCJwb2ludCI6WzAsMF0sInNlbGVjdGVkSWRzIjpbXSwiYWN' + | ||
'0aXZlU2hhcGVzIjpbXSwic2Vzc2lvbiI6ZmFsc2V9fQ=='; | ||
|
||
const setupWs = async (docName?: string) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A good test setups for sockets are missed for now. We should put it as todo to the socket milestone @Metauriel . |
||
if (docName) { | ||
ws = new WebSocket(`${wsUrl}/${docName}`); | ||
} else { | ||
ws = new WebSocket(`${wsUrl}`); | ||
} | ||
await new Promise((resolve) => { | ||
ws.on('open', resolve); | ||
}); | ||
}; | ||
|
||
const delay = (ms: number) => | ||
new Promise((resolve) => { | ||
setTimeout(resolve, ms); | ||
}); | ||
|
||
const getMessage = () => new TextEncoder().encode(testMessage); | ||
|
||
beforeAll(async () => { | ||
const imports = [CoreModule, ConfigModule.forRoot(createConfigModuleOptions(config))]; | ||
const testingModule = await Test.createTestingModule({ | ||
imports, | ||
providers: [TldrawGateway], | ||
}).compile(); | ||
gateway = testingModule.get<TldrawGateway>(TldrawGateway); | ||
app = testingModule.createNestApplication(); | ||
app.useWebSocketAdapter(new WsAdapter(app)); | ||
await app.init(); | ||
}); | ||
|
||
afterAll(async () => { | ||
await app.close(); | ||
}); | ||
|
||
beforeEach(() => { | ||
jest.useFakeTimers({ advanceTimers: true, doNotFake: ['setInterval', 'clearInterval', 'setTimeout'] }); | ||
}); | ||
|
||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
describe('when tldraw is correctly setup', () => { | ||
const setup = async () => { | ||
const handleConnectionSpy = jest.spyOn(gateway, 'handleConnection'); | ||
jest.spyOn(Uint8Array.prototype, 'reduce').mockReturnValue(1); | ||
|
||
await setupWs('TEST'); | ||
|
||
const { buffer } = getMessage(); | ||
|
||
return { handleConnectionSpy, buffer }; | ||
}; | ||
|
||
it(`should handle connection and data transfer`, async () => { | ||
const { handleConnectionSpy, buffer } = await setup(); | ||
ws.send(buffer, () => {}); | ||
|
||
expect(handleConnectionSpy).toHaveBeenCalled(); | ||
expect(handleConnectionSpy).toHaveBeenCalledTimes(1); | ||
|
||
await delay(2000); | ||
ws.close(); | ||
}); | ||
|
||
it(`check if client will receive message`, async () => { | ||
const { buffer } = await setup(); | ||
ws.send(buffer, () => {}); | ||
|
||
gateway.server.on('connection', (client) => { | ||
client.on('message', (payload) => { | ||
expect(payload).toBeInstanceOf(ArrayBuffer); | ||
}); | ||
}); | ||
|
||
await delay(200); | ||
ws.close(); | ||
}); | ||
}); | ||
|
||
describe('when tldraw doc has multiple clients', () => { | ||
const setup = async () => { | ||
const handleConnectionSpy = jest.spyOn(gateway, 'handleConnection'); | ||
await setupWs('TEST2'); | ||
const ws2 = new WebSocket(`${wsUrl}/TEST2`); | ||
await new Promise((resolve) => { | ||
ws2.on('open', resolve); | ||
}); | ||
|
||
const { buffer } = getMessage(); | ||
|
||
return { | ||
handleConnectionSpy, | ||
ws2, | ||
buffer, | ||
}; | ||
}; | ||
|
||
it(`should handle 2 connections at same doc and data transfer`, async () => { | ||
const { handleConnectionSpy, ws2, buffer } = await setup(); | ||
ws.send(buffer); | ||
ws2.send(buffer); | ||
|
||
expect(handleConnectionSpy).toHaveBeenCalled(); | ||
expect(handleConnectionSpy).toHaveBeenCalledTimes(2); | ||
|
||
await delay(200); | ||
ws.close(); | ||
ws2.close(); | ||
}, 120000); | ||
}); | ||
|
||
describe('when tldraw is not correctly setup', () => { | ||
const setup = async () => { | ||
const handleConnectionSpy = jest.spyOn(gateway, 'handleConnection'); | ||
const utilsSpy = jest.spyOn(Utils, 'messageHandler').mockReturnValue(); | ||
|
||
await setupWs(); | ||
|
||
return { | ||
handleConnectionSpy, | ||
utilsSpy, | ||
}; | ||
}; | ||
|
||
it(`should refuse connection if there is no docName`, async () => { | ||
const { handleConnectionSpy, utilsSpy } = await setup(); | ||
|
||
const { buffer } = getMessage(); | ||
ws.send(buffer); | ||
|
||
expect(gateway.server).toBeDefined(); | ||
expect(handleConnectionSpy).toHaveBeenCalled(); | ||
expect(handleConnectionSpy).toHaveBeenCalledTimes(1); | ||
expect(utilsSpy).not.toHaveBeenCalled(); | ||
|
||
await delay(200); | ||
ws.close(); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './tldraw.gateway'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be switch back to main, before merge