refactor(search): read OPENSEARCH_* env per call
Some checks failed
CI / check (push) Failing after 12s

Tests can't override values captured at module load because ES
imports hoist before any Deno.env.set call. Reading env on each
helper invocation makes the module trivially testable and has
no production cost (env doesn't change at runtime).
This commit is contained in:
Julien Calixte
2026-06-07 22:23:36 +02:00
parent 6159ec00e0
commit 02f3139f46

View File

@@ -1,21 +1,19 @@
import type { API } from "@opensearch-project/opensearch";
import { log } from "../log.ts";
const OPENSEARCH_URL = Deno.env.get("OPENSEARCH_URL");
const OPENSEARCH_USERNAME = Deno.env.get("OPENSEARCH_USERNAME");
const OPENSEARCH_PASSWORD = Deno.env.get("OPENSEARCH_PASSWORD");
const OPENSEARCH_INDEX = Deno.env.get("OPENSEARCH_INDEX") ?? "notes";
const MAX_LIMIT = 100;
const enabled = (): boolean => {
if (!OPENSEARCH_URL) return false;
return true;
};
const openSearchUrl = (): string | undefined => Deno.env.get("OPENSEARCH_URL");
const openSearchIndex = (): string =>
Deno.env.get("OPENSEARCH_INDEX") ?? "notes";
const enabled = (): boolean => Boolean(openSearchUrl());
const authHeaders = (): Record<string, string> => {
if (OPENSEARCH_USERNAME && OPENSEARCH_PASSWORD) {
const creds = btoa(`${OPENSEARCH_USERNAME}:${OPENSEARCH_PASSWORD}`);
const username = Deno.env.get("OPENSEARCH_USERNAME");
const password = Deno.env.get("OPENSEARCH_PASSWORD");
if (username && password) {
const creds = btoa(`${username}:${password}`);
return { Authorization: `Basic ${creds}` };
}
return {};
@@ -29,7 +27,7 @@ const request = async (
path: string,
body?: unknown,
): Promise<Response> => {
const url = `${OPENSEARCH_URL}${path}`;
const url = `${openSearchUrl()}${path}`;
const headers: Record<string, string> = { ...authHeaders() };
if (body !== undefined) headers["Content-Type"] = "application/json";
return await fetch(url, {
@@ -59,11 +57,12 @@ const indexMappings = {
export const ensureIndex = async (): Promise<void> => {
if (!enabled()) return;
const head = await request("HEAD", `/${OPENSEARCH_INDEX}`);
const index = openSearchIndex();
const head = await request("HEAD", `/${index}`);
if (head.status === 200) return;
const res = await request("PUT", `/${OPENSEARCH_INDEX}`, indexMappings);
const res = await request("PUT", `/${index}`, indexMappings);
if (res.ok) {
log(`[opensearch] created index ${OPENSEARCH_INDEX}`);
log(`[opensearch] created index ${index}`);
return;
}
const text = await res.text();
@@ -96,7 +95,7 @@ export const indexNote = async (note: IndexedNote): Promise<void> => {
};
const res = await request(
"PUT",
`/${OPENSEARCH_INDEX}/_doc/${docId(note.did, note.rkey)}`,
`/${openSearchIndex()}/_doc/${docId(note.did, note.rkey)}`,
doc,
);
if (!res.ok) {
@@ -114,7 +113,7 @@ export const removeNote = async (
if (!enabled()) return;
const res = await request(
"DELETE",
`/${OPENSEARCH_INDEX}/_doc/${docId(did, rkey)}`,
`/${openSearchIndex()}/_doc/${docId(did, rkey)}`,
);
if (!res.ok && res.status !== 404) {
const text = await res.text();
@@ -219,7 +218,7 @@ export const searchNotes = async (
...(searchAfter ? { search_after: searchAfter } : {}),
};
const res = await request("POST", `/${OPENSEARCH_INDEX}/_search`, body);
const res = await request("POST", `/${openSearchIndex()}/_search`, body);
if (!res.ok) {
const text = await res.text();
throw new Error(`Search failed: ${res.status} ${text}`);