diff --git a/README.md b/README.md index 5121a5f..c994232 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,64 @@ # codeanalyzer-schema -Versioned schema + +Versioned contracts for CodeLLM analyzers and SDK consumers. + +## v1 snapshots + +The `v1/` tree contains the published language-analysis and Neo4j contracts. +These files are compatibility snapshots: new schema-v2 work must not rewrite +them. A path selector can validate one snapshot independently, for example: + +```bash +python3 scripts/check.py v1/json/java +``` + +## IaC schema v2 contract + +The files under `v2/iac/` are the normative contract for +`codeanalyzer-iac`: + +- `v2/iac/json/analysis.schema.json` is the normative JSON output schema. + Analyzer golden outputs must validate against it at their declared analysis + level and must also pass `scripts/check_iac.py` semantic validation. +- `v2/iac/json/analysis.l1.sample.json`, `analysis.l2.sample.json`, and + `analysis.l3.sample.json` demonstrate the additive level contract. Higher + levels preserve every lower-level fact; resolution fields may be added but + existing facts may not be replaced. +- `v2/iac/neo4j/contract.schema.json` describes the graph-catalog format. + The `codeanalyzer-iac` catalog must byte-match + `v2/iac/neo4j/schema.neo4j.sample.json`. + +Draft 2020-12 validation covers document structure. The repository semantic +gate additionally enforces globally unique node IDs across all named +collections, real edge endpoints, source digests and span bounds, relative +artifact paths, contiguous render-profile layer ordinals, hash-only Secret +data, and `Package.id == Package.purl`. Every identity alias—including a Helm +Chart alias—must target a collected canonical node, must not self-target or +target another alias, and must have exactly one matching `iac_alias_of` edge. +The graph catalog admits only the shared neutral relationships +`HAS_ARTIFACT` and `DEFINES_CONFIG`; IaC-owned names use the `IAC_*` namespace, +relationship properties are empty, and all referenced labels must exist. + +Install the checker dependency once, then run the repository gates: + +```bash +python3 -m pip install jsonschema +python3 scripts/check.py +python3 -m unittest discover -s tests -v +``` + +The unit and repository check suites use only checked-in files and stay +network-free. These live repositories are downstream backend-consumer gates, +not inputs fetched by this repository: + +- `sample-daytrader/sample.daytrader.microservices@8a68b59430a94a242c54384763da9eb7682728b4`: + each emitted document + must pass structural validation with `v2/iac/json/analysis.schema.json` and + semantic `scripts/check_iac.py` validation. +- `quarkuscoffeeshop/quarkuscoffeeshop-helm@aa3c842658e0fc7e44fa25132d8b817eab225cbe`: + each emitted document must pass + structural validation with `v2/iac/json/analysis.schema.json` and semantic + `scripts/check_iac.py` validation. + +Preserve the emitted JSON from both pinned repositories and do not weaken +either contract to admit a consumer output. diff --git a/scripts/check.py b/scripts/check.py new file mode 100644 index 0000000..d7c116e --- /dev/null +++ b/scripts/check.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +"""Check every schema in this repo is a valid JSON Schema, and validate the +documents it claims to cover. + +A schema validates any *.sample.json sitting in its own directory, plus every +file matched by the globs in its x-cldk.validates list (relative to the schema). + + python3 scripts/check.py # all versions + python3 scripts/check.py v1/java # one directory +""" +import json +import sys +from pathlib import Path + +from jsonschema import Draft202012Validator + +if __package__: + from .check_iac import assert_monotone, check_catalog, check_document +else: + from check_iac import assert_monotone, check_catalog, check_document + +ROOT = Path(__file__).resolve().parent.parent + + +def check(schema_path: Path) -> int: + schema = json.loads(schema_path.read_text()) + Draft202012Validator.check_schema(schema) + rel = schema_path.relative_to(ROOT) + print(f"ok {rel}") + + covered = list(schema_path.parent.glob("*.sample.json")) + for pattern in schema.get("x-cldk", {}).get("validates", []): + covered += schema_path.parent.glob(pattern) + + failures = 0 + validator = Draft202012Validator(schema) + for sample in sorted(set(covered)): + errors = sorted(validator.iter_errors(json.loads(sample.read_text())), + key=lambda e: list(e.absolute_path)) + if errors: + failures += 1 + print(f"FAIL {sample.relative_to(ROOT)}: {len(errors)} error(s)") + for e in errors[:5]: + where = "/".join(str(p) for p in e.absolute_path) or "" + print(f" {where}: {e.message[:160]}") + else: + print(f"ok {sample.relative_to(ROOT)}") + return failures + + +def selected_iac_root(where: Path) -> Path | None: + """Return the IaC contract root only when the selected path includes it.""" + iac_root = ROOT / "v2/iac" + if not iac_root.exists(): + return None + try: + iac_root.relative_to(where) + return iac_root + except ValueError: + pass + try: + where.relative_to(iac_root) + return iac_root + except ValueError: + return None + + +def check_iac_contract(iac_root: Path) -> int: + """Run semantic and level-monotonicity checks for the IaC contract.""" + failures = 0 + analysis_paths = sorted((iac_root / "json").glob("analysis.l*.sample.json")) + analyses: list[tuple[Path, dict]] = [] + + for path in analysis_paths: + document = json.loads(path.read_text()) + analyses.append((path, document)) + errors = check_document(document) + if errors: + failures += 1 + print(f"FAIL {path.relative_to(ROOT)}: {len(errors)} semantic error(s)") + for error in errors: + print(f" {error}") + else: + print(f"ok {path.relative_to(ROOT)} (semantic)") + + catalog_path = iac_root / "neo4j/schema.neo4j.sample.json" + if catalog_path.exists(): + errors = check_catalog(json.loads(catalog_path.read_text())) + if errors: + failures += 1 + print(f"FAIL {catalog_path.relative_to(ROOT)}: {len(errors)} semantic error(s)") + for error in errors: + print(f" {error}") + else: + print(f"ok {catalog_path.relative_to(ROOT)} (semantic)") + + for (lower_path, lower), (higher_path, higher) in zip(analyses, analyses[1:]): + errors = assert_monotone(lower, higher) + label = f"{lower_path.name} <= {higher_path.name}" + if errors: + failures += 1 + print(f"FAIL {label}: {len(errors)} monotonicity error(s)") + for error in errors: + print(f" {error}") + else: + print(f"ok {label} (monotone)") + + return failures + + +def main() -> int: + where = ROOT / sys.argv[1] if len(sys.argv) > 1 else ROOT + schemas = sorted(where.rglob("*.schema.json")) + if not schemas: + print(f"no schemas under {where}") + return 1 + failures = sum(check(schema) for schema in schemas) + if failures: + return 1 + iac_root = selected_iac_root(where) + if iac_root is not None: + failures += check_iac_contract(iac_root) + return min(failures, 1) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/check_iac.py b/scripts/check_iac.py new file mode 100644 index 0000000..18d2a5c --- /dev/null +++ b/scripts/check_iac.py @@ -0,0 +1,668 @@ +#!/usr/bin/env python3 +"""Semantic conformance checks for the schema-v2 IaC contract.""" + +from __future__ import annotations + +import hashlib +import json +from pathlib import Path, PurePosixPath +import re + + +NEUTRAL_RELATIONSHIP_TYPES = {"DEFINES_CONFIG", "HAS_ARTIFACT"} +SUPPORTED_PURL_PREFIX = "pkg:oci/" +_LABEL_REFERENCE = re.compile(r":\s*`?([A-Za-z][A-Za-z0-9_]*)`?") +_CATALOG_PATH = ( + Path(__file__).resolve().parents[1] + / "v2" + / "iac" + / "neo4j" + / "schema.neo4j.sample.json" +) + +_FACET_LABELS = { + "helm_chart": "HelmChart", + "helm_requirements": "HelmRequirements", + "helm_lock": "HelmLock", + "helm_values": "HelmValues", + "helm_values_schema": "HelmValuesSchema", + "helm_template": "HelmTemplate", + "helm_crd": "HelmCRD", + "helm_ignore": "HelmIgnore", +} +_KIND_LABELS = { + "helm_dependency": {"HelmDependency"}, + "helm_chart_reference": {"HelmChartReference"}, + "helm_named_template": {"HelmNamedTemplate"}, + "helm_template_call": {"HelmTemplateCall"}, + "helm_value_reference": {"HelmValueReference"}, + "helm_resource_template": {"HelmResourceTemplate"}, + "helm_lookup_reference": {"HelmLookupReference"}, + "helm_render_profile": {"HelmRenderProfile"}, + "helm_value_layer": {"HelmValueLayer"}, + "helm_render": {"HelmRender"}, + "diagnostic": {"IaCDiagnostic", "HelmDiagnostic"}, + "kubernetes_resource": {"KubernetesResource"}, + "kubernetes_resource_address": {"KubernetesResourceAddress"}, + "package": {"Package"}, +} +_L2_KINDS = {"helm_chart_reference", "package"} +_L3_KINDS = { + "helm_render_profile", + "helm_value_layer", + "helm_render", + "kubernetes_resource", + "kubernetes_resource_address", +} +_L2_EDGES = { + "iac_part_of_chart", + "iac_targets_chart_reference", + "iac_resolves_to_chart", + "iac_identified_by_package", + "iac_calls_template", + "iac_references_value", +} +_L3_EDGES = { + "iac_declares_profile", + "iac_renders_chart", + "iac_has_value_layer", + "iac_reads_from", + "iac_has_render", + "iac_configured_by", + "iac_produces", + "iac_targets_resource", + "iac_derived_from", +} + + +def _walk(value: object): + yield value + if isinstance(value, dict): + for child in value.values(): + yield from _walk(child) + elif isinstance(value, list): + for child in value: + yield from _walk(child) + + +def _node_items(application: dict): + for value in _walk(application): + if not isinstance(value, dict): + continue + node_id = value.get("id") + kind = value.get("kind") + if isinstance(node_id, str) and isinstance(kind, str): + yield node_id, value + + +def collect_nodes(application: dict) -> dict[str, dict]: + """Collect all recursively nested nodes and reject every duplicate ID.""" + nodes: dict[str, dict] = {} + duplicate_ids: set[str] = set() + for node_id, node in _node_items(application): + if node_id in nodes: + duplicate_ids.add(node_id) + else: + nodes[node_id] = node + if duplicate_ids: + raise ValueError( + "\n".join(f"duplicate node id: {node_id}" for node_id in sorted(duplicate_ids)) + ) + return nodes + + +def _aliases(application: dict): + for value in _walk(application): + if not isinstance(value, dict): + continue + aliases = value.get("aliases") + if isinstance(aliases, list): + yield from (alias for alias in aliases if isinstance(alias, dict)) + + +def _edges(application: dict): + edge_groups = application.get("edges", {}) + if not isinstance(edge_groups, dict): + return + for edge_type, edge_map in edge_groups.items(): + if not isinstance(edge_map, dict): + continue + for edge_name, edge in edge_map.items(): + if isinstance(edge, dict): + yield edge_type, edge_name, edge + + +def _path_is_relative(path: str) -> bool: + parsed = PurePosixPath(path) + return ( + bool(path) + and not parsed.is_absolute() + and "\\" not in path + and all(part not in {".", ".."} for part in path.split("/")) + ) + + +def _check_artifact(artifact: dict, errors: list[str]) -> None: + artifact_id = artifact.get("id", "") + path = artifact.get("path") + if isinstance(path, str) and not _path_is_relative(path): + errors.append(f"artifact path is not relative: {artifact_id}: {path}") + + source = artifact.get("source") + if not isinstance(source, str): + return + source_bytes = source.encode("utf-8") + if artifact.get("size_bytes") != len(source_bytes): + errors.append(f"artifact size_bytes does not match UTF-8 source length: {artifact_id}") + if not source and any( + ( + artifact.get("iac") is not None, + artifact.get("codeanalyzer_iac_config") is not None, + bool(artifact.get("config_keys")), + bool(artifact.get("aliases")), + ) + ): + errors.append(f"empty-source artifact must remain raw: {artifact_id}") + if source: + digest = hashlib.sha256(source_bytes).hexdigest() + if artifact.get("sha256") != digest: + errors.append(f"artifact sha256 does not match source: {artifact_id}") + + for value in _walk(artifact): + if not isinstance(value, dict) or not isinstance(value.get("span"), dict): + continue + byte_range = value["span"].get("bytes") + if ( + not isinstance(byte_range, list) + or len(byte_range) != 2 + or not all(isinstance(offset, int) for offset in byte_range) + or byte_range[0] < 0 + or byte_range[0] > byte_range[1] + or byte_range[1] > len(source_bytes) + ): + node_id = value.get("id", "") + errors.append(f"span bytes out of bounds: {node_id} in {artifact_id}") + + +def _projected_labels(node_id: str, node: dict, alias_ids: set[object]) -> set[str]: + if node_id in alias_ids: + return {"IdentityAlias", "IaCAlias"} + kind = node.get("kind") + if kind == "application": + return {"Application", "IaCApplication"} + if kind == "artifact": + labels = {"Artifact"} + facet = node.get("iac") + if isinstance(facet, dict): + labels.update({"IaCArtifact", "HelmArtifact"}) + label = _FACET_LABELS.get(facet.get("kind")) + if label: + labels.add(label) + if isinstance(node.get("codeanalyzer_iac_config"), dict): + labels.add("CodeAnalyzerIaCConfig") + return labels + if kind == "config_key": + labels = {"ConfigKey"} + facet = node.get("iac") + if isinstance(facet, dict) and facet.get("kind") == "helm_value": + labels.update({"IaCValue", "HelmValue"}) + return labels + return set(_KIND_LABELS.get(kind, ())) + + +def _relationship_contract() -> dict[str, dict]: + catalog = json.loads(_CATALOG_PATH.read_text()) + return { + relationship["type"].lower(): relationship + for relationship in catalog.get("relationship_types", []) + if isinstance(relationship, dict) and isinstance(relationship.get("type"), str) + } + + +def _edge_count(edge_rows: list[tuple[str, str, dict]], edge_type: str, src: object, dst: object) -> int: + return sum( + candidate_type == edge_type + and edge.get("src") == src + and edge.get("dst") == dst + for candidate_type, _, edge in edge_rows + ) + + +def _require_edge( + edge_rows: list[tuple[str, str, dict]], + edge_type: str, + src: object, + dst: object, + description: str, + errors: list[str], +) -> None: + count = _edge_count(edge_rows, edge_type, src, dst) + if count != 1: + errors.append(f"{description} must have exactly one matching {edge_type} edge: {src}: {dst}: found {count}") + + +def _check_level(document: dict, application: dict, errors: list[str]) -> None: + max_level = document.get("max_level") + if not isinstance(max_level, int): + return + + if max_level < 2: + for _, node in _node_items(application): + kind = node.get("kind") + if kind in _L2_KINDS: + errors.append(f"fact requires max_level 2: {kind}") + if "target_id" in node: + errors.append("fact requires max_level 2: target_id") + if kind == "helm_chart_reference" and any( + field in node for field in ("purl", "resolved_chart_id") + ): + errors.append("fact requires max_level 2: helm_chart_reference resolution") + for edge_type, _, _ in _edges(application): + if edge_type in _L2_EDGES: + errors.append(f"edge requires max_level 2: {edge_type}") + + if max_level < 3: + for _, node in _node_items(application): + kind = node.get("kind") + if kind in _L3_KINDS: + errors.append(f"fact requires max_level 3: {kind}") + for artifact in application.get("artifacts", {}).values(): + if isinstance(artifact, dict) and "codeanalyzer_iac_config" in artifact: + errors.append("fact requires max_level 3: codeanalyzer_iac_config") + for edge_type, _, _ in _edges(application): + if edge_type in _L3_EDGES: + errors.append(f"edge requires max_level 3: {edge_type}") + + +def _check_scalar_references( + nodes: dict[str, dict], + alias_ids: set[object], + edge_rows: list[tuple[str, str, dict]], + errors: list[str], +) -> None: + def has_label(node_id: object, label: str) -> bool: + node = nodes.get(node_id) + return isinstance(node, dict) and label in _projected_labels(str(node_id), node, alias_ids) + + for node_id, node in nodes.items(): + kind = node.get("kind") + if kind == "helm_template_call" and "target_id" in node: + target = node.get("target_id") + if not has_label(target, "HelmNamedTemplate"): + errors.append(f"template target_id must target a HelmNamedTemplate: {node_id}: {target}") + _require_edge(edge_rows, "iac_calls_template", node_id, target, "target_id", errors) + elif kind == "helm_value_reference" and "target_id" in node: + target = node.get("target_id") + if not has_label(target, "HelmValue"): + errors.append(f"value target_id must target a HelmValue: {node_id}: {target}") + _require_edge(edge_rows, "iac_references_value", node_id, target, "target_id", errors) + elif kind == "helm_chart_reference": + target = node.get("resolved_chart_id") + if target is not None: + if not has_label(target, "HelmChart"): + errors.append(f"resolved_chart_id must target a HelmChart Artifact: {node_id}: {target}") + _require_edge(edge_rows, "iac_resolves_to_chart", node_id, target, "resolved_chart_id", errors) + purl = node.get("purl") + if purl is not None: + if not has_label(purl, "Package"): + errors.append(f"purl must target a Package: {node_id}: {purl}") + _require_edge(edge_rows, "iac_identified_by_package", node_id, purl, "purl", errors) + elif kind == "helm_render_profile": + chart_id = node.get("chart_id") + if not has_label(chart_id, "HelmChart"): + errors.append(f"chart_id must target a HelmChart Artifact: {node_id}: {chart_id}") + _require_edge(edge_rows, "iac_renders_chart", node_id, chart_id, "chart_id", errors) + layers = node.get("value_layers", {}) + if isinstance(layers, dict): + for layer in layers.values(): + if not isinstance(layer, dict): + continue + layer_id = layer.get("id") + source_id = layer.get("source_id") + _require_edge(edge_rows, "iac_has_value_layer", node_id, layer_id, "value layer", errors) + if not (has_label(source_id, "Artifact") or has_label(source_id, "ConfigKey")): + errors.append(f"source_id must target an Artifact or ConfigKey: {layer_id}: {source_id}") + _require_edge(edge_rows, "iac_reads_from", layer_id, source_id, "source_id", errors) + elif kind == "helm_render": + profile_id = node.get("profile_id") + if not has_label(profile_id, "HelmRenderProfile"): + errors.append(f"profile_id must target a HelmRenderProfile: {node_id}: {profile_id}") + _require_edge(edge_rows, "iac_configured_by", node_id, profile_id, "profile_id", errors) + profile = nodes.get(profile_id) + if isinstance(profile, dict): + layers = profile.get("value_layers", {}) + if isinstance(layers, dict): + expected = [ + layer.get("id") + for layer in sorted( + (value for value in layers.values() if isinstance(value, dict)), + key=lambda value: value.get("ordinal", -1), + ) + ] + if node.get("value_layer_ids") != expected: + errors.append(f"render value_layer_ids do not match profile layers: {node_id}") + elif kind == "diagnostic" and "artifact_id" in node: + artifact_id = node.get("artifact_id") + if not has_label(artifact_id, "Artifact"): + errors.append(f"artifact_id must target an Artifact: {node_id}: {artifact_id}") + elif kind == "kubernetes_resource": + address_id = node.get("address_id") + if address_id is not None: + if not has_label(address_id, "KubernetesResourceAddress"): + errors.append(f"address_id must target a KubernetesResourceAddress: {node_id}: {address_id}") + _require_edge(edge_rows, "iac_targets_resource", node_id, address_id, "address_id", errors) + for origin_id in node.get("origin_ids", []): + if not has_label(origin_id, "HelmResourceTemplate"): + errors.append(f"origin_id must target a HelmResourceTemplate: {node_id}: {origin_id}") + _require_edge(edge_rows, "iac_derived_from", node_id, origin_id, "origin_id", errors) + + +def _check_containment( + application: dict, + edge_rows: list[tuple[str, str, dict]], + max_level: object, + errors: list[str], +) -> None: + app_id = application.get("id") + artifacts = application.get("artifacts", {}) + if not isinstance(artifacts, dict): + return + + chart_artifacts: list[tuple[str, str]] = [] + for path, artifact in artifacts.items(): + if not isinstance(artifact, dict): + continue + facet = artifact.get("iac") + if isinstance(facet, dict) and facet.get("kind") == "helm_chart": + chart_artifacts.append((str(PurePosixPath(path).parent), artifact.get("id"))) + + for path, artifact in artifacts.items(): + if not isinstance(artifact, dict): + continue + artifact_id = artifact.get("id") + _require_edge(edge_rows, "has_artifact", app_id, artifact_id, "artifact containment", errors) + for config_key in artifact.get("config_keys", {}).values(): + if isinstance(config_key, dict): + _require_edge(edge_rows, "defines_config", artifact_id, config_key.get("id"), "config-key containment", errors) + for alias in artifact.get("aliases", []): + if isinstance(alias, dict): + _require_edge(edge_rows, "iac_has_alias", artifact_id, alias.get("id"), "alias containment", errors) + + facet = artifact.get("iac") + if isinstance(facet, dict): + facet_kind = facet.get("kind") + if facet_kind == "helm_template": + for child in facet.get("named_templates", {}).values(): + if isinstance(child, dict): + _require_edge(edge_rows, "iac_defines_template", artifact_id, child.get("id"), "named-template containment", errors) + for child in facet.get("template_calls", {}).values(): + if isinstance(child, dict): + _require_edge(edge_rows, "iac_has_template_call", artifact_id, child.get("id"), "template-call containment", errors) + for child in facet.get("value_references", {}).values(): + if isinstance(child, dict): + _require_edge(edge_rows, "iac_has_value_reference", artifact_id, child.get("id"), "value-reference containment", errors) + for child in facet.get("resource_templates", {}).values(): + if isinstance(child, dict): + _require_edge(edge_rows, "iac_has_resource_template", artifact_id, child.get("id"), "resource-template containment", errors) + for child in facet.get("lookup_references", {}).values(): + if isinstance(child, dict): + _require_edge(edge_rows, "iac_has_lookup_reference", artifact_id, child.get("id"), "lookup-reference containment", errors) + elif facet_kind == "helm_chart": + for child in facet.get("dependencies", {}).values(): + if isinstance(child, dict): + dependency_id = child.get("id") + _require_edge( + edge_rows, + "iac_declares_dependency", + artifact_id, + dependency_id, + "dependency containment", + errors, + ) + if isinstance(max_level, int) and max_level >= 2: + target_count = sum( + edge_type == "iac_targets_chart_reference" + and edge.get("src") == dependency_id + for edge_type, _, edge in edge_rows + ) + if target_count != 1: + errors.append( + "dependency must have exactly one " + "iac_targets_chart_reference edge: " + f"{dependency_id}: found {target_count}" + ) + for child in facet.get("render_profiles", {}).values(): + if isinstance(child, dict): + _require_edge(edge_rows, "iac_declares_profile", artifact_id, child.get("id"), "profile containment", errors) + for render in facet.get("renders", {}).values(): + if not isinstance(render, dict): + continue + render_id = render.get("id") + _require_edge(edge_rows, "iac_has_render", artifact_id, render_id, "render containment", errors) + for diagnostic in render.get("diagnostics", {}).values(): + if isinstance(diagnostic, dict): + _require_edge(edge_rows, "iac_has_diagnostic", render_id, diagnostic.get("id"), "diagnostic containment", errors) + for resource in render.get("resources", {}).values(): + if not isinstance(resource, dict): + continue + resource_id = resource.get("id") + if resource.get("render_id") != render_id: + errors.append(f"resource render_id differs from containing render: {resource_id}") + _require_edge(edge_rows, "iac_produces", render_id, resource_id, "resource containment", errors) + producing_edges = sum( + edge_type == "iac_produces" + and edge.get("dst") == resource_id + for edge_type, _, edge in edge_rows + ) + if producing_edges != 1: + errors.append( + "resource must have exactly one containing " + f"iac_produces edge: {resource_id}: found {producing_edges}" + ) + + if facet_kind != "helm_chart" and isinstance(max_level, int) and max_level >= 2: + artifact_parent = str(PurePosixPath(path).parent) + candidates = [ + (chart_dir, chart_id) + for chart_dir, chart_id in chart_artifacts + if artifact_parent == chart_dir or artifact_parent.startswith(f"{chart_dir}/") + ] + if candidates: + _, chart_id = max(candidates, key=lambda candidate: len(candidate[0])) + _require_edge(edge_rows, "iac_part_of_chart", artifact_id, chart_id, "chart membership", errors) + + config_facet = artifact.get("codeanalyzer_iac_config") + if isinstance(config_facet, dict): + for profile in config_facet.get("render_profiles", {}).values(): + if isinstance(profile, dict): + _require_edge(edge_rows, "iac_declares_profile", artifact_id, profile.get("id"), "profile containment", errors) + + +def check_document(document: dict) -> list[str]: + """Return sorted violations of IaC invariants JSON Schema cannot express.""" + errors: list[str] = [] + application = document.get("application", {}) + if not isinstance(application, dict): + return ["application must be an object"] + + try: + nodes = collect_nodes(application) + except ValueError as error: + errors.extend(str(error).splitlines()) + # Retain the first node for each ID so independent checks can still run. + nodes = {} + for node_id, node in _node_items(application): + nodes.setdefault(node_id, node) + + edge_rows = list(_edges(application)) + aliases = list(_aliases(application)) + alias_ids = {alias.get("id") for alias in aliases} + relationship_contract = _relationship_contract() + for edge_type, edge_name, edge in edge_rows: + src = edge.get("src") + dst = edge.get("dst") + if src not in nodes: + errors.append(f"dangling edge source: {edge_type}/{edge_name}: {src}") + else: + contract = relationship_contract.get(edge_type) + if contract is None: + errors.append(f"edge family is absent from graph catalog: {edge_type}") + elif not ( + _projected_labels(str(src), nodes[src], alias_ids) + & set(contract.get("from", [])) + ): + errors.append(f"edge endpoint type violation: {edge_type}/{edge_name}/src: {src}") + if dst not in nodes: + errors.append(f"dangling edge destination: {edge_type}/{edge_name}: {dst}") + else: + contract = relationship_contract.get(edge_type) + if contract is not None and not ( + _projected_labels(str(dst), nodes[dst], alias_ids) + & set(contract.get("to", [])) + ): + errors.append(f"edge endpoint type violation: {edge_type}/{edge_name}/dst: {dst}") + + alias_edges = [edge for edge_type, _, edge in edge_rows if edge_type == "iac_alias_of"] + for alias in aliases: + alias_id = alias.get("id") + target = alias.get("target") + if alias_id == target or target not in nodes or target in alias_ids: + errors.append(f"alias target is not canonical: {alias_id}: {target}") + matching_edges = sum( + edge.get("src") == alias_id and edge.get("dst") == target for edge in alias_edges + ) + if matching_edges != 1: + errors.append( + f"alias must have exactly one matching iac_alias_of edge: {alias_id}: found {matching_edges}" + ) + + for node_id, node in nodes.items(): + kind = node.get("kind") + if kind == "artifact": + _check_artifact(node, errors) + elif kind == "package": + if node.get("id") != node.get("purl"): + errors.append(f"package id must equal purl: {node_id}") + if not str(node.get("purl", "")).startswith(SUPPORTED_PURL_PREFIX): + errors.append(f"unsupported package URL type: {node_id}") + elif kind == "helm_chart_reference" and "purl" in node: + if not str(node.get("purl", "")).startswith(SUPPORTED_PURL_PREFIX): + errors.append(f"unsupported package URL type: {node_id}") + elif kind == "helm_render_profile": + layers = node.get("value_layers", {}) + if isinstance(layers, dict): + ordinals = [ + layer.get("ordinal") + for layer in layers.values() + if isinstance(layer, dict) + ] + if ( + len(ordinals) != len(layers) + or not all(isinstance(ordinal, int) for ordinal in ordinals) + or sorted(ordinals) != list(range(len(layers))) + ): + errors.append( + f"profile layer ordinals must be contiguous from zero: {node_id}" + ) + elif kind == "kubernetes_resource" and node.get("resource_kind") == "Secret": + secret_data = node.get("secret_data", {}) + if isinstance(secret_data, dict): + for datum_name, datum in secret_data.items(): + if not isinstance(datum, dict) or set(datum) != {"key", "sha256"}: + errors.append( + f"Secret data must contain only key and sha256: {node_id}: {datum_name}" + ) + + _check_level(document, application, errors) + _check_scalar_references(nodes, alias_ids, edge_rows, errors) + _check_containment(application, edge_rows, document.get("max_level"), errors) + + return sorted(set(errors)) + + +def assert_monotone(lower: dict, higher: dict) -> list[str]: + """Require the higher analysis level to preserve the lower projection.""" + errors: list[str] = [] + + def walk(left: object, right: object, path: tuple[str, ...]) -> None: + if isinstance(left, dict): + if not isinstance(right, dict): + errors.append(f"type changed at {'/'.join(path)}") + return + for key, value in left.items(): + if key == "max_level": + continue + if key not in right: + errors.append(f"removed {'/'.join(path + (key,))}") + else: + walk(value, right[key], path + (key,)) + return + if isinstance(left, list): + if not isinstance(right, list) or left != right[: len(left)]: + errors.append(f"list changed at {'/'.join(path)}") + return + if left != right: + errors.append(f"value changed at {'/'.join(path)}") + + walk(lower, higher, ()) + return sorted(errors) + + +def _duplicates(values: list[str]) -> list[str]: + seen: set[str] = set() + duplicates: set[str] = set() + for value in values: + if value in seen: + duplicates.add(value) + seen.add(value) + return sorted(duplicates) + + +def check_catalog(catalog: dict) -> list[str]: + """Return sorted semantic violations in the Neo4j graph catalog.""" + errors: list[str] = [] + labels = [ + entry.get("label") + for entry in catalog.get("node_labels", []) + if isinstance(entry, dict) and isinstance(entry.get("label"), str) + ] + label_set = set(labels) + for label in _duplicates(labels): + errors.append(f"duplicate node label: {label}") + + relationships = [ + entry for entry in catalog.get("relationship_types", []) if isinstance(entry, dict) + ] + relationship_types = [ + entry.get("type") + for entry in relationships + if isinstance(entry.get("type"), str) + ] + for relationship_type in _duplicates(relationship_types): + errors.append(f"duplicate relationship type: {relationship_type}") + + for relationship in relationships: + relationship_type = relationship.get("type", "") + if relationship.get("properties"): + errors.append(f"relationship properties must be empty: {relationship_type}") + if ( + relationship_type not in NEUTRAL_RELATIONSHIP_TYPES + and not str(relationship_type).startswith("IAC_") + ): + errors.append( + f"relationship type must be IAC_* or neutral allowlisted: {relationship_type}" + ) + for endpoint in ("from", "to"): + for label in relationship.get(endpoint, []): + if label not in label_set: + errors.append( + f"unknown relationship endpoint label: {relationship_type}/{endpoint}: {label}" + ) + + for entry_kind, collection_name in (("constraint", "constraints"), ("index", "indexes")): + for statement in catalog.get(collection_name, []): + for label in _LABEL_REFERENCE.findall(statement): + if label not in label_set: + errors.append(f"{entry_kind} mentions unknown label: {label}") + + return sorted(errors) diff --git a/tests/test_check_iac.py b/tests/test_check_iac.py new file mode 100644 index 0000000..c8c1e2d --- /dev/null +++ b/tests/test_check_iac.py @@ -0,0 +1,987 @@ +from copy import deepcopy +import json +from pathlib import Path +import shutil +import subprocess +import sys +import tempfile +import unittest + +from jsonschema import Draft202012Validator + +from scripts.check_iac import assert_monotone, check_catalog, check_document, collect_nodes + + +ROOT = Path(__file__).resolve().parents[1] + + +OWNERSHIP_PROPERTIES = { + "neutral": {}, + "shared_facet": { + "iac_producer": "string", + "iac_analyzer_version": "string", + "iac_app_id": "string", + }, + "owned": { + "producer": "string", + "analyzer_version": "string", + "iac_app_id": "string", + }, +} + + +# Normative JSON-kind/facet -> Neo4j projection table. Each entry names the +# complete label stack, the label whose property contract is checked, its +# ownership posture, and every JSON field projected onto that label. +EXPECTED_NODE_PROJECTIONS = { + "application:neutral": { + "definition": "Application", + "labels": ["Application"], + "catalog_label": "Application", + "ownership": "neutral", + "fields": {"id": ("id", "string")}, + "omitted": {"kind", "artifacts", "packages", "external_chart_references", "kubernetes_resource_addresses", "diagnostics", "edges"}, + }, + "application:iac": { + "definition": None, + "labels": ["Application", "IaCApplication"], + "catalog_label": "IaCApplication", + "ownership": "shared_facet", + "fields": {"$node_id": ("id", "string")}, + "omitted": set(), + }, + "artifact:neutral": { + "definition": "Artifact", + "labels": ["Artifact"], + "catalog_label": "Artifact", + "ownership": "neutral", + "fields": { + "id": ("id", "string"), + "path": ("path", "string"), + "format": ("format", "string"), + "source": ("source", "string"), + "sha256": ("sha256", "string"), + "size_bytes": ("size_bytes", "integer"), + }, + "omitted": {"kind", "config_keys", "aliases", "iac", "codeanalyzer_iac_config"}, + }, + "artifact:iac": { + "definition": None, + "labels": ["Artifact", "IaCArtifact"], + "catalog_label": "IaCArtifact", + "ownership": "shared_facet", + "fields": { + "$node_id": ("id", "string"), + "dialect": ("iac_dialect", "string"), + "kind": ("iac_kind", "string"), + "status": ("iac_status", "string"), + }, + "omitted": set(), + }, + "artifact:helm": { + "definition": None, + "labels": ["Artifact", "IaCArtifact", "HelmArtifact"], + "catalog_label": "HelmArtifact", + "ownership": "shared_facet", + "fields": { + "$node_id": ("id", "string"), + "dialect": ("iac_dialect", "string"), + "kind": ("iac_kind", "string"), + "status": ("iac_status", "string"), + }, + "omitted": set(), + }, + "helm_chart": { + "definition": "HelmChart", + "labels": ["Artifact", "IaCArtifact", "HelmArtifact", "HelmChart"], + "catalog_label": "HelmChart", + "ownership": "shared_facet", + "fields": { + "$node_id": ("id", "string"), + "dialect": ("iac_dialect", "string"), + "kind": ("iac_kind", "string"), + "status": ("iac_status", "string"), + "api_version": ("helm_api_version", "string"), + "name": ("helm_name", "string"), + "version": ("helm_version", "string"), + "kube_version": ("helm_kube_version", "string"), + "description": ("helm_description", "string"), + "chart_type": ("helm_chart_type", "string"), + "keywords": ("helm_keywords", "string[]"), + "home": ("helm_home", "string"), + "sources": ("helm_sources", "string[]"), + "maintainers": ("helm_maintainers_json", "string"), + "icon": ("helm_icon", "string"), + "app_version": ("helm_app_version", "string"), + "deprecated": ("helm_deprecated", "boolean"), + "annotations": ("helm_annotations_json", "string"), + }, + "omitted": {"dependencies", "render_profiles", "renders"}, + }, + "helm_requirements": { + "definition": "HelmRequirements", + "labels": ["Artifact", "IaCArtifact", "HelmArtifact", "HelmRequirements"], + "catalog_label": "HelmRequirements", + "ownership": "shared_facet", + "fields": { + "$node_id": ("id", "string"), "dialect": ("iac_dialect", "string"), + "kind": ("iac_kind", "string"), "status": ("iac_status", "string"), + "roles": ("helm_roles", "string[]"), + }, + "omitted": {"dependencies"}, + }, + "helm_lock": { + "definition": "HelmLock", + "labels": ["Artifact", "IaCArtifact", "HelmArtifact", "HelmLock"], + "catalog_label": "HelmLock", + "ownership": "shared_facet", + "fields": { + "$node_id": ("id", "string"), "dialect": ("iac_dialect", "string"), + "kind": ("iac_kind", "string"), "status": ("iac_status", "string"), + "roles": ("helm_roles", "string[]"), + }, + "omitted": {"dependencies"}, + }, + "helm_values": { + "definition": "HelmValues", + "labels": ["Artifact", "IaCArtifact", "HelmArtifact", "HelmValues"], + "catalog_label": "HelmValues", + "ownership": "shared_facet", + "fields": { + "$node_id": ("id", "string"), "dialect": ("iac_dialect", "string"), + "kind": ("iac_kind", "string"), "status": ("iac_status", "string"), + "roles": ("helm_roles", "string[]"), + }, + "omitted": set(), + }, + "helm_values_schema": { + "definition": "HelmValuesSchema", + "labels": ["Artifact", "IaCArtifact", "HelmArtifact", "HelmValuesSchema"], + "catalog_label": "HelmValuesSchema", + "ownership": "shared_facet", + "fields": { + "$node_id": ("id", "string"), "dialect": ("iac_dialect", "string"), + "kind": ("iac_kind", "string"), "status": ("iac_status", "string"), + "roles": ("helm_roles", "string[]"), + }, + "omitted": set(), + }, + "helm_template": { + "definition": "HelmTemplate", + "labels": ["Artifact", "IaCArtifact", "HelmArtifact", "HelmTemplate"], + "catalog_label": "HelmTemplate", + "ownership": "shared_facet", + "fields": { + "$node_id": ("id", "string"), "dialect": ("iac_dialect", "string"), + "kind": ("iac_kind", "string"), "status": ("iac_status", "string"), + "roles": ("helm_roles", "string[]"), + }, + "omitted": {"named_templates", "template_calls", "value_references", "resource_templates", "lookup_references"}, + }, + "helm_crd": { + "definition": "HelmCRD", + "labels": ["Artifact", "IaCArtifact", "HelmArtifact", "HelmCRD"], + "catalog_label": "HelmCRD", + "ownership": "shared_facet", + "fields": { + "$node_id": ("id", "string"), "dialect": ("iac_dialect", "string"), + "kind": ("iac_kind", "string"), "status": ("iac_status", "string"), + "roles": ("helm_roles", "string[]"), + }, + "omitted": set(), + }, + "helm_ignore": { + "definition": "HelmIgnore", + "labels": ["Artifact", "IaCArtifact", "HelmArtifact", "HelmIgnore"], + "catalog_label": "HelmIgnore", + "ownership": "shared_facet", + "fields": { + "$node_id": ("id", "string"), "dialect": ("iac_dialect", "string"), + "kind": ("iac_kind", "string"), "status": ("iac_status", "string"), + "roles": ("helm_roles", "string[]"), + }, + "omitted": set(), + }, + "config_key:neutral": { + "definition": "ConfigKey", + "labels": ["ConfigKey"], + "catalog_label": "ConfigKey", + "ownership": "neutral", + "fields": { + "id": ("id", "string"), "name": ("name", "string"), + "path": ("path", "string"), "span": ("span_json", "string"), + }, + "omitted": {"kind", "iac"}, + }, + "config_key:iac_value": { + "definition": "HelmValueFacet", + "labels": ["ConfigKey", "IaCValue"], + "catalog_label": "IaCValue", + "ownership": "shared_facet", + "fields": {"$node_id": ("id", "string"), "kind": ("iac_kind", "string")}, + "omitted": set(), + }, + "config_key:helm_value": { + "definition": "HelmValueFacet", + "labels": ["ConfigKey", "IaCValue", "HelmValue"], + "catalog_label": "HelmValue", + "ownership": "shared_facet", + "fields": {"$node_id": ("id", "string"), "kind": ("iac_kind", "string")}, + "omitted": set(), + }, + "codeanalyzer_iac_config": { + "definition": "CodeAnalyzerIaCConfig", + "labels": ["Artifact", "CodeAnalyzerIaCConfig"], + "catalog_label": "CodeAnalyzerIaCConfig", + "ownership": "shared_facet", + "fields": {"$node_id": ("id", "string"), "config_version": ("iac_config_version", "integer")}, + "omitted": {"kind", "render_profiles"}, + }, + "helm_dependency": { + "definition": "HelmDependency", "labels": ["HelmDependency"], "catalog_label": "HelmDependency", "ownership": "owned", + "fields": { + "id": ("id", "string"), "name": ("name", "string"), "version_constraint": ("version_constraint", "string"), + "alias": ("alias", "string"), "repository": ("repository", "string"), "condition": ("condition", "string"), + "tags": ("tags", "string[]"), "import_values": ("import_values_json", "string"), "span": ("span_json", "string"), + }, "omitted": {"kind"}, + }, + "helm_chart_reference": { + "definition": "HelmChartReference", "labels": ["HelmChartReference"], "catalog_label": "HelmChartReference", "ownership": "owned", + "fields": { + "id": ("id", "string"), "name": ("name", "string"), "version_constraint": ("version_constraint", "string"), + "repository": ("repository", "string"), "purl": ("purl", "string"), "resolved_chart_id": ("resolved_chart_id", "string"), + }, "omitted": {"kind"}, + }, + "helm_named_template": { + "definition": "HelmNamedTemplate", "labels": ["HelmNamedTemplate"], "catalog_label": "HelmNamedTemplate", "ownership": "owned", + "fields": {"id": ("id", "string"), "name": ("name", "string"), "span": ("span_json", "string")}, "omitted": {"kind"}, + }, + "helm_template_call": { + "definition": "HelmTemplateCall", "labels": ["HelmTemplateCall"], "catalog_label": "HelmTemplateCall", "ownership": "owned", + "fields": { + "id": ("id", "string"), "call_kind": ("call_kind", "string"), "name_expression": ("name_expression", "string"), + "target_id": ("target_id", "string"), "span": ("span_json", "string"), + }, "omitted": {"kind"}, + }, + "helm_value_reference": { + "definition": "HelmValueReference", "labels": ["HelmValueReference"], "catalog_label": "HelmValueReference", "ownership": "owned", + "fields": { + "id": ("id", "string"), "path_expression": ("path_expression", "string"), + "target_id": ("target_id", "string"), "span": ("span_json", "string"), + }, "omitted": {"kind"}, + }, + "helm_resource_template": { + "definition": "HelmResourceTemplate", "labels": ["HelmResourceTemplate"], "catalog_label": "HelmResourceTemplate", "ownership": "owned", + "fields": {"id": ("id", "string"), "document_index": ("document_index", "integer"), "span": ("span_json", "string")}, "omitted": {"kind"}, + }, + "helm_lookup_reference": { + "definition": "HelmLookupReference", "labels": ["HelmLookupReference"], "catalog_label": "HelmLookupReference", "ownership": "owned", + "fields": { + "id": ("id", "string"), "group_expression": ("group_expression", "string"), + "version_expression": ("version_expression", "string"), "resource_kind_expression": ("resource_kind_expression", "string"), + "namespace_expression": ("namespace_expression", "string"), "name_expression": ("name_expression", "string"), + "span": ("span_json", "string"), + }, "omitted": {"kind"}, + }, + "helm_render_profile": { + "definition": "HelmRenderProfile", "labels": ["HelmRenderProfile"], "catalog_label": "HelmRenderProfile", "ownership": "owned", + "fields": { + "id": ("id", "string"), "name": ("name", "string"), "origin": ("origin", "string"), "chart_id": ("chart_id", "string"), + "release_name": ("release_name", "string"), "namespace": ("namespace", "string"), "api_versions": ("api_versions", "string[]"), + "kube_version": ("kube_version", "string"), + }, "omitted": {"kind", "value_layers"}, + }, + "helm_value_layer": { + "definition": "HelmValueLayer", "labels": ["HelmValueLayer"], "catalog_label": "HelmValueLayer", "ownership": "owned", + "fields": {"id": ("id", "string"), "ordinal": ("ordinal", "integer"), "source_id": ("source_id", "string")}, "omitted": {"kind"}, + }, + "helm_render": { + "definition": "HelmRender", "labels": ["HelmRender"], "catalog_label": "HelmRender", "ownership": "owned", + "fields": { + "id": ("id", "string"), "status": ("status", "string"), "profile_id": ("profile_id", "string"), + "renderer_name": ("renderer_name", "string"), "renderer_version": ("renderer_version", "string"), + "value_layer_ids": ("value_layer_ids", "string[]"), "effective_values_sha256": ("effective_values_sha256", "string"), + "phase": ("phase", "string"), + }, "omitted": {"kind", "diagnostics", "resources"}, + }, + "diagnostic:iac": { + "definition": "Diagnostic", "labels": ["IaCDiagnostic"], "catalog_label": "IaCDiagnostic", "ownership": "owned", + "fields": { + "id": ("id", "string"), "severity": ("severity", "string"), "code": ("code", "string"), "message": ("message", "string"), + "phase": ("phase", "string"), "artifact_id": ("artifact_id", "string"), "span": ("span_json", "string"), + }, "omitted": {"kind"}, + }, + "diagnostic:helm": { + "definition": "Diagnostic", "labels": ["IaCDiagnostic", "HelmDiagnostic"], "catalog_label": "HelmDiagnostic", "ownership": "owned", + "fields": { + "id": ("id", "string"), "severity": ("severity", "string"), "code": ("code", "string"), "message": ("message", "string"), + "phase": ("phase", "string"), "artifact_id": ("artifact_id", "string"), "span": ("span_json", "string"), + }, "omitted": {"kind"}, + }, + "kubernetes_resource": { + "definition": "KubernetesResource", "labels": ["KubernetesResource"], "catalog_label": "KubernetesResource", "ownership": "owned", + "fields": { + "id": ("id", "string"), "api_version": ("api_version", "string"), "resource_kind": ("resource_kind", "string"), + "manifest_sha256": ("manifest_sha256", "string"), "render_id": ("render_id", "string"), "origin_ids": ("origin_ids", "string[]"), + "namespace": ("namespace", "string"), "name": ("name", "string"), "generate_name": ("generate_name", "string"), + "labels": ("labels_json", "string"), "annotations": ("annotations_json", "string"), "plural": ("plural", "string"), + "address_id": ("address_id", "string"), "secret_data": ("secret_data_json", "string"), + }, "omitted": {"kind"}, + }, + "kubernetes_resource_address": { + "definition": "KubernetesResourceAddress", "labels": ["KubernetesResourceAddress"], "catalog_label": "KubernetesResourceAddress", "ownership": "owned", + "fields": { + "id": ("id", "string"), "group": ("group", "string"), "resource_kind": ("resource_kind", "string"), + "namespace": ("namespace", "string"), "name": ("name", "string"), "plural": ("plural", "string"), + }, "omitted": {"kind"}, + }, + "identity_alias:neutral": { + "definition": "IdentityAlias", "labels": ["IdentityAlias"], "catalog_label": "IdentityAlias", "ownership": "owned", + "fields": {"id": ("id", "string"), "kind": ("iac_kind", "string"), "target": ("target", "string")}, "omitted": set(), + }, + "identity_alias:iac": { + "definition": "IdentityAlias", "labels": ["IdentityAlias", "IaCAlias"], "catalog_label": "IaCAlias", "ownership": "owned", + "fields": {"id": ("id", "string"), "kind": ("iac_kind", "string"), "target": ("target", "string")}, "omitted": set(), + }, + "package": { + "definition": "Package", "labels": ["Package"], "catalog_label": "Package", "ownership": "neutral", + "fields": {"id": ("id", "string"), "purl": ("purl", "string")}, "omitted": {"kind"}, + }, +} + + +class CheckIaCTest(unittest.TestCase): + def load(self, level: int) -> dict: + path = ROOT / f"v2/iac/json/analysis.l{level}.sample.json" + return json.loads(path.read_text()) + + def load_schema(self) -> dict: + path = ROOT / "v2/iac/json/analysis.schema.json" + return json.loads(path.read_text()) + + def load_catalog(self) -> dict: + path = ROOT / "v2/iac/neo4j/schema.neo4j.sample.json" + return json.loads(path.read_text()) + + def schema_errors(self, document: dict) -> list[str]: + validator = Draft202012Validator(self.load_schema()) + return [error.message for error in validator.iter_errors(document)] + + def definition_errors(self, definition: str, value: object) -> list[str]: + schema = self.load_schema() + validator = Draft202012Validator( + { + "$schema": schema["$schema"], + "$defs": schema["$defs"], + "$ref": f"#/$defs/{definition}", + } + ) + return [error.message for error in validator.iter_errors(value)] + + def run_checker_copy(self, mutate=None, selector="v2/iac") -> subprocess.CompletedProcess: + with tempfile.TemporaryDirectory() as directory: + checkout = Path(directory) + shutil.copytree(ROOT / "scripts", checkout / "scripts") + shutil.copytree(ROOT / "v1", checkout / "v1") + shutil.copytree(ROOT / "v2" / "iac", checkout / "v2" / "iac") + if mutate is not None: + mutate(checkout) + return subprocess.run( + [sys.executable, "scripts/check.py", selector], + cwd=checkout, + text=True, + capture_output=True, + check=False, + ) + + def test_fixtures_satisfy_semantic_contract(self): + for level in (1, 2, 3): + with self.subTest(level=level): + self.assertEqual([], self.schema_errors(self.load(level))) + self.assertEqual([], check_document(self.load(level))) + self.assertEqual([], check_catalog(self.load_catalog())) + + def test_fixtures_carry_containment_edges_from_level_one(self): + for level in (1, 2, 3): + with self.subTest(level=level): + edges = self.load(level)["application"]["edges"] + self.assertEqual( + ["charts/api/templates/deployment.yaml@1:1"], + list(edges.get("iac_has_resource_template", {})), + ) + self.assertEqual( + ["charts/api/templates/deployment.yaml@11:15"], + list(edges.get("iac_has_lookup_reference", {})), + ) + self.assertEqual( + sorted(edges.get("iac_alias_of", {})), + sorted(edges.get("iac_has_alias", {})), + ) + + def test_resolved_vendored_chart_uses_canonical_artifact_identity(self): + document = self.load(2) + reference = document["application"]["external_chart_references"]["bitnami/postgresql"] + self.assertEqual( + "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml", + reference.get("resolved_chart_id"), + ) + self.assertEqual([], self.schema_errors(document)) + + reference["resolved_chart_id"] = "can://iac/payments/helm/chart/charts/api/charts/postgresql" + self.assertTrue(self.schema_errors(document)) + + def test_canonical_id_definitions_reject_noncanonical_text(self): + cases = { + "ApplicationId": ( + "can://iac/payments", + ["can://iac/payments/extra", "can://iac/pay ments", "can://iac/payments\n"], + ), + "ArtifactId": ( + "can://artifact/pay%20ments/charts/api/Chart.yaml", + [ + "can://artifact/payments/charts//Chart.yaml", + "can://artifact/payments/charts/%ZZ.yaml", + "can://artifact/payments/charts/api?x/Chart.yaml", + "can://artifact/payments/charts/api/Chart.yaml\r", + ], + ), + "ConfigKeyId": ( + "can://artifact/payments/charts/api/values.yaml@key/image.tag", + [ + "can://artifact/payments/charts/api/values.yaml@key/", + "can://artifact/payments/charts/api/values.yaml@key/image tag", + "can://artifact/payments/charts/api/values.yaml@key/image/%GG", + ], + ), + "SemanticId": ( + "can://iac/payments/helm/templates/deployment.yaml/template-call@6:11", + [ + "can://iac/payments/helm//template-call", + "can://iac/payments/helm/bad name", + "can://iac/payments/helm/%XX", + "can://iac/payments/helm/template-call@6:11\n", + ], + ), + } + for definition, (valid, invalid_values) in cases.items(): + self.assertIn(definition, self.load_schema()["$defs"]) + with self.subTest(definition=definition, value=valid): + self.assertEqual([], self.definition_errors(definition, valid)) + for invalid in invalid_values: + with self.subTest(definition=definition, value=invalid): + self.assertTrue(self.definition_errors(definition, invalid)) + + def test_reference_fields_use_complete_canonical_id_definitions(self): + schema = self.load_schema()["$defs"] + direct_refs = { + ("Artifact", "id"): "ArtifactId", + ("ConfigKey", "id"): "ConfigKeyId", + ("IdentityAlias", "id"): "SemanticId", + ("IdentityAlias", "target"): "AliasTargetId", + ("Diagnostic", "id"): "SemanticId", + ("Diagnostic", "artifact_id"): "ArtifactId", + ("Package", "id"): "Purl", + ("Package", "purl"): "Purl", + ("Edge", "src"): "NodeId", + ("Edge", "dst"): "NodeId", + ("HelmDependency", "id"): "SemanticId", + ("HelmNamedTemplate", "id"): "SemanticId", + ("HelmTemplateCall", "id"): "SemanticId", + ("HelmTemplateCall", "target_id"): "SemanticId", + ("HelmValueReference", "id"): "SemanticId", + ("HelmValueReference", "target_id"): "ConfigKeyId", + ("HelmResourceTemplate", "id"): "SemanticId", + ("HelmLookupReference", "id"): "SemanticId", + ("HelmChartReference", "id"): "SemanticId", + ("HelmChartReference", "purl"): "Purl", + ("HelmChartReference", "resolved_chart_id"): "ArtifactId", + ("HelmRenderProfile", "id"): "SemanticId", + ("HelmRenderProfile", "chart_id"): "ArtifactId", + ("HelmValueLayer", "id"): "SemanticId", + ("HelmValueLayer", "source_id"): "ValueSourceId", + ("HelmRender", "id"): "SemanticId", + ("HelmRender", "profile_id"): "SemanticId", + ("KubernetesResource", "id"): "SemanticId", + ("KubernetesResource", "render_id"): "SemanticId", + ("KubernetesResource", "address_id"): "SemanticId", + ("KubernetesResourceAddress", "id"): "SemanticId", + } + for (definition, field), target in direct_refs.items(): + with self.subTest(definition=definition, field=field): + self.assertEqual( + {"$ref": f"#/$defs/{target}"}, + schema[definition]["properties"][field], + ) + + array_refs = { + ("HelmRender", "value_layer_ids"): "SemanticId", + ("KubernetesResource", "origin_ids"): "OriginId", + } + for (definition, field), target in array_refs.items(): + with self.subTest(definition=definition, field=field): + self.assertEqual( + {"$ref": f"#/$defs/{target}"}, + schema[definition]["properties"][field]["items"], + ) + + def test_purl_contract_supports_only_registered_oci_type(self): + self.assertIn("Purl", self.load_schema()["$defs"]) + self.assertEqual([], self.definition_errors("Purl", "pkg:oci/postgresql@sha256%3Aabcd")) + for invalid in ("pkg:helm/postgresql@12.1.0", "pkg:generic/postgresql@12.1.0", "pkg:npm/postgresql"): + with self.subTest(invalid=invalid): + self.assertTrue(self.definition_errors("Purl", invalid)) + + def test_repository_checker_rejects_semantically_invalid_iac_fixture(self): + def corrupt_digest(checkout: Path) -> None: + path = checkout / "v2/iac/json/analysis.l1.sample.json" + document = json.loads(path.read_text()) + document["application"]["artifacts"]["README.md"]["source"] += "changed" + path.write_text(json.dumps(document)) + + result = self.run_checker_copy(corrupt_digest) + self.assertEqual(1, result.returncode) + self.assertIn("sha256 does not match source", result.stdout) + + def test_v1_selector_does_not_run_iac_semantic_checks(self): + def corrupt_digest(checkout: Path) -> None: + path = checkout / "v2/iac/json/analysis.l1.sample.json" + document = json.loads(path.read_text()) + document["application"]["artifacts"]["README.md"]["source"] += "changed" + path.write_text(json.dumps(document)) + + result = self.run_checker_copy(corrupt_digest, "v1/json/java") + self.assertEqual(0, result.returncode, result.stdout + result.stderr) + self.assertNotIn("sha256 does not match source", result.stdout) + + def test_collects_nodes_recursively(self): + nodes = collect_nodes(self.load(3)["application"]) + self.assertIn("can://iac/payments", nodes) + self.assertIn( + "can://iac/payments/helm/render/charts/api/default/kubernetes/core/Secret/default/api-credentials", + nodes, + ) + + def test_rejects_duplicate_ids_across_named_collections(self): + doc = self.load(2) + duplicate = doc["application"]["external_chart_references"]["bitnami/postgresql"] + duplicate["id"] = doc["application"]["artifacts"]["README.md"]["id"] + self.assertIn("duplicate node id", "\n".join(check_document(doc))) + + def test_rejects_dangling_edge(self): + doc = self.load(2) + doc["application"]["edges"]["iac_references_value"]["bad"] = { + "src": "can://iac/payments/missing", + "dst": "can://artifact/payments/charts/api/values.yaml@key/image.tag", + } + self.assertIn("dangling edge source", "\n".join(check_document(doc))) + + def test_rejects_edge_family_endpoint_type_violation(self): + doc = self.load(2) + edge = doc["application"]["edges"]["iac_calls_template"][ + "charts/api/templates/deployment.yaml@6:11" + ] + edge["dst"] = "can://artifact/payments/README.md" + self.assertIn("edge endpoint type violation", "\n".join(check_document(doc))) + + def test_requires_scalar_reference_edge_counterparts(self): + doc = self.load(3) + chart = doc["application"]["artifacts"]["charts/api/Chart.yaml"]["iac"] + chart["renders"]["default"]["profile_id"] = chart["render_profiles"]["default"]["id"] + doc["application"]["edges"]["iac_configured_by"].pop("default") + self.assertIn( + "profile_id must have exactly one matching iac_configured_by edge", + "\n".join(check_document(doc)), + ) + + def test_requires_dependency_target_edge_counterpart(self): + doc = self.load(2) + chart = doc["application"]["artifacts"]["charts/api/Chart.yaml"] + dependency_id = "can://iac/payments/helm/charts/api/dependency/postgresql" + chart["iac"]["dependencies"]["postgresql"] = { + "id": dependency_id, + "kind": "helm_dependency", + "name": "postgresql", + "version_constraint": "12.1.0", + "span": {"start": [1, 1], "end": [1, 2], "bytes": [0, 1]}, + } + doc["application"]["edges"].setdefault("iac_declares_dependency", {})[ + "charts/api/Chart.yaml:postgresql" + ] = {"src": chart["id"], "dst": dependency_id} + + self.assertEqual([], self.schema_errors(doc)) + self.assertIn( + "dependency must have exactly one iac_targets_chart_reference edge", + "\n".join(check_document(doc)), + ) + + def test_rejects_diagnostic_artifact_reference_to_non_artifact(self): + doc = self.load(3) + diagnostic = doc["application"]["artifacts"]["charts/api/Chart.yaml"]["iac"]["renders"]["explicit"]["diagnostics"]["render-failed"] + diagnostic["artifact_id"] = "can://iac/payments/helm/render-profile/config/explicit" + self.assertIn("artifact_id must target an Artifact", "\n".join(check_document(doc))) + + def test_rejects_resource_outside_its_containing_render(self): + doc = self.load(3) + chart = doc["application"]["artifacts"]["charts/api/Chart.yaml"]["iac"] + resource = chart["renders"]["default"]["resources"]["apps/Deployment/default/api"] + resource["render_id"] = chart["renders"]["explicit"]["id"] + self.assertIn("resource render_id differs from containing render", "\n".join(check_document(doc))) + + def test_rejects_resource_produced_by_multiple_renders(self): + doc = self.load(3) + chart = doc["application"]["artifacts"]["charts/api/Chart.yaml"]["iac"] + resource = chart["renders"]["default"]["resources"]["apps/Deployment/default/api"] + doc["application"]["edges"]["iac_produces"]["explicit@deployment"] = { + "src": chart["renders"]["explicit"]["id"], + "dst": resource["id"], + } + self.assertIn( + "resource must have exactly one containing iac_produces edge", + "\n".join(check_document(doc)), + ) + + def test_requires_address_and_origin_targets_and_edges(self): + doc = self.load(3) + resource = doc["application"]["artifacts"]["charts/api/Chart.yaml"]["iac"]["renders"]["default"]["resources"]["apps/Deployment/default/api"] + resource["address_id"] = "can://iac/payments/kubernetes/address/core/Secret/default/api-credentials" + resource["origin_ids"] = ["can://artifact/payments/charts/api/values.yaml"] + errors = "\n".join(check_document(doc)) + self.assertIn("address_id must have exactly one matching iac_targets_resource edge", errors) + self.assertIn("origin_id must target a HelmResourceTemplate", errors) + + def test_returns_violations_in_sorted_order(self): + doc = self.load(2) + edges = doc["application"]["edges"]["iac_references_value"] + edges["z"] = {"src": "can://z", "dst": "can://artifact/payments/README.md"} + edges["a"] = {"src": "can://a", "dst": "can://artifact/payments/README.md"} + errors = check_document(doc) + self.assertEqual(sorted(errors), errors) + + def test_rejects_alias_chain(self): + doc = self.load(1) + alias = doc["application"]["artifacts"]["charts/api/Chart.yaml"]["aliases"][0] + alias["target"] = alias["id"] + self.assertIn("alias target is not canonical", "\n".join(check_document(doc))) + + def test_rejects_alias_targeting_another_alias(self): + doc = self.load(1) + aliases = doc["application"]["artifacts"]["charts/api/Chart.yaml"]["aliases"] + aliases.append( + { + "id": "can://iac/payments/helm/chart/second", + "kind": "helm_chart", + "target": aliases[0]["id"], + } + ) + self.assertIn("alias target is not canonical", "\n".join(check_document(doc))) + + def test_requires_exactly_one_matching_alias_edge(self): + doc = self.load(1) + doc["application"]["edges"]["iac_alias_of"].clear() + self.assertIn("alias must have exactly one matching iac_alias_of edge", "\n".join(check_document(doc))) + + def test_rejects_duplicate_matching_alias_edges(self): + doc = self.load(1) + edges = doc["application"]["edges"]["iac_alias_of"] + edges["duplicate"] = deepcopy(next(iter(edges.values()))) + self.assertIn("alias must have exactly one matching iac_alias_of edge", "\n".join(check_document(doc))) + + def test_requires_resource_template_containment_edge(self): + doc = self.load(1) + doc["application"]["edges"]["iac_has_resource_template"].clear() + self.assertIn( + "resource-template containment must have exactly one matching iac_has_resource_template edge", + "\n".join(check_document(doc)), + ) + + def test_rejects_dangling_resource_template_containment_edge(self): + doc = self.load(1) + doc["application"]["edges"]["iac_has_resource_template"]["bad"] = { + "src": "can://iac/payments/missing", + "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/resource-template@1:1", + } + self.assertIn("dangling edge source", "\n".join(check_document(doc))) + + def test_rejects_reversed_resource_template_containment_edge(self): + doc = self.load(1) + edge = doc["application"]["edges"]["iac_has_resource_template"][ + "charts/api/templates/deployment.yaml@1:1" + ] + edge["src"], edge["dst"] = edge["dst"], edge["src"] + self.assertIn("edge endpoint type violation", "\n".join(check_document(doc))) + + def test_requires_lookup_reference_containment_edge(self): + doc = self.load(1) + doc["application"]["edges"]["iac_has_lookup_reference"].clear() + self.assertIn( + "lookup-reference containment must have exactly one matching iac_has_lookup_reference edge", + "\n".join(check_document(doc)), + ) + + def test_rejects_dangling_lookup_reference_containment_edge(self): + doc = self.load(1) + doc["application"]["edges"]["iac_has_lookup_reference"]["bad"] = { + "src": "can://iac/payments/missing", + "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/lookup-reference@11:15", + } + self.assertIn("dangling edge source", "\n".join(check_document(doc))) + + def test_rejects_reversed_lookup_reference_containment_edge(self): + doc = self.load(1) + edge = doc["application"]["edges"]["iac_has_lookup_reference"][ + "charts/api/templates/deployment.yaml@11:15" + ] + edge["src"], edge["dst"] = edge["dst"], edge["src"] + self.assertIn("edge endpoint type violation", "\n".join(check_document(doc))) + + def test_requires_alias_containment_edge(self): + doc = self.load(1) + doc["application"]["edges"]["iac_has_alias"].clear() + self.assertIn( + "alias containment must have exactly one matching iac_has_alias edge", + "\n".join(check_document(doc)), + ) + + def test_rejects_dangling_alias_containment_edge(self): + doc = self.load(1) + doc["application"]["edges"]["iac_has_alias"]["bad"] = { + "src": "can://iac/payments/missing", + "dst": "can://iac/payments/helm/chart/charts/api", + } + self.assertIn("dangling edge source", "\n".join(check_document(doc))) + + def test_rejects_reversed_alias_containment_edge(self): + doc = self.load(1) + edge = doc["application"]["edges"]["iac_has_alias"]["charts/api/Chart.yaml@helm-chart"] + edge["src"], edge["dst"] = edge["dst"], edge["src"] + self.assertIn("edge endpoint type violation", "\n".join(check_document(doc))) + + def test_rejects_bad_source_digest(self): + doc = self.load(1) + doc["application"]["artifacts"]["README.md"]["source"] += "changed" + self.assertIn("sha256 does not match source", "\n".join(check_document(doc))) + + def test_empty_artifact_source_is_digest_ineligible(self): + doc = self.load(1) + artifact = doc["application"]["artifacts"]["README.md"] + artifact["source"] = "" + artifact["size_bytes"] = 0 + self.assertNotIn("sha256 does not match source", "\n".join(check_document(doc))) + + def test_rejects_empty_source_with_semantic_enrichment(self): + doc = self.load(1) + artifact = doc["application"]["artifacts"]["charts/api/Chart.yaml"] + artifact["source"] = "" + artifact["size_bytes"] = 0 + self.assertIn("empty-source artifact must remain raw", "\n".join(check_document(doc))) + + def test_rejects_size_bytes_different_from_utf8_source_length(self): + doc = self.load(1) + artifact = doc["application"]["artifacts"]["README.md"] + artifact["source"] = "café\n" + artifact["sha256"] = "7b49b9e063bd91a4f9252b413261f5557b9c570aa61516989499f64a62dbcdd6" + artifact["size_bytes"] = len(artifact["source"]) + self.assertIn("size_bytes does not match UTF-8 source length", "\n".join(check_document(doc))) + + def test_rejects_absolute_artifact_path(self): + doc = self.load(1) + doc["application"]["artifacts"]["README.md"]["path"] = "/README.md" + self.assertIn("artifact path is not relative", "\n".join(check_document(doc))) + + def test_rejects_artifact_path_dot_segment(self): + doc = self.load(1) + doc["application"]["artifacts"]["README.md"]["path"] = "./README.md" + self.assertIn("artifact path is not relative", "\n".join(check_document(doc))) + + def test_rejects_span_outside_artifact_source(self): + doc = self.load(1) + span = doc["application"]["artifacts"]["charts/api/values.yaml"]["config_keys"]["image.tag"]["span"] + span["bytes"] = [9, 10_000] + self.assertIn("span bytes out of bounds", "\n".join(check_document(doc))) + + def test_rejects_non_contiguous_profile_layer_ordinals(self): + doc = self.load(3) + profile = doc["application"]["artifacts"]["codeanalyzer-iac.yaml"]["codeanalyzer_iac_config"]["render_profiles"]["explicit"] + profile["value_layers"]["0001"]["ordinal"] = 3 + self.assertIn("profile layer ordinals must be contiguous from zero", "\n".join(check_document(doc))) + + def test_rejects_package_id_different_from_purl(self): + doc = self.load(3) + doc["application"]["packages"]["postgresql"] = { + "id": "can://package/postgresql", + "kind": "package", + "purl": "pkg:helm/postgresql@12.1.0", + } + self.assertIn("package id must equal purl", "\n".join(check_document(doc))) + + def test_rejects_unsupported_purl_type_even_when_id_equals_purl(self): + doc = self.load(2) + doc["application"]["packages"]["postgresql"] = { + "id": "pkg:helm/postgresql@12.1.0", + "kind": "package", + "purl": "pkg:helm/postgresql@12.1.0", + } + self.assertIn("unsupported package URL type", "\n".join(check_document(doc))) + + def test_rejects_resolution_facts_below_level_two(self): + doc = self.load(2) + doc["max_level"] = 1 + errors = "\n".join(check_document(doc)) + self.assertIn("fact requires max_level 2: helm_chart_reference", errors) + self.assertIn("fact requires max_level 2: target_id", errors) + self.assertIn("edge requires max_level 2: iac_calls_template", errors) + + def test_rejects_evaluation_facts_below_level_three(self): + doc = self.load(3) + doc["max_level"] = 2 + errors = "\n".join(check_document(doc)) + for fact in ( + "codeanalyzer_iac_config", + "helm_render_profile", + "helm_value_layer", + "helm_render", + "kubernetes_resource", + "kubernetes_resource_address", + ): + with self.subTest(fact=fact): + self.assertIn(f"fact requires max_level 3: {fact}", errors) + + def test_render_status_requires_truthful_phase_shape(self): + document = self.load(3) + chart = document["application"]["artifacts"]["charts/api/Chart.yaml"]["iac"] + failed = chart["renders"]["explicit"] + self.assertEqual("template", failed.get("phase")) + self.assertTrue(failed.get("diagnostics")) + self.assertEqual([], self.schema_errors(document)) + + failed.pop("phase", None) + self.assertTrue(self.schema_errors(document)) + + succeeded = chart["renders"]["default"] + succeeded["phase"] = "template" + self.assertTrue(self.schema_errors(document)) + + def test_profiles_keep_configuration_layers_out_of_default_render(self): + doc = self.load(3) + artifacts = doc["application"]["artifacts"] + chart = artifacts["charts/api/Chart.yaml"]["iac"] + config = artifacts["codeanalyzer-iac.yaml"] + default_profile = chart["render_profiles"]["default"] + explicit_profile = config["codeanalyzer_iac_config"]["render_profiles"]["explicit"] + + self.assertEqual( + ["can://artifact/payments/charts/api/values.yaml"], + [layer["source_id"] for layer in default_profile["value_layers"].values()], + ) + explicit_sources = [layer["source_id"] for layer in explicit_profile["value_layers"].values()] + self.assertEqual("can://artifact/payments/charts/api/values.yaml", explicit_sources[0]) + self.assertIn(explicit_sources[1], {key["id"] for key in config["config_keys"].values()}) + + def test_rejects_plaintext_secret_amplification(self): + doc = self.load(3) + resources = next( + iter(doc["application"]["artifacts"]["charts/api/Chart.yaml"]["iac"]["renders"].values()) + )["resources"] + secret = next(value for value in resources.values() if value["resource_kind"] == "Secret") + secret["secret_data"]["password"]["value"] = "hunter2" + self.assertIn("Secret data must contain only key and sha256", "\n".join(check_document(doc))) + + def test_monotonic_fixtures_only_add_information(self): + self.assertEqual([], assert_monotone(self.load(1), self.load(2))) + self.assertEqual([], assert_monotone(self.load(2), self.load(3))) + + def test_monotonicity_rejects_changed_lower_level_value(self): + lower = self.load(1) + higher = deepcopy(self.load(2)) + higher["application"]["artifacts"]["README.md"]["path"] = "renamed.md" + self.assertIn("value changed at application/artifacts/README.md/path", assert_monotone(lower, higher)) + + def test_monotonicity_allows_absent_target_id_to_be_added(self): + lower = {"max_level": 1, "reference": {}} + higher = {"max_level": 2, "reference": {"target_id": "can://iac/payments/target"}} + self.assertEqual([], assert_monotone(lower, higher)) + + def test_monotonicity_rejects_present_null_target_id_replacement(self): + lower = {"max_level": 1, "reference": {"target_id": None}} + higher = {"max_level": 2, "reference": {"target_id": "can://iac/payments/target"}} + self.assertEqual( + ["value changed at reference/target_id"], + assert_monotone(lower, higher), + ) + + def test_monotonicity_rejects_changed_target_id(self): + lower = {"max_level": 1, "reference": {"target_id": "can://iac/payments/one"}} + higher = {"max_level": 2, "reference": {"target_id": "can://iac/payments/two"}} + self.assertEqual( + ["value changed at reference/target_id"], + assert_monotone(lower, higher), + ) + + def test_catalog_relationships_are_identity_only(self): + catalog = self.load_catalog() + catalog["relationship_types"][0]["properties"]["ordinal"] = "integer" + self.assertIn("relationship properties must be empty", "\n".join(check_catalog(catalog))) + + def test_catalog_exactly_matches_node_projection_table(self): + catalog = self.load_catalog() + actual = {entry["label"]: entry for entry in catalog["node_labels"]} + self.assertEqual( + {entry["catalog_label"] for entry in EXPECTED_NODE_PROJECTIONS.values()}, + set(actual), + ) + for projection_name, projection in EXPECTED_NODE_PROJECTIONS.items(): + expected_properties = { + graph_name: graph_type + for graph_name, graph_type in projection["fields"].values() + } + expected_properties.update(OWNERSHIP_PROPERTIES[projection["ownership"]]) + entry = actual[projection["catalog_label"]] + with self.subTest(projection=projection_name): + self.assertEqual("id", entry["key"]) + self.assertEqual(expected_properties, entry["properties"]) + + def test_projection_table_covers_each_json_node_property(self): + definitions = self.load_schema()["$defs"] + for projection_name, projection in EXPECTED_NODE_PROJECTIONS.items(): + definition_name = projection["definition"] + if definition_name is None: + continue + json_fields = {name for name in projection["fields"] if not name.startswith("$")} + expected_fields = json_fields | projection["omitted"] + with self.subTest(projection=projection_name): + self.assertEqual( + expected_fields, + set(definitions[definition_name]["properties"]), + ) + + def test_catalog_rejects_duplicate_labels_and_types(self): + catalog = self.load_catalog() + catalog["node_labels"].append(deepcopy(catalog["node_labels"][0])) + catalog["relationship_types"].append(deepcopy(catalog["relationship_types"][0])) + errors = "\n".join(check_catalog(catalog)) + self.assertIn("duplicate node label", errors) + self.assertIn("duplicate relationship type", errors) + + def test_catalog_rejects_unknown_endpoint_labels(self): + catalog = self.load_catalog() + catalog["relationship_types"][0]["from"].append("MissingLabel") + self.assertIn("unknown relationship endpoint label", "\n".join(check_catalog(catalog))) + + def test_catalog_rejects_non_allowlisted_neutral_relationship(self): + catalog = self.load_catalog() + catalog["relationship_types"][2]["type"] = "PART_OF_CHART" + self.assertIn("relationship type must be IAC_* or neutral allowlisted", "\n".join(check_catalog(catalog))) + + def test_catalog_rejects_constraint_and_index_for_unknown_label(self): + catalog = self.load_catalog() + catalog["constraints"].append( + "CREATE CONSTRAINT missing_id IF NOT EXISTS FOR (n:MissingLabel) REQUIRE n.id IS UNIQUE" + ) + catalog["indexes"].append("CREATE INDEX missing_name IF NOT EXISTS FOR (n:AlsoMissing) ON (n.name)") + errors = "\n".join(check_catalog(catalog)) + self.assertIn("constraint mentions unknown label", errors) + self.assertIn("index mentions unknown label", errors) + + +if __name__ == "__main__": + unittest.main() diff --git a/v1/json/analysis.template.schema.json b/v1/json/analysis.template.schema.json new file mode 100644 index 0000000..fc44da9 --- /dev/null +++ b/v1/json/analysis.template.schema.json @@ -0,0 +1,223 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://codellm-devkit.github.io/schema/v1/LANG/analysis.schema.json", + "title": "codeanalyzer-LANG v1 analysis output", + "description": "Template for a v1 (pre-canonical) analysis.json. Copy into v1//, replace LANG, fill the $defs from the analyzer release named in x-cldk.source, and delete anything the language does not emit. v1 is descriptive, not prescriptive: each analyzer's v1 shape is whatever that release actually produced.", + + "x-cldk": { + "schemaVersion": 1, + "language": "LANG", + "source": { + "repo": "codellm-devkit/codeanalyzer-LANG", + "release": "vX.Y.Z", + "definedIn": "path/to/schema/source/in/that/release" + }, + "consumers": [ + { "repo": "codellm-devkit/python-sdk", "release": "v1.5.x" } + ] + }, + + "type": "object", + "required": ["symbol_table"], + "additionalProperties": true, + + "properties": { + "symbol_table": { + "description": "Per-file analysis, keyed by project-relative POSIX path (Java keys on compilation unit path, Python/TypeScript on module file path).", + "type": "object", + "additionalProperties": { "$ref": "#/$defs/CompilationUnit" } + }, + "call_graph": { + "description": "Flat edge list. Endpoints are callable signatures, using the same canonicalizer caller- and callee-side.", + "type": "array", + "items": { "$ref": "#/$defs/CallEdge" }, + "default": [] + }, + "external_symbols": { + "description": "Call-graph endpoints not declared in symbol_table (library / builtin members), keyed by signature. Omit if the analyzer does not emit it.", + "type": "object", + "additionalProperties": { "$ref": "#/$defs/ExternalSymbol" } + }, + "analyzer": { "$ref": "#/$defs/AnalyzerInfo" }, + "repository": { "$ref": "#/$defs/RepositoryInfo" }, + "version": { + "description": "Analyzer version string. Java emits this at the top level instead of an analyzer object.", + "type": "string" + } + }, + + "$defs": { + "CompilationUnit": { + "description": "One source file. Java: JavaCompilationUnit. Python: PyModule. TypeScript: TSModule. Fill in the language's own members (classes, functions/callables, imports, comments, variables, and any language-only members such as TS interfaces/enums/type aliases/namespaces or Java record components).", + "type": "object", + "properties": { + "file_path": { "type": "string" }, + "imports": { "type": "array", "items": { "$ref": "#/$defs/Import" } }, + "comments": { "type": "array", "items": { "$ref": "#/$defs/Comment" } }, + "classes": { "type": "object", "additionalProperties": { "$ref": "#/$defs/Class" } }, + "callables": { "type": "object", "additionalProperties": { "$ref": "#/$defs/Callable" } } + } + }, + + "Class": { + "description": "Type-like declaration (class, record, interface, enum). Key in the parent map is the signature.", + "type": "object", + "properties": { + "name": { "type": "string" }, + "signature": { "type": "string" }, + "code": { "type": ["string", "null"] }, + "comments": { "type": "array", "items": { "$ref": "#/$defs/Comment" } }, + "callables": { "type": "object", "additionalProperties": { "$ref": "#/$defs/Callable" } }, + "attributes": { "type": "object", "additionalProperties": { "$ref": "#/$defs/Field" } }, + "inner_classes": { "type": "object", "additionalProperties": { "$ref": "#/$defs/Class" } } + }, + "allOf": [{ "$ref": "#/$defs/SourceRange" }] + }, + + "Callable": { + "description": "Function, method, constructor, lambda, or other callable. `signature` is the call-graph identity and MUST match the endpoints used in call_graph.", + "type": "object", + "required": ["signature"], + "properties": { + "name": { "type": "string" }, + "signature": { "type": "string" }, + "code": { "type": ["string", "null"] }, + "return_type": { "type": ["string", "null"] }, + "parameters": { "type": "array", "items": { "$ref": "#/$defs/Parameter" } }, + "comments": { "type": "array", "items": { "$ref": "#/$defs/Comment" } }, + "call_sites": { "type": "array", "items": { "$ref": "#/$defs/CallSite" } }, + "accessed_symbols": { "type": "array", "items": { "$ref": "#/$defs/Symbol" } }, + "local_variables": { "type": "array", "items": { "$ref": "#/$defs/VariableDeclaration" } }, + "cyclomatic_complexity": { "type": "integer" } + }, + "allOf": [{ "$ref": "#/$defs/SourceRange" }] + }, + + "CallSite": { + "description": "A call expression inside a callable body. `callee_signature` links to a Callable or an ExternalSymbol.", + "type": "object", + "properties": { + "method_name": { "type": "string" }, + "receiver_expr": { "type": ["string", "null"] }, + "receiver_type": { "type": ["string", "null"] }, + "argument_types": { "type": "array", "items": { "type": "string" } }, + "return_type": { "type": ["string", "null"] }, + "callee_signature": { "type": ["string", "null"] }, + "is_constructor_call": { "type": "boolean" } + }, + "allOf": [{ "$ref": "#/$defs/SourceRange" }] + }, + + "CallEdge": { + "type": "object", + "required": ["source", "target"], + "properties": { + "source": { "description": "Caller callable signature.", "type": "string" }, + "target": { "description": "Callee callable signature.", "type": "string" }, + "type": { "type": "string", "default": "CALL_DEP" }, + "weight": { "type": "integer", "default": 1 } + } + }, + + "ExternalSymbol": { + "type": "object", + "properties": { + "name": { "description": "Member/short name, e.g. `get` for `requests.get`.", "type": "string" }, + "module": { "type": ["string", "null"] } + } + }, + + "Parameter": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "type": { "type": ["string", "null"] }, + "default_value": { "type": ["string", "null"] } + }, + "allOf": [{ "$ref": "#/$defs/SourceRange" }] + }, + + "Field": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "type": { "type": ["string", "null"] }, + "initializer": { "type": ["string", "null"] }, + "comments": { "type": "array", "items": { "$ref": "#/$defs/Comment" } } + }, + "allOf": [{ "$ref": "#/$defs/SourceRange" }] + }, + + "VariableDeclaration": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "type": { "type": ["string", "null"] }, + "initializer": { "type": ["string", "null"] }, + "scope": { "type": "string" } + }, + "allOf": [{ "$ref": "#/$defs/SourceRange" }] + }, + + "Symbol": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "qualified_name": { "type": ["string", "null"] }, + "kind": { "type": "string" }, + "scope": { "type": "string" }, + "type": { "type": ["string", "null"] } + } + }, + + "Import": { + "type": "object", + "properties": { + "module": { "type": "string" }, + "name": { "type": ["string", "null"] }, + "alias": { "type": ["string", "null"] }, + "resolved_module": { "type": ["string", "null"] } + }, + "allOf": [{ "$ref": "#/$defs/SourceRange" }] + }, + + "Comment": { + "type": "object", + "properties": { + "content": { "type": "string" }, + "is_docstring": { "type": "boolean" } + }, + "allOf": [{ "$ref": "#/$defs/SourceRange" }] + }, + + "AnalyzerInfo": { + "description": "Which analyzer produced this snapshot, and how it was configured.", + "type": "object", + "properties": { + "name": { "type": "string" }, + "version": { "type": "string" }, + "config": { "type": "object" } + } + }, + + "RepositoryInfo": { + "type": "object", + "properties": { + "uri": { "type": ["string", "null"] }, + "revision": { "type": "string" }, + "dirty": { "type": "boolean" } + } + }, + + "SourceRange": { + "description": "Positions are 1-based lines, 0-based columns, -1 when unknown. Some analyzers only emit the line pair.", + "type": "object", + "properties": { + "start_line": { "type": "integer", "default": -1 }, + "end_line": { "type": "integer", "default": -1 }, + "start_column": { "type": "integer", "default": -1 }, + "end_column": { "type": "integer", "default": -1 } + } + } + } +} diff --git a/v1/json/java/analysis.schema.json b/v1/json/java/analysis.schema.json new file mode 100644 index 0000000..4b18a9c --- /dev/null +++ b/v1/json/java/analysis.schema.json @@ -0,0 +1,2078 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://codellm-devkit.github.io/schema/v1/java/analysis.schema.json", + "title": "codeanalyzer-java v1 analysis output", + "description": "Generated from the Lombok @Data entity classes of codeanalyzer-java v2.4.1. The repo is on its 2.x version line; the schema it emits is still v1 \u2014 the v2 schema migration has not begun. Property names are the Gson LOWER_CASE_WITH_UNDERSCORES rendering of the Java field names, which CodeAnalyzer configures; each property keeps its Java field under x-java.", + "x-cldk": { + "schemaVersion": 1, + "language": "java", + "source": { + "repo": "codellm-devkit/codeanalyzer-java", + "release": "v2.4.1", + "definedIn": "src/main/java/com/ibm/cldk/entities/ (+ SystemDependencyGraph.java)" + }, + "consumers": [ + { + "repo": "codellm-devkit/python-sdk", + "release": "v1.5.x" + } + ] + }, + "type": "object", + "required": [ + "symbol_table" + ], + "additionalProperties": false, + "properties": { + "symbol_table": { + "description": "Per-compilation-unit analysis, keyed by absolute source file path.", + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/JavaCompilationUnit" + } + }, + "call_graph": { + "description": "Call graph / system dependency graph edges. Present only at analysis level >= 2.", + "type": "array", + "items": { + "$ref": "#/$defs/Dependency" + } + }, + "version": { + "description": "Analyzer version string, or \"unknown\" / \"error retrieving version\" when it could not be resolved.", + "type": "string" + } + }, + "$defs": { + "AbstractGraphEdge": { + "type": "object", + "properties": { + "context": { + "type": [ + "string", + "null" + ], + "description": "The Context. -- GETTER -- Gets context. @return the context", + "x-java": { + "field": "context", + "type": "String" + } + }, + "weight": { + "type": [ + "integer", + "null" + ], + "description": "The Weight. -- GETTER -- Gets weight. @return the weight", + "x-java": { + "field": "weight", + "type": "Integer" + } + } + }, + "additionalProperties": false + }, + "CRUDOperation": { + "type": "object", + "properties": { + "line_number": { + "type": "integer", + "x-java": { + "field": "lineNumber", + "type": "int" + } + }, + "operation_type": { + "oneOf": [ + { + "$ref": "#/$defs/CRUDOperationType" + }, + { + "type": "null" + } + ], + "x-java": { + "field": "operationType", + "type": "CRUDOperationType" + } + }, + "target_table": { + "type": [ + "string", + "null" + ], + "x-java": { + "field": "targetTable", + "type": "String" + } + }, + "involved_columns": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "x-java": { + "field": "involvedColumns", + "type": "List" + } + }, + "condition": { + "type": [ + "string", + "null" + ], + "x-java": { + "field": "condition", + "type": "String" + } + }, + "joined_tables": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "x-java": { + "field": "joinedTables", + "type": "List" + } + } + }, + "additionalProperties": false + }, + "CRUDOperationType": { + "type": "string", + "enum": [ + "CREATE", + "READ", + "UPDATE", + "DELETE" + ] + }, + "CRUDQuery": { + "type": "object", + "properties": { + "line_number": { + "type": "integer", + "x-java": { + "field": "lineNumber", + "type": "int" + } + }, + "query_arguments": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "x-java": { + "field": "queryArguments", + "type": "List" + } + }, + "query_type": { + "oneOf": [ + { + "$ref": "#/$defs/CRUDQueryType" + }, + { + "type": "null" + } + ], + "x-java": { + "field": "queryType", + "type": "CRUDQueryType" + } + } + }, + "additionalProperties": false + }, + "CRUDQueryType": { + "type": "string", + "enum": [ + "READ", + "WRITE", + "NAMED" + ] + }, + "CallEdge": { + "type": "object", + "properties": { + "context": { + "type": [ + "string", + "null" + ], + "description": "The Context. -- GETTER -- Gets context. @return the context", + "x-java": { + "field": "context", + "type": "String" + } + }, + "weight": { + "type": [ + "integer", + "null" + ], + "description": "The Weight. -- GETTER -- Gets weight. @return the weight", + "x-java": { + "field": "weight", + "type": "Integer" + } + }, + "type": { + "type": [ + "string", + "null" + ], + "description": "The Type.", + "x-java": { + "field": "type", + "type": "String" + } + } + }, + "additionalProperties": false, + "x-java-extends": "AbstractGraphEdge" + }, + "CallSite": { + "type": "object", + "properties": { + "method_name": { + "type": [ + "string", + "null" + ], + "description": "Name of the method being called", + "x-java": { + "field": "methodName", + "type": "String" + } + }, + "comment": { + "oneOf": [ + { + "$ref": "#/$defs/Comment" + }, + { + "type": "null" + } + ], + "description": "Comment associated with the call site", + "x-java": { + "field": "comment", + "type": "Comment" + } + }, + "receiver_expr": { + "type": [ + "string", + "null" + ], + "description": "Expression representing the receiver of the method call", + "x-java": { + "field": "receiverExpr", + "type": "String" + } + }, + "receiver_type": { + "type": [ + "string", + "null" + ], + "description": "Type of the receiver object", + "x-java": { + "field": "receiverType", + "type": "String" + } + }, + "argument_types": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "description": "List of argument types for the method call", + "x-java": { + "field": "argumentTypes", + "type": "List" + } + }, + "argument_expr": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "description": "List of argument expressions for the method call", + "x-java": { + "field": "argumentExpr", + "type": "List" + } + }, + "return_type": { + "type": [ + "string", + "null" + ], + "description": "Return type of the called method", + "x-java": { + "field": "returnType", + "type": "String" + } + }, + "callee_signature": { + "type": [ + "string", + "null" + ], + "description": "Full signature of the callee method", + "x-java": { + "field": "calleeSignature", + "type": "String" + } + }, + "is_public": { + "type": "boolean", + "description": "Flag indicating if the method has public access", + "x-java": { + "field": "isPublic", + "type": "boolean" + } + }, + "is_protected": { + "type": "boolean", + "description": "Flag indicating if the method has protected access", + "x-java": { + "field": "isProtected", + "type": "boolean" + } + }, + "is_private": { + "type": "boolean", + "description": "Flag indicating if the method has private access", + "x-java": { + "field": "isPrivate", + "type": "boolean" + } + }, + "is_unspecified": { + "type": "boolean", + "description": "Flag indicating if the method has unspecified access", + "x-java": { + "field": "isUnspecified", + "type": "boolean" + } + }, + "is_static_call": { + "type": "boolean", + "description": "Flag indicating if this is a static method call", + "x-java": { + "field": "isStaticCall", + "type": "boolean" + } + }, + "is_constructor_call": { + "type": "boolean", + "description": "Flag indicating if this is a constructor call", + "x-java": { + "field": "isConstructorCall", + "type": "boolean" + } + }, + "crud_operation": { + "oneOf": [ + { + "$ref": "#/$defs/CRUDOperation" + }, + { + "type": "null" + } + ], + "description": "CRUD operation associated with this call site, if any", + "x-java": { + "field": "crudOperation", + "type": "CRUDOperation" + } + }, + "crud_query": { + "oneOf": [ + { + "$ref": "#/$defs/CRUDQuery" + }, + { + "type": "null" + } + ], + "description": "CRUD query associated with this call site, if any", + "x-java": { + "field": "crudQuery", + "type": "CRUDQuery" + } + }, + "start_line": { + "type": "integer", + "description": "Starting line number of the call site in the source file", + "x-java": { + "field": "startLine", + "type": "int" + } + }, + "start_column": { + "type": "integer", + "description": "Starting column number of the call site in the source file", + "x-java": { + "field": "startColumn", + "type": "int" + } + }, + "end_line": { + "type": "integer", + "description": "Ending line number of the call site in the source file", + "x-java": { + "field": "endLine", + "type": "int" + } + }, + "end_column": { + "type": "integer", + "description": "Ending column number of the call site in the source file", + "x-java": { + "field": "endColumn", + "type": "int" + } + } + }, + "additionalProperties": false + }, + "Callable": { + "type": "object", + "properties": { + "file_path": { + "type": [ + "string", + "null" + ], + "description": "The file path where the callable entity is defined.", + "x-java": { + "field": "filePath", + "type": "String" + } + }, + "signature": { + "type": [ + "string", + "null" + ], + "description": "The signature of the callable entity.", + "x-java": { + "field": "signature", + "type": "String" + } + }, + "comments": { + "type": [ + "array", + "null" + ], + "items": { + "oneOf": [ + { + "$ref": "#/$defs/Comment" + }, + { + "type": "null" + } + ] + }, + "description": "A list of comments associated with the callable entity.", + "x-java": { + "field": "comments", + "type": "List" + } + }, + "annotations": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "description": "A list of annotations applied to the callable entity.", + "x-java": { + "field": "annotations", + "type": "List" + } + }, + "modifiers": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "description": "A list of modifiers applied to the callable entity (e.g., public, private).", + "x-java": { + "field": "modifiers", + "type": "List" + } + }, + "thrown_exceptions": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "description": "A list of exceptions thrown by the callable entity.", + "x-java": { + "field": "thrownExceptions", + "type": "List" + } + }, + "declaration": { + "type": [ + "string", + "null" + ], + "description": "The declaration of the callable entity.", + "x-java": { + "field": "declaration", + "type": "String" + } + }, + "parameters": { + "type": [ + "array", + "null" + ], + "items": { + "oneOf": [ + { + "$ref": "#/$defs/ParameterInCallable" + }, + { + "type": "null" + } + ] + }, + "description": "A list of parameters for the callable entity.", + "x-java": { + "field": "parameters", + "type": "List" + } + }, + "code": { + "type": [ + "string", + "null" + ], + "description": "The code of the callable entity.", + "x-java": { + "field": "code", + "type": "String" + } + }, + "start_line": { + "type": "integer", + "description": "The starting line number of the callable entity in the source file.", + "x-java": { + "field": "startLine", + "type": "int" + } + }, + "end_line": { + "type": "integer", + "description": "The ending line number of the callable entity in the source file.", + "x-java": { + "field": "endLine", + "type": "int" + } + }, + "code_start_line": { + "type": "integer", + "description": "The starting line number of the callable code in the source file.", + "x-java": { + "field": "codeStartLine", + "type": "int" + } + }, + "return_type": { + "type": [ + "string", + "null" + ], + "description": "The return type of the callable entity.", + "x-java": { + "field": "returnType", + "type": "String" + } + }, + "is_implicit": { + "type": "boolean", + "description": "Indicates whether the callable entity is implicit.", + "x-java": { + "field": "isImplicit", + "type": "boolean" + } + }, + "is_constructor": { + "type": "boolean", + "description": "Indicates whether the callable entity is a constructor.", + "x-java": { + "field": "isConstructor", + "type": "boolean" + } + }, + "referenced_types": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "description": "A list of types referenced by the callable entity.", + "x-java": { + "field": "referencedTypes", + "type": "List" + } + }, + "accessed_fields": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "description": "A list of fields accessed by the callable entity.", + "x-java": { + "field": "accessedFields", + "type": "List" + } + }, + "call_sites": { + "type": [ + "array", + "null" + ], + "items": { + "oneOf": [ + { + "$ref": "#/$defs/CallSite" + }, + { + "type": "null" + } + ] + }, + "description": "A list of call sites within the callable entity.", + "x-java": { + "field": "callSites", + "type": "List" + } + }, + "variable_declarations": { + "type": [ + "array", + "null" + ], + "items": { + "oneOf": [ + { + "$ref": "#/$defs/VariableDeclaration" + }, + { + "type": "null" + } + ] + }, + "description": "A list of variable declarations within the callable entity.", + "x-java": { + "field": "variableDeclarations", + "type": "List" + } + }, + "crud_operations": { + "type": [ + "array", + "null" + ], + "items": { + "oneOf": [ + { + "$ref": "#/$defs/CRUDOperation" + }, + { + "type": "null" + } + ] + }, + "description": "A list of CRUD operations associated with the callable entity.", + "x-java": { + "field": "crudOperations", + "type": "List" + } + }, + "crud_queries": { + "type": [ + "array", + "null" + ], + "items": { + "oneOf": [ + { + "$ref": "#/$defs/CRUDQuery" + }, + { + "type": "null" + } + ] + }, + "description": "A list of CRUD queries associated with the callable entity.", + "x-java": { + "field": "crudQueries", + "type": "List" + } + }, + "cyclomatic_complexity": { + "type": "integer", + "description": "The cyclomatic complexity of the callable entity.", + "x-java": { + "field": "cyclomaticComplexity", + "type": "int" + } + }, + "is_entrypoint": { + "type": "boolean", + "description": "Indicates whether the callable entity is an entry point.", + "x-java": { + "field": "isEntrypoint", + "type": "boolean" + } + } + }, + "additionalProperties": false + }, + "CallableVertex": { + "type": "object", + "properties": { + "file_path": { + "type": [ + "string", + "null" + ], + "x-java": { + "field": "filePath", + "type": "String" + } + }, + "type_declaration": { + "type": [ + "string", + "null" + ], + "x-java": { + "field": "typeDeclaration", + "type": "String" + } + }, + "signature": { + "type": [ + "string", + "null" + ], + "x-java": { + "field": "signature", + "type": "String" + } + }, + "callable_declaration": { + "type": [ + "string", + "null" + ], + "x-java": { + "field": "callableDeclaration", + "type": "String" + } + } + }, + "additionalProperties": false + }, + "Comment": { + "type": "object", + "properties": { + "content": { + "type": [ + "string", + "null" + ], + "description": "The textual content of the comment.", + "x-java": { + "field": "content", + "type": "String" + } + }, + "start_line": { + "type": "integer", + "description": "The starting line number of the comment in the source file.

Defaults to {@code -1} if the position is unknown.

", + "x-java": { + "field": "startLine", + "type": "int" + } + }, + "end_line": { + "type": "integer", + "description": "The ending line number of the comment in the source file.

Defaults to {@code -1} if the position is unknown.

", + "x-java": { + "field": "endLine", + "type": "int" + } + }, + "start_column": { + "type": "integer", + "description": "The starting column number of the comment in the source file.

Defaults to {@code -1} if the position is unknown.

", + "x-java": { + "field": "startColumn", + "type": "int" + } + }, + "end_column": { + "type": "integer", + "description": "The ending column number of the comment in the source file.

Defaults to {@code -1} if the position is unknown.

", + "x-java": { + "field": "endColumn", + "type": "int" + } + }, + "is_javadoc": { + "type": "boolean", + "description": "Indicates whether the comment is a Javadoc comment.

Javadoc comments are special block comments used for generating documentation and typically start with {@code }.

", + "x-java": { + "field": "isJavadoc", + "type": "boolean" + } + } + }, + "additionalProperties": false + }, + "Dependency": { + "description": "One call-graph / SDG edge. Emitted only at analysis level >= 2. SDG edges additionally carry source_kind and destination_kind.", + "type": "object", + "properties": { + "source": { + "$ref": "#/$defs/CallableVertex" + }, + "target": { + "$ref": "#/$defs/CallableVertex" + }, + "type": { + "type": "string" + }, + "weight": { + "type": "string", + "description": "Stringified edge weight." + }, + "source_kind": { + "type": [ + "string", + "null" + ] + }, + "destination_kind": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "source", + "target", + "type", + "weight" + ], + "additionalProperties": false, + "x-java": { + "declaredIn": "src/main/java/com/ibm/cldk/SystemDependencyGraph.java", + "classes": [ + "Dependency", + "SDGDependency", + "CallDependency" + ] + } + }, + "EnumConstant": { + "type": "object", + "properties": { + "name": { + "type": [ + "string", + "null" + ], + "x-java": { + "field": "name", + "type": "String" + } + }, + "arguments": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "x-java": { + "field": "arguments", + "type": "List" + } + } + }, + "additionalProperties": false + }, + "Field": { + "type": "object", + "properties": { + "comment": { + "oneOf": [ + { + "$ref": "#/$defs/Comment" + }, + { + "type": "null" + } + ], + "x-java": { + "field": "comment", + "type": "Comment" + } + }, + "name": { + "type": [ + "string", + "null" + ], + "x-java": { + "field": "name", + "type": "String" + } + }, + "type": { + "type": [ + "string", + "null" + ], + "x-java": { + "field": "type", + "type": "String" + } + }, + "start_line": { + "type": [ + "integer", + "null" + ], + "x-java": { + "field": "startLine", + "type": "Integer" + } + }, + "end_line": { + "type": [ + "integer", + "null" + ], + "x-java": { + "field": "endLine", + "type": "Integer" + } + }, + "variables": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "x-java": { + "field": "variables", + "type": "List" + } + }, + "modifiers": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "x-java": { + "field": "modifiers", + "type": "List" + } + }, + "annotations": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "x-java": { + "field": "annotations", + "type": "List" + } + }, + "variable_initializers": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": [ + "string", + "null" + ] + }, + "x-java": { + "field": "variableInitializers", + "type": "Map" + } + } + }, + "additionalProperties": false + }, + "Import": { + "type": "object", + "properties": { + "path": { + "type": [ + "string", + "null" + ], + "x-java": { + "field": "path", + "type": "String" + } + }, + "is_static": { + "type": "boolean", + "x-java": { + "field": "isStatic", + "type": "boolean" + } + }, + "is_wildcard": { + "type": "boolean", + "x-java": { + "field": "isWildcard", + "type": "boolean" + } + } + }, + "additionalProperties": false + }, + "InitializationBlock": { + "type": "object", + "properties": { + "file_path": { + "type": [ + "string", + "null" + ], + "x-java": { + "field": "filePath", + "type": "String" + } + }, + "comments": { + "type": [ + "array", + "null" + ], + "items": { + "oneOf": [ + { + "$ref": "#/$defs/Comment" + }, + { + "type": "null" + } + ] + }, + "x-java": { + "field": "comments", + "type": "List" + } + }, + "annotations": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "x-java": { + "field": "annotations", + "type": "List" + } + }, + "thrown_exceptions": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "x-java": { + "field": "thrownExceptions", + "type": "List" + } + }, + "code": { + "type": [ + "string", + "null" + ], + "x-java": { + "field": "code", + "type": "String" + } + }, + "start_line": { + "type": "integer", + "x-java": { + "field": "startLine", + "type": "int" + } + }, + "end_line": { + "type": "integer", + "x-java": { + "field": "endLine", + "type": "int" + } + }, + "is_static": { + "type": "boolean", + "x-java": { + "field": "isStatic", + "type": "boolean" + } + }, + "referenced_types": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "x-java": { + "field": "referencedTypes", + "type": "List" + } + }, + "accessed_fields": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "x-java": { + "field": "accessedFields", + "type": "List" + } + }, + "call_sites": { + "type": [ + "array", + "null" + ], + "items": { + "oneOf": [ + { + "$ref": "#/$defs/CallSite" + }, + { + "type": "null" + } + ] + }, + "x-java": { + "field": "callSites", + "type": "List" + } + }, + "variable_declarations": { + "type": [ + "array", + "null" + ], + "items": { + "oneOf": [ + { + "$ref": "#/$defs/VariableDeclaration" + }, + { + "type": "null" + } + ] + }, + "x-java": { + "field": "variableDeclarations", + "type": "List" + } + }, + "cyclomatic_complexity": { + "type": "integer", + "x-java": { + "field": "cyclomaticComplexity", + "type": "int" + } + } + }, + "additionalProperties": false + }, + "JPAQueryMethod": { + "type": "string", + "enum": [ + "GET_SINGLE_RESULT", + "GET_FIRST_RESULT", + "GET_MAX_RESULTS", + "GET_HINTS", + "GET_LOCK_MODE", + "GET_PARAMETER", + "GET_PARAMETERS", + "GET_PARAMETER_VALUE", + "IS_BOUND", + "UNWRAP" + ] + }, + "JavaCompilationUnit": { + "type": "object", + "properties": { + "file_path": { + "type": [ + "string", + "null" + ], + "x-java": { + "field": "filePath", + "type": "String" + } + }, + "package_name": { + "type": [ + "string", + "null" + ], + "x-java": { + "field": "packageName", + "type": "String" + } + }, + "comments": { + "type": [ + "array", + "null" + ], + "items": { + "oneOf": [ + { + "$ref": "#/$defs/Comment" + }, + { + "type": "null" + } + ] + }, + "x-java": { + "field": "comments", + "type": "List" + } + }, + "imports": { + "type": [ + "array", + "null" + ], + "items": { + "oneOf": [ + { + "$ref": "#/$defs/Import" + }, + { + "type": "null" + } + ] + }, + "x-java": { + "field": "imports", + "type": "List" + } + }, + "type_declarations": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "oneOf": [ + { + "$ref": "#/$defs/Type" + }, + { + "type": "null" + } + ] + }, + "x-java": { + "field": "typeDeclarations", + "type": "Map" + } + }, + "is_modified": { + "type": "boolean", + "x-java": { + "field": "isModified", + "type": "boolean" + } + } + }, + "additionalProperties": false + }, + "ParameterInCallable": { + "type": "object", + "properties": { + "type": { + "type": [ + "string", + "null" + ], + "description": "The type of the parameter (e.g., int, String).", + "x-java": { + "field": "type", + "type": "String" + } + }, + "name": { + "type": [ + "string", + "null" + ], + "description": "The name of the parameter.", + "x-java": { + "field": "name", + "type": "String" + } + }, + "annotations": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "description": "A list of annotations applied to the parameter.", + "x-java": { + "field": "annotations", + "type": "List" + } + }, + "modifiers": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "description": "A list of modifiers applied to the parameter (e.g., final, static).", + "x-java": { + "field": "modifiers", + "type": "List" + } + }, + "start_line": { + "type": "integer", + "description": "The starting line number of the parameter in the source file.", + "x-java": { + "field": "startLine", + "type": "int" + } + }, + "end_line": { + "type": "integer", + "description": "The ending line number of the parameter in the source file.", + "x-java": { + "field": "endLine", + "type": "int" + } + }, + "start_column": { + "type": "integer", + "description": "The starting column number of the parameter in the source file.", + "x-java": { + "field": "startColumn", + "type": "int" + } + }, + "end_column": { + "type": "integer", + "description": "The ending column number of the parameter in the source file.", + "x-java": { + "field": "endColumn", + "type": "int" + } + } + }, + "additionalProperties": false + }, + "RecordComponent": { + "type": "object", + "properties": { + "comment": { + "oneOf": [ + { + "$ref": "#/$defs/Comment" + }, + { + "type": "null" + } + ], + "description": "The comment associated with the record component.", + "x-java": { + "field": "comment", + "type": "Comment" + } + }, + "name": { + "type": [ + "string", + "null" + ], + "description": "The name of the record component.", + "x-java": { + "field": "name", + "type": "String" + } + }, + "type": { + "type": [ + "string", + "null" + ], + "description": "The type of the record component.", + "x-java": { + "field": "type", + "type": "String" + } + }, + "modifiers": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "description": "A list of modifiers applied to the record component (e.g., final, static).", + "x-java": { + "field": "modifiers", + "type": "List" + } + }, + "annotations": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "description": "A list of annotations applied to the record component.", + "x-java": { + "field": "annotations", + "type": "List" + } + }, + "default_value": { + "description": "The default value of the record component, stored as a string representation.", + "x-java": { + "field": "defaultValue", + "type": "Object" + } + }, + "is_var_args": { + "type": "boolean", + "description": "Indicates whether the record component is a varargs parameter.", + "x-java": { + "field": "isVarArgs", + "type": "boolean" + } + } + }, + "additionalProperties": false + }, + "SystemDepEdge": { + "type": "object", + "properties": { + "context": { + "type": [ + "string", + "null" + ], + "description": "The Context. -- GETTER -- Gets context. @return the context", + "x-java": { + "field": "context", + "type": "String" + } + }, + "weight": { + "type": [ + "integer", + "null" + ], + "description": "The Weight. -- GETTER -- Gets weight. @return the weight", + "x-java": { + "field": "weight", + "type": "Integer" + } + }, + "source_pos": { + "type": [ + "integer", + "null" + ], + "description": "The Source pos.", + "x-java": { + "field": "sourcePos", + "type": "Integer" + } + }, + "destination_pos": { + "type": [ + "integer", + "null" + ], + "description": "The Destination pos.", + "x-java": { + "field": "destinationPos", + "type": "Integer" + } + }, + "type": { + "type": [ + "string", + "null" + ], + "description": "The Type.", + "x-java": { + "field": "type", + "type": "String" + } + }, + "source_kind": { + "type": [ + "string", + "null" + ], + "x-java": { + "field": "sourceKind", + "type": "String" + } + }, + "destination_kind": { + "type": [ + "string", + "null" + ], + "x-java": { + "field": "destinationKind", + "type": "String" + } + } + }, + "additionalProperties": false, + "x-java-extends": "AbstractGraphEdge" + }, + "Type": { + "type": "object", + "properties": { + "is_nested_type": { + "type": "boolean", + "description": "Indicates if this type is nested.", + "x-java": { + "field": "isNestedType", + "type": "boolean" + } + }, + "is_class_or_interface_declaration": { + "type": "boolean", + "description": "Indicates if this type is a class or interface declaration.", + "x-java": { + "field": "isClassOrInterfaceDeclaration", + "type": "boolean" + } + }, + "is_enum_declaration": { + "type": "boolean", + "description": "Indicates if this type is an enum declaration.", + "x-java": { + "field": "isEnumDeclaration", + "type": "boolean" + } + }, + "is_annotation_declaration": { + "type": "boolean", + "description": "Indicates if this type is an annotation declaration.", + "x-java": { + "field": "isAnnotationDeclaration", + "type": "boolean" + } + }, + "is_record_declaration": { + "type": "boolean", + "description": "Indicates if this type is a record declaration.", + "x-java": { + "field": "isRecordDeclaration", + "type": "boolean" + } + }, + "is_interface": { + "type": "boolean", + "description": "Indicates if this type is an interface.", + "x-java": { + "field": "isInterface", + "type": "boolean" + } + }, + "is_inner_class": { + "type": "boolean", + "description": "Indicates if this type is an inner class.", + "x-java": { + "field": "isInnerClass", + "type": "boolean" + } + }, + "is_local_class": { + "type": "boolean", + "description": "Indicates if this type is a local class.", + "x-java": { + "field": "isLocalClass", + "type": "boolean" + } + }, + "extends_list": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "description": "List of types that this type extends.", + "x-java": { + "field": "extendsList", + "type": "List" + } + }, + "comments": { + "type": [ + "array", + "null" + ], + "items": { + "oneOf": [ + { + "$ref": "#/$defs/Comment" + }, + { + "type": "null" + } + ] + }, + "description": "List of comments associated with this type.", + "x-java": { + "field": "comments", + "type": "List" + } + }, + "implements_list": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "description": "List of interfaces that this type implements.", + "x-java": { + "field": "implementsList", + "type": "List" + } + }, + "modifiers": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "description": "List of modifiers for this type.", + "x-java": { + "field": "modifiers", + "type": "List" + } + }, + "annotations": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "description": "List of annotations for this type.", + "x-java": { + "field": "annotations", + "type": "List" + } + }, + "parent_type": { + "type": [ + "string", + "null" + ], + "description": "The parent type of this type.", + "x-java": { + "field": "parentType", + "type": "String" + } + }, + "nested_type_declarations": { + "type": [ + "array", + "null" + ], + "items": { + "type": [ + "string", + "null" + ] + }, + "description": "List of nested type declarations within this type.", + "x-java": { + "field": "nestedTypeDeclarations", + "type": "List" + } + }, + "callable_declarations": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "oneOf": [ + { + "$ref": "#/$defs/Callable" + }, + { + "type": "null" + } + ] + }, + "description": "Map of callable declarations within this type.", + "x-java": { + "field": "callableDeclarations", + "type": "Map" + } + }, + "field_declarations": { + "type": [ + "array", + "null" + ], + "items": { + "oneOf": [ + { + "$ref": "#/$defs/Field" + }, + { + "type": "null" + } + ] + }, + "description": "List of field declarations within this type.", + "x-java": { + "field": "fieldDeclarations", + "type": "List" + } + }, + "enum_constants": { + "type": [ + "array", + "null" + ], + "items": { + "oneOf": [ + { + "$ref": "#/$defs/EnumConstant" + }, + { + "type": "null" + } + ] + }, + "description": "List of enum constants within this type.", + "x-java": { + "field": "enumConstants", + "type": "List" + } + }, + "record_components": { + "type": [ + "array", + "null" + ], + "items": { + "oneOf": [ + { + "$ref": "#/$defs/RecordComponent" + }, + { + "type": "null" + } + ] + }, + "description": "List of record components within this type.", + "x-java": { + "field": "recordComponents", + "type": "List" + } + }, + "initialization_blocks": { + "type": [ + "array", + "null" + ], + "items": { + "oneOf": [ + { + "$ref": "#/$defs/InitializationBlock" + }, + { + "type": "null" + } + ] + }, + "description": "List of initialization blocks within this type.", + "x-java": { + "field": "initializationBlocks", + "type": "List" + } + }, + "is_entrypoint_class": { + "type": "boolean", + "description": "Indicates if this type is an entry point class.", + "x-java": { + "field": "isEntrypointClass", + "type": "boolean" + } + } + }, + "additionalProperties": false + }, + "VariableDeclaration": { + "type": "object", + "properties": { + "comment": { + "oneOf": [ + { + "$ref": "#/$defs/Comment" + }, + { + "type": "null" + } + ], + "description": "The comment associated with the variable declaration.", + "x-java": { + "field": "comment", + "type": "Comment" + } + }, + "name": { + "type": [ + "string", + "null" + ], + "description": "The name of the variable.", + "x-java": { + "field": "name", + "type": "String" + } + }, + "type": { + "type": [ + "string", + "null" + ], + "description": "The type of the variable.", + "x-java": { + "field": "type", + "type": "String" + } + }, + "initializer": { + "type": [ + "string", + "null" + ], + "description": "The initializer of the variable, stored as a string representation.", + "x-java": { + "field": "initializer", + "type": "String" + } + }, + "start_line": { + "type": "integer", + "description": "The starting line number of the variable declaration in the source file.", + "x-java": { + "field": "startLine", + "type": "int" + } + }, + "start_column": { + "type": "integer", + "description": "The starting column number of the variable declaration in the source file.", + "x-java": { + "field": "startColumn", + "type": "int" + } + }, + "end_line": { + "type": "integer", + "description": "The ending line number of the variable declaration in the source file.", + "x-java": { + "field": "endLine", + "type": "int" + } + }, + "end_column": { + "type": "integer", + "description": "The ending column number of the variable declaration in the source file.", + "x-java": { + "field": "endColumn", + "type": "int" + } + } + }, + "additionalProperties": false + } + } +} diff --git a/v1/json/python/analysis.schema.json b/v1/json/python/analysis.schema.json new file mode 100644 index 0000000..c431de7 --- /dev/null +++ b/v1/json/python/analysis.schema.json @@ -0,0 +1,994 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://codellm-devkit.github.io/schema/v1/python/analysis.schema.json", + "title": "codeanalyzer-python v1 analysis output", + "description": "Represents a Python application.", + "x-cldk": { + "schemaVersion": 1, + "language": "python", + "source": { + "repo": "codellm-devkit/codeanalyzer-python", + "release": "v0.3.2", + "definedIn": "codeanalyzer/schema/py_schema.py (PyApplication)" + }, + "consumers": [ + { + "repo": "codellm-devkit/python-sdk", + "release": "v1.5.x" + } + ] + }, + "$defs": { + "PyAnalyzerInfo": { + "description": "Which analyzer produced this snapshot, and how it was configured.", + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "version": { + "title": "Version", + "type": "string" + }, + "config": { + "additionalProperties": true, + "default": {}, + "title": "Config", + "type": "object" + } + }, + "required": [ + "name", + "version" + ], + "title": "PyAnalyzerInfo", + "type": "object" + }, + "PyCallArgument": { + "description": "One call-site argument: AST category + inferred type, kept separate.\n\nThe legacy ``PyCallsite.argument_types`` mixed these two vocabularies\nin one list; this model is the disambiguated replacement (#86).", + "properties": { + "ast_kind": { + "title": "Ast Kind", + "type": "string" + }, + "inferred_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Inferred Type" + } + }, + "required": [ + "ast_kind" + ], + "title": "PyCallArgument", + "type": "object" + }, + "PyCallEdge": { + "description": "Identity-only call-graph edge with weight.\n\nMirrors Java's ``CallDependency``. ``source`` and ``target`` are\n``PyCallable.signature`` strings \u2014 nodes of the graph are the existing\n``PyCallable`` entries in the symbol table, not a separate vertex type.\nRich per-call metadata (receiver, arguments, location, ...) lives on\n``PyCallsite`` inside the source ``PyCallable.call_sites``.", + "properties": { + "source": { + "title": "Source", + "type": "string" + }, + "target": { + "title": "Target", + "type": "string" + }, + "type": { + "const": "CALL_DEP", + "default": "CALL_DEP", + "title": "Type", + "type": "string" + }, + "weight": { + "default": 1, + "title": "Weight", + "type": "integer" + }, + "provenance": { + "default": [], + "items": { + "enum": [ + "jedi", + "pycg", + "joern" + ], + "type": "string" + }, + "title": "Provenance", + "type": "array" + } + }, + "required": [ + "source", + "target" + ], + "title": "PyCallEdge", + "type": "object" + }, + "PyCallable": { + "description": "Represents a Python callable (function/method).", + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "path": { + "title": "Path", + "type": "string" + }, + "signature": { + "title": "Signature", + "type": "string" + }, + "comments": { + "default": [], + "items": { + "$ref": "#/$defs/PyComment" + }, + "title": "Comments", + "type": "array" + }, + "decorators": { + "default": [], + "items": { + "type": "string" + }, + "title": "Decorators", + "type": "array" + }, + "parameters": { + "default": [], + "items": { + "$ref": "#/$defs/PyCallableParameter" + }, + "title": "Parameters", + "type": "array" + }, + "return_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Return Type" + }, + "code": { + "default": null, + "title": "Code", + "type": "string" + }, + "start_line": { + "default": -1, + "title": "Start Line", + "type": "integer" + }, + "end_line": { + "default": -1, + "title": "End Line", + "type": "integer" + }, + "code_start_line": { + "default": -1, + "title": "Code Start Line", + "type": "integer" + }, + "accessed_symbols": { + "default": [], + "items": { + "$ref": "#/$defs/PySymbol" + }, + "title": "Accessed Symbols", + "type": "array" + }, + "call_sites": { + "default": [], + "items": { + "$ref": "#/$defs/PyCallsite" + }, + "title": "Call Sites", + "type": "array" + }, + "inner_callables": { + "additionalProperties": { + "$ref": "#/$defs/PyCallable" + }, + "default": {}, + "title": "Inner Callables", + "type": "object" + }, + "inner_classes": { + "additionalProperties": { + "$ref": "#/$defs/PyClass" + }, + "default": {}, + "title": "Inner Classes", + "type": "object" + }, + "local_variables": { + "default": [], + "items": { + "$ref": "#/$defs/PyVariableDeclaration" + }, + "title": "Local Variables", + "type": "array" + }, + "cyclomatic_complexity": { + "default": 0, + "title": "Cyclomatic Complexity", + "type": "integer" + } + }, + "required": [ + "name", + "path", + "signature" + ], + "title": "PyCallable", + "type": "object" + }, + "PyCallableParameter": { + "description": "Represents a parameter of a Python callable (function/method).", + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Type" + }, + "default_value": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Default Value" + }, + "start_line": { + "default": -1, + "title": "Start Line", + "type": "integer" + }, + "end_line": { + "default": -1, + "title": "End Line", + "type": "integer" + }, + "start_column": { + "default": -1, + "title": "Start Column", + "type": "integer" + }, + "end_column": { + "default": -1, + "title": "End Column", + "type": "integer" + } + }, + "required": [ + "name" + ], + "title": "PyCallableParameter", + "type": "object" + }, + "PyCallsite": { + "description": "Represents a Python call site (function or method invocation) with contextual metadata.", + "properties": { + "method_name": { + "title": "Method Name", + "type": "string" + }, + "receiver_expr": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Receiver Expr" + }, + "receiver_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Receiver Type" + }, + "argument_types": { + "default": [], + "items": { + "type": "string" + }, + "title": "Argument Types", + "type": "array" + }, + "arguments": { + "default": [], + "items": { + "$ref": "#/$defs/PyCallArgument" + }, + "title": "Arguments", + "type": "array" + }, + "return_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Return Type" + }, + "callee_signature": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Callee Signature" + }, + "is_constructor_call": { + "default": false, + "title": "Is Constructor Call", + "type": "boolean" + }, + "start_line": { + "default": -1, + "title": "Start Line", + "type": "integer" + }, + "start_column": { + "default": -1, + "title": "Start Column", + "type": "integer" + }, + "end_line": { + "default": -1, + "title": "End Line", + "type": "integer" + }, + "end_column": { + "default": -1, + "title": "End Column", + "type": "integer" + } + }, + "required": [ + "method_name" + ], + "title": "PyCallsite", + "type": "object" + }, + "PyClass": { + "description": "Represents a Python class.", + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "signature": { + "title": "Signature", + "type": "string" + }, + "comments": { + "default": [], + "items": { + "$ref": "#/$defs/PyComment" + }, + "title": "Comments", + "type": "array" + }, + "code": { + "default": null, + "title": "Code", + "type": "string" + }, + "base_classes": { + "default": [], + "items": { + "type": "string" + }, + "title": "Base Classes", + "type": "array" + }, + "methods": { + "additionalProperties": { + "$ref": "#/$defs/PyCallable" + }, + "default": {}, + "title": "Methods", + "type": "object" + }, + "attributes": { + "additionalProperties": { + "$ref": "#/$defs/PyClassAttribute" + }, + "default": {}, + "title": "Attributes", + "type": "object" + }, + "inner_classes": { + "additionalProperties": { + "$ref": "#/$defs/PyClass" + }, + "default": {}, + "title": "Inner Classes", + "type": "object" + }, + "start_line": { + "default": -1, + "title": "Start Line", + "type": "integer" + }, + "end_line": { + "default": -1, + "title": "End Line", + "type": "integer" + } + }, + "required": [ + "name", + "signature" + ], + "title": "PyClass", + "type": "object" + }, + "PyClassAttribute": { + "description": "Represents a Python class attribute.", + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Type" + }, + "initializer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Initializer" + }, + "comments": { + "default": [], + "items": { + "$ref": "#/$defs/PyComment" + }, + "title": "Comments", + "type": "array" + }, + "start_line": { + "default": -1, + "title": "Start Line", + "type": "integer" + }, + "end_line": { + "default": -1, + "title": "End Line", + "type": "integer" + } + }, + "required": [ + "name" + ], + "title": "PyClassAttribute", + "type": "object" + }, + "PyComment": { + "description": "Represents a Python comment.", + "properties": { + "content": { + "title": "Content", + "type": "string" + }, + "start_line": { + "default": -1, + "title": "Start Line", + "type": "integer" + }, + "end_line": { + "default": -1, + "title": "End Line", + "type": "integer" + }, + "start_column": { + "default": -1, + "title": "Start Column", + "type": "integer" + }, + "end_column": { + "default": -1, + "title": "End Column", + "type": "integer" + }, + "is_docstring": { + "default": false, + "title": "Is Docstring", + "type": "boolean" + } + }, + "required": [ + "content" + ], + "title": "PyComment", + "type": "object" + }, + "PyExternalSymbol": { + "description": "A call-graph target outside the analyzed project -- an imported library or\nbuiltin member. Mirrors codeanalyzer-typescript's ``TSExternalSymbol`` and is\nkeyed in ``PyApplication.external_symbols`` by its call-graph signature.", + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "module": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Module" + } + }, + "required": [ + "name" + ], + "title": "PyExternalSymbol", + "type": "object" + }, + "PyImport": { + "description": "Represents a Python import statement.", + "properties": { + "module": { + "title": "Module", + "type": "string" + }, + "name": { + "title": "Name", + "type": "string" + }, + "alias": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Alias" + }, + "resolved_module": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Resolved Module" + }, + "start_line": { + "default": -1, + "title": "Start Line", + "type": "integer" + }, + "end_line": { + "default": -1, + "title": "End Line", + "type": "integer" + }, + "start_column": { + "default": -1, + "title": "Start Column", + "type": "integer" + }, + "end_column": { + "default": -1, + "title": "End Column", + "type": "integer" + } + }, + "required": [ + "module", + "name" + ], + "title": "PyImport", + "type": "object" + }, + "PyModule": { + "description": "Represents a Python module.", + "properties": { + "file_path": { + "title": "File Path", + "type": "string" + }, + "module_name": { + "title": "Module Name", + "type": "string" + }, + "imports": { + "default": [], + "items": { + "$ref": "#/$defs/PyImport" + }, + "title": "Imports", + "type": "array" + }, + "comments": { + "default": [], + "items": { + "$ref": "#/$defs/PyComment" + }, + "title": "Comments", + "type": "array" + }, + "classes": { + "additionalProperties": { + "$ref": "#/$defs/PyClass" + }, + "default": {}, + "title": "Classes", + "type": "object" + }, + "functions": { + "additionalProperties": { + "$ref": "#/$defs/PyCallable" + }, + "default": {}, + "title": "Functions", + "type": "object" + }, + "variables": { + "default": [], + "items": { + "$ref": "#/$defs/PyVariableDeclaration" + }, + "title": "Variables", + "type": "array" + }, + "content_hash": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Content Hash" + }, + "last_modified": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Last Modified" + }, + "file_size": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "File Size" + } + }, + "required": [ + "file_path", + "module_name" + ], + "title": "PyModule", + "type": "object" + }, + "PyRepositoryInfo": { + "description": "Where the analyzed source came from: git provenance captured at analysis time.", + "properties": { + "uri": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Uri" + }, + "revision": { + "title": "Revision", + "type": "string" + }, + "dirty": { + "default": false, + "title": "Dirty", + "type": "boolean" + } + }, + "required": [ + "revision" + ], + "title": "PyRepositoryInfo", + "type": "object" + }, + "PySymbol": { + "description": "Represents a symbol used or declared in Python code.", + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "scope": { + "enum": [ + "local", + "nonlocal", + "global", + "class", + "module" + ], + "title": "Scope", + "type": "string" + }, + "kind": { + "enum": [ + "variable", + "parameter", + "attribute", + "function", + "class", + "module" + ], + "title": "Kind", + "type": "string" + }, + "type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Type" + }, + "qualified_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Qualified Name" + }, + "is_builtin": { + "default": false, + "title": "Is Builtin", + "type": "boolean" + }, + "lineno": { + "default": -1, + "title": "Lineno", + "type": "integer" + }, + "col_offset": { + "default": -1, + "title": "Col Offset", + "type": "integer" + } + }, + "required": [ + "name", + "scope", + "kind" + ], + "title": "PySymbol", + "type": "object" + }, + "PyVariableDeclaration": { + "description": "Represents a Python variable declaration.", + "properties": { + "name": { + "title": "Name", + "type": "string" + }, + "type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Type" + }, + "initializer": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Initializer" + }, + "value": { + "anyOf": [ + {}, + { + "type": "null" + } + ], + "default": null, + "title": "Value" + }, + "scope": { + "default": "module", + "enum": [ + "module", + "class", + "function" + ], + "title": "Scope", + "type": "string" + }, + "start_line": { + "default": -1, + "title": "Start Line", + "type": "integer" + }, + "end_line": { + "default": -1, + "title": "End Line", + "type": "integer" + }, + "start_column": { + "default": -1, + "title": "Start Column", + "type": "integer" + }, + "end_column": { + "default": -1, + "title": "End Column", + "type": "integer" + } + }, + "required": [ + "name", + "type" + ], + "title": "PyVariableDeclaration", + "type": "object" + } + }, + "properties": { + "symbol_table": { + "additionalProperties": { + "$ref": "#/$defs/PyModule" + }, + "title": "Symbol Table", + "type": "object" + }, + "call_graph": { + "default": [], + "items": { + "$ref": "#/$defs/PyCallEdge" + }, + "title": "Call Graph", + "type": "array" + }, + "external_symbols": { + "additionalProperties": { + "$ref": "#/$defs/PyExternalSymbol" + }, + "default": {}, + "title": "External Symbols", + "type": "object" + }, + "analyzer": { + "anyOf": [ + { + "$ref": "#/$defs/PyAnalyzerInfo" + }, + { + "type": "null" + } + ], + "default": null + }, + "repository": { + "anyOf": [ + { + "$ref": "#/$defs/PyRepositoryInfo" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "symbol_table" + ], + "type": "object" +} diff --git a/v1/json/typescript/analysis.schema.json b/v1/json/typescript/analysis.schema.json new file mode 100644 index 0000000..ae2b9ce --- /dev/null +++ b/v1/json/typescript/analysis.schema.json @@ -0,0 +1,1502 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://codellm-devkit.github.io/schema/v1/typescript/analysis.schema.json", + "title": "codeanalyzer-typescript v1 analysis output", + "description": "Generated from the TypeScript interfaces of codeanalyzer-typescript v0.4.3 \u2014 the last release before src/schema/v2 landed, and the release python-sdk pins.", + "x-cldk": { + "schemaVersion": 1, + "language": "typescript", + "source": { + "repo": "codellm-devkit/codeanalyzer-typescript", + "release": "v0.4.3", + "definedIn": "src/schema/schema.ts (TSApplication)" + }, + "consumers": [ + { + "repo": "codellm-devkit/python-sdk", + "release": "v1.5.x" + } + ] + }, + "$defs": { + "TSCallEdge": { + "additionalProperties": false, + "properties": { + "provenance": { + "items": { + "type": "string" + }, + "type": "array" + }, + "source": { + "type": "string" + }, + "tags": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "target": { + "type": "string" + }, + "type": { + "const": "CALL_DEP", + "type": "string" + }, + "weight": { + "type": "number" + } + }, + "required": [ + "source", + "target", + "type", + "weight", + "provenance", + "tags" + ], + "type": "object" + }, + "TSCallable": { + "additionalProperties": false, + "properties": { + "accessed_symbols": { + "items": { + "$ref": "#/$defs/TSSymbol" + }, + "type": "array" + }, + "accessibility": { + "type": [ + "string", + "null" + ] + }, + "accessor_kind": { + "type": [ + "string", + "null" + ] + }, + "call_sites": { + "items": { + "$ref": "#/$defs/TSCallsite" + }, + "type": "array" + }, + "code": { + "type": [ + "string", + "null" + ] + }, + "code_start_line": { + "type": "number" + }, + "comments": { + "items": { + "$ref": "#/$defs/TSComment" + }, + "type": "array" + }, + "cyclomatic_complexity": { + "type": "number" + }, + "decorators": { + "items": { + "$ref": "#/$defs/TSDecorator" + }, + "type": "array" + }, + "end_line": { + "type": "number" + }, + "entrypoints": { + "items": { + "$ref": "#/$defs/TSEntrypoint" + }, + "type": "array" + }, + "inner_callables": { + "additionalProperties": { + "$ref": "#/$defs/TSCallable" + }, + "type": "object" + }, + "inner_classes": { + "additionalProperties": { + "$ref": "#/$defs/TSClass" + }, + "type": "object" + }, + "is_abstract": { + "type": "boolean" + }, + "is_ambient": { + "type": "boolean" + }, + "is_async": { + "type": "boolean" + }, + "is_exported": { + "type": "boolean" + }, + "is_generator": { + "type": "boolean" + }, + "is_implicit": { + "type": "boolean" + }, + "is_optional": { + "type": "boolean" + }, + "is_readonly": { + "type": "boolean" + }, + "is_static": { + "type": "boolean" + }, + "kind": { + "$ref": "#/$defs/TSCallableKind" + }, + "local_variables": { + "items": { + "$ref": "#/$defs/TSVariableDeclaration" + }, + "type": "array" + }, + "name": { + "type": "string" + }, + "overload_signatures": { + "items": { + "$ref": "#/$defs/TSOverloadSignature" + }, + "type": "array" + }, + "parameters": { + "items": { + "$ref": "#/$defs/TSCallableParameter" + }, + "type": "array" + }, + "path": { + "type": "string" + }, + "return_type": { + "type": [ + "string", + "null" + ] + }, + "signature": { + "type": "string" + }, + "start_line": { + "type": "number" + }, + "type_parameters": { + "items": { + "$ref": "#/$defs/TSTypeParameter" + }, + "type": "array" + } + }, + "required": [ + "name", + "path", + "signature", + "comments", + "decorators", + "parameters", + "type_parameters", + "return_type", + "code", + "start_line", + "end_line", + "code_start_line", + "accessed_symbols", + "call_sites", + "inner_callables", + "inner_classes", + "local_variables", + "cyclomatic_complexity", + "entrypoints", + "kind", + "accessibility", + "is_static", + "is_abstract", + "is_async", + "is_generator", + "is_optional", + "is_readonly", + "is_exported", + "is_ambient", + "is_implicit", + "accessor_kind", + "overload_signatures" + ], + "type": "object" + }, + "TSCallableKind": { + "enum": [ + "function", + "method", + "constructor", + "getter", + "setter", + "arrow", + "function_expression" + ], + "type": "string" + }, + "TSCallableParameter": { + "additionalProperties": false, + "properties": { + "accessibility": { + "type": [ + "string", + "null" + ] + }, + "decorators": { + "items": { + "$ref": "#/$defs/TSDecorator" + }, + "type": "array" + }, + "default_value": { + "type": [ + "string", + "null" + ] + }, + "end_column": { + "type": "number" + }, + "end_line": { + "type": "number" + }, + "is_optional": { + "type": "boolean" + }, + "is_readonly": { + "type": "boolean" + }, + "is_rest": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "start_column": { + "type": "number" + }, + "start_line": { + "type": "number" + }, + "type": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "name", + "type", + "default_value", + "is_optional", + "is_rest", + "is_readonly", + "accessibility", + "decorators", + "start_line", + "end_line", + "start_column", + "end_column" + ], + "type": "object" + }, + "TSCallsite": { + "additionalProperties": false, + "properties": { + "argument_types": { + "items": { + "type": "string" + }, + "type": "array" + }, + "callee_signature": { + "type": [ + "string", + "null" + ] + }, + "end_column": { + "type": "number" + }, + "end_line": { + "type": "number" + }, + "is_constructor_call": { + "type": "boolean" + }, + "is_optional_chain": { + "type": "boolean" + }, + "method_name": { + "type": "string" + }, + "receiver_expr": { + "type": [ + "string", + "null" + ] + }, + "receiver_type": { + "type": [ + "string", + "null" + ] + }, + "return_type": { + "type": [ + "string", + "null" + ] + }, + "start_column": { + "type": "number" + }, + "start_line": { + "type": "number" + }, + "type_arguments": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "method_name", + "receiver_expr", + "receiver_type", + "argument_types", + "type_arguments", + "return_type", + "callee_signature", + "is_constructor_call", + "is_optional_chain", + "start_line", + "start_column", + "end_line", + "end_column" + ], + "type": "object" + }, + "TSClass": { + "additionalProperties": false, + "properties": { + "attributes": { + "additionalProperties": { + "$ref": "#/$defs/TSClassAttribute" + }, + "type": "object" + }, + "base_classes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "code": { + "type": [ + "string", + "null" + ] + }, + "comments": { + "items": { + "$ref": "#/$defs/TSComment" + }, + "type": "array" + }, + "decorators": { + "items": { + "$ref": "#/$defs/TSDecorator" + }, + "type": "array" + }, + "end_line": { + "type": "number" + }, + "entrypoints": { + "items": { + "$ref": "#/$defs/TSEntrypoint" + }, + "type": "array" + }, + "implements_types": { + "items": { + "type": "string" + }, + "type": "array" + }, + "inner_classes": { + "additionalProperties": { + "$ref": "#/$defs/TSClass" + }, + "type": "object" + }, + "is_abstract": { + "type": "boolean" + }, + "is_ambient": { + "type": "boolean" + }, + "is_exported": { + "type": "boolean" + }, + "methods": { + "additionalProperties": { + "$ref": "#/$defs/TSCallable" + }, + "type": "object" + }, + "name": { + "type": "string" + }, + "signature": { + "type": "string" + }, + "start_line": { + "type": "number" + }, + "type_parameters": { + "items": { + "$ref": "#/$defs/TSTypeParameter" + }, + "type": "array" + } + }, + "required": [ + "name", + "signature", + "comments", + "code", + "decorators", + "base_classes", + "implements_types", + "type_parameters", + "methods", + "attributes", + "inner_classes", + "entrypoints", + "is_abstract", + "is_exported", + "is_ambient", + "start_line", + "end_line" + ], + "type": "object" + }, + "TSClassAttribute": { + "additionalProperties": false, + "properties": { + "accessibility": { + "type": [ + "string", + "null" + ] + }, + "comments": { + "items": { + "$ref": "#/$defs/TSComment" + }, + "type": "array" + }, + "decorators": { + "items": { + "$ref": "#/$defs/TSDecorator" + }, + "type": "array" + }, + "end_line": { + "type": "number" + }, + "initializer": { + "type": [ + "string", + "null" + ] + }, + "is_abstract": { + "type": "boolean" + }, + "is_optional": { + "type": "boolean" + }, + "is_readonly": { + "type": "boolean" + }, + "is_static": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "start_line": { + "type": "number" + }, + "type": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "name", + "type", + "comments", + "decorators", + "initializer", + "accessibility", + "is_static", + "is_readonly", + "is_optional", + "is_abstract", + "start_line", + "end_line" + ], + "type": "object" + }, + "TSComment": { + "additionalProperties": false, + "properties": { + "content": { + "type": "string" + }, + "end_column": { + "type": "number" + }, + "end_line": { + "type": "number" + }, + "is_docstring": { + "type": "boolean" + }, + "start_column": { + "type": "number" + }, + "start_line": { + "type": "number" + } + }, + "required": [ + "content", + "is_docstring", + "start_line", + "end_line", + "start_column", + "end_column" + ], + "type": "object" + }, + "TSDecorator": { + "additionalProperties": false, + "properties": { + "end_column": { + "type": "number" + }, + "end_line": { + "type": "number" + }, + "keyword_arguments": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "name": { + "type": "string" + }, + "positional_arguments": { + "items": { + "type": "string" + }, + "type": "array" + }, + "qualified_name": { + "type": [ + "string", + "null" + ] + }, + "start_column": { + "type": "number" + }, + "start_line": { + "type": "number" + } + }, + "required": [ + "name", + "qualified_name", + "positional_arguments", + "keyword_arguments", + "start_line", + "end_line", + "start_column", + "end_column" + ], + "type": "object" + }, + "TSEntrypoint": { + "additionalProperties": false, + "properties": { + "detection_source": { + "type": "string" + }, + "framework": { + "type": "string" + }, + "http_methods": { + "items": { + "type": "string" + }, + "type": "array" + }, + "route_path": { + "type": [ + "string", + "null" + ] + }, + "tags": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + }, + "required": [ + "framework", + "detection_source", + "route_path", + "http_methods", + "tags" + ], + "type": "object" + }, + "TSEnum": { + "additionalProperties": false, + "properties": { + "code": { + "type": [ + "string", + "null" + ] + }, + "comments": { + "items": { + "$ref": "#/$defs/TSComment" + }, + "type": "array" + }, + "end_line": { + "type": "number" + }, + "is_ambient": { + "type": "boolean" + }, + "is_const": { + "type": "boolean" + }, + "is_exported": { + "type": "boolean" + }, + "members": { + "items": { + "$ref": "#/$defs/TSEnumMember" + }, + "type": "array" + }, + "name": { + "type": "string" + }, + "signature": { + "type": "string" + }, + "start_line": { + "type": "number" + } + }, + "required": [ + "name", + "signature", + "comments", + "code", + "members", + "is_const", + "is_exported", + "is_ambient", + "start_line", + "end_line" + ], + "type": "object" + }, + "TSEnumMember": { + "additionalProperties": false, + "properties": { + "end_line": { + "type": "number" + }, + "name": { + "type": "string" + }, + "start_line": { + "type": "number" + }, + "value": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "name", + "value", + "start_line", + "end_line" + ], + "type": "object" + }, + "TSExport": { + "additionalProperties": false, + "properties": { + "alias": { + "type": [ + "string", + "null" + ] + }, + "end_column": { + "type": "number" + }, + "end_line": { + "type": "number" + }, + "export_kind": { + "enum": [ + "named", + "default", + "namespace", + "re_export" + ], + "type": "string" + }, + "is_type_only": { + "type": "boolean" + }, + "module": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "start_column": { + "type": "number" + }, + "start_line": { + "type": "number" + } + }, + "required": [ + "module", + "name", + "alias", + "is_type_only", + "export_kind", + "start_line", + "end_line", + "start_column", + "end_column" + ], + "type": "object" + }, + "TSExternalSymbol": { + "additionalProperties": false, + "properties": { + "module": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "name", + "module" + ], + "type": "object" + }, + "TSImport": { + "additionalProperties": false, + "description": "The canonical CLDK analysis schema for TypeScript.\n\nMirrors the identity-only Python schema (codeanalyzer-python/.../py_schema.py) field for field on the invariant spine \u2014 `TSApplication { symbol_table, call_graph, entrypoints }`, `Module \u2192 Class/Callable` nesting, identity-only `TSCallEdge` whose `source`/`target` are bare signature strings \u2014 and extends it at the leaves with TypeScript-native node kinds (interface / type-alias / enum / namespace) and typed fields (generics, modifiers, ...). See SCHEMA_DECISIONS.md.\n\nAll field names are snake_case so `JSON.stringify` emits keys the SDK Pydantic models parse. The matching Pydantic models live in python-sdk/cldk/models/typescript/models.py and MUST be co-evolved with this file.", + "properties": { + "alias": { + "type": [ + "string", + "null" + ] + }, + "end_column": { + "type": "number" + }, + "end_line": { + "type": "number" + }, + "import_kind": { + "enum": [ + "named", + "default", + "namespace", + "side_effect" + ], + "type": "string" + }, + "is_type_only": { + "type": "boolean" + }, + "module": { + "type": "string" + }, + "name": { + "type": "string" + }, + "start_column": { + "type": "number" + }, + "start_line": { + "type": "number" + } + }, + "required": [ + "module", + "name", + "alias", + "is_type_only", + "import_kind", + "start_line", + "end_line", + "start_column", + "end_column" + ], + "type": "object" + }, + "TSInterface": { + "additionalProperties": false, + "properties": { + "base_classes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "call_signatures": { + "items": { + "type": "string" + }, + "type": "array" + }, + "code": { + "type": [ + "string", + "null" + ] + }, + "comments": { + "items": { + "$ref": "#/$defs/TSComment" + }, + "type": "array" + }, + "end_line": { + "type": "number" + }, + "index_signatures": { + "items": { + "type": "string" + }, + "type": "array" + }, + "is_ambient": { + "type": "boolean" + }, + "is_exported": { + "type": "boolean" + }, + "methods": { + "additionalProperties": { + "$ref": "#/$defs/TSCallable" + }, + "type": "object" + }, + "name": { + "type": "string" + }, + "properties": { + "additionalProperties": { + "$ref": "#/$defs/TSClassAttribute" + }, + "type": "object" + }, + "signature": { + "type": "string" + }, + "start_line": { + "type": "number" + }, + "type_parameters": { + "items": { + "$ref": "#/$defs/TSTypeParameter" + }, + "type": "array" + } + }, + "required": [ + "name", + "signature", + "comments", + "code", + "base_classes", + "type_parameters", + "methods", + "properties", + "call_signatures", + "index_signatures", + "is_exported", + "is_ambient", + "start_line", + "end_line" + ], + "type": "object" + }, + "TSModule": { + "additionalProperties": false, + "properties": { + "classes": { + "additionalProperties": { + "$ref": "#/$defs/TSClass" + }, + "type": "object" + }, + "comments": { + "items": { + "$ref": "#/$defs/TSComment" + }, + "type": "array" + }, + "content_hash": { + "type": [ + "string", + "null" + ] + }, + "enums": { + "additionalProperties": { + "$ref": "#/$defs/TSEnum" + }, + "type": "object" + }, + "exports": { + "items": { + "$ref": "#/$defs/TSExport" + }, + "type": "array" + }, + "file_path": { + "type": "string" + }, + "file_size": { + "type": [ + "number", + "null" + ] + }, + "functions": { + "additionalProperties": { + "$ref": "#/$defs/TSCallable" + }, + "type": "object" + }, + "imports": { + "items": { + "$ref": "#/$defs/TSImport" + }, + "type": "array" + }, + "interfaces": { + "additionalProperties": { + "$ref": "#/$defs/TSInterface" + }, + "type": "object" + }, + "is_declaration_file": { + "type": "boolean" + }, + "is_tsx": { + "type": "boolean" + }, + "last_modified": { + "type": [ + "number", + "null" + ] + }, + "module_name": { + "type": "string" + }, + "namespaces": { + "additionalProperties": { + "$ref": "#/$defs/TSNamespace" + }, + "type": "object" + }, + "type_aliases": { + "additionalProperties": { + "$ref": "#/$defs/TSTypeAlias" + }, + "type": "object" + }, + "variables": { + "items": { + "$ref": "#/$defs/TSVariableDeclaration" + }, + "type": "array" + } + }, + "required": [ + "file_path", + "module_name", + "imports", + "exports", + "comments", + "classes", + "interfaces", + "enums", + "type_aliases", + "functions", + "namespaces", + "variables", + "is_tsx", + "is_declaration_file", + "content_hash", + "last_modified", + "file_size" + ], + "type": "object" + }, + "TSNamespace": { + "additionalProperties": false, + "properties": { + "classes": { + "additionalProperties": { + "$ref": "#/$defs/TSClass" + }, + "type": "object" + }, + "comments": { + "items": { + "$ref": "#/$defs/TSComment" + }, + "type": "array" + }, + "end_line": { + "type": "number" + }, + "enums": { + "additionalProperties": { + "$ref": "#/$defs/TSEnum" + }, + "type": "object" + }, + "functions": { + "additionalProperties": { + "$ref": "#/$defs/TSCallable" + }, + "type": "object" + }, + "interfaces": { + "additionalProperties": { + "$ref": "#/$defs/TSInterface" + }, + "type": "object" + }, + "is_ambient": { + "type": "boolean" + }, + "is_exported": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "namespaces": { + "additionalProperties": { + "$ref": "#/$defs/TSNamespace" + }, + "type": "object" + }, + "signature": { + "type": "string" + }, + "start_line": { + "type": "number" + }, + "type_aliases": { + "additionalProperties": { + "$ref": "#/$defs/TSTypeAlias" + }, + "type": "object" + }, + "variables": { + "items": { + "$ref": "#/$defs/TSVariableDeclaration" + }, + "type": "array" + } + }, + "required": [ + "name", + "signature", + "comments", + "classes", + "interfaces", + "enums", + "type_aliases", + "functions", + "variables", + "namespaces", + "is_exported", + "is_ambient", + "start_line", + "end_line" + ], + "type": "object" + }, + "TSOverloadSignature": { + "additionalProperties": false, + "properties": { + "end_line": { + "type": "number" + }, + "parameters": { + "items": { + "$ref": "#/$defs/TSCallableParameter" + }, + "type": "array" + }, + "return_type": { + "type": [ + "string", + "null" + ] + }, + "start_line": { + "type": "number" + }, + "type_parameters": { + "items": { + "$ref": "#/$defs/TSTypeParameter" + }, + "type": "array" + } + }, + "required": [ + "parameters", + "return_type", + "type_parameters", + "start_line", + "end_line" + ], + "type": "object" + }, + "TSSymbol": { + "additionalProperties": false, + "properties": { + "col_offset": { + "type": "number" + }, + "is_builtin": { + "type": "boolean" + }, + "kind": { + "type": "string" + }, + "lineno": { + "type": "number" + }, + "name": { + "type": "string" + }, + "qualified_name": { + "type": [ + "string", + "null" + ] + }, + "scope": { + "type": "string" + }, + "type": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "name", + "scope", + "kind", + "type", + "qualified_name", + "is_builtin", + "lineno", + "col_offset" + ], + "type": "object" + }, + "TSSynthesizedCallable": { + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "path": { + "type": "string" + }, + "start_column": { + "type": "number" + }, + "start_line": { + "type": "number" + } + }, + "required": [ + "name", + "path", + "start_line", + "start_column" + ], + "type": "object" + }, + "TSTypeAlias": { + "additionalProperties": false, + "properties": { + "aliased_type": { + "type": "string" + }, + "code": { + "type": [ + "string", + "null" + ] + }, + "comments": { + "items": { + "$ref": "#/$defs/TSComment" + }, + "type": "array" + }, + "end_line": { + "type": "number" + }, + "is_ambient": { + "type": "boolean" + }, + "is_exported": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "signature": { + "type": "string" + }, + "start_line": { + "type": "number" + }, + "type_parameters": { + "items": { + "$ref": "#/$defs/TSTypeParameter" + }, + "type": "array" + } + }, + "required": [ + "name", + "signature", + "comments", + "code", + "aliased_type", + "type_parameters", + "is_exported", + "is_ambient", + "start_line", + "end_line" + ], + "type": "object" + }, + "TSTypeParameter": { + "additionalProperties": false, + "properties": { + "constraint": { + "type": [ + "string", + "null" + ] + }, + "default": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + } + }, + "required": [ + "name", + "constraint", + "default" + ], + "type": "object" + }, + "TSVariableDeclaration": { + "additionalProperties": false, + "properties": { + "declaration_kind": { + "enum": [ + "const", + "let", + "var", + "using", + "unknown" + ], + "type": "string" + }, + "end_column": { + "type": "number" + }, + "end_line": { + "type": "number" + }, + "initializer": { + "type": [ + "string", + "null" + ] + }, + "is_exported": { + "type": "boolean" + }, + "is_readonly": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "scope": { + "enum": [ + "module", + "namespace", + "class", + "function", + "block" + ], + "type": "string" + }, + "start_column": { + "type": "number" + }, + "start_line": { + "type": "number" + }, + "type": { + "type": [ + "string", + "null" + ] + }, + "value": { + "anyOf": [ + {}, + { + "type": "null" + } + ] + } + }, + "required": [ + "name", + "type", + "initializer", + "value", + "scope", + "declaration_kind", + "is_readonly", + "is_exported", + "start_line", + "end_line", + "start_column", + "end_column" + ], + "type": "object" + } + }, + "additionalProperties": false, + "properties": { + "call_graph": { + "items": { + "$ref": "#/$defs/TSCallEdge" + }, + "type": "array" + }, + "external_symbols": { + "additionalProperties": { + "$ref": "#/$defs/TSExternalSymbol" + }, + "type": "object" + }, + "symbol_table": { + "additionalProperties": { + "$ref": "#/$defs/TSModule" + }, + "type": "object" + }, + "synthesized_callables": { + "additionalProperties": { + "$ref": "#/$defs/TSSynthesizedCallable" + }, + "type": "object" + } + }, + "required": [ + "symbol_table", + "call_graph", + "external_symbols", + "synthesized_callables" + ], + "type": "object" +} diff --git a/v1/neo4j/contract.schema.json b/v1/neo4j/contract.schema.json new file mode 100644 index 0000000..ef39ff1 --- /dev/null +++ b/v1/neo4j/contract.schema.json @@ -0,0 +1,481 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://codellm-devkit.github.io/schema/neo4j/contract.schema.json", + "title": "codeanalyzer Neo4j graph contract", + "description": "Meta-schema for the schema.neo4j.json document each codeanalyzer emits with `--emit schema`. The document describes the graph an analyzer writes \u2014 node labels, relationship types, and the constraints and indexes the loader creates \u2014 so it is a contract about a graph, not a JSON Schema for analysis.json. Its schema_version is an independent line from the analysis schema version: an analyzer can sit on analysis schema v1 while its graph contract is on 2.x. Every label and relationship type must carry its language prefix \u2014 J/J_ for Java, Py/PY_ for Python, TS/TS_ for TypeScript \u2014 everywhere it appears, including inside the Cypher constraint and index statements.", + "x-cldk": { + "kind": "neo4j-graph-contract", + "sources": [ + { + "repo": "codellm-devkit/codeanalyzer-java", + "release": "v2.4.1", + "schemaVersion": "1.0.0", + "path": "schema.neo4j.json" + }, + { + "repo": "codellm-devkit/codeanalyzer-python", + "release": "v0.3.2", + "schemaVersion": "1.2.0", + "path": "schema.neo4j.json" + }, + { + "repo": "codellm-devkit/codeanalyzer-typescript", + "release": "v0.4.3", + "schemaVersion": "1.0.0", + "path": "schema.neo4j.json", + "normalized": true + } + ], + "note": "The copies under v1/neo4j// are byte-identical to the tagged release, so `diff` against the analyzer repo is the drift check. Each is pinned to the same release as the v1 analysis schema for that language: the graph contract an analyzer emitted while it was still on analysis schema v1. The contract's own schema_version runs on an independent line and does not track v1/v2. One exception: the TypeScript copy is normalized rather than byte-identical, because v0.4.3 predates prefixing on that line. It carries an x-cldk block saying so.", + "validates": [ + "*/schema.neo4j.json" + ] + }, + "type": "object", + "required": [ + "schema_version", + "generator", + "node_labels", + "relationship_types" + ], + "additionalProperties": false, + "properties": { + "schema_version": { + "description": "Version of the graph contract itself, independent of the analysis schema version.", + "type": "string", + "pattern": "^\\d+\\.\\d+\\.\\d+$" + }, + "generator": { + "description": "Analyzer that emitted this contract. Closed set \u2014 a new analyzer must register its label prefix under $defs/prefixRules before its contract can validate.", + "type": "string", + "enum": [ + "codeanalyzer-java", + "codeanalyzer-python", + "codeanalyzer-typescript" + ] + }, + "marker_labels": { + "description": "Extra labels attached to nodes that satisfy some property (e.g. JEntrypoint), rather than to a node kind.", + "type": "array", + "items": { + "type": "string" + } + }, + "node_labels": { + "type": "array", + "items": { + "$ref": "#/$defs/NodeLabel" + }, + "minItems": 1 + }, + "relationship_types": { + "type": "array", + "items": { + "$ref": "#/$defs/RelationshipType" + }, + "minItems": 1 + }, + "constraints": { + "description": "Cypher CREATE CONSTRAINT statements the loader runs before ingest.", + "type": "array", + "items": { + "type": "string", + "pattern": "^CREATE CONSTRAINT " + } + }, + "indexes": { + "description": "Cypher CREATE INDEX statements the loader runs before ingest.", + "type": "array", + "items": { + "type": "string", + "pattern": "^CREATE (INDEX|FULLTEXT INDEX|VECTOR INDEX) " + } + }, + "x-cldk": { + "description": "Provenance annotation carried by a tracked copy, not something an analyzer emits. Present only on a copy that is not byte-identical to its release, where it records the origin and what was changed.", + "type": "object", + "required": [ + "normalized", + "origin" + ], + "properties": { + "normalized": { + "type": "boolean" + }, + "origin": { + "type": "object", + "required": [ + "repo", + "release" + ], + "properties": { + "repo": { + "type": "string" + }, + "release": { + "type": "string" + }, + "path": { + "type": "string" + }, + "schemaVersion": { + "type": "string" + } + } + }, + "note": { + "type": "string" + } + } + } + }, + "$defs": { + "NodeLabel": { + "type": "object", + "required": [ + "label", + "key", + "properties" + ], + "additionalProperties": false, + "properties": { + "label": { + "description": "Neo4j label written on the node.", + "type": "string", + "pattern": "^[A-Za-z][A-Za-z0-9_]*$" + }, + "merge_label": { + "description": "Label used in the MERGE that upserts the node, when it differs from `label`. Java and Python spell this merge_label; TypeScript spells it mergeLabel. Either is accepted here \u2014 the divergence is real in the shipped contracts.", + "type": "string" + }, + "mergeLabel": { + "description": "TypeScript's spelling of merge_label. See the note on merge_label.", + "type": "string" + }, + "key": { + "description": "Property that uniquely identifies a node with this label.", + "type": "string" + }, + "properties": { + "description": "Property name to property type. Values are contract type names, not JSON Schema types.", + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/PropertyType" + } + } + }, + "anyOf": [ + { + "required": [ + "merge_label" + ] + }, + { + "required": [ + "mergeLabel" + ] + } + ] + }, + "RelationshipType": { + "type": "object", + "required": [ + "type", + "from", + "to" + ], + "additionalProperties": false, + "properties": { + "type": { + "description": "Relationship type written on the edge, e.g. PY_HAS_MODULE.", + "type": "string", + "pattern": "^[A-Z][A-Z0-9_]*$" + }, + "from": { + "description": "Node labels this edge may start at.", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "to": { + "description": "Node labels this edge may end at.", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "properties": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/PropertyType" + } + } + } + }, + "PropertyType": { + "description": "Contract-level property type. This is the set the three shipped contracts use; extend it when an analyzer starts emitting a new one.", + "type": "string", + "enum": [ + "string", + "integer", + "float", + "boolean", + "string[]", + "integer[]", + "float[]", + "boolean[]" + ] + }, + "prefixRules": { + "description": "Per-language naming rules. Every label an analyzer writes \u2014 node label, merge label, marker label, relationship endpoint, and every label referenced from a Cypher constraint or index \u2014 carries that language's prefix, and every relationship type carries the uppercase form of it. No exceptions: a shared or generic label is a contract violation, because two analyzers loading into one database would collide on it.", + "java": { + "properties": { + "marker_labels": { + "items": { + "type": "string", + "pattern": "^J[A-Z][A-Za-z0-9_]*$" + } + }, + "node_labels": { + "items": { + "properties": { + "label": { + "type": "string", + "pattern": "^J[A-Z][A-Za-z0-9_]*$" + }, + "merge_label": { + "type": "string", + "pattern": "^J[A-Z][A-Za-z0-9_]*$" + }, + "mergeLabel": { + "type": "string", + "pattern": "^J[A-Z][A-Za-z0-9_]*$" + } + } + } + }, + "relationship_types": { + "items": { + "properties": { + "type": { + "type": "string", + "pattern": "^J_[A-Z][A-Z0-9_]*$" + }, + "from": { + "items": { + "type": "string", + "pattern": "^J[A-Z][A-Za-z0-9_]*$" + } + }, + "to": { + "items": { + "type": "string", + "pattern": "^J[A-Z][A-Za-z0-9_]*$" + } + } + } + } + }, + "constraints": { + "items": { + "type": "string", + "not": { + "pattern": "\\([A-Za-z_][A-Za-z0-9_]*:(?!J[A-Z])" + } + } + }, + "indexes": { + "items": { + "type": "string", + "not": { + "pattern": "\\([A-Za-z_][A-Za-z0-9_]*:(?!J[A-Z])" + } + } + } + } + }, + "python": { + "properties": { + "marker_labels": { + "items": { + "type": "string", + "pattern": "^Py[A-Z][A-Za-z0-9_]*$" + } + }, + "node_labels": { + "items": { + "properties": { + "label": { + "type": "string", + "pattern": "^Py[A-Z][A-Za-z0-9_]*$" + }, + "merge_label": { + "type": "string", + "pattern": "^Py[A-Z][A-Za-z0-9_]*$" + }, + "mergeLabel": { + "type": "string", + "pattern": "^Py[A-Z][A-Za-z0-9_]*$" + } + } + } + }, + "relationship_types": { + "items": { + "properties": { + "type": { + "type": "string", + "pattern": "^PY_[A-Z][A-Z0-9_]*$" + }, + "from": { + "items": { + "type": "string", + "pattern": "^Py[A-Z][A-Za-z0-9_]*$" + } + }, + "to": { + "items": { + "type": "string", + "pattern": "^Py[A-Z][A-Za-z0-9_]*$" + } + } + } + } + }, + "constraints": { + "items": { + "type": "string", + "not": { + "pattern": "\\([A-Za-z_][A-Za-z0-9_]*:(?!Py[A-Z])" + } + } + }, + "indexes": { + "items": { + "type": "string", + "not": { + "pattern": "\\([A-Za-z_][A-Za-z0-9_]*:(?!Py[A-Z])" + } + } + } + } + }, + "typescript": { + "properties": { + "marker_labels": { + "items": { + "type": "string", + "pattern": "^TS[A-Z][A-Za-z0-9_]*$" + } + }, + "node_labels": { + "items": { + "properties": { + "label": { + "type": "string", + "pattern": "^TS[A-Z][A-Za-z0-9_]*$" + }, + "merge_label": { + "type": "string", + "pattern": "^TS[A-Z][A-Za-z0-9_]*$" + }, + "mergeLabel": { + "type": "string", + "pattern": "^TS[A-Z][A-Za-z0-9_]*$" + } + } + } + }, + "relationship_types": { + "items": { + "properties": { + "type": { + "type": "string", + "pattern": "^TS_[A-Z][A-Z0-9_]*$" + }, + "from": { + "items": { + "type": "string", + "pattern": "^TS[A-Z][A-Za-z0-9_]*$" + } + }, + "to": { + "items": { + "type": "string", + "pattern": "^TS[A-Z][A-Za-z0-9_]*$" + } + } + } + } + }, + "constraints": { + "items": { + "type": "string", + "not": { + "pattern": "\\([A-Za-z_][A-Za-z0-9_]*:(?!TS[A-Z])" + } + } + }, + "indexes": { + "items": { + "type": "string", + "not": { + "pattern": "\\([A-Za-z_][A-Za-z0-9_]*:(?!TS[A-Z])" + } + } + } + } + } + } + }, + "allOf": [ + { + "if": { + "properties": { + "generator": { + "const": "codeanalyzer-java" + } + }, + "required": [ + "generator" + ] + }, + "then": { + "$ref": "#/$defs/prefixRules/java" + } + }, + { + "if": { + "properties": { + "generator": { + "const": "codeanalyzer-python" + } + }, + "required": [ + "generator" + ] + }, + "then": { + "$ref": "#/$defs/prefixRules/python" + } + }, + { + "if": { + "properties": { + "generator": { + "const": "codeanalyzer-typescript" + } + }, + "required": [ + "generator" + ] + }, + "then": { + "$ref": "#/$defs/prefixRules/typescript" + } + } + ] +} diff --git a/v1/neo4j/java/schema.neo4j.json b/v1/neo4j/java/schema.neo4j.json new file mode 100644 index 0000000..09fff6b --- /dev/null +++ b/v1/neo4j/java/schema.neo4j.json @@ -0,0 +1,517 @@ +{ + "schema_version": "1.0.0", + "generator": "codeanalyzer-java", + "marker_labels": [ + "JEntrypoint" + ], + "node_labels": [ + { + "label": "JApplication", + "merge_label": "JApplication", + "key": "name", + "properties": { + "name": "string", + "schema_version": "string" + } + }, + { + "label": "JCompilationUnit", + "merge_label": "JCompilationUnit", + "key": "file_key", + "properties": { + "file_key": "string", + "file_path": "string", + "package_name": "string", + "content_hash": "string", + "comment_count": "integer", + "is_modified": "boolean", + "_module": "string" + } + }, + { + "label": "JType", + "merge_label": "JSymbol", + "key": "id", + "properties": { + "id": "string", + "name": "string", + "fqn": "string", + "kind": "string", + "modifiers": "string[]", + "annotations": "string[]", + "extends_list": "string[]", + "implements_list": "string[]", + "nested_type_declarations": "string[]", + "is_interface": "boolean", + "is_nested_type": "boolean", + "is_inner_class": "boolean", + "is_local_class": "boolean", + "is_entrypoint_class": "boolean", + "parent_type": "string", + "docstring": "string", + "_module": "string" + } + }, + { + "label": "JCallable", + "merge_label": "JSymbol", + "key": "id", + "properties": { + "id": "string", + "name": "string", + "signature": "string", + "file_path": "string", + "declaration": "string", + "return_type": "string", + "modifiers": "string[]", + "annotations": "string[]", + "thrown_exceptions": "string[]", + "parameter_types": "string[]", + "referenced_types": "string[]", + "accessed_fields": "string[]", + "code": "string", + "code_start_line": "integer", + "start_line": "integer", + "end_line": "integer", + "cyclomatic_complexity": "integer", + "is_constructor": "boolean", + "is_implicit": "boolean", + "is_entrypoint": "boolean", + "docstring": "string", + "_module": "string" + } + }, + { + "label": "JField", + "merge_label": "JField", + "key": "id", + "properties": { + "id": "string", + "name": "string", + "type": "string", + "modifiers": "string[]", + "annotations": "string[]", + "variables": "string[]", + "variable_initializers_json": "string", + "start_line": "integer", + "end_line": "integer", + "docstring": "string", + "_module": "string" + } + }, + { + "label": "JParameter", + "merge_label": "JParameter", + "key": "id", + "properties": { + "id": "string", + "name": "string", + "type": "string", + "annotations": "string[]", + "modifiers": "string[]", + "start_line": "integer", + "end_line": "integer", + "start_column": "integer", + "end_column": "integer", + "_module": "string" + } + }, + { + "label": "JVariable", + "merge_label": "JVariable", + "key": "id", + "properties": { + "id": "string", + "name": "string", + "type": "string", + "initializer": "string", + "start_line": "integer", + "end_line": "integer", + "start_column": "integer", + "end_column": "integer", + "docstring": "string", + "_module": "string" + } + }, + { + "label": "JCallSite", + "merge_label": "JCallSite", + "key": "id", + "properties": { + "id": "string", + "method_name": "string", + "receiver_expr": "string", + "receiver_type": "string", + "return_type": "string", + "callee_signature": "string", + "argument_types": "string[]", + "argument_expr": "string[]", + "is_static_call": "boolean", + "is_constructor_call": "boolean", + "is_public": "boolean", + "is_private": "boolean", + "is_protected": "boolean", + "is_unspecified": "boolean", + "start_line": "integer", + "start_column": "integer", + "end_line": "integer", + "end_column": "integer", + "docstring": "string", + "_module": "string" + } + }, + { + "label": "JEnumConstant", + "merge_label": "JEnumConstant", + "key": "id", + "properties": { + "id": "string", + "name": "string", + "arguments": "string[]", + "_module": "string" + } + }, + { + "label": "JRecordComponent", + "merge_label": "JRecordComponent", + "key": "id", + "properties": { + "id": "string", + "name": "string", + "type": "string", + "modifiers": "string[]", + "annotations": "string[]", + "default_value": "string", + "is_var_args": "boolean", + "docstring": "string", + "_module": "string" + } + }, + { + "label": "JInitializationBlock", + "merge_label": "JInitializationBlock", + "key": "id", + "properties": { + "id": "string", + "file_path": "string", + "code": "string", + "annotations": "string[]", + "thrown_exceptions": "string[]", + "referenced_types": "string[]", + "accessed_fields": "string[]", + "is_static": "boolean", + "cyclomatic_complexity": "integer", + "start_line": "integer", + "end_line": "integer", + "docstring": "string", + "_module": "string" + } + }, + { + "label": "JCrudOperation", + "merge_label": "JCrudOperation", + "key": "id", + "properties": { + "id": "string", + "line_number": "integer", + "operation_type": "string", + "target_table": "string", + "involved_columns": "string[]", + "condition": "string", + "joined_tables": "string[]", + "_module": "string" + } + }, + { + "label": "JCrudQuery", + "merge_label": "JCrudQuery", + "key": "id", + "properties": { + "id": "string", + "line_number": "integer", + "query_type": "string", + "query_arguments": "string[]", + "_module": "string" + } + }, + { + "label": "JComment", + "merge_label": "JComment", + "key": "id", + "properties": { + "id": "string", + "content": "string", + "is_javadoc": "boolean", + "start_line": "integer", + "start_column": "integer", + "end_line": "integer", + "end_column": "integer", + "_module": "string" + } + }, + { + "label": "JPackage", + "merge_label": "JPackage", + "key": "name", + "properties": { + "name": "string" + } + }, + { + "label": "JAnnotation", + "merge_label": "JAnnotation", + "key": "name", + "properties": { + "name": "string" + } + } + ], + "relationship_types": [ + { + "type": "J_HAS_UNIT", + "from": [ + "JApplication" + ], + "to": [ + "JCompilationUnit" + ], + "properties": {} + }, + { + "type": "J_DECLARES_TYPE", + "from": [ + "JCompilationUnit" + ], + "to": [ + "JType" + ], + "properties": {} + }, + { + "type": "J_HAS_NESTED_TYPE", + "from": [ + "JType" + ], + "to": [ + "JType" + ], + "properties": {} + }, + { + "type": "J_HAS_CALLABLE", + "from": [ + "JType" + ], + "to": [ + "JCallable" + ], + "properties": {} + }, + { + "type": "J_HAS_FIELD", + "from": [ + "JType" + ], + "to": [ + "JField" + ], + "properties": {} + }, + { + "type": "J_HAS_PARAMETER", + "from": [ + "JCallable" + ], + "to": [ + "JParameter" + ], + "properties": {} + }, + { + "type": "J_HAS_CALLSITE", + "from": [ + "JCallable", + "JInitializationBlock" + ], + "to": [ + "JCallSite" + ], + "properties": {} + }, + { + "type": "J_DECLARES_VAR", + "from": [ + "JCallable", + "JInitializationBlock" + ], + "to": [ + "JVariable" + ], + "properties": {} + }, + { + "type": "J_HAS_ENUM_CONSTANT", + "from": [ + "JType" + ], + "to": [ + "JEnumConstant" + ], + "properties": {} + }, + { + "type": "J_HAS_RECORD_COMPONENT", + "from": [ + "JType" + ], + "to": [ + "JRecordComponent" + ], + "properties": {} + }, + { + "type": "J_HAS_INIT_BLOCK", + "from": [ + "JType" + ], + "to": [ + "JInitializationBlock" + ], + "properties": {} + }, + { + "type": "J_EXTENDS", + "from": [ + "JType" + ], + "to": [ + "JType" + ], + "properties": {} + }, + { + "type": "J_IMPLEMENTS", + "from": [ + "JType" + ], + "to": [ + "JType" + ], + "properties": {} + }, + { + "type": "J_ANNOTATED_BY", + "from": [ + "JType", + "JCallable", + "JField" + ], + "to": [ + "JAnnotation" + ], + "properties": {} + }, + { + "type": "J_IMPORTS", + "from": [ + "JCompilationUnit" + ], + "to": [ + "JType", + "JPackage" + ], + "properties": { + "path": "string", + "is_static": "boolean", + "is_wildcard": "boolean" + } + }, + { + "type": "J_RESOLVES_TO", + "from": [ + "JCallSite" + ], + "to": [ + "JCallable" + ], + "properties": {} + }, + { + "type": "J_CALLS", + "from": [ + "JCallable" + ], + "to": [ + "JCallable" + ], + "properties": { + "type": "string", + "weight": "integer", + "source_kind": "string", + "destination_kind": "string" + } + }, + { + "type": "J_HAS_CRUD_OPERATION", + "from": [ + "JCallable", + "JCallSite" + ], + "to": [ + "JCrudOperation" + ], + "properties": {} + }, + { + "type": "J_HAS_CRUD_QUERY", + "from": [ + "JCallable", + "JCallSite" + ], + "to": [ + "JCrudQuery" + ], + "properties": {} + }, + { + "type": "J_HAS_COMMENT", + "from": [ + "JCompilationUnit", + "JType", + "JCallable", + "JField", + "JCallSite", + "JVariable", + "JRecordComponent", + "JInitializationBlock" + ], + "to": [ + "JComment" + ], + "properties": {} + } + ], + "constraints": [ + "CREATE CONSTRAINT j_symbol_id IF NOT EXISTS FOR (s:JSymbol) REQUIRE s.id IS UNIQUE", + "CREATE CONSTRAINT j_application_name IF NOT EXISTS FOR (a:JApplication) REQUIRE a.name IS UNIQUE", + "CREATE CONSTRAINT j_compilation_unit_key IF NOT EXISTS FOR (c:JCompilationUnit) REQUIRE c.file_key IS UNIQUE", + "CREATE CONSTRAINT j_package_name IF NOT EXISTS FOR (p:JPackage) REQUIRE p.name IS UNIQUE", + "CREATE CONSTRAINT j_annotation_name IF NOT EXISTS FOR (an:JAnnotation) REQUIRE an.name IS UNIQUE", + "CREATE CONSTRAINT j_callsite_id IF NOT EXISTS FOR (cs:JCallSite) REQUIRE cs.id IS UNIQUE", + "CREATE CONSTRAINT j_field_id IF NOT EXISTS FOR (f:JField) REQUIRE f.id IS UNIQUE", + "CREATE CONSTRAINT j_parameter_id IF NOT EXISTS FOR (p:JParameter) REQUIRE p.id IS UNIQUE", + "CREATE CONSTRAINT j_variable_id IF NOT EXISTS FOR (v:JVariable) REQUIRE v.id IS UNIQUE", + "CREATE CONSTRAINT j_enum_constant_id IF NOT EXISTS FOR (e:JEnumConstant) REQUIRE e.id IS UNIQUE", + "CREATE CONSTRAINT j_record_component_id IF NOT EXISTS FOR (r:JRecordComponent) REQUIRE r.id IS UNIQUE", + "CREATE CONSTRAINT j_init_block_id IF NOT EXISTS FOR (ib:JInitializationBlock) REQUIRE ib.id IS UNIQUE", + "CREATE CONSTRAINT j_crud_operation_id IF NOT EXISTS FOR (co:JCrudOperation) REQUIRE co.id IS UNIQUE", + "CREATE CONSTRAINT j_crud_query_id IF NOT EXISTS FOR (cq:JCrudQuery) REQUIRE cq.id IS UNIQUE", + "CREATE CONSTRAINT j_comment_id IF NOT EXISTS FOR (cm:JComment) REQUIRE cm.id IS UNIQUE" + ], + "indexes": [ + "CREATE INDEX j_callable_name IF NOT EXISTS FOR (c:JCallable) ON (c.name)", + "CREATE INDEX j_type_name IF NOT EXISTS FOR (t:JType) ON (t.name)", + "CREATE INDEX j_annotation_name_idx IF NOT EXISTS FOR (an:JAnnotation) ON (an.name)", + "CREATE FULLTEXT INDEX j_code_fts IF NOT EXISTS FOR (c:JCallable) ON EACH [c.code, c.docstring]" + ] +} diff --git a/v1/neo4j/python/schema.neo4j.json b/v1/neo4j/python/schema.neo4j.json new file mode 100644 index 0000000..7b0fd42 --- /dev/null +++ b/v1/neo4j/python/schema.neo4j.json @@ -0,0 +1,289 @@ +{ + "schema_version": "1.2.0", + "generator": "codeanalyzer-python", + "marker_labels": [], + "node_labels": [ + { + "label": "PyApplication", + "merge_label": "PyApplication", + "key": "name", + "properties": { + "name": "string", + "schema_version": "string", + "analyzer_name": "string", + "analyzer_version": "string", + "repo_uri": "string", + "source_revision": "string", + "repo_dirty": "boolean" + } + }, + { + "label": "PyModule", + "merge_label": "PyModule", + "key": "file_key", + "properties": { + "file_key": "string", + "module_name": "string", + "content_hash": "string", + "last_modified": "float", + "file_size": "integer", + "_module": "string" + } + }, + { + "label": "PyClass", + "merge_label": "PySymbol", + "key": "signature", + "properties": { + "signature": "string", + "name": "string", + "code": "string", + "base_classes": "string[]", + "docstring": "string", + "start_line": "integer", + "end_line": "integer", + "_module": "string" + } + }, + { + "label": "PyCallable", + "merge_label": "PySymbol", + "key": "signature", + "properties": { + "signature": "string", + "name": "string", + "path": "string", + "return_type": "string", + "cyclomatic_complexity": "integer", + "code": "string", + "code_start_line": "integer", + "start_line": "integer", + "end_line": "integer", + "docstring": "string", + "decorators": "string[]", + "parameters_json": "string", + "accessed_symbols_json": "string", + "_module": "string" + } + }, + { + "label": "PyExternal", + "merge_label": "PySymbol", + "key": "signature", + "properties": { + "signature": "string", + "name": "string", + "module": "string" + } + }, + { + "label": "PyPackage", + "merge_label": "PyPackage", + "key": "name", + "properties": { + "name": "string" + } + }, + { + "label": "PyDecorator", + "merge_label": "PyDecorator", + "key": "name", + "properties": { + "name": "string" + } + }, + { + "label": "PyCallSite", + "merge_label": "PyCallSite", + "key": "id", + "properties": { + "id": "string", + "method_name": "string", + "receiver_expr": "string", + "receiver_type": "string", + "argument_types": "string[]", + "arguments_json": "string", + "return_type": "string", + "callee_signature": "string", + "is_constructor_call": "boolean", + "start_line": "integer", + "start_column": "integer", + "end_line": "integer", + "end_column": "integer", + "_module": "string" + } + }, + { + "label": "PyAttribute", + "merge_label": "PyAttribute", + "key": "id", + "properties": { + "id": "string", + "name": "string", + "type": "string", + "initializer": "string", + "docstring": "string", + "start_line": "integer", + "end_line": "integer", + "_module": "string" + } + }, + { + "label": "PyVariable", + "merge_label": "PyVariable", + "key": "id", + "properties": { + "id": "string", + "name": "string", + "type": "string", + "initializer": "string", + "scope": "string", + "start_line": "integer", + "end_line": "integer", + "_module": "string" + } + } + ], + "relationship_types": [ + { + "type": "PY_HAS_MODULE", + "from": [ + "PyApplication" + ], + "to": [ + "PyModule" + ], + "properties": {} + }, + { + "type": "PY_DECLARES", + "from": [ + "PyModule", + "PyClass", + "PyCallable" + ], + "to": [ + "PyClass", + "PyCallable" + ], + "properties": {} + }, + { + "type": "PY_HAS_METHOD", + "from": [ + "PyClass" + ], + "to": [ + "PyCallable" + ], + "properties": {} + }, + { + "type": "PY_HAS_ATTRIBUTE", + "from": [ + "PyClass" + ], + "to": [ + "PyAttribute" + ], + "properties": {} + }, + { + "type": "PY_DECLARES_VAR", + "from": [ + "PyModule", + "PyCallable" + ], + "to": [ + "PyVariable" + ], + "properties": {} + }, + { + "type": "PY_HAS_CALLSITE", + "from": [ + "PyCallable" + ], + "to": [ + "PyCallSite" + ], + "properties": {} + }, + { + "type": "PY_RESOLVES_TO", + "from": [ + "PyCallSite" + ], + "to": [ + "PyCallable", + "PyExternal" + ], + "properties": {} + }, + { + "type": "PY_CALLS", + "from": [ + "PyCallable", + "PyExternal" + ], + "to": [ + "PyCallable", + "PyExternal" + ], + "properties": { + "weight": "integer", + "provenance": "string[]" + } + }, + { + "type": "PY_EXTENDS", + "from": [ + "PyClass" + ], + "to": [ + "PyClass" + ], + "properties": {} + }, + { + "type": "PY_IMPORTS", + "from": [ + "PyModule" + ], + "to": [ + "PyModule", + "PyPackage" + ], + "properties": { + "spellings": "string[]", + "imported_names": "string[]", + "aliases": "string[]" + } + }, + { + "type": "PY_DECORATED_BY", + "from": [ + "PyCallable" + ], + "to": [ + "PyDecorator" + ], + "properties": {} + } + ], + "constraints": [ + "CREATE CONSTRAINT pyapplication_name IF NOT EXISTS FOR (x:PyApplication) REQUIRE x.name IS UNIQUE", + "CREATE CONSTRAINT pymodule_file_key IF NOT EXISTS FOR (x:PyModule) REQUIRE x.file_key IS UNIQUE", + "CREATE CONSTRAINT pysymbol_signature IF NOT EXISTS FOR (x:PySymbol) REQUIRE x.signature IS UNIQUE", + "CREATE CONSTRAINT pypackage_name IF NOT EXISTS FOR (x:PyPackage) REQUIRE x.name IS UNIQUE", + "CREATE CONSTRAINT pydecorator_name IF NOT EXISTS FOR (x:PyDecorator) REQUIRE x.name IS UNIQUE", + "CREATE CONSTRAINT pycallsite_id IF NOT EXISTS FOR (x:PyCallSite) REQUIRE x.id IS UNIQUE", + "CREATE CONSTRAINT pyattribute_id IF NOT EXISTS FOR (x:PyAttribute) REQUIRE x.id IS UNIQUE", + "CREATE CONSTRAINT pyvariable_id IF NOT EXISTS FOR (x:PyVariable) REQUIRE x.id IS UNIQUE" + ], + "indexes": [ + "CREATE INDEX py_callable_name IF NOT EXISTS FOR (c:PyCallable) ON (c.name)", + "CREATE INDEX py_class_name IF NOT EXISTS FOR (c:PyClass) ON (c.name)", + "CREATE FULLTEXT INDEX py_code_fts IF NOT EXISTS FOR (c:PyCallable) ON EACH [c.code, c.docstring]" + ] +} diff --git a/v1/neo4j/typescript/schema.neo4j.json b/v1/neo4j/typescript/schema.neo4j.json new file mode 100644 index 0000000..131f384 --- /dev/null +++ b/v1/neo4j/typescript/schema.neo4j.json @@ -0,0 +1,470 @@ +{ + "schema_version": "1.0.0", + "generator": "codeanalyzer-typescript", + "marker_labels": [ + "TSEntrypoint" + ], + "node_labels": [ + { + "label": "TSApplication", + "mergeLabel": "TSApplication", + "key": "name", + "properties": { + "name": "string", + "schema_version": "string" + } + }, + { + "label": "TSModule", + "mergeLabel": "TSModule", + "key": "file_key", + "properties": { + "file_key": "string", + "module_name": "string", + "is_tsx": "boolean", + "is_declaration_file": "boolean", + "content_hash": "string", + "last_modified": "integer", + "file_size": "integer", + "_module": "string" + } + }, + { + "label": "TSClass", + "mergeLabel": "TSSymbol", + "key": "signature", + "properties": { + "signature": "string", + "name": "string", + "code": "string", + "base_classes": "string[]", + "implements_types": "string[]", + "type_parameter_names": "string[]", + "docstring": "string", + "is_abstract": "boolean", + "is_exported": "boolean", + "is_ambient": "boolean", + "start_line": "integer", + "end_line": "integer", + "framework": "string", + "detection_source": "string", + "route_path": "string", + "http_methods": "string[]", + "entrypoint_count": "integer", + "_module": "string" + } + }, + { + "label": "TSInterface", + "mergeLabel": "TSSymbol", + "key": "signature", + "properties": { + "signature": "string", + "name": "string", + "code": "string", + "base_classes": "string[]", + "type_parameter_names": "string[]", + "call_signatures": "string[]", + "index_signatures": "string[]", + "docstring": "string", + "is_exported": "boolean", + "is_ambient": "boolean", + "start_line": "integer", + "end_line": "integer", + "_module": "string" + } + }, + { + "label": "TSEnum", + "mergeLabel": "TSSymbol", + "key": "signature", + "properties": { + "signature": "string", + "name": "string", + "code": "string", + "member_names": "string[]", + "member_values": "string[]", + "docstring": "string", + "is_const": "boolean", + "is_exported": "boolean", + "is_ambient": "boolean", + "start_line": "integer", + "end_line": "integer", + "_module": "string" + } + }, + { + "label": "TSTypeAlias", + "mergeLabel": "TSSymbol", + "key": "signature", + "properties": { + "signature": "string", + "name": "string", + "code": "string", + "aliased_type": "string", + "type_parameter_names": "string[]", + "docstring": "string", + "is_exported": "boolean", + "is_ambient": "boolean", + "start_line": "integer", + "end_line": "integer", + "_module": "string" + } + }, + { + "label": "TSNamespace", + "mergeLabel": "TSSymbol", + "key": "signature", + "properties": { + "signature": "string", + "name": "string", + "docstring": "string", + "is_exported": "boolean", + "is_ambient": "boolean", + "start_line": "integer", + "end_line": "integer", + "_module": "string" + } + }, + { + "label": "TSCallable", + "mergeLabel": "TSSymbol", + "key": "signature", + "properties": { + "signature": "string", + "name": "string", + "path": "string", + "kind": "string", + "return_type": "string", + "cyclomatic_complexity": "integer", + "code": "string", + "code_start_line": "integer", + "start_line": "integer", + "end_line": "integer", + "accessibility": "string", + "accessor_kind": "string", + "docstring": "string", + "type_parameter_names": "string[]", + "parameters_json": "string", + "accessed_symbols_json": "string", + "is_static": "boolean", + "is_abstract": "boolean", + "is_async": "boolean", + "is_generator": "boolean", + "is_optional": "boolean", + "is_readonly": "boolean", + "is_exported": "boolean", + "is_ambient": "boolean", + "is_implicit": "boolean", + "framework": "string", + "detection_source": "string", + "route_path": "string", + "http_methods": "string[]", + "entrypoint_count": "integer", + "_module": "string" + } + }, + { + "label": "TSExternal", + "mergeLabel": "TSSymbol", + "key": "signature", + "properties": { + "signature": "string", + "name": "string", + "module": "string" + } + }, + { + "label": "TSAnonymousCallable", + "mergeLabel": "TSSymbol", + "key": "signature", + "properties": { + "signature": "string", + "name": "string", + "path": "string", + "start_line": "integer", + "start_column": "integer", + "_module": "string" + } + }, + { + "label": "TSPackage", + "mergeLabel": "TSPackage", + "key": "name", + "properties": { + "name": "string" + } + }, + { + "label": "TSDecorator", + "mergeLabel": "TSDecorator", + "key": "qualified_name", + "properties": { + "qualified_name": "string", + "name": "string" + } + }, + { + "label": "TSCallSite", + "mergeLabel": "TSCallSite", + "key": "id", + "properties": { + "id": "string", + "method_name": "string", + "receiver_expr": "string", + "receiver_type": "string", + "argument_types": "string[]", + "type_arguments": "string[]", + "return_type": "string", + "callee_signature": "string", + "is_constructor_call": "boolean", + "is_optional_chain": "boolean", + "start_line": "integer", + "start_column": "integer", + "end_line": "integer", + "end_column": "integer", + "_module": "string" + } + }, + { + "label": "TSAttribute", + "mergeLabel": "TSAttribute", + "key": "id", + "properties": { + "id": "string", + "name": "string", + "type": "string", + "initializer": "string", + "accessibility": "string", + "docstring": "string", + "is_static": "boolean", + "is_readonly": "boolean", + "is_optional": "boolean", + "is_abstract": "boolean", + "start_line": "integer", + "end_line": "integer", + "_module": "string" + } + }, + { + "label": "TSVariable", + "mergeLabel": "TSVariable", + "key": "id", + "properties": { + "id": "string", + "name": "string", + "type": "string", + "initializer": "string", + "scope": "string", + "declaration_kind": "string", + "is_readonly": "boolean", + "is_exported": "boolean", + "start_line": "integer", + "end_line": "integer", + "_module": "string" + } + } + ], + "relationship_types": [ + { + "type": "TS_HAS_MODULE", + "from": [ + "TSApplication" + ], + "to": [ + "TSModule" + ], + "properties": {} + }, + { + "type": "TS_DECLARES", + "from": [ + "TSModule", + "TSNamespace", + "TSClass", + "TSCallable" + ], + "to": [ + "TSClass", + "TSInterface", + "TSEnum", + "TSTypeAlias", + "TSNamespace", + "TSCallable" + ], + "properties": {} + }, + { + "type": "TS_HAS_METHOD", + "from": [ + "TSClass", + "TSInterface" + ], + "to": [ + "TSCallable" + ], + "properties": {} + }, + { + "type": "TS_HAS_ATTRIBUTE", + "from": [ + "TSClass", + "TSInterface" + ], + "to": [ + "TSAttribute" + ], + "properties": {} + }, + { + "type": "TS_DECLARES_VAR", + "from": [ + "TSModule", + "TSNamespace", + "TSCallable" + ], + "to": [ + "TSVariable" + ], + "properties": {} + }, + { + "type": "TS_HAS_CALLSITE", + "from": [ + "TSCallable" + ], + "to": [ + "TSCallSite" + ], + "properties": {} + }, + { + "type": "TS_RESOLVES_TO", + "from": [ + "TSCallSite" + ], + "to": [ + "TSCallable", + "TSExternal" + ], + "properties": {} + }, + { + "type": "TS_CALLS", + "from": [ + "TSCallable" + ], + "to": [ + "TSCallable", + "TSExternal" + ], + "properties": { + "weight": "integer", + "provenance": "string[]", + "dispatch": "string", + "external": "boolean", + "module": "string" + } + }, + { + "type": "TS_EXTENDS", + "from": [ + "TSClass", + "TSInterface" + ], + "to": [ + "TSClass", + "TSInterface" + ], + "properties": {} + }, + { + "type": "TS_IMPLEMENTS", + "from": [ + "TSClass" + ], + "to": [ + "TSInterface" + ], + "properties": {} + }, + { + "type": "TS_IMPORTS", + "from": [ + "TSModule" + ], + "to": [ + "TSModule", + "TSPackage" + ], + "properties": { + "imported_names": "string[]", + "import_kinds": "string[]", + "is_type_only": "boolean" + } + }, + { + "type": "TS_RE_EXPORTS", + "from": [ + "TSModule" + ], + "to": [ + "TSModule", + "TSPackage" + ], + "properties": {} + }, + { + "type": "TS_MEMBER_OF", + "from": [ + "TSExternal" + ], + "to": [ + "TSPackage" + ], + "properties": {} + }, + { + "type": "TS_DECORATED_BY", + "from": [ + "TSClass", + "TSCallable", + "TSAttribute" + ], + "to": [ + "TSDecorator" + ], + "properties": { + "positional_arguments": "string[]", + "keyword_arguments_json": "string", + "start_line": "integer", + "end_line": "integer" + } + } + ], + "constraints": [ + "CREATE CONSTRAINT application_name IF NOT EXISTS FOR (x:TSApplication) REQUIRE x.name IS UNIQUE", + "CREATE CONSTRAINT module_file_key IF NOT EXISTS FOR (x:TSModule) REQUIRE x.file_key IS UNIQUE", + "CREATE CONSTRAINT symbol_signature IF NOT EXISTS FOR (x:TSSymbol) REQUIRE x.signature IS UNIQUE", + "CREATE CONSTRAINT package_name IF NOT EXISTS FOR (x:TSPackage) REQUIRE x.name IS UNIQUE", + "CREATE CONSTRAINT decorator_qualified_name IF NOT EXISTS FOR (x:TSDecorator) REQUIRE x.qualified_name IS UNIQUE", + "CREATE CONSTRAINT callsite_id IF NOT EXISTS FOR (x:TSCallSite) REQUIRE x.id IS UNIQUE", + "CREATE CONSTRAINT attribute_id IF NOT EXISTS FOR (x:TSAttribute) REQUIRE x.id IS UNIQUE", + "CREATE CONSTRAINT variable_id IF NOT EXISTS FOR (x:TSVariable) REQUIRE x.id IS UNIQUE" + ], + "indexes": [ + "CREATE INDEX callable_name IF NOT EXISTS FOR (c:TSCallable) ON (c.name)", + "CREATE INDEX decorator_name IF NOT EXISTS FOR (d:TSDecorator) ON (d.name)", + "CREATE FULLTEXT INDEX code_fts IF NOT EXISTS FOR (c:TSCallable) ON EACH [c.code, c.docstring]" + ], + "x-cldk": { + "normalized": true, + "origin": { + "repo": "codellm-devkit/codeanalyzer-typescript", + "release": "v0.4.3", + "path": "schema.neo4j.json", + "schemaVersion": "1.0.0" + }, + "note": "Not byte-identical to the release. codeanalyzer-typescript v0.4.3 emitted every label and relationship type unprefixed; this copy applies the TS/TS_ prefix rule to labels, relationship types, and the label positions inside the Cypher statements. Property names, cardinalities, constraint and index identifiers, and schema_version are untouched." + } +} diff --git a/v2/iac/json/analysis.l1.sample.json b/v2/iac/json/analysis.l1.sample.json new file mode 100644 index 0000000..451794d --- /dev/null +++ b/v2/iac/json/analysis.l1.sample.json @@ -0,0 +1,240 @@ +{ + "schema_version": "2.0.0", + "language": "iac", + "max_level": 1, + "analyzer": {"name": "codeanalyzer-iac", "version": "0.1.0"}, + "application": { + "id": "can://iac/payments", + "kind": "application", + "artifacts": { + "README.md": { + "id": "can://artifact/payments/README.md", + "kind": "artifact", + "path": "README.md", + "format": "text", + "source": "payments infrastructure\n", + "sha256": "79accdf71a72e00a8b25fe1284094368e885e2fe55db0cf686a83c5359bf43df", + "size_bytes": 24, + "config_keys": {}, + "aliases": [] + }, + "charts/api/Chart.yaml": { + "id": "can://artifact/payments/charts/api/Chart.yaml", + "kind": "artifact", + "path": "charts/api/Chart.yaml", + "format": "yaml", + "source": "apiVersion: v2\nname: api\nversion: 1.2.3\ntype: application\n", + "sha256": "4a0bf85edced2838bb82ae1a52bf8830a718462ff54f4c6fb11ce539fabb53af", + "size_bytes": 58, + "config_keys": {}, + "aliases": [{ + "id": "can://iac/payments/helm/chart/charts/api", + "kind": "helm_chart", + "target": "can://artifact/payments/charts/api/Chart.yaml" + }], + "iac": { + "dialect": "helm", + "kind": "helm_chart", + "status": "complete", + "api_version": "v2", + "name": "api", + "version": "1.2.3", + "chart_type": "application", + "maintainers": [], + "keywords": [], + "sources": [], + "annotations": {}, + "dependencies": {}, + "renders": {} + } + }, + "charts/api/charts/postgresql/Chart.yaml": { + "id": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml", + "kind": "artifact", + "path": "charts/api/charts/postgresql/Chart.yaml", + "format": "yaml", + "source": "apiVersion: v2\nname: postgresql\nversion: 12.1.0\ntype: application\n", + "sha256": "57ea5ae02fdfc54e1a02eac39e9b1d98d289c6f182c77d90d80d7a9124d11161", + "size_bytes": 66, + "config_keys": {}, + "aliases": [{ + "id": "can://iac/payments/helm/chart/charts/api/charts/postgresql", + "kind": "helm_chart", + "target": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml" + }], + "iac": { + "dialect": "helm", + "kind": "helm_chart", + "status": "complete", + "api_version": "v2", + "name": "postgresql", + "version": "12.1.0", + "chart_type": "application", + "maintainers": [], + "keywords": [], + "sources": [], + "annotations": {}, + "dependencies": {}, + "renders": {} + } + }, + "charts/api/values.yaml": { + "id": "can://artifact/payments/charts/api/values.yaml", + "kind": "artifact", + "path": "charts/api/values.yaml", + "format": "yaml", + "source": "image:\n tag: 1.2.3x\n", + "sha256": "3d3ecdd860e9d3e2fedf29a4fb6e1667c8295ed8e500aae00901ce43e79bf497", + "size_bytes": 21, + "config_keys": { + "image.tag": { + "id": "can://artifact/payments/charts/api/values.yaml@key/image.tag", + "kind": "config_key", + "name": "tag", + "path": "image.tag", + "span": {"start": [2, 3], "end": [2, 14], "bytes": [9, 20]}, + "iac": {"kind": "helm_value"} + } + }, + "aliases": [], + "iac": { + "dialect": "helm", + "kind": "helm_values", + "status": "complete", + "roles": ["default"] + } + }, + "charts/api/templates/deployment.yaml": { + "id": "can://artifact/payments/charts/api/templates/deployment.yaml", + "kind": "artifact", + "path": "charts/api/templates/deployment.yaml", + "format": "yaml", + "source": "apiVersion: v1\nkind: Deployment\nmetadata:\n a: \n x: hello!!\n name: {{include \"api.fullname\" .}}\nspec:\n template:\n containers: # comment!\n image: {{.Values.image.tag }}\n lookup: {{lookup \"\" \"v1\" \"ConfigMap\" \"default\" \"settings\"}}\n{{define \"api.fullname\"}}api{{end}}\n", + "sha256": "e8367060ea9d4118f7bf38113b4fdccf7ea2d5f24442c580b07e487dbcdb103e", + "size_bytes": 285, + "config_keys": {}, + "aliases": [], + "iac": { + "dialect": "helm", + "kind": "helm_template", + "status": "complete", + "roles": ["resource"], + "named_templates": { + "12:1": { + "id": "can://iac/payments/helm/charts/api/templates/deployment.yaml/named-template/api.fullname", + "kind": "helm_named_template", + "name": "api.fullname", + "span": {"start": [12, 1], "end": [12, 36], "bytes": [249, 284]} + } + }, + "template_calls": { + "6:11": { + "id": "can://iac/payments/helm/charts/api/templates/deployment.yaml/template-call@6:11", + "kind": "helm_template_call", + "call_kind": "include", + "name_expression": "api.fullname", + "span": {"start": [6, 11], "end": [6, 39], "bytes": [73, 101]} + } + }, + "value_references": { + "10:16": { + "id": "can://iac/payments/helm/charts/api/templates/deployment.yaml/value-reference@10:16", + "kind": "helm_value_reference", + "path_expression": "image.tag", + "span": {"start": [10, 16], "end": [10, 33], "bytes": [162, 179]} + } + }, + "resource_templates": { + "1:1": { + "id": "can://iac/payments/helm/charts/api/templates/deployment.yaml/resource-template@1:1", + "kind": "helm_resource_template", + "document_index": 0, + "span": {"start": [1, 1], "end": [12, 1], "bytes": [0, 249]} + } + }, + "lookup_references": { + "11:15": { + "id": "can://iac/payments/helm/charts/api/templates/deployment.yaml/lookup-reference@11:15", + "kind": "helm_lookup_reference", + "group_expression": "\"\"", + "version_expression": "\"v1\"", + "resource_kind_expression": "\"ConfigMap\"", + "namespace_expression": "\"default\"", + "name_expression": "\"settings\"", + "span": {"start": [11, 15], "end": [11, 66], "bytes": [197, 248]} + } + } + } + } + }, + "packages": {}, + "external_chart_references": {}, + "kubernetes_resource_addresses": {}, + "diagnostics": {}, + "edges": { + "has_artifact": { + "README.md": {"src": "can://iac/payments", "dst": "can://artifact/payments/README.md"}, + "charts/api/Chart.yaml": {"src": "can://iac/payments", "dst": "can://artifact/payments/charts/api/Chart.yaml"}, + "charts/api/charts/postgresql/Chart.yaml": {"src": "can://iac/payments", "dst": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml"}, + "charts/api/values.yaml": {"src": "can://iac/payments", "dst": "can://artifact/payments/charts/api/values.yaml"}, + "charts/api/templates/deployment.yaml": {"src": "can://iac/payments", "dst": "can://artifact/payments/charts/api/templates/deployment.yaml"} + }, + "defines_config": { + "charts/api/values.yaml@key/image.tag": { + "src": "can://artifact/payments/charts/api/values.yaml", + "dst": "can://artifact/payments/charts/api/values.yaml@key/image.tag" + } + }, + "iac_has_alias": { + "charts/api/Chart.yaml@helm-chart": { + "src": "can://artifact/payments/charts/api/Chart.yaml", + "dst": "can://iac/payments/helm/chart/charts/api" + }, + "charts/api/charts/postgresql/Chart.yaml@helm-chart": { + "src": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml", + "dst": "can://iac/payments/helm/chart/charts/api/charts/postgresql" + } + }, + "iac_alias_of": { + "charts/api/Chart.yaml@helm-chart": { + "src": "can://iac/payments/helm/chart/charts/api", + "dst": "can://artifact/payments/charts/api/Chart.yaml" + }, + "charts/api/charts/postgresql/Chart.yaml@helm-chart": { + "src": "can://iac/payments/helm/chart/charts/api/charts/postgresql", + "dst": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml" + } + }, + "iac_defines_template": { + "charts/api/templates/deployment.yaml@12:1": { + "src": "can://artifact/payments/charts/api/templates/deployment.yaml", + "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/named-template/api.fullname" + } + }, + "iac_has_template_call": { + "charts/api/templates/deployment.yaml@6:11": { + "src": "can://artifact/payments/charts/api/templates/deployment.yaml", + "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/template-call@6:11" + } + }, + "iac_has_value_reference": { + "charts/api/templates/deployment.yaml@10:16": { + "src": "can://artifact/payments/charts/api/templates/deployment.yaml", + "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/value-reference@10:16" + } + }, + "iac_has_resource_template": { + "charts/api/templates/deployment.yaml@1:1": { + "src": "can://artifact/payments/charts/api/templates/deployment.yaml", + "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/resource-template@1:1" + } + }, + "iac_has_lookup_reference": { + "charts/api/templates/deployment.yaml@11:15": { + "src": "can://artifact/payments/charts/api/templates/deployment.yaml", + "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/lookup-reference@11:15" + } + } + } + } +} diff --git a/v2/iac/json/analysis.l2.sample.json b/v2/iac/json/analysis.l2.sample.json new file mode 100644 index 0000000..61d1220 --- /dev/null +++ b/v2/iac/json/analysis.l2.sample.json @@ -0,0 +1,279 @@ +{ + "schema_version": "2.0.0", + "language": "iac", + "max_level": 2, + "analyzer": {"name": "codeanalyzer-iac", "version": "0.1.0"}, + "application": { + "id": "can://iac/payments", + "kind": "application", + "artifacts": { + "README.md": { + "id": "can://artifact/payments/README.md", + "kind": "artifact", + "path": "README.md", + "format": "text", + "source": "payments infrastructure\n", + "sha256": "79accdf71a72e00a8b25fe1284094368e885e2fe55db0cf686a83c5359bf43df", + "size_bytes": 24, + "config_keys": {}, + "aliases": [] + }, + "charts/api/Chart.yaml": { + "id": "can://artifact/payments/charts/api/Chart.yaml", + "kind": "artifact", + "path": "charts/api/Chart.yaml", + "format": "yaml", + "source": "apiVersion: v2\nname: api\nversion: 1.2.3\ntype: application\n", + "sha256": "4a0bf85edced2838bb82ae1a52bf8830a718462ff54f4c6fb11ce539fabb53af", + "size_bytes": 58, + "config_keys": {}, + "aliases": [{ + "id": "can://iac/payments/helm/chart/charts/api", + "kind": "helm_chart", + "target": "can://artifact/payments/charts/api/Chart.yaml" + }], + "iac": { + "dialect": "helm", + "kind": "helm_chart", + "status": "complete", + "api_version": "v2", + "name": "api", + "version": "1.2.3", + "chart_type": "application", + "maintainers": [], + "keywords": [], + "sources": [], + "annotations": {}, + "dependencies": {}, + "renders": {} + } + }, + "charts/api/charts/postgresql/Chart.yaml": { + "id": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml", + "kind": "artifact", + "path": "charts/api/charts/postgresql/Chart.yaml", + "format": "yaml", + "source": "apiVersion: v2\nname: postgresql\nversion: 12.1.0\ntype: application\n", + "sha256": "57ea5ae02fdfc54e1a02eac39e9b1d98d289c6f182c77d90d80d7a9124d11161", + "size_bytes": 66, + "config_keys": {}, + "aliases": [{ + "id": "can://iac/payments/helm/chart/charts/api/charts/postgresql", + "kind": "helm_chart", + "target": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml" + }], + "iac": { + "dialect": "helm", + "kind": "helm_chart", + "status": "complete", + "api_version": "v2", + "name": "postgresql", + "version": "12.1.0", + "chart_type": "application", + "maintainers": [], + "keywords": [], + "sources": [], + "annotations": {}, + "dependencies": {}, + "renders": {} + } + }, + "charts/api/values.yaml": { + "id": "can://artifact/payments/charts/api/values.yaml", + "kind": "artifact", + "path": "charts/api/values.yaml", + "format": "yaml", + "source": "image:\n tag: 1.2.3x\n", + "sha256": "3d3ecdd860e9d3e2fedf29a4fb6e1667c8295ed8e500aae00901ce43e79bf497", + "size_bytes": 21, + "config_keys": { + "image.tag": { + "id": "can://artifact/payments/charts/api/values.yaml@key/image.tag", + "kind": "config_key", + "name": "tag", + "path": "image.tag", + "span": {"start": [2, 3], "end": [2, 14], "bytes": [9, 20]}, + "iac": {"kind": "helm_value"} + } + }, + "aliases": [], + "iac": { + "dialect": "helm", + "kind": "helm_values", + "status": "complete", + "roles": ["default"] + } + }, + "charts/api/templates/deployment.yaml": { + "id": "can://artifact/payments/charts/api/templates/deployment.yaml", + "kind": "artifact", + "path": "charts/api/templates/deployment.yaml", + "format": "yaml", + "source": "apiVersion: v1\nkind: Deployment\nmetadata:\n a: \n x: hello!!\n name: {{include \"api.fullname\" .}}\nspec:\n template:\n containers: # comment!\n image: {{.Values.image.tag }}\n lookup: {{lookup \"\" \"v1\" \"ConfigMap\" \"default\" \"settings\"}}\n{{define \"api.fullname\"}}api{{end}}\n", + "sha256": "e8367060ea9d4118f7bf38113b4fdccf7ea2d5f24442c580b07e487dbcdb103e", + "size_bytes": 285, + "config_keys": {}, + "aliases": [], + "iac": { + "dialect": "helm", + "kind": "helm_template", + "status": "complete", + "roles": ["resource"], + "named_templates": { + "12:1": { + "id": "can://iac/payments/helm/charts/api/templates/deployment.yaml/named-template/api.fullname", + "kind": "helm_named_template", + "name": "api.fullname", + "span": {"start": [12, 1], "end": [12, 36], "bytes": [249, 284]} + } + }, + "template_calls": { + "6:11": { + "id": "can://iac/payments/helm/charts/api/templates/deployment.yaml/template-call@6:11", + "kind": "helm_template_call", + "call_kind": "include", + "name_expression": "api.fullname", + "target_id": "can://iac/payments/helm/charts/api/templates/deployment.yaml/named-template/api.fullname", + "span": {"start": [6, 11], "end": [6, 39], "bytes": [73, 101]} + } + }, + "value_references": { + "10:16": { + "id": "can://iac/payments/helm/charts/api/templates/deployment.yaml/value-reference@10:16", + "kind": "helm_value_reference", + "path_expression": "image.tag", + "target_id": "can://artifact/payments/charts/api/values.yaml@key/image.tag", + "span": {"start": [10, 16], "end": [10, 33], "bytes": [162, 179]} + } + }, + "resource_templates": { + "1:1": { + "id": "can://iac/payments/helm/charts/api/templates/deployment.yaml/resource-template@1:1", + "kind": "helm_resource_template", + "document_index": 0, + "span": {"start": [1, 1], "end": [12, 1], "bytes": [0, 249]} + } + }, + "lookup_references": { + "11:15": { + "id": "can://iac/payments/helm/charts/api/templates/deployment.yaml/lookup-reference@11:15", + "kind": "helm_lookup_reference", + "group_expression": "\"\"", + "version_expression": "\"v1\"", + "resource_kind_expression": "\"ConfigMap\"", + "namespace_expression": "\"default\"", + "name_expression": "\"settings\"", + "span": {"start": [11, 15], "end": [11, 66], "bytes": [197, 248]} + } + } + } + } + }, + "packages": {}, + "external_chart_references": { + "bitnami/postgresql": { + "id": "can://iac/payments/helm/chart-reference/bitnami/postgresql", + "kind": "helm_chart_reference", + "name": "postgresql", + "version_constraint": "12.1.0", + "repository": "https://charts.bitnami.com/bitnami", + "resolved_chart_id": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml" + } + }, + "kubernetes_resource_addresses": {}, + "diagnostics": {}, + "edges": { + "has_artifact": { + "README.md": {"src": "can://iac/payments", "dst": "can://artifact/payments/README.md"}, + "charts/api/Chart.yaml": {"src": "can://iac/payments", "dst": "can://artifact/payments/charts/api/Chart.yaml"}, + "charts/api/charts/postgresql/Chart.yaml": {"src": "can://iac/payments", "dst": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml"}, + "charts/api/values.yaml": {"src": "can://iac/payments", "dst": "can://artifact/payments/charts/api/values.yaml"}, + "charts/api/templates/deployment.yaml": {"src": "can://iac/payments", "dst": "can://artifact/payments/charts/api/templates/deployment.yaml"} + }, + "defines_config": { + "charts/api/values.yaml@key/image.tag": { + "src": "can://artifact/payments/charts/api/values.yaml", + "dst": "can://artifact/payments/charts/api/values.yaml@key/image.tag" + } + }, + "iac_has_alias": { + "charts/api/Chart.yaml@helm-chart": { + "src": "can://artifact/payments/charts/api/Chart.yaml", + "dst": "can://iac/payments/helm/chart/charts/api" + }, + "charts/api/charts/postgresql/Chart.yaml@helm-chart": { + "src": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml", + "dst": "can://iac/payments/helm/chart/charts/api/charts/postgresql" + } + }, + "iac_alias_of": { + "charts/api/Chart.yaml@helm-chart": { + "src": "can://iac/payments/helm/chart/charts/api", + "dst": "can://artifact/payments/charts/api/Chart.yaml" + }, + "charts/api/charts/postgresql/Chart.yaml@helm-chart": { + "src": "can://iac/payments/helm/chart/charts/api/charts/postgresql", + "dst": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml" + } + }, + "iac_part_of_chart": { + "charts/api/values.yaml": { + "src": "can://artifact/payments/charts/api/values.yaml", + "dst": "can://artifact/payments/charts/api/Chart.yaml" + }, + "charts/api/templates/deployment.yaml": { + "src": "can://artifact/payments/charts/api/templates/deployment.yaml", + "dst": "can://artifact/payments/charts/api/Chart.yaml" + } + }, + "iac_resolves_to_chart": { + "bitnami/postgresql": { + "src": "can://iac/payments/helm/chart-reference/bitnami/postgresql", + "dst": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml" + } + }, + "iac_defines_template": { + "charts/api/templates/deployment.yaml@12:1": { + "src": "can://artifact/payments/charts/api/templates/deployment.yaml", + "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/named-template/api.fullname" + } + }, + "iac_has_template_call": { + "charts/api/templates/deployment.yaml@6:11": { + "src": "can://artifact/payments/charts/api/templates/deployment.yaml", + "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/template-call@6:11" + } + }, + "iac_calls_template": { + "charts/api/templates/deployment.yaml@6:11": { + "src": "can://iac/payments/helm/charts/api/templates/deployment.yaml/template-call@6:11", + "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/named-template/api.fullname" + } + }, + "iac_has_value_reference": { + "charts/api/templates/deployment.yaml@10:16": { + "src": "can://artifact/payments/charts/api/templates/deployment.yaml", + "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/value-reference@10:16" + } + }, + "iac_references_value": { + "charts/api/templates/deployment.yaml@10:16": { + "src": "can://iac/payments/helm/charts/api/templates/deployment.yaml/value-reference@10:16", + "dst": "can://artifact/payments/charts/api/values.yaml@key/image.tag" + } + }, + "iac_has_resource_template": { + "charts/api/templates/deployment.yaml@1:1": { + "src": "can://artifact/payments/charts/api/templates/deployment.yaml", + "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/resource-template@1:1" + } + }, + "iac_has_lookup_reference": { + "charts/api/templates/deployment.yaml@11:15": { + "src": "can://artifact/payments/charts/api/templates/deployment.yaml", + "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/lookup-reference@11:15" + } + } + } + } +} diff --git a/v2/iac/json/analysis.l3.sample.json b/v2/iac/json/analysis.l3.sample.json new file mode 100644 index 0000000..daf57f2 --- /dev/null +++ b/v2/iac/json/analysis.l3.sample.json @@ -0,0 +1,97 @@ +{ + "schema_version": "2.0.0", + "language": "iac", + "max_level": 3, + "analyzer": {"name": "codeanalyzer-iac", "version": "0.1.0"}, + "application": { + "id": "can://iac/payments", + "kind": "application", + "artifacts": { + "README.md": {"id": "can://artifact/payments/README.md", "kind": "artifact", "path": "README.md", "format": "text", "source": "payments infrastructure\n", "sha256": "79accdf71a72e00a8b25fe1284094368e885e2fe55db0cf686a83c5359bf43df", "size_bytes": 24, "config_keys": {}, "aliases": []}, + "charts/api/Chart.yaml": { + "id": "can://artifact/payments/charts/api/Chart.yaml", "kind": "artifact", "path": "charts/api/Chart.yaml", "format": "yaml", "source": "apiVersion: v2\nname: api\nversion: 1.2.3\ntype: application\n", "sha256": "4a0bf85edced2838bb82ae1a52bf8830a718462ff54f4c6fb11ce539fabb53af", "size_bytes": 58, "config_keys": {}, + "aliases": [{"id": "can://iac/payments/helm/chart/charts/api", "kind": "helm_chart", "target": "can://artifact/payments/charts/api/Chart.yaml"}], + "iac": { + "dialect": "helm", "kind": "helm_chart", "status": "complete", "api_version": "v2", "name": "api", "version": "1.2.3", "chart_type": "application", "maintainers": [], "keywords": [], "sources": [], "annotations": {}, "dependencies": {}, + "render_profiles": { + "default": { + "id": "can://iac/payments/helm/render-profile/charts/api/default", "kind": "helm_render_profile", "name": "default", "origin": "default", "chart_id": "can://artifact/payments/charts/api/Chart.yaml", "release_name": "api", "namespace": "default", + "value_layers": { + "0000": {"id": "can://iac/payments/helm/render-profile/charts/api/default/value-layer/0000", "kind": "helm_value_layer", "ordinal": 0, "source_id": "can://artifact/payments/charts/api/values.yaml"} + }, + "api_versions": [] + } + }, + "renders": { + "default": { + "id": "can://iac/payments/helm/render/charts/api/default", "kind": "helm_render", "status": "succeeded", "profile_id": "can://iac/payments/helm/render-profile/charts/api/default", "renderer_name": "helm", "renderer_version": "4.2.4", "value_layer_ids": ["can://iac/payments/helm/render-profile/charts/api/default/value-layer/0000"], "effective_values_sha256": "3d3ecdd860e9d3e2fedf29a4fb6e1667c8295ed8e500aae00901ce43e79bf497", "diagnostics": {}, + "resources": { + "apps/Deployment/default/api": {"id": "can://iac/payments/helm/render/charts/api/default/kubernetes/apps/Deployment/default/api", "kind": "kubernetes_resource", "api_version": "apps/v1", "resource_kind": "Deployment", "manifest_sha256": "e1b97b673a0cfecc5150d46004a6216ada2d48f59c6593f44061139420a8506a", "render_id": "can://iac/payments/helm/render/charts/api/default", "origin_ids": ["can://iac/payments/helm/charts/api/templates/deployment.yaml/resource-template@1:1"], "namespace": "default", "name": "api", "labels": {}, "annotations": {}, "plural": "deployments", "address_id": "can://iac/payments/kubernetes/address/apps/Deployment/default/api"}, + "core/Secret/default/api-credentials": {"id": "can://iac/payments/helm/render/charts/api/default/kubernetes/core/Secret/default/api-credentials", "kind": "kubernetes_resource", "api_version": "v1", "resource_kind": "Secret", "manifest_sha256": "5e65acdc38d1a0309e4c39fc4db1d62d290b3906f8415de307876a614ae957f6", "render_id": "can://iac/payments/helm/render/charts/api/default", "origin_ids": ["can://iac/payments/helm/charts/api/templates/deployment.yaml/resource-template@1:1"], "namespace": "default", "name": "api-credentials", "labels": {}, "annotations": {}, "plural": "secrets", "address_id": "can://iac/payments/kubernetes/address/core/Secret/default/api-credentials", "secret_data": {"password": {"key": "password", "sha256": "5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5"}}} + } + }, + "explicit": {"id": "can://iac/payments/helm/render/charts/api/explicit", "kind": "helm_render", "status": "failed", "phase": "template", "profile_id": "can://iac/payments/helm/render-profile/config/explicit", "renderer_name": "helm", "renderer_version": "4.2.4", "value_layer_ids": ["can://iac/payments/helm/render-profile/config/explicit/value-layer/0000", "can://iac/payments/helm/render-profile/config/explicit/value-layer/0001"], "effective_values_sha256": "3d3ecdd860e9d3e2fedf29a4fb6e1667c8295ed8e500aae00901ce43e79bf497", "diagnostics": {"render-failed": {"id": "can://iac/payments/helm/render/charts/api/explicit/diagnostic/IAC_HELM_RENDER_FAILED", "kind": "diagnostic", "severity": "error", "code": "IAC_HELM_RENDER_FAILED", "message": "template rendering failed", "phase": "template", "artifact_id": "can://artifact/payments/charts/api/Chart.yaml"}}, "resources": {}} + } + } + }, + "charts/api/charts/postgresql/Chart.yaml": { + "id": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml", "kind": "artifact", "path": "charts/api/charts/postgresql/Chart.yaml", "format": "yaml", "source": "apiVersion: v2\nname: postgresql\nversion: 12.1.0\ntype: application\n", "sha256": "57ea5ae02fdfc54e1a02eac39e9b1d98d289c6f182c77d90d80d7a9124d11161", "size_bytes": 66, "config_keys": {}, + "aliases": [{"id": "can://iac/payments/helm/chart/charts/api/charts/postgresql", "kind": "helm_chart", "target": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml"}], + "iac": {"dialect": "helm", "kind": "helm_chart", "status": "complete", "api_version": "v2", "name": "postgresql", "version": "12.1.0", "chart_type": "application", "maintainers": [], "keywords": [], "sources": [], "annotations": {}, "dependencies": {}, "renders": {}} + }, + "charts/api/values.yaml": { + "id": "can://artifact/payments/charts/api/values.yaml", "kind": "artifact", "path": "charts/api/values.yaml", "format": "yaml", "source": "image:\n tag: 1.2.3x\n", "sha256": "3d3ecdd860e9d3e2fedf29a4fb6e1667c8295ed8e500aae00901ce43e79bf497", "size_bytes": 21, + "config_keys": {"image.tag": {"id": "can://artifact/payments/charts/api/values.yaml@key/image.tag", "kind": "config_key", "name": "tag", "path": "image.tag", "span": {"start": [2, 3], "end": [2, 14], "bytes": [9, 20]}, "iac": {"kind": "helm_value"}}}, + "aliases": [], "iac": {"dialect": "helm", "kind": "helm_values", "status": "complete", "roles": ["default"]} + }, + "charts/api/templates/deployment.yaml": { + "id": "can://artifact/payments/charts/api/templates/deployment.yaml", "kind": "artifact", "path": "charts/api/templates/deployment.yaml", "format": "yaml", "source": "apiVersion: v1\nkind: Deployment\nmetadata:\n a: \n x: hello!!\n name: {{include \"api.fullname\" .}}\nspec:\n template:\n containers: # comment!\n image: {{.Values.image.tag }}\n lookup: {{lookup \"\" \"v1\" \"ConfigMap\" \"default\" \"settings\"}}\n{{define \"api.fullname\"}}api{{end}}\n", "sha256": "e8367060ea9d4118f7bf38113b4fdccf7ea2d5f24442c580b07e487dbcdb103e", "size_bytes": 285, "config_keys": {}, "aliases": [], + "iac": { + "dialect": "helm", "kind": "helm_template", "status": "complete", "roles": ["resource"], + "named_templates": {"12:1": {"id": "can://iac/payments/helm/charts/api/templates/deployment.yaml/named-template/api.fullname", "kind": "helm_named_template", "name": "api.fullname", "span": {"start": [12, 1], "end": [12, 36], "bytes": [249, 284]}}}, + "template_calls": {"6:11": {"id": "can://iac/payments/helm/charts/api/templates/deployment.yaml/template-call@6:11", "kind": "helm_template_call", "call_kind": "include", "name_expression": "api.fullname", "target_id": "can://iac/payments/helm/charts/api/templates/deployment.yaml/named-template/api.fullname", "span": {"start": [6, 11], "end": [6, 39], "bytes": [73, 101]}}}, + "value_references": {"10:16": {"id": "can://iac/payments/helm/charts/api/templates/deployment.yaml/value-reference@10:16", "kind": "helm_value_reference", "path_expression": "image.tag", "target_id": "can://artifact/payments/charts/api/values.yaml@key/image.tag", "span": {"start": [10, 16], "end": [10, 33], "bytes": [162, 179]}}}, + "resource_templates": {"1:1": {"id": "can://iac/payments/helm/charts/api/templates/deployment.yaml/resource-template@1:1", "kind": "helm_resource_template", "document_index": 0, "span": {"start": [1, 1], "end": [12, 1], "bytes": [0, 249]}}}, + "lookup_references": {"11:15": {"id": "can://iac/payments/helm/charts/api/templates/deployment.yaml/lookup-reference@11:15", "kind": "helm_lookup_reference", "group_expression": "\"\"", "version_expression": "\"v1\"", "resource_kind_expression": "\"ConfigMap\"", "namespace_expression": "\"default\"", "name_expression": "\"settings\"", "span": {"start": [11, 15], "end": [11, 66], "bytes": [197, 248]}}} + } + }, + "codeanalyzer-iac.yaml": { + "id": "can://artifact/payments/codeanalyzer-iac.yaml", "kind": "artifact", "path": "codeanalyzer-iac.yaml", "format": "yaml", "source": "render_profiles:\n explicit:\n set:\n image.tag: 2.0.0\n", "sha256": "d0928860f1949a7b5437ff0c9b1ce6982efd998c18104f7a4685b4066f8be06e", "size_bytes": 61, + "config_keys": {"render_profiles.explicit.set.image.tag": {"id": "can://artifact/payments/codeanalyzer-iac.yaml@key/render_profiles.explicit.set.image.tag", "kind": "config_key", "name": "image.tag", "path": "render_profiles.explicit.set.image.tag", "span": {"start": [4, 7], "end": [4, 23], "bytes": [44, 60]}}}, "aliases": [], + "codeanalyzer_iac_config": {"kind": "codeanalyzer_iac_config", "config_version": 1, "render_profiles": {"explicit": {"id": "can://iac/payments/helm/render-profile/config/explicit", "kind": "helm_render_profile", "name": "explicit", "origin": "config", "chart_id": "can://artifact/payments/charts/api/Chart.yaml", "release_name": "api-explicit", "namespace": "default", "value_layers": {"0000": {"id": "can://iac/payments/helm/render-profile/config/explicit/value-layer/0000", "kind": "helm_value_layer", "ordinal": 0, "source_id": "can://artifact/payments/charts/api/values.yaml"}, "0001": {"id": "can://iac/payments/helm/render-profile/config/explicit/value-layer/0001", "kind": "helm_value_layer", "ordinal": 1, "source_id": "can://artifact/payments/codeanalyzer-iac.yaml@key/render_profiles.explicit.set.image.tag"}}, "api_versions": []}}} + } + }, + "packages": {}, + "external_chart_references": {"bitnami/postgresql": {"id": "can://iac/payments/helm/chart-reference/bitnami/postgresql", "kind": "helm_chart_reference", "name": "postgresql", "version_constraint": "12.1.0", "repository": "https://charts.bitnami.com/bitnami", "resolved_chart_id": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml"}}, + "kubernetes_resource_addresses": { + "apps/Deployment/default/api": {"id": "can://iac/payments/kubernetes/address/apps/Deployment/default/api", "kind": "kubernetes_resource_address", "group": "apps", "resource_kind": "Deployment", "namespace": "default", "name": "api", "plural": "deployments"}, + "core/Secret/default/api-credentials": {"id": "can://iac/payments/kubernetes/address/core/Secret/default/api-credentials", "kind": "kubernetes_resource_address", "group": "", "resource_kind": "Secret", "namespace": "default", "name": "api-credentials", "plural": "secrets"} + }, + "diagnostics": {}, + "edges": { + "iac_defines_template": {"charts/api/templates/deployment.yaml@12:1": {"src": "can://artifact/payments/charts/api/templates/deployment.yaml", "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/named-template/api.fullname"}}, + "iac_has_template_call": {"charts/api/templates/deployment.yaml@6:11": {"src": "can://artifact/payments/charts/api/templates/deployment.yaml", "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/template-call@6:11"}}, + "iac_calls_template": {"charts/api/templates/deployment.yaml@6:11": {"src": "can://iac/payments/helm/charts/api/templates/deployment.yaml/template-call@6:11", "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/named-template/api.fullname"}}, + "iac_has_value_reference": {"charts/api/templates/deployment.yaml@10:16": {"src": "can://artifact/payments/charts/api/templates/deployment.yaml", "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/value-reference@10:16"}}, + "iac_references_value": {"charts/api/templates/deployment.yaml@10:16": {"src": "can://iac/payments/helm/charts/api/templates/deployment.yaml/value-reference@10:16", "dst": "can://artifact/payments/charts/api/values.yaml@key/image.tag"}}, + "iac_has_resource_template": {"charts/api/templates/deployment.yaml@1:1": {"src": "can://artifact/payments/charts/api/templates/deployment.yaml", "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/resource-template@1:1"}}, + "iac_has_lookup_reference": {"charts/api/templates/deployment.yaml@11:15": {"src": "can://artifact/payments/charts/api/templates/deployment.yaml", "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/lookup-reference@11:15"}}, + "has_artifact": {"README.md": {"src": "can://iac/payments", "dst": "can://artifact/payments/README.md"}, "charts/api/Chart.yaml": {"src": "can://iac/payments", "dst": "can://artifact/payments/charts/api/Chart.yaml"}, "charts/api/charts/postgresql/Chart.yaml": {"src": "can://iac/payments", "dst": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml"}, "charts/api/values.yaml": {"src": "can://iac/payments", "dst": "can://artifact/payments/charts/api/values.yaml"}, "charts/api/templates/deployment.yaml": {"src": "can://iac/payments", "dst": "can://artifact/payments/charts/api/templates/deployment.yaml"}, "codeanalyzer-iac.yaml": {"src": "can://iac/payments", "dst": "can://artifact/payments/codeanalyzer-iac.yaml"}}, + "defines_config": {"charts/api/values.yaml@key/image.tag": {"src": "can://artifact/payments/charts/api/values.yaml", "dst": "can://artifact/payments/charts/api/values.yaml@key/image.tag"}, "codeanalyzer-iac.yaml@key/render_profiles.explicit.set.image.tag": {"src": "can://artifact/payments/codeanalyzer-iac.yaml", "dst": "can://artifact/payments/codeanalyzer-iac.yaml@key/render_profiles.explicit.set.image.tag"}}, + "iac_part_of_chart": {"charts/api/values.yaml": {"src": "can://artifact/payments/charts/api/values.yaml", "dst": "can://artifact/payments/charts/api/Chart.yaml"}, "charts/api/templates/deployment.yaml": {"src": "can://artifact/payments/charts/api/templates/deployment.yaml", "dst": "can://artifact/payments/charts/api/Chart.yaml"}}, + "iac_resolves_to_chart": {"bitnami/postgresql": {"src": "can://iac/payments/helm/chart-reference/bitnami/postgresql", "dst": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml"}}, + "iac_declares_profile": {"chart@default": {"src": "can://artifact/payments/charts/api/Chart.yaml", "dst": "can://iac/payments/helm/render-profile/charts/api/default"}, "codeanalyzer-iac.yaml@explicit": {"src": "can://artifact/payments/codeanalyzer-iac.yaml", "dst": "can://iac/payments/helm/render-profile/config/explicit"}}, + "iac_renders_chart": {"default": {"src": "can://iac/payments/helm/render-profile/charts/api/default", "dst": "can://artifact/payments/charts/api/Chart.yaml"}, "explicit": {"src": "can://iac/payments/helm/render-profile/config/explicit", "dst": "can://artifact/payments/charts/api/Chart.yaml"}}, + "iac_has_value_layer": {"default@0000": {"src": "can://iac/payments/helm/render-profile/charts/api/default", "dst": "can://iac/payments/helm/render-profile/charts/api/default/value-layer/0000"}, "explicit@0000": {"src": "can://iac/payments/helm/render-profile/config/explicit", "dst": "can://iac/payments/helm/render-profile/config/explicit/value-layer/0000"}, "explicit@0001": {"src": "can://iac/payments/helm/render-profile/config/explicit", "dst": "can://iac/payments/helm/render-profile/config/explicit/value-layer/0001"}}, + "iac_reads_from": {"default@0000": {"src": "can://iac/payments/helm/render-profile/charts/api/default/value-layer/0000", "dst": "can://artifact/payments/charts/api/values.yaml"}, "explicit@0000": {"src": "can://iac/payments/helm/render-profile/config/explicit/value-layer/0000", "dst": "can://artifact/payments/charts/api/values.yaml"}, "explicit@0001": {"src": "can://iac/payments/helm/render-profile/config/explicit/value-layer/0001", "dst": "can://artifact/payments/codeanalyzer-iac.yaml@key/render_profiles.explicit.set.image.tag"}}, + "iac_has_render": {"default": {"src": "can://artifact/payments/charts/api/Chart.yaml", "dst": "can://iac/payments/helm/render/charts/api/default"}, "explicit": {"src": "can://artifact/payments/charts/api/Chart.yaml", "dst": "can://iac/payments/helm/render/charts/api/explicit"}}, + "iac_configured_by": {"default": {"src": "can://iac/payments/helm/render/charts/api/default", "dst": "can://iac/payments/helm/render-profile/charts/api/default"}, "explicit": {"src": "can://iac/payments/helm/render/charts/api/explicit", "dst": "can://iac/payments/helm/render-profile/config/explicit"}}, + "iac_has_diagnostic": {"explicit@render-failed": {"src": "can://iac/payments/helm/render/charts/api/explicit", "dst": "can://iac/payments/helm/render/charts/api/explicit/diagnostic/IAC_HELM_RENDER_FAILED"}}, + "iac_produces": {"default@deployment": {"src": "can://iac/payments/helm/render/charts/api/default", "dst": "can://iac/payments/helm/render/charts/api/default/kubernetes/apps/Deployment/default/api"}, "default@secret": {"src": "can://iac/payments/helm/render/charts/api/default", "dst": "can://iac/payments/helm/render/charts/api/default/kubernetes/core/Secret/default/api-credentials"}}, + "iac_targets_resource": {"deployment": {"src": "can://iac/payments/helm/render/charts/api/default/kubernetes/apps/Deployment/default/api", "dst": "can://iac/payments/kubernetes/address/apps/Deployment/default/api"}, "secret": {"src": "can://iac/payments/helm/render/charts/api/default/kubernetes/core/Secret/default/api-credentials", "dst": "can://iac/payments/kubernetes/address/core/Secret/default/api-credentials"}}, + "iac_derived_from": {"deployment": {"src": "can://iac/payments/helm/render/charts/api/default/kubernetes/apps/Deployment/default/api", "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/resource-template@1:1"}, "secret": {"src": "can://iac/payments/helm/render/charts/api/default/kubernetes/core/Secret/default/api-credentials", "dst": "can://iac/payments/helm/charts/api/templates/deployment.yaml/resource-template@1:1"}}, + "iac_has_alias": {"charts/api/Chart.yaml@helm-chart": {"src": "can://artifact/payments/charts/api/Chart.yaml", "dst": "can://iac/payments/helm/chart/charts/api"}, "charts/api/charts/postgresql/Chart.yaml@helm-chart": {"src": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml", "dst": "can://iac/payments/helm/chart/charts/api/charts/postgresql"}}, + "iac_alias_of": {"charts/api/Chart.yaml@helm-chart": {"src": "can://iac/payments/helm/chart/charts/api", "dst": "can://artifact/payments/charts/api/Chart.yaml"}, "charts/api/charts/postgresql/Chart.yaml@helm-chart": {"src": "can://iac/payments/helm/chart/charts/api/charts/postgresql", "dst": "can://artifact/payments/charts/api/charts/postgresql/Chart.yaml"}} + } + } +} diff --git a/v2/iac/json/analysis.schema.json b/v2/iac/json/analysis.schema.json new file mode 100644 index 0000000..b08aee6 --- /dev/null +++ b/v2/iac/json/analysis.schema.json @@ -0,0 +1,659 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://codellm-devkit.github.io/schema/v2/iac/analysis.schema.json", + "title": "codeanalyzer-iac schema v2 analysis output", + "x-cldk": { + "schemaVersion": "2.0.0", + "language": "iac", + "source": { + "repo": "codellm-devkit/codeanalyzer-iac", + "release": "v0.1.0", + "definedIn": "internal/model" + } + }, + "$ref": "#/$defs/Analysis", + "$defs": { + "ApplicationId": { + "type": "string", + "pattern": "^can://iac/(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+(?![\\s\\S])" + }, + "ArtifactId": { + "type": "string", + "pattern": "^can://artifact/(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+/(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+(?:/(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+)*(?![\\s\\S])" + }, + "ConfigKeyId": { + "type": "string", + "pattern": "^can://artifact/(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+/(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+(?:/(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+)*@key/(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+(?:/(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+)*(?![\\s\\S])" + }, + "SemanticId": { + "type": "string", + "pattern": "^can://iac/(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+/(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+(?:/(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+)*(?:@(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+(?::(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+)*)?(?![\\s\\S])" + }, + "Purl": { + "type": "string", + "pattern": "^pkg:oci/(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+(?:/(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+)*(?:@(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+)?(?:\\?[a-z][a-z0-9._-]*=(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+(?:&[a-z][a-z0-9._-]*=(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+)*)?(?:#(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+(?:/(?:[A-Za-z0-9._~-]|%[0-9A-Fa-f]{2})+)*)?(?![\\s\\S])" + }, + "AliasTargetId": { + "oneOf": [ + {"$ref": "#/$defs/ArtifactId"}, + {"$ref": "#/$defs/SemanticId"} + ] + }, + "ValueSourceId": { + "oneOf": [ + {"$ref": "#/$defs/ArtifactId"}, + {"$ref": "#/$defs/ConfigKeyId"} + ] + }, + "OriginId": { + "oneOf": [ + {"$ref": "#/$defs/ArtifactId"}, + {"$ref": "#/$defs/ConfigKeyId"}, + {"$ref": "#/$defs/SemanticId"} + ] + }, + "NodeId": { + "oneOf": [ + {"$ref": "#/$defs/ApplicationId"}, + {"$ref": "#/$defs/ArtifactId"}, + {"$ref": "#/$defs/ConfigKeyId"}, + {"$ref": "#/$defs/SemanticId"}, + {"$ref": "#/$defs/Purl"} + ] + }, + "Analysis": { + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "language", "max_level", "analyzer", "application"], + "properties": { + "schema_version": {"const": "2.0.0"}, + "language": {"const": "iac"}, + "max_level": {"type": "integer", "minimum": 1, "maximum": 3}, + "analyzer": {"$ref": "#/$defs/Analyzer"}, + "application": {"$ref": "#/$defs/Application"} + } + }, + "Analyzer": { + "type": "object", + "additionalProperties": false, + "required": ["name", "version"], + "properties": { + "name": {"const": "codeanalyzer-iac"}, + "version": {"type": "string", "minLength": 1} + } + }, + "Application": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "kind", + "artifacts", + "packages", + "external_chart_references", + "kubernetes_resource_addresses", + "diagnostics", + "edges" + ], + "properties": { + "id": {"$ref": "#/$defs/ApplicationId"}, + "kind": {"const": "application"}, + "artifacts": { + "type": "object", + "additionalProperties": {"$ref": "#/$defs/Artifact"} + }, + "packages": { + "type": "object", + "additionalProperties": {"$ref": "#/$defs/Package"} + }, + "external_chart_references": { + "type": "object", + "additionalProperties": {"$ref": "#/$defs/HelmChartReference"} + }, + "kubernetes_resource_addresses": { + "type": "object", + "additionalProperties": {"$ref": "#/$defs/KubernetesResourceAddress"} + }, + "diagnostics": { + "type": "object", + "additionalProperties": {"$ref": "#/$defs/Diagnostic"} + }, + "edges": {"$ref": "#/$defs/Edges"} + } + }, + "Artifact": { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "path", "format", "source", "sha256", "size_bytes", "config_keys", "aliases"], + "properties": { + "id": {"$ref": "#/$defs/ArtifactId"}, + "kind": {"const": "artifact"}, + "path": { + "type": "string", + "pattern": "^(?!/)(?!.*(?:^|/)\\.{1,2}(?:/|$))[^\\\\]+$" + }, + "format": {"type": "string", "minLength": 1}, + "source": {"type": "string"}, + "sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, + "size_bytes": {"type": "integer", "minimum": 0}, + "config_keys": { + "type": "object", + "additionalProperties": {"$ref": "#/$defs/ConfigKey"} + }, + "aliases": { + "type": "array", + "items": {"$ref": "#/$defs/IdentityAlias"} + }, + "iac": {"$ref": "#/$defs/IaCFacet"}, + "codeanalyzer_iac_config": {"$ref": "#/$defs/CodeAnalyzerIaCConfig"} + } + }, + "Span": { + "type": "object", + "additionalProperties": false, + "required": ["start", "end", "bytes"], + "properties": { + "start": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": {"type": "integer", "minimum": 1} + }, + "end": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": {"type": "integer", "minimum": 1} + }, + "bytes": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": {"type": "integer", "minimum": 0} + } + } + }, + "ConfigKey": { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "name", "path", "span"], + "properties": { + "id": {"$ref": "#/$defs/ConfigKeyId"}, + "kind": {"const": "config_key"}, + "name": {"type": "string", "minLength": 1}, + "path": {"type": "string", "minLength": 1}, + "span": {"$ref": "#/$defs/Span"}, + "iac": {"$ref": "#/$defs/HelmValueFacet"} + } + }, + "IdentityAlias": { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "target"], + "properties": { + "id": {"$ref": "#/$defs/SemanticId"}, + "kind": {"type": "string", "minLength": 1}, + "target": {"$ref": "#/$defs/AliasTargetId"} + } + }, + "Diagnostic": { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "severity", "code", "message"], + "properties": { + "id": {"$ref": "#/$defs/SemanticId"}, + "kind": {"const": "diagnostic"}, + "severity": {"enum": ["info", "warning", "error"]}, + "code": {"type": "string", "minLength": 1}, + "message": {"type": "string", "minLength": 1}, + "phase": {"enum": ["load", "dependency", "values", "template", "decode"]}, + "artifact_id": {"$ref": "#/$defs/ArtifactId"}, + "span": {"$ref": "#/$defs/Span"} + } + }, + "Package": { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "purl"], + "properties": { + "id": {"$ref": "#/$defs/Purl"}, + "kind": {"const": "package"}, + "purl": {"$ref": "#/$defs/Purl"} + } + }, + "Edge": { + "type": "object", + "additionalProperties": false, + "required": ["src", "dst"], + "properties": { + "src": {"$ref": "#/$defs/NodeId"}, + "dst": {"$ref": "#/$defs/NodeId"} + } + }, + "IaCFacet": { + "oneOf": [ + {"$ref": "#/$defs/HelmChart"}, + {"$ref": "#/$defs/HelmRequirements"}, + {"$ref": "#/$defs/HelmLock"}, + {"$ref": "#/$defs/HelmValues"}, + {"$ref": "#/$defs/HelmValuesSchema"}, + {"$ref": "#/$defs/HelmTemplate"}, + {"$ref": "#/$defs/HelmCRD"}, + {"$ref": "#/$defs/HelmIgnore"} + ] + }, + "HelmChart": { + "type": "object", + "additionalProperties": false, + "required": ["dialect", "kind", "status", "api_version", "name", "version", "dependencies", "renders"], + "properties": { + "dialect": {"const": "helm"}, + "kind": {"const": "helm_chart"}, + "status": {"enum": ["complete", "partial", "failed"]}, + "api_version": {"enum": ["v1", "v2"]}, + "name": {"type": "string", "minLength": 1}, + "version": {"type": "string", "minLength": 1}, + "kube_version": {"type": "string"}, + "description": {"type": "string"}, + "chart_type": {"enum": ["application", "library"]}, + "keywords": {"type": "array", "items": {"type": "string"}}, + "home": {"type": "string"}, + "sources": {"type": "array", "items": {"type": "string"}}, + "maintainers": {"type": "array", "items": {"$ref": "#/$defs/HelmMaintainer"}}, + "icon": {"type": "string"}, + "app_version": {"type": "string"}, + "deprecated": {"type": "boolean"}, + "annotations": {"type": "object", "additionalProperties": {"type": "string"}}, + "dependencies": {"type": "object", "additionalProperties": {"$ref": "#/$defs/HelmDependency"}}, + "render_profiles": {"type": "object", "additionalProperties": {"$ref": "#/$defs/HelmRenderProfile"}}, + "renders": {"type": "object", "additionalProperties": {"$ref": "#/$defs/HelmRender"}} + } + }, + "HelmMaintainer": { + "type": "object", + "additionalProperties": false, + "required": ["name"], + "properties": { + "name": {"type": "string", "minLength": 1}, + "email": {"type": "string", "minLength": 1}, + "url": {"type": "string", "minLength": 1} + } + }, + "HelmRequirements": { + "type": "object", + "additionalProperties": false, + "required": ["dialect", "kind", "status", "roles", "dependencies"], + "properties": { + "dialect": {"const": "helm"}, + "kind": {"const": "helm_requirements"}, + "status": {"enum": ["complete", "partial", "failed"]}, + "roles": {"const": ["legacy_dependency_manifest"]}, + "dependencies": {"type": "object", "additionalProperties": {"$ref": "#/$defs/HelmDependency"}} + } + }, + "HelmLock": { + "type": "object", + "additionalProperties": false, + "required": ["dialect", "kind", "status", "roles", "dependencies"], + "properties": { + "dialect": {"const": "helm"}, + "kind": {"const": "helm_lock"}, + "status": {"enum": ["complete", "partial", "failed"]}, + "roles": { + "type": "array", + "uniqueItems": true, + "items": {"enum": ["dependency_lock", "legacy_dependency_lock"]} + }, + "dependencies": {"type": "object", "additionalProperties": {"$ref": "#/$defs/HelmDependency"}} + } + }, + "HelmValues": { + "type": "object", + "additionalProperties": false, + "required": ["dialect", "kind", "status", "roles"], + "properties": { + "dialect": {"const": "helm"}, + "kind": {"const": "helm_values"}, + "status": {"enum": ["complete", "partial", "failed"]}, + "roles": { + "type": "array", + "uniqueItems": true, + "items": {"enum": ["default", "parent", "override"]} + } + } + }, + "HelmValuesSchema": { + "type": "object", + "additionalProperties": false, + "required": ["dialect", "kind", "status", "roles"], + "properties": { + "dialect": {"const": "helm"}, + "kind": {"const": "helm_values_schema"}, + "status": {"enum": ["complete", "partial", "failed"]}, + "roles": {"const": ["validation_schema"]} + } + }, + "HelmTemplate": { + "type": "object", + "additionalProperties": false, + "required": [ + "dialect", + "kind", + "status", + "roles", + "named_templates", + "template_calls", + "value_references", + "resource_templates", + "lookup_references" + ], + "properties": { + "dialect": {"const": "helm"}, + "kind": {"const": "helm_template"}, + "status": {"enum": ["complete", "partial", "failed"]}, + "roles": { + "type": "array", + "uniqueItems": true, + "items": {"enum": ["resource", "helper", "notes", "test", "hook"]} + }, + "named_templates": {"type": "object", "additionalProperties": {"$ref": "#/$defs/HelmNamedTemplate"}}, + "template_calls": {"type": "object", "additionalProperties": {"$ref": "#/$defs/HelmTemplateCall"}}, + "value_references": {"type": "object", "additionalProperties": {"$ref": "#/$defs/HelmValueReference"}}, + "resource_templates": {"type": "object", "additionalProperties": {"$ref": "#/$defs/HelmResourceTemplate"}}, + "lookup_references": {"type": "object", "additionalProperties": {"$ref": "#/$defs/HelmLookupReference"}} + } + }, + "HelmCRD": { + "type": "object", + "additionalProperties": false, + "required": ["dialect", "kind", "status", "roles"], + "properties": { + "dialect": {"const": "helm"}, + "kind": {"const": "helm_crd"}, + "status": {"enum": ["complete", "partial", "failed"]}, + "roles": {"const": ["custom_resource_definition"]} + } + }, + "HelmIgnore": { + "type": "object", + "additionalProperties": false, + "required": ["dialect", "kind", "status", "roles"], + "properties": { + "dialect": {"const": "helm"}, + "kind": {"const": "helm_ignore"}, + "status": {"enum": ["complete", "partial", "failed"]}, + "roles": {"const": ["ignore_rules"]} + } + }, + "HelmDependency": { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "name", "version_constraint", "span"], + "properties": { + "id": {"$ref": "#/$defs/SemanticId"}, + "kind": {"const": "helm_dependency"}, + "name": {"type": "string", "minLength": 1}, + "version_constraint": {"type": "string", "minLength": 1}, + "alias": {"type": "string", "minLength": 1}, + "repository": {"type": "string", "minLength": 1}, + "condition": {"type": "string", "minLength": 1}, + "tags": {"type": "array", "items": {"type": "string"}}, + "import_values": { + "type": "array", + "items": { + "oneOf": [ + {"type": "string"}, + { + "type": "object", + "additionalProperties": false, + "required": ["child", "parent"], + "properties": { + "child": {"type": "string"}, + "parent": {"type": "string"} + } + } + ] + } + }, + "span": {"$ref": "#/$defs/Span"} + } + }, + "HelmNamedTemplate": { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "name", "span"], + "properties": { + "id": {"$ref": "#/$defs/SemanticId"}, + "kind": {"const": "helm_named_template"}, + "name": {"type": "string", "minLength": 1}, + "span": {"$ref": "#/$defs/Span"} + } + }, + "HelmTemplateCall": { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "call_kind", "name_expression", "span"], + "properties": { + "id": {"$ref": "#/$defs/SemanticId"}, + "kind": {"const": "helm_template_call"}, + "call_kind": {"enum": ["template", "include", "block", "tpl"]}, + "name_expression": {"type": "string"}, + "target_id": {"$ref": "#/$defs/SemanticId"}, + "span": {"$ref": "#/$defs/Span"} + } + }, + "HelmValueReference": { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "path_expression", "span"], + "properties": { + "id": {"$ref": "#/$defs/SemanticId"}, + "kind": {"const": "helm_value_reference"}, + "path_expression": {"type": "string", "minLength": 1}, + "target_id": {"$ref": "#/$defs/ConfigKeyId"}, + "span": {"$ref": "#/$defs/Span"} + } + }, + "HelmResourceTemplate": { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "document_index", "span"], + "properties": { + "id": {"$ref": "#/$defs/SemanticId"}, + "kind": {"const": "helm_resource_template"}, + "document_index": {"type": "integer", "minimum": 0}, + "span": {"$ref": "#/$defs/Span"} + } + }, + "HelmLookupReference": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "kind", + "group_expression", + "version_expression", + "resource_kind_expression", + "namespace_expression", + "name_expression", + "span" + ], + "properties": { + "id": {"$ref": "#/$defs/SemanticId"}, + "kind": {"const": "helm_lookup_reference"}, + "group_expression": {"type": "string"}, + "version_expression": {"type": "string"}, + "resource_kind_expression": {"type": "string"}, + "namespace_expression": {"type": "string"}, + "name_expression": {"type": "string"}, + "span": {"$ref": "#/$defs/Span"} + } + }, + "EdgeMap": { + "type": "object", + "additionalProperties": {"$ref": "#/$defs/Edge"} + }, + "Edges": { + "type": "object", + "additionalProperties": false, + "properties": { + "has_artifact": {"$ref": "#/$defs/EdgeMap"}, + "defines_config": {"$ref": "#/$defs/EdgeMap"}, + "iac_part_of_chart": {"$ref": "#/$defs/EdgeMap"}, + "iac_declares_dependency": {"$ref": "#/$defs/EdgeMap"}, + "iac_targets_chart_reference": {"$ref": "#/$defs/EdgeMap"}, + "iac_resolves_to_chart": {"$ref": "#/$defs/EdgeMap"}, + "iac_identified_by_package": {"$ref": "#/$defs/EdgeMap"}, + "iac_defines_template": {"$ref": "#/$defs/EdgeMap"}, + "iac_has_template_call": {"$ref": "#/$defs/EdgeMap"}, + "iac_calls_template": {"$ref": "#/$defs/EdgeMap"}, + "iac_has_value_reference": {"$ref": "#/$defs/EdgeMap"}, + "iac_references_value": {"$ref": "#/$defs/EdgeMap"}, + "iac_has_resource_template": {"$ref": "#/$defs/EdgeMap"}, + "iac_has_lookup_reference": {"$ref": "#/$defs/EdgeMap"}, + "iac_declares_profile": {"$ref": "#/$defs/EdgeMap"}, + "iac_renders_chart": {"$ref": "#/$defs/EdgeMap"}, + "iac_has_value_layer": {"$ref": "#/$defs/EdgeMap"}, + "iac_reads_from": {"$ref": "#/$defs/EdgeMap"}, + "iac_has_render": {"$ref": "#/$defs/EdgeMap"}, + "iac_configured_by": {"$ref": "#/$defs/EdgeMap"}, + "iac_has_diagnostic": {"$ref": "#/$defs/EdgeMap"}, + "iac_produces": {"$ref": "#/$defs/EdgeMap"}, + "iac_targets_resource": {"$ref": "#/$defs/EdgeMap"}, + "iac_derived_from": {"$ref": "#/$defs/EdgeMap"}, + "iac_has_alias": {"$ref": "#/$defs/EdgeMap"}, + "iac_alias_of": {"$ref": "#/$defs/EdgeMap"} + } + }, + "HelmChartReference": { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "name", "version_constraint"], + "properties": { + "id": {"$ref": "#/$defs/SemanticId"}, + "kind": {"const": "helm_chart_reference"}, + "name": {"type": "string", "minLength": 1}, + "version_constraint": {"type": "string", "minLength": 1}, + "repository": {"type": "string", "minLength": 1}, + "purl": {"$ref": "#/$defs/Purl"}, + "resolved_chart_id": {"$ref": "#/$defs/ArtifactId"} + } + }, + "CodeAnalyzerIaCConfig": { + "type": "object", + "additionalProperties": false, + "required": ["kind", "config_version", "render_profiles"], + "properties": { + "kind": {"const": "codeanalyzer_iac_config"}, + "config_version": {"const": 1}, + "render_profiles": {"type": "object", "additionalProperties": {"$ref": "#/$defs/HelmRenderProfile"}} + } + }, + "HelmRenderProfile": { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "name", "origin", "chart_id", "release_name", "namespace", "value_layers", "api_versions"], + "properties": { + "id": {"$ref": "#/$defs/SemanticId"}, + "kind": {"const": "helm_render_profile"}, + "name": {"type": "string", "minLength": 1}, + "origin": {"enum": ["default", "config"]}, + "chart_id": {"$ref": "#/$defs/ArtifactId"}, + "release_name": {"type": "string", "minLength": 1}, + "namespace": {"type": "string", "minLength": 1}, + "value_layers": {"type": "object", "additionalProperties": {"$ref": "#/$defs/HelmValueLayer"}}, + "api_versions": {"type": "array", "items": {"type": "string", "minLength": 1}}, + "kube_version": {"type": "string", "minLength": 1} + } + }, + "HelmValueLayer": { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "ordinal", "source_id"], + "properties": { + "id": {"$ref": "#/$defs/SemanticId"}, + "kind": {"const": "helm_value_layer"}, + "ordinal": {"type": "integer", "minimum": 0}, + "source_id": {"$ref": "#/$defs/ValueSourceId"} + } + }, + "HelmRender": { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "status", "profile_id", "renderer_name", "renderer_version", "value_layer_ids", "effective_values_sha256", "diagnostics", "resources"], + "properties": { + "id": {"$ref": "#/$defs/SemanticId"}, + "kind": {"const": "helm_render"}, + "status": {"enum": ["succeeded", "partial", "failed", "skipped"]}, + "profile_id": {"$ref": "#/$defs/SemanticId"}, + "renderer_name": {"type": "string", "minLength": 1}, + "renderer_version": {"type": "string", "minLength": 1}, + "value_layer_ids": {"type": "array", "items": {"$ref": "#/$defs/SemanticId"}}, + "effective_values_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, + "diagnostics": {"type": "object", "additionalProperties": {"$ref": "#/$defs/Diagnostic"}}, + "resources": {"type": "object", "additionalProperties": {"$ref": "#/$defs/KubernetesResource"}}, + "phase": {"enum": ["load", "dependency", "values", "template", "decode"]} + }, + "allOf": [ + { + "if": {"properties": {"status": {"const": "succeeded"}}}, + "then": {"not": {"required": ["phase"]}}, + "else": {"required": ["phase"]} + } + ] + }, + "KubernetesResource": { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "api_version", "resource_kind", "manifest_sha256", "render_id", "origin_ids"], + "properties": { + "id": {"$ref": "#/$defs/SemanticId"}, + "kind": {"const": "kubernetes_resource"}, + "api_version": {"type": "string", "minLength": 1}, + "resource_kind": {"type": "string", "minLength": 1}, + "manifest_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, + "render_id": {"$ref": "#/$defs/SemanticId"}, + "origin_ids": {"type": "array", "items": {"$ref": "#/$defs/OriginId"}}, + "namespace": {"type": "string", "minLength": 1}, + "name": {"type": "string", "minLength": 1}, + "generate_name": {"type": "string", "minLength": 1}, + "labels": {"type": "object", "additionalProperties": {"type": "string"}}, + "annotations": {"type": "object", "additionalProperties": {"type": "string"}}, + "plural": {"type": "string", "minLength": 1}, + "address_id": {"$ref": "#/$defs/SemanticId"}, + "secret_data": {"type": "object", "additionalProperties": {"$ref": "#/$defs/KubernetesSecretDatum"}} + } + }, + "KubernetesSecretDatum": { + "type": "object", + "additionalProperties": false, + "required": ["key", "sha256"], + "properties": { + "key": {"type": "string", "minLength": 1}, + "sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"} + } + }, + "KubernetesResourceAddress": { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "group", "resource_kind", "namespace", "name"], + "properties": { + "id": {"$ref": "#/$defs/SemanticId"}, + "kind": {"const": "kubernetes_resource_address"}, + "group": {"type": "string"}, + "resource_kind": {"type": "string", "minLength": 1}, + "namespace": {"type": "string", "minLength": 1}, + "name": {"type": "string", "minLength": 1}, + "plural": {"type": "string", "minLength": 1} + } + }, + "HelmValueFacet": { + "type": "object", + "additionalProperties": false, + "required": ["kind"], + "properties": { + "kind": {"const": "helm_value"} + } + } + } +} diff --git a/v2/iac/neo4j/contract.schema.json b/v2/iac/neo4j/contract.schema.json new file mode 100644 index 0000000..ed52100 --- /dev/null +++ b/v2/iac/neo4j/contract.schema.json @@ -0,0 +1,72 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://codellm-devkit.github.io/schema/v2/iac/neo4j/contract.schema.json", + "title": "codeanalyzer-iac Neo4j graph contract", + "x-cldk": { + "kind": "neo4j-graph-contract", + "validates": ["schema.neo4j.sample.json"] + }, + "type": "object", + "additionalProperties": false, + "required": ["schema_version", "generator", "marker_labels", "node_labels", "relationship_types", "constraints", "indexes"], + "properties": { + "schema_version": {"const": "1.0.0"}, + "generator": {"const": "codeanalyzer-iac"}, + "marker_labels": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, + "node_labels": {"type": "array", "items": {"$ref": "#/$defs/NodeLabel"}, "minItems": 1}, + "relationship_types": {"type": "array", "items": {"$ref": "#/$defs/RelationshipType"}, "minItems": 1}, + "constraints": {"type": "array", "items": {"type": "string", "pattern": "^CREATE CONSTRAINT "}}, + "indexes": {"type": "array", "items": {"type": "string", "pattern": "^CREATE (INDEX|FULLTEXT INDEX|VECTOR INDEX) "}} + }, + "$defs": { + "NodeLabel": { + "type": "object", + "additionalProperties": false, + "required": ["label", "merge_label", "key", "properties"], + "properties": { + "label": {"$ref": "#/$defs/LabelName"}, + "merge_label": {"$ref": "#/$defs/LabelName"}, + "key": {"type": "string", "minLength": 1}, + "properties": { + "type": "object", + "additionalProperties": {"$ref": "#/$defs/PropertyType"} + } + } + }, + "RelationshipType": { + "type": "object", + "additionalProperties": false, + "required": ["type", "from", "to", "properties"], + "properties": { + "type": { + "type": "string", + "pattern": "^(HAS_ARTIFACT|DEFINES_CONFIG|IAC_[A-Z0-9_]+)$" + }, + "from": { + "type": "array", + "items": {"$ref": "#/$defs/LabelName"}, + "minItems": 1, + "uniqueItems": true + }, + "to": { + "type": "array", + "items": {"$ref": "#/$defs/LabelName"}, + "minItems": 1, + "uniqueItems": true + }, + "properties": { + "type": "object", + "additionalProperties": {"$ref": "#/$defs/PropertyType"} + } + } + }, + "LabelName": { + "type": "string", + "pattern": "^(Application|Artifact|ConfigKey|Package|IdentityAlias|(IaC|Helm|Kubernetes|CodeAnalyzerIaC)[A-Za-z0-9_]*)$" + }, + "PropertyType": { + "type": "string", + "enum": ["string", "integer", "float", "boolean", "string[]", "integer[]", "float[]", "boolean[]"] + } + } +} diff --git a/v2/iac/neo4j/schema.neo4j.sample.json b/v2/iac/neo4j/schema.neo4j.sample.json new file mode 100644 index 0000000..9b5655c --- /dev/null +++ b/v2/iac/neo4j/schema.neo4j.sample.json @@ -0,0 +1,90 @@ +{ + "schema_version": "1.0.0", + "generator": "codeanalyzer-iac", + "marker_labels": [], + "node_labels": [ + {"label": "Application", "merge_label": "Application", "key": "id", "properties": {"id": "string"}}, + {"label": "IaCApplication", "merge_label": "Application", "key": "id", "properties": {"iac_analyzer_version": "string", "iac_app_id": "string", "iac_producer": "string", "id": "string"}}, + {"label": "Artifact", "merge_label": "Artifact", "key": "id", "properties": {"format": "string", "id": "string", "path": "string", "sha256": "string", "size_bytes": "integer", "source": "string"}}, + {"label": "IaCArtifact", "merge_label": "Artifact", "key": "id", "properties": {"iac_analyzer_version": "string", "iac_app_id": "string", "iac_dialect": "string", "iac_kind": "string", "iac_producer": "string", "iac_status": "string", "id": "string"}}, + {"label": "HelmArtifact", "merge_label": "Artifact", "key": "id", "properties": {"iac_analyzer_version": "string", "iac_app_id": "string", "iac_dialect": "string", "iac_kind": "string", "iac_producer": "string", "iac_status": "string", "id": "string"}}, + {"label": "HelmChart", "merge_label": "Artifact", "key": "id", "properties": {"helm_annotations_json": "string", "helm_api_version": "string", "helm_app_version": "string", "helm_chart_type": "string", "helm_deprecated": "boolean", "helm_description": "string", "helm_home": "string", "helm_icon": "string", "helm_keywords": "string[]", "helm_kube_version": "string", "helm_maintainers_json": "string", "helm_name": "string", "helm_sources": "string[]", "helm_version": "string", "iac_analyzer_version": "string", "iac_app_id": "string", "iac_dialect": "string", "iac_kind": "string", "iac_producer": "string", "iac_status": "string", "id": "string"}}, + {"label": "HelmRequirements", "merge_label": "Artifact", "key": "id", "properties": {"helm_roles": "string[]", "iac_analyzer_version": "string", "iac_app_id": "string", "iac_dialect": "string", "iac_kind": "string", "iac_producer": "string", "iac_status": "string", "id": "string"}}, + {"label": "HelmLock", "merge_label": "Artifact", "key": "id", "properties": {"helm_roles": "string[]", "iac_analyzer_version": "string", "iac_app_id": "string", "iac_dialect": "string", "iac_kind": "string", "iac_producer": "string", "iac_status": "string", "id": "string"}}, + {"label": "HelmValues", "merge_label": "Artifact", "key": "id", "properties": {"helm_roles": "string[]", "iac_analyzer_version": "string", "iac_app_id": "string", "iac_dialect": "string", "iac_kind": "string", "iac_producer": "string", "iac_status": "string", "id": "string"}}, + {"label": "HelmValuesSchema", "merge_label": "Artifact", "key": "id", "properties": {"helm_roles": "string[]", "iac_analyzer_version": "string", "iac_app_id": "string", "iac_dialect": "string", "iac_kind": "string", "iac_producer": "string", "iac_status": "string", "id": "string"}}, + {"label": "HelmTemplate", "merge_label": "Artifact", "key": "id", "properties": {"helm_roles": "string[]", "iac_analyzer_version": "string", "iac_app_id": "string", "iac_dialect": "string", "iac_kind": "string", "iac_producer": "string", "iac_status": "string", "id": "string"}}, + {"label": "HelmCRD", "merge_label": "Artifact", "key": "id", "properties": {"helm_roles": "string[]", "iac_analyzer_version": "string", "iac_app_id": "string", "iac_dialect": "string", "iac_kind": "string", "iac_producer": "string", "iac_status": "string", "id": "string"}}, + {"label": "HelmIgnore", "merge_label": "Artifact", "key": "id", "properties": {"helm_roles": "string[]", "iac_analyzer_version": "string", "iac_app_id": "string", "iac_dialect": "string", "iac_kind": "string", "iac_producer": "string", "iac_status": "string", "id": "string"}}, + {"label": "ConfigKey", "merge_label": "ConfigKey", "key": "id", "properties": {"id": "string", "name": "string", "path": "string", "span_json": "string"}}, + {"label": "IaCValue", "merge_label": "ConfigKey", "key": "id", "properties": {"iac_analyzer_version": "string", "iac_app_id": "string", "iac_kind": "string", "iac_producer": "string", "id": "string"}}, + {"label": "HelmValue", "merge_label": "ConfigKey", "key": "id", "properties": {"iac_analyzer_version": "string", "iac_app_id": "string", "iac_kind": "string", "iac_producer": "string", "id": "string"}}, + {"label": "CodeAnalyzerIaCConfig", "merge_label": "Artifact", "key": "id", "properties": {"iac_analyzer_version": "string", "iac_app_id": "string", "iac_config_version": "integer", "iac_producer": "string", "id": "string"}}, + {"label": "HelmDependency", "merge_label": "HelmDependency", "key": "id", "properties": {"alias": "string", "analyzer_version": "string", "condition": "string", "iac_app_id": "string", "id": "string", "import_values_json": "string", "name": "string", "producer": "string", "repository": "string", "span_json": "string", "tags": "string[]", "version_constraint": "string"}}, + {"label": "HelmChartReference", "merge_label": "HelmChartReference", "key": "id", "properties": {"analyzer_version": "string", "iac_app_id": "string", "id": "string", "name": "string", "producer": "string", "purl": "string", "repository": "string", "resolved_chart_id": "string", "version_constraint": "string"}}, + {"label": "HelmNamedTemplate", "merge_label": "HelmNamedTemplate", "key": "id", "properties": {"analyzer_version": "string", "iac_app_id": "string", "id": "string", "name": "string", "producer": "string", "span_json": "string"}}, + {"label": "HelmTemplateCall", "merge_label": "HelmTemplateCall", "key": "id", "properties": {"analyzer_version": "string", "call_kind": "string", "iac_app_id": "string", "id": "string", "name_expression": "string", "producer": "string", "span_json": "string", "target_id": "string"}}, + {"label": "HelmValueReference", "merge_label": "HelmValueReference", "key": "id", "properties": {"analyzer_version": "string", "iac_app_id": "string", "id": "string", "path_expression": "string", "producer": "string", "span_json": "string", "target_id": "string"}}, + {"label": "HelmResourceTemplate", "merge_label": "HelmResourceTemplate", "key": "id", "properties": {"analyzer_version": "string", "document_index": "integer", "iac_app_id": "string", "id": "string", "producer": "string", "span_json": "string"}}, + {"label": "HelmLookupReference", "merge_label": "HelmLookupReference", "key": "id", "properties": {"analyzer_version": "string", "group_expression": "string", "iac_app_id": "string", "id": "string", "name_expression": "string", "namespace_expression": "string", "producer": "string", "resource_kind_expression": "string", "span_json": "string", "version_expression": "string"}}, + {"label": "HelmRenderProfile", "merge_label": "HelmRenderProfile", "key": "id", "properties": {"analyzer_version": "string", "api_versions": "string[]", "chart_id": "string", "iac_app_id": "string", "id": "string", "kube_version": "string", "name": "string", "namespace": "string", "origin": "string", "producer": "string", "release_name": "string"}}, + {"label": "HelmValueLayer", "merge_label": "HelmValueLayer", "key": "id", "properties": {"analyzer_version": "string", "iac_app_id": "string", "id": "string", "ordinal": "integer", "producer": "string", "source_id": "string"}}, + {"label": "HelmRender", "merge_label": "HelmRender", "key": "id", "properties": {"analyzer_version": "string", "effective_values_sha256": "string", "iac_app_id": "string", "id": "string", "phase": "string", "producer": "string", "profile_id": "string", "renderer_name": "string", "renderer_version": "string", "status": "string", "value_layer_ids": "string[]"}}, + {"label": "IaCDiagnostic", "merge_label": "IaCDiagnostic", "key": "id", "properties": {"analyzer_version": "string", "artifact_id": "string", "code": "string", "iac_app_id": "string", "id": "string", "message": "string", "phase": "string", "producer": "string", "severity": "string", "span_json": "string"}}, + {"label": "HelmDiagnostic", "merge_label": "IaCDiagnostic", "key": "id", "properties": {"analyzer_version": "string", "artifact_id": "string", "code": "string", "iac_app_id": "string", "id": "string", "message": "string", "phase": "string", "producer": "string", "severity": "string", "span_json": "string"}}, + {"label": "KubernetesResource", "merge_label": "KubernetesResource", "key": "id", "properties": {"address_id": "string", "analyzer_version": "string", "annotations_json": "string", "api_version": "string", "generate_name": "string", "iac_app_id": "string", "id": "string", "labels_json": "string", "manifest_sha256": "string", "name": "string", "namespace": "string", "origin_ids": "string[]", "plural": "string", "producer": "string", "render_id": "string", "resource_kind": "string", "secret_data_json": "string"}}, + {"label": "KubernetesResourceAddress", "merge_label": "KubernetesResourceAddress", "key": "id", "properties": {"analyzer_version": "string", "group": "string", "iac_app_id": "string", "id": "string", "name": "string", "namespace": "string", "plural": "string", "producer": "string", "resource_kind": "string"}}, + {"label": "IdentityAlias", "merge_label": "IdentityAlias", "key": "id", "properties": {"analyzer_version": "string", "iac_app_id": "string", "iac_kind": "string", "id": "string", "producer": "string", "target": "string"}}, + {"label": "IaCAlias", "merge_label": "IdentityAlias", "key": "id", "properties": {"analyzer_version": "string", "iac_app_id": "string", "iac_kind": "string", "id": "string", "producer": "string", "target": "string"}}, + {"label": "Package", "merge_label": "Package", "key": "id", "properties": {"id": "string", "purl": "string"}} + ], + "relationship_types": [ + {"type": "HAS_ARTIFACT", "from": ["IaCApplication"], "to": ["Artifact"], "properties": {}}, + {"type": "DEFINES_CONFIG", "from": ["Artifact"], "to": ["ConfigKey"], "properties": {}}, + {"type": "IAC_PART_OF_CHART", "from": ["HelmArtifact"], "to": ["HelmChart"], "properties": {}}, + {"type": "IAC_DECLARES_DEPENDENCY", "from": ["HelmChart"], "to": ["HelmDependency"], "properties": {}}, + {"type": "IAC_TARGETS_CHART_REFERENCE", "from": ["HelmDependency"], "to": ["HelmChartReference"], "properties": {}}, + {"type": "IAC_RESOLVES_TO_CHART", "from": ["HelmChartReference"], "to": ["HelmChart"], "properties": {}}, + {"type": "IAC_IDENTIFIED_BY_PACKAGE", "from": ["HelmChartReference"], "to": ["Package"], "properties": {}}, + {"type": "IAC_DEFINES_TEMPLATE", "from": ["HelmTemplate"], "to": ["HelmNamedTemplate"], "properties": {}}, + {"type": "IAC_HAS_TEMPLATE_CALL", "from": ["HelmTemplate"], "to": ["HelmTemplateCall"], "properties": {}}, + {"type": "IAC_CALLS_TEMPLATE", "from": ["HelmTemplateCall"], "to": ["HelmNamedTemplate"], "properties": {}}, + {"type": "IAC_HAS_VALUE_REFERENCE", "from": ["HelmTemplate"], "to": ["HelmValueReference"], "properties": {}}, + {"type": "IAC_REFERENCES_VALUE", "from": ["HelmValueReference"], "to": ["HelmValue"], "properties": {}}, + {"type": "IAC_HAS_RESOURCE_TEMPLATE", "from": ["HelmTemplate"], "to": ["HelmResourceTemplate"], "properties": {}}, + {"type": "IAC_HAS_LOOKUP_REFERENCE", "from": ["HelmTemplate"], "to": ["HelmLookupReference"], "properties": {}}, + {"type": "IAC_DECLARES_PROFILE", "from": ["CodeAnalyzerIaCConfig", "HelmChart"], "to": ["HelmRenderProfile"], "properties": {}}, + {"type": "IAC_RENDERS_CHART", "from": ["HelmRenderProfile"], "to": ["HelmChart"], "properties": {}}, + {"type": "IAC_HAS_VALUE_LAYER", "from": ["HelmRenderProfile"], "to": ["HelmValueLayer"], "properties": {}}, + {"type": "IAC_READS_FROM", "from": ["HelmValueLayer"], "to": ["Artifact", "ConfigKey"], "properties": {}}, + {"type": "IAC_HAS_RENDER", "from": ["HelmChart"], "to": ["HelmRender"], "properties": {}}, + {"type": "IAC_CONFIGURED_BY", "from": ["HelmRender"], "to": ["HelmRenderProfile"], "properties": {}}, + {"type": "IAC_HAS_DIAGNOSTIC", "from": ["Artifact", "HelmRender"], "to": ["HelmDiagnostic"], "properties": {}}, + {"type": "IAC_PRODUCES", "from": ["HelmRender"], "to": ["KubernetesResource"], "properties": {}}, + {"type": "IAC_TARGETS_RESOURCE", "from": ["KubernetesResource"], "to": ["KubernetesResourceAddress"], "properties": {}}, + {"type": "IAC_DERIVED_FROM", "from": ["KubernetesResource"], "to": ["HelmResourceTemplate"], "properties": {}}, + {"type": "IAC_HAS_ALIAS", "from": ["Artifact"], "to": ["IaCAlias"], "properties": {}}, + {"type": "IAC_ALIAS_OF", "from": ["IaCAlias"], "to": ["Artifact", "KubernetesResourceAddress"], "properties": {}} + ], + "constraints": [ + "CREATE CONSTRAINT application_id IF NOT EXISTS FOR (n:Application) REQUIRE n.id IS UNIQUE", + "CREATE CONSTRAINT artifact_id IF NOT EXISTS FOR (n:Artifact) REQUIRE n.id IS UNIQUE", + "CREATE CONSTRAINT config_key_id IF NOT EXISTS FOR (n:ConfigKey) REQUIRE n.id IS UNIQUE", + "CREATE CONSTRAINT helm_dependency_id IF NOT EXISTS FOR (n:HelmDependency) REQUIRE n.id IS UNIQUE", + "CREATE CONSTRAINT helm_chart_reference_id IF NOT EXISTS FOR (n:HelmChartReference) REQUIRE n.id IS UNIQUE", + "CREATE CONSTRAINT helm_named_template_id IF NOT EXISTS FOR (n:HelmNamedTemplate) REQUIRE n.id IS UNIQUE", + "CREATE CONSTRAINT helm_template_call_id IF NOT EXISTS FOR (n:HelmTemplateCall) REQUIRE n.id IS UNIQUE", + "CREATE CONSTRAINT helm_value_reference_id IF NOT EXISTS FOR (n:HelmValueReference) REQUIRE n.id IS UNIQUE", + "CREATE CONSTRAINT helm_resource_template_id IF NOT EXISTS FOR (n:HelmResourceTemplate) REQUIRE n.id IS UNIQUE", + "CREATE CONSTRAINT helm_lookup_reference_id IF NOT EXISTS FOR (n:HelmLookupReference) REQUIRE n.id IS UNIQUE", + "CREATE CONSTRAINT helm_render_profile_id IF NOT EXISTS FOR (n:HelmRenderProfile) REQUIRE n.id IS UNIQUE", + "CREATE CONSTRAINT helm_value_layer_id IF NOT EXISTS FOR (n:HelmValueLayer) REQUIRE n.id IS UNIQUE", + "CREATE CONSTRAINT helm_render_id IF NOT EXISTS FOR (n:HelmRender) REQUIRE n.id IS UNIQUE", + "CREATE CONSTRAINT iac_diagnostic_id IF NOT EXISTS FOR (n:IaCDiagnostic) REQUIRE n.id IS UNIQUE", + "CREATE CONSTRAINT kubernetes_resource_id IF NOT EXISTS FOR (n:KubernetesResource) REQUIRE n.id IS UNIQUE", + "CREATE CONSTRAINT kubernetes_resource_address_id IF NOT EXISTS FOR (n:KubernetesResourceAddress) REQUIRE n.id IS UNIQUE", + "CREATE CONSTRAINT identity_alias_id IF NOT EXISTS FOR (n:IdentityAlias) REQUIRE n.id IS UNIQUE", + "CREATE CONSTRAINT package_id IF NOT EXISTS FOR (n:Package) REQUIRE n.id IS UNIQUE" + ], + "indexes": [] +} diff --git a/v2/java/.gitkeep b/v2/java/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/v2/python/.gitkeep b/v2/python/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/v2/typescript/.gitkeep b/v2/typescript/.gitkeep new file mode 100644 index 0000000..e69de29