@milaboratories/pl-middle-layer 1.36.4 → 1.37.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +19 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2031 -1918
- package/dist/index.mjs.map +1 -1
- package/dist/js_render/computable_context.d.ts +68 -0
- package/dist/js_render/computable_context.d.ts.map +1 -0
- package/dist/js_render/context.d.ts +22 -64
- package/dist/js_render/context.d.ts.map +1 -1
- package/dist/js_render/index.d.ts +2 -0
- package/dist/js_render/index.d.ts.map +1 -1
- package/dist/middle_layer/block.d.ts.map +1 -1
- package/dist/middle_layer/block_ctx.d.ts +5 -0
- package/dist/middle_layer/block_ctx.d.ts.map +1 -1
- package/dist/middle_layer/middle_layer.d.ts +2 -0
- package/dist/middle_layer/middle_layer.d.ts.map +1 -1
- package/dist/middle_layer/project.d.ts.map +1 -1
- package/dist/middle_layer/project_overview.d.ts.map +1 -1
- package/dist/middle_layer/util.d.ts +2 -0
- package/dist/middle_layer/util.d.ts.map +1 -1
- package/dist/model/args.d.ts +4 -2
- package/dist/model/args.d.ts.map +1 -1
- package/dist/model/project_helper.d.ts +14 -0
- package/dist/model/project_helper.d.ts.map +1 -0
- package/dist/model/project_model_util.d.ts +14 -4
- package/dist/model/project_model_util.d.ts.map +1 -1
- package/dist/mutator/project.d.ts +16 -10
- package/dist/mutator/project.d.ts.map +1 -1
- package/package.json +14 -14
- package/src/js_render/computable_context.ts +753 -0
- package/src/js_render/context.ts +32 -720
- package/src/js_render/index.ts +37 -3
- package/src/middle_layer/block.ts +2 -0
- package/src/middle_layer/block_ctx.ts +6 -0
- package/src/middle_layer/middle_layer.ts +7 -2
- package/src/middle_layer/project.ts +15 -17
- package/src/middle_layer/project_overview.ts +13 -4
- package/src/middle_layer/util.ts +3 -1
- package/src/model/args.ts +12 -6
- package/src/model/project_helper.ts +41 -0
- package/src/model/project_model_util.test.ts +13 -4
- package/src/model/project_model_util.ts +37 -12
- package/src/mutator/project.test.ts +18 -12
- package/src/mutator/project.ts +159 -61
- package/src/mutator/template/template_render.test.ts +2 -2
|
@@ -12,18 +12,22 @@ import {
|
|
|
12
12
|
BPSpecSumV042NotPrepared,
|
|
13
13
|
TestBPPreparer
|
|
14
14
|
} from '../test/block_packs';
|
|
15
|
+
import { getQuickJS } from 'quickjs-emscripten';
|
|
16
|
+
import { ProjectHelper } from '../model/project_helper';
|
|
15
17
|
|
|
16
18
|
test('simple test #1', async () => {
|
|
19
|
+
const quickJs = await getQuickJS();
|
|
20
|
+
|
|
17
21
|
await TestHelpers.withTempRoot(async (pl) => {
|
|
18
22
|
const prj = await pl.withWriteTx('CreatingProject', async (tx) => {
|
|
19
23
|
const prjRef = await createProject(tx);
|
|
20
24
|
tx.createField(field(tx.clientRoot, 'prj'), 'Dynamic', prjRef);
|
|
21
25
|
await tx.commit();
|
|
22
26
|
return await toGlobalResourceId(prjRef);
|
|
23
|
-
});
|
|
27
|
+
});
|
|
24
28
|
|
|
25
29
|
await pl.withWriteTx('AddBlock1', async (tx) => {
|
|
26
|
-
const mut = await ProjectMutator.load(tx, prj);
|
|
30
|
+
const mut = await ProjectMutator.load(new ProjectHelper(quickJs), tx, prj);
|
|
27
31
|
mut.addBlock(
|
|
28
32
|
{ id: 'block1', label: 'Block1', renderingMode: 'Heavy' },
|
|
29
33
|
{
|
|
@@ -37,7 +41,7 @@ test('simple test #1', async () => {
|
|
|
37
41
|
});
|
|
38
42
|
|
|
39
43
|
await pl.withWriteTx('AddBlock2', async (tx) => {
|
|
40
|
-
const mut = await ProjectMutator.load(tx, prj);
|
|
44
|
+
const mut = await ProjectMutator.load(new ProjectHelper(quickJs), tx, prj);
|
|
41
45
|
mut.addBlock(
|
|
42
46
|
{ id: 'block2', label: 'Block2', renderingMode: 'Heavy' },
|
|
43
47
|
{
|
|
@@ -52,7 +56,7 @@ test('simple test #1', async () => {
|
|
|
52
56
|
});
|
|
53
57
|
|
|
54
58
|
await pl.withWriteTx('AddBlock3', async (tx) => {
|
|
55
|
-
const mut = await ProjectMutator.load(tx, prj);
|
|
59
|
+
const mut = await ProjectMutator.load(new ProjectHelper(quickJs), tx, prj);
|
|
56
60
|
mut.addBlock(
|
|
57
61
|
{ id: 'block3', label: 'Block3', renderingMode: 'Heavy' },
|
|
58
62
|
{
|
|
@@ -98,7 +102,7 @@ test('simple test #1', async () => {
|
|
|
98
102
|
});
|
|
99
103
|
|
|
100
104
|
await pl.withWriteTx('DeleteBlock2', async (tx) => {
|
|
101
|
-
const mut = await ProjectMutator.load(tx, prj);
|
|
105
|
+
const mut = await ProjectMutator.load(new ProjectHelper(quickJs), tx, prj);
|
|
102
106
|
mut.deleteBlock('block2');
|
|
103
107
|
mut.save();
|
|
104
108
|
await tx.commit();
|
|
@@ -110,7 +114,7 @@ test('simple test #1', async () => {
|
|
|
110
114
|
).toBeUndefined();
|
|
111
115
|
});
|
|
112
116
|
|
|
113
|
-
await withProject(pl, prj, (mut) => {
|
|
117
|
+
await withProject(new ProjectHelper(quickJs), pl, prj, (mut) => {
|
|
114
118
|
mut.setUiState('block3', undefined);
|
|
115
119
|
});
|
|
116
120
|
|
|
@@ -120,7 +124,7 @@ test('simple test #1', async () => {
|
|
|
120
124
|
).toBeUndefined();
|
|
121
125
|
});
|
|
122
126
|
|
|
123
|
-
await withProject(pl, prj, (mut) => {
|
|
127
|
+
await withProject(new ProjectHelper(quickJs), pl, prj, (mut) => {
|
|
124
128
|
mut.setUiState('block3', undefined);
|
|
125
129
|
});
|
|
126
130
|
|
|
@@ -140,7 +144,7 @@ test('simple test #1', async () => {
|
|
|
140
144
|
});
|
|
141
145
|
|
|
142
146
|
await pl.withWriteTx('Refresh', async (tx) => {
|
|
143
|
-
const mut = await ProjectMutator.load(tx, prj);
|
|
147
|
+
const mut = await ProjectMutator.load(new ProjectHelper(quickJs), tx, prj);
|
|
144
148
|
mut.doRefresh();
|
|
145
149
|
mut.save();
|
|
146
150
|
await tx.commit();
|
|
@@ -158,7 +162,7 @@ test('simple test #1', async () => {
|
|
|
158
162
|
});
|
|
159
163
|
|
|
160
164
|
await pl.withWriteTx('RenderProduction', async (tx) => {
|
|
161
|
-
const mut = await ProjectMutator.load(tx, prj);
|
|
165
|
+
const mut = await ProjectMutator.load(new ProjectHelper(quickJs), tx, prj);
|
|
162
166
|
mut.renderProduction(['block1', 'block3']);
|
|
163
167
|
mut.doRefresh();
|
|
164
168
|
mut.save();
|
|
@@ -180,6 +184,8 @@ test('simple test #1', async () => {
|
|
|
180
184
|
});
|
|
181
185
|
|
|
182
186
|
test('simple test #2 with bp migration', async () => {
|
|
187
|
+
const quickJs = await getQuickJS();
|
|
188
|
+
|
|
183
189
|
await TestHelpers.withTempRoot(async (pl) => {
|
|
184
190
|
const prj = await pl.withWriteTx('CreatingProject', async (tx) => {
|
|
185
191
|
const prjRef = await createProject(tx);
|
|
@@ -189,7 +195,7 @@ test('simple test #2 with bp migration', async () => {
|
|
|
189
195
|
});
|
|
190
196
|
|
|
191
197
|
await pl.withWriteTx('AddBlock1', async (tx) => {
|
|
192
|
-
const mut = await ProjectMutator.load(tx, prj);
|
|
198
|
+
const mut = await ProjectMutator.load(new ProjectHelper(quickJs), tx, prj);
|
|
193
199
|
mut.addBlock(
|
|
194
200
|
{ id: 'block1', label: 'Block1', renderingMode: 'Heavy' },
|
|
195
201
|
{
|
|
@@ -243,7 +249,7 @@ test('simple test #2 with bp migration', async () => {
|
|
|
243
249
|
});
|
|
244
250
|
|
|
245
251
|
await pl.withWriteTx('MigrateBlock2', async (tx) => {
|
|
246
|
-
const mut = await ProjectMutator.load(tx, prj);
|
|
252
|
+
const mut = await ProjectMutator.load(new ProjectHelper(quickJs), tx, prj);
|
|
247
253
|
// TODO change to dev
|
|
248
254
|
mut.migrateBlockPack('block2', await TestBPPreparer.prepare(BPSpecEnterV041NotPrepared));
|
|
249
255
|
mut.save();
|
|
@@ -260,7 +266,7 @@ test('simple test #2 with bp migration', async () => {
|
|
|
260
266
|
});
|
|
261
267
|
|
|
262
268
|
await pl.withWriteTx('Refresh', async (tx) => {
|
|
263
|
-
const mut = await ProjectMutator.load(tx, prj);
|
|
269
|
+
const mut = await ProjectMutator.load(new ProjectHelper(quickJs), tx, prj);
|
|
264
270
|
mut.doRefresh();
|
|
265
271
|
mut.save();
|
|
266
272
|
await tx.commit();
|
package/src/mutator/project.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
AnyResourceRef,
|
|
4
4
|
BasicResourceData,
|
|
5
5
|
PlTransaction,
|
|
6
|
+
ResourceData,
|
|
6
7
|
ResourceId } from '@milaboratories/pl-client';
|
|
7
8
|
import {
|
|
8
9
|
ensureResourceIdNotNull,
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
isNotNullResourceId,
|
|
11
12
|
isNullResourceId,
|
|
12
13
|
isResource,
|
|
14
|
+
isResourceId,
|
|
13
15
|
isResourceRef,
|
|
14
16
|
Pl,
|
|
15
17
|
PlClient,
|
|
@@ -42,7 +44,8 @@ import {
|
|
|
42
44
|
} from '../model/project_model';
|
|
43
45
|
import { BlockPackTemplateField, createBlockPack } from './block-pack/block_pack';
|
|
44
46
|
import type {
|
|
45
|
-
BlockGraph
|
|
47
|
+
BlockGraph,
|
|
48
|
+
ProductionGraphBlockInfo } from '../model/project_model_util';
|
|
46
49
|
import {
|
|
47
50
|
allBlocks,
|
|
48
51
|
graphDiff,
|
|
@@ -52,6 +55,7 @@ import {
|
|
|
52
55
|
import type { BlockPackSpecPrepared } from '../model';
|
|
53
56
|
import type {
|
|
54
57
|
AuthorMarker,
|
|
58
|
+
BlockPackSpec,
|
|
55
59
|
BlockSettings,
|
|
56
60
|
ProjectMeta,
|
|
57
61
|
} from '@milaboratories/pl-model-middle-layer';
|
|
@@ -61,7 +65,10 @@ import {
|
|
|
61
65
|
import Denque from 'denque';
|
|
62
66
|
import { exportContext, getPreparedExportTemplateEnvelope } from './context_export';
|
|
63
67
|
import { loadTemplate } from './template/template_loading';
|
|
64
|
-
import {
|
|
68
|
+
import { cachedDeserialize, notEmpty } from '@milaboratories/ts-helpers';
|
|
69
|
+
import type { ProjectHelper } from '../model/project_helper';
|
|
70
|
+
import { extractConfig, type BlockConfig } from '@platforma-sdk/model';
|
|
71
|
+
import type { BlockPackInfo } from '../model/block_pack';
|
|
65
72
|
type FieldStatus = 'NotReady' | 'Ready' | 'Error';
|
|
66
73
|
|
|
67
74
|
interface BlockFieldState {
|
|
@@ -77,6 +84,8 @@ type BlockFieldStateValue = Omit<BlockFieldState, 'modCount'>;
|
|
|
77
84
|
interface BlockInfoState {
|
|
78
85
|
readonly id: string;
|
|
79
86
|
readonly fields: BlockFieldStates;
|
|
87
|
+
blockConfig?: BlockConfig;
|
|
88
|
+
blockPack?: BlockPackSpec;
|
|
80
89
|
}
|
|
81
90
|
|
|
82
91
|
function cached<ModId, T>(modIdCb: () => ModId, valueCb: () => T): () => T {
|
|
@@ -103,6 +112,8 @@ class BlockInfo {
|
|
|
103
112
|
constructor(
|
|
104
113
|
public readonly id: string,
|
|
105
114
|
public readonly fields: BlockFieldStates,
|
|
115
|
+
public readonly config: BlockConfig,
|
|
116
|
+
public readonly source: BlockPackSpec,
|
|
106
117
|
) {}
|
|
107
118
|
|
|
108
119
|
public check() {
|
|
@@ -131,22 +142,22 @@ class BlockInfo {
|
|
|
131
142
|
if (this.fields.currentArgs === undefined) throw new Error('no current args field');
|
|
132
143
|
}
|
|
133
144
|
|
|
134
|
-
private readonly
|
|
145
|
+
private readonly currentArgsC = cached(
|
|
135
146
|
() => this.fields.currentArgs!.modCount,
|
|
136
|
-
() =>
|
|
147
|
+
() => cachedDeserialize(this.fields.currentArgs!.value!),
|
|
137
148
|
);
|
|
138
149
|
|
|
139
|
-
private readonly
|
|
150
|
+
private readonly prodArgsC = cached(
|
|
140
151
|
() => this.fields.prodArgs?.modCount,
|
|
141
152
|
() => {
|
|
142
153
|
const bin = this.fields.prodArgs?.value;
|
|
143
154
|
if (bin === undefined) return undefined;
|
|
144
|
-
return
|
|
155
|
+
return cachedDeserialize(bin);
|
|
145
156
|
},
|
|
146
157
|
);
|
|
147
158
|
|
|
148
|
-
get
|
|
149
|
-
return this.
|
|
159
|
+
get currentArgs(): unknown {
|
|
160
|
+
return this.currentArgsC();
|
|
150
161
|
}
|
|
151
162
|
|
|
152
163
|
get stagingRendered(): boolean {
|
|
@@ -176,8 +187,8 @@ class BlockInfo {
|
|
|
176
187
|
return !this.productionRendered || this.productionStaleC() || this.productionHasErrors;
|
|
177
188
|
}
|
|
178
189
|
|
|
179
|
-
get
|
|
180
|
-
return this.
|
|
190
|
+
get prodArgs(): unknown {
|
|
191
|
+
return this.prodArgsC();
|
|
181
192
|
}
|
|
182
193
|
|
|
183
194
|
public getTemplate(tx: PlTransaction): AnyRef {
|
|
@@ -247,6 +258,7 @@ export class ProjectMutator {
|
|
|
247
258
|
private readonly blockInfos: Map<string, BlockInfo>,
|
|
248
259
|
private readonly blockFrontendStates: Map<string, string>,
|
|
249
260
|
private readonly ctxExportTplHolder: AnyResourceRef,
|
|
261
|
+
private readonly projectHelper: ProjectHelper,
|
|
250
262
|
) {}
|
|
251
263
|
|
|
252
264
|
private fixProblemsAndMigrate() {
|
|
@@ -300,11 +312,41 @@ export class ProjectMutator {
|
|
|
300
312
|
return this.stagingGraph;
|
|
301
313
|
}
|
|
302
314
|
|
|
315
|
+
private getProductionGraphBlockInfo(blockId: string, prod: boolean): ProductionGraphBlockInfo | undefined {
|
|
316
|
+
const bInfo = this.getBlockInfo(blockId);
|
|
317
|
+
|
|
318
|
+
let argsField: BlockFieldState;
|
|
319
|
+
let args: unknown;
|
|
320
|
+
|
|
321
|
+
if (prod) {
|
|
322
|
+
if (bInfo.fields.prodArgs === undefined) return undefined;
|
|
323
|
+
argsField = bInfo.fields.prodArgs;
|
|
324
|
+
args = bInfo.prodArgs;
|
|
325
|
+
} else {
|
|
326
|
+
argsField = notEmpty(bInfo.fields.currentArgs);
|
|
327
|
+
args = bInfo.currentArgs;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const blockPackField = notEmpty(bInfo.fields.blockPack);
|
|
331
|
+
|
|
332
|
+
if (isResourceId(argsField.ref!) && isResourceId(blockPackField.ref!))
|
|
333
|
+
return {
|
|
334
|
+
args,
|
|
335
|
+
enrichmentTargets: this.projectHelper.getEnrichmentTargets(() => bInfo.config, () => args,
|
|
336
|
+
{ argsRid: argsField.ref, blockPackRid: blockPackField.ref }),
|
|
337
|
+
};
|
|
338
|
+
else
|
|
339
|
+
return {
|
|
340
|
+
args,
|
|
341
|
+
enrichmentTargets: this.projectHelper.getEnrichmentTargets(() => bInfo.config, () => args),
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
303
345
|
private getPendingProductionGraph(): BlockGraph {
|
|
304
346
|
if (this.pendingProductionGraph === undefined)
|
|
305
347
|
this.pendingProductionGraph = productionGraph(
|
|
306
348
|
this.struct,
|
|
307
|
-
(blockId) => this.
|
|
349
|
+
(blockId) => this.getProductionGraphBlockInfo(blockId, false),
|
|
308
350
|
);
|
|
309
351
|
return this.pendingProductionGraph;
|
|
310
352
|
}
|
|
@@ -313,7 +355,7 @@ export class ProjectMutator {
|
|
|
313
355
|
if (this.actualProductionGraph === undefined)
|
|
314
356
|
this.actualProductionGraph = productionGraph(
|
|
315
357
|
this.struct,
|
|
316
|
-
(blockId) => this.
|
|
358
|
+
(blockId) => this.getProductionGraphBlockInfo(blockId, true),
|
|
317
359
|
);
|
|
318
360
|
return this.actualProductionGraph;
|
|
319
361
|
}
|
|
@@ -597,14 +639,7 @@ export class ProjectMutator {
|
|
|
597
639
|
|
|
598
640
|
const newStagingGraph = stagingGraph(newStructure);
|
|
599
641
|
|
|
600
|
-
// new actual production graph without new blocks
|
|
601
|
-
const newActualProductionGraph = productionGraph(
|
|
602
|
-
newStructure,
|
|
603
|
-
(blockId) => this.blockInfos.get(blockId)?.actualProductionInputs,
|
|
604
|
-
);
|
|
605
|
-
|
|
606
642
|
const stagingDiff = graphDiff(currentStagingGraph, newStagingGraph);
|
|
607
|
-
const prodDiff = graphDiff(currentActualProductionGraph, newActualProductionGraph);
|
|
608
643
|
|
|
609
644
|
// removing blocks
|
|
610
645
|
for (const blockId of stagingDiff.onlyInA) {
|
|
@@ -617,10 +652,12 @@ export class ProjectMutator {
|
|
|
617
652
|
|
|
618
653
|
// creating new blocks
|
|
619
654
|
for (const blockId of stagingDiff.onlyInB) {
|
|
620
|
-
const info = new BlockInfo(blockId, {});
|
|
621
|
-
this.blockInfos.set(blockId, info);
|
|
622
655
|
const spec = newBlockSpecProvider(blockId);
|
|
623
656
|
|
|
657
|
+
// adding new block info
|
|
658
|
+
const info = new BlockInfo(blockId, {}, extractConfig(spec.blockPack.config), spec.blockPack.source);
|
|
659
|
+
this.blockInfos.set(blockId, info);
|
|
660
|
+
|
|
624
661
|
// block pack
|
|
625
662
|
const bp = createBlockPack(this.tx, spec.blockPack);
|
|
626
663
|
this.setBlockField(blockId, 'blockPack', Pl.wrapInHolder(this.tx, bp), 'NotReady');
|
|
@@ -648,6 +685,14 @@ export class ProjectMutator {
|
|
|
648
685
|
// resetting stagings affected by topology change
|
|
649
686
|
for (const blockId of stagingDiff.different) this.resetStaging(blockId);
|
|
650
687
|
|
|
688
|
+
// new actual production graph without new blocks
|
|
689
|
+
const newActualProductionGraph = productionGraph(
|
|
690
|
+
newStructure,
|
|
691
|
+
(blockId) => this.getProductionGraphBlockInfo(blockId, true),
|
|
692
|
+
);
|
|
693
|
+
|
|
694
|
+
const prodDiff = graphDiff(currentActualProductionGraph, newActualProductionGraph);
|
|
695
|
+
|
|
651
696
|
// applying changes due to topology change in production to affected nodes and
|
|
652
697
|
// all their downstreams
|
|
653
698
|
currentActualProductionGraph.traverse('downstream', [...prodDiff.different], (node) => {
|
|
@@ -965,10 +1010,15 @@ export class ProjectMutator {
|
|
|
965
1010
|
}
|
|
966
1011
|
|
|
967
1012
|
public static async load(
|
|
1013
|
+
projectHelper: ProjectHelper,
|
|
968
1014
|
tx: PlTransaction,
|
|
969
1015
|
rid: ResourceId,
|
|
970
1016
|
author?: AuthorMarker,
|
|
971
1017
|
): Promise<ProjectMutator> {
|
|
1018
|
+
//
|
|
1019
|
+
// Sending initial requests to read project state (start of round-trip #1)
|
|
1020
|
+
//
|
|
1021
|
+
|
|
972
1022
|
const fullResourceStateP = tx.getResourceData(rid, true);
|
|
973
1023
|
const schemaP = tx.getKValueJson<string>(rid, SchemaVersionKey);
|
|
974
1024
|
const lastModifiedP = tx.getKValueJson<number>(rid, ProjectLastModifiedTimestamp);
|
|
@@ -978,9 +1028,52 @@ export class ProjectMutator {
|
|
|
978
1028
|
|
|
979
1029
|
const allKVP = tx.listKeyValuesString(rid);
|
|
980
1030
|
|
|
1031
|
+
const fullResourceState = await fullResourceStateP;
|
|
1032
|
+
|
|
1033
|
+
// loading field information
|
|
1034
|
+
const blockInfoStates = new Map<string, BlockInfoState>();
|
|
1035
|
+
for (const f of fullResourceState.fields) {
|
|
1036
|
+
const projectField = parseProjectField(f.name);
|
|
1037
|
+
|
|
1038
|
+
// processing only fields with known structure
|
|
1039
|
+
if (projectField === undefined) continue;
|
|
1040
|
+
|
|
1041
|
+
let info = blockInfoStates.get(projectField.blockId);
|
|
1042
|
+
if (info === undefined) {
|
|
1043
|
+
info = {
|
|
1044
|
+
id: projectField.blockId,
|
|
1045
|
+
fields: {},
|
|
1046
|
+
};
|
|
1047
|
+
blockInfoStates.set(projectField.blockId, info);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
info.fields[projectField.fieldName] = isNullResourceId(f.value)
|
|
1051
|
+
? { modCount: 0 }
|
|
1052
|
+
: { modCount: 0, ref: f.value };
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
//
|
|
1056
|
+
// Roundtrip #1 not yet finished, but as soon as field list is received,
|
|
1057
|
+
// we can start sending requests to read states of referenced resources
|
|
1058
|
+
// (start of round-trip #2)
|
|
1059
|
+
//
|
|
1060
|
+
|
|
1061
|
+
const blockFieldRequests: [BlockInfoState, ProjectField['fieldName'], BlockFieldState, Promise<BasicResourceData | ResourceData>][] = [];
|
|
1062
|
+
blockInfoStates.forEach((info) => {
|
|
1063
|
+
const fields = info.fields;
|
|
1064
|
+
for (const [fName, state] of Object.entries(fields)) {
|
|
1065
|
+
if (state.ref === undefined) continue;
|
|
1066
|
+
if (!isResource(state.ref) || isResourceRef(state.ref))
|
|
1067
|
+
throw new Error('unexpected behaviour');
|
|
1068
|
+
const fieldName = fName as ProjectField['fieldName'];
|
|
1069
|
+
blockFieldRequests.push([
|
|
1070
|
+
info, fieldName,
|
|
1071
|
+
state, tx.getResourceData(state.ref, fieldName == 'blockPack')]);
|
|
1072
|
+
}
|
|
1073
|
+
});
|
|
1074
|
+
|
|
981
1075
|
// loading jsons
|
|
982
1076
|
const [
|
|
983
|
-
fullResourceState,
|
|
984
1077
|
schema,
|
|
985
1078
|
lastModified,
|
|
986
1079
|
meta,
|
|
@@ -988,7 +1081,6 @@ export class ProjectMutator {
|
|
|
988
1081
|
{ stagingRefreshTimestamp, blocksInLimbo },
|
|
989
1082
|
allKV,
|
|
990
1083
|
] = await Promise.all([
|
|
991
|
-
fullResourceStateP,
|
|
992
1084
|
schemaP,
|
|
993
1085
|
lastModifiedP,
|
|
994
1086
|
metaP,
|
|
@@ -1001,28 +1093,48 @@ export class ProjectMutator {
|
|
|
1001
1093
|
`Can't act on this project resource because it has a wrong schema version: ${schema}`,
|
|
1002
1094
|
);
|
|
1003
1095
|
|
|
1004
|
-
//
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
const projectField = parseProjectField(f.name);
|
|
1096
|
+
//
|
|
1097
|
+
// <- at this point we have all the responses from round-trip #1
|
|
1098
|
+
//
|
|
1008
1099
|
|
|
1009
|
-
|
|
1010
|
-
|
|
1100
|
+
//
|
|
1101
|
+
// Receiving responses from round-trip #2 and sending requests to read block pack descriptions
|
|
1102
|
+
// (start of round-trip #3)
|
|
1103
|
+
//
|
|
1011
1104
|
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1105
|
+
const blockPackRequests: [BlockInfoState, Promise<BasicResourceData>][] = [];
|
|
1106
|
+
for (const [info, fieldName, state, response] of blockFieldRequests) {
|
|
1107
|
+
const result = await response;
|
|
1108
|
+
state.value = result.data;
|
|
1109
|
+
if (isNotNullResourceId(result.error)) state.status = 'Error';
|
|
1110
|
+
else if (result.resourceReady || isNotNullResourceId(result.originalResourceId))
|
|
1111
|
+
state.status = 'Ready';
|
|
1112
|
+
else state.status = 'NotReady';
|
|
1113
|
+
|
|
1114
|
+
// For block pack we need to traverse the ref field from the resource data
|
|
1115
|
+
if (fieldName === 'blockPack') {
|
|
1116
|
+
const refField = (result as ResourceData).fields.find((f) => f.name === Pl.HolderRefField);
|
|
1117
|
+
if (refField === undefined)
|
|
1118
|
+
throw new Error('Block pack ref field is missing');
|
|
1119
|
+
blockPackRequests.push([info, tx.getResourceData(ensureResourceIdNotNull(refField.value), false)]);
|
|
1019
1120
|
}
|
|
1121
|
+
}
|
|
1020
1122
|
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1123
|
+
//
|
|
1124
|
+
// <- at this point we have all the responses from round-trip #2
|
|
1125
|
+
//
|
|
1126
|
+
|
|
1127
|
+
for (const [info, response] of blockPackRequests) {
|
|
1128
|
+
const result = await response;
|
|
1129
|
+
const bpInfo = cachedDeserialize(notEmpty(result.data)) as BlockPackInfo;
|
|
1130
|
+
info.blockConfig = extractConfig(bpInfo.config);
|
|
1131
|
+
info.blockPack = bpInfo.source;
|
|
1024
1132
|
}
|
|
1025
1133
|
|
|
1134
|
+
//
|
|
1135
|
+
// <- at this point we have all the responses from round-trip #3
|
|
1136
|
+
//
|
|
1137
|
+
|
|
1026
1138
|
// loading ctx export template to check if we already have cached materialized template in our project
|
|
1027
1139
|
const ctxExportTplEnvelope = await getPreparedExportTemplateEnvelope();
|
|
1028
1140
|
|
|
@@ -1053,26 +1165,9 @@ export class ProjectMutator {
|
|
|
1053
1165
|
blockFrontendStates.set(blockId, kv.value);
|
|
1054
1166
|
}
|
|
1055
1167
|
|
|
1056
|
-
const requests: [BlockFieldState, Promise<BasicResourceData>][] = [];
|
|
1057
|
-
blockInfoStates.forEach(({ fields }) => {
|
|
1058
|
-
for (const [, state] of Object.entries(fields)) {
|
|
1059
|
-
if (state.ref === undefined) continue;
|
|
1060
|
-
if (!isResource(state.ref) || isResourceRef(state.ref))
|
|
1061
|
-
throw new Error('unexpected behaviour');
|
|
1062
|
-
requests.push([state, tx.getResourceData(state.ref, false)]);
|
|
1063
|
-
}
|
|
1064
|
-
});
|
|
1065
|
-
for (const [state, response] of requests) {
|
|
1066
|
-
const result = await response;
|
|
1067
|
-
state.value = result.data;
|
|
1068
|
-
if (isNotNullResourceId(result.error)) state.status = 'Error';
|
|
1069
|
-
else if (result.resourceReady || isNotNullResourceId(result.originalResourceId))
|
|
1070
|
-
state.status = 'Ready';
|
|
1071
|
-
else state.status = 'NotReady';
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
1168
|
const blockInfos = new Map<string, BlockInfo>();
|
|
1075
|
-
blockInfoStates.forEach(({ id, fields }) => blockInfos.set(id,
|
|
1169
|
+
blockInfoStates.forEach(({ id, fields, blockConfig, blockPack }) => blockInfos.set(id,
|
|
1170
|
+
new BlockInfo(id, fields, notEmpty(blockConfig), notEmpty(blockPack))));
|
|
1076
1171
|
|
|
1077
1172
|
// check consistency of project state
|
|
1078
1173
|
const blockInStruct = new Set<string>();
|
|
@@ -1101,6 +1196,7 @@ export class ProjectMutator {
|
|
|
1101
1196
|
blockInfos,
|
|
1102
1197
|
blockFrontendStates,
|
|
1103
1198
|
ctxExportTplHolder,
|
|
1199
|
+
projectHelper,
|
|
1104
1200
|
);
|
|
1105
1201
|
|
|
1106
1202
|
prj.fixProblemsAndMigrate();
|
|
@@ -1140,14 +1236,16 @@ export async function createProject(
|
|
|
1140
1236
|
}
|
|
1141
1237
|
|
|
1142
1238
|
export async function withProject<T>(
|
|
1239
|
+
projectHelper: ProjectHelper,
|
|
1143
1240
|
txOrPl: PlTransaction | PlClient,
|
|
1144
1241
|
rid: ResourceId,
|
|
1145
1242
|
cb: (p: ProjectMutator) => T | Promise<T>,
|
|
1146
1243
|
): Promise<T> {
|
|
1147
|
-
return withProjectAuthored(txOrPl, rid, undefined, cb);
|
|
1244
|
+
return withProjectAuthored(projectHelper, txOrPl, rid, undefined, cb);
|
|
1148
1245
|
}
|
|
1149
1246
|
|
|
1150
1247
|
export async function withProjectAuthored<T>(
|
|
1248
|
+
projectHelper: ProjectHelper,
|
|
1151
1249
|
txOrPl: PlTransaction | PlClient,
|
|
1152
1250
|
rid: ResourceId,
|
|
1153
1251
|
author: AuthorMarker | undefined,
|
|
@@ -1155,7 +1253,7 @@ export async function withProjectAuthored<T>(
|
|
|
1155
1253
|
): Promise<T> {
|
|
1156
1254
|
if (txOrPl instanceof PlClient) {
|
|
1157
1255
|
return await txOrPl.withWriteTx('ProjectAction', async (tx) => {
|
|
1158
|
-
const mut = await ProjectMutator.load(tx, rid, author);
|
|
1256
|
+
const mut = await ProjectMutator.load(projectHelper, tx, rid, author);
|
|
1159
1257
|
const result = await cb(mut);
|
|
1160
1258
|
if (!mut.wasModified)
|
|
1161
1259
|
// skipping save and commit altogether if no modifications were
|
|
@@ -1166,7 +1264,7 @@ export async function withProjectAuthored<T>(
|
|
|
1166
1264
|
return result;
|
|
1167
1265
|
});
|
|
1168
1266
|
} else {
|
|
1169
|
-
const mut = await ProjectMutator.load(txOrPl, rid, author);
|
|
1267
|
+
const mut = await ProjectMutator.load(projectHelper, txOrPl, rid, author);
|
|
1170
1268
|
const result = await cb(mut);
|
|
1171
1269
|
mut.save();
|
|
1172
1270
|
return result;
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from '@milaboratories/pl-client';
|
|
16
16
|
import { loadTemplate } from './template_loading';
|
|
17
17
|
import { createBContextEnd, createRenderHeavyBlock, HeavyBlockOutputs } from './render_block';
|
|
18
|
-
import { notEmpty, sleep } from '@milaboratories/ts-helpers';
|
|
18
|
+
import { cachedDeserialize, notEmpty, sleep } from '@milaboratories/ts-helpers';
|
|
19
19
|
import { TemplateSpecPrepared } from '../../model/template_spec';
|
|
20
20
|
import {
|
|
21
21
|
TplSpecEnterExplicit,
|
|
@@ -269,4 +269,4 @@ function expectFields(res: ResourceData, fields: string[]) {
|
|
|
269
269
|
|
|
270
270
|
const jsonToData = (data: unknown) => Buffer.from(JSON.stringify(data));
|
|
271
271
|
|
|
272
|
-
const resDataToJson = (res: ResourceData) =>
|
|
272
|
+
const resDataToJson = (res: ResourceData) => cachedDeserialize(notEmpty(res.data));
|