Skip to content

fix: double-encoded SoQL wildcard broke agency search - #6

Merged
noneck merged 1 commit into
mainfrom
fix/agency-search-double-encoding
Jul 6, 2026
Merged

fix: double-encoded SoQL wildcard broke agency search#6
noneck merged 1 commit into
mainfrom
fix/agency-search-double-encoding

Conversation

@noneck

@noneck noneck commented Jul 6, 2026

Copy link
Copy Markdown
Member

What was broken

getNoticesByAgency built its SoQL $where value with %25 and encodeURIComponent(agencyName) already baked in, then URLSearchParams.set encoded the whole value a second time. Socrata received a literal %25NAME%25 pattern (with %20 for spaces) instead of the % wildcard, so get_notices_by_agency LIKE matches — especially agency names with spaces or special characters — silently failed (empty results).

Fix

Per the SoQL like docs, the wildcard is a literal % in the query; %25 is only its URL encoding, which the HTTP layer already applies once. The SoQL value is now built unencoded (single quotes escaped by doubling per SQL rules) and URLSearchParams performs the single encoding pass.

Adds a minimal node:test regression test (test/encoding.test.mjs) that mocks fetch, asserts the decoded $where contains the literal % wildcard and raw agency name, and that the raw query string contains %25 but not %2525. Adds the npm test script to run it.

Test plan

  • npm run build — clean
  • Test verified to fail against the unfixed code (main's src/city-record.ts): actual '%25Parks%20%26%20Recreation%25' vs expected '%Parks & Recreation%'
  • npm test passes with the fix

Note

Draft PR #5 (refactor/mcpserver-migration) contains this same fix and will need a rebase after this merges.

🤖 Generated with Claude Code

The $where value embedded %25 and encodeURIComponent output, which
URLSearchParams.set then encoded again — Socrata received a literal
'%25NAME%25' pattern (with %20 for spaces) instead of the % wildcard,
so agency matches with spaces or special characters silently failed.

Per the SoQL like docs (https://dev.socrata.com/docs/functions/like.html)
the wildcard is a literal % in the query; %25 is only its URL encoding,
which the HTTP layer already applies once. Build the SoQL value
unencoded (escaping single quotes by doubling) and let searchParams.set
do the single encoding pass. Test written first; it fails against the
old code and passes now.

Also fix the test script glob (node --test with a bare directory arg
fails on newer Node).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@noneck
noneck merged commit c0dd6f2 into main Jul 6, 2026
2 checks passed
@noneck
noneck deleted the fix/agency-search-double-encoding branch July 6, 2026 11:00
noneck added a commit that referenced this pull request Jul 6, 2026
…refresh

- Bump package.json/lockfile to 1.0.2.
- Server reads its version from package.json via createRequire instead of
  a hardcoded string.
- CHANGELOG: move Unreleased items into 1.0.2 (double-encoding fix #6,
  SoQL escaping + enum enforcement + NY-timezone today + error detail #7,
  hono bump #4, CI #3, release automation #8).
- README: Claude Code config updated to .mcp.json / claude mcp add;
  add npm test instructions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
noneck added a commit that referenced this pull request Jul 6, 2026
* fix: SoQL escaping, NY-timezone today, error detail

- Add soqlString() helper (quote-doubling) and use it in every $where
  builder: getNoticesByType, getNoticesByAgency, getNoticesByDateRange.
- Validate notice_type with z.enum (mirrors the advertised tool enum,
  now derived from a single NOTICE_TYPES constant) and date params as
  YYYY-MM-DD.
- getOpenSolicitations: compute "today" in America/New_York (nyToday())
  instead of UTC, which dropped same-day deadlines after 8pm ET.
- Include the (truncated, 300-char) Socrata error body in thrown errors.
- Live-verified 2026-07-06: dataset's distinct type_of_notice_description
  values exactly match the hardcoded lists/enum (10 values, plus null
  rows); all start_date values are midnight, so the <= end bound is
  correct (documented in code).
- Tests: escaping regressions, injected-clock NY-date checks (EDT/EST),
  error-body inclusion + truncation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(release): 1.0.2 — version from package.json, changelog, README refresh

- Bump package.json/lockfile to 1.0.2.
- Server reads its version from package.json via createRequire instead of
  a hardcoded string.
- CHANGELOG: move Unreleased items into 1.0.2 (double-encoding fix #6,
  SoQL escaping + enum enforcement + NY-timezone today + error detail #7,
  hono bump #4, CI #3, release automation #8).
- README: Claude Code config updated to .mcp.json / claude mcp add;
  add npm test instructions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant