fork of hey-api/openapi-ts because I need some additional things
at feat/skip-token 229 lines 6.8 kB view raw
1import './App.css'; 2 3import * as Form from '@radix-ui/react-form'; 4import { DownloadIcon, PlusIcon, ReloadIcon } from '@radix-ui/react-icons'; 5import { 6 Avatar, 7 Box, 8 Button, 9 Card, 10 Container, 11 Flex, 12 Heading, 13 Section, 14 Text, 15 TextField, 16} from '@radix-ui/themes'; 17import { useState } from 'react'; 18 19import { createClient } from './client/client'; 20import { PetSchema } from './client/schemas.gen'; 21import { addPet, getPetById, updatePet } from './client/sdk.gen'; 22import type { Pet } from './client/types.gen'; 23 24const localClient = createClient({ 25 // set default base url for requests made by this client 26 baseUrl: 'https://petstore3.swagger.io/api/v3', 27 /** 28 * Set default headers only for requests made by this client. This is to 29 * demonstrate local clients and their configuration taking precedence over 30 * internal service client. 31 */ 32 headers: { 33 Authorization: 'Bearer <token_from_local_client>', 34 }, 35}); 36 37localClient.interceptors.request.use((request, options) => { 38 // Middleware is great for adding authorization tokens to requests made to 39 // protected paths. Headers are set randomly here to allow surfacing the 40 // default headers, too. 41 if (options.url === '/pet/{petId}' && options.method === 'GET' && Math.random() < 0.5) { 42 request.headers.set('Authorization', 'Bearer <token_from_interceptor>'); 43 } 44 return request; 45}); 46 47localClient.interceptors.error.use((error) => { 48 console.log(error); 49 return error; 50}); 51 52function App() { 53 const [pet, setPet] = useState<Pet>(); 54 const [isRequiredNameError, setIsRequiredNameError] = useState(false); 55 56 const onAddPet = async (formData: FormData) => { 57 // simple form field validation to demonstrate using schemas 58 if (PetSchema.required.includes('name') && !formData.get('name')) { 59 setIsRequiredNameError(true); 60 return; 61 } 62 63 const { data, error } = await addPet({ 64 body: { 65 category: { 66 id: 0, 67 name: formData.get('category') as string, 68 }, 69 id: 0, 70 name: formData.get('name') as string, 71 photoUrls: ['string'], 72 status: 'available', 73 tags: [ 74 { 75 id: 0, 76 name: 'string', 77 }, 78 ], 79 }, 80 }); 81 if (error) { 82 console.log(error); 83 return; 84 } 85 setPet(data!); 86 setIsRequiredNameError(false); 87 }; 88 89 const onGetPetById = async () => { 90 const { data, error } = await getPetById({ 91 client: localClient, 92 path: { 93 // random id 1-10 94 petId: Math.floor(Math.random() * (10 - 1 + 1) + 1), 95 }, 96 }); 97 if (error) { 98 console.log(error); 99 return; 100 } 101 setPet(data!); 102 }; 103 104 const onUpdatePet = async () => { 105 const { data, error } = await updatePet({ 106 body: { 107 category: { 108 id: 0, 109 name: 'Cats', 110 }, 111 id: 2, 112 name: 'Updated Kitty', 113 photoUrls: ['string'], 114 status: 'available', 115 tags: [ 116 { 117 id: 0, 118 name: 'string', 119 }, 120 ], 121 }, 122 // setting headers per request 123 headers: { 124 Authorization: 'Bearer <token_from_method>', 125 }, 126 }); 127 if (error) { 128 console.log(error); 129 return; 130 } 131 setPet(data!); 132 }; 133 134 return ( 135 <Box style={{ background: 'var(--gray-a2)', borderRadius: 'var(--radius-3)' }}> 136 <Container size="1"> 137 <Section size="1" /> 138 <Flex align="center"> 139 <a className="shrink-0" href="https://heyapi.dev/" target="_blank"> 140 <img 141 src="https://heyapi.dev/assets/raw/logo.png" 142 className="h-16 w-16 transition duration-300 will-change-auto" 143 alt="Hey API logo" 144 /> 145 </a> 146 <Heading>@hey-api/openapi-ts 🤝 Fetch API</Heading> 147 </Flex> 148 <Section size="1" /> 149 <Flex direction="column" gapY="2"> 150 <Box maxWidth="240px"> 151 <Card> 152 <Flex gap="3" align="center"> 153 <Avatar 154 size="3" 155 src={pet?.photoUrls[0]} 156 radius="full" 157 fallback={pet?.name.slice(0, 1) ?? 'N'} 158 /> 159 <Box> 160 <Text as="div" size="2" weight="bold"> 161 Name: {pet?.name ?? 'N/A'} 162 </Text> 163 <Text as="div" size="2" color="gray"> 164 Category: {pet?.category?.name ?? 'N/A'} 165 </Text> 166 </Box> 167 </Flex> 168 </Card> 169 </Box> 170 <Button onClick={onGetPetById}> 171 <DownloadIcon /> Get Random Pet 172 </Button> 173 </Flex> 174 <Section size="1" /> 175 <Flex direction="column" gapY="2"> 176 <Form.Root 177 className="w-[260px]" 178 onSubmit={(event) => { 179 event.preventDefault(); 180 onAddPet(new FormData(event.currentTarget)); 181 }} 182 > 183 <Form.Field className="grid mb-[10px]" name="email"> 184 <div className="flex items-baseline justify-between"> 185 <Form.Label className="text-[15px] font-medium leading-[35px] text-white"> 186 Name 187 </Form.Label> 188 {isRequiredNameError && ( 189 <Form.Message className="text-[13px] text-white opacity-[0.8]"> 190 Please enter a name 191 </Form.Message> 192 )} 193 </div> 194 <Form.Control asChild> 195 <TextField.Root placeholder="Kitty" name="name" type="text" /> 196 </Form.Control> 197 </Form.Field> 198 <Form.Field className="grid mb-[10px]" name="question"> 199 <div className="flex items-baseline justify-between"> 200 <Form.Label className="text-[15px] font-medium leading-[35px] text-white"> 201 Category 202 </Form.Label> 203 <Form.Message className="text-[13px] text-white opacity-[0.8]" match="valueMissing"> 204 Please enter a category 205 </Form.Message> 206 </div> 207 <Form.Control asChild> 208 <TextField.Root placeholder="Cats" name="category" type="text" required /> 209 </Form.Control> 210 </Form.Field> 211 <Flex gapX="2"> 212 <Form.Submit asChild> 213 <Button type="submit"> 214 <PlusIcon /> Add Pet 215 </Button> 216 </Form.Submit> 217 <Button onClick={onUpdatePet} type="button"> 218 <ReloadIcon /> Update Pet 219 </Button> 220 </Flex> 221 </Form.Root> 222 </Flex> 223 <Section size="1" /> 224 </Container> 225 </Box> 226 ); 227} 228 229export default App;