swagger-ui.js.map 파일 업데이트:

- 최신 버전으로 맵 파일 교체
- Swagger UI 동작 관련 불일치 문제 수정
This commit is contained in:
Rinjae
2026-07-02 18:16:38 +09:00
parent c403118289
commit e8d5c8ca03
33 changed files with 476 additions and 9224 deletions
+6 -3
View File
@@ -30,9 +30,12 @@ dependencies {
implementation 'com.eactive.elink.common:elink-common-data:4.5.5'
// implementation 'io.swagger.core.v3:swagger-core:2.2.25'
// implementation 'io.swagger.parser.v3:swagger-parser:2.1.23'
implementation fileTree(dir: 'libs/swagger', include: ['*.jar'])
// OpenAPI 3.0/3.1 spec 지원 (Nexus/Maven 해석). 기존 libs/swagger fileTree 대체.
// swagger 최신 릴리스(2.2.52/2.1.45)도 SpecVersion 은 V30/V31 까지만 — OpenAPI 3.2 는
// 아직 swagger-core/parser 어느 버전도 미지원. 지원 추가 시 버전만 상향하면 됨. JDK8 호환.
implementation 'io.swagger.core.v3:swagger-core:2.2.52' // io.swagger.v3.oas.models.*, io.swagger.v3.core.util.Json
implementation 'io.swagger.parser.v3:swagger-parser:2.1.45' // io.swagger.parser.OpenAPIParser, io.swagger.v3.parser.*
implementation 'io.swagger:swagger-annotations:1.6.14' // io.swagger.annotations.ApiModelProperty (CommissionSearch)
implementation project(':elink-online-core-jpa')
implementation project(':elink-portal-common')
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -4,6 +4,8 @@ package com.eactive.apim.portal.apps.apis.filter;
import com.eactive.apim.portal.apps.apis.dto.ApiSpecInfoDto;
import com.eactive.apim.portal.apps.apis.service.ApiService;
import com.eactive.apim.portal.common.util.ApplicationContextUtil;
import com.eactive.apim.portal.djb.testbed.config.DjbTestbedGatewayProperty;
import com.eactive.apim.portal.djb.testbed.enums.DjbGatewayMode;
import java.io.BufferedReader;
import java.io.IOException;
import java.net.URI;
@@ -65,7 +67,13 @@ public class ApiTesterFilter implements Filter {
ApiService apiSpecInfoDtoService = ApplicationContextUtil.getContext().getBean(ApiService.class);
String url = httpServletRequest.getHeader("original-url");
if (url.contains("/api/v1/oauth/token")){
// 게이트웨이 모드에 따라 OAuth 토큰 발급 요청을 mock 또는 실 게이트웨이 forward 로 분기 (DJPGPT0001)
DjbTestbedGatewayProperty gatewayProperty = ApplicationContextUtil.getContext().getBean(DjbTestbedGatewayProperty.class);
DjbGatewayMode gatewayMode = gatewayProperty.resolveGatewayMode();
boolean tokenRequest = url.contains(DjbTestbedGatewayProperty.PORTAL_MOCK_TOKEN_PATH)
|| url.contains(gatewayProperty.tokenPath());
if (tokenRequest) {
StringBuilder sb = new StringBuilder();
BufferedReader reader = httpServletRequest.getReader();
String line;
@@ -74,27 +82,40 @@ public class ApiTesterFilter implements Filter {
}
String body = sb.toString();
// Parse request parameters
Map<String, String> params = new HashMap<>();
String[] pairs = body.split("&");
for (String pair : pairs) {
String[] keyValue = pair.split("=");
if (keyValue.length == 2) {
params.put(keyValue[0], keyValue[1]);
if (gatewayMode == DjbGatewayMode.PORTAL_MOCK) {
// PortalMock: 고정 mock 토큰 반환 (기존 동작 유지)
Map<String, String> params = new HashMap<>();
String[] pairs = body.split("&");
for (String pair : pairs) {
String[] keyValue = pair.split("=");
if (keyValue.length == 2) {
params.put(keyValue[0], keyValue[1]);
}
}
String scope = params.getOrDefault("scope", "default");
String token = "{\n" +
" \"access_token\": \"djbank_gw_sample_token\",\n" +
" \"token_type\": \"bearer\",\n" +
" \"expires_in\": 86400,\n" +
" \"scope\": \""+scope +"\",\n" +
" \"jti\": \""+ UUID.randomUUID().toString()+"\"\n" +
"}";
response.setContentType("application/json");
response.getWriter().println(token);
} else {
// GATEWAY: 실 게이트웨이 토큰 엔드포인트로 forward (token 발급만)
APISender apiSender = ApplicationContextUtil.getContext().getBean(APISender.class);
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/x-www-form-urlencoded");
headers.put("Accept", "application/json");
String target = gatewayProperty.baseUrl() + gatewayProperty.tokenPath();
String tokenResponse = apiSender.requestPost(target, headers, new HashMap<>(), body);
response.setContentType("application/json");
response.getWriter().println(tokenResponse);
}
String scope = params.getOrDefault("scope", "default");
String token = "{\n" +
" \"access_token\": \"djbank_gw_sample_token\",\n" +
" \"token_type\": \"bearer\",\n" +
" \"expires_in\": 86400,\n" +
" \"scope\": \""+scope +"\",\n" +
" \"jti\": \""+ UUID.randomUUID().toString()+"\"\n" +
"}";
response.setContentType("application/json");
response.getWriter().println(token);
} else {
ApiSpecInfoDto apiSpecInfoDto = apiSpecInfoDtoService.selectDetailByURLAndMethod(parseUri(url), httpServletRequest.getMethod());
+111
View File
@@ -9451,6 +9451,117 @@ button.djb-comment-submit:disabled {
display: flex;
}
.testbed-app-panel {
margin: 8px 0 24px;
padding: 24px;
background: #FFFFFF;
border: 1px solid #E2E8F0;
border-radius: 12px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.testbed-app-panel .testbed-app-panel__head {
display: flex;
align-items: baseline;
flex-wrap: wrap;
gap: 4px 8px;
margin-bottom: 16px;
}
.testbed-app-panel .testbed-app-panel__title {
position: relative;
padding-left: 16px;
font-size: 16px;
font-weight: 700;
color: #1A1A2E;
letter-spacing: -0.01em;
}
.testbed-app-panel .testbed-app-panel__title::before {
content: "";
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 15px;
background: #0049b4;
border-radius: 2px;
}
.testbed-app-panel .testbed-app-panel__desc {
font-size: 14px;
color: #64748B;
}
.testbed-app-panel .testbed-app-field {
position: relative;
max-width: 380px;
}
.testbed-app-panel .testbed-app-field::after {
content: "";
position: absolute;
right: 16px;
top: 50%;
width: 9px;
height: 9px;
margin-top: -6px;
border-right: 2px solid #64748B;
border-bottom: 2px solid #64748B;
transform: rotate(45deg);
pointer-events: none;
}
.testbed-app-panel #apps {
width: 100%;
height: 46px;
padding: 0 42px 0 16px;
font-family: "Spoqa Han Sans Neo", "SpoqaHanSans", "Noto Sans KR", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 14px;
color: #1A1A2E;
background: #F8FAFC;
border: 1px solid #E2E8F0;
border-radius: 8px;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
cursor: pointer;
transition: all 0.15s ease;
}
.testbed-app-panel #apps:hover:not(:disabled) {
border-color: #94A3B8;
background: #FFFFFF;
}
.testbed-app-panel #apps:focus {
outline: none;
background: #FFFFFF;
border-color: #0049b4;
box-shadow: 0 0 0 3px rgba(0, 73, 180, 0.14);
}
.testbed-app-panel #apps:disabled {
color: #94A3B8;
background: #F8FAFC;
cursor: not-allowed;
}
.testbed-app-panel .testbed-app-notice {
margin: 16px 0 0;
padding: 11px 16px;
font-size: 14px;
line-height: 1.6;
color: #64748B;
background: #EFF6FF;
border: 1px solid rgba(0, 73, 180, 0.18);
border-left: 3px solid #0049b4;
border-radius: 6px;
}
.testbed-app-panel .testbed-app-notice::before {
content: "ⓘ ";
color: #0049b4;
font-weight: 700;
}
@media (max-width: 768px) {
.testbed-app-panel {
padding: 16px;
}
.testbed-app-panel .testbed-app-field {
max-width: 100%;
}
}
.api-overview-card {
background: transparent;
border-radius: 0;
File diff suppressed because one or more lines are too long
@@ -1,7 +1,7 @@
html {
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
height: 100%;
overflow-y: scroll;
}
*,
@@ -12,4 +12,5 @@ html {
body {
margin: 0;
background: #fafafa;
}
@@ -1,79 +1,6 @@
<!doctype html>
<html lang="en-US">
<head>
<title>Swagger UI: OAuth2 Redirect</title>
</head>
<body>
<script>
'use strict';
function run () {
var oauth2 = window.opener.swaggerUIRedirectOauth2;
var sentState = oauth2.state;
var redirectUrl = oauth2.redirectUrl;
var isValid, qp, arr;
if (/code|token|error/.test(window.location.hash)) {
qp = window.location.hash.substring(1).replace('?', '&');
} else {
qp = location.search.substring(1);
}
arr = qp.split("&");
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';});
qp = qp ? JSON.parse('{' + arr.join() + '}',
function (key, value) {
return key === "" ? value : decodeURIComponent(value);
}
) : {};
isValid = qp.state === sentState;
if ((
oauth2.auth.schema.get("flow") === "accessCode" ||
oauth2.auth.schema.get("flow") === "authorizationCode" ||
oauth2.auth.schema.get("flow") === "authorization_code"
) && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "warning",
message: "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server."
});
}
if (qp.code) {
delete oauth2.state;
oauth2.auth.code = qp.code;
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
} else {
let oauthErrorMsg;
if (qp.error) {
oauthErrorMsg = "["+qp.error+"]: " +
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
(qp.error_uri ? "More info: "+qp.error_uri : "");
}
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "error",
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server."
});
}
} else {
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
}
window.close();
}
if (document.readyState !== 'loading') {
run();
} else {
document.addEventListener('DOMContentLoaded', function () {
run();
});
}
</script>
<script src="oauth2-redirect.js"></script>
</body>
</html>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -602,6 +602,130 @@
}
}
// Testbed 선택 패널 (DJPGPT0001)
.testbed-app-panel {
margin: $spacing-sm 0 $spacing-lg;
padding: $spacing-lg;
background: $white;
border: 1px solid $border-gray;
border-radius: $border-radius-lg;
box-shadow: $shadow-sm;
.testbed-app-panel__head {
display: flex;
align-items: baseline;
flex-wrap: wrap;
gap: $spacing-xs $spacing-sm;
margin-bottom: $spacing-md;
}
.testbed-app-panel__title {
position: relative;
padding-left: $spacing-md;
font-size: $font-size-base;
font-weight: $font-weight-bold;
color: $text-dark;
letter-spacing: -0.01em;
&::before {
content: "";
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 15px;
background: $primary-blue;
border-radius: 2px;
}
}
.testbed-app-panel__desc {
font-size: $font-size-sm;
color: $text-gray;
}
.testbed-app-field {
position: relative;
max-width: 380px;
&::after {
content: "";
position: absolute;
right: $spacing-md;
top: 50%;
width: 9px;
height: 9px;
margin-top: -6px;
border-right: 2px solid $text-gray;
border-bottom: 2px solid $text-gray;
transform: rotate(45deg);
pointer-events: none;
}
}
#apps {
width: 100%;
height: 46px;
padding: 0 42px 0 $input-padding-x;
font-family: $font-family-primary;
font-size: $font-size-sm;
color: $text-dark;
background: $gray-bg;
border: 1px solid $border-gray;
border-radius: $border-radius-md;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
cursor: pointer;
transition: $transition-fast;
&:hover:not(:disabled) {
border-color: $text-light;
background: $white;
}
&:focus {
outline: none;
background: $white;
border-color: $primary-blue;
box-shadow: 0 0 0 3px rgba($primary-blue, 0.14);
}
&:disabled {
color: $text-light;
background: $gray-bg;
cursor: not-allowed;
}
}
.testbed-app-notice {
margin: $spacing-md 0 0;
padding: 11px $spacing-md;
font-size: $font-size-sm;
line-height: $line-height-normal;
color: $text-gray;
background: $light-bg;
border: 1px solid rgba($primary-blue, 0.18);
border-left: 3px solid $primary-blue;
border-radius: $border-radius-sm;
&::before {
content: "";
color: $primary-blue;
font-weight: $font-weight-bold;
}
}
@media (max-width: $breakpoint-sm) {
padding: $spacing-md;
.testbed-app-field {
max-width: 100%;
}
}
}
// API Overview Card (Flat Style)
.api-overview-card {
background: transparent;
@@ -147,6 +147,21 @@
<div class="tab-content" id="testbed-tab">
<link rel="stylesheet" type="text/css" th:href="@{/plugins/swaggerUI/swagger-ui.css}"/>
<link rel="stylesheet" type="text/css" th:href="@{/plugins/swaggerUI/index.css}"/>
<link rel="stylesheet" type="text/css" th:href="@{/plugins/swaggerUI/djb-swagger-testbed.css}"/>
<!-- DJPGPT0001: 앱(인증키) 선택 → 인증 정보 자동 주입 -->
<div class="testbed-app-panel" id="appsWrap">
<div class="testbed-app-panel__head">
<span class="testbed-app-panel__title">앱 선택</span>
<span class="testbed-app-panel__desc">테스트할 앱(인증키)을 선택하면 인증 정보가 자동 입력됩니다.</span>
</div>
<div class="testbed-app-field">
<select id="apps">
<option value="">앱을 선택하세요</option>
</select>
</div>
<p class="testbed-app-notice" id="appsNotice" style="display:none;"></p>
</div>
<div id="swagger-ui"></div>
</div>
@@ -160,6 +175,8 @@
</body>
<th:block layout:fragment="contentScript">
<!-- DJB Swagger UI 한글화 패치 (MutationObserver DOM patch) -->
<script th:src="@{/plugins/swaggerUI/djb-swagger-i18n.js}" charset="UTF-8"></script>
<script th:inline="javascript">
document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
@@ -284,7 +301,7 @@
return;
}
const swaggerUrl = `/api/apis/${apiId}/swagger.json`;
const swaggerUrl = `/djb/testbed/apis/${apiId}/swagger.json`;
// Load Swagger UI scripts dynamically
const loadScript = (src) => {
@@ -305,54 +322,13 @@
// Load code template script
return loadScript(/*[[@{/js/code_template.js}]]*/ '');
}).then(() => {
// Initialize Swagger UI
const sampleCodeBaseUrl = /*[[@{/api/sample_code/}]]*/ '/api/sample_code/';
const generateSnippet = (parsedRequest, language) => {
let result;
try {
$.ajax({
url: sampleCodeBaseUrl + language,
type: 'POST',
async: false,
contentType: 'application/json',
headers: {
'X-XSRF-TOKEN': /*[[${_csrf.token}]]*/ ''
},
data: JSON.stringify(parsedRequest),
success: function(data) {
result = data;
},
error: function(jqXHR, textStatus, errorThrown) {
console.error('Error:', errorThrown);
}
});
} catch (error) {
console.error('Error:', error);
}
return result;
};
const SnippetGeneratorPlugin = {
fn: {
requestSnippetGenerator_csharp: (request) => {
var parsedRequest = parseSwaggerRequest(request);
return generateSnippet(parsedRequest, 'csharp');
},
requestSnippetGenerator_ecma5: (request) => {
var parsedRequest = parseSwaggerRequest(request);
return generateSnippet(parsedRequest, 'ecma5');
},
requestSnippetGenerator_java: (request) => {
var parsedRequest = parseSwaggerRequest(request);
return generateSnippet(parsedRequest, 'java');
},
requestSnippetGenerator_python: (request) => {
var parsedRequest = parseSwaggerRequest(request);
return generateSnippet(parsedRequest, 'python');
}
}
};
// DJB 요청 스니펫(7종) / 상시 스니펫 패널 / 커스텀 응답 그리드 스크립트
return Promise.all([
loadScript(/*[[@{/plugins/swaggerUI/djb-swagger-snippets.js}]]*/ ''),
loadScript(/*[[@{/plugins/swaggerUI/djb-swagger-snippet-panel.js}]]*/ ''),
loadScript(/*[[@{/plugins/swaggerUI/djb-swagger-response.js}]]*/ '')
]);
}).then(() => {
// Clear existing Swagger UI
document.getElementById('swagger-ui').innerHTML = '';
@@ -387,48 +363,154 @@
}
return req;
},
responseInterceptor: function(res) {
// 커스텀 응답 그리드에 렌더 (기본 응답영역은 CSS 로 숨김)
try { window.DjbSwaggerResponse.renderResponse(res); } catch (e) { console.error(e); }
return res;
},
onComplete: function() {
// 단일 API 페이지 — 상시 요청 스니펫 패널 마운트
setTimeout(function() { try { window.DjbSwaggerSnippetPanel.mount({ rootSel: '#swagger-ui' }); } catch (e) { console.error(e); } }, 50);
},
showMutatedRequest: false,
validatorUrl: '',
deepLinking: true,
// 항상 1개 API 노출 → operation 을 펼친 상태로, Try it out 활성 기본
docExpansion: 'full',
defaultModelsExpandDepth: -1,
defaultModelExpandDepth: 5,
tryItOutEnabled: true,
displayRequestDuration: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset.slice(1)
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl,
SnippetGeneratorPlugin
window.SnippetGeneratorPlugin
],
layout: 'StandaloneLayout',
requestSnippetsEnabled: true,
requestSnippets: {
generators: {
'csharp': {
title: 'C#',
syntax: 'csharp'
},
'ecma5': {
title: 'ECMA5',
syntax: 'javascript'
},
'java': {
title: 'Java',
syntax: 'java'
},
'python': {
title: 'Python',
syntax: 'python'
}
'curl_bash': { title: 'cURL (Bash)', syntax: 'bash' },
'curl_powershell': { title: 'cURL (PowerShell)', syntax: 'powershell' },
'curl_cmd': { title: 'cURL (CMD)', syntax: 'bash' },
'csharp': { title: 'C#', syntax: 'csharp' },
'ecma5': { title: 'ECMA5 (JavaScript)', syntax: 'javascript' },
'java': { title: 'Java', syntax: 'java' },
'python': { title: 'Python', syntax: 'python' }
},
defaultExpanded: true,
languages: null
}
});
window.ui = ui;
// Execute 클릭 캡처(응답 그리드 대상/타이머) 등록 — #swagger-ui 는 재초기화돼도 유지됨
window.DjbSwaggerResponse.attach('#swagger-ui');
// 앱이 이미 선택돼 있으면 스펙 로드 후 재주입
if (appsSelect && appsSelect.value) {
setTimeout(function () { onAppSelected(appsSelect.value); }, 300);
}
}).catch(error => {
console.error('Failed to load Swagger UI:', error);
document.getElementById('swagger-ui').innerHTML = '<p>Swagger UI를 불러오는 중 오류가 발생했습니다.</p>';
});
}
// ===== DJPGPT0001: 앱 인증 정보 자동 주입 =====
const DEFAULT_TOKEN_API_ID = 'default-token-api-spec';
const appsSelect = document.getElementById('apps');
function eligibilityMessage(reason) {
switch (reason) {
case 'ANONYMOUS': return '로그인 후 본인 앱의 인증 정보를 사용할 수 있습니다.';
case 'INDIVIDUAL_USER': return '개인사용자는 앱 인증 정보를 사용할 수 없습니다. 법인 계정으로 이용해 주세요.';
case 'NO_CREDENTIAL': return '사용 가능한 앱(인증키)이 없습니다. 인증 키 관리에서 앱을 생성해 주세요.';
default: return '앱 인증 정보를 사용할 수 없습니다.';
}
}
function loadTestbedContext() {
fetch(/*[[@{/djb/testbed/auth/context}]]*/ '/djb/testbed/auth/context')
.then(function (r) { return r.json(); })
.then(function (ctx) {
window.__djbGateway = ctx.gateway || {};
if (!appsSelect) return;
const notice = document.getElementById('appsNotice');
if (!ctx.eligible) {
appsSelect.disabled = true;
if (notice) { notice.style.display = 'block'; notice.textContent = eligibilityMessage(ctx.reason); }
return;
}
appsSelect.disabled = false;
if (notice) notice.style.display = 'none';
(ctx.credentials || []).forEach(function (c) {
const opt = document.createElement('option');
opt.value = c.clientId;
opt.textContent = c.clientName ? (c.clientName + ' (' + c.clientId + ')') : c.clientId;
appsSelect.appendChild(opt);
});
})
.catch(function (e) { console.error('테스트베드 컨텍스트 로드 실패', e); });
}
function fetchOAuthToken(clientId, clientSecret, gw) {
const body = 'grant_type=client_credentials'
+ '&client_id=' + encodeURIComponent(clientId)
+ '&client_secret=' + encodeURIComponent(clientSecret)
+ '&scope=api';
return fetch(window.location.origin + (/*[[@{/api/call-api}]]*/ '/api/call-api'), {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'original-url': gw.tokenUrl,
'X-XSRF-TOKEN': /*[[${_csrf.token}]]*/ ''
},
body: body
}).then(function (r) { return r.ok ? r.json() : null; })
.then(function (data) { return data ? data.access_token : null; });
}
function authorizeApp(secret) {
const gw = window.__djbGateway || {};
if (!window.ui) return;
if (secret.authType === 'OAUTH') {
fetchOAuthToken(secret.clientId, secret.clientSecret, gw).then(function (token) {
if (!token) return;
window.ui.authActions.authorize({
djbOAuth: {
name: 'djbOAuth',
schema: { type: 'apiKey', in: 'header', name: gw.oauthTokenHeader },
value: 'Bearer ' + token
}
});
});
} else if (secret.authType === 'API_KEY') {
window.ui.authActions.authorize({
djbApiKey: {
name: 'djbApiKey',
schema: { type: 'apiKey', in: 'header', name: gw.apiKeyHeader },
value: secret.clientSecret
}
});
}
}
function onAppSelected(clientId) {
if (!clientId) return;
if (!currentApiId || currentApiId === 'default' || currentApiId === DEFAULT_TOKEN_API_ID) return;
fetch((/*[[@{/djb/testbed/auth/credentials/}]]*/ '/djb/testbed/auth/credentials/')
+ encodeURIComponent(clientId) + '/secret?apiId=' + encodeURIComponent(currentApiId))
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (secret) { if (secret) authorizeApp(secret); })
.catch(function (e) { console.error('앱 인증정보 주입 실패', e); });
}
if (appsSelect) {
appsSelect.addEventListener('change', function () { onAppSelected(this.value); });
}
loadTestbedContext();
});
</script>
</th:block>
@@ -6,6 +6,7 @@
<section layout:fragment="contentFragment" class="content">
<link rel="stylesheet" type="text/css" th:href="@{/plugins/swaggerUI/swagger-ui.css}"/>
<link rel="stylesheet" type="text/css" th:href="@{/plugins/swaggerUI/index.css}"/>
<link rel="stylesheet" type="text/css" th:href="@{/plugins/swaggerUI/djb-swagger-testbed.css}"/>
<div class="content_wrap">
<div class="sub_title3">
@@ -40,54 +41,14 @@
<th:block layout:fragment="contentScript">
<script th:src="@{/js/code_template.js}"></script>
<!-- DJB Swagger UI 한글화 패치 (MutationObserver DOM patch) -->
<script th:src="@{/plugins/swaggerUI/djb-swagger-i18n.js}" charset="UTF-8"></script>
<!-- DJB 요청 스니펫(클라이언트 7종) / 상시 스니펫 패널 / 커스텀 응답 그리드 -->
<script th:src="@{/plugins/swaggerUI/djb-swagger-snippets.js}" charset="UTF-8"></script>
<script th:src="@{/plugins/swaggerUI/djb-swagger-snippet-panel.js}" charset="UTF-8"></script>
<script th:src="@{/plugins/swaggerUI/djb-swagger-response.js}" charset="UTF-8"></script>
<script>
const generateSnippet = (parsedRequest, language) => {
let result;
try {
$.ajax({
url: '[[@{/api/sample_code/}]]' + language,
type: 'POST',
async: false,
contentType: 'application/json',
headers: {
'X-XSRF-TOKEN': '[[${_csrf.token}]]'
},
data: JSON.stringify(parsedRequest),
success: function(data) {
result = data;
},
error: function(jqXHR, textStatus, errorThrown) {
console.error('Error:', errorThrown);
}
});
} catch (error) {
console.error('Error:', error);
}
return result;
};
const SnippetGeneratorPlugin = {
fn: {
requestSnippetGenerator_csharp: (request) => {
var parsedRequest = parseSwaggerRequest(request);
return generateSnippet(parsedRequest, 'csharp');
},
requestSnippetGenerator_ecma5: (request) => {
var parsedRequest = parseSwaggerRequest(request);
return generateSnippet(parsedRequest, 'ecma5');
},
requestSnippetGenerator_java: (request) => {
var parsedRequest = parseSwaggerRequest(request);
return generateSnippet(parsedRequest, 'java');
},
requestSnippetGenerator_python: (request) => {
var parsedRequest = parseSwaggerRequest(request);
return generateSnippet(parsedRequest, 'python');
}
}
};
document.addEventListener('DOMContentLoaded', function() {
let currentApiId = '[[${id}]]';
@@ -131,43 +92,51 @@
}
return req;
},
responseInterceptor: function(res) {
// 커스텀 응답 그리드에 렌더 (기본 응답영역은 CSS 로 숨김)
try { window.DjbSwaggerResponse.renderResponse(res); } catch (e) { console.error(e); }
return res;
},
onComplete: function() {
// 단일 API 페이지 — 상시 요청 스니펫 패널 마운트
setTimeout(function() { try { window.DjbSwaggerSnippetPanel.mount({ rootSel: '#swagger-ui' }); } catch (e) { console.error(e); } }, 50);
},
showMutatedRequest: false,
validatorUrl: '',
deepLinking: true,
// 항상 1개 API 노출 → operation 을 펼친 상태로, Try it out 활성 기본
docExpansion: 'full',
defaultModelsExpandDepth: -1,
defaultModelExpandDepth: 5,
tryItOutEnabled: true,
displayRequestDuration: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset.slice(1)
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl,
SnippetGeneratorPlugin
window.SnippetGeneratorPlugin
],
layout: 'StandaloneLayout',
requestSnippetsEnabled: true,
requestSnippets: {
generators: {
'csharp': {
title: 'C#',
syntax: 'csharp'
},
'ecma5': {
title: 'ECMA5',
syntax: 'javascript'
},
'java': {
title: 'Java',
syntax: 'java'
},
'python': {
title: 'Python',
syntax: 'python'
}
'curl_bash': { title: 'cURL (Bash)', syntax: 'bash' },
'curl_powershell': { title: 'cURL (PowerShell)', syntax: 'powershell' },
'curl_cmd': { title: 'cURL (CMD)', syntax: 'bash' },
'csharp': { title: 'C#', syntax: 'csharp' },
'ecma5': { title: 'ECMA5 (JavaScript)', syntax: 'javascript' },
'java': { title: 'Java', syntax: 'java' },
'python': { title: 'Python', syntax: 'python' }
},
defaultExpanded: true,
languages: null
}
});
window.ui = ui;
// Execute 클릭 캡처(응답 그리드 대상/타이머) 등록 — #swagger-ui 는 재초기화돼도 유지됨
window.DjbSwaggerResponse.attach('#swagger-ui');
}
const apiList = document.getElementById('apiList');
createCustomSelect(apiList, {