From fd4ee0aa84ce0e1d8d32f900f4e1a9d2bb6fa462 Mon Sep 17 00:00:00 2001 From: Sin-Kang Date: Sun, 31 May 2026 16:06:08 +0900 Subject: [PATCH] refactor(properties): use Lombok @Getter/@Setter on @ConfigurationProperties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces hand-written accessors on the kit's configuration-properties classes with Lombok, cutting boilerplate while keeping field declarations + javadoc (which the Spring config processor uses as property descriptions). - CacheProperties (cache-core): @Getter/@Setter, manual accessors removed. - DevslabKitProperties (autoconfigure): @Getter on the outer class (its nested holders are final → getters only), @Getter/@Setter on each nested class (Identity, Jwt, Access, Tenant, Menu, Audit, Bootstrap). final fields like Identity.jwt keep getter-only, matching the previous behaviour exactly. - autoconfigure build: add Lombok compileOnly + annotationProcessor (it only had the Spring config processor before). Lombok generates the accessors the config processor then reads. Verified end-to-end: ./gradlew build --no-daemon green (fresh). The sample-app integration tests bind devslab.kit.bootstrap.* from application.yaml through the Lombok-generated setters — BootstrapStatusEndpointTests 2/0/0 and SampleApplicationTests 5/0/0 (incl. bootstrapProvisionsAdminUser) both pass, so Spring @ConfigurationProperties binding works with the Lombok accessors. --- .../admin/audit/AuditLogAdminController.java | 2 +- devslab-kit-autoconfigure/build.gradle.kts | 7 + .../autoconfigure/DevslabKitProperties.java | 257 ++---------------- 3 files changed, 25 insertions(+), 241 deletions(-) diff --git a/devslab-kit-admin-api/src/main/java/kr/devslab/kit/admin/audit/AuditLogAdminController.java b/devslab-kit-admin-api/src/main/java/kr/devslab/kit/admin/audit/AuditLogAdminController.java index b31bf2c..96b2058 100644 --- a/devslab-kit-admin-api/src/main/java/kr/devslab/kit/admin/audit/AuditLogAdminController.java +++ b/devslab-kit-admin-api/src/main/java/kr/devslab/kit/admin/audit/AuditLogAdminController.java @@ -53,7 +53,7 @@ public Page search( tenantId, actorLogin, action, targetType, outcome, from, to); Pageable pageable = PageRequest.of( Math.max(0, page), - Math.min(MAX_PAGE_SIZE, Math.max(1, size == 0 ? DEFAULT_PAGE_SIZE : size)), + Math.clamp(size == 0 ? DEFAULT_PAGE_SIZE : size, 1, MAX_PAGE_SIZE), Sort.by(Sort.Direction.DESC, "occurredAt")); return service.search(criteria, pageable).map(entity -> AuditLogResponse.from(entity, objectMapper)); } diff --git a/devslab-kit-autoconfigure/build.gradle.kts b/devslab-kit-autoconfigure/build.gradle.kts index b4ad67f..3695e67 100644 --- a/devslab-kit-autoconfigure/build.gradle.kts +++ b/devslab-kit-autoconfigure/build.gradle.kts @@ -19,6 +19,13 @@ dependencies { implementation("org.springframework.boot:spring-boot-autoconfigure") annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") + // Lombok generates the @ConfigurationProperties accessors (DevslabKitProperties). + // Declared before nothing in particular, but both Lombok and the Spring config + // processor run as annotation processors — Lombok generates the getters/setters + // that the config processor then reads for metadata. + compileOnly("org.projectlombok:lombok") + annotationProcessor("org.projectlombok:lombok") + // Micrometer for custom metrics; consumer's spring-boot-actuator activates it at runtime. compileOnly("io.micrometer:micrometer-core") } diff --git a/devslab-kit-autoconfigure/src/main/java/kr/devslab/kit/autoconfigure/DevslabKitProperties.java b/devslab-kit-autoconfigure/src/main/java/kr/devslab/kit/autoconfigure/DevslabKitProperties.java index 772647f..4c67565 100644 --- a/devslab-kit-autoconfigure/src/main/java/kr/devslab/kit/autoconfigure/DevslabKitProperties.java +++ b/devslab-kit-autoconfigure/src/main/java/kr/devslab/kit/autoconfigure/DevslabKitProperties.java @@ -1,9 +1,12 @@ package kr.devslab.kit.autoconfigure; import kr.devslab.kit.tenant.TenantMode; +import lombok.Getter; +import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "devslab.kit") +@Getter public class DevslabKitProperties { private final Identity identity = new Identity(); @@ -13,30 +16,8 @@ public class DevslabKitProperties { private final Audit audit = new Audit(); private final Bootstrap bootstrap = new Bootstrap(); - public Identity getIdentity() { - return identity; - } - - public Bootstrap getBootstrap() { - return bootstrap; - } - - public Access getAccess() { - return access; - } - - public Tenant getTenant() { - return tenant; - } - - public Menu getMenu() { - return menu; - } - - public Audit getAudit() { - return audit; - } - + @Getter + @Setter public static class Identity { private boolean enabled = true; @@ -45,53 +26,12 @@ public static class Identity { private java.time.Duration lockoutDuration = java.time.Duration.ofMinutes(15); private final Jwt jwt = new Jwt(); - public Jwt getJwt() { - return jwt; - } - + @Getter + @Setter public static class Jwt { private String secret = "change-me-please-this-is-not-a-real-secret-32b!"; private java.time.Duration ttl = java.time.Duration.ofHours(8); private String issuer = "devslab-kit"; - - public String getSecret() { return secret; } - public void setSecret(String secret) { this.secret = secret; } - public java.time.Duration getTtl() { return ttl; } - public void setTtl(java.time.Duration ttl) { this.ttl = ttl; } - public String getIssuer() { return issuer; } - public void setIssuer(String issuer) { this.issuer = issuer; } - } - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public Mode getMode() { - return mode; - } - - public void setMode(Mode mode) { - this.mode = mode; - } - - public int getMaxFailedAttempts() { - return maxFailedAttempts; - } - - public void setMaxFailedAttempts(int maxFailedAttempts) { - this.maxFailedAttempts = maxFailedAttempts; - } - - public java.time.Duration getLockoutDuration() { - return lockoutDuration; - } - - public void setLockoutDuration(java.time.Duration lockoutDuration) { - this.lockoutDuration = lockoutDuration; } public enum Mode { @@ -102,19 +42,15 @@ public enum Mode { } } + @Getter + @Setter public static class Access { private boolean enabled = true; - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } } + @Getter + @Setter public static class Tenant { private boolean enabled = true; @@ -124,54 +60,6 @@ public static class Tenant { private String headerName = "X-Tenant-Id"; private int subdomainIndex = 0; - public String getHeaderName() { - return headerName; - } - - public void setHeaderName(String headerName) { - this.headerName = headerName; - } - - public int getSubdomainIndex() { - return subdomainIndex; - } - - public void setSubdomainIndex(int subdomainIndex) { - this.subdomainIndex = subdomainIndex; - } - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public TenantMode getMode() { - return mode; - } - - public void setMode(TenantMode mode) { - this.mode = mode; - } - - public String getDefaultTenantId() { - return defaultTenantId; - } - - public void setDefaultTenantId(String defaultTenantId) { - this.defaultTenantId = defaultTenantId; - } - - public Resolver getResolver() { - return resolver; - } - - public void setResolver(Resolver resolver) { - this.resolver = resolver; - } - public enum Resolver { FIXED, HEADER, @@ -181,6 +69,8 @@ public enum Resolver { } } + @Getter + @Setter public static class Menu { private boolean enabled = true; @@ -193,24 +83,10 @@ public static class Menu { * mutations should be visible immediately. */ private java.time.Duration cacheTtl = java.time.Duration.ofMinutes(5); - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public java.time.Duration getCacheTtl() { - return cacheTtl; - } - - public void setCacheTtl(java.time.Duration cacheTtl) { - this.cacheTtl = cacheTtl; - } } + @Getter + @Setter public static class Audit { private boolean enabled = true; @@ -224,37 +100,10 @@ public static class Audit { * trading a slow request for a dropped audit row. */ private int asyncQueueCapacity = 1024; - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public int getAsyncQueueCapacity() { - return asyncQueueCapacity; - } - - public void setAsyncQueueCapacity(int asyncQueueCapacity) { - this.asyncQueueCapacity = asyncQueueCapacity; - } } - /** - * First-admin bootstrap (ADR 0001). Disabled by default so a no-config - * production deploy provisions nothing. When enabled, an idempotent runner - * creates a tenant, an admin role with the full {@code admin.*} permission - * set, and a single admin user on first boot — just enough to log in to the - * dashboard and take over. - * - *

Drive it per environment from profile-specific config (e.g. - * {@code application-local.yml} sets {@code admin-password: admin} with - * {@code must-change-password: false}; staging/prod inject a secret and - * leave the forced rotation on). Never commit a fixed password to the - * shared {@code application.yml}. - */ + @Getter + @Setter public static class Bootstrap { /** Master switch. Default OFF — bootstrap runs only when explicitly enabled. */ @@ -294,77 +143,5 @@ public static class Bootstrap { * startup. The primary control is that no fixed default exists at all. */ private boolean failOnDefaultPasswordInProd = true; - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public String getTenantId() { - return tenantId; - } - - public void setTenantId(String tenantId) { - this.tenantId = tenantId; - } - - public String getAdminLoginId() { - return adminLoginId; - } - - public void setAdminLoginId(String adminLoginId) { - this.adminLoginId = adminLoginId; - } - - public String getAdminPassword() { - return adminPassword; - } - - public void setAdminPassword(String adminPassword) { - this.adminPassword = adminPassword; - } - - public String getAdminEmail() { - return adminEmail; - } - - public void setAdminEmail(String adminEmail) { - this.adminEmail = adminEmail; - } - - public String getRoleCode() { - return roleCode; - } - - public void setRoleCode(String roleCode) { - this.roleCode = roleCode; - } - - public String getRoleName() { - return roleName; - } - - public void setRoleName(String roleName) { - this.roleName = roleName; - } - - public boolean isMustChangePassword() { - return mustChangePassword; - } - - public void setMustChangePassword(boolean mustChangePassword) { - this.mustChangePassword = mustChangePassword; - } - - public boolean isFailOnDefaultPasswordInProd() { - return failOnDefaultPasswordInProd; - } - - public void setFailOnDefaultPasswordInProd(boolean failOnDefaultPasswordInProd) { - this.failOnDefaultPasswordInProd = failOnDefaultPasswordInProd; - } } }