@kapeta/local-cluster-service 0.54.12 → 0.55.0

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.
Files changed (42) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/definitions.d.ts +11 -0
  3. package/dist/cjs/src/storm/archetype.d.ts +12 -0
  4. package/dist/cjs/src/storm/archetype.js +98 -0
  5. package/dist/cjs/src/storm/codegen.d.ts +2 -0
  6. package/dist/cjs/src/storm/codegen.js +28 -4
  7. package/dist/cjs/src/storm/event-parser.d.ts +7 -4
  8. package/dist/cjs/src/storm/event-parser.js +190 -160
  9. package/dist/cjs/src/storm/events.d.ts +1 -0
  10. package/dist/cjs/src/storm/predefined.d.ts +27 -0
  11. package/dist/cjs/src/storm/predefined.js +64 -0
  12. package/dist/cjs/src/storm/routes.js +3 -3
  13. package/dist/cjs/test/storm/codegen.test.d.ts +5 -0
  14. package/dist/cjs/test/storm/codegen.test.js +41 -0
  15. package/dist/cjs/test/storm/event-parser.test.d.ts +25 -1
  16. package/dist/cjs/test/storm/event-parser.test.js +34 -15
  17. package/dist/cjs/test/storm/predefined-user-events.json +13 -0
  18. package/dist/esm/src/storm/archetype.d.ts +12 -0
  19. package/dist/esm/src/storm/archetype.js +98 -0
  20. package/dist/esm/src/storm/codegen.d.ts +2 -0
  21. package/dist/esm/src/storm/codegen.js +28 -4
  22. package/dist/esm/src/storm/event-parser.d.ts +7 -4
  23. package/dist/esm/src/storm/event-parser.js +190 -160
  24. package/dist/esm/src/storm/events.d.ts +1 -0
  25. package/dist/esm/src/storm/predefined.d.ts +27 -0
  26. package/dist/esm/src/storm/predefined.js +64 -0
  27. package/dist/esm/src/storm/routes.js +3 -3
  28. package/dist/esm/test/storm/codegen.test.d.ts +5 -0
  29. package/dist/esm/test/storm/codegen.test.js +41 -0
  30. package/dist/esm/test/storm/event-parser.test.d.ts +25 -1
  31. package/dist/esm/test/storm/event-parser.test.js +34 -15
  32. package/dist/esm/test/storm/predefined-user-events.json +13 -0
  33. package/package.json +6 -1
  34. package/src/storm/archetype.ts +85 -0
  35. package/src/storm/codegen.ts +34 -4
  36. package/src/storm/event-parser.ts +200 -159
  37. package/src/storm/events.ts +1 -0
  38. package/src/storm/predefined.ts +52 -0
  39. package/src/storm/routes.ts +3 -3
  40. package/test/storm/codegen.test.ts +46 -0
  41. package/test/storm/event-parser.test.ts +32 -10
  42. package/test/storm/predefined-user-events.json +13 -0
@@ -36,11 +36,15 @@ import {
36
36
  } from '@kapeta/kaplang-core';
37
37
  import { v5 as uuid } from 'uuid';
38
38
  import { definitionsManager } from '../definitionsManager';
39
+ import { PREDEFINED_BLOCKS } from './predefined';
40
+
41
+ import _ from 'lodash';
39
42
 
40
43
  export interface BlockDefinitionInfo {
41
44
  uri: string;
42
45
  content: BlockDefinition;
43
46
  aiName: string;
47
+ archetype?: string;
44
48
  }
45
49
 
46
50
  export interface StormDefinitions {
@@ -268,7 +272,7 @@ export class StormEventParser {
268
272
  /**
269
273
  * Builds plan and block definitions - and enriches events with relevant refs and ids
270
274
  */
271
- public processEvent(handle: string, evt: StormEvent): StormDefinitions {
275
+ public async processEvent(handle: string, evt: StormEvent): Promise<StormDefinitions> {
272
276
  let blockInfo;
273
277
  this.events.push(evt);
274
278
  switch (evt.type) {
@@ -338,7 +342,7 @@ export class StormEventParser {
338
342
  break;
339
343
  }
340
344
 
341
- return this.toResult(handle);
345
+ return await this.toResult(handle);
342
346
  }
343
347
 
344
348
  public getEvents(): StormEvent[] {
@@ -356,9 +360,9 @@ export class StormEventParser {
356
360
  return this.error;
357
361
  }
358
362
 
359
- public toResult(handle: string): StormDefinitions {
363
+ public async toResult(handle: string): Promise<StormDefinitions> {
360
364
  const planRef = StormEventParser.toRef(handle, this.planName || 'undefined');
361
- const blockDefinitions = this.toBlockDefinitions(handle);
365
+ const blockDefinitions = await this.toBlockDefinitions(handle);
362
366
  const refIdMap: { [key: string]: string } = {};
363
367
  const blocks = Object.entries(blockDefinitions).map(([ref, block]) => {
364
368
  // Create a deterministic uuid
@@ -504,163 +508,182 @@ export class StormEventParser {
504
508
  };
505
509
  }
506
510
 
507
- public toBlockDefinitions(handle: string): { [key: string]: BlockDefinitionInfo } {
511
+ public async toBlockDefinitions(handle: string): Promise<{ [key: string]: BlockDefinitionInfo }> {
508
512
  const result: { [key: string]: BlockDefinitionInfo } = {};
509
513
 
510
- Object.entries(this.blocks).forEach(([, blockInfo]) => {
514
+ for (const [, blockInfo] of Object.entries(this.blocks)) {
511
515
  const blockRef = StormEventParser.toRef(handle, blockInfo.name);
512
516
 
513
- const blockDefinitionInfo: BlockDefinitionInfo = {
514
- uri: blockRef.toNormalizedString(),
515
- aiName: blockInfo.name,
516
- content: {
517
- kind: this.toBlockKind(blockInfo.type),
518
- metadata: {
519
- title: blockInfo.name,
520
- name: blockRef.fullName,
521
- description: blockInfo.description,
522
- },
523
- spec: {
524
- entities: {
525
- types: [],
526
- source: {
527
- type: KAPLANG_ID,
528
- version: KAPLANG_VERSION,
529
- value: '',
530
- },
517
+ let blockDefinitionInfo: BlockDefinitionInfo;
518
+
519
+ if (blockInfo.archetype) {
520
+ blockDefinitionInfo = await this.resolveArchetypeBlockDefinition(blockRef, blockInfo);
521
+ } else {
522
+ blockDefinitionInfo = this.createBlockDefinitionInfo(blockRef, blockInfo, handle);
523
+ }
524
+
525
+ result[blockRef.toNormalizedString()] = blockDefinitionInfo;
526
+ }
527
+
528
+ return result;
529
+ }
530
+
531
+ private createBlockDefinitionInfo(
532
+ blockRef: KapetaURI,
533
+ blockInfo: StormBlockInfoFilled,
534
+ handle: string
535
+ ): BlockDefinitionInfo {
536
+ const blockDefinitionInfo: BlockDefinitionInfo = {
537
+ uri: blockRef.toNormalizedString(),
538
+ aiName: blockInfo.name,
539
+ content: {
540
+ kind: this.toBlockKind(blockInfo.type),
541
+ metadata: {
542
+ title: blockInfo.name,
543
+ name: blockRef.fullName,
544
+ description: blockInfo.description,
545
+ },
546
+ spec: {
547
+ entities: {
548
+ types: [],
549
+ source: {
550
+ type: KAPLANG_ID,
551
+ version: KAPLANG_VERSION,
552
+ value: '',
531
553
  },
532
- target: this.toBlockTarget(handle, blockInfo.type),
533
- providers: [],
534
- consumers: [],
535
554
  },
555
+ target: this.toBlockTarget(handle, blockInfo.type),
556
+ providers: [],
557
+ consumers: [],
536
558
  },
537
- };
559
+ },
560
+ };
538
561
 
539
- const blockSpec = blockDefinitionInfo.content.spec;
562
+ const blockSpec = blockDefinitionInfo.content.spec;
540
563
 
541
- const apiResources: { [key: string]: Resource | undefined } = {};
542
- let dbResource: Resource | undefined = undefined;
564
+ const apiResources: { [key: string]: Resource | undefined } = {};
565
+ let dbResource: Resource | undefined = undefined;
543
566
 
544
- (blockInfo.resources || []).forEach((resource) => {
545
- const port = {
546
- type: this.toPortType(resource.type),
547
- };
548
- switch (resource.type) {
549
- case 'API': {
550
- const apiResource = {
551
- kind: this.toResourceKind(resource.type),
552
- metadata: {
553
- name: resource.name,
554
- description: resource.description,
555
- },
556
- spec: {
557
- port,
558
- methods: {},
559
- source: {
560
- type: KAPLANG_ID,
561
- version: KAPLANG_VERSION,
562
- value: '',
563
- } satisfies SourceCode,
564
- },
565
- };
566
- apiResources[resource.name] = apiResource;
567
- blockSpec.providers!.push(apiResource);
567
+ (blockInfo.resources || []).forEach((resource) => {
568
+ const port = {
569
+ type: this.toPortType(resource.type),
570
+ };
571
+ switch (resource.type) {
572
+ case 'API': {
573
+ const apiResource = {
574
+ kind: this.toResourceKind(resource.type),
575
+ metadata: {
576
+ name: resource.name,
577
+ description: resource.description,
578
+ },
579
+ spec: {
580
+ port,
581
+ methods: {},
582
+ source: {
583
+ type: KAPLANG_ID,
584
+ version: KAPLANG_VERSION,
585
+ value: '',
586
+ } satisfies SourceCode,
587
+ },
588
+ };
589
+ apiResources[resource.name] = apiResource;
590
+ blockSpec.providers!.push(apiResource);
591
+ break;
592
+ }
593
+ case 'CLIENT':
594
+ blockSpec.consumers!.push({
595
+ kind: this.toResourceKind(resource.type),
596
+ metadata: {
597
+ name: resource.name,
598
+ description: resource.description,
599
+ },
600
+ spec: {
601
+ port,
602
+ methods: {},
603
+ source: {
604
+ type: KAPLANG_ID,
605
+ version: KAPLANG_VERSION,
606
+ value: '',
607
+ } satisfies SourceCode,
608
+ },
609
+ });
610
+ break;
611
+ case 'EXTERNAL_API':
612
+ break;
613
+ case 'EXCHANGE':
614
+ break;
615
+ case 'PUBLISHER':
616
+ break;
617
+ case 'QUEUE':
618
+ break;
619
+ case 'SUBSCRIBER':
620
+ break;
621
+ case 'JWTPROVIDER':
622
+ case 'WEBPAGE':
623
+ blockSpec.providers!.push({
624
+ kind: this.toResourceKind(resource.type),
625
+ metadata: {
626
+ name: resource.name,
627
+ description: resource.description,
628
+ },
629
+ spec: {
630
+ port,
631
+ },
632
+ });
633
+ break;
634
+ case 'DATABASE':
635
+ if (dbResource) {
568
636
  break;
569
637
  }
570
- case 'CLIENT':
571
- blockSpec.consumers!.push({
572
- kind: this.toResourceKind(resource.type),
573
- metadata: {
574
- name: resource.name,
575
- description: resource.description,
576
- },
577
- spec: {
578
- port,
579
- methods: {},
580
- source: {
581
- type: KAPLANG_ID,
582
- version: KAPLANG_VERSION,
583
- value: '',
584
- } satisfies SourceCode,
585
- },
586
- });
587
- break;
588
- case 'EXTERNAL_API':
589
- break;
590
- case 'EXCHANGE':
591
- break;
592
- case 'PUBLISHER':
593
- break;
594
- case 'QUEUE':
595
- break;
596
- case 'SUBSCRIBER':
597
- break;
598
- case 'JWTPROVIDER':
599
- case 'WEBPAGE':
600
- blockSpec.providers!.push({
601
- kind: this.toResourceKind(resource.type),
602
- metadata: {
603
- name: resource.name,
604
- description: resource.description,
605
- },
606
- spec: {
607
- port,
608
- },
609
- });
610
- break;
611
- case 'DATABASE':
612
- if (dbResource) {
613
- break;
614
- }
615
- dbResource = {
616
- kind: this.toResourceKind(resource.type),
617
- metadata: {
618
- name: resource.name,
619
- description: resource.description,
620
- },
621
- spec: {
622
- port,
623
- models: [],
624
- source: {
625
- type: KAPLANG_ID,
626
- version: KAPLANG_VERSION,
627
- value: '',
628
- } satisfies SourceCode,
629
- },
630
- };
631
- blockSpec.consumers!.push(dbResource);
632
- break;
633
- case 'JWTCONSUMER':
634
- case 'WEBFRAGMENT':
635
- case 'SMTPCLIENT':
636
- blockSpec.consumers!.push({
637
- kind: this.toResourceKind(resource.type),
638
- metadata: {
639
- name: resource.name,
640
- description: resource.description,
641
- },
642
- spec: {
643
- port,
644
- },
645
- });
646
- }
647
- });
638
+ dbResource = {
639
+ kind: this.toResourceKind(resource.type),
640
+ metadata: {
641
+ name: resource.name,
642
+ description: resource.description,
643
+ },
644
+ spec: {
645
+ port,
646
+ models: [],
647
+ source: {
648
+ type: KAPLANG_ID,
649
+ version: KAPLANG_VERSION,
650
+ value: '',
651
+ } satisfies SourceCode,
652
+ },
653
+ };
654
+ blockSpec.consumers!.push(dbResource);
655
+ break;
656
+ case 'JWTCONSUMER':
657
+ case 'WEBFRAGMENT':
658
+ case 'SMTPCLIENT':
659
+ blockSpec.consumers!.push({
660
+ kind: this.toResourceKind(resource.type),
661
+ metadata: {
662
+ name: resource.name,
663
+ description: resource.description,
664
+ },
665
+ spec: {
666
+ port,
667
+ },
668
+ });
669
+ }
670
+ });
648
671
 
649
- blockInfo.apis.forEach((api) => {
650
- const dslApi = DSLAPIParser.parse(api, {
651
- ignoreSemantics: true,
652
- }) as (DSLMethod | DSLController)[];
653
-
654
- let exactMatch = false;
655
- if (dslApi[0] && dslApi[0].type == DSLEntityType.CONTROLLER) {
656
- const name = dslApi[0].name.toLowerCase();
657
- const apiResourceName = Object.keys(apiResources).find((key) => key.indexOf(name) > -1);
658
- if (apiResourceName) {
659
- const exactResource = apiResources[apiResourceName];
660
- exactResource!.spec.source.value += api + '\n\n';
661
- exactMatch = true;
662
- }
672
+ blockInfo.apis.forEach((api) => {
673
+ const dslApi = DSLAPIParser.parse(api, {
674
+ ignoreSemantics: true,
675
+ }) as (DSLMethod | DSLController)[];
676
+
677
+ let exactMatch = false;
678
+ if (dslApi[0] && dslApi[0].type == DSLEntityType.CONTROLLER) {
679
+ const name = dslApi[0].name.toLowerCase();
680
+ const apiResourceName = Object.keys(apiResources).find((key) => key.indexOf(name) > -1);
681
+ if (apiResourceName) {
682
+ const exactResource = apiResources[apiResourceName];
683
+ exactResource!.spec.source.value += api + '\n\n';
684
+ exactMatch = true;
663
685
  }
686
+ }
664
687
 
665
688
  if (!exactMatch) {
666
689
  // if we couldn't place the given api on the exact resource we just park it on the first
@@ -675,20 +698,17 @@ export class StormEventParser {
675
698
  }
676
699
  });
677
700
 
678
- blockInfo.types.forEach((type) => {
679
- blockSpec.entities!.source!.value += type + '\n';
680
- });
681
-
682
- if (dbResource) {
683
- blockInfo.models.forEach((model) => {
684
- dbResource!.spec.source.value += model + '\n';
685
- });
686
- }
687
-
688
- result[blockRef.toNormalizedString()] = blockDefinitionInfo;
701
+ blockInfo.types.forEach((type) => {
702
+ blockSpec.entities!.source!.value += type + '\n';
689
703
  });
690
704
 
691
- return result;
705
+ if (dbResource) {
706
+ blockInfo.models.forEach((model) => {
707
+ dbResource!.spec.source.value += model + '\n';
708
+ });
709
+ }
710
+
711
+ return blockDefinitionInfo;
692
712
  }
693
713
 
694
714
  private toResourceKind(type: StormResourceType) {
@@ -861,4 +881,25 @@ export class StormEventParser {
861
881
  }
862
882
  return undefined;
863
883
  }
884
+
885
+ private async resolveArchetypeBlockDefinition(
886
+ blockRef: KapetaURI,
887
+ blockInfo: StormBlockInfoFilled
888
+ ): Promise<BlockDefinitionInfo> {
889
+ const predefinedBlock = PREDEFINED_BLOCKS.get(blockInfo.archetype!);
890
+ if (!predefinedBlock) {
891
+ throw new Error('Predefined block not found for archetype [' + blockInfo.archetype + ']');
892
+ }
893
+
894
+ const blockDefinition = await predefinedBlock.getBlockDefinition();
895
+ _.set(blockDefinition!, ['metadata', 'name'], blockRef.fullName);
896
+ _.set(blockDefinition!, ['metadata', 'title'], blockRef.name);
897
+
898
+ return {
899
+ uri: blockRef.toNormalizedString(),
900
+ aiName: blockInfo.name,
901
+ content: blockDefinition,
902
+ archetype: blockInfo.archetype,
903
+ } as BlockDefinitionInfo;
904
+ }
864
905
  }
@@ -32,6 +32,7 @@ export interface StormBlockInfo {
32
32
  }[];
33
33
  blockRef?: string;
34
34
  instanceId?: string;
35
+ archetype?: string;
35
36
  }
36
37
 
37
38
  export interface StormBlockInfoFilled extends StormBlockInfo {
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Copyright 2023 Kapeta Inc.
3
+ * SPDX-License-Identifier: BUSL-1.1
4
+ */
5
+
6
+ import { BlockDefinition } from '@kapeta/schemas';
7
+ import * as yaml from 'js-yaml';
8
+
9
+ export class PredefinedBlock {
10
+ constructor(
11
+ public archetype: string,
12
+ public kapetaYml: string,
13
+ public gitRepo: {
14
+ owner: string;
15
+ repo: string;
16
+ path: string;
17
+ }
18
+ ) {}
19
+
20
+ public getGitRepo(): { owner: string; repo: string; path: string } {
21
+ return this.gitRepo;
22
+ }
23
+
24
+ private async getKapetaYML(): Promise<string> {
25
+ const response = await fetch(this.kapetaYml);
26
+ if (!response.ok) {
27
+ throw new Error(`HTTP error! Status: ${response.status}`);
28
+ }
29
+ return await response.text();
30
+ }
31
+
32
+ public async getBlockDefinition(): Promise<BlockDefinition> {
33
+ const kapetaYml = await this.getKapetaYML();
34
+ return yaml.load(kapetaYml) as BlockDefinition;
35
+ }
36
+ }
37
+
38
+ const predefinedBlocks: PredefinedBlock[] = [
39
+ new PredefinedBlock(
40
+ 'USER_SERVICE',
41
+ 'https://raw.githubusercontent.com/kapetacom/everything/master/blocks/everything-user/kapeta.yml',
42
+ {
43
+ owner: 'kapetacom',
44
+ repo: 'everything',
45
+ path: 'blocks/everything-user',
46
+ }
47
+ ),
48
+ ];
49
+
50
+ export const PREDEFINED_BLOCKS = new Map<string, PredefinedBlock>(
51
+ predefinedBlocks.map((block) => [block.archetype, block])
52
+ );
@@ -54,9 +54,9 @@ router.post('/:handle/all', async (req: KapetaBodyRequest, res: Response) => {
54
54
 
55
55
  // Helper to avoid sending the plan multiple times in a row
56
56
  const sendUpdatedPlan = _.debounce(sendDefinitions, 50, { maxWait: 200 });
57
- metaStream.on('data', (data: StormEvent) => {
57
+ metaStream.on('data', async (data: StormEvent) => {
58
58
  try {
59
- const result = eventParser.processEvent(handle, data);
59
+ const result = await eventParser.processEvent(handle, data);
60
60
 
61
61
  switch (data.type) {
62
62
  case 'CREATE_API':
@@ -103,7 +103,7 @@ router.post('/:handle/all', async (req: KapetaBodyRequest, res: Response) => {
103
103
  return;
104
104
  }
105
105
 
106
- const result = eventParser.toResult(handle);
106
+ const result = await eventParser.toResult(handle);
107
107
 
108
108
  if (metaStream.isAborted()) {
109
109
  return;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Copyright 2023 Kapeta Inc.
3
+ * SPDX-License-Identifier: BUSL-1.1
4
+ */
5
+
6
+ import predefinedUserEvents from './predefined-user-events.json';
7
+ import { StormEventParser } from '../../src/storm/event-parser';
8
+ import { parserOptions } from './event-parser.test';
9
+ import { StormEvent } from '../../src/storm/events';
10
+ import { StormCodegen } from '../../src/storm/codegen';
11
+ import uuid from 'node-uuid';
12
+ import { StormStream } from '../../src/storm/stream';
13
+
14
+ describe('codegen', () => {
15
+ it('predefined components', async () => {
16
+ const events = predefinedUserEvents as StormEvent[];
17
+ const parser = new StormEventParser(parserOptions);
18
+ for (const event of events) {
19
+ await parser.processEvent('kapeta', event);
20
+ }
21
+
22
+ const result = await parser.toResult('kapeta');
23
+
24
+ const conversationId = uuid.v4().toString();
25
+
26
+ let codegen = new StormCodegen(conversationId, '', result.blocks, parser.getEvents());
27
+ let codegenPromise = consumeStream(codegen.getStream());
28
+ await codegen.process();
29
+
30
+ const stormEvents = await codegenPromise;
31
+ expect(stormEvents[0].type).toBe('FILE_DONE');
32
+ expect(stormEvents[stormEvents.length - 1].type).toBe('BLOCK_READY');
33
+ });
34
+ });
35
+
36
+ async function consumeStream(stream: StormStream): Promise<StormEvent[]> {
37
+ const events: StormEvent[] = [];
38
+ return new Promise<StormEvent[]>((resolve) => {
39
+ stream.on('data', (data) => {
40
+ events.push(data);
41
+ });
42
+
43
+ stream.waitForDone();
44
+ resolve(events);
45
+ });
46
+ }
@@ -6,8 +6,9 @@
6
6
  import { StormEventParser } from '../../src/storm/event-parser';
7
7
  import { StormEvent } from '../../src/storm/events';
8
8
  import simpleBlogEvents from './simple-blog-events.json';
9
+ import predefinedUserEvents from './predefined-user-events.json';
9
10
 
10
- const parserOptions = {
11
+ export const parserOptions = {
11
12
  serviceKind: 'kapeta/block-service:local',
12
13
  serviceLanguage: 'kapeta/language-target-nodejs-ts:local',
13
14
 
@@ -138,11 +139,13 @@ const events: StormEvent[] = [
138
139
  ];
139
140
 
140
141
  describe('event-parser', () => {
141
- it('it can parse events into a plan and blocks with proper layout', () => {
142
+ it('it can parse events into a plan and blocks with proper layout', async () => {
142
143
  const parser = new StormEventParser(parserOptions);
143
- events.forEach((event) => parser.processEvent('kapeta', event));
144
+ for (const event of events) {
145
+ await parser.processEvent('kapeta', event);
146
+ }
144
147
 
145
- const result = parser.toResult('kapeta');
148
+ const result = await parser.toResult('kapeta');
146
149
 
147
150
  expect(result.plan.metadata.name).toBe('kapeta/my-plan');
148
151
  expect(result.plan.metadata.description).toBe('my plan description');
@@ -181,21 +184,40 @@ describe('event-parser', () => {
181
184
  expect(result.plan.spec.connections[0].provider.resourceName).toBe(apiResource?.metadata.name);
182
185
  });
183
186
 
184
- it('it will split api into correct provider', () => {
187
+ it('it will split api into correct provider', async () => {
185
188
  const events = simpleBlogEvents as StormEvent[];
186
189
  const parser = new StormEventParser(parserOptions);
187
- events.forEach((event) => parser.processEvent('kapeta', event));
190
+ for (const event of events) {
191
+ await parser.processEvent('kapeta', event);
192
+ }
188
193
 
189
- const result = parser.toResult('kapeta');
194
+ const result = await parser.toResult('kapeta');
190
195
 
191
196
  const blogService = result.blocks.find((block) => block.aiName === 'blog-service');
192
197
  expect(blogService).toBeDefined();
193
198
  expect(blogService?.content).toBeDefined();
194
199
 
195
- const apiProviders = blogService?.content?.spec?.providers?.filter((provider) => provider.kind === 'kapeta/block-type-api:local');
200
+ const apiProviders = blogService?.content?.spec?.providers?.filter(
201
+ (provider) => provider.kind === 'kapeta/block-type-api:local'
202
+ );
196
203
  expect(apiProviders).toBeDefined();
197
204
  expect(apiProviders!.length).toBe(2);
198
- expect(apiProviders!["0"].spec.source.value).not.toBe('');
199
- expect(apiProviders!["1"].spec.source.value).not.toBe('');
205
+ expect(apiProviders!['0'].spec.source.value).not.toBe('');
206
+ expect(apiProviders!['1'].spec.source.value).not.toBe('');
207
+ });
208
+
209
+ it('predefined components', async () => {
210
+ const events = predefinedUserEvents as StormEvent[];
211
+ const parser = new StormEventParser(parserOptions);
212
+ for (const event of events) {
213
+ await parser.processEvent('kapeta', event);
214
+ }
215
+
216
+ const result = await parser.toResult('kapeta');
217
+ expect(result.blocks.length).toBe(1);
218
+ expect(result.blocks[0].content.metadata.title).toBe('user-service');
219
+ expect(result.blocks[0].content.metadata.title).toBe('user-service');
220
+ expect(result.blocks[0].content.metadata.name).toBe('kapeta/user-service');
221
+ expect(result.blocks[0].archetype).toBeDefined();
200
222
  });
201
223
  });
@@ -0,0 +1,13 @@
1
+ [
2
+ {
3
+ "type": "CREATE_BLOCK",
4
+ "reason": "Handles user registration, authentication, and authorization.",
5
+ "payload": {
6
+ "name": "user-service",
7
+ "resources": [],
8
+ "type": "BACKEND",
9
+ "archetype": "USER_SERVICE"
10
+ },
11
+ "created": 1720597268038
12
+ }
13
+ ]