Skip to content

Commit d4dc0c4

Browse files
authored
Merge pull request #449 from streamich/demos-2
Demos 2
2 parents 450c7fe + 008de8d commit d4dc0c4

File tree

3 files changed

+87
-7
lines changed

3 files changed

+87
-7
lines changed

src/server/__tests__/blocks.spec.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,4 +422,67 @@ describe('blocks.*', () => {
422422
});
423423
});
424424
});
425+
426+
describe('blocks.get', () => {
427+
test('returns whole history when block is loaded', async () => {
428+
const {client} = setup();
429+
const model = Model.withLogicalClock();
430+
model.api.root({
431+
text: 'Hell',
432+
});
433+
const patch1 = model.api.flush();
434+
await client.call('blocks.create', {
435+
id: 'my-block',
436+
patches: [
437+
{
438+
seq: 0,
439+
created: Date.now(),
440+
blob: patch1.toBinary(),
441+
},
442+
],
443+
});
444+
model.api.str(['text']).ins(4, 'o');
445+
const patch2 = model.api.flush();
446+
model.api.obj([]).set({
447+
age: 26,
448+
});
449+
const patch3 = model.api.flush();
450+
await client.call('blocks.edit', {
451+
id: 'my-block',
452+
patches: [
453+
{
454+
seq: 1,
455+
created: Date.now(),
456+
blob: patch2.toBinary(),
457+
},
458+
{
459+
seq: 2,
460+
created: Date.now(),
461+
blob: patch3.toBinary(),
462+
},
463+
],
464+
});
465+
const result = await client.call('blocks.get', {id: 'my-block'});
466+
expect(result).toMatchObject({
467+
block: expect.any(Object),
468+
patches: [
469+
{
470+
seq: 0,
471+
created: expect.any(Number),
472+
blob: patch1.toBinary(),
473+
},
474+
{
475+
seq: 1,
476+
created: expect.any(Number),
477+
blob: patch2.toBinary(),
478+
},
479+
{
480+
seq: 2,
481+
created: expect.any(Number),
482+
blob: patch3.toBinary(),
483+
},
484+
],
485+
});
486+
});
487+
});
425488
});

src/server/routes/blocks/methods/get.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type {RoutesBase, TypeRouter} from '../../../../json-type/system/TypeRouter';
22
import type {RouteDeps} from '../../types';
3-
import type {Block, BlockId} from '../schema';
3+
import type {Block, BlockId, BlockPatch} from '../schema';
44

55
export const get =
66
({services}: RouteDeps) =>
@@ -14,7 +14,13 @@ export const get =
1414
}),
1515
);
1616

17-
const Response = t.Object(t.prop('block', t.Ref<typeof Block>('Block').options({})));
17+
const Response = t.Object(
18+
t.prop('block', t.Ref<typeof Block>('Block').options({})),
19+
t.prop('patches', t.Array(t.Ref<typeof BlockPatch>('BlockPatch'))).options({
20+
title: 'Patches',
21+
description: 'The list of all patches.',
22+
}),
23+
);
1824

1925
const Func = t
2026
.Function(Request, Response)
@@ -24,9 +30,10 @@ export const get =
2430
description: 'Fetches a block by ID.',
2531
})
2632
.implement(async ({id}) => {
27-
const {block} = await services.blocks.get(id);
33+
const {block, patches} = await services.blocks.get(id);
2834
return {
29-
block: block,
35+
block,
36+
patches,
3037
};
3138
});
3239

src/server/services/blocks/BlocksServices.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@ import {StorePatch} from './types';
33
import {RpcError, RpcErrorCodes} from '../../../reactive-rpc/common/rpc/caller';
44
import type {Services} from '../Services';
55

6-
const BLOCK_TTL = 1000 * 60 * 20; // 20 minutes
6+
const BLOCK_TTL = 1000 * 60 * 60; // 1 hour
7+
8+
const validatePatches = (patches: StorePatch[]) => {
9+
for (const patch of patches) {
10+
if (patch.blob.length > 2000) throw RpcError.validation('patch blob too large');
11+
if (patch.seq > 500_000) throw RpcError.validation('patch seq too large');
12+
}
13+
};
714

815
export class BlocksServices {
916
protected readonly store = new MemoryStore();
@@ -13,6 +20,7 @@ export class BlocksServices {
1320
public async create(id: string, patches: StorePatch[]) {
1421
this.maybeGc();
1522
const {store} = this;
23+
validatePatches(patches);
1624
const {block} = await store.create(id, patches);
1725
const data = {
1826
block,
@@ -29,8 +37,9 @@ export class BlocksServices {
2937
const {store} = this;
3038
const result = await store.get(id);
3139
if (!result) throw RpcError.fromCode(RpcErrorCodes.NOT_FOUND);
40+
const patches = await store.history(id, 0, result.block.seq);
3241
const {block} = result;
33-
return {block};
42+
return {block, patches};
3443
}
3544

3645
public async remove(id: string) {
@@ -47,12 +56,13 @@ export class BlocksServices {
4756
return {patches};
4857
}
4958

50-
public async edit(id: string, patches: any[]) {
59+
public async edit(id: string, patches: StorePatch[]) {
5160
this.maybeGc();
5261
if (!Array.isArray(patches)) throw RpcError.validation('patches must be an array');
5362
if (!patches.length) throw RpcError.validation('patches must not be empty');
5463
const seq = patches[0].seq;
5564
const {store} = this;
65+
validatePatches(patches);
5666
const {block} = await store.edit(id, patches);
5767
this.services.pubsub.publish(`__block:${id}`, {patches}).catch((error) => {
5868
// tslint:disable-next-line:no-console

0 commit comments

Comments
 (0)