Skip to content

Commit c906f11

Browse files
committed
feat(New tool): API Tester
Fix CorentinTh#837
1 parent b430bae commit c906f11

File tree

3 files changed

+132
-0
lines changed

3 files changed

+132
-0
lines changed

src/tools/api-tester/api-tester.vue

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<script setup lang="ts">
2+
import { useQueryParamOrStorage } from '@/composable/queryParams';
3+
import TextareaCopyable from '@/components/TextareaCopyable.vue';
4+
5+
interface KeyValuePair {
6+
key: string
7+
value?: string
8+
}
9+
const baseUrl = useQueryParamOrStorage({ name: 'url', storageName: 'api-tester:url', defaultValue: '' });
10+
const method = useQueryParamOrStorage({ name: 'method', storageName: 'api-tester:m', defaultValue: 'POST' });
11+
const queryParams = useQueryParamOrStorage<KeyValuePair[]>({ name: 'params', storageName: 'api-tester:params', defaultValue: [] });
12+
const headers = useQueryParamOrStorage<KeyValuePair[]>({ name: 'headers', storageName: 'api-tester:headers', defaultValue: [] });
13+
const body = useQueryParamOrStorage({ name: 'body', storageName: 'api-tester:body', defaultValue: '' });
14+
const apiCallResult = ref();
15+
16+
const inprogress = ref(false);
17+
async function callAPI() {
18+
const url = new URL(baseUrl.value);
19+
for (const kv of queryParams.value) {
20+
url.searchParams.append(kv.key, kv.value || '');
21+
}
22+
const queryHeaders = [] as [string, string][];
23+
for (const kv of headers.value) {
24+
queryHeaders.push([kv.key, kv.value || '']);
25+
}
26+
27+
try {
28+
const response = await fetch(url, {
29+
method: method.value,
30+
headers: queryHeaders,
31+
body: body.value,
32+
});
33+
34+
apiCallResult.value = {
35+
code: response.status,
36+
error: '',
37+
result: await response.text(),
38+
};
39+
}
40+
catch (err: any) {
41+
apiCallResult.value = {
42+
code: -1,
43+
error: err.toString(),
44+
result: null,
45+
};
46+
}
47+
}
48+
</script>
49+
50+
<template>
51+
<div>
52+
<c-card title="">
53+
<c-input-text
54+
v-model:value="baseUrl"
55+
label="Base API Url"
56+
placeholder="Base API Url"
57+
mb-2
58+
/>
59+
60+
<c-select
61+
v-model:value="method"
62+
label="HTTP Method:"
63+
:options="['GET', 'POST', 'PUT', 'DELETE', 'PATCH']"
64+
/>
65+
66+
<c-card title="Headers" mb-2>
67+
<n-dynamic-input v-model:value="headers">
68+
<template #create-button-default>
69+
Add a new HTTP Header
70+
</template>
71+
<template #default="{ value }">
72+
<div style="display: flex; align-items: center; width: 100%">
73+
<n-input v-model:value="value.key" type="text" />
74+
<n-input v-model:value="value.value" type="text" />
75+
</div>
76+
</template>
77+
</n-dynamic-input>
78+
</c-card>
79+
80+
<c-card title="Query Parameters" mb-2>
81+
<n-dynamic-input v-model:value="queryParams">
82+
<template #create-button-default>
83+
Add a new HTTP Header
84+
</template>
85+
<template #default="{ value }">
86+
<div style="display: flex; align-items: center; width: 100%">
87+
<n-input v-model:value="value.key" type="text" />
88+
<n-input v-model:value="value.value" type="text" />
89+
</div>
90+
</template>
91+
</n-dynamic-input>
92+
</c-card>
93+
<c-input-text
94+
v-model:value="body"
95+
label="Body"
96+
placeholder="HTTP Query body"
97+
multiline
98+
monospace
99+
mb-2
100+
/>
101+
102+
<c-button secondary @click="callAPI">
103+
Call API
104+
</c-button>
105+
</c-card>
106+
<n-spin
107+
v-if="inprogress"
108+
size="small"
109+
/>
110+
<c-alert v-if="!inprogress && apiCallResult.code !== 200" type="error" mt-12 title="Error while calling API">
111+
<p><strong>Status code = {{ apiCallResult.code }}</strong></p>
112+
<TextareaCopyable :value="apiCallResult.error" />
113+
</c-alert>
114+
<c-card v-if="!inprogress && apiCallResult.code === 200" mt-12 title="API Call result">
115+
<TextareaCopyable :value="apiCallResult.result" />
116+
</c-card>
117+
</div>
118+
</template>

src/tools/api-tester/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { World } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'API Tester',
6+
path: '/api-tester',
7+
description: 'HTTP API Tester',
8+
keywords: ['api', 'http', 'call', 'tester'],
9+
component: () => import('./api-tester.vue'),
10+
icon: World,
11+
createdAt: new Date('2024-05-11'),
12+
});

src/tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { tool as asciiTextDrawer } from './ascii-text-drawer';
66

77
import { tool as textToUnicode } from './text-to-unicode';
88
import { tool as safelinkDecoder } from './safelink-decoder';
9+
import { tool as apiTester } from './api-tester';
910
import { tool as pdfSignatureChecker } from './pdf-signature-checker';
1011
import { tool as numeronymGenerator } from './numeronym-generator';
1112
import { tool as macAddressGenerator } from './mac-address-generator';
@@ -128,6 +129,7 @@ export const toolsByCategory: ToolCategory[] = [
128129
httpStatusCodes,
129130
jsonDiff,
130131
safelinkDecoder,
132+
apiTester,
131133
],
132134
},
133135
{

0 commit comments

Comments
 (0)