implemented ADD_NEW_PET, ADD_VISIT, OWNER_SEARCH flag

This commit is contained in:
XTiNCT 2026-02-08 16:02:22 +05:30
parent b17b470db9
commit c2ab02af3d
8 changed files with 37 additions and 11 deletions

View file

@ -46,6 +46,7 @@ public class FeatureToggleAspect {
}
// Check if feature is enabled
logger.debug("Checking feature toggle '{}' with context '{}'", flagKey, context);
boolean isEnabled = featureFlagService.isFeatureEnabled(flagKey, context);
logger.debug("Feature toggle '{}' check: enabled={}, context={}", flagKey, isEnabled, context);

View file

@ -106,7 +106,7 @@ public class FeatureFlagController {
}
/**
* POST /api/feature-flags/{flagKey}/toggle Toggle a feature flag on/off
* POST /feature-flags/{flagKey}/toggle Toggle a feature flag on/off
*/
@PostMapping("/{flagKey}/toggle")
public ResponseEntity<?> toggleFlag(@PathVariable String flagKey) {
@ -130,7 +130,7 @@ public class FeatureFlagController {
}
/**
* GET /api/feature-flags/check/{flagKey} Check if a feature is enabled (simple check
* GET /feature-flags/check/{flagKey} Check if a feature is enabled (simple check
* without context)
*/
@GetMapping("/check/{flagKey}")

View file

@ -106,6 +106,9 @@ public class FeatureFlagService {
case PERCENTAGE:
return evaluatePercentage(flag, context);
case GLOBAL_DISABLE:
return false;
default:
logger.warn("Unknown flag type for '{}': {}", flag.getFlagKey(), flag.getFlagType());
@ -121,7 +124,8 @@ public class FeatureFlagService {
logger.debug("Whitelist flag '{}' requires context, got null/empty", flag.getFlagKey());
return false;
}
logger.debug("Whitelist flag '{}' - checking if context '{}' is in whitelist: {}", flag.getFlagKey(), context,
flag.getWhitelist());
boolean inWhitelist = flag.getWhitelist().contains(context.trim());
logger.debug("Whitelist flag '{}' for context '{}': {}", flag.getFlagKey(), context, inWhitelist);
return inWhitelist;

View file

@ -22,6 +22,7 @@ import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.samples.petclinic.featureflag.annotation.FeatureToggle;
import org.springframework.samples.petclinic.featureflag.service.FeatureFlagService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
@ -90,13 +91,27 @@ class OwnerController {
}
@GetMapping("/owners/find")
public String initFindForm() {
public String initFindForm(Model model) {
model.addAttribute("owner", new Owner());
boolean ownerSearchEnabled =
featureFlagService.isFeatureEnabled("OWNER_SEARCH", null);
model.addAttribute("ownerSearchEnabled", ownerSearchEnabled);
return "owners/findOwners";
}
@FeatureToggle(
key = "OWNER_SEARCH",
disabledMessage = "Owner search is restricted",
disabledRedirect = "/owners/find"
)
@GetMapping("/owners")
public String processFindForm(@RequestParam(defaultValue = "1") int page, Owner owner, BindingResult result,
Model model) {
// allow parameterless GET request for /owners to return all records
String lastName = owner.getLastName();
if (lastName == null) {
@ -176,6 +191,8 @@ class OwnerController {
// displaying add pet button based on feature toggle
boolean addNewPetEnabled = featureFlagService.isFeatureEnabled("ADD_NEW_PET","addNewPetEnabled");
boolean addVisitEnabled = featureFlagService.isFeatureEnabled("ADD_VISIT","addVisitEnabled");
mav.addObject("addVisitEnabled", addVisitEnabled);
mav.addObject("addNewPetEnabled", addNewPetEnabled);
return mav;
}

View file

@ -18,6 +18,7 @@ package org.springframework.samples.petclinic.owner;
import java.util.Map;
import java.util.Optional;
import org.springframework.samples.petclinic.featureflag.annotation.FeatureToggle;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
@ -81,6 +82,7 @@ class VisitController {
// Spring MVC calls method loadPetWithVisit(...) before initNewVisitForm is
// called
@FeatureToggle(key = "ADD_VISIT", disabledMessage = "Adding visits is currently disabled", disabledRedirect = "/owners/{ownerId}")
@GetMapping("/owners/{ownerId}/pets/{petId}/visits/new")
public String initNewVisitForm() {
return "pets/createOrUpdateVisitForm";
@ -88,6 +90,7 @@ class VisitController {
// Spring MVC calls method loadPetWithVisit(...) before processNewVisitForm is
// called
@FeatureToggle(key = "ADD_VISIT", disabledMessage = "Adding visits is currently disabled", disabledRedirect = "/owners/{ownerId}")
@PostMapping("/owners/{ownerId}/pets/{petId}/visits/new")
public String processNewVisitForm(@ModelAttribute Owner owner, @PathVariable int petId, @Valid Visit visit,
BindingResult result, RedirectAttributes redirectAttributes) {

View file

@ -65,14 +65,14 @@ VALUES ('ADD_VISIT', 'Controls whether users can add new visits for pets', 'SIMP
-- 3. WHITELIST flag: Owner Search (only specific users can search)
INSERT IGNORE INTO feature_flags (flag_key, description, flag_type, enabled, percentage, created_at, updated_at)
VALUES ('OWNER_SEARCH', 'Controls who can search for owners', 'WHITELIST', TRUE, NULL, NOW(), NOW());
VALUES ('OWNER_SEARCH', 'Controls who can search for owners', 'SIMPLE', TRUE, NULL, NOW(), NOW());
-- Add whitelist items for owner-search (example user contexts)
INSERT IGNORE INTO feature_flag_whitelist (feature_flag_id, whitelist)
SELECT id, 'admin' FROM feature_flags WHERE flag_key = 'OWNER_SEARCH';
-- INSERT IGNORE INTO feature_flag_whitelist (feature_flag_id, whitelist)
-- SELECT id, 'admin' FROM feature_flags WHERE flag_key = 'OWNER_SEARCH';
INSERT IGNORE INTO feature_flag_whitelist (feature_flag_id, whitelist)
SELECT id, 'Ramprakash' FROM feature_flags WHERE flag_key = 'OWNER_SEARCH';
-- INSERT IGNORE INTO feature_flag_whitelist (feature_flag_id, whitelist)
-- SELECT id, 'Ramprakash' FROM feature_flags WHERE flag_key = 'OWNER_SEARCH';
-- 4. PERCENTAGE flag: New UI Theme (gradually roll out to 50% of users)
INSERT IGNORE INTO feature_flags (flag_key, description, flag_type, enabled, percentage, created_at, updated_at)

View file

@ -20,13 +20,14 @@
</div>
</div>
</div>
<div class="form-group">
<div class="form-group" th:if="${ownerSearchEnabled}">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary" th:text="#{findOwner}">Find Owner</button>
</div>
</div>
<a class="btn btn-primary" th:href="@{/owners/new}" th:text="#{addOwner}">Add Owner</a>
</form>

View file

@ -70,7 +70,7 @@
</tr>
<tr>
<td><a th:href="@{__${owner.id}__/pets/__${pet.id}__/edit}" th:text="#{editPet}">Edit Pet</a></td>
<td><a th:href="@{__${owner.id}__/pets/__${pet.id}__/visits/new}" th:text="#{addVisit}">Add Visit</a></td>
<td><a th:if="${addVisitEnabled}" th:href="@{__${owner.id}__/pets/__${pet.id}__/visits/new}" th:text="#{addVisit}">Add Visit</a></td>
</tr>
</table>
</td>