@@ -126,10 +126,34 @@ limitations under the License.
126
126
127
127
<!-- Request Payload Tab -->
128
128
<n-tab-pane name="Request Payload" tab="Request Payload">
129
- <!-- Test Request Button -->
129
+ <!-- Test Request and Import Buttons -->
130
130
<n-space justify="end" size="large">
131
131
<v-btn variant="flat" color="secondary" class="mt-2" @click="testRequest"> Test Request </v-btn>
132
+ <v-btn variant="flat" color="secondary" class="mt-2" @click="openImportModal"> Import </v-btn>
132
133
</n-space>
134
+
135
+ <!-- Import Modal -->
136
+ <n-modal v-model:show="showImportModal">
137
+ <n-card style="width: 600px" title="Import Curl Command" :bordered="false" size="huge" role="dialog" aria-modal="true">
138
+ <n-input
139
+ v-model:value="curlCommandInput"
140
+ type="textarea"
141
+ placeholder="Paste your curl command here"
142
+ :autosize="{
143
+ minRows: 5,
144
+ maxRows: 10
145
+ }"
146
+ />
147
+ <n-divider></n-divider>
148
+ <div class="modal-footer">
149
+ <n-space>
150
+ <v-btn variant="flat" color="primary" class="mt-2" @click="handleImport">Import</v-btn>
151
+ <v-btn @click="showImportModal = false" class="mt-2">Cancel</v-btn>
152
+ </n-space>
153
+ </div>
154
+ </n-card>
155
+ </n-modal>
156
+
133
157
<n-divider />
134
158
<!-- JSON Editor for Request Payload -->
135
159
<json-editor-vue v-model="templateData.test_request" mode="text"></json-editor-vue>
@@ -245,7 +269,8 @@ import {
245
269
useMessage,
246
270
NSelect,
247
271
NInputNumber,
248
- NCheckbox
272
+ NCheckbox,
273
+ NModal
249
274
} from 'naive-ui';
250
275
import type { TabsInst } from 'naive-ui';
251
276
import type { UploadFileInfo } from 'naive-ui';
@@ -395,6 +420,36 @@ const rules = {
395
420
// ... add validation rules for other fields
396
421
};
397
422
423
+ // Ref for the modal visibility
424
+ const showImportModal = ref(false);
425
+
426
+ // Ref for the curl command input
427
+ const curlCommandInput = ref('');
428
+
429
+ /**
430
+ * Opens the import modal.
431
+ */
432
+ const openImportModal = () => {
433
+ showImportModal.value = true;
434
+ };
435
+
436
+ /**
437
+ * Handles the import of a curl command.
438
+ */
439
+ const handleImport = () => {
440
+ try {
441
+ const json = curlToJson(curlCommandInput.value); // Call curlToJson function
442
+ templateData.value.test_request = JSON.stringify(json, null, 2); // Format as JSON string
443
+ message.success('Curl command imported successfully!');
444
+ } catch (error) {
445
+ console.error('Error importing curl command:', error);
446
+ message.error('Invalid curl command.');
447
+ } finally {
448
+ showImportModal.value = false; // Close the modal
449
+ curlCommandInput.value = ''; // Reset the input
450
+ }
451
+ };
452
+
398
453
/**
399
454
* Handles file uploads, validating for JSON format and structure.
400
455
* @param data - Upload event data including file and fileList.
@@ -562,6 +617,11 @@ const addItem = () => {
562
617
const testRequest = async () => {
563
618
const reqString = templateData.value.test_request;
564
619
620
+ // Type guard function to check if an object is an Error
621
+ function isError(error: unknown): error is Error {
622
+ return typeof error === 'object' && error !== null && 'message' in error;
623
+ }
624
+
565
625
if (typeof reqString === 'string') {
566
626
try {
567
627
const req = JSON.parse(reqString);
@@ -577,11 +637,31 @@ const testRequest = async () => {
577
637
test_response = JSON.stringify(resptemp);
578
638
showOutputUI.value = true;
579
639
} else {
580
- message.error('Error: ' + response.status + ' ' + response.statusText);
640
+ // Handle non-OK responses with more detail
641
+ let errorMessage = `Error: ${response.status} ${response.statusText}`;
642
+
643
+ try {
644
+ // Attempt to parse the error response as JSON for more context
645
+ const errorJson = await response.json();
646
+ if (errorJson && errorJson.message) {
647
+ errorMessage += ` - ${errorJson.message}`;
648
+ } else if (errorJson) {
649
+ errorMessage += ` - ${JSON.stringify(errorJson)}`;
650
+ }
651
+ } catch (error) {
652
+ // If parsing the error response fails, just use the status/statusText
653
+ }
654
+
655
+ message.error(errorMessage);
581
656
}
582
657
} catch (parseError) {
658
+ // Handle JSON parsing errors with the specific error message (using the type guard)
583
659
console.error('Error parsing JSON:', parseError);
584
- message.error('Invalid JSON format in Request Payload');
660
+ if (isError(parseError)) {
661
+ message.error(`Invalid JSON format in Request Payload: ${parseError.message}`);
662
+ } else {
663
+ message.error(`Invalid JSON format in Request Payload (unknown error): ${parseError}`);
664
+ }
585
665
}
586
666
} else {
587
667
console.error('test_request is undefined');
@@ -796,4 +876,154 @@ watch(showDeepEvalOptions, (newValue) => {
796
876
onUnmounted(() => {
797
877
// ... Perform any cleanup if necessary
798
878
});
879
+
880
+ interface RequestBody {
881
+ [key: string]: any; // Allow any key-value pairs in the request body
882
+ }
883
+
884
+ interface TransformedRequest {
885
+ body?: RequestBody | string; // Allow string body for non-JSON data
886
+ headers: { [key: string]: string };
887
+ method: string;
888
+ url: string;
889
+ }
890
+
891
+ function curlToJson(curlCommand: string): TransformedRequest {
892
+ const lines = curlCommand.split('\n');
893
+ let requestBody: RequestBody | string | undefined;
894
+ const headers: { [key: string]: string } = {};
895
+ let method = 'GET'; // Default to GET if not specified
896
+ let url = '';
897
+ let inDataSection = false;
898
+ let inHeredoc = false;
899
+ let heredocContent = '';
900
+
901
+ // Extract values from the curl command
902
+ lines.forEach((line) => {
903
+ line = line.trim();
904
+
905
+ // Remove backslashes and leading/trailing single quotes from URL, data, and method (where appropriate)
906
+ if (
907
+ line.startsWith('curl') ||
908
+ line.startsWith('-X') ||
909
+ line.startsWith('-d') ||
910
+ line.startsWith('--data') ||
911
+ line.startsWith('--data-raw')
912
+ ) {
913
+ line = line.replace(/\\\\/g, ''); // Only remove backslashes from these lines
914
+ }
915
+ if (line.startsWith('curl') || line.startsWith('-d') || line.startsWith('--data') || line.startsWith('--data-raw')) {
916
+ line = line.replace(/^'/, '').replace(/'$/, ''); // Only remove single quotes from these lines
917
+ }
918
+
919
+ if (line.startsWith('cat << EOF')) {
920
+ inHeredoc = true;
921
+ } else if (line === 'EOF') {
922
+ inHeredoc = false;
923
+ try {
924
+ requestBody = JSON.parse(heredocContent);
925
+ } catch (error) {
926
+ // If not valid JSON, treat as plain text
927
+ requestBody = heredocContent;
928
+ }
929
+ heredocContent = ''; // Reset for potential future heredocs
930
+ } else if (inHeredoc) {
931
+ heredocContent += line + '\n';
932
+ } else if (line.startsWith('curl')) {
933
+ // Extract URL if present on the same line (handle both single and double quotes)
934
+ const urlMatch = line.match(/'(.*?)'|"(.*?)"/);
935
+ if (urlMatch) {
936
+ url = urlMatch[1] || urlMatch[2]; // Use the captured group that matched
937
+ }
938
+ } else if (line.startsWith('-X')) {
939
+ // Capture the method (and remove any trailing backslashes and spaces)
940
+ method = line.substring('-X '.length).trim().replace(/\\$/, '').toUpperCase().replace(/\s/g, '');
941
+ } else if (line.startsWith('-H')) {
942
+ // Capture headers (handle both single and double quotes correctly, and URLs)
943
+ const headerMatch = line.match(/'(.*?)'|"(.*?)"/);
944
+ if (headerMatch) {
945
+ const headerLine = headerMatch[1] || headerMatch[2];
946
+ const colonIndex = headerLine.indexOf(':');
947
+ if (colonIndex > -1) {
948
+ const key = headerLine.substring(0, colonIndex).trim();
949
+ const value = headerLine.substring(colonIndex + 1).trim();
950
+ headers[key] = value;
951
+ }
952
+ }
953
+ } else if (line.startsWith('-d') || line.startsWith('--data') || line.startsWith('--data-raw')) {
954
+ // Capture request body (handle file path or inline data)
955
+ const singleQuoteDataStartIndex = line.indexOf("'") + 1; // Assuming data is enclosed in single quotes
956
+ const doubleQuoteDataStartIndex = line.indexOf('"') + 1; // Assuming data is enclosed in double quotes
957
+ let data = '';
958
+ if (line.includes("'")) {
959
+ data = line.substring(singleQuoteDataStartIndex);
960
+ if (data.endsWith("'")) {
961
+ data = data.substring(0, data.length - 1);
962
+ }
963
+ } else if (line.includes('"')) {
964
+ data = line.substring(doubleQuoteDataStartIndex);
965
+ if (data.endsWith('"')) {
966
+ // Corrected: Removed extra double quote
967
+ data = data.substring(0, data.length - 1);
968
+ }
969
+ }
970
+
971
+ if (data.startsWith('@')) {
972
+ // If data starts with '@', it's a file path, so use the heredoc content as the body
973
+ requestBody = requestBody; // Assuming requestBody was already populated from the heredoc
974
+ } else {
975
+ // Otherwise, it's inline data, try parsing as JSON or keep as string
976
+ if (headers['Content-Type'] && headers['Content-Type'].includes('application/json')) {
977
+ try {
978
+ requestBody = JSON.parse(data);
979
+ } catch (error) {
980
+ // If it's supposed to be JSON but parsing fails, try to fix it or keep as string
981
+ try {
982
+ requestBody = fixInvalidJson(data);
983
+ } catch (fixError) {
984
+ requestBody = data;
985
+ console.warn('Invalid JSON in request body (could not fix):', data);
986
+ }
987
+ }
988
+ } else if (headers['Content-Type'] && headers['Content-Type'].includes('application/x-www-form-urlencoded')) {
989
+ requestBody = parseUrlEncodedData(data);
990
+ } else {
991
+ requestBody = data;
992
+ }
993
+ }
994
+ } else if (line.startsWith("'") && !inDataSection) {
995
+ // If no other match and not in the data section, assume it's the URL
996
+ url = line.substring(1, line.length - 1);
997
+ } else if (line.startsWith('"') && !inDataSection) {
998
+ // If no other match and not in the data section, assume it's the URL
999
+ url = line.substring(1, line.length - 1);
1000
+ }
1001
+ });
1002
+
1003
+ // Construct the output object
1004
+ const outputObject: TransformedRequest = {
1005
+ body: requestBody,
1006
+ headers,
1007
+ method,
1008
+ url
1009
+ };
1010
+
1011
+ return outputObject;
1012
+ }
1013
+
1014
+ function fixInvalidJson(jsonString: string): RequestBody {
1015
+ // Basic attempt to fix common JSON errors (e.g., trailing commas)
1016
+ const fixedJsonString = jsonString.replace(/,\s*([}\]])/g, '$1');
1017
+ return JSON.parse(fixedJsonString);
1018
+ }
1019
+
1020
+ function parseUrlEncodedData(data: string): RequestBody {
1021
+ const result: RequestBody = {};
1022
+ const pairs = data.split('&');
1023
+ pairs.forEach((pair) => {
1024
+ const [key, value] = pair.split('=');
1025
+ result[decodeURIComponent(key)] = decodeURIComponent(value);
1026
+ });
1027
+ return result;
1028
+ }
799
1029
</script>
0 commit comments