@eventcatalog/core 4.10.15 → 4.11.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.
Files changed (165) hide show
  1. package/dist/analytics/analytics.cjs +1 -1
  2. package/dist/analytics/analytics.js +2 -2
  3. package/dist/analytics/log-build.cjs +1 -1
  4. package/dist/analytics/log-build.js +3 -3
  5. package/dist/astro-output.cjs +60 -1
  6. package/dist/astro-output.js +1 -1
  7. package/dist/{chunk-U52FCZQ6.js → chunk-2YJRF4ZX.js} +1 -1
  8. package/dist/chunk-634NFV5U.js +75 -0
  9. package/dist/{chunk-I6HUOX4R.js → chunk-AXDNSGAN.js} +1 -1
  10. package/dist/chunk-DHH5U5PF.js +53 -0
  11. package/dist/{chunk-A4JRWRCV.js → chunk-DSCIWCX2.js} +1 -1
  12. package/dist/chunk-FWXEV4LF.js +19 -0
  13. package/dist/{chunk-Q2ATWE7N.js → chunk-GZVDXCYF.js} +1 -1
  14. package/dist/{chunk-7LWOCX4R.js → chunk-RYNPEIHQ.js} +1 -1
  15. package/dist/{chunk-HBVTU6ZP.js → chunk-YYVW4DCO.js} +1 -1
  16. package/dist/constants.cjs +1 -1
  17. package/dist/constants.js +1 -1
  18. package/dist/copy-core.cjs +88 -0
  19. package/dist/copy-core.d.cts +4 -0
  20. package/dist/copy-core.d.ts +4 -0
  21. package/dist/copy-core.js +8 -0
  22. package/dist/eventcatalog.cjs +234 -115
  23. package/dist/eventcatalog.js +36 -30
  24. package/dist/federation/federate.js +3 -3
  25. package/dist/federation/source-provider.js +2 -2
  26. package/dist/generate.cjs +1 -1
  27. package/dist/generate.js +3 -3
  28. package/dist/update-check.cjs +44 -0
  29. package/dist/update-check.d.cts +18 -0
  30. package/dist/update-check.d.ts +18 -0
  31. package/dist/update-check.js +8 -0
  32. package/dist/utils/cli-logger.cjs +1 -1
  33. package/dist/utils/cli-logger.js +2 -2
  34. package/eventcatalog/astro.config.mjs +8 -1
  35. package/eventcatalog/public/icons/languages/android.svg +1 -0
  36. package/eventcatalog/public/icons/languages/angular.svg +1 -0
  37. package/eventcatalog/public/icons/languages/apple.svg +1 -0
  38. package/eventcatalog/public/icons/languages/arduino.svg +1 -0
  39. package/eventcatalog/public/icons/languages/bash.svg +1 -0
  40. package/eventcatalog/public/icons/languages/bun.svg +1 -0
  41. package/eventcatalog/public/icons/languages/c.svg +1 -0
  42. package/eventcatalog/public/icons/languages/clojure.svg +1 -0
  43. package/eventcatalog/public/icons/languages/cpp.svg +1 -0
  44. package/eventcatalog/public/icons/languages/crystal.svg +1 -0
  45. package/eventcatalog/public/icons/languages/csharp.svg +1 -0
  46. package/eventcatalog/public/icons/languages/css.svg +1 -0
  47. package/eventcatalog/public/icons/languages/curl.svg +1 -0
  48. package/eventcatalog/public/icons/languages/dart.svg +1 -0
  49. package/eventcatalog/public/icons/languages/deno.svg +1 -0
  50. package/eventcatalog/public/icons/languages/docker.svg +1 -0
  51. package/eventcatalog/public/icons/languages/elm.svg +1 -0
  52. package/eventcatalog/public/icons/languages/erlang.svg +1 -0
  53. package/eventcatalog/public/icons/languages/flutter.svg +1 -0
  54. package/eventcatalog/public/icons/languages/fortran.svg +1 -0
  55. package/eventcatalog/public/icons/languages/fsharp.svg +1 -0
  56. package/eventcatalog/public/icons/languages/git.svg +1 -0
  57. package/eventcatalog/public/icons/languages/gradle.svg +1 -0
  58. package/eventcatalog/public/icons/languages/graphql.svg +1 -0
  59. package/eventcatalog/public/icons/languages/grpc.svg +1 -0
  60. package/eventcatalog/public/icons/languages/haskell.svg +1 -0
  61. package/eventcatalog/public/icons/languages/hasura.svg +1 -0
  62. package/eventcatalog/public/icons/languages/html.svg +1 -0
  63. package/eventcatalog/public/icons/languages/httpie.svg +1 -0
  64. package/eventcatalog/public/icons/languages/javascript.svg +1 -0
  65. package/eventcatalog/public/icons/languages/json-schema.svg +1 -0
  66. package/eventcatalog/public/icons/languages/json.svg +1 -0
  67. package/eventcatalog/public/icons/languages/julia.svg +1 -0
  68. package/eventcatalog/public/icons/languages/jupyter.svg +1 -0
  69. package/eventcatalog/public/icons/languages/kafka.svg +1 -0
  70. package/eventcatalog/public/icons/languages/kotlin.svg +1 -0
  71. package/eventcatalog/public/icons/languages/lua.svg +1 -0
  72. package/eventcatalog/public/icons/languages/markdown.svg +1 -0
  73. package/eventcatalog/public/icons/languages/maven.svg +1 -0
  74. package/eventcatalog/public/icons/languages/mongodb.svg +1 -0
  75. package/eventcatalog/public/icons/languages/mysql.svg +1 -0
  76. package/eventcatalog/public/icons/languages/nginx.svg +1 -0
  77. package/eventcatalog/public/icons/languages/nim.svg +1 -0
  78. package/eventcatalog/public/icons/languages/ocaml.svg +1 -0
  79. package/eventcatalog/public/icons/languages/perl.svg +1 -0
  80. package/eventcatalog/public/icons/languages/php.svg +1 -0
  81. package/eventcatalog/public/icons/languages/postgresql.svg +1 -0
  82. package/eventcatalog/public/icons/languages/prisma.svg +1 -0
  83. package/eventcatalog/public/icons/languages/purescript.svg +1 -0
  84. package/eventcatalog/public/icons/languages/python.svg +1 -0
  85. package/eventcatalog/public/icons/languages/r.svg +1 -0
  86. package/eventcatalog/public/icons/languages/react.svg +1 -0
  87. package/eventcatalog/public/icons/languages/reasonml.svg +1 -0
  88. package/eventcatalog/public/icons/languages/redis.svg +1 -0
  89. package/eventcatalog/public/icons/languages/ruby.svg +1 -0
  90. package/eventcatalog/public/icons/languages/rust.svg +1 -0
  91. package/eventcatalog/public/icons/languages/sass.svg +1 -0
  92. package/eventcatalog/public/icons/languages/scala.svg +1 -0
  93. package/eventcatalog/public/icons/languages/solidity.svg +1 -0
  94. package/eventcatalog/public/icons/languages/spring.svg +1 -0
  95. package/eventcatalog/public/icons/languages/sqlite.svg +1 -0
  96. package/eventcatalog/public/icons/languages/svelte.svg +1 -0
  97. package/eventcatalog/public/icons/languages/swagger.svg +1 -0
  98. package/eventcatalog/public/icons/languages/swift.svg +1 -0
  99. package/eventcatalog/public/icons/languages/tailwind.svg +1 -0
  100. package/eventcatalog/public/icons/languages/terraform.svg +1 -0
  101. package/eventcatalog/public/icons/languages/toml.svg +1 -0
  102. package/eventcatalog/public/icons/languages/typescript.svg +1 -0
  103. package/eventcatalog/public/icons/languages/unity.svg +1 -0
  104. package/eventcatalog/public/icons/languages/vim.svg +1 -0
  105. package/eventcatalog/public/icons/languages/vue.svg +1 -0
  106. package/eventcatalog/public/icons/languages/webassembly.svg +1 -0
  107. package/eventcatalog/public/icons/languages/yaml.svg +1 -0
  108. package/eventcatalog/public/icons/languages/zig.svg +1 -0
  109. package/eventcatalog/src/components/MDX/CodeGroup/CodeGroup.astro +12 -0
  110. package/eventcatalog/src/components/MDX/CodeGroup/CodeGroup.tsx +176 -0
  111. package/eventcatalog/src/components/MDX/CodeGroup/MarkdownCodeGroup.tsx +22 -0
  112. package/eventcatalog/src/components/MDX/CodeGroup/code-group-icons.ts +345 -0
  113. package/eventcatalog/src/components/MDX/CodeGroup/code-group.css +22 -0
  114. package/eventcatalog/src/components/MDX/CodeGroup/parse-panels.ts +11 -0
  115. package/eventcatalog/src/components/MDX/Columns/Column.astro +10 -0
  116. package/eventcatalog/src/components/MDX/Columns/Columns.astro +14 -0
  117. package/eventcatalog/src/components/MDX/Columns/MarkdownColumns.tsx +37 -0
  118. package/eventcatalog/src/components/MDX/Columns/layout.ts +16 -0
  119. package/eventcatalog/src/components/MDX/components.tsx +6 -0
  120. package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +1 -1
  121. package/eventcatalog/src/components/SchemaExplorer/ExamplesViewer.tsx +151 -114
  122. package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +510 -248
  123. package/eventcatalog/src/components/SchemaExplorer/SchemaFlows.tsx +76 -0
  124. package/eventcatalog/src/components/SchemaExplorer/SchemaGraph.tsx +47 -0
  125. package/eventcatalog/src/components/SchemaExplorer/SchemaPageViewer.tsx +11 -0
  126. package/eventcatalog/src/components/SchemaExplorer/SchemaUsage.tsx +17 -0
  127. package/eventcatalog/src/components/SchemaExplorer/types.ts +48 -3
  128. package/eventcatalog/src/components/SchemaExplorer/utils.ts +28 -1
  129. package/eventcatalog/src/content.config.ts +28 -0
  130. package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/[...filename].astro +38 -7
  131. package/eventcatalog/src/pages/schemas/[type]/[id]/[version]/index.astro +144 -37
  132. package/eventcatalog/src/pages/schemas/explorer/index.astro +5 -0
  133. package/eventcatalog/src/plugins/empty-collection-warning.d.ts +21 -0
  134. package/eventcatalog/src/plugins/empty-collection-warning.mjs +107 -0
  135. package/eventcatalog/src/plugins/quiet-empty-collections-logger.mjs +11 -0
  136. package/eventcatalog/src/remark-plugins/code-group.ts +39 -0
  137. package/eventcatalog/src/remark-plugins/markdown-code-group.ts +59 -0
  138. package/eventcatalog/src/remark-plugins/markdown-columns.ts +66 -0
  139. package/eventcatalog/src/utils/collections/channels.ts +12 -4
  140. package/eventcatalog/src/utils/collections/examples.ts +133 -41
  141. package/eventcatalog/src/utils/collections/flows.ts +43 -0
  142. package/eventcatalog/src/utils/collections/messages.ts +2 -2
  143. package/eventcatalog/src/utils/collections/schemas.ts +15 -0
  144. package/eventcatalog/src/utils/node-graphs/message-node-graph.ts +17 -15
  145. package/eventcatalog/src/utils/schema-explorer.ts +73 -7
  146. package/eventcatalog/src/utils/schema-usage-graph.ts +92 -0
  147. package/package.json +5 -4
  148. package/dist/chunk-RDYYZQYU.js +0 -18
  149. package/eventcatalog/src/components/EnvironmentDropdown.test.ts +0 -64
  150. package/eventcatalog/src/components/Grids/message-link.spec.ts +0 -59
  151. package/eventcatalog/src/components/MDX/CustomProperties/custom-properties.spec.ts +0 -38
  152. package/eventcatalog/src/components/MDX/SchemaViewer/schema-viewer-utils.spec.ts +0 -116
  153. package/eventcatalog/src/components/SchemaExplorer/JSONSchemaViewer.test.tsx +0 -114
  154. package/eventcatalog/src/components/SchemaExplorer/ProtobufSchemaViewer.test.tsx +0 -91
  155. package/eventcatalog/src/components/SchemaExplorer/SchemaViewerHover.test.tsx +0 -33
  156. package/eventcatalog/src/components/SchemaExplorer/protobuf-validation.test.ts +0 -84
  157. package/eventcatalog/src/components/Search/search-utils.spec.ts +0 -173
  158. package/eventcatalog/src/components/SideNav/NestedSideBar/utils.spec.ts +0 -105
  159. package/eventcatalog/src/components/navigation-progress.spec.ts +0 -68
  160. package/eventcatalog/src/enterprise/directory/user-team-directory.spec.ts +0 -527
  161. package/eventcatalog/src/enterprise/fields/field-extractor.test.ts +0 -302
  162. package/eventcatalog/src/enterprise/fields/fields-db.test.ts +0 -185
  163. package/eventcatalog/src/stores/eventcatalog-store.spec.ts +0 -60
  164. package/eventcatalog/src/utils/collections/glob-loader.spec.ts +0 -87
  165. package/dist/{chunk-6K7XZAYI.js → chunk-A2RZR3U4.js} +3 -3
@@ -1,64 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { buildEnvironmentUrl, findCurrentEnvironment } from './EnvironmentDropdown';
3
-
4
- const environments = [
5
- {
6
- name: 'Development',
7
- url: 'https://example.com/event-catalog/dev',
8
- shortName: 'Dev',
9
- },
10
- {
11
- name: 'UAT',
12
- url: 'https://example.com/event-catalog/uat',
13
- shortName: 'UAT',
14
- },
15
- {
16
- name: 'Production',
17
- url: 'https://example.com/event-catalog/prod',
18
- shortName: 'Prod',
19
- },
20
- ];
21
-
22
- describe('EnvironmentDropdown', () => {
23
- it('finds the current environment when environments share an origin but use different base paths', () => {
24
- expect(findCurrentEnvironment(environments, 'https://example.com/event-catalog/uat/services/foo')?.name).toBe('UAT');
25
- });
26
-
27
- it('builds environment urls by replacing the current environment base path', () => {
28
- expect(
29
- buildEnvironmentUrl(
30
- 'https://example.com/event-catalog/prod',
31
- 'https://example.com/event-catalog/uat/services/foo',
32
- 'https://example.com/event-catalog/uat'
33
- )
34
- ).toBe('https://example.com/event-catalog/prod/services/foo');
35
- });
36
-
37
- it('preserves search params and hashes when switching environments', () => {
38
- expect(
39
- buildEnvironmentUrl(
40
- 'https://example.com/event-catalog/prod',
41
- 'https://example.com/event-catalog/uat/services/foo?tab=messages#latest',
42
- 'https://example.com/event-catalog/uat'
43
- )
44
- ).toBe('https://example.com/event-catalog/prod/services/foo?tab=messages#latest');
45
- });
46
-
47
- it('keeps existing origin based environment switching for root catalogs', () => {
48
- expect(
49
- buildEnvironmentUrl('https://prod.example.com', 'https://uat.example.com/services/foo', 'https://uat.example.com')
50
- ).toBe('https://prod.example.com/services/foo');
51
- });
52
-
53
- it('switches from a root catalog to a subpath catalog', () => {
54
- expect(buildEnvironmentUrl('https://example.com/prod', 'https://example.com/services/foo', 'https://example.com')).toBe(
55
- 'https://example.com/prod/services/foo'
56
- );
57
- });
58
-
59
- it('preserves the current path when the current environment is unknown', () => {
60
- expect(buildEnvironmentUrl('https://example.com/prod', 'http://localhost:3000/services/foo')).toBe(
61
- 'https://example.com/prod/services/foo'
62
- );
63
- });
64
- });
@@ -1,59 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { getMessageLinkProps } from './message-link';
3
-
4
- describe('getMessageLinkProps', () => {
5
- it('builds a command docs url and prefers the resource name over the id', () => {
6
- expect(
7
- getMessageLinkProps({
8
- collection: 'commands',
9
- data: { id: 'order-create', name: 'Create Order', version: '0.0.1' },
10
- })
11
- ).toEqual({
12
- collection: 'commands',
13
- id: 'order-create',
14
- name: 'Create Order',
15
- version: '0.0.1',
16
- href: '/docs/commands/order-create/0.0.1',
17
- });
18
- });
19
-
20
- it('builds a query docs url from a hydrated collection entry', () => {
21
- expect(
22
- getMessageLinkProps({
23
- collection: 'queries',
24
- data: { id: 'get-stock-level', name: 'Get Stock Level', version: '1.0.0' },
25
- })
26
- ).toEqual({
27
- collection: 'queries',
28
- id: 'get-stock-level',
29
- name: 'Get Stock Level',
30
- version: '1.0.0',
31
- href: '/docs/queries/get-stock-level/1.0.0',
32
- });
33
- });
34
-
35
- it('does not default a missing collection to events', () => {
36
- expect(getMessageLinkProps({ id: 'order-create', version: '0.0.1' })).toEqual({
37
- collection: undefined,
38
- id: 'order-create',
39
- name: 'order-create',
40
- version: '0.0.1',
41
- href: undefined,
42
- });
43
- });
44
-
45
- it('ignores unknown collection values instead of linking to events', () => {
46
- expect(
47
- getMessageLinkProps({
48
- collection: 'services',
49
- data: { id: 'order-service', name: 'Order Service', version: '1.0.0' },
50
- })
51
- ).toEqual({
52
- collection: undefined,
53
- id: 'order-service',
54
- name: 'Order Service',
55
- version: '1.0.0',
56
- href: undefined,
57
- });
58
- });
59
- });
@@ -1,38 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { formatCustomPropertyLabel, getCustomProperties, getCustomProperty, isCustomPropertyName } from './custom-properties';
3
-
4
- describe('custom properties MDX components', () => {
5
- it('recognizes extension property names', () => {
6
- expect(isCustomPropertyName('x-owner')).toBe(true);
7
- expect(isCustomPropertyName('x-')).toBe(false);
8
- expect(isCustomPropertyName('owner')).toBe(false);
9
- });
10
-
11
- it('creates a readable label without changing uppercase text', () => {
12
- expect(formatCustomPropertyLabel('x-operational-tier')).toBe('Operational Tier');
13
- expect(formatCustomPropertyLabel('x-PII_classification')).toBe('PII Classification');
14
- });
15
-
16
- it('extracts only custom properties and preserves their values', () => {
17
- const properties = getCustomProperties({
18
- id: 'payment-api',
19
- 'x-operational-tier': 1,
20
- 'x-on-call': { schedule: 'payments-primary' },
21
- });
22
-
23
- expect(properties).toEqual([
24
- { name: 'x-operational-tier', label: 'Operational Tier', value: 1 },
25
- { name: 'x-on-call', label: 'On Call', value: { schedule: 'payments-primary' } },
26
- ]);
27
- });
28
-
29
- it('gets a named custom property', () => {
30
- expect(getCustomProperty({ 'x-owner': 'payments' }, 'x-owner')).toEqual({
31
- name: 'x-owner',
32
- label: 'Owner',
33
- value: 'payments',
34
- });
35
- expect(getCustomProperty({ owner: 'payments' }, 'owner')).toBeUndefined();
36
- expect(getCustomProperty({ 'x-owner': 'payments' }, 'x-missing')).toBeUndefined();
37
- });
38
- });
@@ -1,116 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { resolveSchemaViewer } from './schema-viewer-utils';
3
-
4
- const currentMessageSchema = {
5
- id: 'schema:events:OrderPlaced:1.0.0:git://contracts/events/OrderPlaced.schema.json',
6
- data: {
7
- ref: 'git://contracts/events/OrderPlaced.schema.json',
8
- format: 'jsonschema',
9
- content: '{"type":"object","title":"OrderPlaced"}',
10
- default: true,
11
- message: {
12
- collectionName: 'events',
13
- id: 'OrderPlaced',
14
- version: '1.0.0',
15
- },
16
- },
17
- };
18
-
19
- describe('resolveSchemaViewer', () => {
20
- it('uses the current message default schema when no file is set', async () => {
21
- const schema = await resolveSchemaViewer({
22
- id: 'OrderPlaced',
23
- version: '1.0.0',
24
- collection: 'events',
25
- filePath: 'events/OrderPlaced/index.mdx',
26
- schemaViewerProps: {},
27
- collectionSchemas: [currentMessageSchema],
28
- index: 0,
29
- });
30
-
31
- expect(schema.exists).toBe(true);
32
- expect(schema.schema.title).toBe('OrderPlaced');
33
- expect(schema.schemaPath).toBe('git://contracts/events/OrderPlaced.schema.json');
34
- });
35
-
36
- it('parses protobuf schemas from the schema collection', async () => {
37
- const protoSchema = {
38
- id: 'schema:events:FraudCheckCompleted:1.0.0:git://contracts/events/FraudCheckCompleted.proto',
39
- data: {
40
- ref: 'git://contracts/events/FraudCheckCompleted.proto',
41
- format: 'protobuf',
42
- content: 'syntax = "proto3";\nmessage FraudCheckCompleted {\n string transaction_id = 1;\n}',
43
- default: true,
44
- message: {
45
- collectionName: 'events',
46
- id: 'FraudCheckCompleted',
47
- version: '1.0.0',
48
- },
49
- },
50
- };
51
-
52
- const schema = await resolveSchemaViewer({
53
- id: 'FraudCheckCompleted',
54
- version: '1.0.0',
55
- collection: 'events',
56
- filePath: 'events/FraudCheckCompleted/index.mdx',
57
- schemaViewerProps: {},
58
- collectionSchemas: [protoSchema],
59
- index: 0,
60
- });
61
-
62
- expect(schema.exists).toBe(true);
63
- expect(schema.isProtobufSchema).toBe(true);
64
- expect(schema.schema.syntax).toBe('proto3');
65
- expect(schema.schema.messages[0].name).toBe('FraudCheckCompleted');
66
- expect(schema.schema.messages[0].fields[0].name).toBe('transaction_id');
67
- });
68
-
69
- it('captures parse errors for invalid protobuf schemas', async () => {
70
- const protoSchema = {
71
- id: 'schema:events:FraudCheckCompleted:1.0.0:git://contracts/events/FraudCheckCompleted.proto',
72
- data: {
73
- ref: 'git://contracts/events/FraudCheckCompleted.proto',
74
- format: 'protobuf',
75
- content: '{"this": "is not protobuf"}',
76
- default: true,
77
- message: {
78
- collectionName: 'events',
79
- id: 'FraudCheckCompleted',
80
- version: '1.0.0',
81
- },
82
- },
83
- };
84
-
85
- const schema = await resolveSchemaViewer({
86
- id: 'FraudCheckCompleted',
87
- version: '1.0.0',
88
- collection: 'events',
89
- filePath: 'events/FraudCheckCompleted/index.mdx',
90
- schemaViewerProps: {},
91
- collectionSchemas: [protoSchema],
92
- index: 0,
93
- });
94
-
95
- expect(schema.exists).toBe(false);
96
- expect(schema.parseError).toBeDefined();
97
- });
98
-
99
- it('does not use the schema collection when a file is set and missing', async () => {
100
- const schema = await resolveSchemaViewer({
101
- id: 'OrderPlaced',
102
- version: '1.0.0',
103
- collection: 'events',
104
- filePath: 'events/OrderPlaced/index.mdx',
105
- schemaViewerProps: {
106
- file: 'missing-schema.json',
107
- },
108
- collectionSchemas: [currentMessageSchema],
109
- index: 0,
110
- });
111
-
112
- expect(schema.exists).toBe(false);
113
- expect(schema.schema).toBeUndefined();
114
- expect(schema.schemaKey).toBe('missing-schema.json');
115
- });
116
- });
@@ -1,114 +0,0 @@
1
- // @vitest-environment jsdom
2
- import { act, createElement } from 'react';
3
- import { createRoot, type Root } from 'react-dom/client';
4
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
- import JSONSchemaViewer from './JSONSchemaViewer';
6
-
7
- const constraintOnlyVariantSchema = {
8
- type: 'object',
9
- properties: {
10
- detail: { $ref: '#/$defs/detail' },
11
- },
12
- $defs: {
13
- detail: {
14
- type: 'object',
15
- properties: {
16
- data: { $ref: '#/$defs/data' },
17
- },
18
- },
19
- data: {
20
- type: 'object',
21
- properties: {
22
- userId: { type: 'string' },
23
- customerId: { type: 'string' },
24
- correlationId: { type: 'string' },
25
- },
26
- required: ['correlationId'],
27
- oneOf: [
28
- {
29
- title: 'Authenticated user',
30
- required: ['userId'],
31
- not: { required: ['customerId'] },
32
- },
33
- {
34
- title: 'Customer account',
35
- required: ['customerId'],
36
- not: { required: ['userId'] },
37
- },
38
- ],
39
- },
40
- },
41
- };
42
-
43
- describe('JSONSchemaViewer variants', () => {
44
- let container: HTMLDivElement;
45
- let root: Root;
46
-
47
- beforeEach(() => {
48
- vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true);
49
- container = document.createElement('div');
50
- document.body.appendChild(container);
51
- root = createRoot(container);
52
- });
53
-
54
- afterEach(() => {
55
- act(() => root.unmount());
56
- container.remove();
57
- vi.unstubAllGlobals();
58
- });
59
-
60
- it('renders parent properties selected by constraint-only oneOf branches', () => {
61
- act(() => {
62
- root.render(createElement(JSONSchemaViewer, { schema: constraintOnlyVariantSchema, expand: true, search: false }));
63
- });
64
-
65
- expect(container.textContent).toContain('Authenticated user');
66
- expect(container.textContent).toContain('userId');
67
- expect(container.textContent).not.toContain('customerId');
68
- expect(container.textContent).toContain('correlationId');
69
-
70
- const selector = container.querySelector<HTMLSelectElement>('select');
71
- expect(selector).not.toBeNull();
72
-
73
- act(() => {
74
- selector!.value = '1';
75
- selector!.dispatchEvent(new Event('change', { bubbles: true }));
76
- });
77
-
78
- expect(container.textContent).toContain('customerId');
79
- expect(container.textContent).not.toContain('userId');
80
- expect(container.textContent).toContain('correlationId');
81
- });
82
-
83
- it('applies constraint-only oneOf branches at the schema root', () => {
84
- const rootSchema = constraintOnlyVariantSchema.$defs.data;
85
-
86
- act(() => {
87
- root.render(createElement(JSONSchemaViewer, { schema: rootSchema, expand: true, search: false }));
88
- });
89
-
90
- expect(container.textContent).toContain('userId');
91
- expect(container.textContent).not.toContain('customerId');
92
- expect(container.textContent).toContain('correlationId');
93
- });
94
-
95
- it('does not render array item parent properties twice when variants are present', () => {
96
- const arraySchema = {
97
- type: 'object',
98
- properties: {
99
- identities: {
100
- type: 'array',
101
- items: constraintOnlyVariantSchema.$defs.data,
102
- },
103
- },
104
- };
105
-
106
- act(() => {
107
- root.render(createElement(JSONSchemaViewer, { schema: arraySchema, expand: true, search: false }));
108
- });
109
-
110
- expect(container.textContent?.match(/userId/g)).toHaveLength(1);
111
- expect(container.textContent).not.toContain('customerId');
112
- expect(container.textContent?.match(/correlationId/g)).toHaveLength(1);
113
- });
114
- });
@@ -1,91 +0,0 @@
1
- import { renderToStaticMarkup } from 'react-dom/server';
2
- import { describe, expect, it } from 'vitest';
3
- import { parseProtobufSchema } from '@utils/protobuf-schema';
4
- import ProtobufSchemaViewer from './ProtobufSchemaViewer';
5
-
6
- describe('ProtobufSchemaViewer', () => {
7
- it('renders Protovalidate required and constraint annotations', () => {
8
- const schema = parseProtobufSchema(`
9
- syntax = "proto3";
10
-
11
- enum Status {
12
- STATUS_UNSPECIFIED = 0;
13
- STATUS_ACTIVE = 1;
14
- }
15
-
16
- message Journey {
17
- string id = 1 [
18
- (buf.validate.field).required = true,
19
- (buf.validate.field).string.uuid = true
20
- ];
21
- uint32 passenger_count = 2 [(buf.validate.field).uint32 = { gte: 1, lte: 20 }];
22
- Status status = 3 [(buf.validate.field).enum = { defined_only: true, not_in: [0] }];
23
- repeated string stops = 4 [(buf.validate.field).repeated = {
24
- min_items: 2
25
- unique: true
26
- items: { string: { min_len: 3 } }
27
- }];
28
- planning.model.v1.TrainServiceReference train_service = 5;
29
- optional string description = 6 [(datahub.v1.gdpr_rule).string = true];
30
- }
31
- `);
32
-
33
- const html = renderToStaticMarkup(<ProtobufSchemaViewer schema={schema} />);
34
- const statusField = html.slice(html.indexOf('>status</span>'), html.indexOf('>stops</span>'));
35
-
36
- expect(html).toContain('required');
37
- expect(html).toContain('Format');
38
- expect(html).toContain('UUID');
39
- expect(html).toContain('Minimum');
40
- expect(html).toContain('Maximum');
41
- expect(html).toContain('Defined enum values only');
42
- expect(html).toContain('Excluded values');
43
- expect(html).toContain('Minimum items');
44
- expect(html).toContain('Unique items');
45
- expect(html).toContain('Item minimum length');
46
- expect(html).toContain('title="planning.model.v1.TrainServiceReference"');
47
- expect(html).toContain('>TrainServiceReference</span>');
48
- expect(statusField).not.toContain('STATUS_UNSPECIFIED');
49
- expect(statusField).toContain('STATUS_ACTIVE');
50
- expect(html).toContain('Annotation:');
51
- expect(html).toContain('(datahub.v1.gdpr_rule).string');
52
- expect(html).toContain('>true</code>');
53
- });
54
-
55
- it('filters enum values using repeated-item and map-value constraints', () => {
56
- const schema = parseProtobufSchema(`
57
- syntax = "proto3";
58
-
59
- enum Status {
60
- STATUS_UNSPECIFIED = 0;
61
- STATUS_ACTIVE = 1;
62
- STATUS_ARCHIVED = 2;
63
- }
64
-
65
- message Journey {
66
- repeated Status statuses = 1 [(buf.validate.field).repeated = {
67
- items: { enum: { in: [1] } }
68
- }];
69
- map<string, Status> status_by_key = 2 [(buf.validate.field).map = {
70
- values: { enum: { not_in: [0, 2] } }
71
- }];
72
- Status status = 3 [(buf.validate.field).enum.const = 2];
73
- }
74
- `);
75
-
76
- const html = renderToStaticMarkup(<ProtobufSchemaViewer schema={schema} />);
77
- const repeatedField = html.slice(html.indexOf('>statuses</span>'), html.indexOf('>status_by_key</span>'));
78
- const mapField = html.slice(html.indexOf('>status_by_key</span>'), html.indexOf('>status</span>'));
79
- const directField = html.slice(html.indexOf('>status</span>'), html.indexOf('>Enum:</span>'));
80
-
81
- expect(repeatedField).toContain('STATUS_ACTIVE');
82
- expect(repeatedField).not.toContain('STATUS_UNSPECIFIED');
83
- expect(repeatedField).not.toContain('STATUS_ARCHIVED');
84
- expect(mapField).toContain('STATUS_ACTIVE');
85
- expect(mapField).not.toContain('STATUS_UNSPECIFIED');
86
- expect(mapField).not.toContain('STATUS_ARCHIVED');
87
- expect(directField).toContain('STATUS_ARCHIVED');
88
- expect(directField).not.toContain('STATUS_UNSPECIFIED');
89
- expect(directField).not.toContain('STATUS_ACTIVE');
90
- });
91
- });
@@ -1,33 +0,0 @@
1
- import { renderToStaticMarkup } from 'react-dom/server';
2
- import { describe, expect, it } from 'vitest';
3
- import { parseProtobufSchema } from '@utils/protobuf-schema';
4
- import AvroSchemaViewer from './AvroSchemaViewer';
5
- import JSONSchemaViewer from './JSONSchemaViewer';
6
- import ProtobufSchemaViewer from './ProtobufSchemaViewer';
7
-
8
- const PROPERTY_HOVER_CLASS = 'hover:bg-[rgb(var(--ec-content-hover))]';
9
-
10
- describe('schema viewer property hover states', () => {
11
- it('adds a hover background to JSON Schema properties', () => {
12
- const html = renderToStaticMarkup(
13
- <JSONSchemaViewer schema={{ type: 'object', properties: { id: { type: 'string' } } }} search={false} />
14
- );
15
-
16
- expect(html).toContain(PROPERTY_HOVER_CLASS);
17
- });
18
-
19
- it('adds a hover background to Avro fields', () => {
20
- const html = renderToStaticMarkup(
21
- <AvroSchemaViewer schema={{ type: 'record', name: 'Example', fields: [{ name: 'id', type: 'string' }] }} search={false} />
22
- );
23
-
24
- expect(html).toContain(PROPERTY_HOVER_CLASS);
25
- });
26
-
27
- it('adds a hover background to Protobuf fields', () => {
28
- const schema = parseProtobufSchema('syntax = "proto3"; message Example { string id = 1; }');
29
- const html = renderToStaticMarkup(<ProtobufSchemaViewer schema={schema} search={false} />);
30
-
31
- expect(html).toContain(PROPERTY_HOVER_CLASS);
32
- });
33
- });
@@ -1,84 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import type { ProtobufField } from '@utils/protobuf-schema';
3
- import {
4
- formatProtobufOptionValue,
5
- formatProtovalidateRule,
6
- getCustomFieldAnnotations,
7
- getProtovalidateRules,
8
- isProtovalidateRequired,
9
- } from './protobuf-validation';
10
-
11
- describe('protobuf validation helpers', () => {
12
- it('flattens direct and aggregate Protovalidate options', () => {
13
- const field: ProtobufField = {
14
- name: 'status',
15
- type: 'Status',
16
- options: [
17
- { name: '(buf.validate.field).required', value: true },
18
- {
19
- name: '(buf.validate.field).enum',
20
- value: { defined_only: true, not_in: [0] },
21
- },
22
- ],
23
- };
24
-
25
- expect(getProtovalidateRules(field)).toEqual([
26
- { path: 'required', value: true },
27
- { path: 'enum.defined_only', value: true },
28
- { path: 'enum.not_in', value: [0] },
29
- ]);
30
- expect(isProtovalidateRequired(field)).toBe(true);
31
- });
32
-
33
- it('ignores non-Protovalidate custom options', () => {
34
- const field: ProtobufField = {
35
- name: 'description',
36
- type: 'string',
37
- options: [{ name: '(datahub.v1.gdpr_rule).string', value: true }],
38
- };
39
-
40
- expect(getProtovalidateRules(field)).toEqual([]);
41
- expect(isProtovalidateRequired(field)).toBe(false);
42
- expect(getCustomFieldAnnotations(field)).toEqual([{ name: '(datahub.v1.gdpr_rule).string', value: true }]);
43
- });
44
-
45
- it('only treats extension options as custom annotations and formats aggregate values', () => {
46
- const field: ProtobufField = {
47
- name: 'description',
48
- type: 'string',
49
- options: [
50
- { name: 'deprecated', value: true },
51
- { name: '(buf.validate.field).string.min_len', value: 1 },
52
- { name: '(datahub.v1.classification)', value: { level: 'personal', encrypted: true } },
53
- ],
54
- };
55
-
56
- expect(getCustomFieldAnnotations(field)).toEqual([
57
- {
58
- name: '(datahub.v1.classification)',
59
- value: { level: 'personal', encrypted: true },
60
- },
61
- ]);
62
- expect(formatProtobufOptionValue(getCustomFieldAnnotations(field)[0].value)).toBe('{"level":"personal","encrypted":true}');
63
- });
64
-
65
- it('formats common rules for display', () => {
66
- expect(formatProtovalidateRule({ path: 'string.email', value: true })).toEqual({ label: 'Format', value: 'Email' });
67
- expect(formatProtovalidateRule({ path: 'uint32.gte', value: 1 })).toEqual({ label: 'Minimum', value: '1' });
68
- expect(formatProtovalidateRule({ path: 'enum.defined_only', value: true })).toEqual({
69
- label: 'Defined enum values only',
70
- });
71
- expect(formatProtovalidateRule({ path: 'enum.not_in', value: [0] })).toEqual({
72
- label: 'Excluded values',
73
- value: '0',
74
- });
75
- expect(formatProtovalidateRule({ path: 'repeated.items.string.min_len', value: 3 })).toEqual({
76
- label: 'Item minimum length',
77
- value: '3',
78
- });
79
- expect(formatProtovalidateRule({ path: 'map.values.string.max_len', value: 100 })).toEqual({
80
- label: 'Value maximum length',
81
- value: '100',
82
- });
83
- });
84
- });