api-render-ui 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import { GLOBAL_STYLES } from './inlined-styles';
2
2
  import { OpenAPIV3 } from 'openapi-types';
3
3
 
4
- type OpenAPIV3CustomDoc = OpenAPIV3.Document & { swagger: string }
4
+ type OpenAPIV3CustomDoc = OpenAPIV3.Document & { swagger: string; definitions: any } //Compatible with version 2.0
5
5
 
6
6
  export class ApiRenderer {
7
7
  options: any;
@@ -138,14 +138,53 @@ export class ApiRenderer {
138
138
  }
139
139
 
140
140
 
141
+ const auths = [
142
+ {
143
+ value: "No Auth"
144
+ },
145
+ {
146
+ value: "Basic Auth",
147
+ parameters: [
148
+ {
149
+ name: "userName",
150
+ value: ""
151
+ },
152
+ {
153
+ name: "password",
154
+ value: ""
155
+ }
156
+ ]
157
+ },
158
+ {
159
+ value: "OAuth 2.0",
160
+ }
161
+ ]
162
+
163
+ const grantTypes = [
164
+ { value: "authorization_code" },
165
+ { value: "Implicit" },
166
+ { value: "Resource Owner Password Credentials" },
167
+ { value: "client_credentials" }
168
+ ]
169
+
170
+ const clientAuthenticationOpts = [
171
+ { value: "Headers", displayName: "Send credentials in headers" },
172
+ { value: "Body", displayName: "Send credentials in body" }
173
+ ]
174
+
175
+ // ========== 数据定义 ==========
176
+ const consumeOpts = [
177
+ { value: "None" },
178
+ { value: "application/json" },
179
+ { value: "application/xml" },
180
+ { value: "application/x-www-form-urlencoded" },
181
+ { value: "multipart/form-data" },
182
+ { value: "text/plain" }
183
+ ];
184
+
141
185
 
142
186
  function renderApiUnit(apiOperator: any, containerRef: any, elementMap: any) {
143
187
  return function (evt: any) {
144
- console.log('点击的API操作:');
145
- console.log('方法:', apiOperator.method);
146
- console.log('URL:', apiOperator.url);
147
- console.log('------------------------');
148
-
149
188
  const currentTarget = evt.currentTarget;
150
189
  // 检查是否已存在对应的新元素
151
190
  if (elementMap.has(currentTarget)) {
@@ -193,7 +232,7 @@ function createApiUnit(apiOperator: any) {
193
232
  // 授权部分
194
233
  const authSection = createSectionAuth(apiOperator);
195
234
  // 请求体部分
196
- const bodySection = createSectionRequestBody(apiOperator.requestBody);
235
+ const bodySection = createSectionRequestBody(apiOperator);
197
236
 
198
237
  reqContent.append(paramSection, headerSection, authSection, bodySection);
199
238
  apiContainer.appendChild(reqContent);
@@ -233,7 +272,6 @@ function createReqOperator(apiOperator: any) {
233
272
  reqUrl.value = apiOperator.url; // 绑定初始值
234
273
  // 可选:添加输入事件监听(根据需求)
235
274
  reqUrl.addEventListener('input', (e: any) => {
236
- console.log('当前值:', e.target.value);
237
275
  // 这里可以添加保存逻辑(如更新状态/发送请求
238
276
  apiOperator.url = e.target.value
239
277
  });
@@ -249,7 +287,6 @@ function createReqOperator(apiOperator: any) {
249
287
  sendButton.appendChild(sendText);
250
288
 
251
289
  sendButton.addEventListener('click', (e: any) => {
252
- console.log('当前值:', e.target.value);
253
290
  // 这里可以添加保存逻辑(如更新状态/发送请求
254
291
  // responseSectionRef 在渲染时会被挂载到 apiOperator._responseSectionRef 上
255
292
  const respRef = apiOperator._responseSectionRef || null;
@@ -331,19 +368,191 @@ function createSectionAuth(apiOperator: any) {
331
368
  authValues.setAttribute('data-layer', 'paraKeyValues');
332
369
  authValues.className = 'Parakeyvalues codigma-apiunit-parakeyvalues';
333
370
 
334
- let auths = apiOperator.auths || []
335
- const authTypeRow = createSelectRow(auths, authValues)
336
- if (auths.length > 0) {
337
- let parameters = apiOperator.auths[0].parameters || [];
338
- const authRows = parameters.map((parameter: any) => createRow(parameter));
339
- authValues.append(authTypeRow, ...authRows);
340
- }
371
+ const authTypeRow = createSelectRow('Authorization Type', auths)
372
+ // 添加选择事件监听
373
+ authTypeRow.children[1].addEventListener('change', function (event: any) {
374
+ //切换前先移除掉原来的元素
375
+ authValues && Array.from(authValues.children).slice(1).forEach((el: any) => el.remove());
376
+ const auth = auths[event.target.selectedIndex];
377
+ if (auth.value == 'No Auth') {
378
+
379
+ } else if (auth.value == 'Basic Auth') {
380
+ apiOperator.auth = [
381
+ {
382
+ name: "userName",
383
+ value: ""
384
+ },
385
+ {
386
+ name: "password",
387
+ value: ""
388
+ }
389
+ ]
390
+ const authRows = apiOperator.auth.map((parameter: any) => createRow(parameter));
391
+ authValues.append(...authRows);
392
+ } else if (auth.value == 'OAuth 2.0') {
393
+ apiOperator.auth = {}
394
+ renderAuthForm(apiOperator, authValues, authTypeRow)
395
+ }
396
+ });
341
397
 
398
+ authValues.append(authTypeRow);
342
399
  authSection.append(authCnr, authValues);
343
400
  return authSection;
344
401
  }
345
402
 
346
- function createSectionRequestBody(requestBody: any) {
403
+ // 工具函数:创建带 class 和属性的元素
404
+ function createElement(tag: string, props: Record<string, string> = {}, classes: string[] = []): HTMLElement {
405
+ const el = document.createElement(tag);
406
+ Object.entries(props).forEach(([key, value]) => el.setAttribute(key, value));
407
+ if (classes.length) el.classList.add(...classes);
408
+ return el;
409
+ }
410
+
411
+ // 工具函数:创建 label + input 结构
412
+ function createLabeledInput(apiOperator: any,
413
+ labelText: string,
414
+ inputId: string,
415
+ inputName: string,
416
+ modelKey: string,
417
+ required: boolean = false
418
+ ): HTMLElement {
419
+ const wrapper = createElement('div', {}, ['Keyvalue', 'codigma-apiunit-keyvalue']);
420
+ const label = createElement('label', { for: inputId }, ['type', 'codigma-apiunit-send']);
421
+ label.textContent = labelText + ':';
422
+ const input = createElement('input', {
423
+ type: 'text',
424
+ id: inputId,
425
+ name: inputName,
426
+ autocomplete: 'off',
427
+ ...(required ? { required: 'true' } : {})
428
+ }, ['Valuetext', 'codigma-apiunit-valuetext']) as HTMLInputElement;
429
+
430
+ // 双向绑定模拟:初始化值 + 监听输入
431
+ input.value = apiOperator.auth[modelKey] || '';
432
+ input.addEventListener('input', (e) => {
433
+ apiOperator.auth[modelKey] = (e.target as HTMLInputElement).value;
434
+ });
435
+
436
+ wrapper.appendChild(label);
437
+ wrapper.appendChild(input);
438
+ return wrapper;
439
+ }
440
+
441
+ // 创建下拉选择框(替代 ast-select)
442
+ function createSelect(apiOperator: any,
443
+ labelText: string,
444
+ selectId: string,
445
+ options: { value: string; displayName?: string }[],
446
+ modelKey: string,
447
+ changeHandler: (value: string) => void
448
+ ): HTMLElement {
449
+ const wrapper = createElement('div', {}, ['Keyvalue', 'codigma-apiunit-keyvalue']);
450
+ const label = createElement('label', { for: selectId }, ['type', 'codigma-apiunit-send']);
451
+ label.textContent = labelText + ':';
452
+
453
+ const select = createElement('select', {
454
+ id: selectId,
455
+ name: selectId
456
+ }, []) as HTMLSelectElement;
457
+
458
+ options.forEach(opt => {
459
+ const option = createElement('option', { value: opt.value }) as HTMLOptionElement;
460
+ option.textContent = opt.displayName || opt.value;
461
+ select.appendChild(option);
462
+ });
463
+
464
+ // 初始化选中值
465
+ select.value = apiOperator.auth[modelKey] || options[0]?.value || '';
466
+ // changeHandler(select.value); // 触发初始设置
467
+
468
+ select.addEventListener('change', (e) => {
469
+ const val = (e.target as HTMLSelectElement).value;
470
+ changeHandler(val);
471
+ });
472
+
473
+ wrapper.appendChild(label);
474
+ wrapper.appendChild(select);
475
+ return wrapper;
476
+ }
477
+
478
+ // 重新渲染整个表单(基于当前 apiOperator.auth.grantType)
479
+ function renderAuthForm(apiOperator: any, authValues: any, authTypeRow: any) {
480
+ authValues.innerHTML = ''; // 清空
481
+
482
+ authValues.append(authTypeRow);
483
+ // Token
484
+ authValues.appendChild(createLabeledInput(apiOperator, 'Token', 'token', 'token', 'token', true));
485
+
486
+ // Grant Type
487
+ authValues.appendChild(
488
+ createSelect(apiOperator,
489
+ 'Grant Type',
490
+ 'grantType',
491
+ grantTypes,
492
+ 'grantType',
493
+ (value: string) => {
494
+ apiOperator.auth.grantType = value;
495
+ renderAuthForm(apiOperator, authValues, authTypeRow); // 重新渲染以反映条件字段
496
+ }
497
+ )
498
+ );
499
+
500
+ const gt = apiOperator.auth.grantType || 'authorization_code';
501
+
502
+ // Conditional: Authorization Code or Implicit
503
+ if (gt === 'authorization_code' || gt === 'Implicit') {
504
+ authValues.appendChild(createLabeledInput(apiOperator, 'Authorization Endpoint', 'authorizationEndpoint', 'authorizationEndpoint', 'authorizationEndpoint', true));
505
+ authValues.appendChild(createLabeledInput(apiOperator, 'Redirect URL', 'RedirectURI', 'RedirectURI', 'redirectURI', true));
506
+ }
507
+
508
+ // Conditional: Token Endpoint needed
509
+ if (gt === 'authorization_code' || gt === 'client_credentials' || gt === 'Resource Owner Password Credentials') {
510
+ authValues.appendChild(createLabeledInput(apiOperator, 'Token Endpoint', 'TokenEndpoint', 'TokenEndpoint', 'tokenEndpoint', true));
511
+ }
512
+
513
+ // Client ID (always shown)
514
+ authValues.appendChild(createLabeledInput(apiOperator, 'Client ID', 'ClientID', 'ClientID', 'clientId', true));
515
+
516
+ // Conditional: Client Secret
517
+ if (gt === 'authorization_code' || gt === 'client_credentials' || gt === 'Resource Owner Password Credentials') {
518
+ authValues.appendChild(createLabeledInput(apiOperator, 'Client Secret', 'ClientSecret', 'ClientSecret', 'clientSecret', true));
519
+ }
520
+
521
+ // Conditional: Username & Password
522
+ if (gt === 'Resource Owner Password Credentials') {
523
+ authValues.appendChild(createLabeledInput(apiOperator, 'Username', 'Username', 'Username', 'username', true));
524
+ authValues.appendChild(createLabeledInput(apiOperator, 'Password', 'Password', 'Password', 'password', true));
525
+ }
526
+
527
+ // Scopes (always shown)
528
+ authValues.appendChild(createLabeledInput(apiOperator, 'Scopes', 'Scopes', 'Scopes', 'scopes', true));
529
+
530
+ // Client Authentication
531
+ authValues.appendChild(
532
+ createSelect(apiOperator,
533
+ 'Client Authentication',
534
+ 'clientAuthentication',
535
+ clientAuthenticationOpts,
536
+ 'clientAuthentication',
537
+ (value: string) => {
538
+ apiOperator.auth.clientAuthentication = value;
539
+ // 不需要重新渲染整个表单,除非 UI 依赖此值
540
+ }
541
+ )
542
+ );
543
+
544
+ // --- 新增:Generate Token 按钮 ---
545
+ const buttonWrapper = createElement('div', {}, ['parameter-item']);
546
+ const generateBtn = createElement('button', {}, ['parameter-button']) as HTMLButtonElement;
547
+ generateBtn.type = 'button';
548
+ generateBtn.textContent = 'Generate Token';
549
+ generateBtn.addEventListener('click', () => generateOAuth2Token(apiOperator)); // 绑定点击事件
550
+
551
+ buttonWrapper.appendChild(generateBtn);
552
+ authValues.appendChild(buttonWrapper);
553
+ }
554
+
555
+ function createSectionRequestBody(apiOperator: any) {
347
556
  const bodySection = document.createElement('div');
348
557
  bodySection.setAttribute('data-layer', 'request-body-section');
349
558
  bodySection.className = 'RequestBodySection codigma-apiunit-request-body-section';
@@ -358,14 +567,170 @@ function createSectionRequestBody(requestBody: any) {
358
567
  bodyCnr.appendChild(bodyText);
359
568
  bodySection.appendChild(bodyCnr);
360
569
  // 请求体内容
361
- const bodyValue = document.createElement('textarea');
362
- bodyValue.setAttribute('data-layer', 'bodyTextValue');
363
- bodyValue.className = 'Id0CategoryId0NameNamePhotourlsTagsId0NameStatusAvailable codigma-apiunit-parakeyvalues';
364
- bodyValue.value = JSON.stringify(requestBody);
365
- bodySection.appendChild(bodyValue);
570
+ renderConsumeSection(apiOperator, bodySection, bodyCnr);
366
571
  return bodySection;
367
572
  }
368
573
 
574
+
575
+
576
+
577
+ // ========== 创建下拉选择框 ==========
578
+ function createConsumeSelect(apiOperator: any, container: HTMLElement, bodyCnr: any) {
579
+ const wrapper = createElement('div');
580
+ const label = createElement('label');
581
+ label.style.margin = '0 1rem';
582
+ label.textContent = 'Content-Type';
583
+
584
+ const select = createElement('select') as HTMLSelectElement;
585
+ select.style.width = '18rem'; // 模拟 [width]="'18rem'"
586
+
587
+ consumeOpts.forEach(opt => {
588
+ const option = document.createElement('option');
589
+ option.value = opt.value;
590
+ option.textContent = opt.value;
591
+ select.appendChild(option);
592
+ });
593
+
594
+ // 设置当前值
595
+ select.value = apiOperator.currentConsume || getCurrentConsume(apiOperator);
596
+
597
+ // 监听 change
598
+ select.addEventListener('change', (e) => {
599
+ const newValue = (e.target as HTMLSelectElement).value;
600
+ if (apiOperator.currentConsume !== newValue) {
601
+ apiOperator.currentConsume = newValue;
602
+ renderConsumeSection(apiOperator, container, bodyCnr); // 重新渲染整个区域
603
+ }
604
+ });
605
+
606
+ wrapper.appendChild(label);
607
+ wrapper.appendChild(select);
608
+ return wrapper;
609
+ }
610
+
611
+ // ========== 渲染请求体到指定容器(无 ID 依赖) ==========
612
+ function renderRequestBodyInto(apiOperator: any, targetDiv: HTMLElement) {
613
+ targetDiv.innerHTML = ''; // 清空目标区域
614
+
615
+ // 确保 requestBody 和 content 存在
616
+ const ct = apiOperator.currentConsume || getCurrentConsume(apiOperator);
617
+ // ✅ 情况 1: 使用 textarea(JSON/XML/None/text/plain)
618
+ if (
619
+ ct === null ||
620
+ ct === 'None' ||
621
+ ct === 'application/json' ||
622
+ ct === 'application/xml' ||
623
+ ct === 'text/plain'
624
+ ) {
625
+ // 决定使用哪个 key:'None' 也视为一种 content 类型(或可映射为 '',但这里直接用 'None')
626
+ const mimeType = ct || 'None';
627
+
628
+ // 从 content[mimeType] 读取,若无则默认空字符串
629
+ let rawValue = apiOperator.requestBody.content[mimeType];
630
+ if (rawValue == null) {
631
+ rawValue = '';
632
+ apiOperator.requestBody.content[mimeType] = rawValue; // 初始化
633
+ }
634
+
635
+ const textarea = createElement('textarea', {}, []) as HTMLTextAreaElement;
636
+ textarea.style.width = '100%';
637
+ textarea.style.height = '200px';
638
+ textarea.style.fontFamily = 'monospace';
639
+ textarea.style.fontSize = '14px';
640
+ textarea.placeholder = `Enter ${mimeType} body...`;
641
+
642
+ textarea.value = rawValue;
643
+
644
+ // 监听输入 → 写回 content[mimeType]
645
+ textarea.addEventListener('input', () => {
646
+ apiOperator.requestBody.content[mimeType] = textarea.value;
647
+ console.log(`Content updated for ${mimeType}:`, textarea.value);
648
+ });
649
+
650
+ targetDiv.appendChild(textarea);
651
+
652
+ }
653
+ // ✅ 情况 2: 表单参数
654
+ else if (
655
+ (ct === 'application/x-www-form-urlencoded' || ct === 'multipart/form-data') &&
656
+ apiOperator.requestBody.content &&
657
+ Array.isArray(apiOperator.requestBody.content[ct])
658
+ ) {
659
+ const params = apiOperator.requestBody.content[ct];
660
+ params.forEach((param: any, index: number) => {
661
+ const item = createElement('div', {}, ['Keyvalue', 'codigma-apiunit-keyvalue']);
662
+
663
+ const label = createElement('label', { for: `param-${Date.now()}-${index}` }, ['petId', 'codigma-apiunit-send']);
664
+ label.textContent = `${param.name}:`;
665
+
666
+ const input = createElement('input', {
667
+ type: param.uiType || 'text',
668
+ // 使用时间戳+索引避免 id 冲突(仅用于 label for)
669
+ id: `param-${Date.now()}-${index}`,
670
+ name: 'name'
671
+ }, ['Valuetext', 'codigma-apiunit-valuetext']) as HTMLInputElement;
672
+
673
+ input.value = param.value || '';
674
+ input.autocomplete = 'off';
675
+ input.required = true;
676
+
677
+ input.addEventListener('keydown', (e) => {
678
+ setEditStatus(e, true);
679
+ });
680
+
681
+ input.addEventListener('input', () => {
682
+ param.value = input.value;
683
+ });
684
+
685
+ item.appendChild(label);
686
+ item.appendChild(input);
687
+ targetDiv.appendChild(item);
688
+ });
689
+ }
690
+ }
691
+
692
+ function getCurrentConsume(apiOperator: any) {
693
+ if (!apiOperator.requestBody) {
694
+ apiOperator.requestBody = { content: {} };
695
+ }
696
+ if (!apiOperator.requestBody.content) {
697
+ apiOperator.requestBody.content = {};
698
+ }
699
+
700
+ const keys = Object.keys(apiOperator.requestBody.content);
701
+ const ct = apiOperator.currentConsume = keys.length > 0 ? keys[0] : 'None';
702
+ return ct;
703
+ }
704
+
705
+ // ========== 模拟 setEditStatus(你原有逻辑) ==========
706
+ function setEditStatus(event: KeyboardEvent, status: boolean) {
707
+ // TODO: 根据你的业务逻辑实现
708
+ console.log('setEditStatus called:', event.key, status);
709
+ }
710
+
711
+ // ========== 主渲染函数 ==========
712
+ function renderConsumeSection(apiOperator: any, container: HTMLElement, bodyCnr: any) {
713
+ container.innerHTML = '';
714
+
715
+ container.appendChild(bodyCnr);
716
+
717
+ // 创建外层包裹 div
718
+ const wrapperDiv = createElement('div', {}, ['codigma-apiunit-parakeyvalues']);
719
+
720
+ // 1. Content-Type 选择器
721
+ const selectWrapper = createConsumeSelect(apiOperator, container, bodyCnr);
722
+ wrapperDiv.appendChild(selectWrapper);
723
+
724
+ // 2. 请求体区域(不再使用固定 ID)
725
+ const reqBodyContainer = createElement('div', {}, ['codigma-apiunit-request-body-info']); // 无 id,避免冲突
726
+ renderRequestBodyInto(apiOperator, reqBodyContainer); // 改为传入目标容器
727
+ wrapperDiv.appendChild(reqBodyContainer);
728
+
729
+ // 将整个包裹 div 添加到外部容器
730
+ container.appendChild(wrapperDiv);
731
+ }
732
+
733
+
369
734
  function createSectionResponse(apiOperator: any) {
370
735
  const responseSection = document.createElement('div');
371
736
  responseSection.setAttribute('data-layer', 'reqresponse');
@@ -405,6 +770,11 @@ function createTimeStatusElement(apiOperator: any) {
405
770
  return timeStatus;
406
771
  }
407
772
 
773
+ function updateTimeStatus(timeStatus: any, apiOperator: any) {
774
+ timeStatus.textContent
775
+ = `Status: ${apiOperator.response.status || ""} ${apiOperator.response.statusText || ""} Time: ${apiOperator.requestDuration || ""}`;
776
+ }
777
+
408
778
  function createRow(parameter: any) {
409
779
  const petIdRow = document.createElement('div');
410
780
  petIdRow.setAttribute('data-layer', 'keyValue');
@@ -420,7 +790,6 @@ function createRow(parameter: any) {
420
790
 
421
791
  // 可选:添加输入事件监听(根据需求)
422
792
  petIdValue.addEventListener('input', (e: any) => {
423
- console.log('当前值:', e.target.value);
424
793
  // 这里可以添加保存逻辑(如更新状态/发送请求
425
794
  parameter["value"] = e.target.value
426
795
  });
@@ -437,7 +806,7 @@ function createInputElement() {
437
806
  return inputText;
438
807
  }
439
808
 
440
- function createSelectRow(auths: any, authValues: any) {
809
+ function createSelectRow(name: string, args: any) {
441
810
  // 创建外层容器div
442
811
  const container = document.createElement('div');
443
812
  container.setAttribute('data-layer', 'keyValue');
@@ -445,9 +814,9 @@ function createSelectRow(auths: any, authValues: any) {
445
814
 
446
815
  // 创建type显示div
447
816
  const typeLabel = document.createElement('div');
448
- typeLabel.setAttribute('data-layer', "type");
817
+ typeLabel.setAttribute('data-layer', name);
449
818
  typeLabel.className = 'type codigma-apiunit-send';
450
- typeLabel.textContent = 'Type:';
819
+ typeLabel.textContent = name + ":";
451
820
 
452
821
  // 创建select元素
453
822
  const selectElement: any = document.createElement('select');
@@ -456,22 +825,13 @@ function createSelectRow(auths: any, authValues: any) {
456
825
  selectElement.setAttribute('data-layer', 'valueText');
457
826
 
458
827
  // 示例选项(可根据实际需求添加
459
- auths.forEach((auth: any) => {
828
+ args.forEach((auth: any) => {
460
829
  const option1 = document.createElement('option');
461
- option1.value = auth["type"];
462
- option1.textContent = auth["type"];
830
+ option1.value = auth["value"];
831
+ option1.textContent = (auth['displayName'] != null || auth['label'] != null) ? (auth["displayName"] || auth['label']): auth["value"];
463
832
  selectElement.appendChild(option1);
464
833
  })
465
834
 
466
- // 添加选择事件监听
467
- selectElement.addEventListener('change', function (event: any) {
468
- //切换前先移除掉原来的元素
469
- authValues && Array.from(authValues.children).slice(1).forEach((el: any) => el.remove());
470
- const auth = auths[event.target.selectedIndex];
471
- let parameters = auth.parameters || [];
472
- const authRows = parameters.map((parameter: any) => createRow(parameter));
473
- authValues.append(...authRows);
474
- });
475
835
  // 组装DOM结构
476
836
  container.appendChild(typeLabel);
477
837
  container.appendChild(selectElement);
@@ -486,6 +846,179 @@ function isHeaderParam(param: any) {
486
846
  return param.in === 'header';
487
847
  }
488
848
 
849
+
850
+ async function generateOAuth2Token(apiInfo: any) {
851
+ // TODO 生成OAuth2.0 Token
852
+ // 1. 获取访问令牌
853
+ const accessToken = await getAccessToken(apiInfo);
854
+ apiInfo.auth.token = accessToken
855
+ }
856
+
857
+
858
+ /**
859
+ * 获取 OAuth 2.0 访问令牌
860
+ * @returns {Promise<string>} 访问令牌
861
+ */
862
+ async function getAccessToken(apiInfo: any) {
863
+ const auth = apiInfo.auth;
864
+ if (auth == null) {
865
+ throw new Error('OAuth2.0认证信息未配置');
866
+ }
867
+ const CLIENT_ID = auth.clientId;
868
+ const CLIENT_SECRET = auth.clientSecret;
869
+ const SCOPE = auth.scopes;
870
+
871
+ if (apiInfo.auth.grantType == 'client_credentials') {
872
+ try {
873
+ console.log('正在获取访问令牌...');
874
+
875
+ // 构造请求头
876
+ const headerParams: any = {
877
+ 'Content-Type': 'application/x-www-form-urlencoded'
878
+ };
879
+ // 构造请求体 (使用 client_credentials 流)
880
+ const bodyParams = new URLSearchParams();
881
+ bodyParams.append('grant_type', 'client_credentials');
882
+ if (SCOPE) {
883
+ bodyParams.append('scope', SCOPE);
884
+ }
885
+
886
+ if (apiInfo.auth.clientAuthentication == 'Body') {
887
+ bodyParams.append('client_id', CLIENT_ID);
888
+ bodyParams.append('client_secret', CLIENT_SECRET);
889
+ } else {
890
+ // 如果是 Headers 方式,则在后续的 fetch 请求中添加 Authorization 头
891
+ headerParams['Authorization'] = 'Basic ' + btoa(CLIENT_ID + ':' + CLIENT_SECRET);
892
+ }
893
+
894
+ const response = await fetch(auth.tokenEndpoint, {
895
+ method: 'POST',
896
+ headers: headerParams,
897
+ body: bodyParams.toString()
898
+ })
899
+
900
+ if (!response.ok) {
901
+ const errorData = await response.json().catch(() => ({}));
902
+ throw new Error(`获取令牌失败: ${response.status} ${response.statusText}. ${JSON.stringify(errorData)}`);
903
+ }
904
+
905
+ const tokenData = await response.json();
906
+ console.log('令牌获取成功:', tokenData);
907
+ return tokenData.access_token;
908
+ } catch (error) {
909
+ console.error('获取访问令牌时出错:', error);
910
+ throw error;
911
+ }
912
+ } else if (apiInfo.auth.grantType == 'Resource Owner Password Credentials') {
913
+
914
+ } else if (apiInfo.auth.grantType == 'authorization_code') {
915
+ // ========== 配置:替换为你在本地 OAuth 服务注册的客户端信息 ==========
916
+ const config = {
917
+ clientId: apiInfo.auth.clientId, // 替换为你的 client_id
918
+ clientSecret: apiInfo.auth.clientSecret, // 替换为你的 client_secret
919
+ redirectUri: apiInfo.auth.redirectURI, // 必须与注册的 redirect_uri 一致
920
+ authUrl: apiInfo.auth.authorizationEndpoint,
921
+ tokenUrl: apiInfo.auth.tokenEndpoint,
922
+ scope: apiInfo.auth.scopes // 可选: 'openid profile email' 如果服务支持
923
+ };
924
+
925
+ // 从 URL 获取参数
926
+ function getUrlParams() {
927
+ const params = new URLSearchParams(window.location.search);
928
+ return Object.fromEntries(params.entries());
929
+ }
930
+
931
+ // 构建授权 URL 并跳转
932
+ function redirectToAuth() {
933
+ const state = Math.random().toString(36).substring(2);
934
+ const nonce = Math.random().toString(36).substring(2); // OpenID Connect 推荐使用 nonce
935
+ const authUrl = new URL(config.authUrl);
936
+ authUrl.searchParams.append('client_id', config.clientId);
937
+ authUrl.searchParams.append('redirect_uri', config.redirectUri);
938
+ authUrl.searchParams.append('response_type', 'code');
939
+ authUrl.searchParams.append('scope', config.scope);
940
+ authUrl.searchParams.append('state', state);
941
+ authUrl.searchParams.append('nonce', nonce); // 用于 ID Token 验证
942
+ authUrl.searchParams.append('access_type', 'offline'); // 请求 refresh_token(如果支持)
943
+
944
+ const newTab = window.open(authUrl.toString(), '_blank');
945
+ }
946
+
947
+ // // 使用授权码换取 Token
948
+ // async function exchangeCodeForToken(code: any) {
949
+ // try {
950
+ // const response = await fetch(config.tokenUrl, {
951
+ // method: 'POST',
952
+ // headers: {
953
+ // 'Content-Type': 'application/x-www-form-urlencoded',
954
+ // 'Authorization': 'Basic ' + btoa(config.clientId + ':' + config.clientSecret)
955
+ // },
956
+ // body: new URLSearchParams({
957
+ // // 'client_id': config.clientId,
958
+ // // 'client_secret': config.clientSecret,
959
+ // 'code': code,
960
+ // 'redirect_uri': config.redirectUri,
961
+ // 'grant_type': 'authorization_code'
962
+ // })
963
+ // });
964
+
965
+ // if (!response.ok) {
966
+ // const errorText = await response.text();
967
+ // throw new Error(`HTTP ${response.status}: ${errorText}`);
968
+ // }
969
+
970
+ // const tokenData = await response.json();
971
+ // console.log('Token Response:', tokenData);
972
+
973
+ // // 可选:清除 URL 参数
974
+ // // history.replaceState({}, document.title, window.location.pathname);
975
+ // } catch (error) {
976
+ // console.error('Token Exchange Error:', error);
977
+ // }
978
+ // }
979
+
980
+ // // 页面初始化
981
+ // document.addEventListener('DOMContentLoaded', () => {
982
+ // const loginBtn = document.getElementById('loginBtn');
983
+ // const urlParams = getUrlParams();
984
+
985
+ // if (urlParams.code) {
986
+ // // 已收到授权码
987
+ // loginBtn.style.display = 'none';
988
+ // exchangeCodeForToken(urlParams.code);
989
+ // } else {
990
+
991
+ // }
992
+ // });
993
+
994
+ redirectToAuth();
995
+ } else if (apiInfo.auth.grantType == 'Implicit') {
996
+
997
+ } else {
998
+
999
+ }
1000
+ }
1001
+
1002
+
1003
+ function getBodyEditorContent(apiOperator: any): string {
1004
+ const ct = apiOperator.currentConsume || 'None';
1005
+ return apiOperator.requestBody.content[ct];
1006
+ }
1007
+
1008
+
1009
+
1010
+ let gotResponse = false;
1011
+
1012
+ let ifSendingRequest = false;
1013
+ let requestDuration: string = "0";
1014
+ let responseObj: {
1015
+ status ?: number | string;
1016
+ statusText ?: string;
1017
+ body ?: string;//响应体统一转换成字符串
1018
+ } = { };
1019
+ // 创建一个新的 AbortController 实例
1020
+ let controller: AbortController | undefined;
1021
+
489
1022
  /**
490
1023
  *
491
1024
  *
@@ -500,43 +1033,45 @@ function isHeaderParam(param: any) {
500
1033
  * @param {*} apiOperator
501
1034
  * @param {*} apiInfo
502
1035
  */
503
-
504
1036
  function sendRequest(apiOperator: any, responseSectionRef: any) {
505
- // 防御性检查:确保传入了 responseSectionRef,避免后续调用 querySelector 时抛出错误
506
- if (!responseSectionRef) {
507
- console.error('sendRequest: missing responseSectionRef. Aborting request to avoid runtime errors.');
508
- return;
509
- }
510
- let reuqestUrl = getRequestUrl(apiOperator);
511
- const result = checkIfParameter(apiOperator);
1037
+ const apiInfo = apiOperator
1038
+ let reuqestUrl = getRequestUrl(apiInfo);
1039
+
1040
+ //TODO 根据参数类型构造请求头和请求体,apiInfo['parameterHasFormDataVer2']表示有formData参数,暂时未区分具体的content-type,待改造
1041
+ let header = apiInfo['parameterHasFormDataVer2'] ? 'application/x-www-form-urlencoded' :
1042
+ ((apiInfo['parameterHasBody']) ? apiInfo['currentConsume'] : 'application/json');
512
1043
 
513
- let header = result.hasRequestBody ? 'application/json' :
514
- (result.hasRequestFormData ? 'application/x-www-form-urlencoded' : 'application/json');
515
1044
  let headers: any = {
516
1045
  'Content-Type': header
517
1046
  }
518
-
519
- //TODO
520
- if (apiOperator.custom) {
521
- for (let index = 0; index < apiOperator.customHeaderparameters.length; index++) {
522
- const paras = apiOperator.customHeaderparameters[index];
523
- if (paras.name != '' && paras.value != '' && paras.name != null && paras.value != null) {
524
- headers[paras.name] = paras.value
1047
+ const headerParas = getHeadersParams(apiInfo, apiInfo.rawApiInfo.parameters || []);
1048
+ // 合并自定义头参数
1049
+ Object.assign(headers, headerParas);
1050
+ // 构造 Basic Auth
1051
+ addAuthHeader(apiInfo, headers);
1052
+
1053
+ let body;
1054
+ if (apiInfo.method.toUpperCase() == "POST" || apiInfo.method.toUpperCase() == "PUT") {
1055
+ if (apiInfo['currentConsume'] == 'application/json' && apiInfo.requestBody != null) {
1056
+ body = getBodyEditorContent(apiOperator);
1057
+ } else if (apiInfo['parameterHasFormDataVer2']) {
1058
+ body = getRequestFormData(apiInfo.rawApiInfo);
1059
+ } else if (apiInfo['currentConsume'] == 'application/x-www-form-urlencoded') {
1060
+ for (const key of apiInfo["requestBody"]["content"][apiInfo['currentConsume']]) {
1061
+ if (body == null || body == '') {
1062
+ body = key.name + "=" + key.value;
1063
+ } else {
1064
+ body = body + "&" + key.name + "=" + key.value;
1065
+ }
525
1066
  }
1067
+ } else {
1068
+ body = "";
526
1069
  }
1070
+ } else {
1071
+ body = "";
527
1072
  }
528
1073
 
529
- let body = result.hasRequestBody ? apiOperator.requestBody :
530
- (result.hasRequestFormData ? getRequestFormData(apiOperator.rawApiInfo) : null);
531
-
532
- //TODO
533
- if (apiOperator.custom) {
534
- if (apiOperator.method.toUpperCase() == "POST" || apiOperator.method.toUpperCase() == "PUT") {
535
- body = apiOperator.requestBody;
536
- }
537
- }
538
-
539
- apiOperator.ifSendingRequest = true;
1074
+ ifSendingRequest = true;
540
1075
  const startTime = Date.now(); // 记录开始时间
541
1076
 
542
1077
  apiOperator.controller = new AbortController();
@@ -547,32 +1082,32 @@ function sendRequest(apiOperator: any, responseSectionRef: any) {
547
1082
 
548
1083
  // 使用 fetch 发送请求,并传递 signal
549
1084
  fetch(reuqestUrl, {
550
- method: apiOperator.method.toUpperCase(),
1085
+ method: apiInfo.method.toUpperCase(),
551
1086
  headers: headers,
552
- body: body != null ? JSON.stringify(body) : null,
1087
+ body: body,
553
1088
  signal: signal
554
1089
  })
555
1090
  .then(response => {
556
1091
  if (!response.ok) {
557
- apiOperator.response = {
1092
+ responseObj = {
558
1093
  status: response.status,
559
- statusText: response.statusText
1094
+ statusText: response.statusText,
1095
+ body: ""
560
1096
  }
561
1097
 
562
1098
  const endTime = Date.now(); // 即使在错误的情况下也记录结束时间
563
- apiOperator.requestDuration = formatDuration(endTime - startTime);
564
- apiOperator.ifSendingRequest = false;
565
- // apiOperator.responseJsoneditor.value = "";
1099
+ requestDuration = formatDuration(endTime - startTime);
1100
+ ifSendingRequest = false;
566
1101
  throw new Error('Network response was not ok.');
567
1102
  }
568
1103
  const endTime = Date.now(); // 记录结束时间
569
- apiOperator.requestDuration = formatDuration(endTime - startTime); // 计算耗时
1104
+ requestDuration = formatDuration(endTime - startTime); // 计算耗时
570
1105
 
571
1106
  const responsebodyElement = responseSectionRef.querySelector('[data-layer="responsebody"]');
572
1107
  responsebodyElement.removeChild(overlayLayerContainer);
573
1108
 
574
- apiOperator.ifSendingRequest = false;
575
- apiOperator.response = {
1109
+ ifSendingRequest = false;
1110
+ responseObj = {
576
1111
  status: response.status,
577
1112
  statusText: response.statusText
578
1113
  }
@@ -593,18 +1128,19 @@ function sendRequest(apiOperator: any, responseSectionRef: any) {
593
1128
  });
594
1129
  })
595
1130
  .then(data => {
596
- apiOperator.gotResponse = true;
1131
+ gotResponse = true;
597
1132
  const responsebodyElement = responseSectionRef.querySelector('[data-layer="responsebody"]');
598
1133
  // 此时 data 可能是 JSON 对象,也可能是文本字符串
599
1134
  if (typeof data === 'object') {
600
1135
  // 假设 data 是 JSON 对象,你可以在这里处理它
601
1136
  console.log('Received JSON:', data);
602
- responsebodyElement.textContent = JSON.stringify(data, null, 4)
1137
+ responseObj.body = JSON.stringify(data, null, 4);
603
1138
  } else {
604
1139
  // 假设 data 是文本字符串,你可以在这里处理它
605
1140
  console.log('Received text:', data);
606
- responsebodyElement.textContent = data;
1141
+ responseObj.body = data;
607
1142
  }
1143
+ responsebodyElement.textContent = responseObj.body;
608
1144
  })
609
1145
  .catch(error => {
610
1146
  // 错误处理
@@ -612,11 +1148,6 @@ function sendRequest(apiOperator: any, responseSectionRef: any) {
612
1148
  });
613
1149
  }
614
1150
 
615
- function updateTimeStatus(timeStatus: any, apiOperator: any) {
616
- timeStatus.textContent
617
- = `Status: ${apiOperator.response.status || ""} ${apiOperator.response.statusText || ""} Time: ${apiOperator.requestDuration || ""}`;
618
- }
619
-
620
1151
  function createRequestOverlayLayer(apiOperator: any, responseSectionRef: any) {
621
1152
  // 创建主容器
622
1153
  const container = document.createElement('div');
@@ -673,40 +1204,15 @@ function createRequestOverlayLayer(apiOperator: any, responseSectionRef: any) {
673
1204
  return container
674
1205
  }
675
1206
 
676
- function checkIfParameter(apiOperator: any) {
677
- let hasRequestBody = false;
678
- let hasRequestFormData = false;
679
- const parameters = apiOperator.rawApiInfo.parameters;
680
- if (parameters) {
681
- for (let index = 0; index < parameters.length; index++) {
682
- const parameter = parameters[index];
683
- if (parameter.in == "query" || parameter.in == "path") {
684
- } else if (parameter.in == "body") {
685
- hasRequestBody = true;
686
- parameter.name = parameter.name.charAt(0).toUpperCase() + parameter.name.slice(1);
687
- } else if (parameter.in == "formData") {
688
- hasRequestFormData = true;
689
- }
690
- }
691
- }
692
-
693
- //support openapi 3.0
694
- const requestBody = apiOperator.rawApiInfo.requestBody;
695
- if (requestBody) {
696
- hasRequestBody = true;
697
- }
698
- // 返回包含两个状态的对象
699
- return { hasRequestBody, hasRequestFormData };
700
- }
701
1207
 
702
1208
  function formatDuration(milliseconds: number) {
703
- let totalSeconds: string | number = Math.floor(milliseconds / 1000);
704
- let seconds: string | number = totalSeconds % 60;
705
- let minutes: string | number = Math.floor(totalSeconds / 60) % 60;
706
- let hours: string | number = Math.floor(totalSeconds / (60 * 60));
1209
+ let totalSeconds = Math.floor(milliseconds / 1000);
1210
+ let seconds: any = totalSeconds % 60;
1211
+ let minutes: any = Math.floor(totalSeconds / 60) % 60;
1212
+ let hours: any = Math.floor(totalSeconds / (60 * 60));
707
1213
 
708
1214
  // 毫秒部分
709
- let millisecondsPart: string | number = Math.floor(milliseconds % 1000);
1215
+ let millisecondsPart: any = Math.floor(milliseconds % 1000);
710
1216
  // 毫秒不足三位时前面补0
711
1217
  millisecondsPart = millisecondsPart.toString().padStart(3, '0');
712
1218
 
@@ -719,10 +1225,18 @@ function formatDuration(milliseconds: number) {
719
1225
  return `${hours}h${minutes}m${seconds}s${millisecondsPart}ms`;
720
1226
  }
721
1227
 
722
- function getRequestUrl(apiOperator: any) {
723
- let reuqestUrl = apiOperator.url;
1228
+ function clickAbortRequest() {
1229
+ // 如果你想取消请求,调用 controller 的 abort 方法
1230
+ if (controller) {
1231
+ controller.abort();
1232
+ ifSendingRequest = false;
1233
+ }
1234
+ }
1235
+
1236
+ function getRequestUrl(apiInfo: any) {
1237
+ let reuqestUrl = apiInfo.url;
724
1238
 
725
- const requestParameters = apiOperator.rawApiInfo.parameters || [];
1239
+ const requestParameters = apiInfo.rawApiInfo.parameters || [];
726
1240
  if (requestParameters == null) {
727
1241
  return reuqestUrl;
728
1242
  }
@@ -733,13 +1247,51 @@ function getRequestUrl(apiOperator: any) {
733
1247
  }
734
1248
  }
735
1249
 
736
- let queryParams = getQueryParams(apiOperator, requestParameters);
1250
+ let queryParams = getQueryParams(apiInfo, requestParameters);
737
1251
  reuqestUrl = queryParams.length > 0 ? (reuqestUrl + "?" + queryParams.join("&")) : reuqestUrl;
738
1252
 
739
1253
  return reuqestUrl;
740
1254
  }
741
1255
 
742
- function getQueryParams(apiOperator: any, requestParameters: any) {
1256
+ function addAuthHeader(apiInfo: any, headers: any) {
1257
+ const env = isBrowserEnvironment();
1258
+ if (apiInfo.auth.authType == "Basic Auth" && apiInfo.auth != null) {
1259
+ if (env) {
1260
+ const credentials = btoa(`${apiInfo.auth.username}:${apiInfo.auth.passWord}`); // btoa 是浏览器内置的 Base64 编码函数
1261
+ headers['Authorization'] = `Basic ${credentials}`;
1262
+ } else {
1263
+ const buffer = Buffer.from(`${apiInfo.auth.username}:${apiInfo.auth.passWord}`);
1264
+ const credentials = buffer.toString('base64');
1265
+ headers['Authorization'] = `Basic ${credentials}`;
1266
+ }
1267
+ } else if (apiInfo.auth.authType == "OAuth 2.0" && apiInfo.auth != null) {
1268
+ if (apiInfo.auth.token != null || apiInfo.auth.token != '') {
1269
+ headers['Authorization'] = `Bearer ${apiInfo.auth.token}`;
1270
+ }
1271
+ }
1272
+ }
1273
+
1274
+ function getHeadersParams(apiInfo: any, requestParameters: any) {
1275
+ let headersParams = [];
1276
+ for (const element of requestParameters) {
1277
+ if (element.in == "header") {
1278
+ headersParams[element.name] = element.value
1279
+ }
1280
+ }
1281
+
1282
+ if (apiInfo.customHeaderparameters) {
1283
+ for (let index = 0; index < apiInfo.customHeaderparameters.length; index++) {
1284
+ const paras = apiInfo.customHeaderparameters[index];
1285
+ if (paras.name != '' && paras.value != '' && paras.name != null && paras.value != null) {
1286
+ headersParams[paras.name] = paras.value
1287
+ }
1288
+ }
1289
+ }
1290
+
1291
+ return headersParams;
1292
+ }
1293
+
1294
+ function getQueryParams(apiInfo: any, requestParameters: any) {
743
1295
  let queryParams = [];
744
1296
  for (const element of requestParameters) {
745
1297
  if (element.in == "query") {
@@ -755,9 +1307,9 @@ function getQueryParams(apiOperator: any, requestParameters: any) {
755
1307
  }
756
1308
  }
757
1309
 
758
- if (apiOperator.custom) {
759
- for (let index = 0; index < apiOperator.customQueryparameters.length; index++) {
760
- const paras = apiOperator.customQueryparameters[index];
1310
+ if (apiInfo.customQueryparameters) {
1311
+ for (let index = 0; index < apiInfo.customQueryparameters.length; index++) {
1312
+ const paras = apiInfo.customQueryparameters[index];
761
1313
  if (paras.name != '' && paras.value != '' && paras.name != null && paras.value != null) {
762
1314
  queryParams.push(paras.name + "=" + paras.value)
763
1315
  }
@@ -788,6 +1340,16 @@ function getRequestFormData(rawApiInfo: any) {
788
1340
  return formData;
789
1341
  }
790
1342
 
1343
+ function isBrowserEnvironment() {
1344
+ if (typeof window !== 'undefined' && typeof document !== 'undefined') {
1345
+ return true; // 浏览器环境
1346
+ } else if (typeof process !== 'undefined' && process.versions && process.versions.node) {
1347
+ return false; // Node.js 环境
1348
+ } else {
1349
+ return false;
1350
+ }
1351
+ }
1352
+
791
1353
 
792
1354
  function createSvg() {
793
1355
  const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
@@ -810,20 +1372,17 @@ function createSvg() {
810
1372
 
811
1373
  function parseParaModel(parameterObj: any, dataDef: any) {
812
1374
  let bodyModel;
813
- if (parameterObj && parameterObj.schema) {
814
- if (parameterObj.schema['$ref']) { // 对象类型
815
- bodyModel = parseModel(parameterObj.schema['$ref'], dataDef);
816
- } else if (parameterObj.schema['type'] == 'array') { // 数组类型
817
- const itemObj = parameterObj.schema['items'];
818
- if (itemObj['$ref']) {
819
- bodyModel = parseModel(itemObj['$ref'], dataDef);
820
- } else if (itemObj['type']) {
821
- bodyModel = parseElement(itemObj);
822
- }
823
- bodyModel = [bodyModel];
1375
+ if (parameterObj.schema['$ref']) { // 对象类型
1376
+ bodyModel = parseModel(parameterObj.schema['$ref'], dataDef);
1377
+ } else if (parameterObj.schema['type'] == 'array') { // 数组类型
1378
+ const itemObj = parameterObj.schema['items'];
1379
+ if (itemObj['$ref']) {
1380
+ bodyModel = parseModel(itemObj['$ref'], dataDef);
1381
+ } else if (itemObj['type']) {
1382
+ bodyModel = parseElement(itemObj);
824
1383
  }
1384
+ bodyModel = [bodyModel];
825
1385
  }
826
-
827
1386
  return bodyModel;
828
1387
  }
829
1388
 
@@ -856,10 +1415,69 @@ function parseModel(modelDef: any, apiDef: any) {
856
1415
  return model;
857
1416
  }
858
1417
 
1418
+ function parseFormDataModel(modelDef: any, apiDef: any) {
1419
+ const model: any = [];
1420
+ const bodyName = modelDef.substring(modelDef.lastIndexOf('/') + 1);
1421
+ const def = apiDef[bodyName];
1422
+ const props = def['properties'];
1423
+ if (props) {
1424
+ for (const key in props) {
1425
+ if (Object.prototype.hasOwnProperty.call(props, key)) {
1426
+ const element = props[key];
1427
+ let modelEle: any;
1428
+ if (element.hasOwnProperty('items') && element['type'] == 'array') {
1429
+ if (element["items"]['$ref']) {
1430
+ modelEle = [parseModel(element["items"]['$ref'], apiDef)]
1431
+ } else if (element["items"]['type']) {
1432
+ modelEle = [parseElement(element["items"])];
1433
+ }
1434
+ if (modelEle) {
1435
+ model.push({
1436
+ type: 'string',
1437
+ uiType: 'text',
1438
+ name: key,
1439
+ value: JSON.stringify(modelEle)
1440
+ });
1441
+ }
1442
+ } else if (element['type']) {
1443
+ model.push({
1444
+ type: element['type'],
1445
+ uiType: ((element['type'] == 'integer' || element['type'] == 'number') ? 'number' : 'text'),
1446
+ name: key,
1447
+ value: parseElement(element)
1448
+ });
1449
+ } else if (element['$ref']) {
1450
+ const bodyModel = parseModel(element['$ref'], apiDef);
1451
+ model.push({
1452
+ type: 'string',
1453
+ uiType: 'text',
1454
+ name: key,
1455
+ value: JSON.stringify(bodyModel)
1456
+ });
1457
+ }
1458
+ }
1459
+ }
1460
+ }
1461
+
1462
+ return model;
1463
+ }
1464
+
1465
+ function parseFormDataParaModel(parameterObj: any, dataDef: any) {
1466
+ let bodyModel: any = [];
1467
+ if (parameterObj.schema['$ref']) { // 对象类型
1468
+ bodyModel = parseFormDataModel(parameterObj.schema['$ref'], dataDef);
1469
+ }
1470
+ return bodyModel;
1471
+ }
1472
+
859
1473
  function parseElement(element: any) {
860
1474
  let elementValue;
861
1475
  if (element['type'].includes('integer')) {
862
- elementValue = 0;
1476
+ if (element['enum']) {
1477
+ elementValue = element['enum'][0];
1478
+ } else {
1479
+ elementValue = 0;
1480
+ }
863
1481
  } else if (element['type'].includes('boolean')) {
864
1482
  elementValue = false;
865
1483
  } else if (element['type'].includes('string')) {
@@ -870,6 +1488,12 @@ function parseElement(element: any) {
870
1488
  }
871
1489
  }
872
1490
 
1491
+ // 如果有default则用default的值
1492
+ if (element['default']) {
1493
+ elementValue = element['default'];
1494
+ }
1495
+
1496
+ //如果有example则用example的值
873
1497
  if (element['example']) {
874
1498
  elementValue = element['example'];
875
1499
  }
@@ -877,6 +1501,18 @@ function parseElement(element: any) {
877
1501
  return elementValue;
878
1502
  }
879
1503
 
1504
+ function parseElementType(element: any) {
1505
+ if (element['type'].includes('integer')) {
1506
+ return 'integer';
1507
+ } else if (element['type'].includes('boolean')) {
1508
+ return 'boolean';
1509
+ } else if (element['type'].includes('string')) {
1510
+ return 'string';
1511
+ }
1512
+ return 'string';
1513
+ }
1514
+
1515
+
880
1516
  const HTTP_METHODS = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'] as const;
881
1517
  type HttpMethod = typeof HTTP_METHODS[number]; // 等价于你的 HttpMethods 的值类型
882
1518
 
@@ -916,56 +1552,123 @@ function parseOpenAPI(openapiSpec: OpenAPIV3CustomDoc) {
916
1552
  const apiOperator: any = {
917
1553
  method: method.toUpperCase(),
918
1554
  url: path,
919
- rawApiInfo: null,
1555
+ rawApiInfo: operation,
920
1556
  requestBody: null,
921
1557
  response: {},
922
- auths: [
923
- {
924
- type: "No Auth"
925
- },
926
- {
927
- type: "Basic Auth",
928
- parameters: [
929
- {
930
- name: "userName",
931
- value: ""
932
- },
933
- {
934
- name: "password",
935
- value: ""
936
- }
937
- ]
938
- }
939
- ]
1558
+ auth: {}
940
1559
  };
941
1560
 
942
1561
  if (operation) {
943
1562
  // requestBody
944
1563
  // support openapi 2.0
945
- if (operation.parameters) {
946
- const parameter = operation.parameters.filter((val: any) => val.in == 'body');
947
- if (parameter && parameter.length > 0) {
948
- // let requestBody = parseParaModel(parameter[0], data['definitions']);
949
- // apiOperator.requestBody = requestBody;
1564
+ // if (operation.parameters) {
1565
+ // const parameter = operation.parameters.filter((val: any) => val.in == 'body');
1566
+ // if (parameter && parameter.length > 0) {
1567
+ // // let requestBody = parseParaModel(parameter[0], data['definitions']);
1568
+ // // apiOperator.requestBody = requestBody;
1569
+ // }
1570
+ // }
1571
+
1572
+ // //support openapi 3.0
1573
+ // if (operation.requestBody) {
1574
+ // const requestBodyObject = operation.requestBody as OpenAPIV3.RequestBodyObject //目前只支持对象类型 TODO 引用类型待支持
1575
+ // const content = requestBodyObject.content;
1576
+ // for (const key in content) {
1577
+ // if (Object.prototype.hasOwnProperty.call(content, key)) {
1578
+ // const element: OpenAPIV3.MediaTypeObject = content[key];
1579
+ // if (element) {
1580
+ // let requestBody = parseParaModel(element, openapiSpec["components"]!["schemas"]);
1581
+ // apiOperator.requestBody = requestBody;
1582
+ // }
1583
+ // }
1584
+ // }
1585
+ // }
1586
+
1587
+
1588
+ if (apiOperator.rawApiInfo.parameters) {
1589
+ const parameterBody = apiOperator.rawApiInfo.parameters.filter((val: any) => val.in == 'body');
1590
+ if (parameterBody && parameterBody.length > 0) {
1591
+ apiOperator["requestBody"] = {
1592
+ content: {},
1593
+ }
1594
+ apiOperator['consumes'] = apiOperator.rawApiInfo['consumes']
1595
+ const currentConsume = apiOperator.rawApiInfo['consumes'].length > 0 ? apiOperator.rawApiInfo['consumes'][0] : "application/json" //TODO 简化处理暂时取第一个
1596
+ apiOperator['currentConsume'] = currentConsume
1597
+ let requestBody: any = parseParaModel(parameterBody[0], openapiSpec['definitions']); // support openapi 2.0
1598
+ apiOperator["requestBody"]["content"][currentConsume] = JSON.stringify(requestBody, null, 4);
1599
+ apiOperator["parameterHasBody"] = true;//标记该接口有body参数,仅适用于openapi 2.0
1600
+ } else {
1601
+ for (const parameter of apiOperator.rawApiInfo.parameters) {
1602
+ if (parameter.in == 'query' || parameter.in == 'header' || parameter.in == 'path' || parameter.in == 'formData') {
1603
+ if (parameter.type == 'integer' || (parameter.schema != null && parameter.schema.type == 'integer')
1604
+ || parameter.type == 'number' || (parameter.schema != null && parameter.schema.type == 'number')) {
1605
+ parameter.uiType = 'number';
1606
+ }
1607
+ if (parameter.type == 'string' || (parameter.schema != null && parameter.schema.type == 'string')) {
1608
+ parameter.uiType = 'text';
1609
+ }
1610
+ if (parameter.type == 'boolean' || (parameter.schema != null && parameter.schema.type == 'boolean')) {
1611
+ parameter.uiType = 'text';// TODO 未来可以改成checkbox
1612
+ }
1613
+ }
1614
+ }
1615
+ const parameterFormData = apiOperator.rawApiInfo.parameters.filter((val: any) => val.in == 'formData');
1616
+ if (parameterFormData && parameterFormData.length > 0) {
1617
+ apiOperator["parameterHasFormDataVer2"] = true;//标记该接口有formData参数,仅适用于openapi 2.0 body参数和formData参数互斥
1618
+ }
1619
+ // support openapi 3.0 enum parameter parsing
1620
+ for (const param of apiOperator.rawApiInfo.parameters) {
1621
+ const paramSchema = param.schema;
1622
+ if (paramSchema && paramSchema['$ref']) {
1623
+ const bodyName = paramSchema['$ref'].substring(paramSchema['$ref'].lastIndexOf('/') + 1);
1624
+ if (openapiSpec && openapiSpec["components"] != null && openapiSpec["components"]["schemas"]!=null) {
1625
+ const def = openapiSpec["components"] != null ? openapiSpec["components"]["schemas"][bodyName] : {type: ''};
1626
+ param.type = parseElementType(def);
1627
+ param.uiType = (param.type == 'integer' || param.type == 'number') ? 'number' : 'text';
1628
+ param.value = parseElement(def);
1629
+ }
1630
+ }
1631
+ }
950
1632
  }
951
1633
  }
952
1634
 
953
1635
  //support openapi 3.0
954
- if (operation.requestBody) {
955
- const requestBodyObject = operation.requestBody as OpenAPIV3.RequestBodyObject //目前只支持对象类型 TODO 引用类型待支持
956
- const content = requestBodyObject.content;
1636
+ if (apiOperator.rawApiInfo.requestBody) {
1637
+ const content = apiOperator.rawApiInfo.requestBody.content;
1638
+ apiOperator["requestBody"] = {
1639
+ content: {}
1640
+ }
1641
+
957
1642
  for (const key in content) {
958
1643
  if (Object.prototype.hasOwnProperty.call(content, key)) {
959
- const element: OpenAPIV3.MediaTypeObject = content[key];
1644
+ const element = content[key];
1645
+ apiOperator['consumes']?.push(key);
960
1646
  if (element) {
961
- let requestBody = parseParaModel(element, openapiSpec["components"]!["schemas"]);
962
- apiOperator.requestBody = requestBody;
1647
+ if (element.schema == undefined) {
1648
+ console.log("pathKey is:", path);
1649
+ console.log("key is:", key);
1650
+ console.log("content is:", content);
1651
+ console.log("Unsupported requestBody schema format:", element);
1652
+ continue;
1653
+ }
1654
+ if (key != 'application/x-www-form-urlencoded' && key != 'multipart/form-data') {//TODO 支持更多的content-type
1655
+ let requestBody: any = parseParaModel(element, openapiSpec["components"]!["schemas"]);
1656
+ apiOperator["requestBody"]["content"][key] = JSON.stringify(requestBody, null, 4)
1657
+ } else {
1658
+ let requestFormDataBody: any = parseFormDataParaModel(element, openapiSpec["components"]!["schemas"]);
1659
+ apiOperator["requestBody"]["content"][key] = requestFormDataBody;
1660
+ }
1661
+ apiOperator["parameterHasBody"] = true;//标记该接口有body参数,仅适用于openapi 3.0
963
1662
  }
964
1663
  }
965
1664
  }
1665
+ if (apiOperator.consumes) {
1666
+ if (apiOperator.consumes.length > 0) {
1667
+ apiOperator['currentConsume'] = apiOperator['consumes'][0]
1668
+ }
1669
+ }
966
1670
  }
967
1671
 
968
- apiOperator.rawApiInfo = operation;
969
1672
  // 添加到结果列表
970
1673
  apiOperatorList.push(apiOperator);
971
1674
  }