Skip to content

Commit

Permalink
Merge branch 'main' into BC-8587-paths-and-type
Browse files Browse the repository at this point in the history
  • Loading branch information
bischofmax authored Jan 6, 2025
2 parents ff9e4d0 + a9b0669 commit d2d6782
Show file tree
Hide file tree
Showing 9 changed files with 301 additions and 68 deletions.
4 changes: 3 additions & 1 deletion .env.default
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ S3_SSL=false
FEATURE_TLDRAW_ENABLED=true
TLDRAW_WEBSOCKET_URL=ws://localhost:3345

X_API_ALLOWED_KEYS=randomString
X_API_ALLOWED_KEYS=randomString

NOT_AUTHENTICATED_REDIRECT_URL=http://localhost:4000/login
7 changes: 6 additions & 1 deletion .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,9 @@ FEATURE_TLDRAW_ENABLED=true
TLDRAW_WEBSOCKET_URL=ws://localhost:3399
TLDRAW_WEBSOCKET_PORT=3399

X_API_ALLOWED_KEYS=randomString
X_API_ALLOWED_KEYS=randomString

WORKER_TASK_DEBOUNCE=500
WORKER_MIN_MESSAGE_LIFETIME=1000

NOT_AUTHENTICATED_REDIRECT_URL=http://localhost:4000/login
3 changes: 2 additions & 1 deletion ansible/roles/tldraw-server/defaults/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ TLDRAW_ASSETS_ALLOWED_MIME_TYPES_LIST : "image/png,image/jpeg,image/gif,image/sv
TLDRAW_LOG_LEVEL: "error"
TLDRAW_METRICS_COLLECT_DEFAULT: "true"
TLDRAW_ASSETS_ENABLED: "true"
TLDRAW_WEBSOCKET_PATH: "/tldraw-server"
TLDRAW_WEBSOCKET_PATH: "/tldraw-server"
NOT_AUTHENTICATED_REDIRECT_URL: "https://{{ DOMAIN }}/login"
1 change: 1 addition & 0 deletions ansible/roles/tldraw-server/templates/configmap.yml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ data:
FEATURE_TLDRAW_ENABLED: "{{ FEATURE_TLDRAW_ENABLED }}"
TLDRAW_WEBSOCKET_PATH: "{{ TLDRAW_WEBSOCKET_PATH }}"
TLDRAW_WEBSOCKET_URL: "wss://{{ DOMAIN }}{{ TLDRAW_WEBSOCKET_PATH }}"
NOT_AUTHENTICATED_REDIRECT_URL: "{{ NOT_AUTHENTICATED_REDIRECT_URL }}"
4 changes: 4 additions & 0 deletions src/modules/server/api/dto/tldraw-config.response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export class TldrawPublicConfigResponse {
this.TLDRAW_ASSETS_MAX_SIZE_BYTES = config.TLDRAW_ASSETS_MAX_SIZE_BYTES;
this.TLDRAW_ASSETS_ALLOWED_MIME_TYPES_LIST = config.TLDRAW_ASSETS_ALLOWED_MIME_TYPES_LIST;
this.FEATURE_TLDRAW_ENABLED = config.FEATURE_TLDRAW_ENABLED;
this.NOT_AUTHENTICATED_REDIRECT_URL = config.NOT_AUTHENTICATED_REDIRECT_URL;
}

@ApiProperty()
Expand All @@ -24,4 +25,7 @@ export class TldrawPublicConfigResponse {

@ApiProperty()
public FEATURE_TLDRAW_ENABLED!: boolean;

@ApiProperty()
public NOT_AUTHENTICATED_REDIRECT_URL: string;
}
1 change: 1 addition & 0 deletions src/modules/server/api/test/tldraw-config.api.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ describe('Tldraw-Config Api Test', () => {
TLDRAW_ASSETS_ENABLED: true,
TLDRAW_ASSETS_MAX_SIZE_BYTES: 10485760,
TLDRAW_WEBSOCKET_URL: 'ws://localhost:3399',
NOT_AUTHENTICATED_REDIRECT_URL: 'http://localhost:4000/login',
});
});
});
Expand Down
118 changes: 53 additions & 65 deletions src/modules/server/api/test/websocket.api.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,6 @@ describe('Websocket Api Test', () => {
return isSynced;
});

/* const waitUntilDocValueMatches = (ydoc: Doc, key: string, value: number): Promise<void> =>
promise.until(0, () => {
const result = ydoc.getMap().get(key);
const isMatch = result === value;
return isMatch;
}); */

describe('when clients have permission for room', () => {
describe('when two clients connect to the same doc before any changes', () => {
const setup = () => {
Expand All @@ -83,25 +75,30 @@ describe('Websocket Api Test', () => {
error: null,
});

const { ydoc: client1Doc } = createWsClient(room);
const { ydoc: client2Doc } = createWsClient(room);
const { ydoc: client1Doc, provider: provider1 } = createWsClient(room);
const { ydoc: client2Doc, provider: provider2 } = createWsClient(room);

return { client1Doc, client2Doc };
return { client1Doc, client2Doc, provider1, provider2 };
};

it('syncs doc changes of first client to second client', async () => {
const { client1Doc, client2Doc } = setup();
const { client1Doc, client2Doc, provider1, provider2 } = setup();

client1Doc.getMap().set('a', 1);

await waitUntilDocsEqual(client1Doc, client2Doc);

const result = client2Doc.getMap().get('a');
expect(result).toBe(1);

provider1.awareness.destroy();
provider2.awareness.destroy();
provider1.destroy();
provider2.destroy();
});

it('syncs subsequent doc changes of second client to first client', async () => {
const { client1Doc, client2Doc } = setup();
const { client1Doc, client2Doc, provider1, provider2 } = setup();

client1Doc.getMap().set('a', 1);
await waitUntilDocsEqual(client1Doc, client2Doc);
Expand All @@ -111,85 +108,74 @@ describe('Websocket Api Test', () => {

const result = client1Doc.getMap().get('a');
expect(result).toBe(2);
});

/* it('syncs nearly parallel doc changes of second client to first client', async () => {
// This test is instable
const { client1Doc, client2Doc } = setup();
client1Doc.getMap().set('a', 1);
client2Doc.getMap().set('a', 2);
await waitUntilDocValueMatches(client1Doc, 'a', 2);

const result = client1Doc.getMap().get('a');
expect(result).toBe(2);
}); */
provider1.awareness.destroy();
provider2.awareness.destroy();
provider1.destroy();
provider2.destroy();
});
});

describe('when two clients connect to the same doc one before and one after the changes', () => {
const setup = () => {
const randomString = Math.random().toString(36).substring(7);
const room = randomString;

authorizationService.hasPermission.mockResolvedValue({
authorizationService.hasPermission.mockResolvedValueOnce({
hasWriteAccess: true,
room: randomString,
userid: 'userId1',
error: null,
});
authorizationService.hasPermission.mockResolvedValueOnce({
hasWriteAccess: true,
room: randomString,
userid: 'userId',
userid: 'userId2',
error: null,
});

const { ydoc: client1Doc } = createWsClient(room);
const { ydoc: client1Doc, provider } = createWsClient(room);

return { client1Doc, room };
return { client1Doc, room, provider };
};

it('syncs doc changes of first client to second client', async () => {
const { client1Doc, room } = setup();
const { client1Doc, room, provider } = setup();

client1Doc.getMap().set('a', 1);

const { ydoc: client2Doc } = createWsClient(room);
const { ydoc: client2Doc, provider: provider2 } = createWsClient(room);
await waitUntilDocsEqual(client1Doc, client2Doc);

const result = client2Doc.getMap().get('a');
expect(result).toBe(1);

provider.awareness.destroy();
provider.destroy();
provider2.awareness.destroy();
provider2.destroy();
});

it('syncs subsequent doc changes of second client to first client', async () => {
const { client1Doc, room } = setup();
const { client1Doc, room, provider } = setup();

client1Doc.getMap().set('a', 1);

const { ydoc: client2Doc } = createWsClient(room);
const { ydoc: client2Doc, provider: provider2 } = createWsClient(room);
await waitUntilDocsEqual(client1Doc, client2Doc);

client2Doc.getMap().set('a', 2);
await waitUntilDocsEqual(client1Doc, client2Doc);

const result = client1Doc.getMap().get('a');
expect(result).toBe(2);
});

/* it('syncs nearly parallel doc changes of second client to first client', async () => {
// This test is instable
const { client1Doc, room } = setup();
client1Doc.getMap().set('a', 1);
const { ydoc: client2Doc } = createWsClient(room);
client2Doc.getMap().set('a', 2);
await waitUntilDocValueMatches(client1Doc, 'a', 2);

const result = client1Doc.getMap().get('a');
expect(result).toBe(2);
}); */
provider.awareness.destroy();
provider.destroy();
provider2.awareness.destroy();
provider2.destroy();
});
});

/* describe('when doc is only pesisted in storage and not in redis', () => {
// Need to implement this test
}); */
});

describe('when client has no permission for room', () => {
Expand All @@ -199,14 +185,14 @@ describe('Websocket Api Test', () => {
const room = randomString;

const errorResponse = ResponsePayloadBuilder.buildWithError(4401, 'Unauthorized');
authorizationService.hasPermission.mockResolvedValue(errorResponse);
authorizationService.hasPermission.mockResolvedValueOnce(errorResponse);

const { ydoc: client1Doc, provider } = createWsClient(room);

return { client1Doc, provider };
};

it('syncs doc changes of first client to second client', async () => {
it('returns unauthorized error', async () => {
const { provider } = setup();

let error: CloseEvent;
Expand All @@ -224,6 +210,9 @@ describe('Websocket Api Test', () => {
expect(error.reason).toBe('Unauthorized');
// @ts-ignore
expect(error.code).toBe(4401);

provider.awareness.destroy();
provider.destroy();
});
});

Expand All @@ -233,14 +222,14 @@ describe('Websocket Api Test', () => {
const room = randomString;

const response = ResponsePayloadBuilder.build(null, 'userId');
authorizationService.hasPermission.mockResolvedValue(response);
authorizationService.hasPermission.mockResolvedValueOnce(response);

const { ydoc: client1Doc, provider } = createWsClient(room);

return { client1Doc, provider };
};

it('syncs doc changes of first client to second client', async () => {
it('returns error', async () => {
const { provider } = setup();

let error: CloseEvent;
Expand All @@ -258,24 +247,24 @@ describe('Websocket Api Test', () => {
expect(error.reason).toBe('Missing room or userid');
// @ts-ignore
expect(error.code).toBe(1008);

provider.awareness.destroy();
provider.destroy();
});
});
});

/*describe('when openCallback catch an error', () => {
describe('when openCallback catch an error', () => {
const setup = () => {
const randomString = Math.random().toString(36).substring(7);
const room = randomString;

const response = ResponsePayloadBuilder.build(room, 'userId');
authorizationService.hasPermission.mockResolvedValue(response);
const { ydoc: client1Doc, provider } = createWsClient(room);

return { client1Doc, provider };
};

it('syncs doc changes of first client to second client', async () => {
it('returns internal server error', async () => {
const { provider } = setup();

let error: CloseEvent;
Expand All @@ -284,16 +273,15 @@ describe('Websocket Api Test', () => {
error = event as CloseEvent;
};
}
//spyOn(provider.ws, 'end').and.callThrough();

await promise.until(0, () => {
return error as unknown as boolean;
});

// @ts-ignore
//expect(error.reason).toBe('Internal Server Error');
expect(error.reason).toBe('Internal Server Error');
// @ts-ignore
expect(error.code).toBe(1011);
});
});*/
});
});
3 changes: 3 additions & 0 deletions src/modules/server/tldraw-server.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,7 @@ export class TldrawServerConfig {
@Transform(({ value }) => value === 'true')
@IsBoolean()
public FEATURE_TLDRAW_ENABLED!: boolean;

@IsUrl({ protocols: ['https', 'http'], require_tld: false })
public NOT_AUTHENTICATED_REDIRECT_URL!: string;
}
Loading

0 comments on commit d2d6782

Please sign in to comment.