toga-ai 1.0.913 → 1.0.914
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -6,7 +6,7 @@ project: API
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-29
|
|
10
10
|
owners: ["bala", "rgirish"]
|
|
11
11
|
files:
|
|
12
12
|
- api2/Component/Api/V2/V2.php
|
|
@@ -17,6 +17,7 @@ related:
|
|
|
17
17
|
- ../../../clients/compass-usa/features/item-catalogs-and-duplicate-items.md
|
|
18
18
|
- ../../../clients/elite/features/supply2-tableview-config-drift.md
|
|
19
19
|
- tableview-field-metadata.md
|
|
20
|
+
- ../../_underscore/features/calculated-sql-fields.md
|
|
20
21
|
---
|
|
21
22
|
|
|
22
23
|
## Summary
|
|
@@ -167,8 +168,40 @@ None — engine behavior. A specific client's view may carry its own `apiWhereCl
|
|
|
167
168
|
- **Operator allowlist.** `parseOptionsWhere` whitelists the operators above and rejects unknown
|
|
168
169
|
ones (e.g. `regexp` returns a 500) — see the Compass cost-centers doc. Numeric/regex filtering
|
|
169
170
|
that the grammar can't express must be done client-side or by adding an operator to api2.
|
|
171
|
+
- **⚠ The FIELD name is never validated — an unknown one is a bare 500 with an EMPTY body.**
|
|
172
|
+
`parseOptionsWhere` maps only the operator token; the field slot passes straight into the SQL.
|
|
173
|
+
A name no model declares reaches MySQL as `Unknown column '<x>' in 'where clause'`, nothing
|
|
174
|
+
catches it, and the request dies as a 500 with **no message at all** (the 1.0 monitor then reports
|
|
175
|
+
its fallback literal *"No message found in response payload"* — see
|
|
176
|
+
[v2-api-error-codes](v2-api-error-codes.md) note 13). Live case: a `_isServiceClass` condition
|
|
177
|
+
stored on Elite's `inventory_items` clause 500'd `GET /v2/items` **91 times in 5 minutes across
|
|
178
|
+
7 prod api2 instances** (2026-09-17). **Open gap:** api2 has no guard that rejects an unknown
|
|
179
|
+
`_`-prefixed field with a 400 before it reaches MySQL.
|
|
180
|
+
- **⚠ Deploy order: the `_underscore` model field ships BEFORE the migration that references it.**
|
|
181
|
+
An `_`-prefixed name in a clause only resolves if the model declares it `FIELD_SQL` with a
|
|
182
|
+
matching same-name static (e.g. `_qtyOnHand` on `_Model_Client_Item`) — see
|
|
183
|
+
[calculated SQL fields](../../_underscore/features/calculated-sql-fields.md). A clause is **data**,
|
|
184
|
+
so it goes live the instant the `dbchanges2` file runs, with no deploy gate; applying it first
|
|
185
|
+
takes the listing endpoint down for that tenant. Check the client's own model too — a
|
|
186
|
+
`_Model_<Client>_Item` inherits from `_Model_Client_Item` and may declare neither.
|
|
187
|
+
- **An unexplained field in a `where=` lives in the DB, not the frontend.** Filters reaching api2
|
|
188
|
+
are stored in `Client_<Tenant>.TableViews.apiWhereClause`, not written in app code. When a field
|
|
189
|
+
name appears in **no** repo and **no** git history, query `TableViews` across the client DBs —
|
|
190
|
+
that is where it is.
|
|
170
191
|
|
|
171
192
|
## Change history
|
|
193
|
+
- 2026-09-29 — **Recorded that an unvalidated field name in a stored clause is a production
|
|
194
|
+
outage class, not a typo.** `parseOptionsWhere` maps only the operator and passes the field slot
|
|
195
|
+
straight to MySQL, so an undeclared `_`-prefixed name becomes `Unknown column` → an uncaught
|
|
196
|
+
500 with an **empty body**. Live case: a draft `_isServiceClass` condition on Elite's
|
|
197
|
+
`inventory_items` view 500'd `GET /v2/items` 91 times in 5 minutes across 7 prod api2 instances
|
|
198
|
+
(2026-09-17); `_isServiceClass` was declared on no model and existed in no repo or git history.
|
|
199
|
+
Added the deploy-ordering rule (the `_underscore` `FIELD_SQL` field must be deployed **before**
|
|
200
|
+
the `dbchanges2` file that references it, because a clause is data and needs no deploy), the
|
|
201
|
+
debugging path (an unexplained `where=` field lives in `TableViews.apiWhereClause`, not the
|
|
202
|
+
frontend repo), and the open gap that api2 rejects unknown operators but not unknown fields.
|
|
203
|
+
Diagnosis only, no code changed. Prod re-verified 2026-09-29: zero `TableViews` rows across all
|
|
204
|
+
37 `Client_*` databases reference the field. (bala)
|
|
172
205
|
|
|
173
206
|
- 2026-09-17 — **Recorded that subqueries ARE possible in a stored `apiWhereClause`**: the parser
|
|
174
207
|
splits on literal spaces, so writing `/**/` in place of every space (MySQL reads it as
|
|
@@ -6,7 +6,7 @@ project: Database Changes
|
|
|
6
6
|
client: elite
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-29
|
|
10
10
|
owners: [tcox, bala, rgirish]
|
|
11
11
|
files:
|
|
12
12
|
- dbchanges2/Client_Elite/
|
|
@@ -297,6 +297,17 @@ for the technique and the parser mechanics.
|
|
|
297
297
|
required a `_isServiceClass` model field on `_Model_Elite_Item`; **that field does not exist and is
|
|
298
298
|
not needed** — do not add one.
|
|
299
299
|
|
|
300
|
+
**⚠ That earlier draft reached PRODUCTION and took `/v2/items` down.** Before the rewrite, the
|
|
301
|
+
stored clause was `(Items.partNumber:excludes:SVC-,_isServiceClass:eq:0)`. `_isServiceClass` is
|
|
302
|
+
declared on neither `_Model_Client_Item` nor `_Model_Elite_Item`, so the raw name went into the SQL
|
|
303
|
+
and MySQL threw `Unknown column '_isServiceClass' in 'where clause'` — **91 empty-body 500s on
|
|
304
|
+
`GET /v2/items` in 5 minutes across 7 prod api2 instances (2026-09-17)**. A stored clause needs no
|
|
305
|
+
deploy, so it went live the moment the migration ran. Closed: `2026-09-15a` reverted the clause and
|
|
306
|
+
the current one uses only `_qtyOnHand` / `_qtyAvailable`, which **are** declared `FIELD_SQL`.
|
|
307
|
+
Re-verified 2026-09-29 — zero `TableViews` rows in any of the 37 prod `Client_*` databases mention
|
|
308
|
+
`ServiceClass`. The general rule and the debugging path are in
|
|
309
|
+
[apiWhereClause row filtering](../../../2.0/apps/api2/features/tableview-apiwhereclause-row-filtering.md#gotchas--known-issues).
|
|
310
|
+
|
|
300
311
|
### Why the class check replaced the `SVC-` prefix
|
|
301
312
|
|
|
302
313
|
The `SVC-` prefix was an unreliable signal. Verified on **prod `Client_Elite`** (2026-09-15):
|
|
@@ -388,6 +399,15 @@ the old `SVC-` prefix check back into the clause.
|
|
|
388
399
|
[error-reporting-issue-event](../../../2.0/apps/_underscore/features/error-reporting-issue-event.md).
|
|
389
400
|
|
|
390
401
|
## Change history
|
|
402
|
+
- 2026-09-29 — **Recorded that the abandoned `_isServiceClass` draft clause actually shipped to
|
|
403
|
+
production and caused an outage**, which this doc previously reported only as "does not exist and
|
|
404
|
+
is not needed". The stored clause `(Items.partNumber:excludes:SVC-,_isServiceClass:eq:0)` put an
|
|
405
|
+
undeclared field into the SQL, giving `Unknown column '_isServiceClass' in 'where clause'` and 91
|
|
406
|
+
empty-body 500s on `GET /v2/items` in 5 minutes across 7 prod api2 instances (2026-09-17).
|
|
407
|
+
Incident is closed — `2026-09-15a` reverted the clause, the live one uses only the declared
|
|
408
|
+
`FIELD_SQL` fields `_qtyOnHand` / `_qtyAvailable`, and a 2026-09-29 sweep of all 37 prod
|
|
409
|
+
`Client_*` databases found no `TableViews` row mentioning `ServiceClass`. Diagnosis only, no code
|
|
410
|
+
changed. (bala)
|
|
391
411
|
- 2026-09-17 — **Replaced the `SVC-` partNumber prefix filter with an ItemClass-hierarchy check**
|
|
392
412
|
on the `inventory_items` view (`2026-09-15a - InventoryHideServiceClassItems.sql`): the clause
|
|
393
413
|
now walks the `ItemClasses` parent chain and excludes items whose ROOT class id is 1, 22 or 40,
|
package/package.json
CHANGED