## Summary
Problem: I have a feature which I want to ramp on the server. but I also
don't want to prevent table owners from self-serve opting in. A generic
function to handle that overlap doesn't exist today.
Extend the existing `TableFeatureToggle` with self-service table
overrides while preserving its server-managed targeting API.
An explicit `<featureId>.enabled=true|false` table property wins. When
the property is absent, activation delegates to the server toggle.
Server rules now support trailing-`*` prefix matching independently for
database and table names.
## Changes
- [ ] Client-facing API Changes
- [x] Internal API Changes
- [ ] Bug Fixes
- [x] New Features
- [ ] Performance Improvements
- [ ] Code Style
- [ ] Refactoring
- [ ] Documentation
- [x] Tests
Adds a binary-compatible default method to `TableFeatureToggle`:
```java
isFeatureActivatedWithOverride(TableDto tableDto, String featureId)
```
It is deliberately **not** an overload of `isFeatureActivated`. The two
carry different safety contracts, and a distinct name makes the
difference visible at the call site: authorization gates such as
`enable_mor` decide whether a user may write a preserved table property,
so they must keep using the server-only `isFeatureActivated(String,
String, String)`. The override-honoring form reads a property the gated
user can write.
An override that is neither `true` nor `false` fails closed: it is
logged and the feature is treated as inactive. The gate is evaluated on
the table-load path, so throwing would turn a typo like
`read-bridge.enabled=flase` into a `400` and make the table unloadable.
Extends the existing toggle rule matcher while preserving exact and `*`
matching:
- `tracking.events` matches exactly.
- `tracking_*.events_*` matches database and table prefixes.
- `*.*` matches every table.
## Testing Done
- [ ] Manually Tested on local docker setup. Please include commands
ran, and their output.
- [x] Added new tests for the changes made.
- [ ] Updated existing tests to reflect the changes made.
- [ ] No tests added or updated. Please explain why. If unsure, please
feel free to ask for help.
- [ ] Some other form of testing like staging or soak time in
production. Please explain.
Ran:
```shell
JAVA_HOME=$(/usr/libexec/java_home -v 17) ./gradlew \
:services:tables:test \
--tests 'com.linkedin.openhouse.tables.toggle.TableFeatureToggleTest' \
:services:housetables:test \
--tests 'com.linkedin.openhouse.housetables.mock.WildcardTableToggleRuleMatcherTest' \
-x CopyGitHooksTask
```
All 12 focused tests passed, covering server fallback, explicit opt-in
and opt-out, fail-closed handling of unparseable overrides, exact
matching, wildcard matching, and paired database/table prefix matching.
# Additional Information
- [ ] Breaking Changes
- [ ] Deprecations
- [x] Large PR broken into smaller PRs, and PR plan linked in the
description.
This is an independent OSS foundation for the read-bridge stack in #645
and the corresponding `li-openhouse` implementation PRs.
Feature-specific default derivation remains outside this PR.
Note for reviewers: the matcher change widens any existing
`table_toggle_rule` row whose pattern ends in `*` but is not exactly
`*`. Those previously matched nothing. Worth auditing HTS before merge.