btlqql opened a new pull request, #4716:
URL: https://github.com/apache/rocketmq-dashboard/pull/4716

   <!-- Base branch: `rocketmq-studio`, the RocketMQ Studio trunk. -->
   
   ### Related issue
   
   No open issue covers this defect: the tracker has no report about a 
persisted instance row without a
   vendor, so there is nothing to close here.
   
   ### Brief Description
   
   `MybatisPlusInstanceRepository.parseVendor`
   
(`server/src/main/java/org/apache/rocketmq/studio/instance/MybatisPlusInstanceRepository.java:194`)
   passed the persisted value straight to `InstanceVendor.valueOf`:
   
   ```java
   private InstanceVendor parseVendor(Long instanceId, String vendor) {
       try {
           return InstanceVendor.valueOf(vendor);
       } catch (IllegalArgumentException | NullPointerException ex) {
           throw invalidPersistedValue(instanceId, "vendor", vendor);
       }
   }
   ```
   
   `rmq_instance` declares the column as optional - `vendor VARCHAR(32)` with 
no `NOT NULL` and no
   default (`server/src/main/resources/db/schema.sql:84`) - so a row written 
before the column existed
   carries no vendor, and `valueOf(null)` (or `valueOf("")`) turned that into
   `BusinessException(500, "Invalid persisted instance vendor for instance 5: 
null")`. Because the
   mapping runs on every read (`findAll`, `findByType`, `search`, 
`findByTypeAndSearch`,
   `findByIdentifier`), a single such row failed the entire instance page with 
HTTP 500 instead of
   listing the instances - and the row could not be repaired through the page 
either, since editing it
   loads it with the same mapper.
   
   A missing vendor is not a corrupt row everywhere else in the codebase:
   
   - `InstanceService` defaults it to APACHE for the resource counts, the 
delete-time check, the list
     ordering and the copy helper (`InstanceService.java:185`, `:650`, `:125`, 
`:789`);
   - `InstanceCapabilityService.java:43` and `InstanceProviderRegistry.java:75` 
do the same, and
     
`InstanceCapabilityServiceTest#getCapabilitiesShouldDefaultLegacyNullVendorToApacheTest`
 pins that
     rule;
   - `toEntity` normalises `null` to `APACHE` on every write
     (`MybatisPlusInstanceRepository.java:214`), so the repository's own write 
path already guarantees
     the column is only ever missing for rows it did not write.
   
   The read path was the only place that treated the same value as an error. 
`parseVendor` now returns
   `InstanceVendor.APACHE` for a null or blank value, and still fails with the 
same 500 for a value
   that names no vendor at all (`"UNKNOWN_VENDOR"`), which
   `findByIdShouldRejectInvalidPersistedInstanceVendor` keeps covering. 
`parseType` is unchanged
   (`type` is `NOT NULL` in the schema, so it has no legacy-null case).
   
   ### How Did You Test This Change?
   
   Two cases in `MybatisPlusInstanceRepositoryTest`: a NULL vendor read through 
`findAll` (the path that
   breaks the whole page) and a blank vendor read through `findById`.
   
   Before the fix (red) - the test file on the unmodified repository:
   
   ```
   $ cd server && mvn -B -ntp test 
-Dtest='MybatisPlusInstanceRepositoryTest#findAllShouldDefaultALegacyNullVendorToApacheTest+findByIdShouldDefaultABlankVendorToApacheTest'
   [ERROR] Tests run: 2, Failures: 0, Errors: 2, Skipped: 0, Time elapsed: 
2.708 s <<< FAILURE! -- in 
org.apache.rocketmq.studio.instance.MybatisPlusInstanceRepositoryTest
   [ERROR] 
org.apache.rocketmq.studio.instance.MybatisPlusInstanceRepositoryTest.findAllShouldDefaultALegacyNullVendorToApacheTest
 -- Time elapsed: 2.607 s <<< ERROR!
   org.apache.rocketmq.studio.common.exception.BusinessException: Invalid 
persisted instance vendor for instance 5: null
        at 
org.apache.rocketmq.studio.instance.MybatisPlusInstanceRepositoryTest.findAllShouldDefaultALegacyNullVendorToApacheTest(MybatisPlusInstanceRepositoryTest.java:181)
   [ERROR] 
org.apache.rocketmq.studio.instance.MybatisPlusInstanceRepositoryTest.findByIdShouldDefaultABlankVendorToApacheTest
 -- Time elapsed: 0.030 s <<< ERROR!
   org.apache.rocketmq.studio.common.exception.BusinessException: Invalid 
persisted instance vendor for instance 6:
        at 
org.apache.rocketmq.studio.instance.MybatisPlusInstanceRepositoryTest.findByIdShouldDefaultABlankVendorToApacheTest(MybatisPlusInstanceRepositoryTest.java:193)
   [ERROR] Tests run: 2, Failures: 0, Errors: 2, Skipped: 0
   [INFO] BUILD FAILURE
   ```
   
   Those two lines are the production behaviour: the read did not return the 
instance, it threw the
   500 that the page shows to the user.
   
   After the fix (green) - the whole instance package test set, including the 
untouched
   `findByIdShouldRejectInvalidPersistedInstanceVendor` and
   `findByIdShouldRejectInvalidPersistedInstanceType` cases:
   
   ```
   $ cd server && mvn -B -ntp test 
-Dtest='MybatisPlusInstanceRepositoryTest,InstanceServiceTest,InstanceResolverTest,InstanceCapabilityServiceTest,InstanceControllerTest,InstanceResourceCountRunnerTest'
   [INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0 -- in 
org.apache.rocketmq.studio.instance.InstanceCapabilityServiceTest
   [INFO] Tests run: 12, Failures: 0, Errors: 0, Skipped: 0 -- in 
org.apache.rocketmq.studio.instance.InstanceControllerTest
   [INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0 -- in 
org.apache.rocketmq.studio.instance.InstanceResolverTest
   [INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0 -- in 
org.apache.rocketmq.studio.instance.InstanceResourceCountRunnerTest
   [INFO] Tests run: 83, Failures: 0, Errors: 0, Skipped: 0 -- in 
org.apache.rocketmq.studio.instance.InstanceServiceTest
   [INFO] Tests run: 20, Failures: 0, Errors: 0, Skipped: 0 -- in 
org.apache.rocketmq.studio.instance.MybatisPlusInstanceRepositoryTest
   [INFO] Tests run: 130, Failures: 0, Errors: 0, Skipped: 0
   [INFO] BUILD SUCCESS
   [INFO] You have 0 Checkstyle violations.
   ```
   
   Note on the full suite: on a clean `rocketmq-studio` checkout `mvn -B -ntp 
test` already reports
   `Tests run: 3051, Failures: 6, Errors: 25, Skipped: 4`. The 11 red classes 
are the MySQL 8 backed
   Spring integration tests (`AuthServiceBootstrapIntegrationTest`,
   `AuthServiceConcurrencyIntegrationTest`, 
`AuthServiceSessionOverviewIntegrationTest`,
   `HealthProbeIntegrationTest`, `QueryHistoryServiceIntegrationTest`,
   `NativeAlertEvaluationTransactionTest`, 
`NotificationOutboxMapperIntegrationTest`,
   `RmqAlertStateMapperIntegrationTest`, `StudioApplicationTest`) plus the 
external-CLI ones
   (`CliAgentProviderTest`, `ClaudeCodeAgentProviderTest`). None of them are 
touched by this change; the
   six classes above are the ones this change can affect.
   
   ### Overlap with other open pull requests
   
   Two open PRs also touch this file, in unrelated hunks: #4458 (LIKE wildcard 
escaping) edits the
   `search` / `findByTypeAndSearch` wrappers around lines 66-84, and #4455 
edits the file for its cloud
   direct-consume feature. This change is confined to `parseVendor` (line 194) 
and the two new tests, so
   a rebase stays mechanical.
   
   ### Checklist
   
   - [x] One coherent change; unrelated modifications are not bundled in
   - [x] Commit subject follows Conventional Commits (`fix(instance): …`)
   - [x] Tests added or updated for non-trivial changes, test methods named 
`...Test`
   - [x] New UI text has both Chinese and English entries under `web/src/i18n/` 
(no UI text in this change)
   - [x] Architecture constraints stay green (`mvn test` runs the ArchUnit 
checks)
   - [x] New source files carry the ASF license header (no new files)
   - [x] Documentation touched where behaviour changed (the default is 
documented at the mapping; no API contract change, since the alternative was a 
500)
   


-- 
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