All files / src/lib neo4j.ts

81.81% Statements 27/33
82.92% Branches 34/41
100% Functions 4/4
86.2% Lines 25/29

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77    9x     9x 8x             8x 8x 8x 8x 8x   8x         8x                 9x               6x 6x 6x 2x                   2x     2x 2x 4x   2x             6x 6x 6x 5x   6x      
import neo4j, { Config, Driver } from "neo4j-driver";
 
let driver: Driver | null = null;
 
export function getDriver(): Driver {
  if (!driver) {
    const uri = process.env.NEO4J_URI ?? "bolt://localhost:7687";
 
    // Encryption is controlled by the URI scheme for +s / +ssc variants.
    // When the scheme is plain bolt:// or neo4j://, default to the driver's
    // implicit behaviour unless NEO4J_ENCRYPTED is explicitly set. ACA internal
    // TCP ingress is passthrough (no envoy TLS), so we want encryption OFF for
    // mvhd-neo4j.internal.* endpoints.
    const config: Config = { disableLosslessIntegers: true };
    const schemeHasTLS = /^(bolt|neo4j)\+s(sc)?:/.test(uri);
    Eif (!schemeHasTLS) {
      const encEnv = (process.env.NEO4J_ENCRYPTED ?? "").toLowerCase();
      Iif (encEnv === "false" || encEnv === "0" || encEnv === "no") {
        config.encrypted = "ENCRYPTION_OFF";
      I} else if (encEnv === "true" || encEnv === "1" || encEnv === "yes") {
        config.encrypted = "ENCRYPTION_ON";
      }
    }
 
    driver = neo4j.driver(
      uri,
      neo4j.auth.basic(
        process.env.NEO4J_USER ?? "neo4j",
        process.env.NEO4J_PASSWORD ?? "healthdataspace",
      ),
      config,
    );
  }
  return driver;
}
 
// Recursively convert Neo4j temporal types (Date, DateTime, Time, etc.) and
// Integer objects to JSON-safe primitives. React cannot render objects with
// {year, month, day} shape as children, so unconverted Date values crash the
// client with "Minified React error #31".
function normalizeNeo4jValue(v: unknown): unknown {
  Iif (v === null || v === undefined) return v;
  Iif (Array.isArray(v)) return v.map(normalizeNeo4jValue);
  if (typeof v !== "object") return v;
  Iif (
    neo4j.isDate(v as never) ||
    neo4j.isDateTime(v as never) ||
    neo4j.isLocalDateTime(v as never) ||
    neo4j.isTime(v as never) ||
    neo4j.isLocalTime(v as never) ||
    neo4j.isDuration(v as never)
  ) {
    return (v as { toString(): string }).toString();
  }
  Iif (neo4j.isInt(v as never)) {
    return (v as { toNumber(): number }).toNumber();
  }
  const out: Record<string, unknown> = {};
  for (const [k, val] of Object.entries(v as Record<string, unknown>)) {
    out[k] = normalizeNeo4jValue(val);
  }
  return out;
}
 
export async function runQuery<T>(
  cypher: string,
  params: Record<string, unknown> = {},
): Promise<T[]> {
  const session = getDriver().session();
  try {
    const result = await session.run(cypher, params);
    return result.records.map((r) => normalizeNeo4jValue(r.toObject()) as T);
  } finally {
    await session.close();
  }
}