Openapi customizer property name enricher
Skill kjuhwa/skills-hub/skills/workflow/openapi-customizer-property-name-enricher
Self-correcting knowledge corpus for Claude Code — 9 stable shape clusters, bias-correction pipeline baked into contribution flow. 47 papers, 45 techniques, 1.1k skills.
npx -y skills add kjuhwa/skills-hub --skill openapi-customizer-property-name-enricherAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Spring Boot springdoc 에서 DTO 코드를 수정하지 않고, OpenApiCustomizer 로 components.schemas 를 walk 하면서 property name 이 특정 패턴(resourceId, agentId 등)인 필드에 pattern + example + x-discovery-* 확장을 후처리 주입한다.
SKILL.md
6.3 KB, as published. Nobody here has run it
언제 쓰는가
Swagger/OpenAPI 스펙의 여러 DTO 에 반복 등장하는 property(예: resourceId, agentId, confId) 에 공통 metadata(pattern, example, x-discovery-endpoint 등)를 부여해야 할 때. 일반적 방식은 DTO 마다 @Schema(pattern=..., extensions=...) 를 붙이는 것인데, 수백 개 DTO 를 일일이 건드리면 유지보수 지옥.
대안: OpenApiCustomizer 후처리 단계에서 components.schemas 를 순회하며 property name 매칭으로 일괄 주입한다. DTO 코드 zero touch.
언제 쓰지 말아야 하는가
- Property 별로 metadata 가 다른 경우 (예: 각 DTO 의
resourceId가 서로 다른 discovery endpoint 를 가리켜야 함) → 이때는 DTO 각각에@Schema명시. - Enum closing / complex schema transformation → 이건 Jackson mixin 이나
@JsonTypeInfo영역.
핵심 구조
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.media.Schema;
import org.springdoc.core.customizers.OpenApiCustomizer;
@Configuration
public class SchemaEnrichmentConfig {
private static final Map<String, Map<String, String>> IDENTIFIER_META = Map.of(
"resourceId", Map.of(
"pattern", "^MA_.+$",
"example", "MA_LINUX_TEST_SERVER_01",
"x-discovery-endpoint", "/api/sms/hosts-filter",
"x-discovery-field", "resourceId"),
"agentId", Map.of(
"pattern", "^MA_.+$",
"example", "MA_ADServer_20250108202624",
"x-discovery-endpoint", "/api/sms/agent/list-filter",
"x-discovery-field", "agentId")
// ... confId, customMonitorId 등
);
@Bean
public OpenApiCustomizer enrichIdentifierFields() {
return openApi -> {
Components components = openApi.getComponents();
if (components == null || components.getSchemas() == null) return;
for (Schema<?> schema : components.getSchemas().values()) {
@SuppressWarnings({"rawtypes", "unchecked"})
Map<String, Schema> props = schema.getProperties();
if (props == null) continue;
for (Map.Entry<String, Schema> entry : props.entrySet()) {
Map<String, String> meta = IDENTIFIER_META.get(entry.getKey());
if (meta == null) continue;
Schema<?> property = entry.getValue();
if (property == null || !"string".equals(property.getType())) continue;
if (property.getPattern() == null && meta.containsKey("pattern")) {
property.setPattern(meta.get("pattern"));
}
if (property.getExample() == null && meta.containsKey("example")) {
property.setExample(meta.get("example"));
}
property.addExtension("x-discovery-endpoint", meta.get("x-discovery-endpoint"));
property.addExtension("x-discovery-field", meta.get("x-discovery-field"));
}
}
};
}
}
동작 원리
springdoc이 컨트롤러/DTO 를 스캔하여components.schemas를 생성- 모든
OpenApiCustomizerBean 이 생성 후 순차 실행 - 본 Customizer 가 schemas 전체를 순회하며 property name 매칭 → metadata 주입
- 최종
/v3/api-docs에 enriched spec 노출
주의사항
- 실행 순서 엄수: 같은 Customizer 내에서
addCanonicalEnums같은 schema 등록 메소드를 먼저,enrichIdentifierFields류 walker 는 마지막. 등록된 schema 만 순회할 수 있기 때문. - 기존 값 보존:
getPattern() == null체크 후에만 설정. DTO 에 이미 명시된@Schema(pattern=...)을 덮어쓰지 않기. - type 체크:
"string".equals(property.getType())— 같은 이름이라도 string 이 아닌 필드(객체/배열)는 skip. - 확장은 덮어쓰기:
addExtension은 기존 값 덮어쓰므로 필요 시 먼저 체크. discovery endpoint 는 덮어쓰는 게 의도적일 수 있음. - 성능: schema 수백 개 × property 수십 개 = 수천 순회. Spring 기동 시 1회만 실행되므로 영향 없음.
검증 명령어
# bootRun 후 /v3/api-docs 덤프
curl -s http://localhost:59595/v3/api-docs > spec.json
# resourceId property 에 x-discovery-endpoint 가 주입됐는지
python -c "
import json
s = json.load(open('spec.json'))
hits = 0
for n, sc in s.get('components', {}).get('schemas', {}).items():
for p, pd in (sc.get('properties') or {}).items():
if p in ('resourceId','agentId','confId') and 'x-discovery-endpoint' in pd:
hits += 1
print('enriched identifier fields:', hits)
"
확장 포인트
- ID 이외 분야: 같은 pattern 으로
createdAt/updatedAt시간 필드 포맷 명시,email/phone형식 주입,color팔레트 enum 닫기 등에 응용 가능. - 복잡 조건:
entry.getKey()매칭 외에 schema name + property name 조합으로 더 정교한 룰 가능 (예:HostDto.resourceId만 특정 discovery endpoint). - 조건부 주입: DTO 의 다른 property 값/타입에 따라 분기하는 경우
schema.getProperties().get("confType")식으로 동일 schema 내 다른 property 참조 가능.
실제 사용 맥락
lucida-domain-sms 에서 #118871 "Swagger 최적화 sms V3" 작업으로 513 property 중 대부분에 @Schema(title, description, requiredMode) 가 이미 적용돼 있었다. 이 위에 resourceId/agentId/confId/customMonitorId 공통 discovery metadata 를 부여해야 했으나, 각 DTO 에 @Schema(extensions=@Extension(name="x-discovery-endpoint", ...)) 를 추가하는 건 수백 파일 변경 → 본 Customizer 방식으로 SwaggerExtensionConfig.java 한 파일만 추가하여 해결. 관련 상위 스킬: swagger-ai-optimization Part D Phase 24.