From 39fcc8b9d6c0dd0674eb91f2268db781c9996078 Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Mon, 27 Apr 2026 22:45:08 +0530 Subject: [PATCH 1/3] chore: adds nyt source doc Signed-off-by: Amit Singh --- sources/nyt.yaml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 sources/nyt.yaml diff --git a/sources/nyt.yaml b/sources/nyt.yaml new file mode 100644 index 0000000..3f0b3d1 --- /dev/null +++ b/sources/nyt.yaml @@ -0,0 +1,4 @@ +applicant: "The New York Times" +summary: "The New York Times is dedicated to helping people understand and engage with the world through on-the-ground, expert and deeply reported independent journalism." +tags: "american" +uri: "https://www.nytimes.com" \ No newline at end of file From 7273e0e4f89e2494a51c2fa68eb5028a59598e60 Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Tue, 28 Apr 2026 09:55:11 +0530 Subject: [PATCH 2/3] chore: adds nyt claim with proof Signed-off-by: Amit Singh --- .github/workflows/validate.yml | 2 +- claims/nyt/ai-backlash.yaml | 4 ++++ proofs/nyt/ai-backlash/supports/idahonews.yaml | 4 ++++ sources/nyt.yaml | 2 +- 4 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 claims/nyt/ai-backlash.yaml create mode 100644 proofs/nyt/ai-backlash/supports/idahonews.yaml diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index f879c85..c714814 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -1,7 +1,7 @@ name: Validate Request Documents on: - workflow_run: + workflow_dispatch: pull_request: branches: - main diff --git a/claims/nyt/ai-backlash.yaml b/claims/nyt/ai-backlash.yaml new file mode 100644 index 0000000..f812b02 --- /dev/null +++ b/claims/nyt/ai-backlash.yaml @@ -0,0 +1,4 @@ +sourceUriDigest: "f217d823160d39ca379a09044e1242ac641c8b1d79a1d6f499c59f08a1b67500" +summary: "The global push for AI infrastructure is increasingly clashing with local environmental and economic limits. In the U.S., massive data center expansion has sparked significant resistance due to the immense electricity and water these facilities require, which threatens to strain power grids and increase costs for residents. This tension represents a broader international challenge: balancing the resource-heavy needs of the tech sector against community sustainability and energy security." +title: "From Indiana to Idaho, a Backlash Against A.I. Gathers Momentum" +uri: "https://www.nytimes.com/2026/04/27/technology/ai-artificial-intelligence-backlash.html" \ No newline at end of file diff --git a/proofs/nyt/ai-backlash/supports/idahonews.yaml b/proofs/nyt/ai-backlash/supports/idahonews.yaml new file mode 100644 index 0000000..d6895f5 --- /dev/null +++ b/proofs/nyt/ai-backlash/supports/idahonews.yaml @@ -0,0 +1,4 @@ +claimUriDigest: "a6aa62d59056d0f6b74c26c26c3a8c561f6ec43320626443d2759bc771181464" +supportsClaim: true +reviewedBy: "semmet95" +uri: "https://idahonews.com/news/nation-world/ai-data-centers-spark-local-backlash-across-the-us-artificial-intelligence-electricity-utilities-noise-land-use-tax-incentives" \ No newline at end of file diff --git a/sources/nyt.yaml b/sources/nyt.yaml index 3f0b3d1..d04ee50 100644 --- a/sources/nyt.yaml +++ b/sources/nyt.yaml @@ -1,4 +1,4 @@ -applicant: "The New York Times" +name: "The New York Times" summary: "The New York Times is dedicated to helping people understand and engage with the world through on-the-ground, expert and deeply reported independent journalism." tags: "american" uri: "https://www.nytimes.com" \ No newline at end of file From a83b512a088b4eaa8378af47c8842ec2c5abea4d Mon Sep 17 00:00:00 2001 From: Amit Singh Date: Tue, 28 Apr 2026 15:35:41 +0530 Subject: [PATCH 3/3] chore: adds basic logging Signed-off-by: Amit Singh --- scripts/post_requests.py | 69 ++++++++++++++++++++++++++++++++-------- scripts/validate.py | 40 +++++++++++++++++------ 2 files changed, 87 insertions(+), 22 deletions(-) diff --git a/scripts/post_requests.py b/scripts/post_requests.py index 21a9036..a5546d6 100644 --- a/scripts/post_requests.py +++ b/scripts/post_requests.py @@ -12,6 +12,7 @@ import urllib.error import urllib.request from pathlib import Path +import traceback # Shared utilities (try package import first, fallback to local module) try: @@ -61,58 +62,100 @@ def post(url: str, data: dict, api_key: str) -> tuple[int, str]: def main() -> int: + def _info(msg: str, *args) -> None: + print("INFO:", msg % args if args else msg) + + def _error(msg: str, *args) -> None: + print("ERROR:", msg % args if args else msg, file=sys.stderr) + + def _exception(msg: str, *args) -> None: + print("ERROR:", msg % args if args else msg, file=sys.stderr) + traceback.print_exc(file=sys.stderr) + base_url = os.environ.get("API_BASE_URL", "").rstrip("/") api_key = os.environ.get("API_KEY", "") if not base_url: - print("API_BASE_URL environment variable is not set", file=sys.stderr) + _error("API_BASE_URL environment variable is not set") return 1 if not api_key: - print("API_KEY environment variable is not set", file=sys.stderr) + _error("API_KEY environment variable is not set") return 1 files = [f for f in os.environ.get("ADDED_FILES", "").splitlines() if f.strip()] if not files: - print("No added files to process.") + _info("No added files to process.") return 0 + _info("POST run: %d file(s) to process", len(files)) + spec = load_oapi("oapi.yaml") schema_paths = extract_post_paths(spec) failed = False + allowed_exts = {".yaml", ".yml", ".json"} for f in files: f = f.strip() - parts = Path(f).parts - if not parts or parts[0] not in SCHEMA_MAP: + if not f: + continue + + # normalize path parts (skip '.' and '..') + p = Path(f) + parts = [part for part in p.parts if part not in (".", "..")] + if not parts: continue folder = parts[0] + if folder not in SCHEMA_MAP: + continue + + norm_path = Path(*parts) + if norm_path.suffix.lower() not in allowed_exts: + _info("Skipping non-document file: %s", str(norm_path)) + continue + schema_name = SCHEMA_MAP[folder] path = schema_paths.get(schema_name) if not path: - print(f"No POST path found for schema {schema_name}, skipping {f}") + _error("No POST path found for schema %s, skipping %s", schema_name, str(norm_path)) failed = True continue url = f"{base_url}{path}" + # warn if BASE_URL likely duplicates path prefix (common misconfiguration) + path_segments = [seg for seg in path.split("/") if seg] + if path_segments and base_url.rstrip("/").endswith("/" + path_segments[0]): + _info("Warning: BASE_URL '%s' may duplicate path segment '%s' when combined with '%s'", base_url, path_segments[0], path) + + if not norm_path.exists(): + _error("%s: File not found", str(norm_path)) + failed = True + continue + + try: + data = load_doc(str(norm_path)) + except Exception as e: + _exception("%s: Failed to parse: %s", str(norm_path), e) + failed = True + continue - if not Path(f).exists(): - print(f"{f}: File not found") + if not isinstance(data, dict): + _error("%s: Parsed document is not a JSON object; skipping", str(norm_path)) failed = True continue + _info("Posting %s -> %s", str(norm_path), url) try: - data = load_doc(f) + status, body = post(url, data, api_key) except Exception as e: - print(f"{f}: Failed to parse: {e}") + _exception("%s: Request failed: %s", str(norm_path), e) failed = True continue - status, body = post(url, data, api_key) if 200 <= status < 300: - print(f"{f} → {url} ({status})") + _info("%s → %s (%d)", str(norm_path), url, status) else: - print(f"{f} → {url} ({status}): {body[:200]}") + _error("%s → %s (%d): %s", str(norm_path), url, status, body[:200]) failed = True return 1 if failed else 0 diff --git a/scripts/validate.py b/scripts/validate.py index 463e654..c67ed3f 100644 --- a/scripts/validate.py +++ b/scripts/validate.py @@ -1,10 +1,12 @@ #!/usr/bin/env python3 """Validate request documents against OpenAPI schemas.""" -import json import sys import os from pathlib import Path +import traceback +from referencing.jsonschema import DRAFT202012 +from referencing import Registry, Resource # Shared utilities (try package import first, fallback to local module) try: @@ -32,7 +34,10 @@ def resolve_schema(spec: dict, ref: str) -> dict: return current def validate(data: dict, schema: dict, spec: dict) -> list[str]: - registry = Registry().with_resource("oapi", Resource.from_contents(spec)) # type: ignore + registry = Registry().with_resource( + "oapi", + Resource.from_contents(spec, DRAFT202012) # explicit spec + ) validator = Draft202012Validator(schema, registry=registry) return [f"{e.json_path}: {e.message}" for e in validator.iter_errors(data)] @@ -45,21 +50,38 @@ def scan_tracked_files() -> list[str]: files.extend(Path(folder).rglob(ext)) return [str(f) for f in sorted(files)] + +def _info(msg: str, *args) -> None: + print("INFO:", msg % args if args else msg) + + +def _error(msg: str, *args) -> None: + print("ERROR:", msg % args if args else msg, file=sys.stderr) + + +def _exception(msg: str, *args) -> None: + print("ERROR:", msg % args if args else msg, file=sys.stderr) + traceback.print_exc(file=sys.stderr) + def main() -> int: files = sys.argv[1:] + if not files: - print("Validating all files...") + _info("Validating new docs...") files = scan_tracked_files() - return 0 spec = load_oapi("oapi.yaml") failed = False + _info("Validation run: %d file(s) to check", len(files)) + for f in files: f = f.strip() if not f: continue + _info("Validating %s", f) + parts = Path(f).parts if not parts or parts[0] not in SCHEMA_MAP: continue @@ -69,25 +91,25 @@ def main() -> int: schema = resolve_schema(spec, schema_ref) if not Path(f).exists(): - print(f"{f}: File not found") + _error("%s: File not found", f) failed = True continue try: data = load_doc(f) except Exception as e: - print(f"{f}: Failed to parse document: {e}") + _exception("%s: Failed to parse document: %s", f, e) failed = True continue errors = validate(data, schema, spec) if errors: - print(f"{f}:") + _error("%s: %d validation error(s)", f, len(errors)) for e in errors: - print(f" - {e}") + _error(" - %s", e) failed = True else: - print(f"{f}") + _info("%s: OK", f) return 1 if failed else 0