LiteSun opened a new pull request, #3477:
URL: https://github.com/apache/apisix-dashboard/pull/3477

   Please answer these questions before submitting a pull request, **or your PR 
will get closed**.
   
   **Why submit this pull request?**
   
   - [x] Bugfix
   - [ ] New feature provided
   - [ ] Improve performance
   - [ ] Backport patches
   
   **What changes will this PR take into?**
   
   A route (or service / stream route) whose inline upstream section the user 
never opened could not be saved from the dashboard: the request carried a 
half-built `upstream` object and the Admin API rejected it. This is #3395 — a 
plugins-only route such as the `public-api` metrics example needs no upstream, 
yet the form forced one.
   
   ### Root cause
   
   The Route / Service / Stream Route forms always render the inline upstream 
section, and its Select / Switch widgets register a value the moment they mount 
(`scheme=http`, `type=roundrobin`, `hash_on=vars`, `pass_host=pass`, 
`tls.verify=false`, plus `discovery_args={}` from the JSON input). 
react-hook-form keeps those in the submit values, so the Add Route form filled 
with only Name + URI + `public-api: {}` POSTs
   
   ```json
   
{"uri":"/…","upstream":{"scheme":"http","type":"roundrobin","hash_on":"vars","pass_host":"pass","tls":{"verify":false},"discovery_args":{}},"enable_websocket":false,"priority":0,"status":1,"name":"…","plugins":{"public-api":{}}}
   ```
   
   and the Admin API answers `400 invalid configuration: property "upstream" 
validation failed: value should match only one schema, but matches none` (an 
upstream needs `nodes` or discovery). The same happens on a no-op Edit → Save 
of a plugins-only route created through the Admin API, so the API workaround 
only covers creation.
   
   ### Why `master` looked fixed
   
   The issue thread concluded the client-side problem was gone on `master`. It 
is not — it was hidden by the React build flavour:
   
   | Build | React | Request body | Result |
   | --- | --- | --- | --- |
   | APISIX 3.16.0 image (embeds bbe05ad9) | production | `upstream: {scheme, 
type, …}` (and the `public-api: {}` entry dropped, pre-#3277) | 400 |
   | APISIX 3.17.0 image (embeds c8d3466d) | production | `upstream: {scheme, 
type, …}` | 400 |
   | `master` (713803e3), plain `vite build` | production | `upstream: {scheme, 
type, …, discovery_args: {}}` | 400 |
   | `master`, `vite dev` | development | `{uri, name, plugins}` | 201 |
   | `master`, `vite dev` with `<StrictMode>` removed | development | 
`upstream: {scheme, type, …}` | 400 |
   | `master`, e2e image (`NODE_ENV=test` build) | development | `{uri, name, 
plugins}` | 201 |
   | this branch, `pnpm build --mode test` | production | `{uri, name, 
plugins}` | 201 |
   
   Under `<StrictMode>` the double-invoked controller effects run 
react-hook-form's `shouldUnregister` cleanup, which wipes the mount-time values 
from the form state again. `vite dev` is development React by design, and the 
e2e image was too: `ENV NODE_ENV=test` makes Vite bundle development React 
(verified by grepping `react-dom.development` in the shipped assets). So 
neither the dev server nor CI could ever see this class of bug — the `services 
… neither — a service with no upstream at all still saves` case from #3447 
passes in CI only for that reason, and fails against a production build.
   
   ### Fix
   
   - `src/components/form-slice/FormPartUpstream/util.ts`: 
`produceRmEmptyUpstreamFields` — the producer shared by the route, service and 
stream-route submit pipelines — now drops an inline `upstream` that holds 
nothing but the widgets' mount-time defaults (`isUntouchedInlineUpstream`). 
Every value in such an upstream is the gateway default, so nothing is lost. 
Anything the user did supply (a node row, even an empty one; a different 
scheme; a name; health checks; …) keeps the upstream in the request for the 
Admin API to validate — silently discarding user input is the #3417 failure 
mode and is deliberately avoided. The defaults are read from the same schema 
constants the widgets use, so they cannot drift apart silently.
   - `e2e/server/Dockerfile`: build the e2e image with `pnpm build --mode test` 
instead of `NODE_ENV=test`, so the suite runs against the production React the 
release ships while `import.meta.env.MODE === 'test'` still hides the devtools.
   - `src/components/form/Editor.tsx`: the `window.__monacoEditor__` test seam 
the Playwright helpers rely on was keyed on `NODE_ENV !== 'production'` (the 
reason the image set `NODE_ENV=test`); it is now keyed on `import.meta.env.DEV 
|| import.meta.env.MODE === 'test'`.
   
   Not changed here: an inline upstream that *does* have nodes still carries 
`discovery_args: {}` from the JSON widget's mount value (kept on purpose by the 
#3438 producer). The Admin API accepts and stores it; worth a separate look.
   
   ### Tests
   
   - `src/components/form-slice/FormPartUpstream/util.test.ts` (35 cases): the 
untouched-upstream predicate across every widget a user can touch, 
`produceRmEmptyUpstreamFields` itself, and the three real submit pipelines 
(route, service, stream route) fed with the exact values react-hook-form holds 
for a plugins-only Add Route form.
   - `e2e/tests/regression/routes.plugins-only-no-upstream.spec.ts`: add page 
(only Name + URI + `public-api`) and edit page (no-op save of an API-created 
plugins-only route); both capture the request body and assert no `upstream` is 
sent and none is stored. With the Dockerfile change these run against 
production React, where they fail without the fix.
   
   ### Verification
   
   - `pnpm test`: 198 passed (163 before + 35 new)
   - `pnpm lint`, `pnpm build --mode test` (tsc + vite) clean; the built assets 
contain `react-dom-client.production` and no `react-dom.development`; the 
rebuilt e2e image embeds the same
   - Standalone reproduction (fills the form, captures the XHR) against the 
APISIX 3.16.0 and 3.17.0 images and a production build of `master`: 400 as 
above; against a production build of this branch: 201 with 
`{"uri","name","plugins"}` only
   - Full e2e suite against the rebuilt e2e image (production React): in 
progress locally, CI run attached to this PR
   
   **Related issues**
   
   fix #3395
   
   **Checklist:**
   
   - [x] Did you explain what problem does this PR solve? Or what new features 
have been added?
   - [x] Have you added corresponding test cases?
   - [ ] Have you modified the corresponding document? (no documentation impact)
   - [x] Is this PR backward compatible? If it is not backward compatible, 
please discuss on the mailing list first
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to