@intentius/chant-lexicon-otel 0.124.45 → 0.124.48

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 (211) hide show
  1. package/README.md +2 -2
  2. package/dist/build/capability-plugin.d.ts +10 -0
  3. package/dist/build/capability-plugin.d.ts.map +1 -0
  4. package/dist/build/capability-plugin.js +19 -0
  5. package/dist/build/ocb-build.d.ts +38 -0
  6. package/dist/build/ocb-build.d.ts.map +1 -0
  7. package/dist/build/ocb-build.js +49 -0
  8. package/dist/clickhouse-tables.d.ts +73 -0
  9. package/dist/clickhouse-tables.d.ts.map +1 -0
  10. package/dist/clickhouse-tables.js +199 -0
  11. package/dist/codegen/docs.d.ts.map +1 -1
  12. package/dist/codegen/docs.js +4 -2
  13. package/dist/codegen/generate.d.ts +29 -10
  14. package/dist/codegen/generate.d.ts.map +1 -1
  15. package/dist/codegen/generate.js +82 -17
  16. package/dist/components/common.d.ts +3 -1
  17. package/dist/components/common.d.ts.map +1 -1
  18. package/dist/components/common.js +2 -0
  19. package/dist/components/connectors.d.ts +25 -90
  20. package/dist/components/connectors.d.ts.map +1 -1
  21. package/dist/components/connectors.js +3 -0
  22. package/dist/components/exporters.d.ts +78 -34
  23. package/dist/components/exporters.d.ts.map +1 -1
  24. package/dist/components/exporters.js +26 -1
  25. package/dist/components/extensions.d.ts +16 -33
  26. package/dist/components/extensions.d.ts.map +1 -1
  27. package/dist/components/filtering.d.ts +13 -60
  28. package/dist/components/filtering.d.ts.map +1 -1
  29. package/dist/components/k8s-receivers.d.ts +5 -53
  30. package/dist/components/k8s-receivers.d.ts.map +1 -1
  31. package/dist/components/overlay.d.ts +25 -0
  32. package/dist/components/overlay.d.ts.map +1 -0
  33. package/dist/components/overlay.js +19 -0
  34. package/dist/components/processors.d.ts +18 -79
  35. package/dist/components/processors.d.ts.map +1 -1
  36. package/dist/components/receivers.d.ts +17 -71
  37. package/dist/components/receivers.d.ts.map +1 -1
  38. package/dist/components/receivers.js +3 -1
  39. package/dist/components/sampling.d.ts +19 -35
  40. package/dist/components/sampling.d.ts.map +1 -1
  41. package/dist/config-hygiene.d.ts +10 -8
  42. package/dist/config-hygiene.d.ts.map +1 -1
  43. package/dist/config-hygiene.js +36 -13
  44. package/dist/coverage.d.ts +50 -0
  45. package/dist/coverage.d.ts.map +1 -0
  46. package/dist/coverage.js +92 -0
  47. package/dist/define.d.ts +8 -5
  48. package/dist/define.d.ts.map +1 -1
  49. package/dist/define.js +10 -6
  50. package/dist/distribution.d.ts +125 -0
  51. package/dist/distribution.d.ts.map +1 -0
  52. package/dist/distribution.js +227 -0
  53. package/dist/generated/index.d.ts +4234 -0
  54. package/dist/generated/index.d.ts.map +1 -0
  55. package/dist/generated/index.js +23 -0
  56. package/dist/index.d.ts +7 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +9 -1
  59. package/dist/init-templates.js +2 -2
  60. package/dist/integrity.json +11 -9
  61. package/dist/lint/audit-catalog.d.ts +3 -0
  62. package/dist/lint/audit-catalog.d.ts.map +1 -1
  63. package/dist/lint/audit-catalog.js +5 -0
  64. package/dist/lint/post-synth/index.d.ts.map +1 -1
  65. package/dist/lint/post-synth/index.js +4 -0
  66. package/dist/lint/post-synth/otel-helpers.d.ts +2 -1
  67. package/dist/lint/post-synth/otel-helpers.d.ts.map +1 -1
  68. package/dist/lint/post-synth/otel-helpers.js +1 -1
  69. package/dist/lint/post-synth/otel119.d.ts +1 -1
  70. package/dist/lint/post-synth/otel119.js +1 -1
  71. package/dist/lint/post-synth/otel125.d.ts +1 -1
  72. package/dist/lint/post-synth/otel125.js +1 -1
  73. package/dist/lint/post-synth/otel126.d.ts +1 -1
  74. package/dist/lint/post-synth/otel126.js +1 -1
  75. package/dist/lint/post-synth/otel127.d.ts +1 -1
  76. package/dist/lint/post-synth/otel127.js +1 -1
  77. package/dist/lint/post-synth/otel128.d.ts +8 -0
  78. package/dist/lint/post-synth/otel128.d.ts.map +1 -0
  79. package/dist/lint/post-synth/otel128.js +17 -0
  80. package/dist/lint/post-synth/otel129.d.ts +15 -0
  81. package/dist/lint/post-synth/otel129.d.ts.map +1 -0
  82. package/dist/lint/post-synth/otel129.js +92 -0
  83. package/dist/lsp/completions.d.ts +10 -1
  84. package/dist/lsp/completions.d.ts.map +1 -1
  85. package/dist/lsp/completions.js +29 -1
  86. package/dist/lsp/hover.d.ts +5 -1
  87. package/dist/lsp/hover.d.ts.map +1 -1
  88. package/dist/lsp/hover.js +16 -3
  89. package/dist/lsp/properties.d.ts +34 -0
  90. package/dist/lsp/properties.d.ts.map +1 -0
  91. package/dist/lsp/properties.js +135 -0
  92. package/dist/manifest.json +1 -1
  93. package/dist/meta.json +5 -0
  94. package/dist/metric-names.d.ts +36 -8
  95. package/dist/metric-names.d.ts.map +1 -1
  96. package/dist/metric-names.js +42 -9
  97. package/dist/model.d.ts +1 -1
  98. package/dist/model.js +1 -1
  99. package/dist/okf/index.md +3 -0
  100. package/dist/okf/rules/OTEL128.md +11 -0
  101. package/dist/okf/rules/OTEL129.md +11 -0
  102. package/dist/okf/types/ClickHouseExporter.md +9 -0
  103. package/dist/op/activities/collector-audit.d.ts +18 -6
  104. package/dist/op/activities/collector-audit.d.ts.map +1 -1
  105. package/dist/op/activities/collector-audit.js +37 -21
  106. package/dist/plugin.d.ts.map +1 -1
  107. package/dist/plugin.js +51 -8
  108. package/dist/rules/otel-helpers.ts +1 -1
  109. package/dist/rules/otel119.ts +1 -1
  110. package/dist/rules/otel125.ts +1 -1
  111. package/dist/rules/otel126.ts +1 -1
  112. package/dist/rules/otel127.ts +1 -1
  113. package/dist/rules/otel128.ts +22 -0
  114. package/dist/rules/otel129.ts +99 -0
  115. package/dist/serializer.d.ts +3 -0
  116. package/dist/serializer.d.ts.map +1 -1
  117. package/dist/serializer.js +12 -2
  118. package/dist/spec/builtins.d.ts +17 -0
  119. package/dist/spec/builtins.d.ts.map +1 -0
  120. package/dist/spec/builtins.js +45 -0
  121. package/dist/spec/fetch.d.ts +120 -0
  122. package/dist/spec/fetch.d.ts.map +1 -0
  123. package/dist/spec/fetch.js +282 -0
  124. package/dist/spec/parse.d.ts +77 -0
  125. package/dist/spec/parse.d.ts.map +1 -0
  126. package/dist/spec/parse.js +337 -0
  127. package/dist/spec/pin.d.ts +82 -0
  128. package/dist/spec/pin.d.ts.map +1 -0
  129. package/dist/spec/pin.js +126 -0
  130. package/dist/spec/snapshot-cli.d.ts +13 -0
  131. package/dist/spec/snapshot-cli.d.ts.map +1 -0
  132. package/dist/spec/snapshot-cli.js +31 -0
  133. package/dist/spec/snapshot.d.ts +9 -0
  134. package/dist/spec/snapshot.d.ts.map +1 -0
  135. package/dist/spec/snapshot.js +14 -0
  136. package/dist/types/index.d.ts +3 -2
  137. package/dist/validate-config.d.ts +1 -1
  138. package/dist/validate-config.d.ts.map +1 -1
  139. package/dist/validate.d.ts.map +1 -1
  140. package/dist/validate.js +1 -0
  141. package/dist/workspace-links.d.ts +25 -0
  142. package/dist/workspace-links.d.ts.map +1 -0
  143. package/dist/workspace-links.js +49 -0
  144. package/package.json +3 -2
  145. package/src/build/capability-plugin.ts +30 -0
  146. package/src/build/ocb-build.ts +73 -0
  147. package/src/clickhouse-exporter.e2e.test.ts +208 -0
  148. package/src/clickhouse-tables.test.ts +216 -0
  149. package/src/clickhouse-tables.ts +223 -0
  150. package/src/codegen/docs.ts +4 -2
  151. package/src/codegen/generate.ts +95 -19
  152. package/src/components/common.ts +4 -2
  153. package/src/components/connectors.ts +30 -77
  154. package/src/components/exporters.ts +128 -61
  155. package/src/components/extensions.ts +22 -36
  156. package/src/components/filtering.ts +17 -58
  157. package/src/components/k8s-receivers.test.ts +1 -1
  158. package/src/components/k8s-receivers.ts +3 -45
  159. package/src/components/overlay.ts +24 -0
  160. package/src/components/processors.ts +18 -63
  161. package/src/components/receivers.ts +20 -60
  162. package/src/components/sampling.ts +24 -37
  163. package/src/config-hygiene.ts +36 -13
  164. package/src/coverage.test.ts +37 -0
  165. package/src/coverage.ts +121 -0
  166. package/src/define.test.ts +2 -0
  167. package/src/define.ts +10 -6
  168. package/src/distribution.test.ts +250 -0
  169. package/src/distribution.ts +305 -0
  170. package/src/generated/index.ts +4576 -0
  171. package/src/generated/lexicon-otel.json +5 -0
  172. package/src/generated/properties.json +1 -0
  173. package/src/import/roundtrip.test.ts +16 -0
  174. package/src/import/testdata/clickhouse.yaml +37 -0
  175. package/src/import/testdata/fixtures.ts +17 -2
  176. package/src/import/testdata/upstream/clickhouse-exporter.yaml +60 -0
  177. package/src/index.ts +69 -0
  178. package/src/init-templates.ts +2 -2
  179. package/src/lint/audit-catalog.ts +15 -0
  180. package/src/lint/post-synth/index.ts +4 -0
  181. package/src/lint/post-synth/otel-helpers.ts +1 -1
  182. package/src/lint/post-synth/otel119.ts +1 -1
  183. package/src/lint/post-synth/otel125.ts +1 -1
  184. package/src/lint/post-synth/otel126.ts +1 -1
  185. package/src/lint/post-synth/otel127.ts +1 -1
  186. package/src/lint/post-synth/otel128.ts +22 -0
  187. package/src/lint/post-synth/otel129.ts +99 -0
  188. package/src/lsp/completions.ts +30 -1
  189. package/src/lsp/hover.ts +14 -3
  190. package/src/lsp/properties.test.ts +72 -0
  191. package/src/lsp/properties.ts +141 -0
  192. package/src/metric-names.test.ts +11 -0
  193. package/src/metric-names.ts +64 -16
  194. package/src/model.ts +1 -1
  195. package/src/op/activities/collector-audit.test.ts +29 -22
  196. package/src/op/activities/collector-audit.ts +38 -18
  197. package/src/plugin.test.ts +2 -0
  198. package/src/plugin.ts +57 -6
  199. package/src/serializer.ts +11 -2
  200. package/src/spec/builtins.ts +56 -0
  201. package/src/spec/fetch.ts +332 -0
  202. package/src/spec/otel-schemas.snapshot.json +13932 -0
  203. package/src/spec/parse.ts +364 -0
  204. package/src/spec/pin.ts +155 -0
  205. package/src/spec/snapshot-cli.ts +32 -0
  206. package/src/spec/snapshot.ts +19 -0
  207. package/src/spec/spec.test.ts +144 -0
  208. package/src/validate-config.ts +4 -3
  209. package/src/validate.ts +1 -0
  210. package/src/workspace-links.e2e.test.ts +91 -0
  211. package/src/workspace-links.ts +50 -0
@@ -0,0 +1,144 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { CONTRIB_MODULE, CORE_MODULE, locate, parseManifest, refsIn, resolveRef } from "./fetch";
3
+ import { documentAlias, generateTypes, isDeprecated, propertyDocs } from "./parse";
4
+ import {
5
+ OTEL_SPEC_PIN,
6
+ OTELCOL_CONTRIB_VERSION,
7
+ OTELCOL_K8S_VERSION,
8
+ assertPinnedSnapshot,
9
+ assertSnapshotVersions,
10
+ canonicalJson,
11
+ specDigest,
12
+ } from "./pin";
13
+ import { loadSnapshot } from "./snapshot";
14
+ import { BUILTIN_SPECS } from "./builtins";
15
+ import { BUILTIN_CATALOG } from "../catalog";
16
+
17
+ const OTLP = `${CORE_MODULE}/receiver/otlpreceiver`;
18
+ const TAIL = `${CONTRIB_MODULE}/processor/tailsamplingprocessor`;
19
+
20
+ describe("manifests and refs", () => {
21
+ test("a builder manifest lists component modules by kind, without providers", () => {
22
+ const modules = parseManifest(`
23
+ dist: { name: otelcol-contrib }
24
+ receivers:
25
+ - gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.162.0
26
+ connectors:
27
+ - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/connector/countconnector v0.162.0
28
+ providers:
29
+ - gomod: go.opentelemetry.io/collector/confmap/provider/envprovider v1.48.0
30
+ `);
31
+ expect(modules).toEqual([
32
+ { kind: "receiver", module: OTLP, version: "v0.162.0" },
33
+ { kind: "connector", module: `${CONTRIB_MODULE}/connector/countconnector`, version: "v0.162.0" },
34
+ ]);
35
+ });
36
+
37
+ test("each $ref form resolves to a package and a definition", () => {
38
+ expect(resolveRef(TAIL, "and_cfg")).toEqual({ doc: TAIL, def: "and_cfg" });
39
+ expect(resolveRef(TAIL, "./internal/metadata.config")).toEqual({ doc: `${TAIL}/internal/metadata`, def: "config" });
40
+ expect(resolveRef(OTLP, "/config/configgrpc.server_config")).toEqual({ doc: `${CORE_MODULE}/config/configgrpc`, def: "server_config" });
41
+ expect(resolveRef(TAIL, "/pkg/ottl.error_mode")).toEqual({ doc: `${CONTRIB_MODULE}/pkg/ottl`, def: "error_mode" });
42
+ expect(resolveRef(TAIL, "go.opentelemetry.io/collector/config/configopaque.string")).toEqual({
43
+ doc: `${CORE_MODULE}/config/configopaque`,
44
+ def: "string",
45
+ });
46
+ expect(refsIn({ a: { $ref: "x" }, b: [{ $ref: "y" }] })).toEqual(["x", "y"]);
47
+ });
48
+
49
+ test("core packages read from opentelemetry-collector, contrib ones from contrib", () => {
50
+ const tags = { core: "v0.162.0", contrib: "v0.162.0" };
51
+ expect(locate(OTLP, tags)).toMatchObject({ repo: "open-telemetry/opentelemetry-collector", path: "receiver/otlpreceiver" });
52
+ expect(locate(TAIL, tags)).toMatchObject({ repo: "open-telemetry/opentelemetry-collector-contrib", path: "processor/tailsamplingprocessor" });
53
+ expect(locate("go.opentelemetry.io/obi", tags)).toBeUndefined();
54
+ });
55
+ });
56
+
57
+ describe("schema to TypeScript", () => {
58
+ const docs = {
59
+ "example.com/c": {
60
+ $defs: {
61
+ node: { type: "object", properties: { next: { $ref: "node" }, level: { $ref: "go.opentelemetry.io/collector/config/configtelemetry.level" } } },
62
+ },
63
+ description: "Config of c.",
64
+ type: "object",
65
+ properties: {
66
+ timeout: { type: "string", format: "duration", description: "Timeout. Deprecated: use deadline." },
67
+ grpc: { "x-optional": true, $ref: "node" },
68
+ mode: { type: "string", enum: ["a", "b"] },
69
+ count: { type: "integer", "x-customType": "uint64" },
70
+ id: { type: "string", "x-customType": "go.opentelemetry.io/collector/component.ID" },
71
+ labels: { type: "object", additionalProperties: { type: "string" } },
72
+ missing: { $ref: "/nowhere.thing" },
73
+ },
74
+ allOf: [{ $ref: "go.opentelemetry.io/collector/config/confighttp.server_config" }],
75
+ },
76
+ "go.opentelemetry.io/collector/config/confighttp": { $defs: { server_config: { type: "object", properties: { endpoint: { type: "string" } } } } },
77
+ "go.opentelemetry.io/collector/config/configtelemetry": { $defs: { level: { type: "integer" } } },
78
+ };
79
+
80
+ test("definitions become named aliases; extensions map to TypeScript", () => {
81
+ const g = generateTypes(docs, ["example.com/c"]);
82
+ const root = g.roots.get("example.com/c")!;
83
+ expect(root).toBe("ExampleComCSchema");
84
+ expect(g.declarations).toContain(`export type ${root} = CollectorConfigConfighttpServerConfig & {`);
85
+ expect(g.declarations).toContain("timeout?: Duration;");
86
+ expect(g.declarations).toContain("@deprecated");
87
+ expect(g.declarations).toContain("grpc?: ExampleComCNode | null;");
88
+ expect(g.declarations).toContain('mode?: "a" | "b";');
89
+ expect(g.declarations).toContain("count?: number;");
90
+ expect(g.declarations).toContain("id?: string;");
91
+ expect(g.declarations).toContain("labels?: Record<string, string>;");
92
+ expect(g.declarations).toContain("missing?: unknown;");
93
+ // Recursive definitions refer to themselves by name.
94
+ expect(g.declarations).toContain("next?: ExampleComCNode;");
95
+ // configtelemetry.Level is read from its name, not its number.
96
+ expect(g.declarations).toContain('export type CollectorConfigConfigtelemetryLevel = "none" | "basic" | "normal" | "detailed";');
97
+ expect([...g.unresolved.keys()]).toEqual(["/nowhere.thing"]);
98
+ });
99
+
100
+ test("property docs carry descriptions, short types and deprecation, embedded structs flattened", () => {
101
+ const props = propertyDocs(docs, "example.com/c");
102
+ expect(props.endpoint).toEqual({ type: "string" });
103
+ expect(props.timeout).toMatchObject({ type: "duration", deprecated: true });
104
+ expect(props.grpc.properties?.level.type).toBe('"none" | "basic" | "normal" | "detailed"');
105
+ expect(props.labels.type).toBe("object");
106
+ });
107
+
108
+ test("deprecation is read from Go comment forms", () => {
109
+ expect(isDeprecated("Deprecated: use X")).toBe(true);
110
+ expect(isDeprecated("Size of cache. Deprecated [v0.130.0]: use Y")).toBe(true);
111
+ expect(isDeprecated("Not deprecated at all")).toBe(false);
112
+ expect(documentAlias(`${CONTRIB_MODULE}/pkg/ottl`)).toBe("PkgOttl");
113
+ });
114
+ });
115
+
116
+ describe("the committed snapshot", () => {
117
+ const snapshot = loadSnapshot();
118
+
119
+ test("matches the pin and the version constants", () => {
120
+ expect(snapshot.versions["otelcol-contrib"]).toBe(OTELCOL_CONTRIB_VERSION);
121
+ expect(snapshot.versions["otelcol-k8s"]).toBe(OTELCOL_K8S_VERSION);
122
+ expect(specDigest(snapshot)).toBe(OTEL_SPEC_PIN.digest);
123
+ expect(Object.keys(snapshot.schemas)).toHaveLength(OTEL_SPEC_PIN.documents);
124
+ expect(() => assertPinnedSnapshot(snapshot)).not.toThrow();
125
+ });
126
+
127
+ test("a changed snapshot or version refuses, and the accept env var warns instead", () => {
128
+ const edited = { ...snapshot, schemas: { ...snapshot.schemas, "example.com/x": {} } };
129
+ expect(() => assertPinnedSnapshot(edited, { env: {} })).toThrow(/does not match OTEL_SPEC_PIN/);
130
+ const warnings: string[] = [];
131
+ assertPinnedSnapshot(edited, { env: { CHANT_ACCEPT_OTEL_SPEC: "1" }, warn: (m) => warnings.push(m) });
132
+ expect(warnings[0]).toContain("documents: 134");
133
+ expect(() => assertSnapshotVersions({ "otelcol-contrib": "v0.1.0", "otelcol-k8s": OTELCOL_K8S_VERSION })).toThrow(/just snapshot/);
134
+ });
135
+
136
+ test("the digest ignores key order", () => {
137
+ expect(canonicalJson({ b: 1, a: [{ d: 2, c: 3 }] })).toBe('{"a":[{"c":3,"d":2}],"b":1}');
138
+ });
139
+
140
+ test("BUILTIN_SPECS lists the exported component classes", () => {
141
+ const fromCatalog = BUILTIN_CATALOG.filter((c) => c.type !== undefined).map((c) => `${c.className} ${c.kind} ${c.type}`);
142
+ expect(BUILTIN_SPECS.map((b) => `${b.className} ${b.kind} ${b.type}`).sort()).toEqual(fromCatalog.sort());
143
+ });
144
+ });
@@ -45,7 +45,8 @@ export type CollectorIssueCode =
45
45
  | "OTEL124"
46
46
  | "OTEL125"
47
47
  | "OTEL126"
48
- | "OTEL127";
48
+ | "OTEL127"
49
+ | "OTEL128";
49
50
 
50
51
  export interface CollectorIssue {
51
52
  code: CollectorIssueCode;
@@ -494,8 +495,8 @@ export interface Listener {
494
495
 
495
496
  /**
496
497
  * The listeners OTEL117 knows, by component type: the config key path to an
497
- * address, its default at `COLLECTOR_PIN` (collector and contrib v0.130.0,
498
- * each component's `factory.go`), and the transport. `present` means the
498
+ * address, its default (read from each component's `factory.go` at collector
499
+ * and contrib v0.130.0), and the transport. `present` means the
499
500
  * default applies only when the key's parent is in the config: an otlp or
500
501
  * jaeger protocol that isn't listed doesn't start. A type not listed here is
501
502
  * not checked, since its `endpoint` may be a server it connects to (the
package/src/validate.ts CHANGED
@@ -38,6 +38,7 @@ export const REQUIRED_NAMES = [
38
38
  "PrometheusExporter",
39
39
  "GoogleCloudExporter",
40
40
  "LoadBalancingExporter",
41
+ "ClickHouseExporter",
41
42
  "SpanMetricsConnector",
42
43
  "ServiceGraphConnector",
43
44
  "RoutingConnector",
@@ -0,0 +1,91 @@
1
+ /**
2
+ * An otel member's clickhouse exporter linked to the sql member that declares
3
+ * its tables (#4397), end to end through `chant workspace check`. Both
4
+ * members are folded, never run: the sql member's tables and columns are its
5
+ * outputs (#3931), and the otel lexicon's `workspaceLinks` hook states a link
6
+ * to each table the exporter writes and each column its insert names.
7
+ * Renaming one of those tables in the sql member fails the check (WSP093) at
8
+ * the exporter's declaration.
9
+ */
10
+
11
+ import { execFileSync } from "node:child_process";
12
+ import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
13
+ import { tmpdir } from "node:os";
14
+ import { dirname, join } from "node:path";
15
+ import { afterAll, describe, expect, test } from "vitest";
16
+ import { runDeclarationChecks } from "@intentius/chant/workspace/checks";
17
+
18
+ const scratch: string[] = [];
19
+ afterAll(() => {
20
+ for (const d of scratch) rmSync(d, { recursive: true, force: true });
21
+ });
22
+
23
+ function repo(files: Record<string, string>): string {
24
+ const root = realpathSync(mkdtempSync(join(tmpdir(), "chant-4397-")));
25
+ scratch.push(root);
26
+ execFileSync("git", ["init", "-q"], { cwd: root });
27
+ for (const [path, text] of Object.entries(files)) {
28
+ mkdirSync(dirname(join(root, path)), { recursive: true });
29
+ writeFileSync(join(root, path), text);
30
+ }
31
+ return root;
32
+ }
33
+
34
+ const WORKSPACE = JSON.stringify(
35
+ {
36
+ name: "observability",
37
+ schema: 1,
38
+ members: [
39
+ { name: "warehouse", dir: "warehouse", kind: "chant" },
40
+ { name: "collector", dir: "collector", kind: "chant" },
41
+ ],
42
+ },
43
+ null,
44
+ 2,
45
+ );
46
+
47
+ const COLLECTOR = [
48
+ `import { ClickHouseExporter, OtlpReceiver, Pipeline } from "@intentius/chant-lexicon-otel";`,
49
+ ``,
50
+ `export const otlp = new OtlpReceiver({ protocols: { grpc: { endpoint: "0.0.0.0:4317" } } });`,
51
+ ``,
52
+ `export const clickhouse = new ClickHouseExporter({ endpoint: "tcp://clickhouse:9000", database: "otel", create_schema: false });`,
53
+ ``,
54
+ `export const logs = new Pipeline({ signal: "logs", receivers: [otlp], exporters: [clickhouse] });`,
55
+ `export const traces = new Pipeline({ signal: "traces", receivers: [otlp], exporters: [clickhouse] });`,
56
+ `export const metrics = new Pipeline({ signal: "metrics", receivers: [otlp], exporters: [clickhouse] });`,
57
+ ``,
58
+ ].join("\n");
59
+
60
+ const warehouse = (tables: string) =>
61
+ [`import { OtelTables } from "@intentius/chant-lexicon-sql/clickhouse";`, ``, `export const otel = OtelTables({ database: "otel"${tables} });`, ``].join("\n");
62
+
63
+ function observability(tables = ""): string {
64
+ return repo({
65
+ "chant.workspace.json": WORKSPACE,
66
+ "warehouse/chant.config.ts": `export default { lexicons: ["sql"], sql: { dialect: "clickhouse" } };\n`,
67
+ "warehouse/src/otel.ts": warehouse(tables),
68
+ "collector/chant.config.ts": `export default { lexicons: ["otel"] };\n`,
69
+ "collector/src/collector.ts": COLLECTOR,
70
+ });
71
+ }
72
+
73
+ describe("an otel member's clickhouse exporter links to the sql member's tables (#4397)", () => {
74
+ test("every table and column the exporter writes resolves against OtelTables", async () => {
75
+ const report = await runDeclarationChecks(observability());
76
+ expect(report.diagnostics.filter((d) => d.ruleId.startsWith("WSP09"))).toEqual([]);
77
+ const rows = report.links.filter((r) => r.consumer === "collector");
78
+ expect(rows.length).toBeGreaterThan(7);
79
+ expect(rows.every((r) => r.status === "resolved" && r.producer === "warehouse")).toBe(true);
80
+ expect(rows.map((r) => ("output" in r ? r.output : undefined))).toEqual(expect.arrayContaining(["otel.otel_logs", "otel.otel_traces.Events", "otel.otel_metrics_exponential_histogram.Scale"]));
81
+ });
82
+
83
+ test("renaming a table the exporter writes fails workspace check at the exporter", async () => {
84
+ const report = await runDeclarationChecks(observability(`, tables: { logs: "otel_logs_v2" }`));
85
+ const missing = report.diagnostics.filter((d) => d.ruleId === "WSP093");
86
+ expect(missing.length).toBeGreaterThan(0);
87
+ expect(missing.every((d) => d.file === "collector/src/collector.ts" && d.line === 5)).toBe(true);
88
+ expect(missing[0].message).toMatch(/collector's link to warehouse output otel\.otel_logs.* \(read by lexicon otel from collector\/src\/collector\.ts:5\) does not resolve/);
89
+ expect(report.ok).toBe(false);
90
+ });
91
+ });
@@ -0,0 +1,50 @@
1
+ /**
2
+ * The workspace links an otel member's declarations state (#4397): each
3
+ * table a `clickhouse` exporter writes, and each column its insert names, as
4
+ * a link to the sql member that declares them (#3931).
5
+ *
6
+ * | Link | Output |
7
+ * |---|---|
8
+ * | the table | `<database>.<table>` |
9
+ * | a column | `<database>.<table>.<column>`, anchored on the table |
10
+ *
11
+ * The exporter's config does not name the sql member, so each link is
12
+ * matched to the one member that exposes the table (`resolveLinks` in
13
+ * chant's `workspace/links.ts`). `chant workspace check` then fails (WSP093)
14
+ * when a table the exporter writes, or a column its insert names, is renamed
15
+ * or dropped in the sql member. The tables and columns come from
16
+ * `clickhouse-tables.ts`, which OTEL129 reads in one build root.
17
+ *
18
+ * A config value chant can't read without the environment, such as a
19
+ * database set from `${env:CH_DB}`, is a gap (WSP099), and the tables it
20
+ * names are not linked.
21
+ */
22
+
23
+ import type { Declarable } from "@intentius/chant/declarable";
24
+ import type { LexiconWorkspaceLink, LexiconWorkspaceLinks } from "@intentius/chant/graph-ir";
25
+ import { clickhouseExportersOf } from "./clickhouse-tables";
26
+ import { collectorConfig } from "./collector";
27
+ import type { OTelComponent } from "./define";
28
+
29
+ export function otelWorkspaceLinks(entities: ReadonlyMap<string, Declarable>): LexiconWorkspaceLinks | undefined {
30
+ const keyOf = new Map<string, string>();
31
+ for (const [key, e] of entities) {
32
+ const c = e as Partial<OTelComponent>;
33
+ if (c.componentKind === "exporter" && c.componentType === "clickhouse" && typeof c.componentId === "string") keyOf.set(c.componentId, key);
34
+ }
35
+ if (keyOf.size === 0) return undefined;
36
+
37
+ const links: LexiconWorkspaceLink[] = [];
38
+ const gaps: string[] = [];
39
+ for (const exp of clickhouseExportersOf(collectorConfig(entities.values()))) {
40
+ const entity = keyOf.get(exp.id) ?? exp.id;
41
+ for (const u of exp.unread) gaps.push(`exporter ${exp.id} (${entity}) sets ${u}, which is read from the environment, so the tables it names are not linked`);
42
+ for (const t of exp.tables) {
43
+ const table = `${exp.database}.${t.name}`;
44
+ links.push({ output: table, entity });
45
+ for (const column of t.columns) links.push({ output: `${table}.${column}`, anchor: table, entity });
46
+ }
47
+ }
48
+ if (links.length === 0 && gaps.length === 0) return undefined;
49
+ return { links, ...(gaps.length ? { gaps } : {}) };
50
+ }