initial push
This commit is contained in:
195
scripts/test-licitaya-api.mjs
Normal file
195
scripts/test-licitaya-api.mjs
Normal file
@@ -0,0 +1,195 @@
|
||||
import { readFileSync, existsSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
const DEFAULT_TIMEOUT_MS = 20000;
|
||||
|
||||
function loadDotenv(filePath) {
|
||||
if (!existsSync(filePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const raw = readFileSync(filePath, "utf8");
|
||||
|
||||
for (const line of raw.split(/\r?\n/)) {
|
||||
const trimmed = line.trim();
|
||||
|
||||
if (!trimmed || trimmed.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const eqIndex = trimmed.indexOf("=");
|
||||
if (eqIndex === -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const key = trimmed.slice(0, eqIndex).trim();
|
||||
if (!key || process.env[key] !== undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let value = trimmed.slice(eqIndex + 1).trim();
|
||||
|
||||
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
||||
value = value.slice(1, -1);
|
||||
}
|
||||
|
||||
process.env[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const args = {
|
||||
baseUrl: process.env.LICITAYA_BASE_URL,
|
||||
endpoint: process.env.LICITAYA_TEST_ENDPOINT,
|
||||
accept: process.env.LICITAYA_ACCEPT || "application/json",
|
||||
method: "GET",
|
||||
timeoutMs: Number.parseInt(process.env.LICITAYA_TIMEOUT_MS || "", 10) || DEFAULT_TIMEOUT_MS,
|
||||
};
|
||||
|
||||
for (let i = 0; i < argv.length; i += 1) {
|
||||
const current = argv[i];
|
||||
const next = argv[i + 1];
|
||||
|
||||
if (current === "--base-url" && next) {
|
||||
args.baseUrl = next;
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current === "--endpoint" && next) {
|
||||
args.endpoint = next;
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current === "--accept" && next) {
|
||||
args.accept = next;
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current === "--method" && next) {
|
||||
args.method = next.toUpperCase();
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current === "--timeout" && next) {
|
||||
const parsed = Number.parseInt(next, 10);
|
||||
if (Number.isFinite(parsed) && parsed > 0) {
|
||||
args.timeoutMs = parsed;
|
||||
}
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
function buildUrl(baseUrl, endpoint) {
|
||||
if (!endpoint) {
|
||||
throw new Error("Missing LICITAYA_TEST_ENDPOINT (or --endpoint).");
|
||||
}
|
||||
|
||||
if (endpoint.includes("<") || endpoint.includes(">")) {
|
||||
throw new Error(
|
||||
"LICITAYA_TEST_ENDPOINT still contains placeholders. Use a real path such as /tender/search or /tender/<tenderId>.",
|
||||
);
|
||||
}
|
||||
|
||||
if (/^https?:\/\//i.test(endpoint)) {
|
||||
return new URL(endpoint);
|
||||
}
|
||||
|
||||
if (!baseUrl) {
|
||||
throw new Error("Missing LICITAYA_BASE_URL (or --base-url).");
|
||||
}
|
||||
|
||||
const normalizedBase = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
||||
const cleanEndpoint = endpoint.startsWith("/") ? endpoint.slice(1) : endpoint;
|
||||
|
||||
return new URL(cleanEndpoint, normalizedBase);
|
||||
}
|
||||
|
||||
function previewBody(rawBody, contentType) {
|
||||
const trimmed = rawBody.trim();
|
||||
|
||||
if (!trimmed) {
|
||||
return "(empty body)";
|
||||
}
|
||||
|
||||
const isJson = contentType.includes("application/json") || trimmed.startsWith("{") || trimmed.startsWith("[");
|
||||
|
||||
if (isJson) {
|
||||
try {
|
||||
const json = JSON.parse(trimmed);
|
||||
return JSON.stringify(json, null, 2);
|
||||
} catch {
|
||||
return trimmed.slice(0, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
return trimmed.slice(0, 3000);
|
||||
}
|
||||
|
||||
loadDotenv(resolve(process.cwd(), ".env"));
|
||||
|
||||
const apiKey = process.env.LICITAYA_API_KEY || process.env.X_API_KEY || process.env.X_API_KEY_LICITAYA;
|
||||
if (!apiKey) {
|
||||
console.error("Missing API key. Set LICITAYA_API_KEY in .env or shell env.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
|
||||
let url;
|
||||
|
||||
try {
|
||||
url = buildUrl(args.baseUrl, args.endpoint);
|
||||
} catch (error) {
|
||||
console.error(error instanceof Error ? error.message : String(error));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), args.timeoutMs);
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: args.method,
|
||||
headers: {
|
||||
"X-API-KEY": apiKey,
|
||||
Accept: args.accept,
|
||||
},
|
||||
signal: controller.signal,
|
||||
});
|
||||
|
||||
const contentType = response.headers.get("content-type") || "";
|
||||
const rawBody = await response.text();
|
||||
const bodyPreview = previewBody(rawBody, contentType);
|
||||
|
||||
console.log(`URL: ${url.toString()}`);
|
||||
console.log(`Method: ${args.method}`);
|
||||
console.log(`Status: ${response.status} ${response.statusText}`);
|
||||
console.log(`Content-Type: ${contentType || "(none)"}`);
|
||||
console.log("--- Response Preview ---");
|
||||
console.log(bodyPreview);
|
||||
|
||||
if (response.status === 404 && url.pathname.endsWith("/tender/search")) {
|
||||
console.error("No tenders matched the current filters. Try a broader keyword or fewer filters.");
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.name === "AbortError") {
|
||||
console.error(`Request timed out after ${args.timeoutMs} ms.`);
|
||||
} else {
|
||||
console.error(error instanceof Error ? error.message : String(error));
|
||||
}
|
||||
process.exit(1);
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
Reference in New Issue
Block a user